본문 바로가기

Java

(22)
입찰은 저장됐는데 kafka 이벤트는 사라졌다: 경매 서비스에 Outbox를 적용한 이유 들어가며개인 프로젝트 bowchat은 처음부터 MSA로 시작한 프로젝트가 아니었다. 처음에는 모놀리식으로 시작했고, 이후에 MSA로 전환했다.단순히 서비스를 잘게 쪼개는 것 자체가 목적은 아니었다. Kafka를 제대로 써보고 싶었고, 서비스 간 데이터 정합성 문제를 직접 겪어보면서 풀어보고 싶었다. 그 과정에서 가장 먼저 부딪힌 문제 중 하나가 경매 서비스의 입찰 흐름이었다.실시간 경매에서 입찰은 단순히 숫자 하나를 바꾸는 요청이 아니다.현재 최고 입찰가가 바뀌고입찰 이력이 남아야 하고다른 참여자 화면에도 거의 실시간으로 반영되어야 한다처음 구현은 단순했다. 입찰이 성공하면 같은 흐름 안에서 바로 Kafka로 이벤트를 발행했다.입찰 처리 흐름은 AuctionBidService와 AuctionControl..
내부 서비스 인증을 X-Service-Token에서 OAuth 2.0으로 바꾼 이유 서비스를 분리하면서 내부 인증은 한동안 X-Service-Token 방식으로 두고 있었다.Feign 요청마다 헤더 하나 붙이면 됐고, 단순했다.그런데 auction-service → product-service, auction-service → user-service, chat-service → auction-service 같은 호출이 늘어나면서 이 방식을 계속 가져가는 게 맞나 싶었다. 전체 코드는 여기서 볼 수 있다 GitHub - mangtaeeee/bowchat-auctionContribute to mangtaeeee/bowchat-auction development by creating an account on GitHub.github.com기존 구조외부 사용자 요청은 비교적 명확했다.user..
Kafka를 제대로 쓰고 싶어서 MSA로 전환한 이야기 개인 프로젝트 bowchat을 모놀리식으로 시작해서 MSA로 전환했다.단순히 서비스를 쪼개는 게 목적이 아니었다.Kafka를 제대로 써보고 싶었고, 서비스 간 데이터 정합성 문제를 직접 해결해보고 싶었다.이번 글에서는왜 전환했는지전환하면서 어떤 문제를 만났는지각 문제를 어떻게 해결했는지를 중심으로 정리해보려 한다.왜 전환했는가처음엔 모놀리식에 Kafka를 붙였다.입찰 이벤트를 Kafka로 발행하고 Consumer가 처리하는 구조였는데, 실제로 써보니 이상했다.[모놀리식 + Kafka]같은 서비스 안에서Producer → Kafka → Consumer결국 DB도 같고, 코드도 같고→ 그냥 메서드 호출이랑 다를 게 없는 구조Kafka의 핵심은 "이벤트를 발행하면 구독한 서비스들이 각자 독립적으로 반응"하는 ..
Kafka Consumer Lag 600을 0으로 만든 방법 (k6 + Prometheus +Grafana) 개요 Kafka 기반 실시간 경매 시스템 부하 테스트 및 병목 개선 (k6 + Prometheus + Grafana)개인 프로젝트로 Kafka를 기반으로 한 실시간 경매·채팅 플랫폼을 직접 설계하고 배포했다.이 서비스는 WebSocket을 통해 실시간으로 입찰 및 채팅 이벤트를 Kafka로 전송하고,여러 Consumer들이 메시kimmangtae.tistory.com Kafka 기반 경매 입찰 시스템에 대해 트래픽 테스트를 진행하고, Consumer Lag가 급격히 증가하는 현상을 관찰했다.이번 글에서는왜 Lag가 발생했는지어떤 설정이 병목을 만들었는지실제 배포 환경(CPU 2 코어)에서 어디까지 늘리는 게 합리적인지그리고 개선 후 지표가 어떻게 달라졌는지를 중심으로 정리해보려 한다.테스트 환경 요약배포..
Thread.sleep()은 왜 위험할까? Kafka DLQ로 안전하게 재시도 처리하기 문제의 시작: 왜 Thread.sleep()을 넣었을까?실시간 경매 시스템을 개발하면서 동시성 문제에 직면했다. 여러 사용자가 동시에 같은 경매에 입찰할 때, 낙관적 락(@Version)을 사용해 데이터 정합성을 보장하고 있었다.@Entitypublic class Auction { @Version private Long version; // 낙관적 락 private Long currentPrice; // ...}하지만 문제가 있었다. 동시 입찰이 발생하면 OptimisticLockingFailureException이 발생하고, 입찰이 실패했다. 사용자 입장에서는 "입찰 실패" 메시지만 보게 되는 것이다.첫 번째 시도: 수동 재시도 로직"그럼 재시도하면 되지 않을까?" 라는 생각으로..
로컬에서 Spring Boot 서버 2개 띄워 HAProxy로 트래픽 분산해보기 개요회사 면접을 준비하다가 HAProxy 라는 키워드를 접하게 되었다. 단순히 개념으로만 이해하기보다는, 직접 동작을 확인해보고 싶어서로컬 환경에서 간단한 Spring Boot 서버 두 개를 띄우고 HAProxy를 이용해 트래픽을 분산시키는 실습을 진행했다.이를 통해 “리버스 프록시”가 실제로 어떤 역할을 하는지 직접 체감해볼 수 있었다.리버스 프록시란리버스 프록시란 클라이언트와 웹 서버 간의 중개자 역할을 하는 서버로,클라이언트로부터의 요청을 대신 받아 웹 서버에 전달하고, 웹 서버의 응답을 클라이언트에게 전달하는 역할을 한다.이를 통해 리버스 프록시는 웹 서버의 부하를 분산시키고, 보안을 강화하는 등 다양한 기능을 수행할 수 있다.기본 작동 원리는 클라이언트가 리버스 프록시에 요청을 보내면, 리버스 ..
Kafka 기반 실시간 경매 시스템 부하 테스트 및 병목 개선 (k6 + Prometheus + Grafana) 개인 프로젝트로 Kafka를 기반으로 한 실시간 경매·채팅 플랫폼을 직접 설계하고 배포했다.이 서비스는 WebSocket을 통해 실시간으로 입찰 및 채팅 이벤트를 Kafka로 전송하고,여러 Consumer들이 메시지를 처리하여 PostgreSQL과 Redis에 결과를 반영하는 구조로 되어 있다.실제 서비스 운영 환경을 가정해 1000명의 사용자가 동시에 입찰 요청을 보냈을 때의 시스템 부하를 검증해 보고자,EC2 상에 배포된 서버에 대해 k6를 이용한 부하 테스트를 진행했다.테스트는 실시간 입찰 마감 상황(수 초 내 동시 요청)을 재현하여 Kafka, Redis, PostgreSQL의 처리 성능을 관찰하는 것이 목적이었다. 테스트 환경 및 시나리오 구성실험 환경은 AWS EC2(c7i-flex.large..
Copilot + JUnit 테스트 코드 자동 생성 환경 구성 솔직히 테스트 코드 작성이 귀찮았다.같은 패턴을 계속 반복하다 보니, ‘어차피 코파일럿 자주 쓰는데, 주석만 통일해서 비슷한 코드가 자동으로 나오게 하면 되지 않을까??’ 싶었다. 그래서 Copilot을 커스터마이징해서 주석만 치면 테스트가 만들어지는 구조를 만들어보았다. 세팅1. Copilot 플러그인 설치IntelliJ → Preferences → Plugins → “GitHub Copilot” → 설치로그인하고 “Enable for all languages” 체크.단축키는 Alt + \ (Mac은 Option + ) 2. Copilot 규칙 파일 추가프로젝트 루트에 copilot-instructions.md 파일을 하나 만든다.이 파일이 Copilot한테 “테스트는 이렇게 만들어라”를 알려주는 가이..