밤빵's 개발일지
[TIL]20241116 @Data를 써도 될까? 본문
롬복은 자바에서 코드를 더 간결하고 효율적으로 작성할 수 있게 도와주는 도구로, 자주 사용되는 메서드들을 어노테이션 하나로 자동 생성해 주기 때문에 많은 개발자들이 사용하고 있다. 그중에서도 @Data 어노테이션은 요즘 내가 자주 사용하는 어노테이션 중 하나로,사용하는 것이 정말 괜찮은 것인지, 특히 엔티티(Entity)나 DTO(Data Transfer Object)에 사용해도 되는지에 대한 고민이 생겼다. 이번 개발일지를 통해 @Data 어노테이션에 대해 학습한 내용을 정리하고, 그에 대한 생각을 정리했다.
▶@Data 어노테이션이란?
@Data 어노테이션은 롬복에서 제공하는 어노테이션으로, 클래스에 자동으로 getter, setter, toString, equals, hashCode 메서드를 생성해 준다. 또한 필드의 변경을 쉽게 하기 위해 setter 메서드를 자동으로 추가해 주기 때문에, 굳이 모든 필드를 수동으로 관리하지 않아도 된다는 점에서 개발자의 작업량을 크게 줄여준다. 처음에는 이러한 편리함 때문에 @Data를 마구 사용하기 시작했지만, 실제로 학습해 보니 몇 가지 문제점이 있다는 것을 알게 되었다.
특히 엔티티나 DTO와 같은 중요한 클래스에 @Data를 사용할 경우, 데이터 불변성(immutability)에 문제가 생길 수 있다는 점을 알게 되었다. 데이터 불변성은 객체의 상태가 변경되지 않도록 보장하는 것을 말하는데, 이는 유지보수성과 데이터 안정성에 매우 중요한 개념이다. @Data는 모든 필드에 대해 setter 메서드를 자동 생성하기 때문에, 불변성을 유지해야 할 필드까지 외부에서 변경할 수 있는 위험이 있다.
▶엔티티(Entity)에서 @Data를 사용할 때의 문제점
엔티티는 데이터베이스와 직접적으로 매핑되는 클래스이며, 비즈니스 로직의 중요한 부분을 담고 있는 객체이다. 엔티티는 보통 JPA(Java Persistence API)와 함께 사용되는데, 데이터베이스의 테이블과 연동되어 데이터를 관리하게 된다. 따라서 엔티티의 필드는 데이터베이스의 상태를 반영하며, 데이터의 무결성과 일관성을 유지해야 하는 것이 매우 중요하다.
@Data를 사용하면 모든 필드에 대한 setter 메서드가 생성되기 때문에, 외부에서 엔티티의 상태를 쉽게 변경할 수 있다. 이는 예기치 않은 시점에 데이터가 변경되는 상황을 초래할 수 있으며, 데이터베이스와의 일관성을 해칠 위험이 있다. 예를 들어, 주문 상태와 같이 특정 로직에 따라 신중하게 변경되어야 하는 필드도 외부에서 마음대로 수정될 수 있기 때문에, 엔티티의 무결성을 위협할 수 있다. 이런 이유로 엔티티 클래스에는 @Data 대신 필요한 필드에만 setter를 명시적으로 작성하는 것이 좋다.
▶DTO에서 @Data를 사용할 때의 문제점
DTO는 주로 클라이언트와 서버 간의 데이터 전송을 위한 객체로, 데이터를 담아 요청(Request)이나 응답(Response)을 처리하는 데 사용된다. DTO는 클라이언트와의 인터페이스 역할을 하기 때문에 가급적 불변성을 유지하는 것이 좋다. DTO의 불변성을 유지하면, 클라이언트와의 데이터 교환 시 데이터가 변경될 위험이 줄어들어 안정적인 데이터 전송이 가능해진다. 하지만 @Data를 사용하면 모든 필드에 대해 setter 메서드가 자동 생성되므로, DTO 객체가 외부에서 변경 가능해진다. 이는 DTO의 불변성을 해치며, 데이터의 의도치 않은 변경으로 인해 버그나 안정성 문제가 발생할 수 있다. 따라서 DTO에는 @Data 대신 @Getter와 @AllArgsConstructor 또는 @Builder와 같은 어노테이션을 사용하는 것이 더 적합하다. 이를 통해 필요한 값만 설정하고, 객체 생성 이후에는 상태를 변경하지 않도록 보장할 수 있다.
▶대안: @Getter, @Builder, @Value 사용하기
엔티티나 DTO에 @Data를 사용하는 것의 단점을 보완하기 위해 대표적인 대안으로는 다음과 같은 어노테이션들이 있다.
- @Getter와 @Setter의 제한적 사용
- 엔티티에서는 모든 필드에 대해 setter 메서드를 제공하지 않고, 꼭 필요한 경우에만 setter를 제공하는 것이 좋다. 예를 들어, 특정 비즈니스 로직에서만 변경되어야 하는 필드라면, setter를 사용하지 않고 비즈니스 로직 메서드를 통해 필드 값을 변경하도록 설계한다.
- @Getter를 사용해 필드의 값을 외부에서 읽을 수 있게 하되, setter는 필드에 따라 선택적으로 제공한다.
- @Builder를 사용한 객체 생성
- 빌더 패턴을 사용하면 객체 생성 시 명확하게 어떤 값들이 설정되는지 알 수 있어 가독성이 높아진다. 또한 필수 값만 설정하고 나머지는 기본값을 사용할 수 있어 유연한 객체 생성이 가능하다.
- DTO나 특정 경우의 엔티티에 대해서도 빌더 패턴을 사용하여 객체를 생성하면 불변성을 유지할 수 있다.
- @Value 사용하여 불변 객체 만들기
- DTO에는 @Value 어노테이션을 사용하는 것이 좋다. @Value는 모든 필드를 private final로 설정하고, getter만 제공하며, setter는 제공하지 않는다. 이를 통해 DTO 객체가 불변임을 보장할 수 있다.
▶매퍼(MapStruct)를 활용한 엔티티와 DTO 변환
엔티티와 DTO 간에는 서로 다른 목적을 가지기 때문에, 이를 서로 변환해 주는 작업이 필요하다. 처음에는 엔티티에 @Data를 사용해 간편하게 처리하려 했으나, 매핑 과정에서의 데이터 불변성을 보장하기 위해 매퍼 도구를 사용하는 것이 좋다는 것을 알게 되었다. 대표적인 매퍼 도구로는 MapStruct가 있다. MapStruct는 컴파일 타임에 타입 안전한 매핑 코드를 생성해 주기 때문에, DTO와 엔티티 간의 변환을 효율적으로 처리할 수 있다. 이를 통해 수동으로 매핑 코드를 작성하는 번거로움을 줄일 수 있고, 매핑 과정에서 발생할 수 있는 실수를 최소화할 수 있다.
편리함 때문에 @Data를 사용하고 싶었지만, 엔티티와 DTO의 역할을 고려할 때 데이터 일관성과 불변성을 유지하는 것이 더 중요하다는 것을 알게 되었다. 대신 @Getter, @Builder, @Value와 같은 대안을 사용하여 객체를 안전하게 생성하고 관리하는 방법을 선택하기로 했다. 또한, 엔티티와 DTO 간의 변환에서는 매퍼 도구를 활용하여 효율적이고 안전하게 매핑을 수행하는 것이 좋다.
'개발Article' 카테고리의 다른 글
[TIL]20241118 Spring Security에서 권한별 URL 접근 제어에 대한 고민 (1) | 2024.11.19 |
---|---|
[TIL]20241117 TDD (1) | 2024.11.17 |
[TIL]20241115 소프트웨어에서 도메인이란? (9) | 2024.11.15 |
[TIL]20241114 인터페이스와 추상클래스의 용도 차이? (0) | 2024.11.14 |
[TIL]20241113 MYVM 디자인 패턴 (3) | 2024.11.13 |