@AllArgsConstructor를 지양하는 이유 그리고 @Builder와 함께 쓸 때

규바·2024년 10월 20일
1
post-thumbnail

저번에 지나가다가 본건데 제 동심이 짓밟혔어요

개요

문득 엔티티 클래스에 @Builder를 붙이다가 생각이 들었습니다.
왜 @AllArgsConstructor 쓰지 말라고 했더라.

그리고 여러 프로젝트에서 @Builder와 함께 @AllArgsConstructor를 쓰는 것에 대해서


@AllArgsConstructor를 지양하는 이유

@AllArgsConstructor는 클래스에 존재하는 모든 필드에 대한 생성자를 자동으로 생성하는데, 인스턴스 멤버의 선언 순서에 영향을 받기 때문에 변수의 순서를 바꾸면 생성자의 입력 값 순서도 바뀌게 되어 검출되지 않는 치명적인 오류를 발생시킬 수 있습니다.

본 주제를 검색했을 때 블로그들에 복붙으로 여럿 적혀있는 글이다. 검출되지 않는 치명적인 오류라는 것은 조금 과장인 것 같아요.

정확히는 순서가 바뀐 필드가 타입이 같은 경우에는 런타임에 에러가 발생할 수 있기 때문에 주의가 필요합니다.

+ 당연히 순서가 바뀔 때 뿐 아닌, 필드가 추가되는 경우도 문제가 됩니다.

예시

기존에 작성해둔 코드

@AllArgsConstructor
class Person {
    private String name;
    private int age;
}

아래와 같이 인스턴스를 생성할 것이에요

Person person = new Person("김철수", 25);

그런데 다른 개발자가 필드의 순서를 바꾸었습니다.

@AllArgsConstructor
class Person {
    private int age;
    private String name;
}

어딘가에서 person을 만들고 있던 아래 코드는 오류가 발생할 것입니다.

Person person = new Person("김철수", 25);

@Builder

이에 대한 대책으로 이미 빌더 패턴을 잘 적용하시고 있을 것 같습니다. 그리고 Lombok의 @Builder를 많이 쓰시지 않을까 싶어요.

만약 JPA 엔티티 클래스라면 @NoArgsConstructor(access = AccessLevel.PROTECTED) 를 붙여서 사용하고 계실 것 같습니다.

@Builder는 생성자가 없는 경우에 모든 필드를 파라미터로 받는 생성자를 만들지만, 위 경우에는 따로 생성자를 만들지 않아 문제가 됩니다.

제가 블로그랑 오픈소스 여럿 찾아보니까 보통 두 방향으로 하는 것 같은데요

1. @AllArgsConstructor 쓰기

아무래도 빌더 패턴으로만 객체를 만들자고 약속하고 쓰겠죠 !

@AllArgsConstructor
@Builder
class Person {
    private String name;
    private int age;
}

// 사용 예
Person person = Person.builder()
    .name("김철수")
    .age(25)
    .build();

2. 생성자를 직접 만들고 @Builder 적용하기

class Person {
    private String name;
    private int age;

    @Builder
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 사용 예
Person person = Person.builder()
    .name("김철수")
    .age(25)
    .build();




더 깔끔한 코드를 원한다면 1번을.
@AllArgsConstructor의 문제점을 코드에 담고 싶지 않다면 2번을 택하는 것 같습니당.

@AllArgsConstructor(access = AccessLevel.PRIVATE)

@AllArgsConstructor(access = AccessLevel.PRIVATE) 을 사용한다면 어떨까요?

접근 수준을 private로 설정하는 겁니다

@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
class Person {
    private String name;
    private int age;
}

// 사용 예
Person person = Person.builder()
    .name("김철수")
    .age(25)
    .build();

생성자를 사용해 객체를 생성하는 것을 막을 수 있고, 코드도 깔끔해서 좋은 것 같습니다!

@AllArgsConstructor를 지양하는 이유로 캡슐화 위반을 지적하는 시선도 있는 것 같습니다. 여기에 공감하고 방지하길 원한다면 2번(생성자 만들고 @Builder붙이기)를 택해야 할 것 같습니다

profile
그때그때 학습하고 있는 내용을 올려요

0개의 댓글