-
Notifications
You must be signed in to change notification settings - Fork 8
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] 변경된 ERD 설계에 따른 전체 코드 리팩토링 #72
Conversation
Test Results39 tests 39 ✅ 4s ⏱️ Results for commit 3514227. ♻️ 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.
재즈 로직 구현하느라 고민 많이 하신게 느껴지네요😂
몇 가지 코멘트 남겼습니다! 확인 부탁드려요
import static org.assertj.core.api.Assertions.assertThatCode; | ||
import static org.assertj.core.api.Assertions.assertThatCode; |
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.
import문이 중복되었네요!
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.
conflict 해결할 때 놓친 것 같은데 확인을 못했네요. 😅
제거했습니다.
@Autowired | ||
private AttendeeRepository attendeeRepository; |
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.
attendeeRepository는 메서드 어디에도 사용하지 않고 있는데요!
필드로 선언하신 이유가 궁금합니다!
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.
아마 conflict 해결 과정에서 import문 정렬이 안되있어 보이네요!
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.
재즈 정말 고생 많으셨어요~😊
리뷰 몇 가지 남깁니다!!
backend/src/main/java/com/woowacourse/momo/domain/schedule/Schedule.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/com/woowacourse/momo/domain/timeslot/TimeslotInterval.java
Outdated
Show resolved
Hide resolved
Timeslot timeslot = Timeslot.from(localTime); | ||
|
||
assertThat(timeslot.getLocalTime()).isEqualTo(localTime); | ||
} |
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.
같은 timeslot을 등록해도 TimeslotInterval이 생성되는 것을 검증하는 테스트가 있으면 좋을 것 같아요!
@Test
public void validateIntervalTimeRange() {
assertThatCode(() -> new TimeslotInterval(Timeslot.TIME_1000, Timeslot.TIME_1000))
.doesNotThrowAnyException();
}
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.
추가했습니다.
@EqualsAndHashCode(of = {"startTimeslot", "endTimeslot"}) | ||
@AllArgsConstructor | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class TimeslotInterval { |
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.
이 클래스는 Meeting의 가능시간과 끝 시간을 따로 분리해 둔 클래스로 이해했어요.
그런데 timeslot 도메인 쪽에 있으니, Meeting과 연관되어 있다는 것을 직관적으로 알기 어려워요..!
TimeslotInterval은 단순히 임베디드 타입이니 이 클래스를 Meeting에 넣어주는 것이 어떨까요?
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.
네 좋습니다~!
@Transactional | ||
public String create(MeetingCreateRequest request) { | ||
Meeting meeting = saveMeeting(request.meetingName(), request.meetingStartTime(), request.meetingEndTime()); | ||
saveAvailableDates(request.meetingAvailableDates(), meeting); | ||
saveAttendee(meeting, request.hostName(), request.hostPassword(), Role.HOST); | ||
saveAttendee(meeting, request.hostName(), request.hostPassword()); |
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.
saveAttendee
라는 네이밍보다, 호스트 역할을 가진 Attendee
를 저장하는 것이니까, 그 메서드 명을 saveHost
정도로 수정하면 어떨까요?
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.
좋네요 👍 수정할게요!
void setUp() { | ||
RestAssured.port = port; | ||
|
||
meeting = meetingRepository.save(MeetingFixture.COFFEE.create()); | ||
attendeeRepository.save(AttendeeFixture.HOST_JAZZ.create(meeting)); | ||
meeting = meetingRepository.save(MeetingFixture.DINNER.create()); | ||
List<AvailableDate> availableDates = List.of( | ||
availableDateRepository.save(new AvailableDate(LocalDate.now(), meeting)), | ||
availableDateRepository.save(new AvailableDate(LocalDate.now().plusDays(1), meeting)) | ||
); | ||
dates = availableDates.stream() | ||
.map(AvailableDate::getDate) | ||
.map(LocalDate::toString) | ||
.toList(); | ||
} |
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.
@beforeEach
이 선언된 setUp
메서드에서 데이터를 미리 저장하면 각 테스트가 setUp
메서드에 종속 됩니다.
이러면 나중에 테스트를 짤 때마다 setUp
메서드를 고려해야 하는 번거로움이 있을 것 같아요.
setUp
에서는 port 초기화만 진행하고 데이터 생성이나 초기화는 각 테스트에서 하면 어떨까요?
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.
저도 동의합니다 👍
@DisplayName("UUID로 약속 정보를 조회하는데 성공하면 200 상태 코드를 응답한다.") | ||
@Test | ||
void find() { | ||
Response response = RestAssured.given().log().all() | ||
.contentType(ContentType.JSON) | ||
.when().get("/api/v1/meeting/{uuid}", meeting.getUuid()); | ||
|
||
String meetingName = response.jsonPath().getString("data.meetingName"); | ||
String firstTime = response.jsonPath().getString("data.firstTime"); | ||
String lastTime = response.jsonPath().getString("data.lastTime"); | ||
List<String> availableDatesList = response.jsonPath().getList("data.availableDates", String.class); | ||
|
||
assertSoftly(softAssertions -> { | ||
softAssertions.assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); | ||
softAssertions.assertThat(meetingName).isEqualTo(meeting.getName()); | ||
softAssertions.assertThat(firstTime).isEqualTo(meeting.startTimeslotTime().toString()); | ||
softAssertions.assertThat(lastTime).isEqualTo(meeting.endTimeslotTime().toString()); | ||
softAssertions.assertThat(availableDatesList).containsExactlyElementsOf(dates); | ||
}); | ||
} |
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.
@DisplayName("UUID로 약속 정보를 조회하는데 성공하면 200 상태 코드를 응답한다.") | |
@Test | |
void find() { | |
Response response = RestAssured.given().log().all() | |
.contentType(ContentType.JSON) | |
.when().get("/api/v1/meeting/{uuid}", meeting.getUuid()); | |
String meetingName = response.jsonPath().getString("data.meetingName"); | |
String firstTime = response.jsonPath().getString("data.firstTime"); | |
String lastTime = response.jsonPath().getString("data.lastTime"); | |
List<String> availableDatesList = response.jsonPath().getList("data.availableDates", String.class); | |
assertSoftly(softAssertions -> { | |
softAssertions.assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); | |
softAssertions.assertThat(meetingName).isEqualTo(meeting.getName()); | |
softAssertions.assertThat(firstTime).isEqualTo(meeting.startTimeslotTime().toString()); | |
softAssertions.assertThat(lastTime).isEqualTo(meeting.endTimeslotTime().toString()); | |
softAssertions.assertThat(availableDatesList).containsExactlyElementsOf(dates); | |
}); | |
} | |
@DisplayName("UUID로 약속 정보를 조회하는데 성공하면 200 상태 코드를 응답한다.") | |
@Test | |
void find() { | |
RestAssured.given().log().all() | |
.contentType(ContentType.JSON) | |
.when().get("/api/v1/meeting/{uuid}", meeting.getUuid()) | |
.then().log().all() | |
.statusCode(HttpStatus.OK.value()) | |
.body( | |
"data.meetingName", equalTo(meeting.getName()), | |
"data.firstTime", equalTo(meeting.startTimeslotTime().toString()), | |
"data.lastTime", equalTo(meeting.endTimeslotTime().toString()), | |
"data.availableDates", equalTo(dates) | |
); | |
} |
이렇게 RestAssured만을 사용해서 응답의 데이터를 검증할 수 있어요!
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.
저는 API 호출과 검증 컨텍스트를 분리해서 인수 테스트를 작성하는 걸 선호합니다.
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 validateTimeRange(LocalTime firstTime, LocalTime lastTime) { | ||
if (firstTime.isAfter(lastTime)) { | ||
throw new MomoException(MeetingErrorCode.INVALID_TIME_RANGE); | ||
} | ||
} |
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.
Meeting의 endTime을 클라이언트에 반환할 때 30분을 더해줘야하는 부분들을 확인해봤어요. 클라이언트와 endTime 데이터가 오갈 때 문제가 있을 것 같아요. 그 부분들에 대해 리뷰 남김니다!
availableDates, | ||
schedules | ||
meeting.startTimeslotTime(), | ||
meeting.endTimeslotTime(), |
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.
endTimeslot을 저장할 때 줄여둔 30분을 클라이언트에 반환할 때는 어디선가 30분을 더해줘야해요~
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.
약속 개최자가 시간을 선택하는 부분에 있어 시간 24:00
이 LocalTime
타입으로 파싱되면 문제가 발생하네요.
이 부분은 프론트엔드와 다시 한 번 같이 얘기해봐야할 것 같아요
softAssertions.assertAll(); | ||
SoftAssertions.assertSoftly(softAssertions -> { | ||
softAssertions.assertThat(response.firstTime()).isEqualTo(meeting.startTimeslotTime()); | ||
softAssertions.assertThat(response.lastTime()).isEqualTo(meeting.endTimeslotTime()); |
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.
response.lastTime() 은 meeting.endTimeslotTime 보다 30분 많아야해요~!
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.
이 부분도 프론트엔드와 같이 얘기해보면 좋겠어요.
softAssertions.assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); | ||
softAssertions.assertThat(meetingName).isEqualTo(meeting.getName()); | ||
softAssertions.assertThat(firstTime).isEqualTo(meeting.startTimeslotTime().toString()); | ||
softAssertions.assertThat(lastTime).isEqualTo(meeting.endTimeslotTime().toString()); |
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.
여기도 response.lastTIme 이 meeting.endTimeslotTime 보다 30분 많도록 검증하면 좋을 것 같아요~~
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.
고생 많으셨습니다! 이슈는 30번대인데 PR은 70번대네요ㅋㅋㅋㅋ
변경해야 할 부분들은 다른 분들이 잘 남겨 주셔서, 변경점 중 궁금한 부분들 코멘트 남겨 두었습니다. 확인 부탁드릴게요.
현재는 전반적으로 단위 시간 연산 시 30분
이라는 값을 상수로 활용하지 못하고 값으로 전달하고 있는데요, 나중에 Timeslot
도메인을 단순한 enum으로 정의하는 대신 ''한 슬롯의 크기'만을 상수로 정의해 두고 이에 따라 애플리케이션 실행 시 동적으로 타임슬롯들을 정의할 수 있다면 훨씬 관리하기 편할 것 같아요.
상세 구현을 생각해 보지는 않았지만 Timeslot
은 현재 도메인에서 '시간을 연산하기 위한 단위'로 기능하므로 그 자체로 슬롯의 첫 시각과 끝 시각을 관리할 수 있으면 이상적이겠네요! 같이 한 번 고민해 봅시다🔥
@@ -19,7 +19,7 @@ public class AttendeeController { | |||
|
|||
@PostMapping("/api/v1/login/{uuid}") | |||
public MomoApiResponse<TokenResponse> login( | |||
@PathVariable String uuid, @Valid @RequestBody AttendeeLoginRequest request | |||
@PathVariable String uuid, @RequestBody @Valid AttendeeLoginRequest request |
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.
누락된 어노테이션을 잘 짚어 주셨군요ㅎㅎ
.toList(); | ||
} | ||
|
||
private List<Schedule> createSchedulesForDate( |
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.
어차피 호출부에서 스트림으로 변환하는데createSchedulesForDate()
에서 스트림을 바로 리턴하지 않고 리스트로 변환하는 이유가 있을까요?
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.
좋네요~ 제가 놓친 부분인 것 같은데 무슨 의도로 질문하셨는지 이해했습니다.
createSchedulesForDate()
에서 Stream<>
을 반환하도록 변경했어요.
AvailableDates availableDates = new AvailableDates(availableDateRepository.findAllByMeeting(meeting)); | ||
return request.dateTimes().stream() | ||
.map(dateTimeRequest -> createSchedulesForDate(meeting, attendee, availableDates, dateTimeRequest)) | ||
.flatMap(List::stream) |
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.
map()
후 flatMap()
을 적용하는 것 보다 flatMap()
하나로 해결할 수 있지 않을까요?
@@ -5,5 +5,5 @@ | |||
import java.time.LocalTime; | |||
import java.util.List; | |||
|
|||
public record DateTimesCreateRequest(@NotNull LocalDate date, @NotNull List<LocalTime> times) { | |||
public record DateWithTimesRequest(@NotNull LocalDate date, @NotNull List<LocalTime> times) { |
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.
Create 라는 단어를 클래스명에서 제거하셨네요. 나중에 DTO들이 많아지면 어떤 요청인지 헷갈릴 수도 있지 않을까요?
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.
지금 다시 보니 이전 이름이 의미가 더 명확하네요 다시 변경하겠습니다.
@@ -7,6 +7,9 @@ spring: | |||
hibernate: | |||
format_sql: true | |||
show_sql: true | |||
sql: | |||
init: | |||
mode: never |
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.
제가 코멘트 드렸던 부분들은 모두 반영된 것 같아 Approve 합니다!
고생하셨어요~
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.
많은 양의 코드를 리팩토링하느라 고생이 많았어요 째즈!
회의를 통해 어떤식으로 endTime 데이터를 다룰지 정해봐야겠네요. 수고했어요👍
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.
많은 리뷰 내용 반영하시느라 고생 많으셨어요~👍👍
테스트의 setUp함수와 관련해서 말씀 드렸던 내용이 아직 반영 안된 곳이 있어서 반영 부탁드릴게요!
meeting = meetingRepository.save(MeetingFixture.MOVIE.create()); | ||
attendee = attendeeRepository.save(new Attendee(meeting, "name", "password", Role.GUEST)); | ||
today = availableDateRepository.save(new AvailableDate(LocalDate.now(), meeting)); | ||
tomorrow = availableDateRepository.save(new AvailableDate(LocalDate.now().plusDays(1), meeting)); | ||
|
||
List<LocalTime> times = List.of(Timeslot.TIME_0100.getLocalTime(), Timeslot.TIME_0130.getLocalTime()); | ||
|
||
dateWithTimes = new ArrayList<>(List.of( | ||
new DateTimesCreateRequest(today.getDate(), times), | ||
new DateTimesCreateRequest(tomorrow.getDate(), times) | ||
)); |
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.
@beforeEach
가 선언된 setUp 메서드에서 데이터를 미리 저장하면 각 테스트가 setUp 메서드에 종속 됩니다.
이러면 나중에 테스트를 짤 때마다 setUp 메서드를 고려해야 하는 번거로움이 있을 것 같아요.
setUp에서는 port 초기화만 진행하고 데이터 생성이나 초기화는 각 테스트에서 하면 어떨까요?
이 부분도 수정이 필요해 보입니다..!
마찬가지 이유로 AvailableDateTest의 setUp 함수도 수정이 필요해 보여요.
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.
테스트 클래스 전체 메서드에 공통적으로 필요한 셋업 데이터는 일부러 제거를 안했어요.
Schedule
의 경우 테스트에 필요한 Meeting
, Attendee
, AvailableDate
가 공통적으로 모두 필요한 상황인데,
동일한 데이터를 모두 given절에 매번 정의하는게 오히려 더 번거로운 행동이라 생각합니다.
각 테스트 메서드를 봤을 때, 무엇을 테스트하는지 차이를 확인할 수 있도록 하는게 가장 중요하다고 생각해요.
bae3df5
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.
고생 많으셨어요~!! 🎉
648cb7e
to
3514227
Compare
관련 이슈
작업 내용
Meeting
,Schedule
특이 사항
리뷰 요구사항 (선택)