JiwonDev

DTO๋Š” ์–ด๋–ค Layer์— ํฌํ•จ๋˜์–ด์•ผ ํ•˜๋Š”๊ฐ€.

by JiwonDev

์ตœ๊ทผ์— Dto Mapper์— ๊ด€๋ จ๋œ ๊ธ€์„ ์“ฐ๋ฉฐ, ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์€ ํ”„๋กœ์ ํŠธ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค๋Š” ๊ฑธ ์•Œ์•˜๋‹ค.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/")
public class ItemApiController {
    private final ItemFacade itemFacade; // package.application.ItemFacade
    private final ItemDtoMapper mapper;


    @GetMapping("/register")
    public CommonResponse register(@RequestBody @Valid ItemDto.RegisterItemRequest request) {
        var itemCommand = mapper.toCommand(request); // package.domain.ItemCommand
        var itemInfo = itemFacade.register(itemCommand); // // package.domain.ItemInfo 
        var response = mapper.toResponse(itemInfo); 
        return CommonResponse.success(response); // package.presentation.ItemResponse
    }
}

์‹ค์ œ ์Šคํ”„๋ง ๊ฐ€์ด๋“œ๋กœ ์œ ๋ช…ํ•œ baeldung.com ์˜ ์˜ˆ์ œ์ฝ”๋“œ๋„ ์ด๋Ÿฌํ•˜๋‹ค.

@RestController
class PostRestController {
    @Autowired
    private ModelMapper modelMapper;

    @GetMapping
    public List<PostDto> getPosts(...) {
        //...
        List<Post> posts = postService.getPostsList(page, size, sortDir, sort);
        return posts.stream()
          .map(this::convertToDto)
          .collect(Collectors.toList());
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public PostDto createPost(@RequestBody PostDto postDto) {
        Post post = convertToEntity(postDto);
        Post postCreated = postService.createPost(post));
        return convertToDto(postCreated);
    }

    @GetMapping(value = "/{id}")
    public PostDto getPost(@PathVariable("id") Long id) {
        return convertToDto(postService.getPostById(id));
    }

    @PutMapping(value = "/{id}")
    @ResponseStatus(HttpStatus.OK)
    public void updatePost(@PathVariable("id") Long id, @RequestBody PostDto postDto) {
        if(!Objects.equals(id, postDto.getId())){
            throw new IllegalArgumentException("IDs don't match");
        }
        Post post = convertToEntity(postDto);
        postService.updatePost(post);
    }
}

 

DtoMapper์™€ ํ•จ๊ป˜ ์œ„์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งŽ์€ ์žฅ์ ์ด ์žˆ๋‹ค.

  • Application(Service) ๋ ˆ์ด์–ด๋Š” Request, Response์— ๋Œ€ํ•ด ๋ชฐ๋ผ๋„ ๋œ๋‹ค.
  • Presentation(Controller) ๋ ˆ์ด์–ด๋งŒ ์ž…์ถœ๋ ฅ์„ ๊ด€๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์œ ์—ฐ์„ฑ์ด ๋›ฐ์–ด๋‚˜๊ณ  ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์ง„๋‹ค.

 

 

 

๐Ÿ’ญ ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ ํ”„๋กœ์ ํŠธ์—์„  ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์–ด์š”

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํŒจํ‚ค์ง€ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

๋„๋ฉ”์ธ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ์ด์–ด๋ฅผ ๊ฑฐ์ณ์•ผํ•œ๋‹ค. ์ง„์ž…์ ์„ ํ•˜๋‚˜๋กœ ๋งŒ๋“ค๊ณ , ์˜์กด์„ฑ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. 

์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ปจํŠธ๋กค๋Ÿฌ์—์„œ๋Š” Domain Dto (ItemInfo, ItemDto ๋“ฑ)์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ import ํ•  ์ˆ˜ ์—†๋‹ค.

  • Dto๋Š” ๋„๋ฉ”์ธ ๋ ˆ์ด์–ด์— ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.
