분산 환경에서 결제 이벤트 유실 방지하기 (Outbox, Failover Scheduler, ShedLock)
·
Backend/Spring Boot
이 글은 이벤트 유실 문제 발견 → Outbox 패턴 도입 → 멀티 인스턴스 대응까지, 두 편에 걸쳐 작성했던 일련의 과정을 하나로 정리한 글입니다. 자세한 내용은 아래 링크를 참고해 주시기 바랍니다. 이벤트 유실을 방지하기 위한 Outbox Pattern 도입기Introduce풋살 매칭 서비스에서 매치 신청 시 결제 완료 후 구장주에게 SMS를 보내고, 매치 신청자에게 FCM 알림을 전송하는 등 부가 로직을 실행해야 한다. 초기에는 이 모든 처리를 결제 요청과 동woojjam.tistory.com ShedLock으로 분산 환경에서 스케줄러 중복 실행을 방지하자Introduce 이벤트 유실을 방지하기 위한 Outbox Pattern 도입기Introduce풋살 매칭 서비스에서 매치 신청 시 결제 완료 후..
ShedLock으로 분산 환경에서 스케줄러 중복 실행을 방지하자
·
Backend/Spring Boot
Introduce 이벤트 유실을 방지하기 위한 Outbox Pattern 도입기Introduce풋살 매칭 서비스에서 매치 신청 시 결제 완료 후 구장주에게 SMS를 보내고, 매치 신청자에게 FCM 알림을 전송하는 등 부가 로직을 실행해야 한다. 초기에는 이 모든 처리를 결제 요청과 동woojjam.tistory.com이전 게시글에서는 이벤트 유실을 방지하기 위해 Outbox 패턴을 도입하고, fallback 경로로 스케줄링에서 polling하도록 하였다. 멀티 인스턴스로 배포된 서비스에서 스케줄러를 돌려본 적 있는가? 처음에는 별 문제가 없어 보인다. 하지만 어느 날 고객 CS가 들어온다.결제 완료 알림이 두 번 왔어요. 로그를 뒤져보면 여러개의 인스턴스가 동시에 같은 Outbox 레코드를 조회해서 각자..
이벤트 유실을 방지하기 위한 Outbox Pattern 도입기
·
Backend/Spring Boot
Introduce풋살 매칭 서비스에서 매치 신청 시 결제 완료 후 구장주에게 SMS를 보내고, 매치 신청자에게 FCM 알림을 전송하는 등 부가 로직을 실행해야 한다. 초기에는 이 모든 처리를 결제 요청과 동기적으로 처리하였다. 하지만 이는 부가 로직에서 오류가 발생할 경우 트랜잭션이 rollback 되어 결제 데이터가 유실될 수 있는 가능성이 있었으며 알림 발송에 의해 스레드가 블로킹되어 응답 시간이 지연되기도 하였다. 따라서 Spring Event를 사용하여 `ApplicationEventPublisher + @Async + EventListener` 구조로 변경하였다. 스프링 이벤트를 발행하여 트랜잭션과 관심사 분리하기Introduce 내가 개발중인 서비스에서 특정 상품을 결제하면 FCM을 통해 결제..
카카오톡 Bizmessage로 알림톡 전송하기
·
Backend/Spring Boot
1. 들어가기 전스케줄링으로 정해진 시간에 강수량을 조회하고, 이에 따라 사용자들에게 강수 알림톡을 전송하는 기능을 개발하고자 한다. 정해진 시간에 강수량을 조회하는 기능은 이미 완성되었기에 특정 사용자에게 알림톡을 전송하는 기능만 구현하면 된다.2. 친구톡과 알림톡처음에 강수량에 따라 사용자들에게 메세지를 전송한다 라는 요구사항을 받았을 때 단순히 카카오톡 비즈 채널로 메세지를 전송하면 되겠다고 생각하였다. 어찌보면 반은 맞고, 반은 틀렸다고 할 수 있다. 왜냐하면 카카오톡으로 메세지를 전송하는 방식에는 2가지가 있는데 그게 친구톡 과 알림톡 이다.2.1 친구톡카카오 비즈니스 채널에는 친구톡과 알림톡이 있다. 먼저 친구톡은 수신자 입장에서는 일반적인 모먼트를 통해 발송된 메시지이다. 하지만 발송 과정에..
분산 환경에서 MySQL Named Lock으로 분산 락을 구축하고, 동시성 제어하기
·
Backend/Spring Boot
Introduce본 게시글에서 다루는 모든 테스트 코드는 아래 레포지토리에서 확인하실 수 있습니다. GitHub - WooJJam/concurrency-deep-diveContribute to WooJJam/concurrency-deep-dive development by creating an account on GitHub.github.com 풋살 매치 참가 신청 서비스를 개발하던 중, 매치의 정원을 초과하여 참가자가 등록되는 문제를 발견하였다. 실제로 매치에는 최대 12명이 참여 가능했으나, 동시 요청이 몰릴 경우 12명을 훌쩍 넘는 인원이 등록 되었다. 단일 사용자의 신청 흐름은 단순하다.풋살 매치 조회현재 참가자 수 확인참여 가능하다면 참가자 InsertPG사 결제 API 호출결제 성공 및 실패..
Virtual Thread로 병렬 호출하여 Thread Pool 고갈 문제 해결하기
·
Backend/Spring Boot
Introduce서비스를 개발하다 보면 단일 API 내에서 외부 API 호출이 N번 반복되는 구조가 자연스럽게 생겨난다. 최근에 장소 하나에 사진 10장을 보여주자 라는 요구사항이 있었다. 처음에는 단순히 순차 조회로 구현을 하였고, 로컬에서 테스트해 보니 1초 남짓한 응답 시간이 걸렸고 "좀 느리긴 하지만 괜찮겠지" 싶었다. GitHub - YAPP-Github/NDGL-BEContribute to YAPP-Github/NDGL-BE development by creating an account on GitHub.github.com 나도갈래 프로젝트는 유튜버의 여행 영상 속 동선을 AI가 분석하여 장소 정보를 제공하는 서비스이다. 장소 상세 정보를 조회해야 하는데 이때 Google Maps Places..
RestClient는 어떻게 생성이 될까?
·
Backend/Spring Boot
IntroduceSpring Boot 애플리케이션에서 외부 API를 호출하는 일은 매우 흔하다. Spring 6부터는 기존 RestTemplate을 대체하는 새로운 HTTP 클라이언트로 RestClient가 도입되었고, 비교적 단순한 API 덕분에 빠르게 적용할 수 있다는 장점이 있다. 하지만 실제로 RestClient를 사용하다 보면 이런 궁금증이 생긴다.RestClient.create()는 내부적으로 무엇을 만들까?builder() 방식과는 어떤 차이가 있을까?매번 새로 만들어도 괜찮을까, 아니면 재사용해야 할까? 이 글에서는 RestClient가 생성되는 방식과 내부 동작 구조를 직접 뜯어보며 살펴본다. RestClient ConstructionRestClient는 두 가지 API로 생성할 수 있다..
Java의 final은 불변성을 보장하지 않는다.
·
Backend/Java
Java의 final은 무엇인가?`final`은 '최종적' 이라는 의미를 가지는데, 자바에서 이는 초기값이 한번 정해지면 프로그램 실행 도중에 수정할 수 없음을 의미한다. 또한 이 `final` 은 변수, 메소드, 클래스에 붙을 수 있는데 각각 의미하는바가 달라진다.1. 클래스`final`이 붙은 클래스는 상속할 수 없다.public final class Car { } // final 클래스는 상속할 수 없다!public class Bus extends Car { }2. 메소드`final` 이 붙은 메소드는 오버라이딩 할 수 없다.public class Animal { public final void bark() { System.out.println("짖다."); }}public ..
자바와 스프링의 비동기 처리 - 2편: CompletableFuture의 예외 처리와 타임 아웃
·
Backend/Spring Boot
Introduce 자바와 스프링의 비동기 처리 - 1편: CompletableFuture 톺아보기Introduce 스프링에서 비동기 처리를 위해 흔히 `@Async` 를 사용하곤 한다.나 역시 프로젝트에서 `@Async` 를 적용하여 일부 후속 로직들을 메인 로직과 분리하여 실행하고 있었다. 그런데 얼마 전 면woojjam.tistory.com먼저 해당 글을 읽기 전에 이전 글을 읽고 오는것을 추천한다. `CompletableFuture` 는 비동기 작업을 간결하게 표현할 수 있게 도와주지만, 비동기 환경에서는 예외가 어디서, 어떻게 발생했는지 추적하거나 디버깅 하기도 어렵기에 이를 처리하는것이 쉽지 않다. 그렇다고 예외처리를 해놓지 않으면 다음과 같은 상황이 발생할 수 있다.비동기 로직 중 하나가 실패..
자바와 스프링의 비동기 처리 - 1편: CompletableFuture 톺아보기
·
Backend/Spring Boot
Introduce 스프링에서 비동기 처리를 위해 흔히 `@Async` 를 사용하곤 한다.나 역시 프로젝트에서 `@Async` 를 적용하여 일부 후속 로직들을 메인 로직과 분리하여 실행하고 있었다. 그런데 얼마 전 면접장에서 다음과 같은 질문을 받았다.@Async 로 실행된 메서드에서 예외가 발생하면 어떻게 처리하나요?혹시 CompletableFuture 를 사용해본 경험이 있으신가요? 나는 이 질문에 대해 명확하게 답변하지 못하였다. 제대로 답변하지 못하는 모습을 보니 스스로 내가 비동기에 대해서 잘 모르고 사용하는 구나라는 생각이 들었고, `CompletableFuture` 이 무엇인지 이해해보는 시간을 가지게 되었다. 이번 글에서는 java의 기본 비동기 처리 도구인 Future 를 알아보고, 이를 ..