본문 바로가기
TIL : Today I learned (or Week)

TIL 230808 : 코드 디테일 조금씩 살려보기 (Transactional, NoArgsConstructor(AccessLevel = PROTECTED))

by 우인입니다 2023. 8. 8.

오늘은 팀과제를 하며 기본 틀이 되는 코드를 짜며 오갔던 내용 중 이유가 궁금한 것들을 두가지 정리해보았다.

 


@Transactional (readOnly = true)

팀원이 남겨준 리뷰

 

DB에서 불러온 객체를 수정 하면 더티체킹으로 자동으로 수정사항을 반영해줄 수 있다.

물론, 트랜잭션을 열어줘야한다.

 

그정도로 알고 있었고, 추가적으로 조회시에도 (readOnly = true)를 넣어줘서 해도 좋다고 들었다.

 

근데 오늘 팀원이 상세하게 이유를 덧붙여서 수정 뿐만 아니라, 생성, 삭제시에도 @Transactional을 달아서 트랜잭션 환경을 열어주는 게 좋다고 했다.

 

이유는 롤백기능이다.

 

@Transactional의 rollbackFor 기능

트랜잭션을 열어두면 스프링에서는 디폴트값으로 UnCheckedException과 Error에 대해서 롤백이 되게 되어있다.

 

이게 어떤 의미냐면

@Transactional
payFinish() {
	쿠폰사용(); // 쿠폰을 사용 처리.
   	결제완료();
}

만약 저렇게 쿠폰을 사용해서 쿠폰이 사용처리가 되고 결제가 완료되는 순서가 있다고 해보자.

 

만일 쿠폰까지 검증마치고 사용처리가 되었는데, 결제에서 오류가 난다면? 사용처리된 쿠폰은 다시 원래 상태로 돌아가야 된다.

이렇듯 중간에 예외가 발생하는 경우 전체를 되돌려야하는 경우 롤백기능을 설정해두면 안전하고 이를 @Transactional로 사용한다.

 

물론, 예외적으로 진행된 메소드까지 적용해야하는 경우가 있다면 이 개념을 떠올려서 잘 선택해보기로 한다.


@NoargsConstructor(AccessLevel.PROTECTED) 

 

 

1. @Entity를 설정해준 Entity클래스에는 기본생성자가 필요하다.

만약 @NoArgsConstructor를 주석처리하면 바로 필요하다고 알림을 준다.

왜 그럴까?

 

우선 위처럼 필요하다고 명시가 되어있다.

 

JPA는 Entity 객체를 인스턴스화 하고 필드에 값을 채워넣기 위해 Reflection을 사용해
런타임 시점에 동적으로 기본생성자를 통해 클래스를 인스턴스화하여 값을 매핑하기 때문이다.
 
이 때, Entity Class에 기본생성자가 존재하지 않는다면 JPA는 Entity 객체를 동적으로 인스턴스화 할 수 없으므로 JPA의 기능을 원활하게 사용하지 못한다.

라는 추가 설명이 있는데, 사실 완벽히 이해되진 않는다.

JPA상에서 DB로 넘겨주는 과정에서 기본생성자를 사용하는 느낌으로만 이해됐다.

 

 

 

 

2. access level을 Protected로 제한하는 이유

 

한 마디로 정리해서 지연로딩시 Proxy 객체 생성을 위해서다.

 

아래 블로그 잘 정리되어 있다.

https://hungseong.tistory.com/70

 

 

 

3. AccessLevel.PROTECTED의 장점

 

객체의 일관성을 유지할 수 있다.

 : 위에서 설명된 것처럼 JPA가 요구하는 것 이외에 기본생성자를 쓸 일은 없다.

  혹시나 기본 객체가 생성되는 일을 최대한 방지하기 위해 JPA 요구사항을 충족하는 선에서 최대한 닫아두는 것이다.

 

 

 


더 공부할 것

 

Dto 나눠주기

어느정도까지 나눠주는 게 좋을 지?