RECENT POSTS

최근에 작성한 글

01 / 03

전체 글

전체 보기

[동시성] 동시 요청에서 ACTIVE 데이터는 어떻게 하나만 보장할까?

AI 위험 분석에 사용할 프롬프트를 DB에서 버전별로 관리하는 기능을 구현했다.프롬프트를 새로 등록하면 기존 ACTIVE 프롬프트를 RETIRED 상태로 변경하고, 새로운 버전을 ACTIVE 상태로 저장하는 방식이다. 예를 들어 v1이 활성화된 상태에서 새로운 프롬프트를 등록하면 다음과 같이 변경된다.등록 전v1 ACTIVE등록 후v1 RETIREDv2 ACTIVE 이 구조에서 지켜야 할 중요한 조건이 하나 있었다.동일한 prompt_key에는 ACTIVE 상태의 프롬프트가 항상 하나만 존재해야 한다.단일 요청에서는 문제가 없었지만, 동일한 프롬프트에 대한 등록 요청이 동시에 들어오면 이 조건이 깨질 가능성이 있었다.1. 동시 요청에서 Race Condition 재현하기기존 프롬프트 등록 로직은 크게 다..

[Review] AI 분석을 비동기로 처리하기 : Spring Event부터 다중 인스턴스까지

이번 프로젝트에서는 사용자가 생성한 투자 전략과 백테스트 결과를 LLM에 전달하고, 위험 등급과 위험 요인을 분석하는 기능을 구현했다. AI 분석은 일반적인 DB 조회와 달리 LLM API 호출 시간이 필요하다. 따라서 사용자가 분석을 요청할 때 LLM 응답이 끝날 때까지 HTTP 요청을 유지하는 대신, 분석 요청과 실제 AI 처리를 분리하는 비동기 구조를 적용했다.1. 처음 설계한 AI 분석 흐름AI 분석 요청이 들어오면 먼저 PostgreSQL에 분석 데이터를 PENDING 상태로 저장한다.POST /api/v1/ai/analyses │ ▼전략 / 백테스트 검증 │ ▼AiRiskAnalysis 저장status = PENDING │ ..

[Refactor] QueryDSL 조회 성능 및 코드 구조 리팩토링

오늘 한 일오늘은 HubRoute 조회 기능을 구현한 뒤 코드 구조를 개선하고 유지보수성을 높이는 리팩토링을 진행하였다.특히 Repository 계층의 의존성 개선, QueryDSL 정렬 화이트리스트 적용, 트랜잭션 이후 메모리 Graph 갱신 구조를 중심으로 수정하였다.Repository 계층 의존성 개선기존에는 Repository가 Application 계층의 DTO를 직접 전달받고 있었다.search(HubRouteSearchCondition condition, Pageable pageable)Repository는 Domain 계층에 위치하므로 Application DTO를 의존하지 않는 것이 적절하다고 판단하였다.따라서 Service 계층에서 SearchCondition을 필요한 값으로 분리하여 ..

[Refactor] saveAndFlush() 적용 및 테스트 자동화

오늘 한 일오늘은 Hub Service의 CodeRabbit 리뷰를 반영하면서 코드의 안정성을 개선하고 테스트 환경을 정비했다.주요 작업은 다음과 같다.CodeRabbit 리뷰 반영Hub/HubRoute 초기화 로직 개선Directions API 응답 Null Safety 추가단위 테스트 수정Gradle 테스트 자동화 환경 구축1. save() 대신 saveAndFlush()를 사용한 이유CodeRabbit에서 다음과 같은 리뷰를 받았다.복합 Unique 제약 조건이 존재하지만 동시 요청이 들어오는 경우 DataIntegrityViolationException이 Internal Server Error로 처리될 수 있습니다.기존에는 다음과 같이 저장만 수행하고 있었다.HubRoute savedHubRout..

[Infra] 운영 서버 메모리와 Swap 필요성

🚨 운영 서버에서 확인한 메모리 상태배포 서버에서 free -h 명령어를 실행했다.$ free -h total used free shared buff/cache availableMem: 908Mi 723Mi 11Mi 72Mi 173Mi 184MiSwap: 2.0Gi 197Mi 1.8Gi 처음에는 free 메모리가 11MB밖에 남지 않았다!!물리 메모리(RAM)는 거의 한계까지 사용 중이다.Swap 메모리를 이미 197MB 사용 중이다.available 메모리도 약 184MB 수준이다.즉, 서버는 이미 Swap을 사용하면서 버티고 있는..

[결제 도메인] 프로젝트 피드백 및 배운 점

이번 프로젝트에서는 단순히 기능을 구현하는 것뿐만 아니라, 실제 서비스 환경에서 고려해야 하는 부분들에 대한 피드백을 받을 수 있었다.1. 결제 승인 시 클라이언트 요청 데이터 검증토스 결제 승인 요청 시 클라이언트가 전달하는 orderId, amount 등을 그대로 신뢰하면 안 된다.결제 승인 전에 반드시 DB에 저장된 주문 정보와 비교하여주문 번호가 일치하는지결제 금액이 일치하는지검증해야 한다.클라이언트의 요청은 언제든 변조될 수 있기 때문에 서버가 최종 검증을 수행해야 한다2. . 외부 API 호출과 DB 트랜잭션 설계해야 한다.토스 승인 요청과 DB 저장을 하나의 @Transactional 범위에서 처리하면 문제가 발생할 수 있다.예를 들어토스에서는 결제가 정상 승인되었지만이후 DB 저장 과정에서..

[Infra: AWS] Docker Hub를 활용한 EC2 배포 환경 구축 (AWS EC2 + Docker + Docker Hub)

오늘은 Spring Boot 프로젝트를 Docker Hub에 올리고 AWS EC2에서 실행하는 환경을 구축했다.1. Docker Image 생성 및 Docker Hub 업로드기존에는 Dockerfile을 작성하고 로컬에서 이미지를 생성하였다.docker build -t threeservings:latest .하지만 실제 배포에서는 이미지 이름을 프로젝트명으로만 관리하기보다 Docker Hub Repository를 사용한다.docker build -t leunbin/threeservings-api:latest .Docker Hub 로그인 후docker login이미지를 업로드하였다.docker push leunbin/threeservings-api:latest왜 Docker Hub를 사용하는가?기존 방식E..

[Infra: Docker] docker compose 만들기

프로젝트 환경이번 프로젝트에서는 Docker Compose를 이용해서 다음과 같은 개발 환경을 구성하였다.Spring Boot │ ├── PostgreSQL └── Redis모든 서비스는 하나의 Docker Network 안에서 실행되고 Spring Boot는 PostgreSQL과 Redis를 내부 네트워크를 통해 통신하도록 구성했다.Docker Compose란?Dockerfile은 하나의 컨테이너를 만드는 방법을 정의.실제 프로젝트에서는Spring BootPostgreSQLRedis처럼 여러 개의 컨테이너가 함께 동작해야한다. 이들을 하나의 파일에서 관리하기 위해 사용하는 것이 Dokcer Compose이다docker compose up이 명령어를 실행하면 Spring Boot, Pos..