신규 트랜잭션 테스트
스프링 부트에서 예외 발생시 Propagation.REQUIRES_NEW 트랜잭션으로 생성된 데이터는 정상적으로 커밋된 것을 확인하기 위한 테스트
테스트 환경 구성
Section titled “테스트 환경 구성”- 채번 서비스를 Propagation.REQUIRES_NEW로 설정해서 생성한다.
UniqueIdService.kt @Service@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = [BizException::class])class UniqueIdService(private val uniqueIdMapper: UniqueIdMapper) {fun generateUniqueId(): String {return try {var id: String?do {id = UUID.randomUUID().toString()if (uniqueIdMapper.selectUniqueIdCnt(id) > 0) {continue}uniqueIdMapper.insertUniqueId(UniqueId(id,"Y",null,1000,"0:0:0:0:0:0:0:1",null,1000,"0:0:0:0:0:0:0:1"))break} while (true)id} catch (e: Exception) {throw BizException(e)}}}UniqueIdService.java @Service@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = BizException.class)@RequiredArgsConstructorpublic class UniqueIdService {private final UniqueIdMapper uniqueIdMapper;public String generateUniqueId() {try {String id;do {id = UUID.randomUUID().toString();if (uniqueIdMapper.selectUniqueIdCnt(id) > 0) {continue;}uniqueIdMapper.insertUniqueId( UniqueId.builder().uniqueId(id).useYn("Y").build());break;} while (true);return id;} catch (final Exception e) {throw new BizException(e);}}} - 롤백 테스트를 하기 위한 API를 생성한다.
- 채번을 한다.
- 롤백을 확인하기 위한 임시 데이터를 생성한다.
- 익셉션을 발생시킨다.
any service class @Service@Transactional(rollbackFor = [BizException::class])class SpringBootService(private val uniqueIdService: UniqueIdService,private val photoBoardMapper: PhotoBoardMapper,) {fun testGenerateUniqueId(param: PhotoBoard) {try {photoBoardMapper.insertPhotoBoard(param)uniqueIdService.generateUniqueId()throw RuntimeException("test")} catch (e: Exception) {throw BizException(e)}}}any service class @Service@Transactional(rollbackFor = BizException.class)@RequiredArgsConstructorpublic class SpringBootService {private final PhotoBoardService photoBoardService;private final UniqueIdService uniqueIdService;public void testGenerateUniqueId(final PhotoBoard param) {try {photoBoardService.insertPhotoBoard(param);uniqueIdService.generateUniqueId();throw new RuntimeException("test");} catch (final Exception e) {throw new BizException(e);}}}- 컨트롤러 코드
any controller class @Operation(summary = "롤백되었을때 별도의 트랜잭션으로 생성된 데이터가 남아있는지 확인하기 위한 테스트", description = "롤백되었을때 별도의 트랜잭션으로 생성된 데이터가 남아있는지 확인하기 위한 테스트 API")@PostMapping("/when-exception-occur-another-transaction-commit-test")fun testGenerateUniqueId(@Valid @ModelAttribute param: InsertPhotoBoardRequest): ResponseEntity<ResponseDto<Unit>> {log.info("testGenerateUniqueId param: $param")val model = param.toModel()springBootService.testGenerateUniqueId(model)return ResponseEntity.status(HttpStatus.CREATED).body((ResponseDto(Unit)))}any controller class @Operation(summary = "롤백되었을때 별도의 트랜잭션으로 생성된 데이터가 남아있는지 확인하기 위한 테스트", description = "롤백되었을때 별도의 트랜잭션으로 생성된 데이터가 남아있는지 확인하기 위한 테스트 API")@PostMapping("/when-exception-occur-another-transaction-commit-test")public ResponseEntity<Void> testGenerateUniqueId(@Valid @ModelAttribute InsertPhotoBoardRequest param) {log.info("param: {}", param);springBootService.testGenerateUniqueId(param.toModel());return ResponseEntity.status(HttpStatus.OK).build();} - 테스트 코드를 생성한다.
any test class @DisplayName("롤백되었을때 별도의 트랜잭션으로 생성된 데이터가 남아있는지 확인하기 위한 테스트")@Testfun `롤백되었을때 별도의 트랜잭션으로 생성된 데이터가 남아있는지 확인하기 위한 테스트`() {return Given {spec(multipartRequestSpecification)formParam("title", "test")formParam("content", "test")multiPart("photo", "sample2.jpg", readResourceFileAsBytes("sample/image/sample2.jpg"), MediaType.IMAGE_JPEG_VALUE)} When {post("/spring-boot/when-exception-occur-another-transaction-commit-test")} Then {statusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR)} Extract {println(body().asString())}}any test class @DisplayName("롤백되었을때 별도의 트랜잭션으로 생성된 데이터가 남아있는지 확인하기 위한 테스트")@Testvoid testGenerateUniqueId() {given().spec(multipartRequestSpecification).formParam("title", "test").formParam("content", "test").multiPart("photo", "sample2.jpg", FileUtil.readClasspathFile("sample/image/sample2.jpg"), MediaType.IMAGE_JPEG_VALUE).when().post("/spring-boot/when-exception-occur-another-transaction-commit-test").then().statusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR).log().all();} - 테스트를 수행한다.
- 로그 확인
- 데이터 생성 후 예외발생 확인
1. /*insertPhotoBoard*/INSERT INTO T_PHOTO_BOARD ( TITLE, CONTENT, PHOTO_PATH, CREATE_DT, CREATE_USER_ID, CREATE_IP, UPDATE_DT, UPDATE_USER_ID, UPDATE_IP )VALUES ( 'test', 'test', 'http://localhost/photo/sample2.jpg', NOW(), 1000, '0:0:0:0:0:0:0:1', NOW(), 1000, '0:0:0:0:0:0:0:1' ){executed in 13 msec}2026-02-11 10:29:24,959 [DEBUG] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator: com.zaxxer.hikari.pool.ProxyStatement.getGeneratedKeys(ProxyStatement.java:230)1. getGeneratedKeys on query: /*insertPhotoBoard*/INSERT INTO T_PHOTO_BOARD ( TITLE, CONTENT, PHOTO_PATH, CREATE_DT, CREATE_USER_ID, CREATE_IP, UPDATE_DT, UPDATE_USER_ID, UPDATE_IP )VALUES ( 'test', 'test', 'http://localhost/photo/sample2.jpg', NOW(), 1000, '0:0:0:0:0:0:0:1', NOW(), 1000, '0:0:0:0:0:0:0:1' ){executed in 0 msec}2026-02-11 10:29:24,965 [INFO ] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator:|--------------||generated_key ||--------------||1049 ||--------------|2026-02-11 10:29:24,979 [DEBUG] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator: com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)2. /*selectUniqueIdCnt*/SELECT COUNT(0) AS CNTFROM T_UNIQUE_IDWHERE UNIQUE_ID = '8bee1528-7592-410d-aa9a-0be0e5680955'{executed in 4 msec}2026-02-11 10:29:24,985 [INFO ] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator:|----||cnt ||----||0 ||----|2026-02-11 10:29:24,993 [DEBUG] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator: com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)2. /*insertUniqueId*/INSERT INTO T_UNIQUE_ID ( UNIQUE_ID, USE_YN, CREATE_DT, CREATE_USER_ID, CREATE_IP, UPDATE_DT, UPDATE_USER_ID, UPDATE_IP )VALUES ( '8bee1528-7592-410d-aa9a-0be0e5680955', 'Y', NOW(), 1000, '0:0:0:0:0:0:0:1', NOW(), 1000, '0:0:0:0:0:0:0:1' ){executed in 4 msec}2026-02-11 10:29:25,067 [ERROR] [http-nio-auto-1-exec-1] b.m.c.c.e.a.RestApiExceptionAdvice: ### BizException occurredjava.lang.RuntimeException: testat biz.mintchoco.carrot.api.spring_boot.service.SpringBootService.testGenerateUniqueId(SpringBootService.kt:21)...at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:63)at java.base/java.lang.Thread.run(Thread.java:1583){"payload":null,"isSuccess":false,"resultCode":"FAILURE","message":"test"}1. /*insertPhotoBoard*/INSERT INTO PHOTO_BOARD ( TITLE, CONTENT, PHOTO_PATH, CREATE_DT, CREATE_USER_ID, CREATE_IP, UPDATE_DT, UPDATE_USER_ID, UPDATE_IP )VALUES ( 'test', 'test', 'http://localhost:8080/sample2.jpg', NOW(), 1000, '0:0:0:0:0:0:0:1', NOW(), 1000, '0:0:0:0:0:0:0:1' ){executed in 8 msec}2026-02-13 11:36:50,651 [DEBUG] [http-nio-auto-1-exec-1] o.a.i.l.j.BaseJdbcLogger: <== Updates: 12026-02-13 11:36:50,653 [DEBUG] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator: com.zaxxer.hikari.pool.ProxyStatement.getGeneratedKeys(ProxyStatement.java:229)1. getGeneratedKeys on query: /*insertPhotoBoard*/INSERT INTO PHOTO_BOARD ( TITLE, CONTENT, PHOTO_PATH, CREATE_DT, CREATE_USER_ID, CREATE_IP, UPDATE_DT, UPDATE_USER_ID, UPDATE_IP )VALUES ( 'test', 'test', 'http://localhost:8080/sample2.jpg', NOW(), 1000, '0:0:0:0:0:0:0:1', NOW(), 1000, '0:0:0:0:0:0:0:1' ){executed in 0 msec}2026-02-13 11:36:50,656 [INFO ] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator:|--------------||generated_key ||--------------||1057 ||--------------|2026-02-13 11:36:50,665 [DEBUG] [http-nio-auto-1-exec-1] o.a.i.l.j.BaseJdbcLogger: ==> Preparing: /*selectUniqueIdCnt*/ SELECT COUNT(0) AS CNT FROM UNIQUE_ID WHERE UNIQUE_ID = ?2026-02-13 11:36:50,666 [DEBUG] [http-nio-auto-1-exec-1] o.a.i.l.j.BaseJdbcLogger: ==> Parameters: f99cf9e6-45ed-4848-b531-96de0df18a55(String)2026-02-13 11:36:50,669 [DEBUG] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator: com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)2. /*selectUniqueIdCnt*/SELECT COUNT(0) AS CNTFROM UNIQUE_IDWHERE UNIQUE_ID = 'f99cf9e6-45ed-4848-b531-96de0df18a55'{executed in 3 msec}2026-02-13 11:36:50,684 [INFO ] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator:|----||cnt ||----||0 ||----|2026-02-13 11:36:50,684 [DEBUG] [http-nio-auto-1-exec-1] o.a.i.l.j.BaseJdbcLogger: <== Total: 12026-02-13 11:36:50,686 [DEBUG] [http-nio-auto-1-exec-1] o.a.i.l.j.BaseJdbcLogger: ==> Preparing: /*insertUniqueId*/ INSERT INTO UNIQUE_ID ( UNIQUE_ID , USE_YN , CREATE_DT , CREATE_USER_ID , CREATE_IP , UPDATE_DT , UPDATE_USER_ID , UPDATE_IP ) VALUES ( ? , 'Y' , NOW() , ? , ? , NOW() , ? , ? )2026-02-13 11:36:50,687 [DEBUG] [http-nio-auto-1-exec-1] o.a.i.l.j.BaseJdbcLogger: ==> Parameters: f99cf9e6-45ed-4848-b531-96de0df18a55(String), 1000(Integer), 0:0:0:0:0:0:0:1(String), 1000(Integer), 0:0:0:0:0:0:0:1(String)2026-02-13 11:36:50,693 [DEBUG] [http-nio-auto-1-exec-1] n.s.l.l.s.Slf4jSpyLogDelegator: com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)2. /*insertUniqueId*/INSERT INTO UNIQUE_ID ( UNIQUE_ID, USE_YN, CREATE_DT, CREATE_USER_ID, CREATE_IP, UPDATE_DT, UPDATE_USER_ID, UPDATE_IP) VALUES ( 'f99cf9e6-45ed-4848-b531-96de0df18a55', 'Y', NOW(), 1000, '0:0:0:0:0:0:0:1', NOW(), 1000, '0:0:0:0:0:0:0:1'){executed in 5 msec}2026-02-13 11:36:50,693 [DEBUG] [http-nio-auto-1-exec-1] o.a.i.l.j.BaseJdbcLogger: <== Updates: 1
- T_PHOTO_BOARD 테이블에 생성된 데이터가 롤백된 것을 확인
[2026-02-11 10:35:50] carrot> select * from T_PHOTO_BOARD where PHOTO_BOARD_ID = 1049[2026-02-11 10:35:51] 503 ms (execution: 15 ms, fetching: 488 ms)에서 0개 행 불러옴[2026-02-13 13:33:43] si_helper> select * from PHOTO_BOARD where PHOTO_BOARD_ID = 1057[2026-02-13 13:33:44] 393 ms (execution: 17 ms, fetching: 376 ms)에서 0개 행 불러옴
- T_UNIQUE_ID 테이블에 생성된 데이터는 남아있는것을 확인
[2026-02-11 10:37:45] carrot> select * from T_UNIQUE_ID where UNIQUE_ID = '8bee1528-7592-410d-aa9a-0be0e5680955'[2026-02-11 10:37:46] 513 ms (execution: 19 ms, fetching: 494 ms)에서 1부터 1개 행을 불러왔습니다[2026-02-13 13:35:31] si_helper> select * from UNIQUE_ID where UNIQUE_ID = 'f99cf9e6-45ed-4848-b531-96de0df18a55'[2026-02-13 13:35:32] 472 ms (execution: 21 ms, fetching: 451 ms)에서 1부터 1개 행을 불러왔습니다
- 데이터 생성 후 예외발생 확인