Skip to content
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

[1 - 2단계 방탈출 예약 대기] 이든(최승준) 미션 제출합니다. #86

Merged
merged 31 commits into from
May 18, 2024

Conversation

PgmJun
Copy link

@PgmJun PgmJun commented May 16, 2024

안녕하세요 조앤! 🙂
우테코 첫 번째 미션에서 뵈었는데 다시 만나게 되었네요 :)

이번 미션에서 아직 완전히 최적화되지 않아 아쉬운 코드가 몇몇 존재하긴 하지만 그래도 페어와 고민하면서 열심히 구현해보았습니다.
리뷰 잘 부탁드리겠습니다!

리뷰는 여기를 통해서 진행해주시면 될 것 같습니다.

궁금한 점

Entity 객체의 필드값 사용되는 원시타입을 VO로 포장해주는 것에 대해 어떻게 생각하시나요?

조앤은 이 부분에 대해 실무에서 어떻게 하시나요? 개인적으로 궁금한 부분입니다 ㅎㅎ

저는 VO로 포장하는 경우, 검증 로직 책임 분리로 인해 Entity 객체의 코드가 짧아지고
예외 처리(예외 메시지, 예외 객체) 세분화가 가능해져서 괜찮다고 생각합니다.
조앤은 이 부분에 대해서 어떻게 생각하시나요?

참고로 말씀은 이렇게 드렸지만, SpringJDBC -> JPA로 마이그레이션 하는 과정에서 컴파일 에러가 많이 발생해서
리뷰요청을 보내는 현 시점에는 원시타입으로 관리하고 있는 상태입니다.

Lombok을 사용하는 것에 대해서 어떻게 생각하시나요?

로깅을 위해 toString()을 보통 재정의 해주는 편인데요!
미션 요구사항이 계속 변경되면서 필드에 변경이 종종 생기고 있어서 그때그때 toString()을 재정의해주는 것을 까먹기도 합니다.
또한 JPA를 사용하면서 기본 생성자를 Entity 객체에 항상 정의해주어야 하기도 해서
롬복을 사용하기 괜찮은 상황이라고 생각이 드는데 혹시 적용해두어도 괜찮을까요 :)

@PgmJun PgmJun changed the title [] 이든(최승준) 미션 제출합니다. [1 - 2단계 방탈출 예약 대기] 이든(최승준) 미션 제출합니다. May 16, 2024
@seovalue
Copy link

Entity 객체의 필드값 사용되는 원시타입을 VO로 포장해주는 것에 대해 어떻게 생각하시나요?
조앤은 이 부분에 대해 실무에서 어떻게 하시나요? 개인적으로 궁금한 부분입니다 ㅎㅎ

저는 VO로 포장하는 경우, 검증 로직 책임 분리로 인해 Entity 객체의 코드가 짧아지고
예외 처리(예외 메시지, 예외 객체) 세분화가 가능해져서 괜찮다고 생각합니다.
조앤은 이 부분에 대해서 어떻게 생각하시나요?

참고로 말씀은 이렇게 드렸지만, SpringJDBC -> JPA로 마이그레이션 하는 과정에서 컴파일 에러가 많이 발생해서
리뷰요청을 보내는 현 시점에는 원시타입으로 관리하고 있는 상태입니다.

오잉? Member 보면 감싸주신 것 같은데 맞을까요!
저는 개인적으로는 엔티티에는 원시값 그대로 사용하는 것을 선호해요.
그리고, 도메인에서는 원시값을 감싼 객체를 사용하도록 해주는 것을 선호하고요.

지금은 엔티티 == 도메인이어서 어쩔 수 없는 것 같은데,
MemberEntity -> @Entity 어노테이션이 붙어있고 실제 db schema와 일치하는 형태
Member -> MemberEntity를 비즈니스 로직에서 사용하는 Member 객체로 변환한 형태
위처럼 두 가지 케이스를 나누고 각각에 알맞게 사용해주는 게 좋다고 생각해요.

그 이유는, Entity에서 원시값을 포장하게 되면 불필요한 Embedded 어노테이션을 붙여야하고 Column에 대한 정의를 볼 때 한뎁스 더 이동해야해서 (MemberName으로 감싼 name의 column length를 보려면 MemberName 객체로 이동해야함.) 가독성이 떨어지는 부분도 있다고 생각하기도 해서요ㅎㅎ (물론 완전 제생각입니다)

