-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BE] 쓴 피드백은 피드백 모아보기에서 바로 볼 수 있도록 변경 + 리팩터링(#613) #617
Conversation
Test Results 55 files 55 suites 8s ⏱️ Results for commit 34540ff. ♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로직 커밋 바로가기 해줘서 고마워요 ㅎ.ㅎ
File Change 는 많았지만 reader, writer 분리와 dto 네이밍 수정때문인지라 잘 되었는지 확인을 주로 했습니다.
고생 많았어요!
질문만 코멘트로 달고 로직에 문제 없는 것 같아 approve 드립니당!
아래는 P2 에 대한 생각과 추가 질문입니다.
질문
서비스-도메인 계층에서의 DTO 네이밍을 바꾼건 좋은 관점인 것 같아요.
Input, Output 이라는 네이밍을 사용하게 된 이유가 궁금해요.
콘솔 혹은 DB와 소통할 거라고 오해할 수 있는 네이밍으로도 느껴지긴 하네요.
DTO 변환 책임에 대해서
결국 Mapper 라는, 레이어와 레이어를 이어주는 친구가 생겨야 하는건 어쩔수 없겠지만
Mapper 로 DTO 간 의존성을 끊는 것이 좋은 것 같아요.
DTO 끼리 의존성이 생겼을 때, 기능이 확장됨에 따라 DTO 가 꼭 일대일로만 의존성이 생기지 않을 것 같아 걱정이 되는 것 같아요.
한 DTO 에서 여러 DTO 로 변환하게 되면 유지보수 관점에서 더 불편해 질거라는 생각이 들어요.
그렇다고 Service 계층에서 직접 DTO 를 변환하는건,,,
저는 Mapper 사용이 위 방법중엔 괜찮은 선택인 것 같습니다.
Mapper 를 분석하면서 DTO 의 변환을 통해 로직을 유추할 수 있는 것도 장점이 될 수 있을 것 같고요.
private UserFeedbackResponse getUserFeedbackResponse(Map<Long, List<FeedbackResponse>> developFeedback, Map<Long, List<FeedbackResponse>> socialFeedback) { | ||
List<Room> rooms = roomRepository.findAllById( | ||
extractDistinctKeyStreams(developFeedback, socialFeedback).toList()); | ||
private UserFeedbackResponse getUserFeedbackResponse(Map<Long, List<FeedbackResponse>> developFeedback, Map<Long, List<FeedbackResponse>> socialFeedback, Predicate<Room> predicate) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
predicate 가 이름을 가지면 좋겠는데, 적절한 이름이 떠오르지 않네요..
extractDistinctKeyStreams(developFeedback, socialFeedback).toList()); | ||
private UserFeedbackResponse getUserFeedbackResponse(Map<Long, List<FeedbackResponse>> developFeedback, Map<Long, List<FeedbackResponse>> socialFeedback, Predicate<Room> predicate) { | ||
List<Long> roomIds = extractDistinctKeyStreams(developFeedback, socialFeedback).toList(); | ||
List<Room> rooms = roomRepository.findAllById(roomIds); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
findAllByIds 와 findAllById 가 동작이 같은가요? 궁금해서 질문 남깁니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
||
assertThat(response.feedbacks()).hasSize(2); | ||
assertThat(response.feedbacks().get(0).developFeedback()).hasSize(1); | ||
assertThat(response.feedbacks().get(0).socialFeedback()).hasSize(1); | ||
assertThat(response.feedbacks().get(1).developFeedback()).hasSize(1); | ||
} | ||
|
||
@Test | ||
@DisplayName("자신이 받은 피드백을 받을 때는 닫혀있는 방에서만 받는다.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"자신이 받은 피드백은 닫혀있는 방에서 작성된 피드백만 확인할 수 있다" 와 같은 수정은 어떠신가요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
수정 완😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reader와 Writer의 역할을 분리하면서 RequestDto와 ResponseDto를 어디까지 전달할지 고민했고,
결국 Service 레이어를 넘어가지 않도록 하는 게 좋겠다고 판단했습니다.
...
해당 내용은 느낀점으론
DTO 를 통해 바로 만들 수 있는 도메인 이 있는게 아닌 이상 어쩔수 없는거 같아요.
( Entity - Domain 이 분리 되어야 한다는 뜻 )
지금 처럼 Service 단 DTO 가 생길때만 이렇게 해도 괜찮을 거 같아요.
코드 짠다구 고생했슴당 🙂
} | ||
|
||
public DevelopFeedback findDevelopFeedback(long roomId, long deliverId, String username) { | ||
return developFeedbackRepository.findByRoomIdAndDeliverIdAndReceiverUsername(roomId, deliverId, username) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
나중에 근본적으로 HTTP API 가 username 이 아닌 receiverId 를 받는것도 생각해봐야 겠네요
( 인덱스 지정 및 명확하게 하려면 )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
username 이 아닌 receiverId 를 받는것도 생각해봐야 겠네요
( 인덱스 지정 및 명확하게 하려면 )
👍 동의요~!
근데 memberId를 외부로 노출시키지 않기 위해 username으로 받은 설계를 했던 것 같은데, 그럼 이런 느낌으로 되는게 맞겠죠?
public class DevelopFeedbackService {
public DevelopFeedbackResponse findDevelopFeedback(long roomId, long deliverId, String username) {
// 여기서 username을 통해 memberId를 찾는다.
long receiverId = ...;
DevelopFeedback developFeedback = developFeedbackReader.findDevelopFeedback(roomId, deliverId, receiverId);
return DevelopFeedbackResponse.from(developFeedback);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
뽀로로 의견이 좋은 것 같습니다용 👍
.stream() | ||
.map(FeedbackResponse::fromDeliver) | ||
.collect(Collectors.groupingBy(FeedbackResponse::roomId)); | ||
return getUserFeedbackResponse(developFeedbackOutput, socialFeedbackOutput, room -> true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
해당 부분은 뭔가 뭔가긴 한대 어쩔수 없는듯요 🥲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reader 클래스에서 FeedbackResponse를 반환하도록 한다면 좀 편해지긴 할건데... 저 부분에선 그냥 Response 반환하자라는 유혹이 넘치더라고요ㅋㅋㅋ
방법은 더 고민해봅시당~ 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Room::isNotOpened
는 어떠신가영?
아래 Room::isClosed
조건이랑도 매치되는 것 같구 🤔
피드백 단계면 isNotOpened
반환값도 어차피 무조건 true일 것 같아서요.
물론 FAIL
status도 통과되기는 하겠지만,
지금 로직도 OPEN
이나 FAIL
단계 모두 포함하는 건 마찬가지라고 생각해용.
물론 정 FAIL
인 경우가 포함되는 게 거슬린다면 isProgressOrClosed
같은 boolean
메서드를 RoomStatus
에 하나 추가해도 될 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Room::isNotOpened는 어떠신가영?
생각지 못한 부분인데 좋네요~
수정 완료했습니다! 👍
fcd4592
to
bcbec54
Compare
이전에 조이썬이 Input, Output이란 네이밍으로 사용해본 경험이 있다고 했고, 저도 좋은 것 같아 사용하게 되었습니다. 네이밍은 서로 맞춰보면 되고, 바꾸고 쉬우니 더 나은 네이밍이 있다면 말해주시면 됩니당~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 뽀로로~~ 리뷰가 늦어져서 죄송합니다! 😂
역시 깔끔하게 잘 작성된 것 같네요! 👍👍
수정된 점 확인했고, 간단한 제안 코멘트 몇 가지만 남겨두었습니다.
P2
저도 좋은 것 같습니다!
같은 DTO를 여기저기서 돌려쓰다보니 필요 없는 파라미터까지 전달되는 경우도 많고,
정확한 용례를 판단하기 힘들다고 느꼈던 경우도 많았거든요.
그래서 저도 RoomResponse
쪽 칼질을 진행하고 있었는데
뽀로로와 같은 방식으로 mapper 한 번 활용해보겠습니다. 짱~!
} | ||
|
||
public DevelopFeedback findDevelopFeedback(long roomId, long deliverId, String username) { | ||
return developFeedbackRepository.findByRoomIdAndDeliverIdAndReceiverUsername(roomId, deliverId, username) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
뽀로로 의견이 좋은 것 같습니다용 👍
|
||
private void validateUpdateAuthority(DevelopFeedback developFeedback, long deliverId) { | ||
if (developFeedback.isNotMatchingDeliver(deliverId)) { | ||
throw new CoreaException(ExceptionType.FEEDBACK_UPDATE_AUTHORIZATION_ERROR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
세부 예외 처리 👍
.stream() | ||
.map(FeedbackResponse::fromDeliver) | ||
.collect(Collectors.groupingBy(FeedbackResponse::roomId)); | ||
return getUserFeedbackResponse(developFeedbackOutput, socialFeedbackOutput, room -> true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Room::isNotOpened
는 어떠신가영?
아래 Room::isClosed
조건이랑도 매치되는 것 같구 🤔
피드백 단계면 isNotOpened
반환값도 어차피 무조건 true일 것 같아서요.
물론 FAIL
status도 통과되기는 하겠지만,
지금 로직도 OPEN
이나 FAIL
단계 모두 포함하는 건 마찬가지라고 생각해용.
물론 정 FAIL
인 경우가 포함되는 게 거슬린다면 isProgressOrClosed
같은 boolean
메서드를 RoomStatus
에 하나 추가해도 될 것 같습니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
만약 위에서 room -> true
를 isXXX
로 바꾼다면 여기 "자신이 작성한 피드백을 받는다"
테스트 메서드 로직도 바뀌어야 합니다!
만약 비즈니스 로직을 바꾸지 않는다고 해도 현재는 OPEN
상태인 방을 테스트하고 있는 것 같아서,
CLOSED
나 PROGRESS
인 방을 기준으로 테스트하도록 바꾸어보아도 좋겠네요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
같이 수정완료했습니다~ 👍
@@ -43,7 +43,7 @@ class UserFeedbackServiceTest { | |||
private SocialFeedbackRepository socialFeedbackRepository; | |||
|
|||
@Test | |||
@DisplayName("닫힌 방마다 작성한 피드백들을 구분해서 가져온다.") | |||
@DisplayName("작성한 피드백들을 가져온다.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기도 마찬가지로 room3
의 RoomStatus
확인해주시면 좋을 것 같아요! 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
같이 수정완료했습니다~ 👍
📌 관련 이슈
✨ PR 세부 내용
쓴 피드백은 피드백 모아보기에서 바로 볼 수 있도록 수정하였습니다.
그리고 피드백 서비스 쪽에 대한 리팩터링 범위가 그리 크지 않을줄 알고 같이 작업을 했었는데,
생각보다 범위가 커서 일단 현재 작업한 부분까지만 PR 제출하고 나머지 피드백 서비스쪽 리팩터링 이어서 해볼게요.
현재 파일 변경 범위가 넓어 "쓴 피드백은 피드백 모아보기에서 바로 보기 기능"에 대한 변경 사항만 보고 싶으시다면
쓴 피드백 조회 기능 수정 여기를 통해 봐주세요.
P2: DTO 관리
Reader와 Writer의 역할을 분리하면서 RequestDto와 ResponseDto를 어디까지 전달할지 고민했고,
결국 Service 레이어를 넘어가지 않도록 하는 게 좋겠다고 판단했습니다.
그래서 어제 조이썬과 논의한 결과, Service와 Domain 간의 데이터 전송을 위해
RequestDto는 InputDto로, ResponseDto는 OutputDto로 변환하기로 했습니다.
그래서 각 DTO를 변환하는 작업이 필요했는데, 제가 구현한 방식은, 각 레이어의 DTO가 다른 레이어의 DTO에 의존하지 않도록
Mapper 클래스를 만들어 변환 작업을 관리하도록 했습니다.
이런 구조를 통해 특정 레이어의 DTO가 다른 레이어의 DTO에 의존하지 않도록 분리하려는 것이 목적이였습니다. 만약 서로 다른 레이어의 DTO가 의존하게 된다면, 작업은 편리해질 수 있지만, 레이어 간 의존성이 생겨 복잡도가 높아질 수 있다고 생각했습니다.
하지만 애기 나눠보고 싶은 점은 이런 방식이 유지보수와 확장성 측면에서 정말 최선인지 궁금합니다.
혹은 DTO 간 어느 정도의 의존성을 허용하여 개발 편의성을 높이는 방안도 고려할 수 있을 것 같은데, 다들 어떻게 생각하시나요?
Mapper 클래스를 이용한 관리 방식이 좋은지, 아니면 다른 대안이 있을지 자유롭게 의견을 나눠주시면 좋겠습니다!