@GetMapping("/register")
public CommonResponse register(@RequestBody @Valid ItemDto.RegisterItemRequest request) {
    var itemCommand = mapper.toCommand(request); // package.domain.ItemCommand
    var itemInfo = itemFacade.register(itemCommand); // // package.domain.ItemInfo 
    var response = mapper.toResponse(itemInfo); 
    return CommonResponse.success(response); // package.presentation.ItemResponse
}

 

๐Ÿ™„ : ๊ทธ๋Ÿผ [๋„๋ฉ”์ธ ์•ˆ์— DTO]๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋˜์ž–์•„์š”? ๋‹ค๋ฅธ ๊ณ„์ธต์—๋งŒ ๋”ฐ๋กœ ๋งŒ๋“ค์ฃ ?

๋งž๋Š” ๋ง์ด๋‹ค. '๋„๋ฉ”์ธ DTO'๋ฅผ ์•„์˜ˆ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ , ์•„๋ž˜์ฒ˜๋Ÿผ ๋„๋ฉ”์ธ ์„œ๋น„์Šค์— ํ•˜๋‚˜ํ•˜๋‚˜ ํ•„๋“œ๋ฅผ ๊นŒ์„œ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค.

// Application ๋ ˆ์ด์–ด - AccountFacade ์ฝ”๋“œ
@Transactional
public RegisterAccountResponse register(RegisterAccountCommand request) {
    var entity = accountService.register( // ๋˜๋Š” registerAccountProcessor.register(..)
        request.getUserId(),
        request.getEmail(),
        request.getPassword());

    return new RegisterAccountResponse(entity.getUserId(), entity.getEmail());
}

 

์‹ค์ œ๋กœ ๋ฐ˜ ๋ฒ„๋…ผ - ๋„๋ฉ”์ธ ์ฃผ๋„ ์„ค๊ณ„ ํ•ต์‹ฌ (IDDD) ์ƒ˜ํ”Œ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด, ์œ„์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•„๋“œ๋ฅผ ํ•˜๋‚˜ํ•˜๋‚˜ ๋„ฃ๊ณ ์žˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ์ด ๋ฐฉ๋ฒ•์œผ๋ก  ํ•œ๊ณ„๊ฐ€ ์žˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด ํ•„๋“œ๊ฐ€ ์—„์ฒญ ๋งŽ๋‹ค๋ฉด? ๊ณผ์žฅํ•œ๊ฒŒ ์•„๋‹ˆ๋ผ, ์‹ค์ œ๋กœ ์ €๋Ÿฐ DTO๊ฐ€ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.

@Getter
@Setter
@ToString
public static class RegisterOrderRequest {

    private String orderToken;
    private Long userId;
    private String payMethod;
    private Long totalAmount;
    private String orderedAt;
    private String status;
    private String statusDescription;
    private String receiverName;
    private String receiverPhone;
    private String receiverZipcode;
    private String receiverAddress1;
    private String receiverAddress2;
    private String etcMessage;

    private List<RegisterOrderItem> orderItemList;
}

 

์ด๋Ÿฐ ๊ฒฝ์šฐ, ๋„๋ฉ”์ธ ๋ฉ”์„œ๋“œ์˜ ํŒŒ๋ผ๋ฉ”ํƒ€๋Š” 10๊ฐœ, 20๊ฐœ๊ฐ€ ๋  ๊ฒƒ์ด๋‹ค. 

์ด ์ฝ”๋“œ๋Š” ํ”ํžˆ ๋งํ•˜๋Š” ํด๋ฆฐ์ฝ”๋“œ์™€๋Š” ๊ฑฐ๋ฆฌ๊ฐ€ ๋งค์šฐ ๋ฉ€๋‹ค. ๋„๋ฉ”์ธ Dto(OrderInfo, OrderDto)์— ํฌ์žฅํ•ด์„œ ์ „๋‹ฌํ•ด์•ผํ•œ๋‹ค.

