[우아한명지코스] 고건 Spring Data JPA 4, 5, 6단계 미션 제출합니다.#227
Conversation
There was a problem hiding this comment.
안녕하세요 건님, 우선 미션 진행하느라 수고하셨습니다.
리뷰를 어떻게 남길까 고민하다가, 그냥 보이는거 다 달았습니다ㅎㅎ
질문에 대한 답변
요구사항에서 주어진 예시를 그대로 수용하기보다, 필요에 따라 타입이나 구조 등을 바꾸었습니다. 예를 들어 TimeId를 문자열로 보내는 예시를 숫자 타입으로 바꾸어 적용했습니다.
좋네요ㅎㅎ 추가로 필요하다면 요구사항이나 프론트 코드도 바꿔보는것도 괜찮습니다.
findByMember의 책임
/reservations-mine은 예약 확정 목록과 대기 목록을 합쳐서 반환합니다. 이를 ReservationService.findByMember()하나에서 처리했는데, 서비스가 WaitingRepository까지 의존하게 되는 구조입니다. 서비스를 분리할지, 아니면 조회 전용 서비스/파사드를 둘지 고민되었습니다.
코멘트에 달아놨지만 지금 코드도 괜찮고, 저는 개인적으로 예약 조회랑 대기 조회를 별도의 API로 분리할 것 같네요.
대기 순위 계산 방식
예시처럼 Waiting 클래스에 멤버 변수로 rank를 두어 DB에 저장하지 않고,. count 쿼리로 동적 계산하는 방식을 선택했습니다. 대기 취소 발생 시 나머지 순위를 별도로 계산하지 않는 장점이 있다고 생각합니다. 하지만 조회마다 추가 쿼리가 나가게 되는 트레이드오프가 있어 어떤 것이 더욱 효율적인 방식일지 고민했습니다.
좋은 고민이에요. 판단을 할 때, 대기열의 업데이트 빈도가 자주 일어날까? 라는 서비스 특성을 참고해 고민해보는것도 좋아보입니다.
추가로 코멘트를 남길텐데, 이 코멘트에 담겨있는걸 우선적으로 학습해보시면 좋을 것 같아요.
- 서비스가 정상적으로 동작을 안 합니다.
뭐 여러가지 이유가 있는 것 같은데, 모든 기능이 정상적으로 동작하도록 수정해주세요.
- schema 중복 로딩 문제, jwt secret문제는 제가 수정해서 띄운 상태입니다. 그 외의 문제들을 수정해주세요.
- 양방향 참조 및 Cascade Option에 대해 학습해봅시다.
- OSIV의 개념에 대해서도 학습해주세요.
(하단의 코멘트는 넘어가셔도 됩니다. 오히려 더 헷갈리게 만들수도 있을 것 같아요. 추가로 학습을 하고싶다면 코멘트를 공부 후에 코멘트를 달아주시고, 아니면 그냥 넘어가도 됩니다.
(JPA에 대해 아직 학습이 부족하다면 넘어가는걸 추천드립니다.))
4) 직접 참조 (@manytoone, @onetomany) 등등은 장점만 있을까요?
현 상황에서는, 관리자가 Theme의 이름을 수정하면 예약 조회 시, 모두 변경된 테마 이름을 확인하겠네요. 비즈니스 요구사항마다 다르지만 사용자 입장에서 생각해봤을 때, '여행' 이라는 테마를 조회했는데, 테마 이름이 '신나는 모험' 으로 변경되어있다면, 혼란이 올 수도 있을 것 같아요. 이런 문제는 어떻게 해결할 수 있을까요?
5) 외래키 제약조건을 사용했을 때, 어떤 장단점이 있을까요? 외래키 제약조건은 필수적인가요?
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) |
There was a problem hiding this comment.
@GenerateValue 전략에는 어떤게 있나요? IDENTITY를 선택한 이유가 있나요??
| this.role = role; | ||
| } | ||
|
|
||
| public Member() { |
There was a problem hiding this comment.
public인 이유가 있을까요? protected 혹은 private은 불가능한가요?? private이 불가능하다면 왜 불가능할까요??
| @OneToMany(mappedBy = "member") | ||
| private Set<Reservation> reservations = new HashSet<>(); | ||
|
|
||
| public Member(Long id, String name, String email, Role role) { |
| loginRequest); | ||
| LoginResponse loginResponse = authService.createToken(memberAuthInfo); | ||
|
|
||
| Cookie cookie = new Cookie("token", loginResponse.accessToken()); |
There was a problem hiding this comment.
쿠키에 관한 세부 로직은 별도의 객체에서 관리해도 좋을 것 같아요.
| ); | ||
| } | ||
| } | ||
| //package roomescape.member; |
There was a problem hiding this comment.
혹시 추후에 다시 사용하는 코드인가요?? 주석을 달아서 의도를 표현해도 좋을 것 같고, 그런게 아니라면 삭제하는것도 좋아보입니다.
| } | ||
|
|
||
| public void deleteWaitingById(Long id, Member member) { | ||
| Waiting waiting = waitingRepository.findById(id) |
There was a problem hiding this comment.
이 부분은 생각이 갈릴수도 있는데, 예외처리가 필요한지 궁금해요.
삭제 요청은 멱등하니까, 굳이 예외 메시지를 던져야하나?? 라는 생각이 개인적으로는 듭니다. 이 부분에 대해서 이야기 나눠볼까요?? 건님의 의견을 말해주세요ㅎㅎ
|
|
||
| public void deleteById(Long id) { | ||
| timeDao.deleteById(id); | ||
| timeRepository.deleteById(id); |
There was a problem hiding this comment.
이 부분은 선 검증을 안 하네요?? Waiting 삭제 코드를 봤을 때는 검증을 했는데, 컨벤션 통일이 필요해보여요.
| name VARCHAR(255) NOT NULL, | ||
| time_id BIGINT, | ||
| theme_id BIGINT, | ||
| id BIGINT NOT NULL AUTO_INCREMENT, |
There was a problem hiding this comment.
properties에는 ddl-auto: create-drop 으로 설정되어 있고, schema.sql도 존재해서 부팅하면 오류가 발생하네요. 확인 부탁드려요.
| return getValue(); | ||
| } | ||
|
|
||
| public Set<Reservation> getReservations() { |
There was a problem hiding this comment.
이거는 사용하는 메서드인가요?? reservations를 통째로 넘기는건, 의도치않은 변경에 위험해보이긴 합니다.
|
|
||
| private String description; | ||
|
|
||
| @OneToMany(mappedBy = "theme") |
There was a problem hiding this comment.
- 양방향 연관관계를 사용하신 이유가 궁금해요.
- 현재 테마 조회 시 에러 발생합니다.
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)
실제로 코드 실행 후, 어떤 오류가 발생했는지 파악해주시고, 원인 + 해결방안을 자세히 설명해주세요. 그리고 그 과정에서 추가로 어떤 내용을 학습했는지도 서술해주세요.
안녕하세요. 먼저 이번 리뷰 일정보다 많이 늦어지게 되어서 죄송합니다 .. 다음 미션은 기한에 맞추어 성실히 제출하겠습니다!!
개발하면서 고민한 사항 정리해보겠습니다.
요구사항에서 주어진 예시를 그대로 수용하기보다, 필요에 따라 타입이나 구조 등을 바꾸었습니다. 예를 들어 TimeId를 문자열로 보내는 예시를 숫자 타입으로 바꾸어 적용했습니다.
findByMember의 책임
/reservations-mine은 예약 확정 목록과 대기 목록을 합쳐서 반환합니다. 이를 ReservationService.findByMember()하나에서 처리했는데, 서비스가 WaitingRepository까지 의존하게 되는 구조입니다. 서비스를 분리할지, 아니면 조회 전용 서비스/파사드를 둘지 고민되었습니다.
대기 순위 계산 방식
예시처럼 Waiting 클래스에 멤버 변수로 rank를 두어 DB에 저장하지 않고,. count 쿼리로 동적 계산하는 방식을 선택했습니다. 대기 취소 발생 시 나머지 순위를 별도로 계산하지 않는 장점이 있다고 생각합니다. 하지만 조회마다 추가 쿼리가 나가게 되는 트레이드오프가 있어 어떤 것이 더욱 효율적인 방식일지 고민했습니다.