프로젝트 규모가 커질수록 DTO가 많아져 관리 방법에 대한 고민이 생겼다.
특히 Json 응답에 랩핑된 형태의 개수만큼 DTO가 늘어났다.
예를 들면 다음과 같은 응답이 형태가 있다면,
SellerDto, CategoryDto, AddressDto를 만들어야 한다.
"product": {
"seller": {
"id": 1,
"nickname": "나판매자ㅋ"
},
"category": {
"id": 7,
"name": "가구/인테리어"
},
"address": {
"id": 1,
"name": "역삼 1동"
},
"title": "자전거",
"contents": "싸게 팔아요"
}
최종적으로ProductDto을 만들고 DTO를 조합하면 최소 4개의 파일이 필요하다.
완성된 클래스 형태는 대략 다음과 같겠다.
ProductDto
public class ProductDto {
private final SellerDto seller;
private final CategoryDto category;
private final AddressDto address;
private final String title;
private final String contents;
}
SellerDto
public class SellerDto {
// 생략...
}
CategoryDto
public class CategoryDto {
// 생략...
}
AddressDto
public class AddressDto {
// 생략...
}
이렇게 DTO가 늘어나면서 내가 느낀 문제점은 다음과 같다.
ProductDetail, ProductSummary 이후에는 ProductLessDetail 이렇게 할 것인가?ProductCreate, ProductRead, ProductUpdate, ProductDelete 이렇게 이름 짓고 이후에는?VO도 있다면 ProductVo랑 ProductDto의 역할이 더더욱 헷갈린다.DTO 관리 방법을 찾아보다보니 Inner Class를 활용하는 방법이 있어서 프로젝트에 도입해보았다.
Inner Class를 활용하면 여러개의 DTO 하나의 클래스에서 깔끔하게 관리할 수 있다.
수정사항이 생기면 여러 곳을 돌아다니면서 수정할 필요가 없고 위에서 내가 느꼈던 문제점을 대부분 해결할 수 있었다.
또 Request, Response 내용이 많지 않을 경우에도 활용해보았는데 보기에도 깔끔하고 관리하기에도 좋았다.
public class ProductDto {
private final String title;
private final String contents;
private final SellerDto seller;
private final CategoryDto category;
private final AddressDto address;
// 생성자, getter, setter 등 생략
private static class SellerDto {
private int id;
private String nickname;
// 생성자, getter, setter 등 생략
}
private static class CategoryDto {
private int id;
private String name;
// 생성자, getter, setter 등 생략
}
private static class AddressDto {
private int id;
private String name;
// 생성자, getter, setter 등 생략
}
}
실제로 프로젝트에서 사용했던 코드인데, 회원의 프로필 이미지 수정 요청 및 응답을 한 곳에서 관리했다.
public class MemberProfileImgUpdateDto {
@Getter
public static class Request {
private final MultipartFile newProfileImg;
public Request(MultipartFile newProfileImg) {
this.newProfileImg = newProfileImg;
}
}
@Getter
public static class Response {
private final String updatedImgUrl;
public Response(String updatedImgUrl) {
this.updatedImgUrl = updatedImgUrl;
}
}
}
서비스나 컨트롤러에서는 다음과 같이 사용하면 된다.
public MemberProfileImgUpdateDto.Response updateMemberProfileImg() {
// 로직 생략...
return new MemberProfileImgUpdateDto.Response(newImageUrl);
}
Inner Class 로 DTO 관리를 하면서 아직까지 추가적인 문제점은 느끼지 못했다.
다만 여러 곳에서 똑같은 형태로 사용하는 DTO가 Inner Class로 존재한다면 재사용하기 애매한 감이 있다.
이럴 경우 공용 DTO로 분리를 해도 되고, 사실 여러 곳에서 공용으로 사용한다고 꼭 재사용할 필요는 없다고 생각한다.
왜냐면 추후 한 곳에서만 요구하는 정보가 달라질 경우가 있을 수 있다.
물론 개발자는 중복 코드를 싫어하지만, 이때 의도치 않게 여러 곳이 부수적으로 변경될 이슈가 있으니
정보가 똑같아도 DTO가 여러개 존재해도 괜찮다고 생각한다.
아마 프로젝트 규모가 커지면 결국에 Inner Class 로 관리를 하더라도 이런저런 문제점이 생길 것 같은데,
그때 또 고민해보고 개선을 해보겠다!