하지만 지금은 연습이니 엔티티에서도 이든이 말씀주신대로 원시값을 모두 감싸는 것은 좋다고 생각합니다! 👍

@seovalue
Copy link

seovalue commented May 17, 2024

Lombok을 사용하는 것에 대해서 어떻게 생각하시나요?
로깅을 위해 toString()을 보통 재정의 해주는 편인데요!
미션 요구사항이 계속 변경되면서 필드에 변경이 종종 생기고 있어서 그때그때 toString()을 재정의해주는 것을 까먹기도 합니다.
또한 JPA를 사용하면서 기본 생성자를 Entity 객체에 항상 정의해주어야 하기도 해서
롬복을 사용하기 괜찮은 상황이라고 생각이 드는데 혹시 적용해두어도 괜찮을까요 :)

네네 저는 실무에서는 Kotlin을 사용중이라서 Java 쓰시는 분들께 여쭤봤는데 실무에서도 Lombok을 사용하신다고 하네요!
그래서 사용해보는 경험을 하셔도 좋을 것 같아요ㅎㅎ
다만 사용하신다면.. 제가 각 어노테이션에 대해 질문을 할 것입니다... 어떻게 동작하고 어떤 어노테이션인지ㅎㅎ (잘 학습하고 사용하시길 바라요!)

Copy link

@seovalue seovalue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요, 이든! 반가워요ㅎㅎ
미션 잘 수행해주셨네요~ 몇가지 코멘트 남겼는데 확인 후 리뷰 요청 주세요!

README.md Outdated
- Request

