[우아한테크코스 4기] 220707 F12 개발일지

Jihoon Oh·2022년 7월 7일
1

우아한테크코스 4기

목록 보기
18/43
post-thumbnail

오늘 진행한 일

제품에 대한 리뷰를 작성 또는 조회할 때 제품이 존재하는지 확인하는 로직 추가

특정 제품(현재는 키보드만 있으므로 키보드)에 대해 리뷰를 작성하거나 조회할 때, 해당 제품은 반드시 DB에 저장되어있어야 함이 당연하다. 따라서 만약 DB에 없는 제품에 대해 리뷰를 작성하는 요청이 오면 예외를 반환해주어야한다. 그런데 우리 코드에는 해당 로직이 없었다.

만약 Keyboard - Review가 JPA의 연관관계를 맺고 있다면 연관관계를 맺어주는 과정에서 예외 처리가 진행되겠지만, 현재 우리 팀의 코드는 id값만 참조하고 있을 뿐 연관관계를 직접 맺어주지는 않으므로, 없는 id 값이 들어가는 경우에 대해 검증하는 로직을 추가할 필요가 있었다. 제품이 존재하는지 여부만 판단하면 되므로 굳이 모든 값을 조회해오기 보다는 exists 쿼리를 사용하기로 결정했다. JpaRepository에는 이미 빌트인으로 구현된 exists 메서드가 존재하여 해당 메서드를 사용했다.

private void validateKeyboardExists(final Long productId) {
    if (!keyboardRepository.existsById(productId)) {
        throw new KeyboardNotFoundException();
    }
}

다만 쿼리를 찍어보니 기본으로 존재하는 existsById 메서드는 count 쿼리를 사용해서 존재 여부를 판단하는데, 데이터 수가 많아지면 count 쿼리와 select exists 쿼리의 속도가 차이나는 만큼 추후 쿼리 최적화가 필요할 것으로 보인다.

테스트 코드 리팩토링

기존의 테스트 코드에는 중복된 부분이 많았다. 중복을 최대한 제거하고 객체를 재활용하기 위해 테스트 용 객체를 픽스처로 분리했다. data.sql 같은 스크립트로 데이터를 넣어주는 방법도 있지만 그렇게 하면 어떤 데이터가 들어있는지 테스트 코드에서 확인하기가 어렵기 때문에 테스트 픽스처를 만들어 사용하는 형태로 리팩토링 하기로 했다.

public enum KeyboardFixtures {

    KEYBOARD_1("키보드1", "이미지 주소"),
    KEYBOARD_2("키보드2", "이미지 주소"),
    ;

    private final String name;
    private final String imageUrl;

    KeyboardFixtures(final String name, final String imageUrl) {
        this.name = name;
        this.imageUrl = imageUrl;
    }

    public Keyboard 생성() {
        return 생성(null);
    }

    public Keyboard 생성(final Long id) {
        return Keyboard.builder()
                .id(id)
                .name(this.name)
                .imageUrl(this.imageUrl)
                .build();
    }
}

픽스처는 enum으로 만들었는데, 여러 테스트 객체를 생성하는데 변화시킬 값들을 enum의 필드 값으로 주고, 해당 enum에서 객체를 생성해서 반환해주는 생성 메서드를 만들어 테스트 코드에서 사용했다. 똑같은 생성 메서드가 두 개인 것은, JPA 관련된 테스트에서는 영속화 과정에서 id값이 들어가는데 반해 mocking으로 인해 id를 직접 넣어줘야 하는 경우도 있기 때문이었다.

public enum ReviewFixtures {

    REVIEW_RATING_1("리뷰 내용", 1),
    REVIEW_RATING_2("리뷰 내용", 2),
    REVIEW_RATING_3("리뷰 내용", 3),
    REVIEW_RATING_4("리뷰 내용", 4),
    REVIEW_RATING_5("리뷰 내용", 5),
    ;

    private final String content;
    private final int rating;

    ReviewFixtures(final String content, final int rating) {
        this.content = content;
        this.rating = rating;
    }

    public Review 작성(Long productId) {
        return 작성(null, productId);
    }

    public Review 작성(Long reviewId, Long productId) {
        return Review.builder()
                .id(reviewId)
                .productId(productId)
                .content(this.content)
                .rating(this.rating)
                .createdAt(LocalDateTime.now())
                .build();
    }

    public ExtractableResponse<Response> 작성_요청을_보낸다(Long productId) {
        final ReviewRequest reviewRequest = new ReviewRequest(this.content, this.rating);
        return RestAssuredRequestUtil.POST_요청을_보낸다("/api/v1/keyboards/" + productId + "/reviews", reviewRequest);
    }
}

리뷰 픽스처에는 키보드 픽스처보다 메서드를 하나 더 추가했는데, 키보드의 경우 CRUD에서 C에 해당하는 api가 없었지만 리뷰의 경우 Create를 하는 api가 있기 때문에 인수 테스트 용으로 RestAssured를 사용한 POST 요청을 보내는 메서드가 필요했기 때문이다. 이렇게 픽스처를 분리하므로써 조금 더 읽기 쉬운 테스트 코드를 만들 수 있었다.

@Test
void 전체_리뷰_목록을_최신순으로_조회한다() {
    // given
    Keyboard keyboard1 = 키보드를_저장한다(KEYBOARD_1.생성());
    Keyboard keyboard2 = 키보드를_저장한다(KEYBOARD_2.생성());
    Long reviewId1 = Location_헤더에서_id값을_꺼낸다(REVIEW_RATING_4.작성_요청을_보낸다(keyboard1.getId()));
    Long reviewId2 = Location_헤더에서_id값을_꺼낸다(REVIEW_RATING_4.작성_요청을_보낸다(keyboard2.getId()));

    // when
    ExtractableResponse<Response> response = GET_요청을_보낸다(
            "/api/v1/reviews?page=0&size=2&sort=createdAt,desc");

    // then
    assertAll(
            () -> assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()),
            () -> assertThat(response.as(ReviewPageResponse.class).isHasNext()).isFalse(),
            () -> assertThat(response.as(ReviewPageResponse.class).getItems())
                    .extracting("id")
                    .containsExactly(reviewId2, reviewId1)
    );
}

오늘 발생한 이슈

오늘은 특별한 이슈가 발생하지 않았다.

내일 목표

내일은 데모데이가 있는 날이다. 데모데이를 진행하고 스프린트 2 페어를 정하기 위한 백엔드 팀 회의를 진행할 예정이다.

profile
Backend Developeer

1개의 댓글

comment-user-thumbnail
2022년 7월 7일

감사합니다 :)

답글 달기