본문 바로가기

프로젝트 일지

#7. 싱글 프로젝트 일지 - Book 도메인

- Book 도메인 구조 -

Book 도메인은 도서와 관련된 데이터를 다루는 곳이다.
실질적인 데이터는 .sql로 DB에 직접 넣을 것으로 Controller와 Entity 외에 다른 구조는 없다.

 

1. Controller

A. 책 대여 메서드

도서관 별로 등록된 회원이 도서관에 보관중인 책을 대여하므로 libraryId, bookId, memberId 를 전달받음

- 로직 구성 -
#1. libraryId와 memberId를 가지고, 해당 도서관에 등록된 회원 LibraryMember를 호출.

#2. 호출된 LibraryMember가 null 이라면, Member의 Data가 없다고 예외 처리.
#3. 호출한 LibraryMember 객체가 연체된 기록이 있는지 조회.
#3-1.
최근 연체가 발생했던 날짜와 오늘 날짜를 DAY 단위로 비교하여, Long 타입 변수에 담는다. = 변수 A.
#3-2.
호출한 libraryMember에 있는 페널티 일수와 변수 A를 비교.
#3-3.
기존에 있던 연체 패널티 일수가 변수 A보다 클 경우, 아직 패널티 일수가 남아 있다는 뜻이므로 

도서 대여 요청 예외 처리 + 도서 대여 가능한 날짜를 계산하여 응답으로 전달함.

[도서 대여 가능 날짜 계산]
:
연체 발생 날짜 계산 + 패널티 일수

if (libraryMember.getOverdueDays() != null && libraryMember.getOverdueDays() > 0) {
    LocalDate now = LocalDate.now();

// daysSinceLastOverdue = 지난번 연체 발생한 날짜와 오늘을 DAY 기준으로 비교한 Long 변수.
    Long daysSinceLastOverdue = ChronoUnit.DAYS.between(libraryMember.getLastOverdueDate(), now);
// libraryMember.getOverdueDays = penaltyDays
// = 대여 날짜와 반납 날짜를 계산하여 14일이 넘을 경우 실제 값에서 14를 뺀 Long 변수. 즉 패널티 적용 기간.
    if(daysSinceLastOverdue < libraryMember.getOverdueDays()){
        LocalDate availableDate = libraryMember.getLastOverdueDate().plusDays(libraryMember.getOverdueDays());
        String message = "당신은" + libraryMember.getOverdueDays() 
        + "일의 연체 기록이 있습니다. 이 기간동안은 대여할 수 없습니다." +
               availableDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+
               "부터 대여할 수 있습니다.";
        return new ResponseEntity<>(message, HttpStatus.FORBIDDEN);
    }
}

 

#4. libraryId, bookId를 통해 도서관에 보관중인 책(libraryBook)을 호출.
#5. libraryBook이 찾아지지 않으면 BOOK_NOT_FOUND 예외 처리.
#6.
찾은 libraryBook의 Status가 UNAVAILALBE 상태라면 이미 대여중이라는 의미로 대여 불가 예외 처리

#7. memberId로 Member 회원 조회.
#8. member객체가 가진 memberBook 중 대여 반납 기록이 없는 Data의 count 값을 담는 변수 생성.
#9. 그 횟수가 5회가 넘어가면 개인별 최대 대여 가능 횟수를 가지고 있는 관계로 예외 처리.

int rentalHistoryCount = memberBookRepository.countByMemberAndReturnedAtIsNull(targetMember);

if (rentalHistoryCount >= 5) {
throw new BusinessLogicException(ExceptionCode.MAXIMUM_RENTAL_ALREADY);
}

<Spring Data JPA 자동완성 메서드>
int countByMemberAndReturnedAtIsNull(Member member);

<JPQL 쿼리 작성>
@Query("SELECT COUNT(mb) FROM MemberBook mb 
WHERE mb.member.memberId = :memberId AND mb.returnedAt IS NULL")
int countUnreturnedBooksByMember(@Param("memberId") Long memberId);

Spring Data JPA 자동완성 메서드는 간편

 

#10. libraryBook은 이제 대여된 상태이므로 도서 상태를 UNAVAILABLE로 변경 후 DB 저장.
#11. 대여한 도서인 MemberBook을 새롭게 생성하여
libraryId, member, book, createdAt, dueReturnDate를 set 해주고 DB에 저장.
#12. 응답으로 주기 위해 MemberBookMapper를 사용해 MemberBookDto.Response 타입으로 생성.



B. 책 반납 메서드
대여했던 책을 가진 회원이 도서관에 책을 반납하므로 libraryId,bookId,memberId 를 전달받음


- 로직 구성 -
#1. 반환하고자 하는 것이 주 목적이기 때문에 libraryId, bookId로
LibraryBook 호출하여
불러온 LibraryBook의 상태가 AVAILABLE이면 대여중인 책이 아니므로 예외 처리.
#2.
memberId, bookId로 대여한 책인 MemberBook 을 호출하고 null 이면 대여중이 아니므로 예외 처리.
#3.
호출한 MemberBook의 대여시점과 지금 시점을 비교하여 Long 타입 변수(대여기간) 에 담는다.
#4.
대여 기간, 연체기간을 각 각 Long 타입 변수로생성.
#5.
대여 기간이 14일보다 많으면 연체가 발생하게 되므로
연체 기간 = 대여기간 - 14 으로 계산하여 할당하고, MemberBook 객체에 set해준다.
#6.
대여했던 회원의 정보를 수정하기 위해 libraryId와 memberId로 도서관 소속 회원을 호출한다.
호출되지 않으면 예외 처리.
#7.
호출한 LibraryMember 연체날짜 필드에 #5 에서 할당해둔 연체일수를 set해주고 DB에 저장.
#8.
libraryBook은 반납 되었으므로 status를 UNAVAILABLE 에서 AVAILABLE로 변경 후 DB에 저장.
#9.
memberBook은 반납 되었으므로 필드 returnedAt을 LocalDate.now()로 기록 후 DB에 저장.
#10.
MemberBookMapper를 통해 Response를 생성하고, message로 반납 완료 내용 전달.
만약 연체일수가 발생했다면, 몇일 인지 message에 전달하고, 대여 가능 날짜 또한 message에 전달.

[도서 대여 가능 날짜 계산]:
반납한 날짜(returnedAt) + 연체일수

if (foundMemberBook.getOverdueDays() != null && foundMemberBook.getOverdueDays() >0) {
   response.setOverdueDays(foundMemberBook.getOverdueDays());
   
   String availableDate = response.getReturnedAt()
   .plusDays(response.getOverdueDays()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
   
   response.setMessage("연체가 발생되어 "+response.getOverdueDays()+"일 동안 대여할 수 없습니다."
   + availableDate + " 일부터 대여할 수 있습니다.");
   }

 

C. 책 검색 메서드
도서관에 보관중인 책을 검색하는 목적이므로 libraryId와 bookId를 받는다.

- 로직 구성 - 
#1.
libraryId와 bookId로 DB에서 LibraryBook 객체를 호출한다.
#2. LibraryBookMapper를 사용하여 LibraryBookDto.Response 객체를 생성
#3. 응답으로 response 객체에 url을 셋팅해준다.

 

2. Entity

1. DB에 생성될 Book 객체들의 정보를 필드로 가지고 있음.
2. 연관 관계에 있는 엔티티들을 필드로 설정하는 곳.