[Spring] 이벤트 시스템으로 느슨한 결합 구현하기

2025. 2. 5. 14:35·spring

스프링 이미지

1. 스프링 이벤트란?

스프링 이벤트는 Observer 패턴을 기반으로 동작합니다. 특정 이벤트가 발생하면, 그 이벤트를 "구독"하고 있는 리스너들이 호출되는 방식이죠. 이를 통해 비동기 처리, 로깅, 알림 시스템 등 다양한 곳에 활용할 수 있습니다.

✨ 왜 사용할까요?

  • 느슨한 결합: 이벤트 발행자와 리스너가 서로의 존재를 몰라도 됩니다.
  • 유지보수 용이: 기능 추가나 변경 시 다른 코드에 영향을 최소화합니다.
  • 비동기 처리 가능: 이벤트 리스너를 비동기로 처리하여 성능을 개선할 수 있습니다.
  • 트랜잭션 후처리: 트랜잭션 완료 후 안전하게 이벤트를 처리할 수 있습니다.

2. 스프링 이벤트 시스템: 개념과 실습

스프링 이벤트 시스템은 크게 이벤트(Event), 이벤트 발행자(Publisher), 그리고 이벤트 리스너(Listener)로 나뉩니다. 이 섹션에서는 각 구성 요소를 설명하고, 이를 활용한 간단한 이벤트 시스템을 직접 구현해보겠습니다.

2.1 이벤트 클래스 정의

이벤트 클래스는 단순한 POJO로 만들어도 됩니다.

// CustomEvent.java
public class CustomEvent {
    private String message;

    public CustomEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

2.2 이벤트 발행자

이벤트 발행자는 ApplicationEventPublisher 인터페이스를 통해 이벤트를 발행합니다.

// EventPublisherService.java
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class EventPublisherService {

    private final ApplicationEventPublisher publisher;

    public EventPublisherService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void publish(String message) {
        CustomEvent event = new CustomEvent(message);
        publisher.publishEvent(event);
    }
}

2.3 이벤트 리스너

리스너는 @EventListener 어노테이션을 사용하거나 ApplicationListener 인터페이스를 구현하여 이벤트를 처리합니다.

// CustomEventListener.java
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class CustomEventListener {

    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        System.out.println("이벤트 수신: " + event.getMessage());
    }
}

2.4 컨트롤러

컨트롤러를 작성하여 EventPublisherService를 호출하겠습니다.

// EventController.java

@RestController
@RequiredArgsConstructor
public class EventController {
    private final EventPublisherService publisherService;
    @GetMapping("/publish")
    public String publishEvent(@RequestParam String message) {
        publisherService.publish(message);
        return "이벤트 발행 완료: " + message;
    }
}

2.4 동작확인

포스트맨으로 서버로 요청을 보내 api를 테스트 합니다.

포스트맨 요청

 

스프링 콘솔을 보면 이벤트 리스너에서 출력한 로그를 확인할 수 있습니다.

 

스프링 콘솔

 

3. 트랜잭션 처리 및 비동기 이벤트

스프링 이벤트 시스템은 트랜잭션과 비동기 처리와의 관계에 따라 이벤트 처리 방식이 달라집니다. 이 섹션에서는 트랜잭션 상태에 따른 이벤트 처리 및 비동기 이벤트 처리 방법을 소개합니다.

3.1 @TransactionalEventListener의 phase 속성

@TransactionalEventListener는 트랜잭션의 상태에 따라 이벤트 리스너의 실행 시점을 제어할 수 있습니다.

phase 값설명

phase  값설명 예외  전달 트랜잭션 롤백 여부
BEFORE_COMMIT 트랜잭션 커밋 직전에 이벤트 리스너 실행 예 예 (트랜잭션 롤백됨)
AFTER_COMMIT (기본값) 트랜잭션 커밋 후 이벤트 리스너 실행 아니요 아니요 (트랜잭션에 영향 없음)
AFTER_ROLLBACK 트랜잭션 롤백 후 이벤트 리스너 실행 아니요 아니요 (이미 롤백 완료됨)
AFTER_COMPLETION 트랜잭션이 커밋되었든 롤백되었든 무조건 실행 아니요 아니요 (트랜잭션에 영향 없음)

 

예제:

@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleAfterCommit(CustomEvent event) {
    System.out.println("트랜잭션 커밋 후 실행");
}

3.2 비동기 이벤트 처리와 트랜잭션

  • @Async를 사용하면 이벤트 리스너가 비동기적으로 실행되어 트랜잭션과 완전히 분리됩니다.
  • 이 경우, 리스너에서 발생한 예외는 부모 서비스로 전파되지 않으며, 트랜잭션에도 영향을 주지 않습니다.
@Async
@EventListener
public void handleAsyncEvent(CustomEvent event) {
    System.out.println("비동기 이벤트 처리");
}

참고: 비동기 이벤트 리스너에서 발생한 예외는 기본적으로 무시됩니다. 필요 시 AsyncUncaughtExceptionHandler를 설정하여 예외를 처리할 수 있습니다.


4. 마무리

스프링 이벤트 시스템은 모듈 간 결합도를 낮추고, 비동기 처리와 같은 기능을 손쉽게 구현할 수 있는 강력한 도구입니다.

 

특히, 트랜잭션 전파와 예외 전달 개념이 처음에는 다소 복잡하게 느껴질 수 있지만, 이벤트를 적절히 활용하면 서비스 코드에서 여러 리포지토리를 직접 참조하지 않고도 필요한 작업을 깔끔하게 분리할 수 있습니다.

 

기존에는 하나의 서비스 안에서 여러 리포지토리를 호출하거나 중첩된 로직으로 코드가 복잡해지는 경우가 많았지만, 이벤트 시스템을 활용하면 이러한 복잡성을 줄이고, 책임을 분리하여 더 깔끔하고 유지보수하기 쉬운 코드를 작성할 수 있습니다.

'spring' 카테고리의 다른 글
  • [Spring]REST API 버전 관리 방법
  • [Spring]파일 업로드 하기
  • [Spring] 날짜/시간 처리하기 (@DateTimeFormat vs @JsonFormat)
  • [Spring] DTO 유효성 검사(Validation) : @Valid로 처리하기
당훈이
당훈이
당훈이 님의 블로그 입니다.
  • 당훈이
    당훈IT
    당훈이
  • 전체
    오늘
    어제
    • 분류 전체보기 (40)
      • spring (7)
      • vue.js (8)
      • docker (1)
      • javascript (1)
      • aws (21)
      • database (1)
        • oracle (1)
      • nuxt (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    스프링 배포
    aws dns
    vue3
    nuxt vue
    AWS EC2
    ec2 spring 배포
    nuxt dedupe
    spring boot
    route53
    AWS
    ec2 domain
    aws 스프링
    nuxt fetch
    AWS ELB
    nuxt cache
    nuxt usefetch
    ec2 nodejs
    스프링부트
    aws spring
    Spring
    elb
    ec2 route53
    nodejs 배포
    배포
    aws domain
    EC2
    aws route53
    스프링
    중복요청
    Vue
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
당훈이
[Spring] 이벤트 시스템으로 느슨한 결합 구현하기
상단으로

티스토리툴바