미니 프로젝트 - Spring에서 비동기 처리

Zyoon·2025년 6월 25일

미니프로젝트

목록 보기
20/36
post-thumbnail

📘Spring에서 주 로직 실패와 무관하게 로그를 남기는 방법


상황

  • 서비스 로직을 처리할 때, 주 트랜잭션이 실패해도 로그는 꼭 남기고 싶은 경우
  • 예를 들어, 사용자 권한 변경이 실패하더라도 시도한 내용은 기록하고 싶은 경우

1. @Async — 비동기 방식

  • 로그 저장을 비동기 스레드에서 수행
  • 주 트랜잭션과 별개로 처리됨

코드예시

@Service
public class LogService {

    @Async
    public void saveLog(LogRequest request) {
        logRepository.save(request.toEntity());
    }
}
@Transactional
public void changeUserRole(...) {
    ...
    logService.saveLog(...); // 주 트랜잭션과 분리되어 실행됨
    ...
    user.updateRole(newRole); //주 로직
}

주의할 점

  • @EnableAsync 설정 필요
  • 로그 저장 보장성이 100%는 아님
  • 성능은 좋지만, 예외 제어가 어렵고 스레드풀 상태에 따라 유실 가능

2. REQUIRES_NEW - 새 트랜잭션 방식

  • @Transactional(propagation = Propagation.REQUIRES_NEW) 사용
  • 로그 저장 로직을 새 트랜잭션으로 분리
  • 로그 저장이 트랜잭션 롤백과 무관하게 보장
  • 예외 제어 및 로그 보존에 더 안정적

코드 예시

@Service
public class LogService {

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(LogRequest request) {
        logRepository.save(request.toEntity());
    }
}
@Transactional
public void changeUserRole(...) {
    ...
    try {
        logService.saveLog(...); // 실패해도 메인 트랜잭션에 영향 없음
    } catch (Exception e) {
        log.error("로그 저장 실패", e);
    }
    ...
}

주의할 점

  • REQUIRES_NEW 방식 사용할 땐 import org.springframework.transaction.annotation.Transactional이 맞는지 확인

3. Spring 이벤트 기반 비동기 처리

  • @Async는 단일 메서드 비동기처리에 적합
  • 하지만, 핵심 로직과 부가 작업 분리에는 이벤트 기반이 더 유리
  • @Async + @EventListener 조합으로 비동기 분리 처리와 유지보수성 향상이 가능

코드예시

  1. Config 등록
@Configuration
@EnableAsync
public class AsyncConfig {
}
  1. 이벤트 발행
@Service
@RequiredArgsConstructor
public class UserService {
    private final ApplicationEventPublisher publisher;
    
		@Transactional
		public void changeUserRole(...) {
		    ...
		    publisher.publishEvent(...);
		    ...
		    user.updateRole(userRole); //주 로직
		}
}
  1. 이벤트 핸들러 비동기처리
@Component
@RequiredArgsConstructor
public class LogEventHandler {

    private final LogService logService;

    @Async
    @EventListener
    public void addLog(LogRequest request){
        logService.addLog(request);
    }
}

주의할점

  • @Async가 작동하려면 클래스에 @EnableAsync가 선언되어 있어야 한다.
  • 보통 @SpringBootApplication에 같이 선언하거나 별도 설정 파일에서 지정해준다.

항목 비교 및 요약

항목@AsyncREQUIRES_NEW@EventListener + @Async
실행 방식비동기 (멀티스레드)동기 (새 트랜잭션)비동기 (멀티스레드 + 이벤트 기반)
트랜잭션 분리기존 트랜잭션 영향받음명시적 분리핸들러에서 분리 가능
성능빠름보통빠름 (확장성↑)
예외 제어어렵고 로그 유실 위험쉬움적절히 제어 가능 (로직 분리)
유지보수성중간 (직접 호출 필요)낮음 (서비스 결합도↑)높음 (핸들러로 관심사 분리)
적합한 상황빠른 처리, 부수작업 허용로그 등 반드시 남겨야 할 때다양한 후처리 분기 처리, 모듈 분리
  • 로그가 무조건 남아야 한다면 → REQUIRES_NEW
  • 성능이 더 중요하고 유실 허용 가능하면@Async
  • 비즈니스-부가기능 분리, 확장 고려한다면 → @EventListener + @Async
profile
기어 올라가는 백엔드 개발

0개의 댓글