[SSE-2] 실시간 알림 서비스 구현해보기 feat. AOP
실시간 알림 서비스를 구현하기 위하여 세팅된 프로젝트 환경은 이와 같다.
- Spring Boot
- Spring Security
- Java21
- Mybatis
SSE 구현
디렉토리 구성
우선 현재 프로젝트 환경에 맞춰서 디렉토리를 구성했다.
sse-project/
├── java/
│ ├── config/
│ │ └── SecurityConfig.java # 스프링 시큐리티 설정 파일
│ ├── sse/
│ │ ├── SseController.java # 컨트롤러
│ │ ├── SseService.java # 서비스
│ │ └── SseMapper.java # 매퍼
│ ├── noti/
│ │ ├── Notification.java # 알림 DTO
│ │ ├── NotifyChange.java # 커스텀 어노테이션
│ │ └── NotifyChangeAspect.java # AOP
│ │
│ resources/
│ ├── mapper/
│ │ └── SseMapper.xml # 매퍼
│ ├── thymeleaf/
│ │ └── sse.html # 알림 수신 페이지
│ ├── js/
│ │ └── sse.js # 연결 스크립트
└ pom.xml # Maven 관리
실제 프로젝트와 동일한 구성은 아니고 대략적으로 필요한 파일만 간추렸다.
1. 백앤드
security 설정
기본적으로 스프링 시큐리티는 애플리케이션으로 들어오는 모든 종류의 요청을 감지하기 때문에,
sse api를 만들기 위해서는 해당 요청에 대한 권한 처리를 하는 설정이 필요했다.
- 테스트를 위해 기본적인 설정들은 비활성화하고, api 요청은 허용하도록 설정했다.
컨트롤러
클라이언트는 로그인 시 /api/sse/stream 으로 요청하여 연결을 해놔야한다.
서비스
멀티스레드 환경을 고려해서 정상적으로 서비스 될 수 있도록 구성했다.
DTO
커스텀 어노테이션
커스텀 어노테이션. 메소드에서 어노테이션으로 사용할 수 있다.
AOP
AOP 클래스. 어노테이션을 기반으로 한 AOP 구현체이다.
- AOP 클래스 용어 설명
| 용어 | 설명 | 코드 내 역할 |
|---|---|---|
| @Aspect | 클래스가 공통 관심사(알림 전송)를 처리하는 클래스임을 선언한다. | ‘알림 전송 로직은 여기서 처리한다’는 선언을 담당한다. |
| @AfterReturning | Advice(시점)의 한 종류로, 타겟 메서드가 성공적으로 실행을 마친 후에만 동작한다. | 메서드가 에러 없이 끝났을 때만 알림을 보내도록 한다. |
| pointcut | 어디에 이 로직을 적용할지 정하는 규칙이다. | @NotifyChange가 붙은 메서드들만 골라내는 기준이 된다. |
| returning | 타겟 메서드가 반환한 결과값을 어떤 파라미터로 받을지 지정한다. | 메서드 실행 결과(result)를 가져와서 알림 내용에 활용한다. |
| JoinPoint | 실행 시점의 모든 정보(메서드 이름, 파라미터 등)를 담고 있는 객체이다. | 실행된 메서드가 무엇인지, 어떤 파라미터가 들어왔는지 확인한다. |
| MethodSignature | JoinPoint에서 추출한 정보로, 메서드의 신분증 같은 정보이다. | 실행된 메서드에서 @NotifyChange 애노테이션 정보를 읽기 위해 사용한다. |
알림 전송이 발생해야하는 메소드에 @NotifyChange 어노테이션을 붙인 뒤 해당 메소드가 성공적으로 실행을 마치면,
알림을 보내는 메소드인 afterNotifyChange가 실행된다.
매퍼
프로세스를 삽입하는 쿼리에 커스텀 어노테이션인 NotifyChange를 사용하면 해당 쿼리를 사용하는 모든 서비스에서 자동으로 알림이 전송된다.
프론트 화면
결과 화면
정보 확인 후 정상적으로 알림을 수신했다.
실제 사용 시나리오
1. 사용자 정보를 조회한다.
↓
2. SSE 연결을 한다.
↓
3. 테스트 알림을 전송한다.
↓
4. 정상적으로 수신 로그에 쌓이는지 확인한다.
내용 정리
단순히 “알림 기능을 만들자”로 시작했던 작업이었지만, SSE와 AOP를 조합해보면서 구조적인 고민을 꽤 많이 했던 과정이었다.
결과적으로 비즈니스 로직은 건드리지 않고 @NotifyChange 어노테이션 하나로 알림 처리를 분리해냈고, 코드의 응집도를 높일 수 있었다.
결국 좋은 설계란 나중에 손대기 편한 코드를 만드는 과정 아닐까 싶다. AOP는 개념만 보면 추상적이지만, 이번처럼 ‘특정 테이블의 변화를 감지해서 알림을 쏜다’는 명확한 트리거가 있을 때 그 효율이 극대화되는 것 같다.
물론 실무에서는 여러 운영 이슈가 생길 수도 있지만, 일단 목적인 SSE 구현과 AOP를 이용한다는 점은 충분히 달성했다고 생각한다.
시간은 오래 걸리지만 역시 눈으로만 보는 것보다 삽질하며 코드를 짜보는 게 이해하기에는 좋기는 하다.
다만 이 AI 세상에서 효율이 좋은지는…














