DevLog ๐ถ
[์ฐํ ์ฝ 5๊ธฐ] ์ฅ๋ฐ๊ตฌ๋ ๋ฏธ์ ํ๊ณ ๋ณธ๋ฌธ
[์ฐํ ์ฝ 5๊ธฐ] ์ฅ๋ฐ๊ตฌ๋ ๋ฏธ์ ํ๊ณ
dolmeng2 2023. 5. 15. 16:24ํํฌ์ ์งํํ ๋ ๋ฒจ 2 ๋ ๋ฒ์งธ ํ์ด ํ๋ก๊ทธ๋๋ฐ ๋ฏธ์ ์ธ ์ฅ๋ฐ๊ตฌ๋ ๋ฏธ์ ์ด๋ค!
์ด์ฉ๋ค ๋ณด๋ ๋ ๋ฒจ 1 ๋ฐ์ผ๋ฆฌ ์กฐ ํ์๋ค๊ณผ ํ ๋ฒ์ฉ ํ์ด ํ๋ก๊ทธ๋๋ฐ์ ํ๋ ๋๋์ด๋ค ใ _ใ ์ฌ๋ฐ๋ค!
์ด๋ฒ ๋ฏธ์ ์์๋ ์๊ตฌ์ฌํญ๋ณด๋ค๋ ๊ฐ์ธ์ ์ผ๋ก ์งํํ๊ณ ์ถ์ ๋ถ๋ถ๋ค์ ๋ํด ๊ตฌํํ๋ค ๋ณด๋ ๋๊ฒ ์ฌ๋ฐ๊ฒ ๊ตฌํํ์๋ค.
ํ์ง๋ง... ์ง๊ธ ์์ ์ฝ๋๋ฅผ ๋ณด๋ ์์ง ๋ง์ด ๋ถ์กฑํ๋ค๋ ์๊ฐ์ด ๋ค์๋ค ๐ฅฒ... ์ด๋ ต๋ค!
โ๏ธ ์์ฑํ ์ฝ๋
โ๏ธ 1์ฐจ PR
โ๏ธ 2์ฐจ PR
โ๏ธ ๊ธฐ๋ฅ ์๊ตฌ์ฌํญ
- ์ํ ๋ชฉ๋ก ํ์ด์ง ์ฐ๋ํ๊ธฐ (Thymeleaf)
- ์ํ ๊ด๋ฆฌ CRUD API ์์ฑํ๊ธฐ
- ๊ด๋ฆฌ์ ๋๊ตฌ ํ์ด์ง ์ฐ๋ํ๊ธฐ
- ์ํ ๋๋ฉ์ธ ์ค๊ณ (์์ ๋กญ๊ฒ, ID, ์ด๋ฆ, ์ด๋ฏธ์ง URL, ๊ฐ๊ฒฉ์ ๊ณ ์ )
- ์ฌ์ฉ์ ๋๋ฉ์ธ ์ค๊ณ (์์ ๋กญ๊ฒ, ์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ๋ ๊ณ ์ )
- ์ฌ์ฉ์ ์ค์ ํ์ด์ง ์ฐ๋
- ์ฅ๋ฐ๊ตฌ๋ ๊ธฐ๋ฅ ๊ตฌํ (์ํ ์ถ๊ฐ, ์ ๊ฑฐ, ๋ชฉ๋ก ์กฐํ, Basic ์ธ์ฆ ๋ฐฉ์)
๋๋ ์ฌ๊ธฐ๋ค๊ฐ ์ฌ์ฉ์ ์ถ๊ฐ, ์ํ ์นดํ ๊ณ ๋ฆฌ ๊ธฐ๋ฅ, ๊ด๋ฆฌ์ ๊ธฐ๋ฅ, api ๋ฌธ์๊น์ง ์ ์ฉํด๋์๋ค.
rest-docs ๊ด๋ จํด์๋ ์ถ๊ฐ์ ์ผ๋ก ๊ธ์ ์์ฑํ ์์ ์ด๋ค ใ _ใ
๋ ๊ทธ๋ฌ๋ฏ์ด ๊ณ ๋ฏผํ๋ ๋ถ๋ถ๊ณผ ๋๋ฆ์ ์๋ฃจ์ ์ ๋ํด์ ์์ฑํด๋ณด๊ณ ์ ํ๋ค.
๐ฌ Redirect๊ฐ ๋ฌด์์ผ๊น?
์ฌ์ค ์ด๊ฑฐ๋... ๋ด๊ฐ ๋ฆฌ๋ค์ด๋ ํธ์ ๋ํ ๊ฐ๋ ์ ์ ๋๋ก ์ก์ง ๋ชปํด์ ๋ฐ์ํ ๋ฌธ์ ์๋ค ๐ฅฒ
@GetMapping
public String getProducts(final Model model) {
final List<ProductDto> products = productService.getProducts();
model.addAttribute("products", products);
return "admin";
}
์์ ๊ฐ์ ์ฝ๋์์ ๋ทฐ ์ด๋ฆ์ ๋ฐํํ๋ ๊ฒ์ ๋ฆฌ๋ค์ด๋ ํธ๋ก ๋ด์ผ ํ๋๊ณ ์ง๋ฌธ์ ๋๋ ธ์๋ค.
๋ฆฌ๋ค์ด๋ ํธ๋ ์์ฒญํ ๋ฆฌ์์ค๊ฐ ๋ค๋ฅธ ์์น์ ์์ผ๋, ํด๋น ๋ฆฌ์์ค ์์น๋ฅผ ๋ฐํํ ๋ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
-> ํด๋น ๋ฆฌ์์ค์ ์ฃผ์๋ก ๋ค์ ์์ฒญ์ ๋ ๋ฆฌ๋ ๊ฒ๊ณผ ๋์ผํ๋ค.
์์ ๊ฐ์ด ๋ทฐ ์ด๋ฆ์ ๋ฐํํ๋ ๊ฒ์ ๋จ์ํ ์์ฒญ์ ๋ํ ๋ ๋๋ง์ ํด์ฃผ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋ฆฌ๋ค์ด๋ ํธ๋ก ๋ณด๊ธฐ๋ ์ด๋ ต๋ค.
์ด๊ฑฐ๋ ์ถ๊ฐ์ ์ผ๋ก JS ์ชฝ ์ฝ๋์ธ๋ฐ... ๊ด๋ จ์ ์์ง๋ง ์๋กญ๊ฒ ์ ๊ฑฐ์ฌ์ ์ ๋ฆฌํด๋๋ค.
location.href | location.replace | |
๊ธฐ๋ฅ | ์๋ก์ด ํ์ด์ง๋ก ์ด๋ | ๊ธฐ์กด ํ์ด์ง๋ฅผ ์๋ก์ด ํ์ด์ง๋ก ๋ณ๊ฒฝ |
ํํ | ์์ฑ | ๋ฉ์๋ |
์ฃผ์ ํ์คํ ๋ฆฌ | ๊ธฐ๋ก๋๋ค (๋ค๋ก ๊ฐ๊ธฐ ๊ฐ๋ฅ) | ๊ธฐ๋ก๋์ง ์๋๋ค |
์์ | location.href="" | location.replace("") |
๐ฌ ์ ํจ์ฑ ๊ฒ์ฌ๋ ์ด๋์์ ํด์ผ ํ๋๊ฐ? - ๋๋ฉ์ธ vs DTO
public class ProductDto {
@Length(min = 1, max = 25, message = "์ํ ์ด๋ฆ์ ๊ธธ์ด๋ {min} ~ {max}๊ธ์์ฌ์ผ ํฉ๋๋ค.")
private final String name;
private final String imageUrl;
@NotNull(message = "์ํ ๊ฐ๊ฒฉ์ ๋น์ด์์ ์ ์์ต๋๋ค.")
@Range(min = 0, max = 10_000_000, message = "์ํ ๊ฐ๊ฒฉ์ {min} ~ {max}์๊น์ง ๊ฐ๋ฅํฉ๋๋ค.")
private final Integer price;
@NotNull(message = "์ํ ์นดํ
๊ณ ๋ฆฌ๋ ๋น์ด์์ ์ ์์ต๋๋ค.")
private final ProductCategory category;
}
์ฒ์ ์ฝ๋๋ฅผ ์์ฑํ์ ๋๋ ์์ ๊ฐ์ด DTO์์ ์ด๋ฆ, ๊ฐ๊ฒฉ์ ๋ํ ์ธ๋ถ ์กฐ๊ฑด๋ค๋ ์ ์ดํ ์ ์๋๋ก validation ์ด๋ ธํ ์ด์ ์ ํ์ฉํ์์๋ค.
๐ฑ
์ ๋ ๊ฐ์ธ์ ์ผ๋ก ์ ํจ์ฑ ๊ฒ์ฌ๋ ์ฌ๋ฌ๊ฐ์ง ์ข ๋ฅ๊ฐ ์๋ค๊ณ ์๊ฐํ๋๋ฐ์.
1. ๋น์ฆ๋์ค์ ๊ด๋ จ๋ ์ ํจ์ฑ ๊ฒ์ฌ
2. ๋จ์ ์ ํจ์ฑ ๊ฒ์ฌ
์๋ ๊ฒ ๋๋์ด์ ์ฒ๋ฆฌํ ์ ์๋ ๋ ์ด์ด๊ฐ ์๋ค๊ณ ์๊ฐํด์!
ํ์ง๋ง ๋ฆฌ๋ทฐ์ด๋์ ๋ค์๊ณผ ๊ฐ์ด ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ํ๋ ๋ถ๋ถ์ ๋๋ ๋ณด๋ ๊ฒ ์ด๋ ๋๊ณ ์ด์ผ๊ธฐ๋ฅผ ํด์ฃผ์ จ๋ค.
๋น์์๋ validation annotation์ ๊ต์ฅํ ์ง์ฐฉ(?)์ ํ๊ณ ์์์ด์, ๋๋ฉ์ธ์์ @Range, @Length ๊ฐ์ ์ด๋ ธํ ์ด์ ์ ํ์ฉํ์ฌ ๊ฒ์ฆํ๋ฉด ๋๋ฉ์ธ์์ ๋ทฐ์ ์ถ๋ ฅํ ๋ฌธ๊ตฌ๋ฅผ ์์กดํ๋ ํํ๊ฐ ๋ ๊ฒ ๊ฐ์์ ์ ๋ป ๊ทธ๋ ๊ฒ ํ๊ธฐ๊ฐ ์ด๋ ค์ ๋ค. (์ง๋๋ฒ ๋ฏธ์ ๋ ์ด ๋ถ๋ถ์ ๋ํด์ ํผ๋๋ฐฑ์ ๋ค์์ด์ ๋ ์กฐ์ฌ์ค๋ฌ์ ๋ ๊ฒ๋ ์๋ค ใ ใ )
ํ์ง๋ง...! ์๊ฐํด๋ณด๋ฉด ๊ตณ์ด validation annotation์ ์ฌ์ฉํ ํ์๋ ์์๋ค.
๋ ๋ฒจ 1 ๋ฏธ์ ์์๋ ๊ทธ๋ฅ ์๋ฐ ์ฝ๋๋ก๋ ์ถฉ๋ถํ ๊ฒ์ฆ์ ํ์๊ณ , ๋ง์ฝ ์๋ก์ด ๊ฐ๋ฐ์๊ฐ ์์ '์ํ ๋๋ฉ์ธ์ ํ์ํ ํ๋๋ค์ ์ ์ฝ ์ฌํญ์ ์ด๋์์ ๊ด๋ฆฌ๋์ง?'๋ผ๊ณ ์๊ฐํ์ ๋ DTO๋ณด๋ค๋ ๋๋ฉ์ธ ํด๋์ค๋ฅผ ๋จผ์ ์ด์ด๋ณผ ๊ฒ ๊ฐ์๋ค.
๋ํ, ์ง๊ธ์ SSR์ด๊ธฐ ๋๋ฌธ์ ๋ฉ์์ง ์์ฒด๊ฐ ๋ฐ๋ก ๋ทฐ๋ก ๋ ธ์ถ๋์ง๋ง, CSR ์ํฉ์ด๋ผ๋ฉด ์ฌ๊ธฐ์ ์ ํ๋๋ ๋ฉ์์ง๋ ์ค์ฌ์ฉ์๊ฐ ์๋ ์๋ ๊ฐ๋ฐ์, ์ฆ ํด๋ผ์ด์ธํธ๊ฐ ํ์ธํ๋ ๋ฉ์์ง์ด๊ธฐ ๋๋ฌธ์ '๋๋ฉ์ธ์ด ๋ทฐ์ ์ถ๋ ฅ๋ ๋ฉ์์ง๋ฅผ ๊ด๋ฆฌํ๋ค'๋ผ๋ ๊ด์ ๊ณผ๋ ์กฐ๊ธ ๋จ์ด์ ธ ์๋ค๊ณ ๋ด๋ ๋๋ค.
๋ฌด์๋ณด๋ค ๋๋ฉ์ธ ๊ฐ์ฒด๋ POJO๋ก ๊ด๋ฆฌ๋๋ ๊ฒ ๋ ์ข์ ๊ฒ ๊ฐ์์, ์๋์ ๊ฐ์ด ๊ทธ๋ฅ ์๋ฐ ์ฝ๋๋ฅผ ํตํด ๊ฒ์ฆ์ ์งํํด์ฃผ์๋ค.
public class ProductName {
private static final int NAME_MIN_LENGTH = 1, NAME_MAX_LENGTH = 25;
private final String name;
...
private static void validateNameLength(final String name) {
if (name.length() < NAME_MIN_LENGTH || name.length() > NAME_MAX_LENGTH) {
throw new GlobalException(PRODUCT_NAME_LENGTH);
}
}
}
๊ทธ๋ฆฌ๊ณ DTO ์ชฝ์๋ ๋จ์ํ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์งํํด์ฃผ์๋ค.
public class ProductRequest {
@NotBlank(message = "์ํ ์ด๋ฆ์ ๋น์ด์์ ์ ์์ต๋๋ค.")
private final String name;
@NotNull(message = "์ํ ๊ฐ๊ฒฉ์ ๋น์ด์์ ์ ์์ต๋๋ค.")
private final Integer price;
@NotNull(message = "์ํ ์นดํ
๊ณ ๋ฆฌ๋ ๋น์ด์์ ์ ์์ต๋๋ค.")
private final String category;
...
}
์์ผ๋ก๋ ๋ค์๊ณผ ๊ฐ์ ๊ธฐ์ค์ ํตํด์ ์ ํจ์ฑ ๊ฒ์ฆ์ ํด๋ณด๊ณ ์ ํ๋ค.
- DTO : ๋ฐ์ดํฐ ํ์ ์ ๋ํ ๊ฒ์ฆ
ex.
์ซ์๊ฐ์ด ์ฌ๋ฐ๋ฅด๊ฒ ๋ค์ด์ค๋๊ฐ?
๋น ๊ฐ์ด ๋ค์ด์ค๋๊ฐ?
์ ํ๋ฒํธ ํํ (-๋ก ๊ตฌ๋ถ๋ 13๊ธ์)์ธ๊ฐ?
์ด๋ฉ์ผ ํํ (@๋ก ๊ตฌ๋ถ๋ ํํ)์ธ๊ฐ?
- ๋๋ฉ์ธ: ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ์ ๋ํ ๊ฒ์ฆ
ex.
๊ฐ๊ฒฉ์ 0์ ~ 10000์ ์ดํ์ ๊ฐ์ ๊ฐ์ง๋๊ฐ?
์ด๋ฆ์ ๊ธธ์ด๋ ์ต์ 1๊ธ์ ์ด์์ธ๊ฐ?
์ด๋ฉ์ผ์ ๋๋ฉ์ธ์ด ํน์ ๋๋ฉ์ธ๋ง ์ฌ์ฉํ๋๊ฐ? (gmail, daum, naver... etc)
๐ฌ Dto <-> Entity ๋ณํ ๋ก์ง์ ์ด๋์ ์์ผ๋ฉด ์ข์๊น?
public class ProductDto {
public Product toEntity() {
return new Product(name, imageUrl, price, category);
}
public static ProductDto fromEntity(final Product product) {
return new ProductDto(product.getId(), product.getName(), product.getImageUrl(),
product.getPrice(), product.getCategory());
}
}
์ฌ์ค ์ด๊ฑฐ๋ ์ ๋ง ๋ฆฌ๋ทฐ์ด๋๋ง๋ค ์ ๋ง ๋ง์ด ๊ฐ๋ฆฌ๋ ๋ถ๋ถ์ธ ๊ฒ ๊ฐ๋ค.
๋ด ๋ฆฌ๋ทฐ์ด๋ ๊ฐ์ ๊ฒฝ์ฐ๋ ์์กด๊ด๊ณ๊ฐ Controller -> Dto -> Entity์ ๊ฐ์ ํํ๋ก ํ๋ฅด๊ฒ ๋๋ค๋ฉด ์ปจํธ๋กค๋ฌ์์ ํด๋น Dto ํด๋์ค๋ฅผ ํตํด์ ๋๋ฉ์ธ์ ๋ํด ์ ๊ทผํ ์ ์๋ ์ฌ์ง๊ฐ ์๊ธฐ๋ ๊ฒ ์๋๋๊ณ ๋ง์ํด ์ฃผ์ จ๋ค.
๋ฌผ๋ก , ๊ฐ๋ฐ์์ ์ดํด๋๊ฐ ๋๋ค๋ฉด Dto์์ Entity๋ก ๋ณํํ์ฌ ์ปจํธ๋กค๋ฌ์์ ์ฝ์ดํ ๋๋ฉ์ธ ๊ณ์ธต์ ๊ฐ์ฒด๋ฅผ ์นจ๋ฒํ๋ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ์๊ฒ ์ง๋ง, ํด๋ฆฐ ์ํคํ ์ฒ์ ์์์ '~ํ ์ํ์ด ์์ง ์์๊น?'๋ก ๋ถํฐ ์์๋๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์ํ์ฑ์ ๋์ด์ฃผ๋ ๊ฒ ์ณ๋ค๊ณ ์๊ฐํ๋ค.
๊ทธ๋์ ๋ฆฌํฉํฐ๋ง ์์๋ service layer์์ private method๋ฅผ ํตํด ์์ ๊ฐ์ ๋ณํ ๋ก์ง์ ์ฒ๋ฆฌํ๋๋ก ๋ง๋ค์๊ณ , ์ถํ ๋งคํํ๋ ํด๋์ค๋ค์ด ๋ง์์ง๋ค๋ฉด ๋ณ๋์ mapper class๋ฅผ ๋์ด์ ์ฒ๋ฆฌํ๋ ๊ฒ์ผ๋ก ์๊ฐํ์๋ค. (๋งคํผ ํด๋์ค ์ญ์ service์ ๊ฐ์ ๊ณณ์ ์์น)
+) ์์กด ๊ด๊ณ๋ฅผ ๋จ๋ฐฉํฅ์ผ๋ก ๊ฐ์ ธ๊ฐ๋ ค๋ฉด dto๊ฐ ์กด์ฌํด์ผ ํ๋ ํจํค์ง๋ service ์ชฝ์ด์ด์ผ ํ๋ค ๐
์๋ฌด ์๊ฐ ์์ด controller ์ชฝ์ ๋์๋ค๊ฐ controller -> service -> dto ๊ฐ์ ์์ํ ํ๋ฆ ๊ด๊ณ๊ฐ ์๊ฒจ๋ฒ๋ ธ์๋ค... ใ _ใ ์ ์ํ์!
๐ฌ ์ํฐํฐ์ ๋๋ฉ์ธ
์๋ง ์ด๋ฒ ๋ฏธ์ ์์ ๋๋ฅผ ๊ฐ์ฅ ๋ง์ด ๊ดด๋กญํ๋ ์ฃผ์ ์ธ ๊ฒ ๊ฐ๋ค ๐ฅฒ (์ฌ์ ํ ๊ฒฐ๋ก ์ ํ์คํ๊ฒ ๋ด๋ฆฌ์ง ๋ชปํ ์ํ์ด๋ค)
์ฌ์ค ์ฒ์ ํ์ด ํ๋ก๊ทธ๋๋ฐ์ ํ ๋๋ ์ํฐํฐ์ ๋๋ฉ์ธ์ ๊ตฌ๋ถํ์ง ์๊ณ , ๊ทธ๋ฅ ์ํฐํฐ ์์ฒด๋ฅผ ๋๋ฉ์ธ์ด๋ผ ์๊ฐํ๊ณ ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
๋ฏธ์ ์๊ตฌ์ฌํญ์ด CRUD์ด๋ค ๋ณด๋๊น DB์ ๊ต์ฅํ ๋ฐ์ ํด์ง๊ณ , ํ ์ด๋ธ์ ๋ด์ฉ์ ์ฝ์ ํ๊ฑฐ๋, ์กฐํํ๊ฑฐ๋ ํ๋ ๋ก์ง๋ง ์๋ค ๋ณด๋ ๋ณ๋์ ๋น์ฆ๋์ค ๋ก์ง์ด ์๋ค๊ณ ์๊ฐ๋์ด ๋ณ๋๋ก ๋ ํ์์ฑ์ ๋๋ผ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ด๋ค. ๋ํ, ์ํฐํฐ์ ๊ฒฝ์ฐ DB์ 1:1๋ก ๊ฐ์ ๋งคํํ๊ณ , ๋ณ๋์ ๋ก์ง ์์ด ๋จ์ํ ๊ฐ์ ๋ฐํํ๋ ์ญํ ๋ง ํด์ผ ํ๋ค๊ณ ์๊ฐํ์๋ค.
ํ์ง๋ง, ์์์ ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ์ ๋ํ ๊ฒ์ฆ์ ์งํํ๊ธฐ ์ํด์๋ ๋๋ฉ์ธ ํด๋์ค๊ฐ ํ์ํ๋ค.
์ํฐํฐ์์ ๊ฒ์ฆ์ ์งํํ๊ธฐ์๋, ์ํฐํฐ๊ฐ ๋น์ฆ๋์ค ๋ก์ง์ ๊ฐ์ง๋ ๊ฒ ๊ฐ์์ ํ๊ณ ์ถ์ง ์์๊ณ , ๊ทธ๋ ๋ค๊ณ ๋๋ฉ์ธ์ ๋ง๋ค๊ธฐ์๋ ๋จ์ํ ๊ฒ์ฆ๋ฐ์ ์ ํ๊ธฐ ๋๋ฌธ์ ๊ตณ์ด ๋ง๋ค์ด์ผ ํ๋...? ๋ผ๋ ์๊ฐ๋ ๋ค์๊ธฐ ๋๋ฌธ์ด๋ค... ๐ฅน (์ฌ๊ธฐ์ ๋จธ๋ฆฌ ํฐ์ง)
ํ์ง๋ง, ๋ฆฌ๋ทฐ์ด๋์ด ๋ค์๊ณผ ๊ฐ์ ํผ๋๋ฐฑ์ ๋จ๊ฒจ ์ฃผ์ จ๋ค.
๐ฑ ์ ๋ ๋๋ฉ์ธ์ ํญ์ ์์ด์ผ ํ๋ค๊ณ ์๊ฐํด์.
์๋ก์ด ๊ฐ๋ฐ์๊ฐ ๋๋ฉ์ธ์ ๋ํ ์๊ตฌ์ฌํญ์ ํ์ธํ๊ธฐ ์ํด์๋ ์ด๋ค ํด๋์ค๋ฅผ ํ์ธํ ๊น์?
์ด ๋ง์ ๋ฃ๊ณ ๋๋ฆ๋๋ก ๋๋ฉ์ธ๊ณผ ์ํฐํฐ์ ๋ํ ์ ์๋ฅผ ๋ด๋ ค ์๋์ ๊ฐ์ด ๋ต๋ณ์ ํ๋ค.
๋ง์ด ๊ธธ์ง๋ง ์์ฝํ์๋ฉด, ๋น์ฆ๋์ค ๋ก์ง์ ํํํ๊ธฐ ์ํ ๊ฐ์ฒด๋ ๋๋ฉ์ธ ๊ฐ์ฒด๋ฉฐ, ์์์ฑ ๋ ์ด์ด์ ๊ฐ๊ฒฐํฉ์ ๋งบ์, ๋๋ฉ์ธ ๊ฐ์ฒด๋ฅผ ํํํ๊ธฐ ์ํ ๋ฐ์ดํฐ๋ค์ ์ ์ฅํ๋ ๊ฐ์ฒด๋ ์ํฐํฐ๋ผ๊ณ ์๊ฐํ๋ค. ๊ทธ๋์ ๋๋ฉ์ธ / ์ํฐํฐ๋ฅผ ๋ฐ๋ก ๋ถ๋ฆฌํด์ ์ฝ๋๋ฅผ ์์ฑํ์๋ค.
ํ์ง๋ง, ์ด๋ฌํ ์ ์์์๋ ๊ฒฐ๊ตญ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์๋ฐ์ ์์๋ค.
๋ฐ๋ก... Join์ ํตํด์ ์กฐํํด์ฌ ๋๋ ์ด๋ป๊ฒ ์ํฐํฐ๋ก ์ฒ๋ฆฌํด์ผ ํ๋์ง ๊ฒฐ์ ํ ์ ์๋ ๊ฒ์ด์๋ค.
๊ธฐ์กด์๋ ์กฐ์ธ์ ์งํํ๋ ์ฃผ ํ ์ด๋ธ์ ๋ํ dao (CartDao)์์ ์๋์ ๊ฐ์ด ์ฝ๋๋ฅผ ์์ฑํ์๊ณ , MemberProductEntity๋ผ๋ ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ ์กฐํํด์จ ๊ฒฐ๊ณผ๋ฅผ ๋ด์์ค๋๋ก ๋ง๋ค์๋ค.
public class MemberProductEntity {
private final Long cartId;
private final Long memberId;
private final Long productId;
private final String productName;
private final String productImageUrl;
private final int productPrice;
private final String productCategory;
...
}
public List<MemberProductEntity> getProductByMemberId(final Long memberId) {
final String query = "SELECT c.id, c.member_id, c.product_id, p.name, p.image_url, p.price, p.category " +
"FROM cart c JOIN product p ON c.product_id = p.id WHERE c.member_id = ?";
return jdbcTemplate.query(query, (result, count) ->
new MemberProductEntity(result.getLong("id"), result.getLong("member_id"),
result.getLong("product_id"), result.getString("name"),
result.getString("image_url"), result.getInt("price"),
result.getString("category")), memberId);
}
-> 1์ค ์์ฝ: SRP ์๋ฐ ์๋๊น์? + ์ํฐํฐ๋ฅผ ์ด๋ฐ ์์ผ๋ก ์จ๋ ๋๋์? + ๊ฒฐ๊ตญ ๋๋ฉ์ธ๊ณผ ์ํฐํฐ์ ์ฐจ์ด์ ์ด ๋ญ์ฃ ? (๋ค์ ์์ )
๋น์์๋ ์ํฐํฐ๋ ๋๋ฉ์ธ์ผ๋ก์ ์ญํ ์ ํ ์ ์์ง๋ง, ์ ๋ณด ์ ๋ฌ์ ๊ฐ๊น์ด ๊ฐ์ฒด๋ผ๋ฉด ๊ฐ๋ฅํ๋ค๊ณ ์๊ฐํด์ ์ ๋ฐ ๊ตฌ์กฐ๋ก ํ๋ค.
๊ฐ ๋ฆฌ๋ทฐ์ด๋์ ์ด๊ฑธ ๋ณด๊ณ ์๋์ ๊ฐ์ด ํผ๋๋ฐฑ์ ๋จ๊ฒจ์ฃผ์ จ๋ค.
๐ฑ ๋๋ฉ์ธ: ์ฐ๋ฆฌ๊ฐ ํด๊ฒฐํ๊ณ ์ ํ๋ ๋ฌธ์ ์์ญ์ด์, ๋ฌธ์ ์์ญ์ ํด๋์คํ ํ์ฌ ์ฝ๋๋ก ์ฎ๊ธด ๋ถ๋ถ
๐ฑ ์ํฐํฐ: ์๋ณ์๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฐ์ฒด
์ ์ ์๋๋ก๋ผ๋ฉด ๋๋ฉ์ธ์ด ๋ ํฐ ๋ฒ์์ด๋ฉด์, ์ํฐํฐ๊ฐ ๊ทธ ์ผ๋ถ๋ถ์ผ๋ก ๋ณด์ผ ํ ๋ฐ, ๋ง์ต๋๋ค!
๋๋ฉ์ธ์๋ VO, Entity, Domain Service ๋ฑ ๋ง์ ๊ฒ๋ค์ด ํฌํจ๋์ด ์์ผ๋ฉฐ, ๋ฌธ์ ์์ญ์ ํด๋์คํ ํ ๊ฒ์ด๋ผ๋ฉด ๋ชจ๋ ๋๋ฉ์ธ์ผ๋ก ๋ฐ๋ผ๋ณธ๋ค๊ณ ํ์ จ๋ค. (์ด์ ๋ํด์ ์ค๋ ํ๋ธ๋๋ ์ด์ผ๊ธฐ๋ฅผ ๋๋ด์๋๋ฐ, ํ๋ธ์ ๋ฆฌ๋ทฐ์ด๋๋ ๋น์ฆ๋์ค ๋ก์ง์ ๋ํ Service Layer๋ฅผ ๋๋ฉ์ธ ์ชฝ์ผ๋ก ๋ฐ๋ผ๋ณธ๋ค๊ณ ๋ง์ํด ์ฃผ์ จ๋ค.) ๊ทธ๋ฆฌ๊ณ , ๊ทธ ์ค์์ ์๋ณ์๊ฐ ์๋ค๋ฉด ์ํฐํฐ๊ฐ ๋๋ค.
์ฌ๊ธฐ์ ์๋ณ์๋ ๋น์ฆ๋์ค ์์ผ๋ก ๋ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ๋ถ๋ฆฌํ ์ ์๋ ๊ณ ์ ํ ๊ธฐ์ค์ด๋ค.
DB์ pk ๊ฐ์ด๋ผ๊ณ ์๊ฐํ ์ ์๊ฒ ์ง๋ง, PK๊ฐ ๋ ์๋ ์๊ณ ๋ค๋ฅธ ํ๋ ๊ฐ์ด ๋ ์๋ ์๋ค.
์ด๋ ํ ๊ฐ์ฒด๋ฅผ ๊ตฌ๋ถํ๋ ๊ธฐ์ค์ด๊ธฐ ๋๋ฌธ์, ๋ง์ฝ ๋น์ฆ๋์ค ๋ก์ง ์ '์ด๋ฆ'์ด๋ผ๋ ํ๋๊ฐ ๊ณ ์ ํ๊ฒ ์กด์ฌํ๋ค๋ฉด ํด๋น ๊ฐ์ฒด๋ '์ด๋ฆ'์ด๋ผ๋ ์๋ณ์๋ฅผ ๊ฐ์ง ์ํฐํฐ๊ฐ ๋ ์๋ ์๋ ๊ฒ์ด๋ค. ๋ณ๋์ ์๋ณ์๊ฐ ์๋ ๋๋ฉ์ธ์ ๊ณง ์ํฐํฐ๋ผ๊ณ ๋งํ ์ ์์ผ๋ฉฐ, ์ด ๋จ๊ณ์์๋ DB์ ๋ํ ์ ๋ณด๋ฅผ ๊ณ ๋ คํ์ง ์๊ฒ ๋๋ค.
๋งํด ํ์ธ๋ฌ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ฐ ๊ฐ๋ ๋ค์ ์ ์ํ์๋ค.
- Entity: Objects that have a distinct identity that runs through time and different representations. You also hear these called "reference objects".
- Value Object: Objects that matter only as the combination of their attributes. Two value objects with the same values for all their attributes are considered equal. I also describe value objects in P of EAA.
- Service: A standalone operation within the context of your domain. A Service Object collects one or more services into an object. Typically you will have only one instance of each service object type within your execution context.
์ด์ ๋ฏธ์ ์ ํ๋ฉด์ Reference Object์ Value Object์ ๋ํด์ ๊ณต๋ถํ ์ ์ด ์์๋๋ฐ ์ฌ๊ธฐ์ ์ํฐํฐ๋ฅผ Reference Object๋ก์ ์๊ฐํ๋ค๋ ์ ์ ๋ณด๊ณ ํ์คํ๊ฒ ์ดํดํ๊ฒ ๋์๋ค. ์๋ณ์๋ก ์ํ๊ฐ ๋ณํ๋ ๊ฐ์ฒด๊ฐ ๋๋ ๊ฒ. ์ฐ์ ๋๋ ์ด ์ ๋๋ก ์ดํด๋ฅผ ํ๋ค. ๊ทธ๋์ ๋ณดํต ์ํฐํฐ๋ ์๋ณ์๋ก ๋์ผ์ฑ์ ์ ์ํ ์ ์๊ธฐ ๋๋ฌธ์ equals, hashcode๋ฅผ ์ผ๋ฐ์ ์ผ๋ก ์ ์ํ์ง ์๋๋ค.
์๋ฌดํผ, ๋ค์ ๋์์์... ์ํฐํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ํด๋นํ๋ ๊ฐ์ฒด๋ก ์์ฃผ ์ฌ์ฉํ๋ ์ด์ ๋ ๋ณดํต PK๋ฅผ ์ด์ฉํด ์๋ณ์ด ๋๋ Reference Object์ด๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ์ด๋ฒ ๋ ๋ฒจ์์๋ DB์ PK๋ฅผ ์๋ณ์๋ก ์ฌ์ฉํ๋ ์ํฐํฐ๋ฅผ ๋ณ๋๋ก ๋์๋ค.
๊ทธ๋ฆฌ๊ณ , ์ฅ๋ฐ๊ตฌ๋์ ๋ํ CUD ๋ก์ง์ ๋ค์๊ณผ ๊ฐ์ด ํ์ด๋๊ฐ๋ค.
- '์ฅ๋ฐ๊ตฌ๋ (Cart)' ๋ผ๋ ๋๋ฉ์ธ์ Member์ List<Product>์ ์กฐํฉ์ผ๋ก ๋ง๋ค๊ธฐ.
: ํ ๋ช ์ ์ฌ์ฉ์๊ฐ ๋ด์ ๋ฌผ๊ฑด ๋ฆฌ์คํธ๋ฅผ ๊ด๋ฆฌํ๋ ๋๋ฉ์ธ ๊ฐ์ฒด.
- ์ฅ๋ฐ๊ตฌ๋ ์ํ ์ ์ฅ, ์ ๊ฑฐ ์ ์๋ณ์๋ฅผ ๊ฐ์ง ์ํฐํฐ์ธ CartEntity๋ฅผ ํ์ฉํ๋ฉด ๋ฐ๋ก insert๊ฐ ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ (๋ณธ ๋น์ฆ๋์ค ๋ก์ง์ DB์ ๋ํ ์ฐ์ฐ์ด ์์ฃผ์) ๋ณ๋์ ๋๋ฉ์ธ ๊ฐ์ฒด๋ก ํํํ ํ์๊ฐ ์์ ๊ฒ ๊ฐ๋ค๊ณ ํ๋จ. (์ด์ฐจํผ ์ํฐํฐ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฒ์๋ก ๋ณธ๋ค๋ฉด ๋๋ฉ์ธ ๊ฐ์ฒด์ ๋ค์ด๊ฐ ์ ์๋ค.)
- ์ฅ๋ฐ๊ตฌ๋ ์ํ ์ ๋ณด ๋ฐํ ์ ์กฐ์ธ์ ์ฌ์ฉํ๋ค ๋ณด๋ Entity๋ณด๋ค๋ DTO๋ก ์ฌ์ฉ (์ฌ๋ฌ ๊ฐ๋ค์ ์กฐํฉํด์ ์ฌ์ฉํ๊ณ ์์)
๋ง์ด ๊ธธ์ง๋ง ๊ฒฐ๊ณผ์ ์ผ๋ก ์ํฐํฐ์ ๋๋ฉ์ธ์ ๋ถ๋ฆฌํ๋ค.
๋์ถฉ ํ ๊ฐ์ง ์์ ์ฝ๋๋ฅผ ๋ณด์ฌ์ฃผ์๋ฉด ์ด๋ฐ ์์ด์๋ค.
public CartResponse getCartResponseByMemberEmail(final String memberEmail) {
final MemberEntity memberEntity = getMemberEntity(memberEmail);
final List<CartDto> cartDtos = cartDao.getProductsByMemberId(memberEntity.getId());
final Cart cart = convertToCart(memberEntity, cartDtos);
final List<ProductResponse> productResponses = convertToProductResponse(cart);
final int productCount = cart.getProductCount();
return new CartResponse(productCount, productResponses);
}
๋ค๋ง, ์ด ๊ณผ์ ์์ entity -> domain์ผ๋ก ๋ง๋๋ ์ปจ๋ฒํ ๋ก์ง์ด ๋ง์์ ธ์ ์ฝ๋๊ฐ ์ฝ๊ฐ ๋ณด๊ธฐ ํ๋ค์ด์ก๋ค.
๋ํ, ์๋น์ค ๋ ์ด์ด์์ ๋๋ฉ์ธ, ์ํฐํฐ๋ฅผ ๋ ๋ค ๊ฐ์ง๊ณ ์์ผ๋ฉด์ service -> entity(persistence layer), service -> domain์ ๊ฐ์ ์ํ์ฐธ์กฐ ํํ๊ฐ ๋ฐ์ํด์ ๋จ๋ฐฉํฅ์ผ๋ก ๋ฐ์ดํฐ๊ฐ ํ๋ฅด์ง ์๊ฒ ๋์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ๋ค์ ๋ฏธ์ ์์๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์ ์ฉํ๋๋ฐ...! ์ด๊ฑฐ๋ ์งํ์ฒ ๋ฏธ์ ๋๋๊ณ ํฌ์คํ ํด๋ณด๊ณ ์ ํ๋ค.
๋ค์ ๋ฏธ์ ๋ ํ์ดํ ! ๐ช
'์ฐ์ํํ ํฌ์ฝ์ค > ๋ ๋ฒจ 2' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[์ฐํ ์ฝ 5๊ธฐ] ๋ ๋ฒจ 2 ๋ ๋ฒจ ์ธํฐ๋ทฐ ์ ๋ฆฌ ๋ฐ ๊ฐ์ ํ๊ณ (5) | 2023.06.16 |
---|---|
[์ฐํ ์ฝ 5๊ธฐ] ์ฅ๋ฐ๊ตฌ๋ ํ์ ๋ฏธ์ ํ๊ณ (2) | 2023.06.16 |
[์ฐํ ์ฝ 5๊ธฐ] ์งํ์ฒ ๋ฏธ์ ํ๊ณ (4) | 2023.05.22 |
[์ฐํ ์ฝ 5๊ธฐ] ์น ์๋์ฐจ ๊ฒฝ์ฃผ ๋ฏธ์ ํ๊ณ (2) | 2023.04.20 |