
@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이 어떻게 작동하는 지 아래 코드를 보자. 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 어노테이션을 제거해주자. 그리고 에러로 인한 ‘게시글 작성’에 대한 롤백은 ‘보상 트랜잭션’을 활용해 처리하는 방식으로 수정하자. @Transactional 코드 제거하고, ‘게시글 저장’에 대한 보상 트랜잭션 추가하기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; } }
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; } }
@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(); } }
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; } }
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; } }