문제의 시작

Lombok은 Java 개발에서 반복 코드를 줄여주지만, 편하다는 이유만으로 모든 필드에 getter와 setter를 열어두면 객체의 책임이 흐려진다. 이 글은 단순한 코드 스타일 문제가 아니라, domain object가 자신의 상태를 어떻게 지켜야 하는지에 대한 작은 기준을 정리한 기록이다.

Spring Boot 의 Lombok annotation 중 흔하게 @Getter 와 @Setter 를 사용한다.

구현하면서 확인한 흐름

public class Person {
    private String name;
}

Person 이라는 class를 정의한다.

    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }

그러면 class 내에 이런 식으로 getter 와 setter 함수를 만든다. @Getter 와 @Setter 는 이런 귀찮은 함수 정의를 빠르게 해 준다.

개발 속도가 빨라지고, boilerplate code의 감소, 쉬운 리팩토링 등 여러 장점이 있다.

@Getter(AccessLevel.NONE)

설계 기준

Getter와 setter를 줄인다는 것은 코드를 불편하게 만들자는 뜻이 아니다. 상태 변경의 의도를 method 이름에 드러내고, 외부에서 객체를 마음대로 조작하지 못하게 하자는 뜻이다. 작은 annotation 하나도 domain boundary를 흐리게 만들 수 있다는 점을 기억해야 한다.

이런 식으로 접근권한 또한 제어할 수 있다.

이런 편의성 때문에 Entity 혹은 DTO, VO 등 구현 시 너도나도 @Getter 와 @Setter 를 남발하며 Class 정의 이전 우선 박고 들어가는게 습관이 되어버린다.

이 글에서는 @Getter 와 @Setter 의 남용에 따른 여러 drawback 을 다뤄본다.

Encapsulation Violation

@Getter 와 @Setter 로 필드를 드러냄으로써 캡슐화의 특징을 훼손시킨다. OOP의 핵심 특징 중 하나인 캡슐화, 즉 정보은닉으로 데이터를 외부로부터 보호하는 것의 가치가 떨어진다는 단점이 있다. public 함수로 인해 엔티티의 상태를 아무 제약 없이 변경할 수 있기 때문이다.

의도치 않은 충돌

엔티티의 필드에 대한 직접 권한을 허락하여 예기치 못한 버그가 생겨난다. 예를 들어, JPA 엔티티의 `id` 필드를 수정하면 데이터베이스와의 관계에 결함이 생길 수 있다.

혹은 여러 엔티티와 엔티티의 관계가 형성된 이후의 변경이 있을 시 data inconsistency가 발생할 수 있다.

API 오염

@Getter 와 @Setter 가 모든 필드에 추가되면 public API 또한 기하급수적으로 늘어난다. 가령 Swagger를 사용하여 API 를 문서화할 경우 핵심 API 들과 getField, setField 등의 API 들이 섞여 비즈니스 로직에 필수적인 것과 그렇지 않은 것에 대한 혼돈이 발생한다.

필요가 없는 경우

엔티티에서 @Getter 와 @Setter 가 아예 필요가 없는 경우도 있다. 예를 들어 DTO를 사용하면 client가 원하는 data에 대한 public 접근이 굳이 필요하지 않는 경우가 있다.

Lazy Loading

JPA 엔티티의 몇몇 필드는 성능의 향상을 위해 lazy loading 으로 설정해놓는 경우가 있다. 실시간 Hibernate session 중 이런 필드들을 @Getter 를 사용하여 가져올 시 LazyInitializationException 등이 발생할 수 있다. 더한 경우 의도치 않은 database query가 실행될 수 있다.