[Bucket - Back] 프로젝트 트러블 슈팅
@Column(nullable=false) VS @NotNull 적용하기
기본적으로 @Column nullable false 설정을 많이 해왔다.
하지만, @NotNulll을 적용하는 방법도 있어 둘중 어떤 방법을 사용할까 고민을 하게 되었다.
@Column(nullable=false)
엔티티 필드의 null을 검증하기 위해서 사용한다. 테이블 생성 시 not null조건이 적용된다.
JPA에서 엔티티가 null로 적용된 필드가 있어도 DB에 도착해서 SQL 쿼리의 not null 제약에 의해서 예외가 발생한다.
@NotNull
DB 쪽으로 SQL 쿼리가 보내지기 전에, JPA엔티티 필드값이 null로 채워지면 예외가 발생한다. DB에 도달하기 전에 null검사를 처리한다.
만약, JPA가 아니었다면 엔티티에 붙은 이 어노테이션을 해석하지 못했을 것이다.
정리
@Column(nullable=false) : 엔티티 필드의 Null을 검증한다. 단, db에 도착해서 ddl제약조건에 의해 null검사를 통해 검증의 깊이가 깊다.
@NotNull : 어플리케이션 단에서 null을 검사하고 제어한다. 단, JPA인 경우에만 적용 가능하며, Hibernate특성상. 가능한 제약이기 때문에 기술에 의존적이다.
그래서 내린 결론은 @NotNull을 사용하자는 것이었는데, 여러 단점이 마음에 걸렸다.
POJO를 지향해야하는 엔티티에서 Spring Validation의존을 가지고 있는 것이 별로 좋지 못했다
그래서, 이걸 사용해야 마나 고민하던 순간에, Java에서 어플리케이션 단에서 이걸 지원해주는 것을 알게 되었다.
Objects.RequiredNotNull
null이 발생하면 빠르게 실패하면서도, Java로 POJO를 지킬 수 있다.
이러한 결과로 미루어보았을때, @NotNull보다 훨씬 좋은 효과를 가지고 있어 이 방법을 선택했다.
DeleteAll VS DeleteAllInBatch
DeleteAll보다 DeleteAllInBatch가 훨씬 더 효율적으로 처리한다는 이야기가 있었다.
실제 코드를 보면 DeleteAllInBatch 는 한번에 지정한 데이터를 삭제할 수 있다.
쿼리를 실행시켜보면 삭제 과정이 한번에 이루어지는 걸 확인할 수 있다. where절 안에 in을 활용하여 해당하는 정보가 있는 지 탐색하고 삭제해버린다.
반면 DeleteAll은 삭제 쿼리가 하나씩 나가기 때문에 상대적으로 성능이 떨어질 수 밖에 없다. 따라서 리팩토링 과정에서 DeleteAllInBatch로 삭제를 시도해야할 것 같다.
추가로 SaveAll은 있지만, SaveAllInBatch메소드는 없다. 이건 왜 없는지 이유와 어떻게 해결해야 하는지 해결 과정을 별개로 정리해둘 계획이다.
@Value null문제
yml에 있는 @Value는 설정값을 분리하기 위해서 존재한다. 그 이유는
1. 초기값 설정
2. 여러 환경마다 적합한 값을 설정할 수 있음 (dev.yml local.yml)
3. 컴파일 과정의 불편함을 해소한다.
먼저 기능에 대해 알아보자.
PropertySourcesPlaceholderConfigurer(spring 5.2)
빈 팩토리의 후처리기(BeanFactoryPostProcessor)의 일종이다.
스프링은 기본적으로 객체를 만들 Bean Definition을 만들고 만들고 이를 바탕으로 객체를 생성한다.
객체를 생성할때 BeanDefinition을 불러오고 조작하는 것이 빈 팩토리 후처리기.(BeanFactoryPostProcessor)
빈이 만들어지고 객체의 내용이나 객체를 수정하기 위해 존재하는 것이 빈 후처리기(BeanPostProcessor)
@Value를 사용하는 경우 BeanFactoryPostProcessor는 Bean으로 등록되어야 한다.
동작 방식
1. BeanFactoryPostProcessor 가 빈 설정정보를 불러옴
2. PropertySourcesPlaceholderConfigurer가 ${}를 yml의 실제 프로퍼티 값으로 치환한다.
3. 빈 설정정보를 바탕으로 객체를 생성한다.
추가정보
PropertySourcesPlaceholderConfigurer가 static으로 구현되어야 하는 이유
bean의 생명주기와 관련이 있다. BeanFactoryPostProcessor는 빈 설정정보 자체를 조작한다. 그러면, BeanPostProcessor보다 항상 앞서서 생성되어 있어야 한다. @Value의 경우, 값을 치환하기 위해서는 PropertySourcesPlaceholderConfigurer가 BeanPostProcessor보다 먼저 등록되어있어야 한다.
PSPC(그냥 줄였다.)를 재정의해야 하는 상황이 있는 경우를 고려할 때, 이런 빈의 생명주기 순서가 꼬일 수 있다.
예를 들어 클래스에 @Configuration이 붙어있다고 생각해보자. 클래스가 생성된 후에 PSPC가 빈으로 등록되면 BeanPostProcessor가 등록되는 순서에 적용되어버린다. 그래서 @Value의 값은 null로 할당이 된다.
이런 경우 Bean 메소드를 static으로 구현해 @Configuration클래스가 생성되는것 보다 더 빨리 동작하도록 설정해야 한다. 그러면, 이 빈들은 스프링 컨테이너 라이프사이클의 초기에 다른 빈들보다 먼저 등록이 된다. (순서가 유지된다.)
이는 다른 빈팩토리후처리기 어노테이션(@PostConstruct, @Autowired)에도 해당되므로, 이를 유의해서 사용해야 한다.
그런데 내가 맞이한 원인은 이렇게 동작원리와 관련된 내용은 아니었다.
사실 jwt의 SecretKey를 바탕으로 토큰에 대한 단위 테스트를 진행하는 과정이었는데, Spring의 영역이 아닌 Java만으로 테스트하는 고정이었기에 @Valule값을 사용할 수 없어서 그랬었다.
이런 경우는 사실 임의로 SecretKey를 정의하고 테스트해서 해결하면 된다.
혹은 @SpringBootTest를 사용하거나(하지만 목적에 맞지 않으므로 기각)
참고
https://blog.yevgnenll.me/posts/jpa-use-not-deleteall-but-deleteallinbatch
https://crispindeity.github.io/posts/(Test)테스트-중-@Value-Null-문제/
(TEST)테스트 중 @Value Null 문제
테스트 중 @Value Null 문제
crispindeity.github.io