[블로그 만들기] 5. 글쓰기 기능 만들기 (2)

@Transactional
Sep 04, 2024
[블로그 만들기] 5. 글쓰기 기능 만들기 (2)

5. 글쓰기 기능 만들기 (2)

DB 에 글쓰기 ‘INSERT’ 를 요청할 Repository 를 만들었다면, 글쓰기 기능을 넣을 차례다.
기능을 만들기 전에 transaction 먼저 이해하고 넘어가자.
 

5-3. Transaction 이해하기

 
DB 에는 여러 명이 접근할 수 있다. SELECT 는 수백 건 이 들어올 수 있다.
 
ex 1) DB 에 ‘사과’ 라는 데이터 read 요청을 한다. 100 명이 아니라 1000 명이 요청해도 항상 데이터는 사과다. 10 명의 사람이 ‘사과’ 데이터를 보고 싶어서 select 요청 했을 때, 요청 시간은 모두가 다르다. 그리고 그 중 한 명이 DB 에 변경을 요청을 할 가능성도 있다. (write)
5번 사람이 ‘사과’ 를 ‘딸기’ 로 변경 요청을 했다고 가정해보자. 그렇다면, 6번부터 10번 사람은 읽은 데이터를 ‘딸기’ 로 보게 된다.
동시에 같은 데이터를 요청했는데, 누구는 ‘사과’ 를 ‘사과’ 로 보고 누구는 ‘딸기’ 로 보는 경우가 생기는 것이다.
 
ex 2) 다른 예시로, 똑같이 10 명의 사람이 데이터 read 요청을 했다고 생각해보자.
1번 부터 7번 사람 까지는 ‘잔액 10,000 원’ 을 보고, 8번 사람이 돌연 5,000 원을 인출했을 때, 9번, 10번 사람은 ‘잔액 5,000 원’ 을 보는 경우가 생긴다.
이 때, 잔액을 출금하게 되면 1~7번 사람은 ‘10,000 원 출금하기’ 를 시도 할 것이고, 9~10번은 ‘5,000 원 출금하기’ 를 시도한다. → (당연히 꼬일 것, 동시성 문제가 생긴다.)
 
⇒ 해결 방법
DB 에서 8번 사람이 write 요청을 하네, 하고 기록을 해둬야 한다.
8번이 출금 하고 있는 도중에 9번 10번의 select 요청이 들어왔다.
이 때 내가 정하는 거야, 정하는 건 내 마음 ★
  1. 나는 5,000 원으로 바꿀거지만, 바꾸기 전 데이터를 줄거야 라고 설정하면 → 9번, 10번은 10,000 원이 있다는 것을 보게 한다. (→ commit 되기 전 데이터를 보여주는 게 일반적)
  1. write 요청이 들어온 시점부터는 변경된 데이터를 줘야지 하고 설정할 수도 있다 → 9번, 10번에게 오천 원이 있다는 것을 보게 한다.
 

고립성

 
💡
고립성(Isolation)
고립성은 여러 트랜잭션이 동시에 수행될 때 서로 간섭하지 않도록 보장하는 성질이다. 즉, 각 트랜잭션이 독립적으로 수행되어 다른 트랜잭션의 중간 상태나 미완료된 데이터를 보지 않도록 한다. 이를 통해 데이터의 일관성을 유지할 수 있다.
 
(위의 예시에 이어)
5,000 원으로 변경 요청 들어와서 트랜잭션이 insert 하고 있고, 그것이 안끝난 상황에서 9번, 10번의 select 요청(read)이 들어오면 9번과 10번에게는 commit 되기 전 데이터를 보여준다. (→ 9번과 10번은 10,000 원을 보게됨)
트랙잭션이 끝나기 전에는 메모리에만 남아있다.
트랜잭션이 끝나고 나서 9번 10번이 요청하면 commit 된 데이터를 준다.
이것을 고립성이라 한다. 내가 write 요청을 하고 있으면, 그 전 데이터를 준다.
고립성의 종류가 많은데 이걸 isolation 이라 한다.
고립성에 대한 설정은 DB 마다 다르다, 내가 설정할 수 도 있음.
commit 되기 전 데이터를 보여주는 게 일반적
트랜잭션이 시작되고 끝나기 전에는 이전데이터를 보여준다.
 
transaction 의 뜻은 일의 최소 단위를 의미한다.
일의 최소 단위는 상황마다 다르다.
tansaction은 망가질 수 있다. transaction이 파괴되면 DB는 기본적으로 rollback 을 한다.
 
notion image
 

5-4. Transaction 설정하기(@Transactional)

 
DB 의 변경 요청은 @Transactional 이 걸려있어야 한다.
걸려있는 동안 다른 사람이 보면 트랜잭션이 안끝났으니까 이전 데이터를 보고 있는것.
트랜잭션이 없을 때 발생할 수 있는 예시로, 통계 쿼리의 경우 만일 트랜잭션이 없다면 데이터가 다 꼬이게 된다. 한번 읽었던 데이터를 또 읽어야 하고, 변경된 데이터를 읽게 되면 유령데이터가 되기도 한다.
반드시 수정은 트랜잭션으로 고립시켜야한다.
내가 수정하고 있는 상태에서 누군가가 읽으러 들어왔을 때,일관되게 시스템이 만들어져있어야한다. 커밋 전의 데이터를 읽게 할 것인지, 커밋 후의 데이터를 읽게 할 것인지가 반드시 정립되어 있어야 한다.
 

Transaction

 
  1. 트랜잭션은 일의 최소 단위를 의미한다.
  1. 트랜잭션이 시작되면 고립성이 시작된다. write 가있기 때문에 데이터가 민감해진다. 내가 write 하고있을때 다른 사람은 write 하지 못하도록 원천 봉쇄시키는것.
ex. DB 의 3번째 row 를 손댈거야, 그 때 고립되어야함. 다른데이터는 손 대도 돼.
동시에 다른 행은 괜찮지만, 같은 행은 안되게 해 → 고립
transaction 을 걸어서 데이터를 고립시킨다.
read 는 읽는거니까 느려지지 않고 고립되지 않지만,
ex. 3번째 행에 transaction 이 걸렸어, 100명이 동일한 행을 수정하고 싶어, 1번째 애가 수정하고 있을 때 → 다른 애들이 wait 걸림 → 시스템이 느려짐 read 할 때는 commit 된 데이터를 볼거니까
(transaction 을 많이 하는게 좋지 않다.)
 
ex. 파라미터로 들어온 title 이 null 일 경우, 트랜잭션 걸리고 DB 에서 not null 설정이 되어있으면 이거 안돼~ 하고 값이 돌아오기 까지 한참이다. 이런 불필요한 작업이 생기지 않게, 고립성이 안되게 유효성 검사를 컨트롤러에서 해줘야 한다.
transaction 을 줄일 수 있다.
게시글 쓰기로 DB 에 insert 할 때, 꼭 @Transactional 붙이기
package shop.mtcoding.blog2.board; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @Repository // @Repository 를 붙이면 스프링이 new 를 해서 IoC(컬렉션 List자료형 같은거) 에 저장한다. public class BoardRepository { @Autowired // IoC에 있는 객체를 찾아온다. private EntityManager em; // insert 하기 @Transactional public void save(String title, String content) { Query query = em.createNativeQuery("insert into board_tb(title, content, created_at) values(?,?,now())"); query.setParameter(1, title); query.setParameter(2, content); query.executeUpdate(); // em 이 위의 해당쿼리를 완성시켜서 전송함 } // insert 나 delete 할 때는 query.executeUpdate(); }
Share article

eunmouse