티스토리 뷰

엔티티를 생성할 때 무의식으로 아래와 같은 어노테이션을 박고 시작하는 분들이 있을 것이다. 

@NoArgsConstructor(access = AccessLevel.PROTECTED)

 

바로 저번 프로젝트 때만 해도 내가 쓰고 있었기 때문이다. 옛날에는 엔티티를 생성할 때 꼭 붙여준 것 같다. 

 

 

@NoArgsConstructor 어노테이션은 객체의 기본생성자를 자동으로 생성해준다. 즉 원래라면 아래코드를 직접 입력해야 하지만, 어노테이션을 붙이면 자동으로 붙여준다는 의미이다. 

public Member() {
}

 

@NoArgsConstuctor은 속성이 있는데, 그 중 액세스레벨을 설정할 수 있다. 액세스 레벨은 접근제한자이다. 

 

 

왜 기본생성자의 접근 제한자를 private도, public도 아닌 애매하게 protected로 하는걸까? 바로 프록시 때문이다.

 

프록시 객체 생성

지연로딩을 할 때 실제 객체를 상속한 프록시 객체를 생성한다. 프록시 객체는 실제 객체의 참조변수를 가지고 있어야 하기 때문에 super를 호출한다. 하지만 실제 객체의 기본생성자가 private되어있다면 super를 호출할 수 없다. 즉 프록시 객체를 생성하는데에 있어 기본생성자의 접근 제한은 최소한 protected라는 것이다. 한번 예시를 들어보겠다. 

 

Member와 Team이 있다. Member와 Team은 다대일 관계이며, 보통처럼 지연로딩을 걸어주었다. 

@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;

    public Member(String name) {
        this.name = name;
    }
}

 

단, 프록시 객체가 생성되지 않는 것을 보여주기 위해 Team 엔티티의 기본생성자를 private로 해주자. 

@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    public Team(String name) {
        this.name = name;
    }
}

 

만약 intellij 유료버전을 쓰고 있다면 빨간 줄을 띄우고, 아래와 같이 친절하게 설명해준다. 

 

 

하지만 실행은 되기에, 런타임에러를 띄우기 위해 waring문구를 무시하고 테스트코드를 작성해보도록 하겠다. 

@Test
    void test() {
        Team team = new Team("수영팀");
        em.persist(team);

        Member member = new Member("남세");
        em.persist(member);
        member.setTeam(team);

        em.flush();
        em.clear();

        Member findMember = em.find(Member.class, member.getId());
    }

 

그럼 아래와 같이 HibernateException을 띄우고, private 기본 생성자는 런타임시 프록시에서 작동하지 않는다고 말하고 있다. 

 

 

Hibernate 공식 문서에서도 아래와 같이 말하고 있다. 

 

 

결국 최소 접근 제한을 PROTECTED로 해야 한다는 것이다. 그럼 public으로 하지 않는 이유는 뭘까? 바로 캡슐화 때문이다. 당연하지만 확실하게 오류를 내지 않기 위함으로 변수에 private을 많이 붙인다. 만약 public으로 한다면 아무런 조치 없이 바로 접근할 수 있기 때문에 내가 언제 어떻게 변수에 접근했는지, 또는 언제 수정했는지 알려면 자기 자신의 코드를 역추적해서 찾을 수 밖에 없다. 이러한 불상사를 사전에 방지하기 위해 public을 지양하는 것이다. 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday