JSCODE Logo
프로그래밍 과외블로그후기멘토진
회사명 : JSCODE대표 : 박재성사업자 등록번호 : 244-22-01557통신판매업 : 제 2023-인천미추홀-0381 호
학원 명칭 : 제이에스코드(JSCODE)원격학원학원설립ㆍ운영 등록번호 : 제6063호

서울특별시 구로구 경인로 20가길 11(오류동, 아델리아)

Copyright ⓒ 2025 JSCODE - 최상위 현업 개발자들의 프로그래밍 교육 All rights reserved.

이용약관개인정보처리방침
← 블로그 목록으로 돌아가기

[실습] 게시글 작성 API에 Saga 패턴 적용시키기 - 1

JSCODE 박재성
JSCODE 박재성
2025-12-06
author
JSCODE 박재성
category
MSA
createdAt
Dec 6, 2025
series
비전공자도 이해할 수 있는 MSA 입문/실전
slug
practice-apply-saga-to-post-create-1
type
post
updatedAt
Dec 6, 2025 05:43 AM

✅ 게시글 작성 API에 Saga 패턴 적용시키기

아래 구조처럼 기존 게시글 작성 API에 Saga 패턴을 적용시켜보자.
notion image
 
  1. ‘게시글 작성 API’ 기존 로직의 문제점 확인하기
    1. service/BaordService
      @Transactional public void create(CreateBoardRequestDto createBoardRequestDto) { // 게시글 작성 전 100 포인트 차감 pointClient.deductPoints(createBoardRequestDto.getUserId(), 100); // 게시글 작성 Board board = new Board( createBoardRequestDto.getTitle(), createBoardRequestDto.getContent(), createBoardRequestDto.getUserId() ); this.boardRepository.save(board); // 게시글 작성 시 작성자에게 활동 점수 10점 부여 userClient.addActivityScore(createBoardRequestDto.getUserId(), 10); }
      위 코드를 보면 ‘포인트 차감 → 게시글 작성 → 활동 점수 적립’ 순으로 코드를 작성한 것처럼 보인다. 하지만 실제 코드 실행은 ‘포인트 차감 → 활동 점수 적립 → 게시글 작성’ 순으로 실행된다. 이렇게 실행되는 이유는 @Transactional 어노테이션 때문이다. @Transactional이 어떻게 작동하는 지 아래 코드를 보자.
       
      service/BoardService
      public void create(CreateBoardRequestDto createBoardRequestDto) { transactionManager.begin(); try { // 게시글 작성 전 100 포인트 차감 pointClient.deductPoints(createBoardRequestDto.getUserId(), 100); // 게시글 작성 Board board = new Board( createBoardRequestDto.getTitle(), createBoardRequestDto.getContent(), createBoardRequestDto.getUserId() ); this.boardRepository.save(board); // 게시글 작성 시 작성자에게 활동 점수 10점 부여 userClient.addActivityScore(createBoardRequestDto.getUserId(), 10); transactionManager.commit(); } catch (Exception e) { transactionManager.rollback(); throw e; } }
      게시글 작성은 commit()이 실행된 이후에 완료되기 때문에 실제로는 ‘포인트 차감 → 활동 점수 적립 → 게시글 작성’ 순으로 작동한다. 그래서 기획에서 의도한 순서대로 작동하게 만들기 위해 @Transactional 어노테이션을 제거해주자. 그리고 에러로 인한 ‘게시글 작성’에 대한 롤백은 ‘보상 트랜잭션’을 활용해 처리하는 방식으로 수정하자.
       
  1. @Transactional 코드 제거하고, ‘게시글 저장’에 대한 보상 트랜잭션 추가하기
    1. service/BoardService
      public void create(CreateBoardRequestDto createBoardRequestDto) { // 게시글 저장을 성공했는 지 판단하는 플래그 boolean isBoardCreated = false; Long savedBoardId = null; // 포인트 차감을 성공했는 지 판단하는 플래그 boolean isPointDeducted = false; try { // 게시글 작성 전 100 포인트 차감 pointClient.deductPoints(createBoardRequestDto.getUserId(), 100); // 게시글 작성 Board board = new Board( createBoardRequestDto.getTitle(), createBoardRequestDto.getContent(), createBoardRequestDto.getUserId() ); Board savedBoard = this.boardRepository.save(board); savedBoardId = savedBoard.getBoardId(); isBoardCreated = true; // 게시글 저장 성공 플래그 // 게시글 작성 시 작성자에게 활동 점수 10점 부여 userClient.addActivityScore(createBoardRequestDto.getUserId(), 10); } catch (Exception e) { if (isBoardCreated) { // 게시글 작성 보상 트랜잭션 => 게시글 삭제 this.boardRepository.deleteById(savedBoardId); } // 실패 응답으로 처리하기 위해 예외 던지기 throw e; } }
       
  1. 포인트 적립 API 요청할 수 있는 로직 추가하기
    1. dto/AddPointsRequestDto
      public class AddPointsRequestDto { private Long userId; private int amount; public AddPointsRequestDto(Long userId, int amount) { this.userId = userId; this.amount = amount; } public Long getUserId() { return userId; } public int getAmount() { return amount; } }
       
      client/PointClient
      @Component public class PointClient { ... public void addPoints(Long userId, int amount) { AddPointsRequestDto addPointsRequestDto = new AddPointsRequestDto(userId, amount); this.restClient.post() .uri("/points/add") .contentType(MediaType.APPLICATION_JSON) .body(addPointsRequestDto) .retrieve() .toBodilessEntity(); } }
       
  1. ‘포인트 차감’에 대한 보상 트랜잭션 추가하기
    1. service/BoardService
      public void create(CreateBoardRequestDto createBoardRequestDto) { // 게시글 저장을 성공했는 지 판단하는 플래그 boolean isBoardCreated = false; Long savedBoardId = null; // 포인트 차감을 성공했는 지 판단하는 플래그 boolean isPointDeducted = false; try { // 게시글 작성 전 100 포인트 차감 pointClient.deductPoints(createBoardRequestDto.getUserId(), 100); isPointDeducted = true; // 포인트 차감 성공 플래그 // 게시글 작성 Board board = new Board( createBoardRequestDto.getTitle(), createBoardRequestDto.getContent(), createBoardRequestDto.getUserId() ); Board savedBoard = this.boardRepository.save(board); savedBoardId = savedBoard.getBoardId(); isBoardCreated = true; // 게시글 저장 성공 플래그 // 게시글 작성 시 작성자에게 활동 점수 10점 부여 userClient.addActivityScore(createBoardRequestDto.getUserId(), 10); } catch (Exception e) { if (isBoardCreated) { // 게시글 작성 보상 트랜잭션 => 게시글 삭제 this.boardRepository.deleteById(savedBoardId); } if (isPointDeducted) { // 포인트 차감 보상 트랜잭션 => 포인트 적립 pointClient.addPoints(createBoardRequestDto.getUserId(), 100); } // 실패 응답으로 처리하기 위해 예외 던지기 throw e; } }
       
  1. ‘활동 점수 적립’에 대한 보상 트랜잭션 추가하기
    1. 아래 로직을 보면 ‘활동 점수 적립’ 코드는 로직의 맨 마지막에 실행된다. 따라서 이 코드가 실패하면 ‘활동 점수 적립’ 자체가 실패하는 것이기 때문에 보상 트랜잭션을 따로 해줄 필요가 없다. 만약 이 코드가 성공하면 로직 전체가 성공으로 끝이 난다. 이런 이유에서 ‘활동 점수 적립’에 대한 보상 트랜잭션은 추가해줄 필요가 없다.
      service/BoardService
      public void create(CreateBoardRequestDto createBoardRequestDto) { // 게시글 저장을 성공했는 지 판단하는 플래그 boolean isBoardCreated = false; Long savedBoardId = null; // 포인트 차감을 성공했는 지 판단하는 플래그 boolean isPointDeducted = false; try { // 게시글 작성 전 100 포인트 차감 pointClient.deductPoints(createBoardRequestDto.getUserId(), 100); isPointDeducted = true; // 포인트 차감 성공 플래그 // 게시글 작성 Board board = new Board( createBoardRequestDto.getTitle(), createBoardRequestDto.getContent(), createBoardRequestDto.getUserId() ); Board savedBoard = this.boardRepository.save(board); savedBoardId = savedBoard.getBoardId(); isBoardCreated = true; // 게시글 저장 성공 플래그 // 게시글 작성 시 작성자에게 활동 점수 10점 부여 userClient.addActivityScore(createBoardRequestDto.getUserId(), 10); } catch (Exception e) { if (isBoardCreated) { // 게시글 작성 보상 트랜잭션 => 게시글 삭제 this.boardRepository.deleteById(savedBoardId); } if (isPointDeducted) { // 포인트 차감 보상 트랜잭션 => 포인트 적립 pointClient.addPoints(createBoardRequestDto.getUserId(), 100); } // 실패 응답으로 처리하기 위해 예외 던지기 throw e; } }
 
👨🏻‍🏫
Saga 패턴에 대한 코드는 전부 작성을 완료했다. 다음 강의에서 작성한 코드가 의도한 대로 잘 작동하는 지 테스트해보자.
 
author
JSCODE 박재성
category
MSA
createdAt
Dec 6, 2025
series
비전공자도 이해할 수 있는 MSA 입문/실전
slug
type
series-footer
updatedAt
Dec 6, 2025 05:45 AM
📎
이 글은 비전공자도 이해할 수 있는 MSA 입문/실전 강의의 수업 자료 중 일부입니다.