상세 컨텐츠

본문 제목

15. 트랜잭션 처리 및 주의점

Java/코딩의 신

by Gopythor 2023. 12. 28. 13:13

본문

728x90
반응형

트랜잭션 적용하는 방법에 대해 알아보겠다.

게시판에 글을 작성하거나 회원가입할 때, 사용자 등 DB가 추가가 된다.

지금까지는 트랜잭션 처리를 따로 하지 않았다.

트랜잭션을 묶게 되면 여러가지 SQL을 하나의 트랜잭션으로 묶을 수 있다.

중간에 하나라도 에러가 나면 처음 상태로 롤백이 됨.

반드시 트랜잭션으로 묶인 것이 모두 성공적으로 실행이 되어야 데이터베이스가 반영이 된다.

모두 반영이 되거나 하나도 반영이 안되거나를 보장해줌.

트랜잭션의 중요한 기능.

 

회원 가입을 하게되면 게시판에 자동으로 가입인사가 작성이 되도록 해보겠다.

현재 register를 호출하면 회원 가입이 됨.

세이브를 보면 사용자가 요청한 비밀번호 암호화해서 사용자 정보를 셋팅.

권한도 셋팅을 함.

 

사용자의 권한을 셋팅해서 최종적으로 userRepository에 저장.

사용자 테이블과 user_role에 insert가 됨.

지금 같은 경우는 한번에 동시에 저장이 됨.

에러가 났다고 해서 쓰레기 값들이 데이터베이스에 적용이 되는 상황은 많지 않을 것임.

그래서 게시글을 작성하는 코드를 작성해볼 것임.

실제로는 게시글보다는 회원 포인트기능이 있다면, 포인트 기능을 올려준다거나 할 수 있음.

게시글 작성을 위해서는 board 클래스를 이용함.

제목

내용

작성하고 중요한 것은 사용자 정보를 셋팅해야 함.

사용자 저장을 게시글 작성 전에 하여, 반환값은 해당 값을 돌려주도록 함.

리턴된 사용자 값을 이용하라고 가이드가 되어 있음.

안정적으로 코딩하기 위해서는 메뉴얼이 시키는대로 하면 됨.

리턴된 변수를 이용해서 세팅을 하도록 함.

문서에 의하면 사용자 값이 달라질 수도 있다고 함.

 

 

BoardRepository도 작성함.

게시글까지 저장이 되도록 하겠음.

user1로 가입.

가입 인사가 작성되었음.

작성자도 셋팅한대로 들어가 있음.

내용도 들어가있음.

조건문으로 예외발생을 일으켜볼 것임.

강제로 익셉션 발생 시킴.

user2로 회원가입

예외발생 발생함.

에러가 발생해도 로그인이 됨.

user2의 글은 작성이 되지 않았음.

사용자 저장까지만 데이터베이스가 반영이 되었음.

아래 로직은 가입인사글이기때문에 중요하지 않을 수 있다.

그러나 회원 포인트 등은 문제가 발생할 수 있음.

그 포인트는 고객에게 약속한 큰 금액의 포인트면 고객이 화를 낼 수 있음.

그러한 상황을 방지.

가입되지 않도록 함.

데이터베이스에 쓰레기 값을 들어가지 않도록 함.

트랜잭션 적용을 하도록 함.

어노테이션 만으로 적용할 수 있음.

다시 회원가입을 시도하도록 함.

예외발생.

전체 코드가 실행되지 않아서 롤백이 됨.

예외 표시를 지우고 회원가입을 하니 가입과 게시글 작성이 잘 됐음.

간단하게 트랜잭션 적용을 해봤음.

쉬워보이지만, 어노테이션으로 @Transactional을 하다보니 몇가지 제약이 있다.

어노테이션 선언을 하는 것만으로 적용이 된다고 보장이 되지 않음.

주의점을 알아보도록 하자.

해당 글을 참조해볼 것임.

스프링에서 설정하는 방법이 있다.

스프링부트를 사용하고 있기 때문에 해당 부분은 건너뛰어도 된다.

서비스 클래스에 작성하면 하위 메소드에도 적용이 된다는 내용이다.

Transactional에 속성값을 넣을 수 있다.

Propogation

지금같은 경우는 메소드 save 하나.

boardService.doSomething 메소드가 있다.

그러면 Transactional이 같이 걸리게 된다.

트랜잭션의 순서를 모른다.

맨 처음부터 걸리는건지 아니면 해당 메소드에서 다시 시작을 하는건지.

그런 것들을 설정하는 것이 propogation.

기본적으로 REQUIRED.

어떻게 동작하는지 설명을 볼 것이다.

Required가 기본 propagation.

active transaction이 있는지 확인.

없으면 새로운 transaction 생성.

있다면 현재 활성화된 transaction에 실행.

 

save 입장에서는 기존 실행중인 transaction이 없다.

만약에 doSomething에 transaction이 있다면 active transaction이 있는 것이다.

기존 transaction에 이어서 실행이 됨.

doSomething에서 에러가 나면 위의 부분도 다 같이 롤백이 될 것임.

active transaction이 없으면 에러 발생.

active trabsaction이 있으면 에러 발생.

일반적으로 propagation은 건들지 않음.

기본 셋팅을 사용하는 것이 일반적.

필요하면 찾아서 셋팅하면 됨.

 

Isolation Level을 지정할 수 있음.

스프링의 기능보다도 데이터베이스의 동시성 제어의 개념.

DEFAULT는 데이터베이스의 기본값 설정.

MariaDB는 REPEATABLE_READ가 기본값.

ChatGPT에 문의해보도록 함.

