| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- 디자인강의
- 디자인교육
- 디자인챌린지
- 오블완
- 국비지원취업
- baekjoon
- 백엔드 부트캠프
- UXUI기초정복
- KDT
- 부트캠프
- API
- 오픈패스
- 백엔드개발자
- 시스템설계
- 패스트캠퍼스
- 백준
- 내일배움카드
- Be
- 국비지원
- Java
- 오픈챌린지
- mysql
- Spring
- OPENPATH
- JPA
- 환급챌린지
- 티스토리챌린지
- UXUIPrimary
- 국비지원교육
- UXUI챌린지
- Today
- Total
군만두의 IT 개발 일지
패스트캠퍼스 백엔드 개발 부트캠프 8기 그룹스터디 - 도서관 시스템 설계 (Java, JDBC, MySQL) 본문
목차
⭐ 요약
- 멘토님이 프로젝트를 처음 해보는 사람도 있으니 스터디그룹 내에서 미니 프로젝트를 해보라고 하셔서 개발하게 되었다.
- Java, JDBC, MySQL을 활용한 도서관 관리 시스템을 설계하고 구현한다.
- 회원 관리, 도서 관리, 대출 관리, 예약 관리 총 4가지 기능을 MVC와 유사한 패턴으로 설계했다.
- 싱글톤 패턴, DTO 패턴, DAO 패턴 등 다양한 디자인 패턴을 실습한다.
⭐ 프로젝트 개요
그룹스터디 팀원들과 함께 콘솔 기반 도서관 관리 시스템을 Java로 직접 설계하고 구현하는 프로젝트를 진행한다. 단순히 코드를 작성하는 것에 그치지 않고, 레이어 분리 구조, 디자인 패턴, Git 커밋 컨벤션까지 함께 적용한다.
| 항목 | 내용 |
| 언어 | Java 17 |
| DB | MySQL + JDBC (mysql-connector-j 8.3.0) |
| 개발 환경 | IntelliJ IDEA |
| 주요 기능 | 회원 관리, 도서 관리, 대출 관리, 예약 관리 |
| 적용 패턴 | MVC 유사 구조, DAO 패턴, DTO 패턴, 싱글톤 패턴 |
⭐ Git 커밋 컨벤션
팀 협업의 기본인 커밋 메시지 규칙부터 먼저 정했다. 커밋 메시지는 Header(필수), Body(생략 가능), Footer(생략 가능)로 구성하며, 각 영역은 빈 행으로 구분한다.
| 타입 | 내용 |
| feat | 새로운 기능에 대한 커밋 |
| fix | 버그 수정에 대한 커밋 |
| build | 빌드 관련 파일 수정 / 모듈 설치 또는 삭제에 대한 커밋 |
| chore | 그 외 자잘한 수정에 대한 커밋 |
| docs | 문서 수정에 대한 커밋 |
| style | 코드 스타일 혹은 포맷 등에 관한 커밋 |
| refactor | 코드 리팩토링에 대한 커밋 |
| test | 테스트 코드 수정에 대한 커밋 |
| perf | 성능 개선에 대한 커밋 |
작성 예시는 아래와 같다.
fix: Safari에서 모달을 띄웠을 때 스크롤 이슈 수정
모바일 사파리에서 Carousel 모달을 띄웠을 때,
모달 밖의 상하 스크롤이 움직이는 이슈 수정.
issues: #1137
⭐ 패키지 구조 및 레이어 설계
Spring Boot 없이 순수 Java로 구현하면서 레이어를 분리했다. 패키지는 도메인 단위로 나누고, 각 도메인 안에 역할에 따라 클래스를 분리했다.
| 패키지 | 역할 | 주요 클래스 |
| db | DB 연결 및 해제 관리 | Database |
| user | 회원 도메인 | User, UserController, UserView |
| book | 도서 도메인 | Book, BookController, BookView, BookDB |
| bookDto | 도서 DTO | BookDto, EBookDto, PhysicalBookDto |
| loan | 대출 도메인 | Loan, LoanController, LoanDAO |
| reservation | 예약 도메인 | ReservationDAO, ReservationVO, BookVO |
⭐ 주요 구현 내용
1. DB 연결 - Database 클래스
DB 접속 정보(서버 주소, DB명, 계정, 비밀번호)는 config.properties 파일로 분리하여 관리한다. 이 파일은 .gitignore에 등록하여 민감 정보가 깃허브에 올라가지 않도록 처리했다.
public static void connect() throws IOException {
prop.load(new FileInputStream("config.properties"));
String classname = prop.getProperty("db.classname");
String server = prop.getProperty("db.server");
String database = prop.getProperty("db.database");
String username = prop.getProperty("db.username");
String password = prop.getProperty("db.password");
Class.forName(classname); // JDBC 드라이버 로드
con = DriverManager.getConnection(
"jdbc:mysql://" + server + "/" + database +
"?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC",
username, password
);
}
2. 회원 관리 - UserController
회원 관련 CRUD와 로그인 기능을 구현했다. DB 연결은 try-with-resources 구문을 사용하여 Connection과 PreparedStatement가 자동으로 닫히도록 처리했다. RETURN_GENERATED_KEYS 옵션으로 INSERT 후 자동 생성된 PK를 즉시 반환받는다.
// try-with-resources로 자원을 자동으로 해제한다.
try (Connection conn = Database.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
pstmt.setString(1, name);
pstmt.setString(2, phone);
pstmt.setString(3, email);
pstmt.setString(4, password);
pstmt.setTimestamp(5, Timestamp.valueOf(LocalDateTime.now()));
pstmt.executeUpdate();
// INSERT 후 자동 생성된 PK를 가져온다.
try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
if (generatedKeys.next()) {
int userId = generatedKeys.getInt(1);
return new User(userId, name, phone, email, LocalDateTime.now(), password);
}
}
}
3. 도서 검색 - 공백 무시 검색 구현
사용자가 공백을 포함하거나 제외하고 검색해도 동일한 결과를 반환하도록 MySQL의 REPLACE 함수를 활용했다. 검색어와 DB 데이터 모두에서 공백을 제거한 뒤 비교하는 방식이다.
// 제목에서 공백을 제거한 뒤 LIKE 검색을 수행한다.
String sql = "SELECT * FROM Book WHERE REPLACE(title, ' ', '') LIKE ?";
pstmt.setString(1, "%" + searchQuery.replace(" ", "") + "%");
4. 대출 관리 - LoanDAO
대출 기능은 아래 순서로 동작한다. 이미 대출 중인 사용자가 또 대출하거나, 이미 대출 중인 책을 대출하는 예외 케이스를 사전에 검증하도록 설계했다.
- 해당 사용자가 이미 대출 중인지 확인 (return_date IS NULL)
- 대출할 책을 제목으로 조회
- 해당 책의
is_available상태 확인 - Loan 테이블에 대출 이력을 INSERT하고, Book 테이블의
is_available을 0으로 UPDATE
// 1. 해당 사용자가 이미 대출 중인지 확인한다.
String checkUserLoan = "SELECT * FROM loan WHERE user_id=? AND return_date IS NULL;";
pstmt = conn.prepareStatement(checkUserLoan);
pstmt.setInt(1, userId);
rs = pstmt.executeQuery();
if (rs.next()) {
return "-1"; // 이미 대출 중
}
// 2. 대출 이력을 삽입하고, 도서 상태를 대출 불가로 변경한다.
String insertSql = "INSERT INTO loan (book_id, user_id, start_date, end_date, return_date)" +
"VALUES (?, ?, now(), date_add(now(), INTERVAL 7 DAY), null);";
String updateSql = "UPDATE book SET is_available = 0 WHERE book_id = ?;";
5. 예약 관리 - ReservationDAO
예약 기능은 대출과 별개로 동작하며, 예약 취소 시 해당 책에 남은 예약이 없으면 is_available을 다시 1로 업데이트하는 로직을 포함한다.
// 예약 취소 후 잔여 예약이 없으면 도서 상태를 대출 가능으로 변경한다.
String selectReservationsSql =
"SELECT COUNT(*) AS num_reservations FROM Reservation WHERE book_id = ?";
pstmt = conn.prepareStatement(selectReservationsSql);
pstmt.setInt(1, bookId);
rs = pstmt.executeQuery();
if (rs.next()) {
int numReservations = rs.getInt("num_reservations");
if (numReservations == 0) {
String updateBookSql = "UPDATE Book SET is_available = 1 WHERE book_id = ?";
pstmt = conn.prepareStatement(updateBookSql);
pstmt.setInt(1, bookId);
pstmt.executeUpdate();
}
}
6. 싱글톤 패턴 - BookDB, LoanTable
인메모리 데이터를 관리하는 클래스에 싱글톤 패턴을 적용했다. BookDB는 LazyHolder 방식으로 구현하여 멀티스레드 환경에서도 안전하게 인스턴스를 생성한다.
// LazyHolder 방식: 클래스가 로딩될 때 JVM이 스레드 안전하게 초기화를 보장한다.
public class BookDB {
private BookDB() { }
private static class LazyHolder {
public static final BookDB INSTANCE = new BookDB();
}
public static BookDB getInstance() {
return LazyHolder.INSTANCE;
}
}
7. DTO 패턴 - BookDto 상속 구조
도서를 실물 도서(PhysicalBook)와 전자책(EBook)으로 구분하고, 공통 속성은 부모 BookDto에 담고 각각의 추가 필드는 자식 클래스에서 관리하도록 상속 구조를 설계했다.
// 공통 속성을 담는 부모 DTO
public class BookDto {
private String title;
private String author;
private int publicationYear;
private Long ISBN;
private Category category;
private boolean is_available;
}
// 실물 도서: 위치 정보(location) 추가 필드
public class PhysicalBookDto extends BookDto {
private String location;
}
// 전자책: 부모 DTO의 속성만 사용
public class EBookDto extends BookDto {
}
⭐ DB 테이블 구조
코드에서 사용하는 주요 테이블 구조는 아래와 같다.
| 테이블 | 주요 컬럼 |
| User | user_id(PK), name, phone, email, password, registered_at |
| Book | book_id(PK), title, author, publication_year, isbn, is_available, publisher, category |
| Loan | loan_id(PK), book_id(FK), user_id(FK), start_date, end_date, return_date |
| Reservation | reservation_id(PK), book_id(FK), user_id(FK), register_date |
⭐ 어려웠던 점
- 대출과 예약 기능이 모두
is_available컬럼을 공유하기 때문에, 예약 취소 시 단순히 1로 되돌리는 것이 아니라 잔여 예약 수를 먼저 확인하는 로직이 필요했다. - Spring Boot의 의존성 주입(DI) 없이 순수 Java로 객체를 직접 생성하다 보니, 의존 관계가 복잡해질수록 코드가 지저분해지는 문제를 직접 느꼈다. Spring의 필요성을 체감할 수 있었다.
- DB 접속 정보를 코드에 하드코딩하지 않고
config.properties로 분리하고.gitignore에 등록하는 처리를 팀 전체가 통일하는 과정에서 협의가 필요했다.
⭐ 후기
- Spring Boot 없이 순수 Java와 JDBC로 도서관 시스템 전체를 직접 구현하면서, 프레임워크가 자동으로 처리해주는 것들이 얼마나 많은지 다시 한번 실감했다. 기초를 직접 구현해보는 경험이 Spring을 더 잘 이해하는 데 큰 도움이 된다.
- 커밋 컨벤션부터 패키지 구조, DB 설계까지 기능 구현 못지않게 팀원 간의 의사소통과 설계 합의가 중요하다는 점을 다시 느꼈다.
- 싱글톤 패턴, DAO 패턴, DTO 패턴을 교재에서만 보다가 직접 적용해보니 각 패턴이 왜 필요한지 훨씬 명확하게 이해되었다.
이 글은 패스트캠퍼스의 백엔드 개발 캠프에서 공부한 내용을 작성한 것입니다.
'개발일지 > 패스트캠퍼스' 카테고리의 다른 글
| [자료구조/알고리즘] LeetCode Two Sum - 완전탐색, 재귀, BFS, DFS로 풀기 (Java) (1) | 2024.03.26 |
|---|---|
| 패스트캠퍼스 백엔드 개발 부트캠프 8기 Java 과제 PR 트러블슈팅 - Git 이메일 오류, 브랜치 충돌 해결 (0) | 2024.03.19 |
| [Java] GoF 디자인 패턴 정리 - 팩토리 메소드, 추상 팩토리, 빌더, 프로토타입, 싱글턴 (0) | 2024.03.14 |
| 패스트캠퍼스 백엔드 개발 부트캠프 8기 그룹 스터디 - Git 브랜치 전략과 협업 플로우 실습 (0) | 2024.03.14 |
| [후기] 패스트캠퍼스 백엔드 개발 부트캠프 8기 네트워킹 데이 (0) | 2024.03.14 |