외부 API를 연동하기 위해 RestClinet를 사용하던 중 아래와 같은 코드를 마주했다. 여기서 builder()가 이 글의 주제다. 결론부터 말하자면 builder 패턴은 때론 anti pattern 이 될 수 있다. 특히 POJO에선 anti pattern 으로 사용하게 될 확률이 높다. 이에 대해 Spring Framework Docs에서도 사용하고 있는데 좋은거 아니야? 라고 반문할 수 있다.
When Builder is anti-pattern
builder 패턴이 왜 문제가 될 수 있는지를 살펴보면 해당 질문에 대한 해답을 어느 정도 얻어갈 수 있다.
아래 아티클의 내용 중 핵심을 뽑아보자면 아래와 같다.
- builder를 사용할 땐 npe가 터지지 않도록 모든 필드들에 default 값을 설정해라.
- builder는 특정 상황에서 유용하다. 예를 들어 환경 설정을 다룰 때엔, 개발자가 필요에 따라 특정 설정만을 명시적으로 변경할 수 있게 하기 위해 builder가 유용할 수 있다. (POJO 는 오히려 적합X)
Wrong Example
사실 나는 프로젝트를 할 때 builder를 애용했었다. 예전에 내가 느꼈던 builder의 장점은 필드와 생성자가 많아졌을 때 사용하는 쪽에서 어떤 필드를 어느 순서로 집어넣어야 하는지 정해, 생성자를 잘못 사용하는 실수를 줄일 수 있단 점이었다.
실제로
new Member("이름",…"전화번호", "주소")와 같이 사용할 때, 필드가 없어졌다 추가됐다를 반복하다가 new Member("이름", …"주소", "전화번호")과 같이 사용해 두 필드의 순서를 바꿔 생성하는 실수를 한적이 있었다. 이는 컴파일 타임은 커녕, 런타임에도 잡히지 않아 나아아중에 해당 데이터를 사용할 때 겨우 알아냈던 경험이 있다.이때 프로젝트 전반적으로
Member.builder().name("이름").phone("전화번호").address("주소")와 같이 builder 패턴을 도입해 개발자의 실수를 줄이는 데 도움을 받았었는데, 지금와서 다시 생각해보면 위 케이스의 경우 builder 패턴이나, 생성자나 어차피 런타임에 잡히지 않는단 건 동일한 데 굳이..? 싶다. 인텔리제이가 친절하게 생성자 매개변수에 대한 미리보기 기능도 지원하기에 builder만이 주는 엄청난 메리트는 없었지 않나 싶다.더 나아가 애초에 사용하는 쪽에서 어떤 필드를 어느 순서로 집어넣을지 정한다는게 객체지향관점에서 모순적이다. 순서가 뒤죽박죽이 되어 오히려 관리포인트가 늘어나는 단점이 따를 수 있다.
Conclusion
오히려 위의
Member 케이스의 경우, 객체의 각 필드에 대한 검증이 필요했던 시점 같다. 앞으론 만일 설계한 도메인 객체에 builder가 쓰고 싶다면, 객체 검증이 부족하진 않은지, 객체의 역할분담이 제대로 되었는지를 한 번 살펴봐야겠다. 참고로 글 초반부에 등장한 Docs의 예시는 환경 설정을 다루는 경우이므로 이 글에서 언급하는 anti pattern이 아니다.