혼자서 사용할 때에는 복잡한 경우를 볼 수 없고, 여러 사용자가 있을 때 경험 가능.

동시에 회원 가입 등.

데이터베이스들의 값들을 얼마나 안정적으로 보장을 해 줄 것인가?

4단계로 되어 있다.

밑으로 갈수록 접근을 제어하는 것임.

SERIALIZABLE은 동시에 접근을 할 때, 어떤 로그를 셀렉트하고 있으면, 다른 셀렉트를 하는 것이 불가능.

타이트하게 하면 성능이 저하됨.

아니면 데드락같이 서로 락을 검.

에러의 빈도가 높아짐.

데이터의 안정성이 높다고해서 반드시 좋은 것은 아님.

그래서 2번 ~ 3번을 보통 사용함.

1번은 일반적으로 사용하지 않음.

이상한 값들이 조회될 확률이 있음.

격리성에 따라서 동일한 쿼리를 날려도 값이 달라질 수 있음.

신경쓰지 않고 코딩하면 문제가 발생했을 때 추적이 어려움.

시나리오를 살펴보겠음.

A테이블에 id와 나이가 있음.

서로 다른 사용자로부터

tx1과

tx2가 동시에 접근함.

select1을 읽어보면

select id.

id값이 1임.

age는 10임.

마찬가지로 select 하면 동일한 값이 조회가 될 것임.

Read Uncommitted라고 가정을 해볼 것임.

여기서 age 업데이트를 20으로 하고 커밋은 안했다.

그러나 그 이후로 동일한 쿼리를 날리게 되면,

tx2에서 age가 20으로 조회가 됨.

다시 롤백을 했다.

그러면 tx1은 끝난 것이다.

다시 셀렉트 해보면 age값이 10이 된다.

동일 쿼리인데 10 -> 20 -> 10이 됐다.

해당 값을 믿을 수 없게 됨.

그래서 사용하지 않음.

ORACLE의 기본값으로 READ COMMITTED가 있다.

동일한 값을 가진다.

커밋하지 않으면 AGE 값이 10으로 동일.

 

commit하면 20으로 조회됨.

동시성 레벨에 따라서 아까는 10이었는데, 20이었는데 지금 10이 되고 등.

동일한 쿼리도 달라지게 됨.

주의해야 함.

REPEATABLE READ라면 MariaDB나 MYSQL에서 기본적으로 사용.

동일하게 update하고 commit해도 동일한 결과값이 보장.

그러나 데이터 값이 언제나 보장되는 것은 아님.

Insert나 Delete 쿼리를 하게 되면, 데이터 값이 변함.

경우에 따라서 값이 조회가 되지 않게 됨.

전체 데이터 값을 계산하게 될 때 등.

행이 추가되거나 삭제될 때, REPEATABLE READ도 값이 보장 되지가 않음.

실제 데이터베이스에서 실행해보면 좋음.

값을 조작하는 것 등.

격리성을 조절할 수 있다.

타임아웃이 너무 오래 지속되면 에러 설정 등.

트랜잭션은 프록시를 이용해서 내부적으로 셋팅.

어노테이션 설정을 하게 되면 바로 실행 되는 것이 아니라 스프링에서 해당 메소드 실행하기 전에 처리를 해줌.

 

지금은 외부 클래스의 save 호출

save가 바로 호출이 된다.

이런 경우는 트랜잭션이 적용이 안된다.

왜냐하면 동일한 클래스에 들어있기 때문.

트랜잭션이 적용이 되지 않는다.

반드시 외부 클래스를 호출해야 트랜잭션이 작동한다.

그리고 public 메소드가 선언이 되어야 한다.

트랜잭션 같은 경우는 에러가 안나면 확인이 어렵다.

 

Read-Only도 True를 주게 되면, 성능을 향상시킬 수 있음.

에러가 발생하지 않기 때문에 주의를 해야 함.

Transaction이 이상한 것 같을 때에는, Trace레벨 이용.

log 확인할 수 있게 패키지를 이용하는 것이 좋다.

상기와 같이 Trace.

기본적으로 에러가 발생하면 롤백이 발생함.

주의사항이 있다.

runtime exception만 기본적으로 롤백이 됨.

Runtime Exception만 롤백이 됨.

Exception은 롤백안됨.

EXCEPTION이 발생해도 롤백을 하고 싶은 경우는 rollbackFor 또는 rollbackForClassName을 이용한다.

진짜로 롤백이 안되는지 확인해보겠음.

우선 throws Exception

여기도 추가.

user4 가입

예외 발생

user4가입은 됐는데, 가입인사는 작성이 안됐다.

해당 예시와 같이 작성.

Exception.class에서 롤백을 하도록 함.

Exception은 Throwable을 상속 받음.

RuntimeException은 Exception을 상속받음.

기본값은 RuntimeException에서 받음.

상위에서는 기본적으로 롤백이 되지 않음.

 

또한 CheckedException과 UncheckedException개념이 있다.

Excecption은 반드시 throws Exception을 선언해야 하거나 try catch를 선언해야 한다.

보통 따로 선언하기 보다는 runtimeException을 상속받음.

선택은 적절한 방법을 하라.

이제는 롤백이 될 것이다.

 

에러가 발생해서 롤백이 됨.

사용자 로그인이 불가능해짐.

해당 클래스는 롤백하지 않는다.

꼭 필요할 때만 명시적으로 할 수도 있음.

 

thorws가 우선시 되어야 함.

https://youtu.be/EjLQk97y_LQ?si=qGyEvXeu5meNP0mc

728x90
반응형

관련글 더보기

댓글 영역