[Spring] @Transaction 어노테이션 적용문제

Nam_JU·2022년 9월 23일
0

ErrorLog

목록 보기
22/26

배경

프로젝트를 하다가 @Transaction 어노테이션 적용에 대한 의견이 오가게되었다. 보통 데이터베이스처리를 할때 트랜잭션 적용하는게 좋다. 개념적으로만 알았지 생각없이 사용하는게 많았다. 이걸 사용한 이유가 뭐야? 그럼 다른곳은 적용을 왜 안했어? 등의 질문을 받았을때 공부의 필요성을 느껴 정리한다



이전의 방식

  • Transaction 어노테이션을 어디에 어떻게 붙여야 할지 모르고 메소드마다 전부 붙인적이 있었다
  • 최근에는 READ에만 필요하다는것을 알게되어 변경하였지만 실질적으로 잘 사용하지 못했다



Transaction 개념


트랜잭션은 데이터의 상태를 변화시키기 위해 수행하는 작업의 단위를 뜻한다 알아야 할것은 아래의 질의어 하나가 작업의 단위 하나가 아니다

  • 상태를 변화시킨다 : SQL 질의어를 통해 DB 접근하는 것

    • SELECT
    • INSERT
    • DELETE
    • UPDATE
  • 작업의 단위 : 많은 SQL명령문들을 사람이 정하는 기준에 따라 정하는 것이다

    • 게시판에 들어가 게시글들을 보고 마음에 드는 게시글을 선택해 작성한다
    • READ로 게시글을 읽고 UPDATE로 게시글을 데이터베이스에 저장한다 두 쿼리문이 하나의 트랜잭션이다

트랜잭션의 특징

  • 원자성 (Atomicity) : 트랜잭션이 데이터베이스에 모두 반영되던가 안되던가
  • 일관성 (Consistency) : 트랜잭션 작업처리 결과가 항상 일관성이어야한다
    처음에 트랜잭션을 진행 하기 위해 참조한 데이터베이스로 진행되어야함
  • 독립성 (Isolation) : 다른 트랜잭션의 연산에 끼어들 수 없다
  • 지속성 (Durability): 트랜잭션이 성공했을경우 결과가 영구적으로 반영되어야한다

@Transaction 어노테이션


스프링에서는 클래스, 인터페이스, 메소드에 부여할 수 있는 트랜잭션 어노테이션이 있다. 해당 어노테이션을 붙이면 트랜잭션 관리대상이 된다
@Transactional이 붙은 메소드는 메소드가 포함하고 있는 작업중 하나라도 실패할경우 작업을 취소한다

  • private/protected 메서드는 @Transactional 무시한다

속성

  • 격리 수준(isolation) : 일관성 없는 데이터를 허용
  • 전파 옵션(propagation) : 트랜잭션 동작 도중 다른 트랜잭션을 호출하는 상황에서 어떻게 할 것인지 사용
  • readOnly 속성 : 트랜잭션을 읽기전용으로 설정할 수 있다
  • 트랜잭션 롤백 예외 : 런타임 예외가 발생하면 롤백된다
  • timeout : 지정한 시간내 완료되지 않으면 롤백 수행

우선순위

  • 트랜잭션 어노테이션은 높은 쪽으로부터 메소드 > 클래스 > 인터페이스 메소드 > 인터페이스 순서로 우선순위를 가진다.
  • 공통적인 트랜잭션 규칙은 클래스에, 특별한 규칙은 메소드에 적용시키자

언제 어디서 어떻게 사용할래

비즈니스 로직을 수행하는 Service 계층에서 사용한다. 데이터를 사용하고 변경하는 등 많은 작업이 일어나는 곳이기 때문이다

  • 변경된 코드

@RequiredArgsConstructor
@Service
@Transactional
public class UserServiceImpl implements UserService {
    private final UserRepository memberRepository;
    Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Override
    public UserDto signupUser(UserDto dto) {
        User member = User.builder()
                .userName(dto.getUserName())
                .password(dto.getPassword())
                .build();
        User result = memberRepository.save(member);

        UserDto memberDto = UserDto.builder()
                .userId(result.getUserId())
                .userName(result.getUserName())
                .password(result.getPassword())
                .build();
        return memberDto;
    }

    @Override
    @Transactional(readOnly = true)
    public List<UserDto> readAllUser() {
        return memberRepository.findAll().stream()
                .map(member -> UserDto.builder()
                        .userId(member.getUserId())
                        .userName(member.getUserName())
                        .password(member.getPassword())
                        .build())
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public UserDto readUser(Long userId) {
        if (!memberRepository.existsById(userId))
            throw new ResponseStatusException(HttpStatus.NOT_FOUND);

        Optional<User> userOptional = memberRepository.findById(userId);
        UserDto userDto = UserDto.builder()
                .userId(userOptional.get().getUserId())
                .userName(userOptional.get().getUserName())
                .build();
        return userDto;
    }


    @Override
    public UserDto loginUser(String userName, String password) {
        Optional<User> userOptional = memberRepository.findByUserName(userName);

        if(!userOptional.get().getPassword().equals(password))
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST);

        UserDto userDto = UserDto.builder()
                .userId(userOptional.get().getUserId())
                .userName(userOptional.get().getUserName())
                .build();
        return userDto;
    }

Transactional(readOnly=true)는 조회를 하는 메소드에 적용하고 전체 클래스에는 Transactional어노테이션을 적용시켰다. default가 false이기 때문에 수정, 삭제, 생성등의 작업을 하는 메소드에 적용이 되었다.

검색을 해보니 같은 것이지만 반대로 readOnly=true를 클래스단에 넣고 생성,수정,삭제 메소드에 @Transactional만 붙이는 경우도 많은것 같다

기회가 된다면 ReadOnly 뿐만아닌 다른 기능도 파악하고 사용하도록 노력해야겠다.




참고자료
https://gyoogle.dev/blog/computer-science/data-base/Transaction.html
https://mommoo.tistory.com/62
https://heekim0719.tistory.com/409
https://data-make.tistory.com/738
https://mangkyu.tistory.com/170

profile
개발기록

0개의 댓글