JAVA/STUDY

[WebFlux] WebFlux 사용 시 Transaction rollback 구현하기 , flatMap , JDBC vs Reactor

Jungry_ 2025. 5. 20. 21:05
반응형

Spring mvc 사용할땐 @Transactional 을 사용하면 모든게 다 해결 되었지만 .. WebFlux 를 사용하면 그렇지 않다 슬프다 !

 

그럼 어떻게 해야할까 바로바로 TransactionalOperator 를 사용해야한다 !

 

일단 build.gradle 에 해당 내용을 추가해준다 특이하게 aop 를 추가해야함 

implementation 'org.springframework.boot:spring-boot-starter-aop:3.4.5'
implementation 'org.springframework.boot:spring-boot-starter-webflux'

 

그리고 ReactiveTransactionManager 를 설정해줘야함 !!!

package com.example.toygry.webfluxstudy.Board.config

import io.r2dbc.spi.ConnectionFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.r2dbc.connection.R2dbcTransactionManager
import org.springframework.transaction.ReactiveTransactionManager
import org.springframework.transaction.annotation.EnableTransactionManagement

@Configuration
@EnableTransactionManagement
class R2dbcTransactionManager {
    @Bean
    fun transactionManager(connectionFactory: ConnectionFactory): ReactiveTransactionManager {
        return R2dbcTransactionManager(connectionFactory)
    }
}

 

최종적으로 코드에서는 

@Service
class BoardService(
    private val boardRepository: BoardRepository,
    private val txOperator: TransactionalOperator
) {
   private val log = LoggerFactory.getLogger(javaClass)
   fun createBoard(request: CreateBoardRequest): Mono<BoardResponse> {
       val board  = Board(title = request.title, content = request.content)

       return boardRepository.save(board)
           .map { saved ->
               BoardResponse(
                   id = saved.id!!,
                   title = saved.title,
                   content = saved.content
               )
           }
           .`as`(txOperator::transactional)
   }

 

중요 부분은 `as` 이 부분이다

as(txOperator::transactional) 를 사용하게 되는데 

전체 체인을 트랜잭션 컨텍스트로 감싸기 -> 예외 발생 시 rollback 이 됨

 

따라서 rollback 을 시키고싶은 부분들에 as 를 사용해서 묶어주면 되는 것이다 ! 어렵지 않음 ~

 

그럼 왜  둘 사이에 이런 차이가 발생하는지 알아보자 ! 

 

JDBC vs R2DBC


 

 

  JDBC R2DBC
차이 RDBMS(관계형 데이터베이스) 접근하기 위한 블로킹용 API RDBMS(관계형 데이터베이스)접근하기 위한 논블로킹용 API
프로그래밍 모델 동기 (blocking) 비동기 (non-blocking)
스레드 처리 요청마다 별도 스레드 사용 (1 req = 1 thread) 이벤트 루프 기반, 스레드 적게 씀
리소스 사용 스레드 수에 비례하여 커넥션 사용 커넥션 재사용 + 효율적
예: .findById() User user = repo.findById(id) → 결과 나올 때까지 대기 Mono<User> → 결과 나올 때까지 블로킹 없음
예외 처리 try-catch .onErrorResume { ... }
트랜잭션 처리 @Transactional 사용 (AOP 기반) TransactionalOperator로 감쌈
쓰기 지연 없음 (즉시 커밋/롤백) 체인 실행 완료 시점에 트랜잭션 처리

 

핵심 개념 차이

  • JDBC: 요청 → 스레드 확보 → DB 커넥션 점유 → 응답 받을 때까지 대기
  • R2DBC: 요청 → 리액터 체인 등록 → 응답 때까지 스레드는 놀고 있음

차이 때문에 WebFlux 같은 논블로킹 시스템에서는 JDBC쓰면 성능이 심각하게 저하됨

 

@TransactionalWebFlux에서 동작하지 않는가?

AOP @Transactional은 실제로는 프록시를 만들어 스레드 단위로 트랜잭션을 걸어줌
WebFlux 체인은 비동기 이벤트로 이루어져 있어 스레드가 고정되지 않음
결과 트랜잭션 시작/종료가 제대로 감지되지 않아서 → rollback/commit 안 됨

 

좀 더 간단하게 설명하면

@Transactional 은 스레드 단위로 트랜잭션을 관리함

JDBC 의 경우에는 요청의 시작부터 끝까지 스레드를 확보해 두기때문에 스레드 단위로 관리 가능

WebFlux 의 경우에는 스레드를 확보해두지 않고 요청의 단계별로 스레드가 바뀔 수 있어서 트랜잭션 시작/ 종료가 제대로 감지되지 않음

반응형