개발 기록/개발 요약

[toy-project] Library Book Manager (CLI, Java)

dev.jelee 2025. 7. 3. 01:59

📌 프로젝트 개요

16일차까지 배운 Java 기초 문법을 활용하여 만든 도서관 도서 관리 시스템 (CLI 기반)
  • 프로젝트 명: Library Book Manager (CLI)
  • 개발 기간: 25.07.02 ~ 25.07.03
  • 목적
    • Java 기초 문법 복습
    • 실무처럼 디렉토리 및 클래스 구조 설계 연습
    • 단순 기능 구현 이상의 설계 감각을 기르기 위한 실습
  • 사용 기술: Java 17, VS Code IDE, Git & GitHub

🧩 구현 기능

기능 설명
책 등록 제목, 저자, 출판 연도 입력 후 리스트에 저장
전체 목록 조회 등록된 도서들을 연도순으로 정렬하여 출력
책 검색 제목 또는 저자 키워드로 부분 검색
책 삭제 제목을 기준으로 도서 삭제
종료 프로그램 종료
예외 처리 연도 음수 입력, 사용자 입력 오류에 대한 처리

💜 GitHub 저장소 & 데모

🔗 GitHub Repository

🎬 데모 영상 보러가기


📁 디렉토리 구조

📂 src/com/example/librarybookmanager
├── LibraryBookManagerApplication.java   // 프로그램 실행 진입점
├── domain/
│   └── Book.java                        // 도서 정보 클래스
├── service/
│   ├── LibraryService.java              // 서비스 인터페이스
│   └── LibraryServiceImpl.java          // 비즈니스 로직 구현체
├── constants/
│   └── Messages.java                    // 메시지 상수 모음
├── exception/
│   └── InvalidYearException.java        // 사용자 정의 예외
├── comparator/
│   ├── BookTitleComparator.java         // 제목 정렬
│   ├── BookAuthorComparator.java        // 저자 정렬
│   └── BookYearComparator.java          // 연도 정렬
└── README.md

🧱 클래스/설계 구조 설명

  • Book:
    • private으로 title, author, year 멤버 필드를 생성했다.
    • 도서 등록을 위해 Book(String title, String author, int year) 사용자 함수를 생성했다.
    • 책 제목 또는 저자, 출판연도로 도서를 검색할 수 있도록 getter를 생성했다.
    • Object를 기본으로 상속 받기 때문에 toString() 메서드를 오버라이드하여 제목 (저자, 출판연도) 이렇게 출력되도록 작성했다.
  • LibraryService:
    • 등록은 registerBook()
    • 조회는 printBook()
    • 검색은 searchBook()
    • 삭제는 deleteBook()으로 정의한 인터페이스 파일이다.
  • LibraryServiceImpl:
    • LibraryService의 구현 클래스이다.
    • List<Book> 기반으로 로직 구성하였다. List<Book> books
    • Scanner로 입력을 받는 형태이다.
    • 책 등록은
      • try-catch문을 사용했다.
      • try 블럭에는 책 제목, 저자, 출판 연도 순서로 값을 입력받아서 books 리스트에 저장하는데, 출판연도가 0보다 작은. 음수의 값이면 예외처리를 해준다. 
      • catch 블럭에는 연도를 음수로 입력하였을 경우 커스텀으로 생성한 InvalidYearExcepion으로 예외처리한다.
      • 두번째 catch 블럭에는 Message클래스에서 정의한 ERROR_INPUT을 불러와 입력 형식이 올바르지 않다는 메시지를 출력해준다.
    • 책 전체 목록은
      • books가 비어 있다면 Message클래스에서 정의한 NO_BOOKS를 불러와 책이 없다는 메시지를 출력해준다.
      • 책이 있다면 기본 연도별로 정렬을 한 다음에 for-each문을 사용해 books에 담긴 책을 출력한다.
    • 책 검색은
      • 사용자로부터 입력받은 값을 keyword 변수에 저장하는데 알파벳의 경우를 대비해 소문자로 변환하여 저장한다.
      • for-each문을 사용하여 books 리스트에 각각의 요소를 꺼내어 해당 keyword가 있는지 .contains()를 사용해 체크한 다음 있으면 해당 책을 출력해준다.
      • 만약에 없으면 Message클래스에서 정의한 BOOK_NOT_FOUND를 불러와 책을 찾을 수 없다는 메시지를 출력한다.
    • 책 삭제는
      • 사용자로부터 삭제할 책 제목을 입력 받아 title 변수에 저장한다.
      • .removeIf()를 사용하여 books 리스트(컬렉션)에 해당 title이 있으면 제거한다.
      • 제거에 성공했으면 Message 클래스에서 정의한 SUCCESS_DELETE를 불러와 삭제 성공했다는 메시지를 출력.
      • 해당 책 제목이 없어서 제거하지 못하면 Message 클래스에서 정의한 BOOK_NOT_FOUND를 불러와 책이 없다는 메시지를 출력한다.
  • Comparator 3종: 연도/제목/저자
    • BookAuthorComparator, BookTitleComparator, BookYearComparator 각각 연도, 제목, 저자 기준으로 오름차순 정렬을 생성해준다.
    • 여기서 나는 BookYearComparator만 사용했다.
  • Messages:
    • UI 메시지 텍스트를 상수로 관리
    • TITLE, MENU, ERROR_INVALID_YEAR, ERROR_INPUT, SUCCESS_ADD, SUCCESS_DELETE, BOOK_NOT_FOUND, NO_BOOKS 이렇게 정의했다.
  • InvalidYearException:
    • Exception을 상속받는다.
    • 출판 연도 음수 입력 시 사용자 정의 예외 발생을 위한 클래스다.