public register(Long userId, Long totalAmount, String orderToken,
    String payMethod, String orderedAt, String status, ...)// ์ด๋ ‡๊ฒŒ 20๊ฐœ์˜ ํŒŒ๋ผ๋ฉ”ํƒ€๋ฅผ ๋ฐ›์„๊ฑด๊ฐ€?

 

 

๐Ÿ’ญ Request, Response๋Š” ์–ด๋Š ๋ ˆ์ด์–ด์— ๋‘ฌ์•ผ ํ•˜๋Š”๊ฐ€.

๐Ÿ™„ : ?? ๋‹น์—ฐํžˆ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์žˆ๋Š” ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋ ˆ์ด์–ด๊ฒ ์ฃ !

 

๋‚˜๋„ ์ฒ˜์Œ์—” ๊ทธ๋ ‡๊ฒŒ ์ƒ๊ฐํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ปจํŠธ๋กค๋Ÿฌ์™€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฝ”๋“œ๋ฅผ ๋ณด์ž.

@PostMapping("/signup")
public ApiResponse<RegisterAccountResponse> register(
    @Valid @RequestBody RegisterAccountCommand request) {
    return ApiResponse.success(accountFacade.register(request));
}
// Application ๋ ˆ์ด์–ด - AccountFacade ์ฝ”๋“œ
@Transactional
public RegisterAccountResponse register(RegisterAccountCommand request) {
    var entity = accountService.register( // ๋˜๋Š” registerAccountProcessor.register(..)
        request.getUserId(),
        request.getEmail(),
        request.getPassword());

    return new RegisterAccountResponse(entity.getUserId(), entity.getEmail());
}

 

Request๊ฐ€ ํ”„๋ ˆ์  ํ…Œ์ด์…˜์— ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ, App ๋ ˆ์ด์–ด๊ฐ€ Presentation ๋ ˆ์ด์–ด๋ฅผ ์˜์กดํ•˜๊ฒŒ ๋œ๋‹ค.

Application์€ Presentation์„ ์•Œ ํ•„์š”๊ฐ€ ์ „ํ˜€์—†๋‹ค. ๋„๋ฉ”์ธ ์ž‘์—…์„ ์œ„์ž„ํ•˜๊ณ , ์™ธ๋ถ€ ์•ฑ์„ ํ˜ธ์ถœํ•˜๋Š” ์—ญํ• ๋งŒ ๊ฐ€์ง„๋‹ค.

 

์ข€ ๋” ์‰ฝ๊ฒŒ ๋น„์œ ํ•ด๋ณด์ž๋ฉด Application์€ TV ๋ฆฌ๋ชจ์ปจ๊ณผ ๊ฐ™์€ ์—ญํ• ์ด๋‹ค. 

๋ฆฌ๋ชจ์ปจ์„ ๋งŒ๋“œ๋Š”๋ฐ ์–ด๋–ค ์‚ฌ๋žŒ์ด ์‚ฌ์šฉํ• ์ง€๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ์•Œ์•„์•ผํ• ๊นŒ? ์•„๋‹ˆ๋‹ค. ์†Œ๋น„์ž(Presentation)๊ฐ€ ๋ฆฌ๋ชจ์ปจ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์ง€, ๋ฆฌ๋ชจ์ปจ์ด ์†Œ๋น„์ž๋ฅผ ์•Œ ํ•„์š”๋Š” ์ „ํ˜€ ์—†๋‹ค.

 

๊ทธ๋Ÿผ ๊ฐ€์žฅ ๊น”๋”ํ•œ ๋ฐฉ๋ฒ•์€ ๊ฐ ๊ณ„์ธต๋งˆ๋‹ค DTO๋กœ ๋„๋ฐฐํ•ด๋†“๋Š” ๊ฒƒ์ด๋‹ค.

