인프런 커뮤니티 질문&답변

신준석님의 프로필 이미지
신준석

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

회원 서비스 개발

ValidateDuplicateMember 메소드와 트랜젝션 관련해서 질문입니다.

해결된 질문

작성

·

594

2

@Transactional 애노테이션을 붙인 메서드는 오버라이딩 할 수 있어야 하기때문에 ValidateDuplicateMember 메서드를 private 접근지정자를 설정하면 명시적으로 @Transactional 애노테이션을 해당 메서드에 붙일 수가 없는데,

강사님께서 하신것처럼 클래스 레벨로 @Transactional(readOnly = true) 애노테이션을 붙이면 ValidateDuplicateMember 메서드는 트랜젝션이 적용이 안되는지 궁금합니다.

만약 적용이 안된다면 접근 지정자를 protected 로 변경해서 사용해도 되는지 궁금합니다.

----------------------- 전체 소스코드 입니다. -------------------------------

@Service
@Transactional
public class MemberService {

@Autowired
private MemberRepository memberRepository;

/**
* 회원가입
* @param member member entity
* @return memberId
*/
public Long join(Member member) {
ValidateDuplicateMember(member);
memberRepository.save(member);

/*
* member를 영속화 할때 key를 member의 id로 설정 (generateValue)
* member 객체의 id 필드에는 값이 채워져있는 것을 보장할 수 있음
*/
return member.getId();
}

@Transactional(readOnly = true)
protected void ValidateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());
if (!findMembers.isEmpty()) {
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}

/**
* 회원 전체 조회
* @return memberList
*/
@Transactional(readOnly = true) // 성능 최적화
public List<Member> findMembers() {
return memberRepository.findAll();
}

/**
* 회원 단건 조회
* @param memberId memberId
* @return findMember
*/
@Transactional(readOnly = true)
public Member findOne(Long memberId) {
return memberRepository.findOne(memberId);
}
}

답변 3

3

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 준석님

스프링의 트랜잭션은 AOP 방식으로 구성되는데요. 

AOP는 외부에서 호출할 때만 기능이 적용됩니다. 한마디로 외부에서 호출할 때만 트랜잭션이 적용됩니다.

예를 들어서 다음과 같을 때는 트랜잭션이 적용되지 않습니다.

class Service {

  public a() {

    b();

  }

  @Transactional

  public b(){}

}

호출 코드1: client.a(); //트랜잭션 적용X

- a()는 트랜잭션이 적용이 안된 상태로 호출이 되었는데, 그 안에서 b()를 호출해도 AOP는 외부에서 호출할 때만 영향을 주기 때문에 트랜잭션이 적용되지 않습니다. (AOP라는 것이 한번 감싸는 것인데, 그 안에서 자기들끼리 호출하면 전혀 효과가 없는 것이지요)

반면에 다음 코드는 트랜잭션이 적용됩니다.

class Service {

  @Transactional

  public a() {

    b();

  }

  public b(){}

}

호출 코드2: client.a(); //트랜잭션 적용O

트랜잭션의 범위는 a()가 시작할 때부터 a()가 끝날때 까지 모두 포함됩니다. 따라서 b()도 트랜잭션 범위안에 들어갑니다.

위에서 private 메서드에 @Transactional을 넣었을 때 아마 오류가 발생한 것은 이걸 넣어도 방금 설명드린 것 처럼 트랜잭션이 적용되지 않기 때문일거에요.

추가로 "스프링 트랜잭션 내부호출"로 검색해보시면 더 자세한 내용을 이해하실 수 있을거에요^^

도움이 되셨길 바래요^^

0

신준석님의 프로필 이미지
신준석
질문자

==============기존 소스코드=================
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class MemberService {
   ...

   private void ValidateDuplicateMember(Member member) {
    List<Member> findMembers = memberRepository.findByName(member.getName());
    if (!findMembers.isEmpty()) {
        throw new IllegalStateException("이미 존재하는 회원입니다.");
    }
   ...
}
==============================================

강사님께서는 해당 메서드의 접근지정자를 private으로 하시고 
클래스 범위에서 @Transactional(readOnly = true) 애노테이션을 붙이셨는데,




==============변경 소스코드1==================
@Service
@RequiredArgsConstructor public class MemberService { ... @Transactional(readOnly = true) private void ValidateDuplicateMember(Member member) { List<Member> findMembers = memberRepository.findByName(member.getName()); if (!findMembers.isEmpty()) { throw new IllegalStateException("이미 존재하는 회원입니다."); } } ... } ========================================== 보시는 것처럼, private 메서드에서 트랜젝션 애노테이션을 붙이면, Methods annotated with '@Transactional' must be overridable 라는 에러가 표시됩니다. ==============변경 소스코드2==================
@Service
@RequiredArgsConstructor public class MemberService { ... @Transactional(readOnly = true) protected void ValidateDuplicateMember(Member member) { List<Member> findMembers = memberRepository.findByName(member.getName()); if (!findMembers.isEmpty()) { throw new IllegalStateException("이미 존재하는 회원입니다."); } } ... } ========================================== 따라서 protected로 변경해주면 에러가 사라지는데, 질문1. 결론은 기존 소스코드에서 처럼 클래스안에 private 메서드가 존재하고 클래스레벨로 트랜잭션 어노테이션을 주었을때 private 메서드에 트랜잭션이 정말 적용되는지 궁금합니다. 음,, 변경 소스코드1 에서 처럼 private 메서드에 트랜잭션 애노테이션을 주면 에러가 나기때문에 이런 궁금증이 생겼습니다. 질문2. 추가적으로, 변경 소스코드2는 protected 로 변경해서 에러를 없앴는데 private 메서드에 트랜잭션 애노테이션을 적용할 때 접근지정자를 이렇게 바꿔서 적용을 해도되는지 궁금합니다. 질문을 너무 난해하게 해서 죄송합니다 ㅜㅜ..

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 준석님

준석님이 생각하는 방향으로 코드를 다시 적어주세요^^

그리고, 어디에서 어떤 호출을 하는 시점에 트랜잭션이 적용되는 것이 궁금한지 호출 단위로 궁금한 포인트를 적어주시겠어요?

신준석님의 프로필 이미지
신준석

작성한 질문수

질문하기