프로젝트 규모가 커질수록 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
로 관리를 하더라도 이런저런 문제점이 생길 것 같은데,
그때 또 고민해보고 개선을 해보겠다!