🔢 개발 순서

순서 내용 상세 설명
1 Book 객체 설계 제목, 저자, 출판연도를 멤버 변수로 가짐
2 예외 처리 출판 연도를 음수로 입력 시 InvalidYearException 발생
3 고정 메시지 설계 - Message.java에 고정으로 사용되는 메시지 작성
- 메시지는 public static final 키워드를 사용하여 작성
4 정렬 관련 파일 설계 - 책 제목, 저자, 출판연도 각각의 comparator 파일을 생성
- BookTitleComparaotr.java, BookAuthorComparator.java,
BookYearComparator.java
5 LibraryService 인터페이스와
구현체 설계
- LibraryService 인터페이스에 책 등록/전체 목록/검색/삭제 메서드 작성
- LibraryServiceImpl을 구현체로 생성하여 비즈니스 로직을 설계
6 프로그램 실행 main 작성 - while문을 사용하여 메뉴 무한 반복 실행.
- try-catch문을 사용하고, try 블럭에는 사용자가 메뉴를 입력하면
swtich-case문으로 각 case별로 메서드 실행

🐞 문제 해결 & 트러블슈팅

✅ 이슈 1 - 정렬 시 에러 (Comparator 관련)

  • 문제: .compareToIgnoreCase() 에서 에러 발생
  • 원인: 정수 비교에 문자열 비교 함수 사용
  • 해결:
// 숫자 정렬
Integer.compare(b1.getYear(), b2.getYear());

// 문자열 정렬
b1.getTitle().compareToIgnoreCase(b2.getTitle());

✅ 이슈 2 - 책 전체 목록 출력 안됨

  • 문제: 책을 등록하고 목록을 조회하면 출력이 되지 않음
  • 원인: 정렬만 하고 출력 메서드 누락
  • 해결: 정렬 후 리스트 출력 코드 추가

✅ 이슈 3 - 도서 검색 시 입력이 되지 않음

  • 문제: 검색 메뉴 선택 후 keyword 입력이 안 되고 전체 목록이 바로 출력됨
  • 원인: Scanner의 nextInt() 이후 nextLine() 사용 -> 버퍼 문제
  • 해결:
int input = Integer.parseInt(sc.nextLine());

✅ 이슈 4 - Scanner 경고 발생

  • 문제: Scanner 인스턴스에 노란 밑줄이 생김 (자원 누수 경고)
  • 해결: try-with-resources 구문 사용
try (Scanner sc = new Scanner(System.in)) {
    // 코드
}

✍ 느낀 점 / 회고

  • 클래스 설계를 하다 보니 처음에 어려웠지만 모르는 것은 찾아가며 나의 속도대로 나아가니 이해를 하게 되었다.
  • 인터페이스와 구현체를 나누는 것을 글로만 봤을 때에는 '그렇구나' 정도로 이해를 했지만, 이번에 직접 만들어보니 나중에 코드를 수정하거나 유지보수면에서도 관리하기 좋다는 걸 느꼈다.
  • Scanner 버퍼 문제, Comparator에 대해서 한 걸음 더 다가가게 되었다.
  • 이번 토이 프로젝트를 진행하면서 구조를 어떻게 나누어야 하는지, 각 구조마다 어떤 역할을 하는지 알게 되었다.