Junlog

Lombok 짚고가기

ByYoon Woo Jun
7

Lombok 이렇게 써도 괜찮을까?

그동안 Java와 Spring boot를 사용해 구현한 백엔드 프로젝트에 Lombok을 사용하면서, 슬슬 의문점이 들기 시작했다.

  1. Lombok이 갑자기 지원 중단되면 어떻게 대처할지
  2. 무분별하게 사용되는 어노테이션들을 어떻게 관리할지
  3. 너무 Lombok에 의존하고 있는건 아닌지

한참 예전 글이지만, 이에 대해 고민하던 내용들을 레딧에서 확인할 수 있었다.
롬복에 대한 고민이 적힌 레딧 본문

롬복이 쓰이게 된 배경은 많은 양의 보일러플레이트를 줄일 수 있다는 편리함 때문이고, 이러한 편리함은 Kotlin에서 어느정도 지원하기 때문에
Kotlin에서는 롬복을 당연히 안쓴다.
하지만, Java의 경우 많은 래퍼런스 코드에서 Lombok을 사용하고 있다.

장점과 단점을 정리해보자면 다음과 같다.

장점

  1. 보일러플레이트를 줄임으로써 가독성, 개발 속도 향상 및 빠른 코드 리뷰
  2. 다양한 기능을 어노테이션을 사용해 적용

단점

  1. 자동 생성으로 인한 문제 발생시 디버깅 어려움
  2. Lombok에 대한 의존 문제

사용을 옹호하는 쪽은 장점을 보통 말하고, 반대는 단점과 함께 Record를 사용하거나, IDE의 자동생성 기능을 사용하면 되는거 아니냐는 의견 등으로
갈리고 있다.


그럼 나는 어떻게 할까?

여러 라이브러리를 사용하면 지원 중단된 경우를 이따금 겪어보아 여러 종속성이 생기는 경우가 맘에 들지는 않지만, 어느 정도 제한을 둬서 사용하는 걸로 결정했다.

Lombok의 편리함은 그대로 가져가고, 문제가 생길만한 Lombok의 기능들은 제한하기로 한 것이다.

팀원들과 상의하고, 나는 프로젝트 루트 경로에 다음과 같은 lombok.config 파일을 추가해, 특정 어노테이션 사용을 막았다.

1config.stopBubbling = true
2
3lombok.data.flagUsage = error
4lombok.setter.flagUsage = error
5lombok.toString.flagUsage = error
6lombok.value.flagUsage = error
7lombok.val.flagUsage = error
8lombok.var.flagUsage = error
9lombok.nonNull.flagUsage = error
10
11lombok.allArgsConstructor.flagUsage = error
12
13lombok.cleanup.flagUsage = error
14lombok.sneakyThrows.flagUsage = error
15lombok.synchronized.flagUsage = error
16
17lombok.experimental.flagUsage = error
  • stopBubbling으로 상위에 있는 lombok.config는 무시하고, 하위에 모듈에 둔 lombok.config는 허용해 개별적으로 적용할 수 있도록 했다. (없어도 그만이지만, 나중에 특정 모듈만 다른 config를 적용할수도 있을 것 같아서 추가했다.)
  • Data는 너무 많은 기능을 가지고 있어, 명시적으로 사용하기 힘들고 무분별하게 남용할 가능성이 있어서 금지했다. (캡슐화 깨짐)
  • Setter의 경우에도 클래스 전체에 걸면, 많은 필드들의 변경 가능성이 열려 따로 메서드로 구현하도록 금지했다. (불변성 깨짐)
  • toString은 객체의 민감한 정보가 노출되는 경우 때문에 금지했다. (잘못된 사용으로 인한 보안및 무한 참조 문제 가능성)
  • Value, val, var, nonNull은 실용성이 없다고 판단해 금지했다. (val, var의 타입 변환 문제 가능성 및 실용성 문제)
  • allArgsConstructor는 직접 생성자를 선언해서 사용하기 위해 금지했다. (생성자 필드 순서 문제 가능성)
  • 그 외에는 잘 사용하지 않아 팀원 전체 학습 비용 증가 및 실험적인 기능이여서 금지했다.

빠진 부분이 있을수도 있겠지만, 결론적으로 현재 진행중인 프로젝트에서는 Lombok의 여러 기능중에 사용하는 기능은 다음과 같다.

  • @Getter
  • @NoArgsConstructor
  • @RequiredArgsConstructor
  • @Builder
  • @EqualsAndHashCode

위 5가지 기능도 문제가 생길 수 있지만, 어느 정도 대처할 수 있다고 판단했고, 5가지 만으로 충분히 개발 편의성은 챙길 수 있을거라 판단했다.


사용하는 기능에 대한 설명

@Getter는 크게 문제 없이, 여러 곳에서 용이하게 사용할 수 있다고 판단했다.
Record는 getter 메서드가 필드 명이지만, @Getter를 사용해서 생성한 getter는 get"필드 명"이라 혼동되어 불편하지 않을까 했지만, 사용하면서 크게 문제되었던 적은 없어서 허용했다.

@NoArgsConstructor는 보통 static한 class를 추가할 때 또는 영속성 엔티티를 추가할 때 사용하기로 했다.
이것도 없어도 크게 불편한점은 없을것 같지만, 기왕 Lombok을 사용할거면 이정도는 허용하는게 맞다고 판단했다.
특히, 이 기능이 붙어있으면, 명확하게 어떤 로직인지 파악할 수 있다는 점과 접근 레벨을 낮게 잡으면 크게 문제될 부분이 없다고 생각했다.

@RequiredArgsConstructor는 Bean 주입을 깔끔하게 처리한다는 점과
Bean을 생성자로 주입하는 것은 해당 클래스가 의존중인 Bean을 확실하게 파악할 수 있다는 장점 대신 가독성을 더 중요시 생각해 허용했다.

@Builder는 정말 dto, entity 등 다양한 객체에서 용이하게 사용할 수 있다고 판단했다.
@EqualsAndHashCode는 @Include를 사용해서 해당 필드가 동일한 경우 같은 객체라 판단하도록 하여, 보통 id가 정의된 필드에 사용하면
편리할거라 판단해 허용했다.


결론

사실 롬복 자체를 사용하지 않는 방향도 100번 고민했지만, 이는 가독성과 개발 속도를 포기한다는 것과 같다고 판단했다.
IDE의 자동 생성 기능으로 커버가 가능하다고 해도, 결국 생기는 보일러플레이트는 오히려 관리해야하는 지점을 늘린다.

당연히 위에 허용한 5개의 기능도 여러 단점과 위험한 부분이 있지만, 사용처나 사용 방식을 컨밴션으로 엄격하게 사용함으로써, 지금까지 사용하면서 큰 문제 없이 잘 사용했다.

당연히 시간이 지나면서 이 결론이 바뀔수도 있지만, 이렇게 한번 짚고 넘어가면서, 불필요하게 팀원들과 논의할 시간을 줄이고, 하나의 종속성에 대한 트레이드 오프를 고민할 수 있었다.

쓸지 말지는 진리의 팀바팀인것 같다. 팀원들과 논의하고 결정한대로 진행하는게 정답이라고 생각한다.
사용하는 기능이 몇개 되지 않고, 단점이 크게 느껴지면, 깔끔하고 맘편하게 쓰지말자