Project Lombok은 중복되고 반복되는 코드 작성의 불편함을 줄여주기 위한 Java 라이브러리다.
클래스 생성 시마다 반복해서 작성해야하는 생성자
, ToString
, HashCode
, Equals
, Getter/Setter
와 같은 코드들을 애노테이션을 이용해 간편하게 생성해주는 역할을 한다.
@Entity
@Table(name ="member")
@Getter
@AllArgsConstructor
@RequiredArgsConstructor
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
}
예를들어, 위 Member
클래스를 살펴보자
사용된 Lombok 애노테이션은 @Getter
, @AllArgsConstructor
, @RequiredArgsConstructor
다
Lombok은 애노테이션 프로세서를 이용하여 바이트코드 생성 시에 애노테이션에 해당되는 코드를 삽입하고, @Generated
애노테이션으로 표기한다.
@Entity
@Table(
name = "member"
)
public class Member {
@Id
@GeneratedValue(
strategy = GenerationType.IDENTITY
)
private Long id;
private String username;
private String password;
@Generated // @Getter에 의해 생성
public Long getId() {
return this.id;
}
@Generated // @Getter에 의해 생성
public String getUsername() {
return this.username;
}
@Generated // @Getter에 의해 생성
public String getPassword() {
return this.password;
}
@Generated // @AllArgsConstructor에 의해 생성
public Member(final Long id, final String username, final String password) {
this.id = id;
this.username = username;
this.password = password;
}
@Generated // @RequiredArgsConstructor에 의해 생성
public Member() {
}
}
빌드를 통해 생성된 클래스파일을 살펴보면 @Generated
애노테이션과 함께 Getter와 생성자들이 추가된 것을 확인할 수 있다.
@Data
는 클래스파일 생성 시에 자주 사용되는 일련의 애노테이션들을 하나로 묶어놓은 애노테이션이다.
포함되는 애노테이션 목록은 아래와 같다.
@Getter
- 모든 필드에 매칭되는 Getter 생성@Setter
-final
이 아닌 필드에 매칭되는 Setter 생성@RequiredArgsConstructor
-final
혹은NonNull
로 선언된 필드가 포함된 생성자 생성@ToString
-toString()
재정의@EqualsAndHashCode
-equals()
와hashCode()
재정의
언뜻 보기에는 클래스를 생성할 때마다 @Data
를 달아놓으면 추가적인 설정이 필요 없겠다는 생각이든다.
이번 포스팅에서는 웹 애플리케이션에서 클래스 용도를 구분짓는데 사용되는 DTO, VO, Entitiy의 입장에서 정말 그런지에 대해 알아볼 생각이다.
DTO(Data Transfer Object) 는 명칭에서 알아볼 수 있듯이 프로세스간 데이터 전달목적으로 사용되는 객체를 뜻한다.
Spring MVC 기준으로 Controller와 Service간 데이터 전송목적으로 사용되는 객체를 예로 들 수 있다.
Controller에서 요청을 받아 Service에서 처리하기 적합한 데이터만 추출하여 DTO를 생성하고,
Service에서는 DTO를 받아 Entity를 생성하고 DAO 혹은 Repository와 통신을 진행한다.
작성자가 프로젝트를 진행할땐 ○○○Request
, ○○○Response
라는 이름의 클래스를 DTO로 사용했던 기억이 있다.
DTO는 처음 객체가 생성되었을 때의 상태가 유지되지 않을 가능성이 있다.
가변필드와 Setter가 제공된다면 외부에서 상태를 변경할 수 있으므로, final
키워드와 함께 불변객체로 만들어서 관리할 수 있다.
즉, 상황에 따라서 @Setter
가 필요하지 않을 수 있다.
VO(Value Object) 는 값 그 자체를 표현하는 객체를 뜻한다.
long
을 이용해 숫자를 표현하는 것은 가능하지만, 이 숫자가 돈이나 나이와 같은 특별한 값을 나타내지는 못한다.
즉, 한 필드로 표현하지 못하거나 까다로운 속성 값들을 관련도에 맞춰 모아놓은 객체를 VO라고 부른다.
예를들어, 주소 표현에 있어 단순한 문자열을 사용할 수 있지만
동
,지번
등으로 나눠서 관리하는게 유용하다.
이런경우 주소를Address
라는 클래스로 따로 관리할 수 있으며, 이를 VO라고 부른다.
이러한 VO로 표현되는 값들은 어지간해서는 불변하다는 특징을 가진다.
예를들어, 돈의 단위는 10원, 50원, 100원, 500원, 1000원, 5000원, 10000원, 50000원이 있다.
이는 새로운 단위가 추가되거나, 기존 단위가 삭제될수는 있더라도 1000원을 1200원이라고 수정해서 부를 일은 없다.
따라서, 그 특징상 @Setter
를 필요로하지 않는다.
만약, VO로 표현가능한 값 목록이 한정되어있고, static
필드로써 제공한다면,
참조값 그 자체로 비교가 가능하므로 @EqualsAndHashCode
도 필요하지 않을 수도 있다.
Entity는 DB와의 직접적인 통신에 사용되는 객체를 뜻한다.
RDBMS기준, 보통 DB의 테이블과 매칭되는 클래스 구조를 띄고있으며, 식별자값이 존재하는 경우에는 DB에서 가져온 데이터임을 나타낸다. DB에서 가져온 데이터는 수정이 불필요하다는 특징을 가지기 때문에 Entity는 불변하다는 특징을 지닌다.
DB는 애플리케이션 동작을 위해 필요한 데이터들을 관리한다.
엄격한 제약조건을 가지고 있으며, 한번 저장된 데이터는 영구히 보존된다는 특징을 가지고 있다.따라서, DB에서 가져온 데이터는 그 자체로 불변해야하며 수정되어서는 안된다.
즉, Entity에서도 @Setter
는 필요로하지 않는다.
JPA에서
@Entity
를 사용하는 경우에는 기본생성자와 Setter가 반드시 필요하다.
지연로딩을 위해 프록시 객체를 생성하는 과정에서 리플렉션이 사용되기 때문이다.다만, lombok이 생성하는 메서드들은 기본적으로 public이기 때문에 protected 접근제한자를 이용하는걸 고려하자
종합해보자면 @Data
에서 제공하는 애노테이션 기능들이 항상 모두 사용되지는 않는다는 것을 알 수 있다.
특히 @Setter
의 경우, 불변객체 생성에서는 필요로하지 않는다.
@Data
의 @Setter
는 final이 아닌 필드만을 대상으로 Setter를 생성하기 때문에 사용해도 무방하기는 하다.
다만, 클래스를 읽는 타인의 입장에서 Lombok이 명확하게 제공하는 코드들을 판단하는데 혼동을 줄 수 있다.
개인적으로는 무분별한 @Data
사용보다는 필요한 애노테이션만 사용하는 것을 선호하지만,
결국은 팀원들과 잘 조율해서 결정하는게 최선이라고 생각한다.