이번엔 프로모션 정보를 조회하는 API를 제작해보겠습니다.
프로모션 정보를 조회하는 API 문서를 보면 아래와 같습니다.
위의 API를 만들기 위해서 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를 구현해봅시다.
응답에 보낼 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를 구현해봅시다.
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를 만들었습니다.