맨땅에 헤딩하는 개바른자

Lombok > 생성자 AccessLevel.PROTECTED 를 알고 사용하자 본문

개발 TIP

Lombok > 생성자 AccessLevel.PROTECTED 를 알고 사용하자

앵낄낄 2023. 1. 19. 17:09
반응형

이번 글에서는 롬복 생성자 어노테이션에서 사용하는 access 메소드에 대해서 알아보겠습니다.

제가 이 내용을 알기전에는 업무에서 access기능을 사용하지 않고 베이스의 어노테이션을 사용했었습니다.

  • 기본 생성자 > @NoArgsConstructor
  • 모든 항목의 생성자 > @AllArgsConstructor
  • 지정 항목의 생성자 > @RequiredArgsConstructor

User라는 객체에 생성자 어노테이션을 사용하면 주석처리 된 영역의 생성자를 생략 할 수 있다.

다만.. User라는 개체는 name, email, age 3가지 항목값이 필수라고한다면 생성자를 난발하는순간 데이터의 정합성이 떨어지게 된다.

여기서 @Setter까지 포함한다면 상황은 더 심각해 질 수 있다.

  • case1 ) 기본생성자로 객체 생성 후 age가 null인 객체가 생성된다.
  • case2) 모든 항목이 null인 객체가 생성된다.
  • case3) 모든 항목 객체 생성 후 setter로 값을 바꿀 수 있어 정보자 맞지 않은 객체가 생성된다.
  • case4) 필드가 많게되면 순서에 따라 변수명 확인이 어려워 잘못 된 값으로 객체가 생성 될 수 있다.

그러면 이러단 단점을 보안하여 객체생성을 하려면 어떻게 할까?

우선은 객체 생성이 난무하는 상황부터 막아볼테다 ~~~

생성자 어노테이션에 access = AccessLevel 옵션을 지정하면된다 지정안한경우는 디볼트로는 AccessLevel.PUBLIC으로 모두 허용이다.

AccessLevel객체를 까보면 enum으로 여러타입이 존재한다.

package lombok;

public enum AccessLevel {
PUBLIC,
MODULE,
PROTECTED,
PACKAGE,
PRIVATE,
NONE;

private AccessLevel() {
}
}

단어를 보았을 때 유추가능한 영역이 떠오르겠죠?

  • PRIVATE
    • 모든 외부 클래스에서 생성자 사용 불가
  • PROTECTED
    • 다른 패키지에 소속된 클래스에서 생성자 사용 불가 (상속 제외)
  • 그외 것들은 잘 사용하지 않아서 pass 궁금하시면 좋을 글로 공유 부탁 드립니다 🙂

그러면 보안 된 코드로 생성자 사용 예를 보겠다

기존코드에서 타 클래스에서 User를 호출하는 형태로 구조를 바꿈

두 생성자 어노테이션에 access = AccessLevel.PRIVATE를 붙였다

그리고서 타 클래스에서 User라는 생성자를 호출하였을 때 컴파일 오류의 빨간줄이 뜨게된다.

외부에서 무분별한 생성자 호출을 막게 된 것이다!!

그 다음은 명확하고 필수적으로 데이터 값을 받아서 객체를 생성하고 불편의 객체데이터 상태를 유지할 수 있는 코드를 수정해보겠다

  • @Setter를 제거하였다
  • 모든 정보를 받아서 필수 값 체크 후 생성자를 통해 객체를 반환해주는 메소드를 만들었다

ConstructorExp.User.valueOfUserAllInfo()메소드를 통해 지정 된 필드 값들을 넘겨주었고 User객체를 반환 받았다.

  • 모든 필드 정보를 확인 할 수 있으며, 넘겨받는 값을 내부에서도 vallidate 처리를 한곳에서 검증할 수 있게됬다
  • 객체 생성 이후 값이 변경되는 것에 대해서 막게되었다