```
GET /reservations-mine HTTP/1.1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

주어진 엔드포인트에 대해서 어떻게 생각하세요?

Copy link
Author

@PgmJun PgmJun May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 API endpoint는 RESTful 하지 않다고 느껴집니다.
REST의 모든 원칙은 지키지 못하더라도 최소한 리소스를 가리키는 의미있는 endpoint를 사용하는게 좋다고 생각이 들어요!
고려할 내용이 정말 많은 부분이라 우선은 LMS 요구사항에 나와있는대로 엔드포인트를 사용해주었는데
조앤이 언급해주신 김에 한번 변경해보겠습니다:)

@GetMapping("/login/check")
public ApiResponse<LoginCheckResponse> checkLogin(@MemberId final Long memberId) {
LoginCheckResponse response = authService.checkLogin(memberId);
return ApiResponse.success(response);
}

// TODO: 토큰 재발급 자동화 로직 구현

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요건 왜 TODO예요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클라이언트에서 401 Unauthorized가 발생하면 /token-reissue API를 다시 호출하도록 만들어주려고 해요!
그래서 TODO로 작성해두었는데 그 전에 리뷰 요청 기간이 되어서 마무리하지 못했던 작업입니다..!

@@ -8,6 +8,7 @@
import org.springframework.web.servlet.HandlerInterceptor;
import roomescape.global.auth.annotation.Admin;
import roomescape.global.auth.jwt.JwtHandler;
import roomescape.global.auth.jwt.constant.JwtKey;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

global이라는 패키지명에 대해서 어떻게 생각하세요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지금은 프로젝트 크기가 작아서 괜찮다고 생각이 들지만
언젠가 프로젝트 규모가 커지면 말도안되는 짬뽕패키지가 될 것 같네요..!
global 말고 다른 이름으로 변경하고 싶은데 마땅히 내부에 있는 패키지들을 어떤 이름으로 분리해주어야할 지 감이 잘 오지 않아요..
알고싶어서 찾아다녀도 검색 능력 부족인지 크게 의미있는 내용의 글을 아직 발견하지는 못했습니다 ㅠ
패키지 네이밍은 정말 어려운 것 같습니다 🥲

혹시 조앤은 패키징 시에 지향하시는 방식이나 래퍼런스가 있으신가요??
있으시다면 참고할 수 있도록 알려주실 수 있으실까요..!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 좀 옛날 글이긴 한데 지금은 모듈 우선 vs 레이어 우선 정도로만 고민해봐도 될 것 같아요!
https://www.slipp.net/questions/36

지금의 global에 들어있는 객체들을 더 작은 단위로 구분하면 어떤걸지, 모듈로 나눠본다면? 레이어로 나눠본다면? 이런 식으로 고민해보시면 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 포비의 글이네요! ㅋㅎㅋㅎ
글을 보니 뭔가 변경해볼만한 포인트를 찾은 것 같아서 다음 Step에서 적용해볼게요!!
감사합니다 조앤:)

@@ -134,6 +190,48 @@ Content-Type: application/json

---

### 내 예약 정보 조회 API

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README에 구현해주신 테이블 명세에 대한 ERD를 추가해주시는건 어때요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무 좋은 것 같습니다:)
반영해볼게요!

private final String value;
public record MemberName(String value) {
public static final int MIN_LENGTH = 1;
public static final int MAX_LENGTH = 50;

public MemberName(final String value) {
this.value = value;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value라는 이름보다는 name 과 같이 변수명에도 이름을 지어주는 건 어떨까요

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

다른 값 객체도 마찬가지로요~

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 이점이 있을 지 고민을 해보았는데 우선 Entity 객체에서 @AttributeOverride 에노테이션 설정을 제거할 수 있다는 점이 꽤나 큰 이점이네요!

현재 JPA에서 엔티티를 기반으로 테이블 생성 시,
@Embedded 에노테이션에 의해서 Entity 객체의 VO 필드의 이름을 사용하지 않고 VO 내부의 필드명인 value를 컬럼명으로 사용하여 테이블 컬럼을 만들어내고 있어서
@AttributeOverride 에노테이션을 통해 매핑정보를 재정의 해서 이름을 변경해주고 있는데요.
이렇게 실수로 빼먹을 수도 있는 로직을 제거해줌으로써 얻는 이점이 크다고 생각돼서 변경해보겠습니다:)

조앤은 어떤 이유로 VO의 필드명을 value가 아닌 이름을 지어 사용하는 방향으로 권장해주셨나요??

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋㅋ저는 value를 개인적으로 그냥 선호하지 않아서 제안드렸어요ㅎㅎ
뭔가 너무 범용적인 이름인 것 같은 느낌?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조앤은 범용적인 부분들을 최대한 제외하려고 노력하시는 것 같아요:)

조앤의 말씀대로 value는 범용적인 느낌이 강해서
.value() 를 수행했을 때, 클래스를 찾아서 하나의 값 만을 가진 값 객체임을 확인하지 않는다면
"value()를 통해 무슨 값을 가져오는거지?" 라는 생각이 충분히 들 수 있을 것 같다고 생각이 들어요ㅎㅎ
이러한 부분 잘 고려해보면서 개발해나가겠습니다!


public class MemberName {
private final String value;
public record MemberName(String value) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

근데 어떤 곳에는 Embeddable이 표시되어있고, 어떤 곳은 누락된 것 같아요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇네요..! 누락된 것 같습니다 바로 처리하겠습니다.
확인감사합니다!

final ThemeRepository themeRepository,
final MemberService memberService, ThemeService themeService) {
this.reservationRepository = reservationRepository;
this.reservationTimeRepository = reservationTimeRepository;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reservationTime은 왜 repository와 service를 모두 물고 있나요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReservationTimeService를 주입받은 이유는 Repository Layer에서 findById를 통해 조회해오는 Optional 객체에 대해 orElseThrow를 처리해주는 로직이 있는데 이를 사용하기 위해서 였습니다.

ReservationTimeRepository 를 사용한 이유는 이외에 다른 로직을 사용하기 위해서였습니다.
Service를 사용하면 찾고자 하는 값을 DTO로 주기 때문에 엔티티 객체를 그대로 사용하려면 Repository를 사용할 수 밖에 없었습니다.

그런데 저도 Service와 Repository를 다 참조하는 것이 조금 아쉬운 설계가 아닐까하는 고민이 있었습니다.
사실 이렇게 된 근본적 이유는 findById를 통해 조회해오는 Optional 객체에 대해 orElseThrow를 해주는 조회로직 때문인데,,
꼭 한 가지만 참조하게 만들어야 하나요?? 궁금합니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그런건 아닌데, 일관성이 있으면 좋을 것 같아서 제안드렸어요!
또한 만약 ReservationTimeService도 ReservationService를 가진다면 양방향 참조가 발생할 수도 있고요!

GROUP BY r.theme_id
ORDER BY COUNT(r.theme_id) DESC, t.id ASC
LIMIT :limit
""", nativeQuery = true)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nativeQuery를 사용하지 않으려면 어떻게 할 수 있을까요?

Copy link
Author

@PgmJun PgmJun May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

특정 DB에 종속적이게 되기 때문에 JPQL로 해결하는 것이 좋긴 하겠습니다!
Limit 관련 처리 때문에 nativeQuery를 우선 사용했었는데 Pageable로 처리하도록 변경해보겠습니다!

@Sql(scripts = "/truncate.sql", executionPhase = ExecutionPhase.BEFORE_TEST_METHOD)
@Import({ReservationTimeDao.class, ThemeDao.class, ReservationService.class, ReservationDao.class, MemberDao.class, MemberService.class})
@Import({ReservationService.class, MemberService.class, ReservationTimeService.class, ThemeService.class})
class ReservationServiceTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번에 추가된 기능에 대한 테스트가 없는 것 같아요?

Copy link
Author

@PgmJun PgmJun May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰요청 시간이 되어서 급하게 요청 드리려고 기능을 우선 개발하다보니 놓치고 간 것 같습니다..!
이 부분에 대해서 테스트 작성 해두겠습니다:)

@@ -34,13 +34,13 @@
public class ReservationControllerTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 이번에 추가된 기능에 대한 테스트가 없어요~

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위와 마찬가지로 리뷰요청 시간이 되어서 급하게 요청 드렸습니다..!
이 부분에 대해서도 테스트 작성 해두겠습니다!

Copy link

@seovalue seovalue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 이든~~!
리팩터링 잘 해주셨네요ㅎㅎ
수정해주신 부분 중 궁금한 부분들에 대해 질문 남겨뒀는데, 다음 단계 진행하시기 전에 코멘트 남겨주세요! 이번 단계는 머지할게요!

@@ -195,7 +199,7 @@ Content-Type: application/json
- Request

```
GET /reservations-mine HTTP/1.1
GET /reservations/member HTTP/1.1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오홍 member로 교체해주셨군요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 member로 교체해보았습니다!

@@ -1,5 +1,9 @@
# 방탈출 예약 관리

## ERD

<img width="896" alt="image" src="https://github.com/PgmJun/spring-roomescape-waiting/assets/84304802/e050da11-0193-4512-bd18-530755f3e767">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

import roomescape.global.exception.error.ErrorType;
import roomescape.global.exception.model.ValidateException;

import java.util.regex.Pattern;

public record Email(String value) {
@Embeddable

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

import roomescape.global.exception.error.ErrorType;
import roomescape.global.exception.model.ValidateException;

public record MemberName(String value) {
@Embeddable

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

명시 안해줬을 땐 어떻게 원하는 대로 잘 동작했을까나요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 그러게요..!! 궁금합니다..! 한 번 찾아볼게요!!

@@ -42,7 +42,7 @@ public ApiResponse<ReservationsResponse> getAllReservations() {
}

@Auth
@GetMapping("/reservations-mine")
@GetMapping("/reservations/member")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

근데 요거 약간
/reservations/member/{memberId} 이런 형태였으면 어떨까? 이런 생각도 드네요!
지금 형태는 endpoint만 봤을 때 모든 멤버들의 예약을 조회할 것만 같은 느낌이 들어요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 처음에 생각했던 endpoint가 /reservations/members/{memberId} 이런 형태였는데
현재 JWT를 통해 memberId를 받고 있어서 그런 형태가 어렵지 않나 싶습니다 ㅠㅠ
어떻게 하면 좋을까요?

@@ -38,9 +39,9 @@ public ThemesResponse findAllThemes() {
public ThemesResponse getTop10Themes(final LocalDate today) {
LocalDate startDate = today.minusDays(7);
LocalDate endDate = today.minusDays(1);
int limit = 10;
Pageable pageable = Pageable.ofSize(10);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

}

@Test
@DisplayName("토큰 재발급을 요청하면 새로운 AccessToken과 RefreshToken을 Response 한다.")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이든, 이거 해주신 김에 accessToken과 refreshToken에 대해서 한번 설명해주시겠어요?
이 과정이 필요하다고 생각해주신 이유도 함께 덧붙여주시면 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵!
회원은 로그인 시에 JWT AccessToken, RefreshToken을 받습니다.
JWT는 보통 발급 후에는 서버에서 관리하지 않는 Stateless 방식으로 사용됩니다.

로그인 시 발급받은 AccessToken은 인증이 필요한 API에 접근할 때 인증 용도로 사용됩니다.
서버에서는 AccessToken을 파싱하여 서버에서 발급한 것이 맞는지, 만료되지는 않았는 지 등을 확인하는 절차를 거치고
여기서 통과된다면 AccessToken에 들어있는 유저를 식별할 수 있는 최소한의 값(저는 memberId 식별자를 사용하였습니다.)을 통해 회원 정보와 그 회원의 권한에 대해서 검증합니다.

또한 위에서 말씀드렸다시피 JWT는 Stateless하게 사용됩니다. 때문에 서버는 JWT 토큰을 관리하지 않고 발급만 해줍니다.
이렇게 관리하게 되면 토큰이 탈취당해도 정상적인 토큰만 맞다면 서버는 요청에 대해 정상적으로 응답값을 내려줍니다.
해당 문제를 막기 위해 AccessToken 토큰의 만료 기간을 짧게 설정하여 토큰이 탈취당하더라도 조금만 쓰면 만료되어 다시 사용하지 못하도록 만드는 것으로 알고 있습니다.

하지만 여기서 또 문제가 있습니다.
AccessToken의 주기를 짧게 설정하면, 유저는 짧은 주기로 다시 로그인을 수행해야하며
재로그인 과정이 계속해서 반복될 때마다 유저 경험은 나빠질 수 밖에 없습니다.
저도 방탈출 미션을 개발하면서 로그인 이후 정상적으로 작동하는 지 웹 환경에서 테스트를 하다보면 토큰이 만료되어, 다시 로그인하는 과정이 계속해서 발생하다보니 너무 불편했습니다.

이러한 문제는 RefreshToken을 통해 해결할 수 있습니다.
RefreshToken은 주기가 짧은 AccessToken을 로그인하지 않고도 재발급받을 수 있도록 만들기 위해 탄생한 '토큰 갱신 목적'의 토큰인 것으로 알고 있습니다.

AccessToken이 만료되면 재로그인 하는 것이 아니라 RefreshToken을 통해 AccessToken을 갱신시켜 다시 로그인할 필요없이 서비스를 이어서 사용할 수 있습니다.
때문에 RefreshToken은 보통 AccessToken보다 만료 주기를 길게 가져갑니다.
그래야 긴 시간동안 재 로그인없이 서비스를 이용할 수 있을테니까요 :)

하지만 또 발생할 수 있는 문제는 RefreshToken까지 탈취당하는 상황입니다.
AccessToken과 더불어 RefreshToken 까지 탈취당하게 된다면, 그리고 Token Refresh 시에 RefreshToken까지 갱신해주게 된다면 공격자는 계속해서 탈취한 유저의 권한을 이용할 수 있게 됩니다.
저는 과거에 이러한 문제를 막기 위해서 RefreshToken 매 요청마다 재발급하며 DB에 저장해서 관리하는 RTR(Refresh Token Rotation) 패턴을 사용하는 등 안전을 위한 장치를 만들어 본 경험이 있었습니다!

그런데 지금 프로젝트는 이러한 안전 처리는 되어있지 않아서 AccessToken, RefreshToken을 탈취당하면 막을 수 없습니다..😭
그래도 알고 있기 때문에 금방 처리할 수 있습니다만, 지금은 중요한 사항이 아니라서 토큰 갱신 기능까지만 만들어두고 마지막에 처리해둘 생각입니다!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오호 굿굿굿굿!
리프레시 토큰 로테이션은 저도 몰랐는데 배워가네요👍🏻

Comment on lines +41 to +44
System.out.println(member1);
System.out.println(member2);
System.out.println(member3);
System.out.println(member4);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지워볼까요~~!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗..! 바로 제거하겠습니다! 확인 감사합니다!

.when().get("/reservations/member")
.then().log().all()
.statusCode(200)
.body("data.reservations.size()", is(3));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좀 더 제대로 검증하기 위해, 하나는 다른 Member의 Reservation도 DB에 넣어두는 건 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그 편이 훨씬 정확한 검증이 되겠네요:) 감사합니다 처리해둘게요!

@seovalue seovalue merged commit 5cc68d4 into woowacourse:pgmjun May 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants