2. Mock API Controller ๊ตฌํ
02) ๊ฐ์ง ๋ฐ์ดํฐ๋ฅผ ์ค๊ณํ์ฌ ์๋ตํ๋ ์คํ๋ง๋ถํธ ์ปจํธ๋กค๋ฌ๋ฅผ ์์ฑํ๊ณ ์์ค์ฝ๋๋ฅผ ์ ๋ก๋ํ์์ค.
์นดํ ์ผ์ ์งํํ๋ฉด์ controller์ DTO ๋ฑ์ ๋ฐฐ์ฐ๊ธด ํ์์ง๋ง, ์ ๋ง ํํ ์ง๋๊ฐ๋ ์ ๋๋ก๋ง ๊ณต๋ถํ์๊ธฐ ๋๋ฌธ์ ๊ณ ๋ ค์ฌํญ๋ค์ ์ด๋ป๊ฒ ๊ตฌํํ๋์ง, ์ด๋ค ๋ฐฉ์์ผ๋ก ๋ก์ง์ ์ง์ผ ํจ์จ์ ์ธ์ง ๋ฑ์ ๋ฌธ์ ๋ ์ ์ณ๋๊ณ ๊ธฐ์กด์ ์ฝ๋๋ฅผ ์ดํดํ๊ณ ํ๋ฌ์ค๋ก ํ์ํ ๋ถ๋ถ๋ง ๊ตฌํํ๋ ์์ผ๋ก ๊ณผ์ ๋ฅผ ์ํํด๋ณด๋ ค๊ณ ํ๋ค.
Mock : ์ค์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๊ธฐ์ ์๊ฐ, ๋น์ฉ ๋ฑ์ Cost๊ฐ ๋๊ฑฐ๋ ํน์ ๊ฐ์ฒด ์๋ก๊ฐ์ ์์กด์ฑ์ด ๊ฐํด ๊ตฌํํ๊ธฐ ํ๋ค ๊ฒฝ์ฐ ๊ฐ์ง ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
๊ฐ์ง ๋ฐ์ดํฐ (dummy data) : ์ ์ฉํ ๋ฐ์ดํฐ๊ฐ ํฌํจ๋์ง ์์ง๋ง ๊ณต๊ฐ์ ์๋นํด๋์ด ์ค์ ๋ฐ์ดํฐ๊ฐ ๋ช ๋ชฉ์ ์กด์ฌํ๋ ๊ฒ์ฒ๋ผ ๋ค๋ฃจ๋ ์ ์ํ ์ ๋ณด๋ฅผ ์๋ฏธ
๊ณผ์ ์ ๋ฐ๋ฅด๋ฉด ์ปจํธ๋กค๋ฌ๋ฅผ ์์ฑํด์ผ ํ๋๋ฐ, ๋ง์ด ๋ค์ด๋ณธ ์ด '์ปจํธ๋กค๋ฌ'์ ์ ํํ ์ ์๊ฐ ๊ถ๊ธํ๋ค.
Controller๋ ํ๋ฉด(View)๊ณผ ๋น์ฆ๋์ค ๋ก์ง(Model)๋ฅผ ์ฐ๊ฒฐ์ํค๋ ๋ค๋ฆฌ ์ญํ ์ด๋ผ๊ณ ํ ์ ์๋ค.
๋์ฑ ์ง๊ด์ ์ผ๋ก ์ค๋ช ํ์๋ฉด, ์ฃผ์๋ฅผ ์ ๋ ฅ ๋ฐ์ ์ด๋๋ก ๊ฐ์ง ๋ถ์ํ๊ณ ๋ง๋ ๊ธธ๋ก ์ฐ๊ฒฐ์์ผ์ฃผ๋ ์ญํ ์ ํ๋ค.
@Controller ์ด๋ ธํ ์ด์ ์ ํตํด ํด๋์ค์ ์ปจํธ๋กค๋ฌ ์ญํ ์ ๋ถ์ฌํ๋ค.
https://luanaeun.tistory.com/200
Spring) ์คํ๋ง Controller ๊ฐ๋ ๊ณผ ํ์ด์ง ์ฐ๊ฒฐ(์ฃผ์๋งคํ)
1. ์ปจํธ๋กค๋ฌ๋? ์คํ๋ง ํ๋ ์์ํฌ๋ MVCํจํด์ ์ฌ์ฉํ๊ณ ์๊ณ , ์ฌ๊ธฐ์ Controller๋ ํ๋ฉด(View)๊ณผ ๋น์ฆ๋์ค ๋ก์ง(Model)๋ฅผ ์ฐ๊ฒฐ์ํค๋ ๋ค๋ฆฌ ์ญํ ์ ํ๋ค. ์ฝ๊ฒ ๋งํ์๋ฉด ํ๋ฉด์์ /aaa ๋ก ๊ฐ์ค~ ํ๊ณ ์์ฒญ
luanaeun.tistory.com
ApiUtils.java
package com.example.kakaoshop._core.utils;
import lombok.*;
import org.springframework.http.HttpStatus;
public class ApiUtils {
public static <T> ApiResult<T> success(T response) {
return new ApiResult<>(true, response, null);
}
public static ApiResult<?> error(String message, HttpStatus status) {
return new ApiResult<>(false, null, new ApiError(message, status.value()));
}
@Getter @Setter @AllArgsConstructor
public static class ApiResult<T> {
private final boolean success;
private final T response;
private final ApiError error;
}
@Getter @Setter @AllArgsConstructor
public static class ApiError {
private final String message;
private final int status;
}
}
์์ ์ ํธ๋ฆฌํฐ ํด๋์ค์์๋ ์ฑ๊ณต ์ฌ๋ถ๋ฅผ Boolean ํํ๋ก๋ ์ค ์ ์์ง๋ง http ์ํ ์ฝ๋ ํํ๋ก๋ ์ค ์ ์๋ค.
์ค์ํ ๊ฒ์ ์ด๋ค ๊ฒ์ด ํ๋ก ํธ์์ ๋ฐ์์ ๋ ๋ ์ดํดํ๊ธฐ ์ฝ๊ฒ ์ง๊ด์ ์ผ๋ก ์ค๊ณ ํ๊ณ ์ผ๊ด์ฑ์ด ์๋์ง๊ฐ ์ค์ํ๋ค.
์ด ApiUtils.java ํด๋์ค๋ฅผ ์ด์ฉํ์ฌ Api ์๋ต์ ์ฒ๋ฆฌํ ๊ฒ์ด๋ค.
์ํ ๋ชฉ๋ก ๋ณด๊ธฐ ๊ธฐ๋ฅ Mock ๊ตฌํ
JSON request body
{
"success": true,
"response": [
{
"id": 1,
"productName": "๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ ",
"description": "",
"image": "/images/1.jpg",
"price": 1000
},
{
"id": 2,
"productName": "[ํฉ๊ธ์ฝ๋จ๋ฐค ๊ณจ๋]2022๋
์ฐ ํ๋ฐค ์นผ์ง๋ฐค700g์ธ/๊ตฐ๋ฐค์ฉ/์์จ",
"description": "",
"image": "/images/2.jpg",
"price": 2000
},
{
"id": 3,
"productName": "์ผ์ฑ์ ์ JBL JR310 ์ธ ์ด๋ฆฐ์ด์ฉ/์ฑ์ธ์ฉ ํค๋์
3์ข
!",
"description": "",
"image": "/images/3.jpg",
"price": 30000
},
{
"id": 4,
"productName": "๋ฐ๋ฅธ ๋๋ฃฝ์ง๋ง ๋ฐํจํจ์ 2๋ฐ์ค ์ญ๊ฐ์์น๋ณด์ฅ / ์ธ 7์ข
",
"description": "",
"image": "/images/4.jpg",
"price": 4000
},
{
"id": 5,
"productName": "[๋์ฃผ] ์ปทํ
๋ง๋์ฅ์กฑ, ์๋ค๋ฆฌ 100g/300g ์ธ ์ฃผ์ ๋ถ๋ฆฌ ๋ชจ์ /์ค๋
์ฑ ์ต๊ณ /๋ง๋ฅธ์์ฃผ",
"description": "",
"image": "/images/5.jpg",
"price": 5000
},
{
"id": 6,
"productName": "๊ตณ์ง์๋ ์๊ธ์ ํธ 1,050g 2ํฉ ์ธ ์ฐ๋ฆฌ์๋ก ๋ชจ์์ ",
"description": "",
"image": "/images/6.jpg",
"price": 15900
},
{
"id": 7,
"productName": "eoe ์ด๋๋๋ฆฌํฐ 30ํฌ, ์ค๋ ์ง๋ง ๊ณ ์์ด์ฌ์ ๋ณด์ถฉ์ ",
"description": "",
"image": "/images/7.jpg",
"price": 26800
},
{
"id": 8,
"productName": "์ ๋๋ฒจ PDRN ํฌ๋ฆผ 2๊ฐ. ํผ๋ถ๋ณด์ต/์ง์ ์ผ์ด",
"description": "",
"image": "/images/8.jpg",
"price": 25900
},
{
"id": 9,
"productName": "ํ๋ ์ด์คํ
์ด์
VR2 ํธ๋ผ์ด์ฆ ๋ฒ๋ค. ์์ํ ๋ชฐ์
๊ฐ",
"description": "",
"image": "/images/9.jpg",
"price": 797000
}
],
"error": null
}
1. ProductRespFindAllDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class ProductRespFindAllDTO {
private int id;
private String productName;
private String description;
private String image;
private int price;
@Builder
public ProductRespFindAllDTO(int id, String productName, String description, String image, int price) {
this.id = id;
this.productName = productName;
this.description = description;
this.image = image;
this.price = price;
}
}
product ํจํค์ง ๋ด์ response ํจํค์ง๋ฅผ ๋ง๋ค์ด DTO ํด๋์ค๋ฅผ ๋ง๋ ๋ค.
@Getter, @Setter ์ด๋ ธํ ์ด์ ์ ํตํด ์๋์ผ๋ก getter์ setter๊ฐ ์์ฑ๋๋๋ก ํ๊ณ Builder ์ด๋ ธํ ์ด์ ์ ํตํด ๋ฉค๋ฒ ํ๋๋ค์ ๋ํด ๋น๋๋ฅผ ๋ง๋ค์ด์ค๋ค.
CartRespFindAllDTO ํด๋์ค๋ ์ฅ๋ฐ๊ตฌ๋ ์กฐํ์ ๋ํ ์๋ต ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๊ธฐ ์ํ DTO(Data Transfer Object)์ ๋๋ค. ์ด DTO๋ @Getter์ @Setter ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ํ๋์ getter์ setter ๋ฉ์๋๋ฅผ ์์ฑํฉ๋๋ค.
์ฐธ๊ณ
1. @Getter, @Setter
https://gocoder.tistory.com/2354
2. @Builder๋ฅผ ์ฌ์ฉํ๋ ์ด์
https://pamyferret.tistory.com/67
2. ProductRestController
@RestController
public class ProductRestController {
@GetMapping("/products")
public ResponseEntity<?> findAll() {
List<ProductRespFindAllDTO> responseDTO = new ArrayList<>();
// ์ํ ํ๋์ฉ ์ง์ด๋ฃ๊ธฐ
responseDTO.add(new ProductRespFindAllDTO(
1, "๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ ", "", "/images/1.jpg", 1000
));
responseDTO.add(new ProductRespFindAllDTO(
2, "[ํฉ๊ธ์ฝ๋จ๋ฐค ๊ณจ๋]2022๋
์ฐ ํ๋ฐค ์นผ์ง๋ฐค700g์ธ/๊ตฐ๋ฐค์ฉ/์์จ", "", "/images/2.jpg", 2000
));
responseDTO.add(new ProductRespFindAllDTO(
3, "์ผ์ฑ์ ์ JBL JR310 ์ธ ์ด๋ฆฐ์ด์ฉ/์ฑ์ธ์ฉ ํค๋์
3์ข
!", "", "/images/3.jpg", 30000
));
responseDTO.add(new ProductRespFindAllDTO(
4, "๋ฐ๋ฅธ ๋๋ฃฝ์ง๋ง ๋ฐํจํจ์ 2๋ฐ์ค ์ญ๊ฐ์์น๋ณด์ฅ / ์ธ 7์ข
", "", "/images/4.jpg", 4000
));
responseDTO.add(new ProductRespFindAllDTO(
5, "[๋์ฃผ] ์ปทํ
๋ง๋์ฅ์กฑ, ์๋ค๋ฆฌ 100g/300g ์ธ ์ฃผ์ ๋ถ๋ฆฌ ๋ชจ์ /์ค๋
์ฑ ์ต๊ณ /๋ง๋ฅธ์์ฃผ", "", "/images/5.jpg", 5000
));
responseDTO.add(new ProductRespFindAllDTO(
6, "๊ตณ์ง์๋ ์๊ธ์ ํธ 1,050g 2ํฉ ์ธ ์ฐ๋ฆฌ์๋ก ๋ชจ์์ ", "", "/images/6.jpg", 15900
));
responseDTO.add(new ProductRespFindAllDTO(
7, "eoe ์ด๋๋๋ฆฌํฐ 30ํฌ, ์ค๋ ์ง๋ง ๊ณ ์์ด์ฌ์ ๋ณด์ถฉ์ ", "", "/images/7.jpg", 26800
));
responseDTO.add(new ProductRespFindAllDTO(
8, "์ ๋๋ฒจ PDRN ํฌ๋ฆผ 2๊ฐ. ํผ๋ถ๋ณด์ต/์ง์ ์ผ์ด", "", "/images/8.jpg", 25900
));
responseDTO.add(new ProductRespFindAllDTO(
9, "ํ๋ ์ด์คํ
์ด์
VR2 ํธ๋ผ์ด์ฆ ๋ฒ๋ค. ์์ํ ๋ชฐ์
๊ฐ", "", "/images/9.jpg", 797000
));
return ResponseEntity.ok().body(ApiUtils.success(responseDTO));
}
}
์ปจํธ๋กค๋ฌ์ ํ๋์ฉ ์ง์ ๋ฃ์ด์ ๊ฐ์ง ์๋ต ๋ฐ์ดํฐ๋ฅผ ๋ง๋ ๋ค.
3. test
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class ProductRestControllerTest {
@Autowired
private MockMvc mvc;
@Test
// ์ ์ฒด ์ํ ๋ชฉ๋ก ์กฐํ
public void findAll_test() throws Exception {
// when
ResultActions resultActions = mvc.perform(get("/products"));
String responseBody = resultActions.andReturn().getResponse().getContentAsString();
System.out.println("ํ
์คํธ : " + responseBody);
// verify
resultActions.andExpect(jsonPath("$.success").value("true"));
resultActions.andExpect(jsonPath("$.response[0].id").value(1));
resultActions.andExpect(jsonPath("$.response[0].productName").value("๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ "));
resultActions.andExpect(jsonPath("$.response[0].description").value(""));
resultActions.andExpect(jsonPath("$.response[0].image").value("/images/1.jpg"));
resultActions.andExpect(jsonPath("$.response[0].price").value(1000));
resultActions.andExpect(jsonPath("$.response[1].id").value(2));
resultActions.andExpect(jsonPath("$.response[1].productName").value("[ํฉ๊ธ์ฝ ๋จ๋ฐค ๊ณจ๋]2022๋
์ฐ ํ๋ฐค ์นผ์ง๋ฐค700g์ธ/๊ตฐ๋ฐค์ฉ/์์จ"));
resultActions.andExpect(jsonPath("$.response[1].description").value(""));
resultActions.andExpect(jsonPath("$.response[1].image").value("/images/2.jpg"));
resultActions.andExpect(jsonPath("$.response[1].price").value(2000));
}
}
์ํ ์์ธ ๋ณด๊ธฐ ๊ธฐ๋ฅ Mock ๊ตฌํ
1. ProductRespFindByIdDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter @Setter
public class ProductRespFindByIdDTO {
private int id;
private String productName;
private String description;
private String image;
private int price;
private int starCount; // 0~5
private List<ProductOptionDTO> options;
@Builder
public ProductRespFindByIdDTO(int id, String productName, String description, String image, int price, int starCount, List<ProductOptionDTO> options) {
this.id = id;
this.productName = productName;
this.description = description;
this.image = image;
this.price = price;
this.starCount = starCount;
this.options = options;
}
}
2. ProductOptionDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class ProductOptionDTO {
private int id;
private String optionName;
private int price;
@Builder
public ProductOptionDTO(int id, String optionName, int price) {
this.id = id;
this.optionName = optionName;
this.price = price;
}
}
3. findById
@GetMapping("/products/{id}")
public ResponseEntity<?> findById(@PathVariable int id) {
// ์ํ์ ๋ด์ DTO ์์ฑ
ProductRespFindByIdDTO responseDTO = null;
if(id == 1){
List<ProductOptionDTO> optionDTOList = new ArrayList<>();
optionDTOList.add(new ProductOptionDTO(1, "01. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค์๋์
4์ข
", 10000));
optionDTOList.add(new ProductOptionDTO(2, "02. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํ๋ผ์์๋์
5์ข
", 10900));
optionDTOList.add(new ProductOptionDTO(3, "๊ณ ๋ฌด์ฅ๊ฐ ๋ฒ ์ด์ง S(์ํ) 6ํฉ", 9900));
optionDTOList.add(new ProductOptionDTO(4, "๋ฝ์์ฐ๋ ํค์นํ์ฌ 130๋งค 12ํฉ", 16900));
optionDTOList.add(new ProductOptionDTO(4, "2๊ฒน ์๋นต์์ธ๋ฏธ 6๋งค", 8900));
responseDTO = new ProductRespFindByIdDTO(1, "๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ ", "", "/images/1.jpg", 1000, 5, optionDTOList);
}else if(id == 2){
List<ProductOptionDTO> optionDTOList = new ArrayList<>();
optionDTOList.add(new ProductOptionDTO(6, "22๋
์ฐ ํ๋จ๋ฐค 700g(ํ์ ํ๋งค)", 9900));
optionDTOList.add(new ProductOptionDTO(7, "22๋
์ฐ ํ๋จ๋ฐค 1kg(ํ์ ํ๋งค)", 14500));
optionDTOList.add(new ProductOptionDTO(8, "๋ฐค๊น๊ธฐ+๋คํ์ฉ ๊ตฌ์ดํ ์ธํธ", 5500));
responseDTO = new ProductRespFindByIdDTO(2, "[ํฉ๊ธ์ฝ๋จ๋ฐค ๊ณจ๋]2022๋
์ฐ ํ๋ฐค ์นผ์ง๋ฐค700g์ธ/๊ตฐ๋ฐค์ฉ/์์จ", "", "/images/2.jpg", 2000, 5, optionDTOList);
}else {
return ResponseEntity.badRequest().body(ApiUtils.error("ํด๋น ์ํ์ ์ฐพ์ ์ ์์ต๋๋ค : " + id, HttpStatus.BAD_REQUEST));
}
return ResponseEntity.ok(ApiUtils.success(responseDTO));
}
3. Test
@Test
// ๊ฐ๋ณ ์ํ ์์ธ ์กฐํ
public void findById_test() throws Exception {
// given
int id = 1;
// when
ResultActions resultActions = mvc.perform(
get("/products/" + id)
);
String responseBody = resultActions.andReturn().getResponse().getContentAsString();
System.out.println("ํ
์คํธ : " + responseBody);
// verify
resultActions.andExpect(jsonPath("$.success").value("true"));
resultActions.andExpect(jsonPath("$.response.id").value(1));
resultActions.andExpect(jsonPath("$.response.productName").value("๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ "));
resultActions.andExpect(jsonPath("$.response.description").value(""));
resultActions.andExpect(jsonPath("$.response.image").value("/images/1.jpg"));
resultActions.andExpect(jsonPath("$.response.price").value(1000));
resultActions.andExpect(jsonPath("$.response.options[0].id").value(1));
resultActions.andExpect(jsonPath("$.response.options[0].optionName").value("01. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค์๋์
4์ข
"));
resultActions.andExpect(jsonPath("$.response.options[0].price").value(10000));
resultActions.andExpect(jsonPath("$.response.options[1].id").value(2));
resultActions.andExpect(jsonPath("$.response.options[1].optionName").value("02. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํ๋ผ์์๋์
5์ข
"));
resultActions.andExpect(jsonPath("$.response.options[1].price").value(10900));
}
}
์ฅ๋ฐ๊ตฌ๋ ๋ชฉ๋ก ๋ณด๊ธฐ Mock ๊ตฌํ
1. CartRespFindAllDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter @Setter
public class CartRespFindAllDTO {
private List<ProductDTO> products;
private int totalPrice;
@Builder
public CartRespFindAllDTO(List<ProductDTO> products, int totalPrice) {
this.products = products;
this.totalPrice = totalPrice;
}
}
CartRespFindAllDTO ํด๋์ค๋ ์ฅ๋ฐ๊ตฌ๋ ์กฐํ์ ๋ํ ์๋ต ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๊ธฐ ์ํ DTO(Data Transfer Object)์ด๋ค. @Getter์ @Setter ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ์๋์ผ๋ก ํ๋์ getter์ setter ๋ฉ์๋๋ฅผ ์์ฑํ๋ค.
๋ํ @Builder ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ๋ฉด ์์ฑ์ ๋์ ์ ๋น๋๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ ํ๋ ๊ฐ์ ์ค์ ํ ์ ์๋ค. ์ด๋ฅผ ํตํด ๊ฐ์ฒด ์์ฑ ์ ํ์ํ ํ๋๋ง ์ ํ์ ์ผ๋ก ์ค์ ํ ์ ์๊ฒ ๋๋ค.
2. ProductDTO
@Getter @Setter
public class ProductDTO {
private int id;
private String productName;
private List<CartItemDTO> cartItems;
@Builder
public ProductDTO(int id, String productName, List<CartItemDTO> cartItems) {
this.id = id;
this.productName = productName;
this.cartItems = cartItems;
}
}
๋ค์์ผ๋ก, ProductDTO ๋ด๋ถ์๋ Product list์ ํ๋์ธ id, productName, cartItems๊ฐ ํฌํจ๋๋ค.
3. CartItemDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class CartItemDTO {
private int id;
private ProductOptionDTO option;
private int quantity;
private int price;
@Builder
public CartItemDTO(int id, ProductOptionDTO option, int quantity, int price) {
this.id = id;
this.option = option;
this.quantity = quantity;
this.price = price;
}
}
4. ProductOptionDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class ProductOptionDTO {
private int id;
private String optionName;
private int price;
@Builder
public ProductOptionDTO(int id, String optionName, int price) {
this.id = id;
this.optionName = optionName;
this.price = price;
}
}
5. CartRestController
import com.example.kakaoshop._core.utils.ApiUtils;
import com.example.kakaoshop.cart.response.CartItemDTO;
import com.example.kakaoshop.cart.response.CartRespFindAllDTO;
import com.example.kakaoshop.cart.response.ProductOptionDTO;
import com.example.kakaoshop.cart.response.ProductDTO;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class CartRestController {
@GetMapping("/carts")
public ResponseEntity<?> findAll() {
// ์นดํธ ์์ดํ
๋ฆฌ์คํธ ๋ง๋ค๊ธฐ
List<CartItemDTO> cartItemDTOList = new ArrayList<>();
// ์นดํธ ์์ดํ
๋ฆฌ์คํธ์ ๋ด๊ธฐ
CartItemDTO cartItemDTO1 = CartItemDTO.builder()
.id(4)
.quantity(5)
.price(50000)
.build();
cartItemDTO1.setOption(ProductOptionDTO.builder()
.id(1)
.optionName("01. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค์๋์
4์ข
")
.price(10000)
.build());
cartItemDTOList.add(cartItemDTO1);
CartItemDTO cartItemDTO2 = CartItemDTO.builder()
.id(5)
.quantity(5)
.price(54500)
.build();
cartItemDTO2.setOption(ProductOptionDTO.builder()
.id(1)
.optionName("02. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค์๋์
5์ข
")
.price(10900)
.build());
cartItemDTOList.add(cartItemDTO2);
// productDTO ๋ฆฌ์คํธ ๋ง๋ค๊ธฐ
List<ProductDTO> productDTOList = new ArrayList<>();
// productDTO ๋ฆฌ์คํธ์ ๋ด๊ธฐ
productDTOList.add(
ProductDTO.builder()
.id(1)
.productName("๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ ")
.cartItems(cartItemDTOList)
.build()
);
CartRespFindAllDTO responseDTO = new CartRespFindAllDTO(productDTOList, 104500);
return ResponseEntity.ok(ApiUtils.success(responseDTO));
}
}
์๋ต JSON์ ๋ง์ถฐ ProductOptionDTO๊น์ง ์์ฑํ ๋ค Controller๋ฅผ ํตํด ๊ฐ์ง ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฅํด์ค๋ค.
6. Test
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class CartRestControllerTest {
@Autowired
private MockMvc mvc;
@Test
@WithMockUser
// ์ ์ฒด ์ํ ๋ชฉ๋ก ์กฐํ
public void findAll_test() throws Exception {
// when
ResultActions resultActions = mvc.perform(
get("/carts")
);
String responseBody = resultActions.andReturn().getResponse().getContentAsString();
System.out.println("ํ
์คํธ : " + responseBody);
// verify
resultActions.andExpect(jsonPath("$.success").value("true"));
resultActions.andExpect(jsonPath("$.response.totalPrice").value(104500));
resultActions.andExpect(jsonPath("$.response.products[0].id").value(1));
resultActions.andExpect(jsonPath("$.response.products[0].productName").value("๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ "));
resultActions.andExpect(jsonPath("$.response.products[0].cartItems[0].id").value(4));
resultActions.andExpect(jsonPath("$.response.products[0].cartItems[0].option.id").value(1));
resultActions.andExpect(jsonPath("$.response.products[0].cartItems[0].option.optionName").value("01. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค์๋์
4์ข
"));
resultActions.andExpect(jsonPath("$.response.products[0].cartItems[0].option.price").value(10000));
resultActions.andExpect(jsonPath("$.response.products[0].cartItems[0].quantity").value(5));
resultActions.andExpect(jsonPath("$.response.products[0].cartItems[0].price").value(50000));
}
}
์ฅ๋ฐ๊ตฌ๋ ๋ด๊ธฐ MOCK ๊ตฌํ
1. CartDTO
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class CartDTO{
private int optionId;
private int quantity;
@Builder
public CartDTO(int optionId, int quantity) {
this.optionId = optionId;
this.quantity = quantity;
}
}
์ฅ๋ฐ๊ตฌ๋์ ๋ด๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ํด optionId, quantity ํ๋๋ฅผ ํฌํจํ๋ DTO๋ฅผ ์์ฑํ๋ค.
2. Controller
import com.example.kakaoshop._core.utils.ApiUtils;
import com.example.kakaoshop.cart.response.*;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class CartRestController {
@PostMapping("/carts/add")
public ResponseEntity<?> add(@RequestBody List<CartDTO> request) {
// ์ฅ๋ฐ๊ตฌ๋ ๋ด๊ธฐ
return ResponseEntity.ok(ApiUtils.success(null));
}
}
๋ค์์ add ๋ฉ์๋๋ "/carts/add" ์๋ํฌ์ธํธ๋ก POST ์์ฒญ์ ๋ฐ๊ณ , ์์ฒญ ๋ฐ๋์ ๋ด๊ธด CartDTO ๋ฆฌ์คํธ๋ฅผ ๋ฐ์์ ์ฅ๋ฐ๊ตฌ๋์ ์ํ์ ์ถ๊ฐํ๋ ๊ธฐ๋ฅ์ ์ํํ๋ค.
์ฅ๋ฐ๊ตฌ๋ ์์ MOCK ๊ตฌํ
1. CartUpdateDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class CartUpdateDTO{
private int cartId;
private int quantity;
@Builder
public CartUpdateDTO(int cartId, int quantity) {
this.cartId = cartId;
this.quantity = quantity;
}
}
๋จผ์ request body์ ๋ง๊ฒ cartId์ quantity ํ๋๊ฐ ํ์ํ๊ธฐ ๋๋ฌธ์ CartUpdateDTO๋ฅผ ํตํด ๊ตฌํํ๋ค.
2. CartOptionDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class CartOptionDTO {
private int cartId;
private int optionId;
private String optionName;
private int quantity;
private int price;
@Builder
public CartOptionDTO(int cartId, int optionId, String optionName, int quantity, int price) {
this.cartId = cartId;
this.optionId = optionId;
this.optionName = optionName;
this.quantity = quantity;
this.price = price;
}
}
3. CartFinalDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter @Setter
public class CartFinalDTO{
private List<CartOptionDTO> carts;
private int totalPrice;
@Builder
public CartFinalDTO(List<CartOptionDTO> carts, int totalPrice) {
this.carts = carts;
this.totalPrice = totalPrice;
}
}
4. Controller
@PostMapping("/carts/update")
public ResponseEntity<?> updateCart(@RequestBody List<CartUpdateDTO> request) {
List<CartOptionDTO> cartOptionDTOList = new ArrayList<>();
CartOptionDTO cartOptionDTO1 = CartOptionDTO.builder()
.cartId(1)
.optionId(1)
.optionName("01. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค์๋์
4์ข
")
.quantity(10)
.price(100000)
.build();
cartOptionDTOList.add(cartOptionDTO1);
CartOptionDTO cartOptionDTO2 = CartOptionDTO.builder()
.cartId(2)
.optionId(2)
.optionName("02. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํ๋ผ์์๋์
5์ข
")
.quantity(10)
.price(109000)
.build();
cartOptionDTOList.add(cartOptionDTO2);
CartFinalDTO responseDTO = CartFinalDTO.builder()
.carts(cartOptionDTOList)
.totalPrice(209000)
.build();
return ResponseEntity.ok(ApiUtils.success(responseDTO));
}
"/carts/update" ์๋ํฌ์ธํธ๋ก POST request๋ฅผ ๋ฐ๊ณ , request body์ ๋ด๊ธด CartUpdateDTO ๋ฆฌ์คํธ๋ฅผ ๋ฐ์์ ์ฅ๋ฐ๊ตฌ๋๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ธฐ๋ฅ์ ์ํํ๋ค.
updateCart ๋ฉ์๋๋ @PostMapping ์ด๋ ธํ ์ด์ ์ ํตํด CartUpdateDTO ๊ฐ์ฒด ๋ฆฌ์คํธ๋ฅผ request ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋๋ค. ๊ทธ ํ ์ฅ๋ฐ๊ตฌ๋์ ์ ๋ฐ์ดํธ๋ฅผ ์๋ฎฌ๋ ์ด์ ํ๊ธฐ ์ํด CartOptionDTO ๊ฐ์ฒด๋ค์ ์์ฑํ๊ณ ๋ฆฌ์คํธ์ ์ถ๊ฐํ ๋ค ๊ฐ ์ ๋ณด๋ค์ ์ ๋ ฅ๋ฐ๊ณ ๋ง์ง๋ง์ผ๋ก CartOptionDTO๋ฅผ ์์ฑํ์ฌ ์ฅ๋ฐ๊ตฌ๋์ ์ต์ข ์ํ ๋ฐ ์ด ๊ฐ๊ฒฉ์ ๋ด๋๋ค.
์์ฝ) controller๋ CartUpdateDTO ๋ฆฌ์คํธ๋ฅผ request body๋ก ๋ฐ์ ์ฅ๋ฐ๊ตฌ๋๋ฅผ ์ ๋ฐ์ดํธํ๊ณ , ์ด๋ฅผ CartFinalDTO๋ก ๋ฐํํ๋ค.
๊ฒฐ์ ํ๊ธฐ - (์ฃผ๋ฌธ ์ธ์ํธ)
1. ProductDTO
import com.example.kakaoshop.cart.response.CartOptionDTO;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class ProductDTO {
private String productName;
private List<CartOptionDTO> items;
@Builder()
public ProductDTO( String productName, List<CartOptionDTO> items) {
this.productName = productName;
this.items = items;
}
}
2. OrderRespFindAllDTO
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class OrderRespFindAllDTO {
private int id;
private ProductDTO products;
private int totalPrice;
@Builder()
public OrderRespFindAllDTO(int id, ProductDTO products, int totalPrice) {
this.id = id;
this.products = products;
this.totalPrice = totalPrice;
}
}
3. Controller
import com.example.kakaoshop._core.utils.ApiUtils;
import com.example.kakaoshop.cart.response.CartOptionDTO;
import com.example.kakaoshop.order.response.OrderRespFindAllDTO;
import com.example.kakaoshop.order.response.ProductDTO;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class OrderRestController {
@GetMapping("/orders/{id}")
public ResponseEntity<?> findById(@PathVariable int id) {
List<CartOptionDTO> CartOptionDTOList = new ArrayList<>();
CartOptionDTO option1 = CartOptionDTO.builder()
.optionId(4)
.optionName("01. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค์๋์
4์ข
")
.quantity(10)
.price(100000)
.build();
CartOptionDTO option2 = CartOptionDTO.builder()
.optionId(5)
.optionName("02. ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํ๋ผ์์๋์
5์ข
")
.quantity(10)
.price(109000)
.build();
CartOptionDTOList.add(option1);
CartOptionDTOList.add(option2);
ProductDTO product1 = ProductDTO.builder()
.productName("๊ธฐ๋ณธ์ ์ฌ๋ผ์ด๋ฉ ์งํผ๋ฐฑ ํฌ๋ฆฌ์ค๋ง์ค/ํ๋ผ์์๋์
์๋์
์ธ ์ฃผ๋ฐฉ์ฉํ ํน๊ฐ์ ")
.items(CartOptionDTOList)
.build();
OrderRespFindAllDTO orderRespFindAllDTO = new OrderRespFindAllDTO(1, product1, 209000);
return ResponseEntity.ok(ApiUtils.success(orderRespFindAllDTO));
}
}
ps. ์ฒ์์ 2์ฃผ์ฐจ ๊ณผ์ ํ ๊ฒ์๊ธ์ ๋ด์ผ๋ ค๊ณ ์กฐ๊ธ์ฉ ์ฌ๋ฆฌ๊ณ ์์ ํ๊ณ ๋ฐ๋ณต ์ค์ด์๋๋ฐ ๋ด์ฉ์ด ๋๋ฌด ๋ง์์ ธ์ ๊ทธ๋ฐ์ง ๊ฐ์๊ธฐ ์ฐฝ์ด ๋ค ๊บผ์ง๋ ์ฌํ ๋ฐ์..
๊ทผ๋ฐ ์ ๊ธ์ฐ๊ธฐ ํ๋ฉด ๊ณ์ ์์์ ์ฅ์ด ๋ผ์ ๊ทธ๋์ ๊ฐ์๊ธฐ ์ธํฐ๋ท ๊บผ์ง๋ ์ํฉ์ด์ด๋ ๊ด์ฐฎ์์๋๋ฐ ์ค๋์ ์์ ์ผ๋ก ๊ธ์ฐ๋๊น ์์์ ์ฅ ๊ทธ๋ฐ๊ฑฐ ์์ ..... 3์๊ฐ๋์ ๋์๊ด์์ ์ด ๊ธ ๋ค ๋ ๋ผ๊ฐ์ ๋ด ๋ฉํ๋ ํจ๊ป ๋ ์๊ฐ ๊ฒ์๊ธ์ด๋ค ใ ใ ใ
์์ผ๋ก๋ ๊ผญ ๋์ด์ ์จ์ผ์ง ํํ
'๐ช kakaotech campus' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์นดํ ์ผ | [STEP2 cloncoding] 4์ฃผ์ฐจ ๊ณผ์ - (1) (0) | 2023.07.19 |
---|---|
์นดํ ์ผ | [STEP2 clone coding] 3์ฃผ์ฐจ ๊ณผ์ (0) | 2023.07.14 |
์นดํ ์ผ | [STEP2 clone coding] 2์ฃผ์ฐจ ๊ณผ์ - (1) (0) | 2023.07.04 |
์นดํ ์ผ | [STEP2 clone coding] 1์ฃผ์ฐจ ๊ณผ์ - (3) (0) | 2023.06.30 |
์นดํ ์ผ | [STEP2 clone coding] 1์ฃผ์ฐจ ๊ณผ์ - (2) (0) | 2023.06.29 |