DTO

seokseungmin·2024년 10월 16일

Today I Learned

목록 보기
5/20

API 개발 기본: 회원 등록, 수정, 조회

Spring Boot를 사용해 기본적인 API를 개발하는 과정을 설명합니다. 회원 등록, 수정, 조회 같은 자주 사용되는 API를 중심으로 DTO(Data Transfer Object)를 사용하는 이유와 방법을 다룹니다.

1. 회원 등록 API

회원 등록 API는 새로운 회원 정보를 받아서 저장하는 기능을 제공합니다. 여기서 중요한 점은 엔티티를 그대로 요청 또는 응답에 사용하지 않고, DTO를 사용하는 것입니다.

V1: 엔티티를 직접 Request Body에 매핑 (문제점)
@PostMapping("/api/v1/members")
public CreateMemberResponse saveMemberV1(@RequestBody @Valid Member member) {
    Long id = memberService.join(member);
    return new CreateMemberResponse(id);
}

V1에서는 Member 엔티티를 그대로 사용하여 회원 정보를 저장합니다. 하지만 이 방식에는 다음과 같은 문제점이 있습니다:

  • 프레젠테이션 계층 로직이 엔티티에 추가됩니다.
  • API 검증 로직이 엔티티에 들어가며, 이는 엔티티가 변하면 API 스펙도 함께 변경됩니다.
V2: DTO를 사용한 회원 등록 API
@PostMapping("/api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request) {
    Member member = new Member();
    member.setName(request.getName());
    Long id = memberService.join(member);
    return new CreateMemberResponse(id);
}

V2에서는 CreateMemberRequest라는 별도의 DTO를 사용하여 요청을 처리합니다. 이렇게 하면 엔티티와 프레젠테이션 계층의 로직을 분리할 수 있고, 엔티티가 변경되더라도 API 스펙은 그대로 유지됩니다.

2. 회원 수정 API

회원 정보 수정 API도 마찬가지로 DTO를 사용하여 업데이트할 데이터를 전달받습니다.

@PutMapping("/api/v2/members/{id}")
public UpdateMemberResponse updateMemberV2(@PathVariable("id") Long id, 
                                           @RequestBody @Valid UpdateMemberRequest request) {
    memberService.update(id, request.getName());
    Member findMember = memberService.findOne(id);
    return new UpdateMemberResponse(findMember.getId(), findMember.getName());
}
  • PUT 방식은 전체 업데이트를 할 때 사용하는 것이 REST 스타일에 맞습니다. 부분 업데이트PATCHPOST를 사용하는 것이 좋습니다.

3. 회원 조회 API

회원 정보를 조회하는 API도 DTO를 사용하여 응답 데이터를 반환하는 것이 바람직합니다.

V1: 엔티티를 직접 노출하는 방식 (문제점)
@GetMapping("/api/v1/members")
public List<Member> membersV1() {
    return memberService.findMembers();
}

이 방식은 엔티티를 직접 외부에 노출하기 때문에 다음과 같은 문제점이 있습니다:

  • 엔티티에 프레젠테이션 로직이 추가됩니다.
  • 기본적으로 엔티티의 모든 값이 노출됩니다.
  • 엔티티가 변하면 API 스펙도 함께 변경됩니다.
V2: DTO를 사용한 회원 조회 API
@GetMapping("/api/v2/members")
public Result membersV2() {
    List<Member> findMembers = memberService.findMembers();
    List<MemberDto> collect = findMembers.stream()
                                         .map(m -> new MemberDto(m.getName()))
                                         .collect(Collectors.toList());
    return new Result(collect);
}

여기서는 MemberDto를 사용하여 엔티티를 DTO로 변환한 후 반환합니다. 이렇게 하면:

  • 엔티티가 변해도 API 스펙은 변하지 않으며,
  • 필요에 따라 추가적인 필드를 DTO에 추가할 수 있습니다.

결론

실무에서는 엔티티를 API 스펙에 노출하지 말고, DTO를 사용해 요청과 응답을 처리하는 것이 중요합니다. 엔티티가 변경되더라도 API 스펙이 영향을 받지 않으며, API 설계의 유연성을 높일 수 있습니다.


profile

0개의 댓글