DomainDto โžก AppDto โžก ControllerResponse ์ด๋ ‡๊ฒŒ 3๊ฐœ์˜ Dto๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, ์˜์กด์„ฑ์ด ๊ผฌ์ด์ง€ ์•Š๊ณ  ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ํด๋ฆฐ์ฝ”๋“œ, ํด๋ฆฐ์•„ํ‚คํ…์ฒ˜๋“ฑ์„ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๋ณ€ํ™”์˜ ๋ฐฉํ–ฅ, ์˜์กด์„ฑ์„ ํ†ต์ œํ•˜๋Š” ๊ฒƒ์˜ ์ค‘์š”ํ•จ์„ ์•Œ๊ณ ์žˆ๋‹ค.

  • application์€ ๋„๋ฉ”์ธ์„ ์บก์Šํ™”ํ•˜๊ณ , ์•ฑ ์ง„์ž…์ ์„ ํ•˜๋‚˜๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.
  • SRP ์›์น™. ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค ํ•ด์„œ ๋„๋ฉ”์ธ์— ์˜ํ–ฅ์ด ์žˆ์œผ๋ฉด ์•ˆ๋œ๋‹ค. ์ด๋Š” ๋ฐ˜๋Œ€๋„ ๋งˆ์ฐฌ๊ฐ€์ง€
  • application์— ์ปจํŠธ๋กค๋Ÿฌ Request์˜ ์˜์กด์„ฑ์ด ์ƒ๊ธฐ๋ฉด ์•ˆ๋œ๋‹ค. ์ด๋Š” Http Request๋ผ๋Š” ํ•˜์œ„ ๊ธฐ์ˆ ์— ์•ฑ์ด ๋ฌถ์ด๊ฒŒ๋œ๋‹ค.


์ด ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ํ•˜๋ฉด '์ด๋ก ์œผ๋กœ๋Š” ๊ฐ€์žฅ ๊น”๋”ํ•œ ์ฝ”๋“œ'๊ฐ€ ๋‚˜์˜จ๋‹ค.

ํ•˜์ง€๋งŒ ๋„๋ฉ”์ธ ๋ชจ๋ธ๊ณผ ๋˜‘๊ฐ™์€ DTO๋ฅผ 3๊ฐœ์”ฉ ๋งŒ๋“ค์–ด์„œ ๋ณ€ํ™˜ํ•˜๋‹ค๋ณด๋ฉด, ์ด ๋…ธ๊ฐ€๋‹ค๋ฅผ ๊ณ„์†ํ•˜๋Š”๊ฒŒ ๋งž๋‚˜ ์˜์‹ฌ์ด ๋“ ๋‹ค.

 

๐Ÿ™„ : ์˜์กด์„ฑ์„..์ด๋ ‡๊ฒŒ ๊นŒ์ง€ ๊ด€๋ฆฌํ•  ํ•„์š”๋Š” ์—†์ง€์•Š์„๊นŒ? DTO์ •๋„๋Š” ๊ทธ๋ƒฅ ์“ฐ์ž



์‚ด์ง ๋ป˜์ง“์ด๊ธดํ•˜์ง€๋งŒ.. DTO๋ฅผ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ˆจ๊ธฐ๋ ค๊ณ  ์•„๋ž˜์™€ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ• ๊นŒ ๊ณ ๋ฏผํ•œ ์ ๋„ ์žˆ๋‹ค.

mapper.toResponse( MemberFacade.create ( mapper.toCommand( request ) ) );

@GetMapping("/register")
public CommonResponse register(@RequestBody @Valid CreateMemberRequest request) {
//        var itemCommand = mapper.toCommand(request);
//        var itemInfo = itemFacade.register(itemCommand); 
//        var response = mapper.toResponse(itemInfo);
 
    // ํ•œ์ค„๋กœ ์ž‘์„ฑํ•ด์„œ DTO ์ˆจ๊ฒจ๋ฒ„๋ฆฌ๊ธฐ (mapper๋งŒ Presentation์— ์˜์กด์„ฑ์ด ์žˆ์œผ๋ฉฐ, ์ด๋Š” Application์— ๋‘”๋‹ค.)
    var response = mapper.toResponse(MemberFacade.create(mapper.toCommand(request)));
    return CommonResponse.success(response); 
}

 

 

 

