728x90
개요
프로젝트를 진행하는 과정에서 Dto 설계방식에 대한 고민이 들었습니다.
DTO를 생성하고 관리하는 방식에는 다양한 접근법이 있으며, 프로젝트의 요구사항에 따라 적합한 방식이 달라집니다.
1. 단일 파일에 여러 독립 DTO 클래스 정의
public class UserDto { /* User 관련 필드와 메서드 */ }
public class UserDetailDto { /* User 상세 정보 */ }
public class UserListDto { /* User 목록 정보 */ }
- 하나의 파일에 여러 DTO를 각각 독립된 클래스로 정의하는 방식입니다.
- 장점: 파일 수가 줄어들어 관련된 DTO들을 한눈에 파악할 수 있습니다.
- 단점: 파일이 길어져 가독성이 떨어질 수 있으며, 클래스 간 논리적 관계가
명확히드러나지 않을 수 있습니다.
2. 중첩 클래스 (Nested Class)로 관리
public class UserResponseDto {
public static class Summary { /* 요약 정보 */ }
public static class Detail { /* 상세 정보 */ }
public static class CreateRequest { /* 생성 요청 정보 */ }
}
- 하나의 상위 클래스 안에 관련된 DTO들을 중첩 클래스로 정의하는 방식입니다.
- 장점: 상위 클래스와 강한 연관성을 가지며 논리적 묶음이 생겨 관리가 용이합니다.
- 단점: 외부에서 중첩 클래스를 사용할 때 상위 클래스 이름을 통해 접근해야 하므로 코드가 길어질 수 있습니다.
- UserResponseDto.Summary
- UserResponseDto.Detail
- UserResponseDto.CreateRequest
3. 내부 정적 클래스 (Static Inner Class)
public class OrderDto {
public static class OrderSummaryDto { /* Order 요약 정보 */ }
public static class OrderDetailDto { /* Order 상세 정보 */ }
}
- DTO 내부에 관련 DTO를 정적 클래스로 선언해
상위 클래스의 인스턴스없이 사용 가능한 방식입니다. - 장점: 상위 클래스와의 관계가 있음을 표현하면서도 독립성을 유지합니다.
- 단점: 상위 클래스의 네임스페이스 하에 있으므로 사용 시 상위 클래스를 통해 접근해야 합니다.
중첩 클래스 Dto
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PostResponseDto {
private UUID postId;
private Long userId;
private String title;
private String content;
private Integer views;
private Integer likes;
public static PostResponseDto of(Post post) {
return PostResponseDto.builder()
.userId(post.getUserId())
.postId(post.getPostId())
.title(post.getTitle())
.content(post.getContent())
.views(post.getViews())
.likes(post.getLikes())
.build();
}
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Get{
private UUID postId;
private Long userId;
private String title;
private String content;
private Integer views;
private Integer likes;
private List<CommentResponseDto> commentList;
public static Get of(Post post) {
return Get.builder()
.userId(post.getUserId())
.postId(post.getPostId())
.title(post.getTitle())
.content(post.getContent())
.views(post.getViews())
.likes(post.getLikes())
.commentList(post.getComments().stream()
.filter(comment -> !comment.getIsDeleted())
.map(CommentResponseDto::of)
.collect(Collectors.toList()))
.build();
}
}
}
저는 보통 다음과 같이 중첩 클래스를 사용하여 DTO를 생성합니다.
이렇게 하는 가장 큰 이유는 DTO의 개수가 많아질 때, 각각의 DTO를 별도 파일로 만들 경우 관리가 어려워지기 때문입니다. 중첩 클래스나 하나의 파일에 여러 독립 DTO 클래스를 모아서 정의하는 방식이 오히려 관리에 유리합니다.
또한, 중첩 클래스를 사용하면 해당 DTO가 상위 클래스의 컨텍스트에 속한다는 것을 직관적으로 알 수 있어 논리적으로 연관된 DTO를 쉽게 찾을 수 있다는 점에서도 장점이 있습니다. 이와 같은 이유로 주로 중첩 클래스를 활용합니다.
예를 들어, 단건 조회에서는 하나의 데이터에 대한 자세한 정보가 필요하고, 전체 조회에서는 여러 데이터에 대한 개괄적인 정보를 원하기 때문에, 단건 조회에 더 상세한 응답이 요구됩니다. 이런 경우, 중첩 클래스를 주로 사용합니다.
하지만 중첩 클래스의 정확한 장단점을 알고 활용하기 위해 정리하고자 합니다.
특징
- 상위 클래스(PaymentResponseDto) 안에 내부 클래스를 정의하여, 각 DTO 클래스가 상위 클래스와의 강한 논리적 관계를 가집니다.
- 중첩된 클래스는 상위 클래스의 맥락 내에서만 사용되는 것이 일반적입니다.
장점
- 논리적 묶음이 명확해져 관련된 클래스를 한곳에 모아두는 효과가 있습니다.
- 외부에서 사용할 때는 상위 클래스를 통해 접근하므로 의도를 명확히 전달할 수 있습니다. (PaymentResponseDto.Detail 처럼 상위 클래스의 이름을 참조하게 됨)
- 중첩 클래스는 상위 클래스에서만 의미가 있는 경우가 많기 때문에
외부에서 접근을 제한하고 구조적으로 캡슐화할 수 있습니다.
단점
- 외부에서 중첩된 하위 클래스에 접근하려면 상위 클래스 이름을 항상 포함해야 하므로 코드가 길어질 수 있습니다.
- 너무 많은 중첩 클래스는 상위 클래스의 책임이 불필요하게 많아지게 만들어 가독성이 떨어질 수 있습니다.
- 논리적 관계가 뚜렷하거나 상위 클래스 맥락에서만 사용될 때는 중첩 클래스가 적합합니다.
- 독립적으로 사용되는 DTO가 많고 클래스 간
강한 결합이 필요하지 않을 때는 여러 DTO를 하나의 파일에서 관리하는 방식이 더 적합합니다.
'TIL,일일 회고' 카테고리의 다른 글
[TIL, 일일 회고] 2024.10.26 - 로컬 개발 환경과 운영 환경을 위한 Logback 설정 (0) | 2024.10.26 |
---|---|
[TIL, 일일 회고] 2024.10.25 - 코드 간결성과 불변성을 갖춘 Record 클래스 (0) | 2024.10.25 |
[TIL, 일일 회고] 2024.10.23 - Spring boot Swagger 주요 어노테이션 (0) | 2024.10.23 |
[TIL, 일일 회고] 2024.10.22 - S3 확장자 에러 처리하기 (0) | 2024.10.22 |
[TIL, 일일 회고] 2024.10.21 - Spring boot 스케줄러 @Schduled (0) | 2024.10.21 |