우리는 스토리보드를 보고 Api 명세를 작성할 때 테이블 설계와 동시에 Request와 Response 등 DTO 디자인을 고민하곤 한다. 하지만 기획 당시 계획한 디자인을 준수하여 개발을 하다보면 프로젝트 패키지와 소스들이 지저분해지는 경우가 있다.
이제부터 더 간결한 코드를 고민하는 시간을 가지며 위 문제점들을 개선한 내용을 정리하고자 한다.
프로젝트 내 VO 혹은 DTO 패키지 안에 필요할 때마다 Class파일을 생성하면 위 사진과 같이 파일수가 매우매우 많아진다.
여기서 파생되는 문제점들은..
- 일단 그냥 보기에 더럽다.
(흐그는드)- 더 이상 ClassName이 중복되지 않는 DTO를 만들기가 어려워진다.
- 필드들이 겹치는 DTO로 대충 Response를 내리다보니 Over-Fetching을 하게된다.
그렇다면 같은 기능을 수행하며 DTO 패키지 내 클래스 파일을 깔끔하게 관리할 수 있는 방법이 있을까?
필자가 주로 사용하는 방법은 Inner Class(Nested Class)로 DTO를 관리하는 것이다.
간단한 예시로 User정보를 저장하는 api와 조회하는 api를 만드는 상황을 가정해보자.
떠오르는 DTO를 나열하자면
- POST Api에서 Request Payload를 매핑할 DTO
- GET Api에서 Return해줄 Response DTO
- 레이어를 옮겨다니거나 결과를 Return하기 위해 실제 User정보를 담은 DTO
홀리몰리.. 우리는 벌써 2~3개의 DTO을 생성하여야한다.
하지만 User라는 도메인에 관련된 DTO들을 Class 하나에 묶어준다는 생각으로 User Class를 만든 후 Inner Class로 DTO들을 구현한다면..?
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
public class User {
@Getter
@AllArgsConstructor
@Builder
public static class Info {
private int id;
private String name;
private int age;
}
@Getter
@Setter
public static class Request {
private String name;
private int age;
}
@Getter
@AllArgsConstructor
public static class Response {
private Info info;
private int returnCode;
private String returnMessage;
}
}
위와 같이 1개의 Class파일로 깔끔하게 관리할 수 있게 된다. 세밀한 Class 구조는 취향대로 바꿔서 써도 무방할 듯하다.
import com.parksh.demo.dto.DefaultResponse;
import com.parksh.demo.dto.user.User;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping(“user”)
public class UserController {
@GetMapping(“/{user_id}”)
public User.Response getUser(@PathVariable(“user_id”) String userId) {
return new User.Response(new User.Info(), 200, “success”);
}
@PostMapping
public DefaultResponse addUser(@RequestBody User.Info info) {
return new DefaultResponse();
}
}
(Controller 구현 부분)
이 방법을 채택하여 DTO를 관리한다면 조금 더 깔끔한 패키지를 만들 수 있고, DTO ClassName을 정하는게 수월해질 것이다.
(귀찮아서 Over-Fetching을 하지 말자!)
DTO 설계 관련해서 중복 코드등이 많아 지는것 같아서 고민이 많았는데! 보는 순간 이거다 싶었어요 감사합니다