Spring Boot로 API를 개발하다 보면, 프론트엔드로 데이터를 보낼 때 보통 엔티티(Entity)를 직접 반환하지 않고, 별도의 DTO(Data Transfer Object)를 만들어서 전달한다. 이번 글에서는 엔티티를 DTO로 변환하는 이유와 다양한 방법, 각각의 방식이 어떤 장단점 및 특징을 가지고 있는지 정리한다.
처음에는 "그냥 엔티티 그대로 반환하면 되지 않을까?"라고 생각할 수 있다. 하지만 실제로는 다음과 같은 이유 때문에 DTO를 만들어 사용하는 것이 좋다.
엔티티에는 사용자 비밀번호, 내부 데이터 등 민감한 정보가 들어 있을 수 있는데, 이 때 엔티티를 그대로 응답으로 내보내면 정보들이 함께 노출될 수 있다.
엔티티 구조가 변경되어도 DTO를 통해 응답 구조를 따로 관리할 수 있다.
List 형태의 엔티티를 DTO로 변환하는 과정이다.
List<AttachmentDTO> attachmentDTOs = attachments.stream()
.map(a -> new AttachmentDTO(
a.getName(),
a.getChangedName(),
a.getSize(),
a.getExtension()
)).toList();
@Builder
public class AttachmentDTO {
private Long attachmentId;
private String name;
private String changedName;
private LocalDateTime createdAt;
private Long size;
private String extension;
먼저, 해당 DTO 클래스에 @Builder
어노테이션을 추가해야 한다.
List<AttachmentDTO> attachmentDTOs = attachments.stream()
.map(a -> AttachmentDTO.builder()
.name(a.getName())
.changedName(a.getChangedName())
.size(a.getSize())
.extension(a.getExtension())
.build()
).toList();
public static AttachmentDTO convertDTO(Attachment attachment) {
return new AttachmentDTO(
attachment.getName(),
attachment.getChangedName(),
attachment.getSize(),
attachment.getExtension()
);
}
DTO 클래스 내부에 정적 메소드로 작성하여 사용한다. 엔티티에서 DTO로 변환하는 메소드라는 뜻으로 다음과 같이 convertDTO
라는 이름으로 작성했다.
// 사용
List<AttachmentDTO> attachmentDTOs = attachments.stream()
.map(AttachmentDTO::convertDTO)
.toList();
AttachmentDTO.convertDTO()
처럼 객체를 생성하지 않고 클래스 이름으로 직접 호출할 수 있어서 직관적이다.
변환 로직을 DTO 클래스 내부에 두어 관련 책임을 부여할 수 있다.
명확한 이름을 붙여서 어떤 역할인지 표현할 수 있다.
* 빌더를 사용하는 방식도 @Builder
를 통해 정적 메소드를 주입한 것이다. 즉, AttachmentDTO.builder()
도 정적 팩토리 메소드처럼 클래스 이름으로 객체를 생성하는 방식이다.
로직이 바뀌더라도 메소드 내부만 수정하면 돼서 유지보수에 좋다.
클래스명.메소드()
형태로 전역으로 호출할 수 있기 때문에, 남용하면 설계가 복잡해질 수 있다.Post <-> List(Attachment) 연관 관계이고,
Post → PostDetailDTO 변환
public static PostDetailDTO convertDTO(Post post, List<AttachmentDTO> attachmentDTOs) {
return new PostDetailDTO(
post.getPostId(),
post.getTitle(),
post.getContent(),
post.getCreatedAt(),
attachmentDTOs
);
}
API 응답을 깔끔하고 안정적으로 만들기 위해서는 DTO 설계와 변환 방식에 신경 써야 한다. 단순히 동작만 되면 끝이 아니라, 나중에 유지보수나 확장할 때 얼마나 편할지를 생각하고 코드를 짜보자.
정적 팩토리 메소드는 처음에는 익숙하지 않을 수 있지만, 점점 큰 프로젝트일수록 의도를 명확히 전달할 수 있고 변환 로직 변경이 필요할 때도 해당 메서드만 수정하면 되어 코드의 가독성과 유지보수성을 높일 수 있다.