๐Ÿ’ญ ์•„๋‹ˆ ๊ทธ๋ž˜์„œ ๊ฒฐ๋ก ์ด ๋ญ์—์š”

์ •ํ•ด์ง„ ๋‹ต์ด ์—†๋‹ค๋Š”๊ฒŒ ๊ฒฐ๋ก ์ด๋‹ค. DTO๋ฅผ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€๋Š” ์ •๋‹ต์ด ์—†๋‹ค.

๋ชจ๋“  ๊ณณ์— ์™„๋ฒฝํ•œ ์€ํƒ„ํ™˜์€ ์—†๋‹ค.

์ƒ์œ„๋ ˆ์ด์–ด(Controller)์—์„œ ํ•˜์œ„๋ ˆ์ด์–ด(App, Entity)๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ฑด ๋‹น์žฅ ํฐ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค.
์‚ฌ์šฉํ•˜๋Š” ๋ชฉ์ ์— ๋งž๊ฒŒ ์ ์ ˆํžˆ ํƒ€ํ˜‘ํ•ด์„œ ์“ฐ๋ฉด ๋œ๋‹ค. ์ •๋‹ต์€ ์—†๋‹ค.

  • 'DTO๋ฅผ ์–ด๋–ป๊ฒŒ ์“ฐ๋ƒ'๋กœ ๊ฐ€๋ฉด ์˜์›ํžˆ ํ•ด๊ฒฐ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • '์™œ DTO๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€?' ์— ์ดˆ์ ์„ ๋งž์ถฐ์„œ ๋‚˜์—๊ฒŒ ๋งž๋Š” ๋ฐฉ๋ฒ•์„ ์ฐพ์•„์•ผํ•œ๋‹ค.

Domain์— ๋‹ค๋ฅธ ๊ณ„์ธต์˜ ์˜์กด์„ฑ์ด ์žˆ์„ ํ•„์š”๋„ ์—†๊ณ , ์žˆ์œผ๋ฉด ์•ˆ๋œ๋‹ค. / ๊ทธ๋ ‡๋‹ค๋ฉด App, Presentation ์˜์กด์„ฑ์€ ๋Œ€์ถฉ ๊ด€๋ฆฌํ•ด๋„ ๋˜๋Š”๊ฐ€?
์งˆ๋ฌธ์— ๋‹ต์ด ์žˆ๋‹ค. '๋‚˜๋Š” ์™œ DTO๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€'์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์„ ํ•ด๋ณธ๋‹ค๋ฉด, ๋‚˜์—๊ฒŒ ๋งž๋Š” ๋ฐฉ๋ฒ•์ด ๋‚˜์˜ฌ ๊ฒƒ์ด๋‹ค.

 

 

 

๐Ÿ’ญ ๊ธ€์“ด์ด๋Š” ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋‚˜์š”?

๋„๋ฉ”์ธ ๋ฉ”์„œ๋“œ๋Š” ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๊ณ , Application ์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ ๋”๋Ÿฝ๊ฒŒ(?) ์‚ฌ์šฉํ•œ๋‹ค.

ํ•„๋“œ๋ฅผ ๊นŒ์„œ ๋„ฃ๊ณ ์žˆ๊ณ  [๋„๋ฉ”์ธ Dto]๋Š” ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ Application์—์„œ ๋ณ€ํ™˜ํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.

  • Request(Command) -> [๋„๋ฉ”์ธ Dto] -> ๋„๋ฉ”์ธ ์„œ๋น„์Šค ์ „๋‹ฌ 
  • ๋„๋ฉ”์ธ ์„œ๋น„์Šค -> [๋„๋ฉ”์ธ Dto] -> Response

