@Builder 어노테이션은 내가 가장 애용하는 도구 중 하나다. 객체 생성 시 높은 유연성을 제공하며, Spring을 사용할 때 자주 활용했다. 하지만 이러한 의존성이 때로는 과도했음을 돌아보게 되었다.
@Builder의 유연성
@Builder의 가장 큰 장점은 객체 생성의 유연함이다. 모든 매개변수를 한꺼번에 전달할 필요 없이, 가독성 있고 깔끔하게 객체를 생성할 수 있다. 이는 특히 복잡한 객체나 선택적 필드를 가진 객체를 생성할 때 매우 유용하다. 하지만 강력한 도구일수록 신중하게 사용해야 한다.
Entity 클래스에서 @Builder의 문제점
특히 DDD(Domain-Driven Design) 관점에서, 엔티티는 무결성과 캡슐화를 엄격히 준수해야 한다. @Builder가 제공하는 유연성은 이러한 원칙을 훼손할 수 있다. 예를 들어, @Builder는 생성자 기반의 검증이나 비즈니스 규칙을 우회할 수 있어 잘못된 엔티티가 생성될 수 있다. 이는 유지보수가 어렵고 취약한 시스템을 초래할 수 있다.
DDD에서 엔티티는 단순한 데이터 운반자가 아니라, 행동과 상태를 캡슐화하는 도메인 개념을 나타낸다. 이러한 객체를 생성하는 데 과도한 유연성을 부여하면 관리와 유지보수가 어려워질 수 있다.
도메인 객체와 엔티티의 분리
DDD를 깊이 공부하면서 도메인 객체와 엔티티를 분리하는 방식을 접하게 되었다. 이러한 분리는 Lombok 같은 외부 라이브러리에 대한 의존성을 최소화하고, 핵심 도메인 로직을 순수하게 유지하는 데 도움을 준다. 도메인 객체를 집중적이고 순수하게 유지함으로써 코드베이스를 더 유지보수하기 쉽게 만들 수 있다.
예를 들어, 엔티티 클래스에 직접 @Builder를 적용하는 대신:
- 객체 생성 로직을 캡슐화하는 전용 팩토리나 빌더 클래스를 사용한다.
- 엔티티 내부에서 필수 비즈니스 규칙을 강제하는 생성자를 활용한다.
- DTO나 엄격한 검증이 필요하지 않은 클래스에 @Builder를 사용한다.
이러한 접근 방식은 명확성, 관심사의 분리, DDD 원칙의 더 나은 준수를 촉진한다.
SOLID 원칙 준수
유지보수 가능한 코드를 작성하는 것은 종종 SOLID 원칙을 따르는 것에서 시작된다. @Builder는 때로는 중요한 설계 단계를 생략하도록 유혹할 수 있다. 하지만 이러한 원칙을 염두에 두면 유연성과 견고성 사이의 균형을 맞출 수 있다.
- 단일 책임 원칙(SRP): 객체 생성을 빌더나 팩토리가 처리하고, 엔티티 자체는 처리하지 않아야 한다.
- 개방-폐쇄 원칙(OCP): 캡슐화를 통해 엔티티는 수정에는 닫혀 있고, 제어된 동작을 통해 확장 가능해야 한다.
- 리스코프 치환 원칙(LSP): 엔티티는 대체 시 예측 가능하게 동작해야 하며, 이를 위해 엄격한 검증이 필요하다.
- 인터페이스 분리 원칙(ISP): 엔티티는 최소한의 의미 있는 인터페이스만 노출해야 한다.
- 의존성 역전 원칙(DIP): 도메인 로직은 Lombok과 같은 외부 라이브러리에 의존하지 않아야 한다.
앞으로 나아갈 길
@Builder를 사랑하지만, 최근 DDD 학습을 통해 이를 더 신중하게 사용해야 함을 깨달았다. DTO 생성 등 유연성이 진정으로 가치를 더하는 사용 사례에 한정하고, 엔티티에서는 이를 피함으로써 기능적이면서도 유지보수 가능한 코드를 작성하고자 한다.
그간 너무 생산성이라는 핑계로 좋은 코드를 짜는 것과는 거리가 멀어진 것 같다.
앞으로는 생산성과 코드 품질의 균형을 맞추며, 더 나은 코드를 작성하기 위해 꾸준히 고민하고 실천해야겠다.
'코딩딩 > Spring' 카테고리의 다른 글
JPA에서 createdAt과 updatedAt 관리: @PreUpdate vs @UpdateTimestamp (DDD 관점) (1) | 2024.12.27 |
---|---|
의존성, 의존성 주입, 그리고 테스트 가능성(Testability) (0) | 2024.12.11 |
TDD와 BDD, 그리고 테스트 작성의 기본 개념들 (3) | 2024.12.11 |
테스트 코드, 어디까지 작성해야 할까? (5) | 2024.12.11 |
Mockito를 활용한 단위 테스트 정리 (3) | 2024.12.11 |