이로써 무분별한 생성자 사용을 막고 데이터 정합성이 완변한 객체를 만들 수 있는 상황을 만들었다.

그렇다면 이쯤에서 궁금점이 생겼을 것이다.

업무를 하다가 어쩔 땐 AccessLevel = PRIVATE를 쓰고 어쩔 땐 AccessLevel = PROTECTED를 쓰일 때가 있다.

처음에는 누가 붙여서 사용하고 있으니까 나도 의식에 흐름에 따라 잘 알지도 못한 상태로 그냥 따라서 붙이게되던 못난 내가 느껴졌을 쯤.. 이러면 안돼 이러다 다 주..ㄱ

JPA를 사용할 때 @Entity 어노테이션을 사용하게되면서 차이를 확인 할 수 있다.

NoArgsConstuctor에 AccessLevel을 PRIVATE로 설정했을 경우에는 다음과 같은 에러가 발생한다.

JSR-000338의 Entity 설명을 보면

The entity class must have a no-arg constructor. The entity class may have other constructors as well.

The no-arg constructor must be public or protected.// Entity 클래스는 매개변수가 없는 생성자의 접근 레벨이 public 또는 protected로 해야 한다.... An instance variable must be directly accessed only from within the methods of the entity by the entity instance itself.// 인스턴스 변수는 직접 접근이 아닌 내부 메소드로 접근해야 한다.

이에 따라 @Entity 선언 후 @NoArgsContructor에서 접근 Level에 따라 경고가 발생하고 있는 것이다. (Complie시 오류 검출 안됌)또한 Entity 클래스 인스턴스 변수는 직접 접근이 아닌 내부 메소드로 접근해야 한다. (ex. Getter, Setter 사용)

NoArgsConstuctor에 PROTECTED선언 후 컴파일 오류가 사라졌다!

(JPA 매핑관계나 프록시의 대한 지식은 사전에 알고있다는 것을 가정하에 설명하였습니다.)

JPA경우는 Entity Proxy를 사용하기 때문에 하위 클래스에 대한 생성자 접근이 필요한 케이스가 발생된다고 보면 되겠다.

예로들면 Company객체와 Employee객체가 있다고 봅시다

둘의 관계는 보통은 1:N이 관계일 것이다.

class Company {

@OneToMany(fetch = FetchType.LAZT)

private List<Employee> employeeList;

}

이때 JAP프록시로 사용되는 Lazy방식으로 Company에 소속 된 Employee를 찾는 쿼리가 수행된다고 보았을 때

Compnay가 먼저 조회되고, Employee가 그 다음 사용 될 때 조회가 들어가는데 이 때 Company에서 Employee의 생성자호출이 이루워 진다.

만약 여기서 Employee의 생성자 access가 PRIVATE라면 오류가 발생하게되고 PROTECTED로 선언하였다면 정상적으로 데이터 조회가 가능하다

좀더 이해를 돕기위한 예시로

그러면 반대로 FetchType.EAGER를 사용하였을 경우에는 PRIVATE도 정상적으로 쿼리 수행이 이루워진다

이 내용으로 짐작해볼 때

결론부터 말씀드리면

PRIVATE, PROTECTED 모두 상황에 맞게 사용한다면 오류없이 사용이 가능할 것이다.

  • 단순 Dto나 모델의 경우
    • AccessLevel.PRIVATE or AccessLevel.PROTECTED 용도에 따른 선택사용 추천
    • 메소드를 통해서 외부데이터를 주입받아서 객체 생성 반환
  • Entity 클래스
    • JPA 프록시가 발동되는 케이스를 염두해둔다면 디폴트로 AccessLevel.P|ROTECTED 사용을 권장

이제부터는 롬성 생성자 어노테이션사용 시 AccessLevel을 어떤것을 사용할지 자신있게 선언할 수 있을 것이다!

참고

https://erjuer.tistory.com/106

https://cobbybb.tistory.com/14

반응형