๊ทธ๋ฆฌ๊ณ  Request, Response๋ฅผ Application ๋ ˆ์ด์–ด์— ๋‘ฌ์„œ Presentation์˜ ์˜์กด์„ฑ์„ ์—†์•ด๋‹ค.

@Transactional
public void write(Long memberId, WriteArticleCommand command) {
    writeArticleProcessor.write(memberId,
        command.getTitle(), command.getContent(),
        command.getImage(), command.getTags());
}

@Transactional(readOnly = true)
public List<ArticlePreviewResponse> findAll(Long articleId, int pageSize) {
    var domainDto = articleReader.findAll(articleId, pageSize);
    
    // ์ถ”ํ›„ return mapper.toResponse(domainDto); ๋กœ ๋ณ€๊ฒฝ.
    return domainDto.stream()
        .map(dto -> new ArticlePreviewResponse(
            dto.getId(), dto.getAuthorId(), dto.getTitle(),
            dto.getNickname(), dto.getImage(), dto.getCreatedAt(),
            dto.getModifiedAt(), dto.getStatus())
        ).collect(Collectors.toList());
}

 

๋‚˜์ค‘์— Application์— Mapstruct๊ฐ™์€ ๋งคํผ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์งˆ ๊ฒƒ๊ฐ™๋‹ค.

 

 

 

์‚ฌ์‹ค ์ œ์ผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๋ฐฉ๋ฒ•์€ ์ฒ˜์Œ์— ๋งํ–ˆ๋˜ ์ฝ”๋“œ์ด๊ธดํ•œ๋ฐ, ์‚ฌ์‹ค ๋‚˜๋„ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/")
public class ItemApiController {
    private final ItemFacade itemFacade; // package.application.ItemFacade
    private final ItemDtoMapper mapper;


    @GetMapping("/register")
    public CommonResponse register(@RequestBody @Valid ItemDto.RegisterItemRequest request) {
        var itemCommand = mapper.toCommand(request); // package.domain.ItemCommand
        var itemInfo = itemFacade.register(itemCommand); // // package.domain.ItemInfo 
        var response = mapper.toResponse(itemInfo); 
        return CommonResponse.success(response); // package.presentation.ItemResponse
    }
}

DTO ์‚ฌ์šฉ๋ฒ•์— ์—ฐ์—ฐํ•˜์ง€ ๋ง๊ณ ,  ํ”„๋กœ์ ํŠธ๋ฅผ ๊ณ„์† ๋งŒ๋“ค๋ฉด์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ณ  ๋ฆฌํŒฉํ† ๋ง ํ•ด์•ผํ•  ์‹œ์ ์ด ์™”์„ ๋•Œ ํ•˜๋‚˜์”ฉ ๋ณ€๊ฒฝํ•  ์ƒ๊ฐ์ด๋‹ค.

 

๊ฒฐ๊ตญ DTO์˜ ์‚ฌ์šฉ๋ฐฉ๋ฒ•๋„ ๊ธฐ์ˆ ์— ์ง‘์ฐฉํ•˜์ง€๋ง๊ณ , ๋ฌธ์ œ๋ฅผ ๋ณด๋ผ๋Š” ๊ฒƒ๊ณผ ๋™์ผํ•œ ๋งฅ๋ฝ์ธ ๋“ฏ ํ•˜๋‹ค.

 

๐Ÿ’ญ ๋ ˆํผ๋Ÿฐ์Šค

tecoble - DTO์˜ ์‚ฌ์šฉ ๋ฒ”์œ„์— ๋Œ€ํ•˜์—ฌ

๋„๋ฉ”์ธ ์—”ํ„ฐํ‹ฐ๋ฅผ DTO์— ํˆฌ์˜ํ•˜๋Š” ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•

DDD - which layer DTO should be implemented

Should service layer accept a DTO or a custom request object from the controller?

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