본문 바로가기

전체 글

(27)
배치에서의 멱등성과 데이터 정합성 배치 재시작은 왜 충분하지 않을까?과제로 배치 시스템을 진행하다 보니 배치가 중간에 실패하고 다시 실행되면, 이미 처리된 데이터는 어떻게 되는 것인지 의문이 생겼다. 이런 상황에서 가장 먼저 떠오른 방식은 Spring Batch의 '재시작(Restart)' 기능이었다. 특히 Chunk 기반 배치를 사용할 경우, Spring Batch는 실행 메타데이터를 관리하기 위해 BATCH_JOB_EXECUTION, BATCH_STEP_EXECUTION같은 메타 테이블을 자동으로 생성하고, 마지막으로 커밋된 Chunk 지점을 기록해 준다. 덕분에 배치는 실패하더라도 해당 지점부터 재시작할 수 있다. 하지만 이건 재시작 가능성(Recoverability) 과 멱등성(Idempotency) 을 동일하게 보는 오해에 가깝..
콜드 스타트는 어떻게 다뤄야 할까 화면이 비어버리는 순간들 — 랭킹 시스템의 콜드 스타트이번에 랭킹 시스템을 구현하면서 콜드 스타트(Cold Start) 문제를 마주하게 되었다.콜드 스타트는 단순히 “데이터가 없다”는 상태를 넘어, 사용자에게 무엇을 보여줄 것인가에 대한 선택의 문제였다.콜드 스타트는 생각보다 다양한 상황에서 발생한다.가장 대표적인 예는 일간 랭킹이다.날짜 기준으로 랭킹 키를 분리하는 구조에서는, 00시 00분이 되는 순간 새로운 기준(Key)이 생성된다.하지만 그 직후에는 아직 클릭, 구매 같은 이벤트가 하나도 발생하지 않았기 때문에 랭킹 리스트가 통째로 비어버린다.비슷한 상황은 다른 곳에서도 쉽게 발생한다.신규 상품 카테고리를 개설했을 때새로운 서비스를 처음 오픈했을 때개인화 추천을 제공하려 하지만, 이제 막 가입한 ..
트랜잭션 경계를 넘어 정합성 지키기 서론이벤트 기반 아키텍처를 도입하면 자연스럽게 트랜잭션의 경계에 대한 고민이 따라온다. 특히 외부 이벤트(메시지 브로커, 다른 서비스 호출 등)를 사용하게 되면, 하나의 트랜잭션 안에서 모든 정합성을 보장하던 기존 방식과는 전혀 다른 문제를 마주하게 된다.물론 이런 고민이 외부 이벤트에서만 발생하는 것은 아니다. 내부 이벤트(ApplicationEvent) 를 사용하더라도 동기/비동기 방식에 따라 트랜잭션 전파 양상이 달라지고, 그에 따라 정합성 보장 전략 역시 달라진다.이 글에서는 내부 이벤트와 외부 이벤트를 기준으로 트랜잭션 전파가 어떻게 달라지는지 살펴보고, 트랜잭션 경계를 넘어선 상황에서 정합성을 지키기 위한 방법들에 대해서 학습한 내용을 정리해 보았다. 내부 이벤트와 트랜잭션 전파1. 동기 이..
ApplicationEvent 왜 사용할까? ApplicationEvent 사용해야 하는 이유는?회사에서 실시간으로 데이터가 갱신되는 기능을 구현해야 해서 ApplicationEvent를 사용해 본 적이 있다.하지만 처음 경험했을 때는 솔직히 이점보다 부담이 더 크게 느껴졌다. 우선적으로 강결합으로 묶인 부분과 약결합으로 묶어야 할 부분을 선별해야 하고 그에 따라 로직을 분리해야 하기 때문에 구현 복잡도 올라가고 이벤트가 실패했을 때 어떻게 되돌릴지 고민해야 한다. 그렇다 보니 API로 순차적으로 호출하면 깔끔하게 끝날 일을, 굳이 이벤트로 쪼개서 처리해야 하나? 의문이 들게 되었다. 게다가 실제로 “API 호출” 방식과 비교하면 API가 더 매력적으로 보이기도 했다.API 방식의 장점 (동기적 방식)API 호출이나 서비스 레이어 간의 직접적인 메..
PG 연동 안정성 강화하기 - Failure-Ready Systems에 대해서 Failure-Ready Systems가 필요한 이유최근 애플리케이션 아키텍처는 과거의 단일 서버(모놀리식) 구조에서 벗어나, 여러 서비스가 독립적으로 동작하면서도 서로 연동하는 마이크로서비스 아키텍처(MSA)로 빠르게 이동하고 있다. 결제(PG), 인증, 알림, 검색 등 핵심 기능을 외부 전문 서비스나 별도 마이크로서비스에 맡기는 것은 개발 속도 향상, 독립적인 배포, 확장성 확보를 위한 필수 전략이다.그러나 서비스 경계가 분리되면서 문제가 생긴다. 내 서비스 내부의 로직이 아무리 빠르게 동작해도, 최종 응답 시간을 결정짓는 것은 종종 내가 직접 통제할 수 없는 외부 호출의 지연이다.즉, 현대 서비스에서는 내 시스템의 장애 원인이 외부에 존재할 가능성이 아주 높아졌다.그리고 외부 호출의 지연은 단순히 ..
Spring Boot + Redis: Look-aside 캐싱 구현과 Page 객체 직렬화 해결법 들어가며회사에서는 Redis를 사용해 보긴 했지만, 그때는 주로 세션 저장소로만 활용했었다. 유저가 로그인하면 정보를 Redis에 잠깐 올려두고 TTL을 설정해 두는 방식이었다. 또한 레포트 서버와 엔진 서버가 분리되어 있어, 환자 정보나 워크리스트 정보를 공유해야 할 때 Redis를 사용했었다. 그러다 보니 나에게 Redis는 “세션 + 임시 데이터 저장소” 정도였다. Redis로 다양한 캐싱 전략을 설계할 수 있다는 생각은 사실 해본 적이 없었다.하지만 이번 과제를 진행하며 상품 조회, 상품 상세 조회의 성능을 개선하기 위해 Redis를 캐시로 활용해 보게 되었고, 그 과정에서 캐시가 왜 필수적인지 깨닫게 되었다. 특히 멘토링 시간에 파레토 법칙처럼 소수의 인기 상품에 전체 트래픽이 집중되는 현상이 ..
WIL- 4주차 이번 주에 새로 배운 것1) 동시성 이슈의 본질은 기술 문제가 아니라 설계 문제단순히 동시성 이슈라고 하면 “락 필요하겠네?” 정도로 생각했는데, 실제로는 조회(Read) 시점과 저장(Write) 시점이 달라 생기는 흐름의 문제라는 걸 확실히 이해했다.읽기–쓰기–수정(read–write–update) 패턴같은 다양한 변형 패턴에서도 동일하게 발생할 수 있다는 점도 새롭게 알게 됐다.2) 정합성(Consistency)에 대한 명확한 정의앞으로는 정합성을 아래처럼 이해할 수 있게 되었다:정합성이란 “내가 예상한 흐름대로 데이터가 존재하는가” 를 보장하는 문제다.정합성이 깨지는 대표 사례:레이스 컨디션으로 값이 덮어씌워질 때(갱신 손실)동시에 SET → READ가 실행돼 예상치 못한 값이 나올 때업데이트가 성..
Facade 패턴에서 트랜잭션 범위 설정과 Optimistic Lock 재시도 처리 문제 상황최근 좋아요 취소 기능(unLike)을 구현하면서 동시성 이슈가 예상되었다.특정 상품에 좋아요를 누른 유저들이 동시에 취소 요청을 보내더라도, 좋아요 개수는 정확하게 줄어들어야 한다.이를 해결하고자 JPA @Version을 활용한 낙관적 락(Optimistic Lock)을 적용했고, 다음과 같은 시나리오로 테스트를 진행했다.초기 좋아요 수: 10개동시에 취소 요청을 보내는 스레드: 10개기대 결과: 0개 @Test @DisplayName("이미 좋아요를 누른 10명이 동시에 취소를 요청하면, 좋아요 개수는 0개가 되어야 한다.") void unlike_concurrency_test() { // arrange int threadCount = 10; Product initial..