DTO에 관한 오해

옹심이·2024년 12월 17일
0
post-thumbnail

나는 DTO를 제대로 알고 사용했나?

DTO가 뭘까 스스로에게 질문 한다면 현재 나의 답은 이렇다. 데이터베이스에 데이터 넣거나 불러올 때 쓰는 것.

DTO를 사용해 본 적 있지만, DTO를 왜 사용하는지 궁금해본 적도 알아본 적도 없다.

DTO에 대한 오해를 풀며 DTO가 무엇인지 제대로 알아볼 것이다.

DTO

DTO(Data Transfer Object)는 직역하면 “데이터 전송에 사용되는 객체”이다.

public class UserCreateRequest {
    public String username;
    public String password;
    public String email;
    public String address;
}

UserCreateRequest는 요청 시 데이터를 전송할 목적으로 만들어진 DTO이다.

왜 DTO를 사용하는걸까?

그 이유는 간단해서이다. 객체의 메서드를 호출할 때 매개 변수에 User에 관한 값을 모두 넣는 방식은 너무 귀찮고 불편하다.

이처럼 DTO는 다른 객체나 시스템에 데이터를 구조화하여 전달하기 위한 객체이다.

이름부터 “나야, 데이터 덩어리”라고 얘기하고 있다.

DTO는 데이터를 전달하는 데 사용되는 데이터 덩어리가 맞기 때문에 그 안에는 데이터를 읽고 쓰는 것 외의 다른 로직이 포함되서는 안된다.

정리해보자

DTO는 매개변수를 하나 하나 전달하는 것이 귀찮아서 만들어진 데이터 덩어리 객체이다

DTO의 역할은 이게 전부이다

DTO에 관한 오해

DTO에 관한 오해
1. DTO는 프로세스, 계층 간 데이터 이동에 사용된다.
2. DTO는 게터, 세터를 가진다.
3. DTO는 데이터베이스에 데이터를 저장하기 위해 사용되는 객체다.

오해 1 - DTO는 프로세스 계층 간 데이터 이동에 사용된다.

맞는 말이지만 DTO를 설명하기에 불충분하다. 이 설명에 따르면 DTO는 API 통신이나 데이터베이스 통신 같은 곳에서 사용하는 객체를 의미하는 것이다.

맞는말인데? 라고 생각할 수 있지만 이것이 DTO은 본질적인 목적은 아니다.

DTO는 조금 더 단순하고 범용적인 개념이다. DTO의 목적은 데이터 전달에 있기 때문에 데이터를 전달하기 위해서는 어디에서든 사용 가능하다.

메서드를 호출하는데 필요한 데이터를 전달할 때 매개변수를 나열하는 것이 불편해서 사용할 수 있고,

원래 여러 번 호출해야 하는 메서드를 하나의 메서드 호출로 바꾸기 위해 DTO를 사용할 수 있다.

따라서 DTO가 어디에서 사용되는지는 중요하지 않다. DTO는 데이터 전송이 필요한 모든 곳에서 사용될 수 있다.

오해 2- DTO는 게터, 세터를 가진다

나를 포함해서 대부분의 사람들이 DTO는 무조건 게터와 세터를 가진다고 알고 있을 것이다. 하지만 이것은 사실이 아니다.

저장한 데이터를 읽고 쓰기 위해 게터와 세터가 무조건 필요한게 아닌가? 반은 맞고 반은 틀리다.

게터와 세터는 DTO의 내부 데이터를 전달하기 위해 수단 중 하나이다.

게터와 세터 없이도 멤버 변수를 public으로 선언하면 내부 데이터에 접근할 수 있다.

그런데 멤버 변수는 원래 private으로 선언 하는게 국룰 아닌가?

자바를 주 언어로 사용하는 나는 이렇게 생각했다. 왜냐하면 이것이 관행이라고 배웠고 지금까지 private으로 선언했기 때문이다.

이러한 관행은 객체 지향 패러다임이 추구하는 캡슐화의 가치를 지키기 위해 사용되는 것으로, 어떤 객체의 속성 값을 모두 감춰서 외부의 직접적인 접근을 막고 메서드를 통해 간접 접근으로 안정성과 유연성을 확보하기 위함이다.

@Getter @Setter
public class UserCreateRequest {
    private String username;
    private String password;
    private String email;
    private String address;
}

그렇다면 private으로 선언한 멤버 변수를 게터와 세터로 직접 접근하는 것은 괜찮은걸까?

생각해보니 이러면 멤버 변수를 private으로 선언할 이유가 없다.

이 코드는 사실상 멤버 변수를 public으로 선언한 것과 다를 것이 없으며 오히려 데이터 접근이 불편하기만 하다.

다른 방법도 살펴보자

class UserCreateRequest {
    public final String username;
    public final String password;
    public final String email;
    public final String address;
    
    @Builder
    public UserCreateRequest(
            @JsonProperty("username") String username,
            ... 이하 생략
    ){
        this.username = username;
        ... 이하 생략
    }
}

JSON 형식으로 문자열을 통한 데이터 통신이 가능하며, 최초 데이터 할당 후 불변성을 보장하여 데이터를 보호 할 수 있게 되었다.

이처럼 게터와 세터를 사용하지 않고도, 다양한 방법으로 데이터 통신이 가능하다. 데이터를 전달한다는 임무를 수행한다면 DTO라고 볼 수 있다.

그렇다고 멤버 변수 선언 시 Public을 남발하는 것은 옳지 않다. user.getEmail()과 user.email은 엄연히 다르다. email은 속성에 의존하는 것이고 getEmail()은 행위에 의존하기 때문이다.

또한 캡슐화를 통한 정보 은닉을 위해서라고 멤버 변수를 private으로 선언하고 일부 변수에 대해 게터를 사용하는 것이 좋은 방안이다.

오해3 - DTO는 데이터베이스에 데이터를 저장하는 데 사용되는 객체다

DTO의 데이터가 데이터베이스의 데이터로 오해할 수 있다.

하지만 앞서 말했 듯 DTO 데이터 전달을 위한 객체이며 모든 곳에서 사용될 수 있다.

request body, response body, 데이터베이스에서 데이터를 불러오고 저장할 때 사용되는 객체

전부 DTO이다.

외람된 얘기지만 개발자들은 @Data를 많이 사용한다.

이 어노테이션은 Getter, Setter, ToString, EqualsAndHashCode를 한 번에 저장할 수 있도록 도와준다.

하지만 이를 무분별 하게 사용하는 것은 객체 지향 코드를 만드는데 방해가 된다.

@Data를 선언하는 순간 이 객체는 데이터 덩어리라고 명시하는 것과 같다.

이는 곳 행동 위주의 사고를 추구하는 객체 지향과 멀어지는 지름길이다.

마치며

DTO를 사용해 본 적 있지만, 올바르게 사용하는지 스스로 판단을 할 수 없었고 정확히 어디에 사용하는 건지 몰랐던 것 같다.

DTO에 관한 세 가지 오해를 풀어 나가며 DTO를 어떤 이유를 위해 어떤 곳에서 사용되는 객체라고 단정 짓기 보다 범용적인 개념으로 받아들이고 사용하는 것이 좋다는 것을 알게되었다.

또한 멤버 변수 선언 시 '관행'이라는 단어 뒤에 숨어 private을 남발하던 나의 모습을 반성하게 되었다.

결론적으로, 구현은 개발자의 몫이며 정답보다는 더 나은 방법을 찾는 것이 개발자로서의 숙제인 것 같다.

0개의 댓글