[Spring] DTO 관리 - Inner Class

Jinny·2023년 10월 2일
1

Spring

목록 보기
8/10

🧐 고민

프로젝트 규모가 커질수록 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가 늘어나면서 내가 느낀 문제점은 다음과 같다.

1. 랩핑된 형태가 많아질 수록 파일이 늘어난다.

  • 단순히 파일이 늘어나는 것이 문제가 아니라 2번 3번 문제가 1번 문제로 인해 파생된다.

2. 비슷한 역할을 하지만 응답 형태가 다른 DTO가 많은데, 클래스 이름 짓기가 힘들다.

  • ProductDetail, ProductSummary 이후에는 ProductLessDetail 이렇게 할 것인가?
    • 마치 최종, 최최종, 최최최종처럼...
  • 아님 CRUD에 따라 ProductCreate, ProductRead, ProductUpdate, ProductDelete 이렇게 이름 짓고 이후에는?
  • VO도 있다면 ProductVoProductDto의 역할이 더더욱 헷갈린다.

3. 어떤 DTO가 어디에서 쓰이고 있는지 헷갈린다.

  • 프로젝트 규모가 작을 때는 문제가 되지 않았는데,
    규모가 커지고 수정사항이 생길 때 어떤 DTO가 어디서 조합되어서 사용되고 있는지 찾는데 시간이 오래 걸린다.


⚡️ 개선 방법

DTO 관리 방법을 찾아보다보니 Inner Class를 활용하는 방법이 있어서 프로젝트에 도입해보았다.

Inner Class를 활용하면 여러개의 DTO 하나의 클래스에서 깔끔하게 관리할 수 있다.
수정사항이 생기면 여러 곳을 돌아다니면서 수정할 필요가 없고 위에서 내가 느꼈던 문제점을 대부분 해결할 수 있었다.

Request, Response 내용이 많지 않을 경우에도 활용해보았는데 보기에도 깔끔하고 관리하기에도 좋았다.

1. 여러개의 DTO를 Inner Class로 관리

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 등 생략
    }
}

2. Request & Resonse를 Inner Class로 관리

실제로 프로젝트에서 사용했던 코드인데, 회원의 프로필 이미지 수정 요청 및 응답을 한 곳에서 관리했다.

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 ClassDTO 관리를 하면서 아직까지 추가적인 문제점은 느끼지 못했다.

다만 여러 곳에서 똑같은 형태로 사용하는 DTOInner Class로 존재한다면 재사용하기 애매한 감이 있다.
이럴 경우 공용 DTO로 분리를 해도 되고, 사실 여러 곳에서 공용으로 사용한다고 꼭 재사용할 필요는 없다고 생각한다.

왜냐면 추후 한 곳에서만 요구하는 정보가 달라질 경우가 있을 수 있다.
물론 개발자는 중복 코드를 싫어하지만, 이때 의도치 않게 여러 곳이 부수적으로 변경될 이슈가 있으니
정보가 똑같아도 DTO가 여러개 존재해도 괜찮다고 생각한다.

아마 프로젝트 규모가 커지면 결국에 Inner Class 로 관리를 하더라도 이런저런 문제점이 생길 것 같은데,
그때 또 고민해보고 개선을 해보겠다!

profile
공부는 마라톤이다. 한꺼번에 많은 것을 하다 지치지 말고 조금씩, 꾸준히, 자주하자.

0개의 댓글