Weekly I Learned: 이벤트 드리븐 아키텍처로의 전환과 나의 예약 조회 API 구현
이번 주에는 기존의 모놀리식 아키텍처를 이벤트 드리븐 아키텍처로 전환하는 과정에서 많은 것을 배웠습니다. 특히 나의 예약 조회 API를 구현하면서 여러 가지 도전과 해결책을 경험했습니다.
1. 한 주 동안 학습한 것들 나열
- 이벤트 드리븐 아키텍처의 기본 개념과 구현 방법
- Kafka를 이용한 마이크로서비스 간 비동기 통신
- Redis를 활용한 캐싱 전략
- Server-Sent Events (SSE)를 이용한 실시간 데이터 전송
- 분산 시스템에서의 데이터 일관성 유지 방법
2. 학습한 내용의 구현 필요성
기존의 모놀리식 아키텍처에서는 단일 데이터베이스를 사용하여 예약 정보를 쉽게 조회할 수 있었습니다. 하지만 마이크로서비스 아키텍처로 전환하면서 예약, 콘서트, 결제 정보가 각각 다른 서비스에 분산되어 있어 이를 효율적으로 조회하고 조합하는 새로운 방법이 필요했습니다.
또한, 실시간성과 성능 개선을 위해 캐싱 전략과 비동기 통신 방식을 도입해야 했습니다. 이는 시스템의 확장성과 응답성을 높이는 데 중요한 요소였습니다.
3. 구현 외 다른 기술적 대안
GraphQL을 이용한 데이터 조회: 여러 서비스의 데이터를 한 번에 조회할 수 있는 GraphQL을 사용할 수 있었습니다. 하지만 기존 REST API 구조를 유지하면서 점진적으로 시스템을 개선하고자 했기 때문에 이 옵션은 선택하지 않았습니다.
폴링 방식의 데이터 갱신: SSE 대신 클라이언트에서 주기적으로 서버에 데이터를 요청하는 폴링 방식을 사용할 수 있었습니다. 그러나 실시간성과 서버 부하 측면에서 SSE가 더 효율적이라고 판단했습니다.
동기식 API 호출: 마이크로서비스 간 동기식 API 호출을 사용할 수 있었지만, 시스템의 결합도를 낮추고 장애 전파를 방지하기 위해 비동기 이벤트 기반 통신을 선택했습니다.
4. 세부 구현에 대한 코드 소개
나의 예약 조회 API 구현의 핵심 부분을 소개하겠습니다.
@GetMapping(value = "/me", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter getMyReservations(@RequestHeader("User-Id") Long userId) {
return service.getMyReservations(userId);
}
이 컨트롤러 메서드에서는 SSE를 사용하여 실시간으로 예약 정보를 클라이언트에 전송합니다.
서비스 레이어에서의 주요 로직은 다음과 같습니다:
public SseEmitter getMyReservations(Long userId) {
SseEmitter emitter = sseService.createEmitter(userId);
CompletableFuture.runAsync(() -> processReservations(userId, emitter));
return emitter;
}
private void processReservations(Long userId, SseEmitter emitter) {
List<Long> reservationIds = reservationRedisRepository.getUserReservationIds(userId);
if (reservationIds.isEmpty()) {
log.warn("No reservations found for user: {}", userId);
}
List<ReservationResponseDTO> reservations = reservationIds.stream()
.map(reservationId -> processReservation(userId, reservationId, emitter))
.collect(Collectors.toList());
sseService.sendEvent(emitter, "TOTAL_COUNT", reservations.size());
sseService.sendEvent(emitter, "RESERVATIONS", reservations);
sseService.sendEvent(emitter, "COMPLETE", "All reservations processed");
}
이 구현에서는 Redis를 사용하여 사용자의 예약 ID 목록을 캐싱하고, 각 예약 정보를 비동기적으로 처리합니다. 처리된 정보는 SSE를 통해 실시간으로 클라이언트에 전송됩니다.
콘서트 정보와 결제 정보는 Kafka를 통해 다른 서비스에 요청하고 응답을 받습니다:
private void requestConcertInfo(Reservation reservation, SseEmitter emitter) {
ConcertInfoRequestEvent event = new ConcertInfoRequestEvent(
reservation.getReservationId(),
reservation.getConcertId(),
reservation.getConcertDateId(),
reservation.getSeatId()
);
kafkaTemplate.send("concert-info-requests", event);
}
@KafkaListener(topics = "concert-info-responses")
public void handleConcertInfoResponse(ConcertInfoResponseEvent event) {
try {
updateReservationInfo(event.reservationId(), "concert_info", event.concertInfo());
} catch (Exception e) {
log.error("Error processing concert info response", e);
}
}
이러한 방식으로 각 서비스의 데이터를 조합하여 완전한 예약 정보를 구성하고 사용자에게 제공했습니다.
이번 주의 학습과 구현 경험을 통해 이벤트 드리븐 아키텍처의 장점과 도전 과제를 깊이 이해할 수 있었습니다. 특히 비동기 통신, 캐싱, 실시간 데이터 전송 등의 기술을 실제 프로젝트에 적용해 봄으로써 시스템의 확장성과 성능을 크게 개선할 수 있었습니다.
'스케쥴 > 스터디' 카테고리의 다른 글
[MSA] SAGA 패턴은 무엇? (0) | 2024.08.02 |
---|---|
[항해99 취업리부트 WIL] 7주차 (0) | 2024.07.09 |
[항해99 취업리부트 WIL] 6주차 (0) | 2024.07.02 |
[항해99 취업리부트 WIL] 5주차 (0) | 2024.06.24 |
[항해99 취업리부트 TIL] 4주차 4일 (1) | 2024.06.17 |