예약 시스템 API - 프로모션 정보 조회

Seyeong·2023년 3월 26일
0

이번엔 프로모션 정보를 조회하는 API를 제작해보겠습니다.

프로모션 정보를 조회하는 API 문서를 보면 아래와 같습니다.

API 문서

요청

응답

위의 API를 만들기 위해서 Repository를 작성해보겠습니다.

Repository 테스트 코드 작성

JdbcReservationRepositoryTest

public class JdbcReservationRepositoryTest {
	
    ...
    
    @Test
    void testApiPromotions() {
        // when
        PromotionsResponseDto actual = repository.getPromotions();

        // then
        PromotionsResponseDto expected = getPromotionsResponseDto();
        assertThat(actual.getSize()).isEqualTo(expected.getSize());
        assertThat(actual.getItems().get(0)).isEqualTo(expected.getItems().get(0));
        assertThat(actual.getItems().get(10)).isEqualTo(expected.getItems().get(10));
    }
    
    public static PromotionsResponseDto getPromotionsResponseDto() {
        PromotionResponseDto promotion1 = new PromotionResponseDto(1, 1, 1, "전시", "Paper, Present:너를 위한 선물", 61);
        PromotionResponseDto promotion2 = new PromotionResponseDto();
        PromotionResponseDto promotion3 = new PromotionResponseDto();
        PromotionResponseDto promotion4 = new PromotionResponseDto();
        PromotionResponseDto promotion5 = new PromotionResponseDto();
        PromotionResponseDto promotion6 = new PromotionResponseDto();
        PromotionResponseDto promotion7 = new PromotionResponseDto();
        PromotionResponseDto promotion8 = new PromotionResponseDto();
        PromotionResponseDto promotion9 = new PromotionResponseDto();
        PromotionResponseDto promotion10 = new PromotionResponseDto();
        PromotionResponseDto promotion11 = new PromotionResponseDto(11, 44, 5, "연극", "어바웃 머니\n", 172);
        List<PromotionResponseDto> promotions = List.of(promotion1, promotion2, promotion3, promotion4, promotion5,
                promotion6, promotion7, promotion8, promotion9, promotion10, promotion11);

        return new PromotionsResponseDto(promotions.size(), promotions);
    }
}

이 테스트를 통과시키기 위해 아래와 같은 테이블 및 쿼리가 필요합니다.

테이블

  • Promotion

  • Product

  • Product_Image

  • Category

  • File_Info

쿼리

SELECT pm.id id, pd.id productId, pd.category_id categoryId, c.name categoryName, pd.description,
       (
           SELECT fi.id
           FROM product_image pi, file_info fi
           WHERE pi.file_id = fi.id and pi.product_id = pd.id and fi.file_name LIKE CONCAT('%', 'ma' '%')
       ) as fileId
FROM promotion pm, product pd, category c
WHERE pd.id = pm.product_id AND pd.category_id = c.id

결과

이 쿼리를 토대로 Repository를 구현해봅시다.

Repository 코드 작성

응답에 보낼 DTO 객체들을 만들어봅시다.

PromotionsResponseDto

@Builder
@Getter
@RequiredArgsConstructor
@NoArgsConstructor(force = true)
public class PromotionsResponseDto implements RestDocsTemplate {
    private final int size;
    private final List<PromotionResponseDto> items;

    @Override
    public List<RestDocsDto> generateRestDocsFields() {
        return List.of(
                RestDocsDto.builder().path("size").description("프로모션 정보의 수").build(),
                RestDocsDto.builder().path("items[]").description("프로모션 상품 정보").build(),
                RestDocsDto.builder().path("items[].id").description("프로모션 상품 PK").build(),
                RestDocsDto.builder().path("items[].productId").description("프로모션 상품 ID").build(),
                RestDocsDto.builder().path("items[].categoryId").description("프로모션 상품 카테고리 ID").build(),
                RestDocsDto.builder().path("items[].categoryName").description("프로모션 상품 카테고리명").build(),
                RestDocsDto.builder().path("items[].description").description("프로모션 상품 설명").build(),
                RestDocsDto.builder().path("items[].fileId").description("file_info 테이블의 id (product_image의 타입중 ma인 경우만)").build()
        );
    }
}

PromotionResponseDto

@Builder
@Getter
@EqualsAndHashCode
@RequiredArgsConstructor
@NoArgsConstructor(force = true)
public class PromotionResponseDto {
    private final int id;
    private final int productId;
    private final int categoryId;
    private final String categoryName;
    private final String description;
    private final int fileId;

    public static final RowMapper<PromotionResponseDto> promotionMapper = (rs, rowNum) -> {
        int id = rs.getInt("id");
        int productId = rs.getInt("productId");
        int categoryId = rs.getInt("categoryId");
        String categoryName = rs.getString("categoryName");
        String description = rs.getString("description");
        int fileId = rs.getInt("fileId");

        return new PromotionResponseDto(id, productId, categoryId, categoryName, description, fileId);
    };
}

이제 쿼리 부분을 만들어봅시다.

SQLMapper

public class SQLMapper {
	
    ...
    
    public static final String SELECT_PROMOTIONS_QUERY =
            "SELECT pm.id id, pd.id productId, pd.category_id categoryId, c.name categoryName, pd.description,\n"
                    + "       (\n"
                    + "           SELECT fi.id\n"
                    + "           FROM product_image pi, file_info fi\n"
                    + "           WHERE pi.file_id = fi.id and pi.product_id = pd.id and fi.file_name LIKE CONCAT('%', 'ma' '%')\n"
                    + "       ) as fileId\n"
                    + "FROM promotion pm, product pd, category c\n"
                    + "WHERE pd.id = pm.product_id AND pd.category_id = c.id";
}

쿼리를 작성해주고,

JdbcReservationRepository

...
public class JdbcReservationRepository implements ReservationRepository {

	...
    
    @Override
    public PromotionsResponseDto getPromotions() {
        List<PromotionResponseDto> results = jdbcTemplate.query(
                SQLMapper.SELECT_PROMOTIONS_QUERY,
                PromotionResponseDto.promotionMapper
        );
        return PromotionsResponseDto.builder().size(results.size()).items(results).build();
    }
}

이렇게 코드를 작성해주면 테스트가 통과할 겁니다

이제 Service를 구현해봅시다.

Service 테스트 코드 작성

ReservationServiceTest

public class ReservationServiceTest {

	...
    
    @Test
    void testApiPromotions() {
        // given
        PromotionsResponseDto promotions = JdbcReservationRepositoryTest.getPromotionsResponseDto();
        
        // when
        when(repository.getPromotions()).thenReturn(promotions);
        PromotionsResponseDto actual = service.getPromotions();
        
        // then
        assertThat(actual.getSize()).isEqualTo(promotions.getSize());
        assertThat(actual.getItems().get(0)).isEqualTo(promotions.getItems().get(0));
        assertThat(actual.getItems().get(10)).isEqualTo(promotions.getItems().get(10));
    }
}

repository.getPromotions() 메서드가 호출되면 반환으로 RepositoryTest에서 선언했던 getPromotionsResponseDto 메서드의 리턴 값이 반환되도록 설정해주었습니다.

이제 이 테스트 코드가 통과하도록 구현해봅시다.

ReservationService

@Service
@RequiredArgsConstructor
public class ReservationService {

	...

    public PromotionsResponseDto getPromotions() {
        return repository.getPromotions();
    }
}

통과했습니다.

이제 컨트롤러를 작성해봅시다.

ReservationControllerTest

class ReservationControllerTest {
	
    ...

    @Test
    void testApiPromotions() throws Exception {
        // given
        PromotionsResponseDto expected = JdbcReservationRepositoryTest.getPromotionsResponseDto();

        // when
        when(service.getPromotions()).thenReturn(expected);

        // then
        mockMvc.perform(get("/api/promotions"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.size").value(11))
                .andExpect(jsonPath("$.items").isArray())
                .andExpect(jsonPath("$.items[0].fileId").value(61))
                .andExpect(jsonPath("$.items[10].fileId").value(172));
    }
}

Service와 마찬가지로 기대하는 값을 Repository Test로부터 가져옵니다.

그리곤 when( ) 으로 스텁을 설정해주고 검증을 해줍니다.
구현은 아래와 같습니다.

ReservationController

...
public class ReservationController {

	...

    @GetMapping("/promotions")
    public PromotionsResponseDto getPromotions() {
        return service.getPromotions();
    }
}

서비스를 호출해주는 것으로 모든 코드 구현이 끝났습니다.

마지막으로 REST Docs 구현 코드는 아래와 같습니다.

ReservationRestDocsTest

...
public class ReservationRestDocsTest {

	...

    @Test
    void generateDocsApiPromotions() throws Exception {
        // then
        String URI = "/api/promotions";
        mockMvc.perform(get(URI))
                .andExpect(status().isOk())
                .andDo(RestDocsGenerator.generate(URI, null, new PromotionsResponseDto()));
    }
}

여기까지 프로모션 조회 API를 만들었습니다.

0개의 댓글