springboot 9

[Troubleshooting] Vercel + Oracle Cloud: 배포 환경에서 사라진 JWT 쿠키를 찾아서 (SameSite & Nginx)

🚨 도입: 로컬에선 잘 되던 로그인이 배포만 하면 풀린다?개인 프로젝트인 '가상 자산 거래 플랫폼(Virtual Exchange)'의 인프라를 확장하면서, 기존 GCP(1GB RAM)의 메모리 한계를 극복하고자 Oracle Cloud A1.Flex(24GB RAM) 환경으로 백엔드를 마이그레이션했습니다.성공적으로 인프라 이전을 마치고 프론트엔드(Vercel)와 연동 테스트를 진행하던 중, 기묘한 버그를 마주했습니다."로컬 환경에서는 Access Token이 만료되면 완벽하게 /reissue 로직을 타며 연장되는데, 배포 환경에서는 무조건 재발급이 실패하고 강제 로그아웃이 되어버린다."프론트엔드와 백엔드의 코드는 단 한 줄도 바뀌지 않았습니다. 그렇다면 문제는 '환경'에 있었습니다.🕵️‍♂️ 원인 분석..

Part 3. MongoDB 로그 시스템 구축 (Polyglot Persistence)

1. 도입 배경핵심 비즈니스 데이터(주문, 체결)와 비정형 데이터(에러 로그, 사용자 활동)의 성격이 완전히 다릅니다.MySQL: 트랜잭션이 중요한 비즈니스 데이터MongoDB: 스키마가 유동적인 로그 데이터모든 로그를 MySQL에 넣으면 불필요한 부하가 생깁니다. 데이터 특성에 맞게 저장소를 분리하는 Polyglot Persistence(폴리글랏 퍼시스턴스) 아키텍처를 도입했습니다.2. 컬렉션 구조📄 error_logs - ErrorLogJava@Document(collection = "error_logs")public class ErrorLog { @Id private String id; @Indexed(expireAfter = "30d") private LocalDa..

Part 2. Slack 알림 연동

1. 도입 배경이상 거래 탐지 로직이 발동할 때 기존에는 이메일로만 알림을 보냈는데, 실제 운영팀이 즉각적으로 대응할 수 있도록 Slack Webhook 실시간 알림을 추가했습니다.2. 설계알림의 성격에 따라 메시지 포맷과 역할을 명확히 분리하기 위해 트리 구조로 서비스를 설계했습니다.PlaintextSlackNotificationService├── sendAbnormalTradeBlocked(userId, reason) → 🚨 이상 거래 차단├── sendAbnormalTradeWarning(email, reason) → ⚠️ 이상 거래 감지└── sendServerError(exceptionType, message) → 💥 서버 오류 발생 [호출 위치 및 트리거 조건]호출 위치메서드트..

이상 거래 탐지 트러블슈팅 - StackOverflowError부터 @Transactional 롤백까지

구현은 무사히 마쳤지만, 실제 테스트를 진행하면서 예상치 못한 문제들이 연속으로 터졌습니다. 이번 포스팅에서는 그 트러블슈팅 과정을 기록합니다.1. 트러블슈팅: StackOverflowError구현 후 테스트를 진행하니 콘솔에 다음과 같은 에러가 발생했습니다.PlaintextCaused by: java.lang.StackOverflowError at DefaultedRedisConnection.pExpire at DefaultedRedisConnection.pExpire at DefaultedRedisConnection.pExpire ...redisTemplate.expire() 호출 시 무한 재귀가 발생하는 현상이었습니다.원인은 의존성 충돌이었습니다. 기존에 redisson-spr..

이상 거래 탐지 구현 - Redis Sliding Window와 이메일 알림

1편에서 설계를 마쳤으니 이번엔 실제 구현 과정을 기록합니다.1. Sliding Window Rate Limiting단시간 과다 주문 탐지에서 처음에는 단순 카운팅 방식을 고려했습니다.PlaintextKey: "order:count:userId"Value: 주문 횟수TTL: 60초 근데 이 방식에는 문제가 있어요. 바로 고정 윈도우(Fixed Window) 문제입니다.Plaintext0초~60초: 4건 주문 → 통과61초: 카운트 초기화61초~70초: 4건 더 주문 → 통과실제로는 55초~65초 사이에 8건이 몰렸는데 차단이 안 돼요. 그래서 Redis Sorted Set으로 Sliding Window를 구현했습니다.Javapublic void checkOverTrade(Long userId) { ..

이상 거래 탐지 시스템 설계 - 금융권은 어떻게 이상 거래를 막는가? (클로드 코드)

가상 자산 거래 플랫폼을 개발하면서 실제 금융권에서 중요하게 다루는 이상 거래 탐지 기능을 구현해봤습니다. 단순히 기능을 추가하는 것이 아니라, 실제 금융 서비스에서 어떤 패턴을 이상 거래로 보는지 먼저 조사하고 설계했습니다.1. 왜 가상 자산 거래 플랫폼에서 이상 거래 탐지가 중요한가?가상 자산 거래 플랫폼은 송금 서비스가 아니지만, 오히려 익명성이 높고 변동성이 커서 이상 거래 탐지가 더 중요한 도메인입니다. 실제 업비트, 빗썸 같은 거래소들도 이런 기능을 갖추고 있어요.조사한 이상 거래 패턴들:단시간 과다 주문 → 시세 조작 방지고액 단일 거래 → 자금 세탁 방지 (AML)심야 고액 거래 → 계정 탈취 의심거래 패턴 급격한 변화 → 보이스피싱, 계정 도용이 중에서 현재 프로젝트에서 구현 가능한 것들..

JWT에서 Access Token + Refresh Token + Rotation 방식으로 전환하기 (클로드 코드)

요즘 유행하는 Claude Code를 활용해서 개인 프로젝트의 보안을 강화해봤습니다. 이번 포스팅에서는 기존 JWT 단일 토큰 방식에서 Access Token + Refresh Token + Rotation 방식으로 전환한 과정과 그 이유를 정리해봤습니다.1. 왜 JWT 토큰 방식을 선택했나?Spring Security의 formLogin은 기본적으로 로그인 성공/실패 시 302 redirect로 페이지를 이동시킵니다. 물론 커스터마이징으로 해결할 수도 있지만, JWT 방식이 React와의 연동에 더 자연스럽고 이후 확장성도 좋아서 JWT 방식으로 전환했습니다.JWT 토큰은 단순히 header.payload.signature 구조의 문자열입니다. 유저가 로그인하면 서버가 JWT 토큰을 발급해주고, 이후 ..

SpringApplication에 대한 정리

개인 프로젝트를 진행하며 ApplicationRunner 인터페이스를 사용할 일이 있었는데, 이 인터페이스는 자동으로 프로젝트를 실행하면 인터페이스가 작동됩니다. 어떻게 알아서 진행되는지 궁금하여 찾아보던 중, SpringApplcation 에서 이게 작동한다는 것을 알게되었습니다. 이는 Spring Initializr 를 실행하면 자동으로 생성되는 main 메서드 안에 존재하는 SpringApplication.run() 메서드에 해당합니다. 내부 코드를 열어보면 무려 13단계에 걸쳐 복잡하게 실행되는 것을 볼 수 있습니다. 처음 코드를 까보면 "이걸 다 알아야 하나?" 하는 막막함이 들었습니다.하지만 결론부터 말씀드리면, 핵심적인 6단계만 정확히 이해하셔도 충분합니다.오늘은 실제 run() 메서드의 ..

스프링 정리

02.16 1. 스프링은 IoC 컨테이너를 가진다**IoC(Inversion of Control)**는 **'제어의 역전'**이란 뜻이다. 기존 자바 프로그래밍에서는 개발자가 직접 객체를 생성(new)하고, 메소드를 호출하며 프로그램의 흐름을 제어했다. 하지만 스프링에서는 이 제어권이 개발자가 아닌 **프레임워크(스프링 컨테이너)**로 넘어간다.개발자: 객체의 설계도(클래스)만 작성하고, 설정(Configuration)만 해준다.스프링 컨테이너: 알아서 객체(Bean)를 생성하고, 관리하고, 필요할 때 없애는 **생명주기(Lifecycle)**를 전담한다.즉, **"내가 호출하는 게 아니라, 프레임워크가 내 코드를 호출한다"**는 것이다.2. 스프링은 DI를 지원한다**DI(Dependency Injec..