본문 바로가기

분류 전체보기153

펀딩 API 성능 개선기: TPS 61에서 100으로 들어가며음악 크라우드펀딩 플랫폼을 개발하면서 가장 중요한 기능은 단연 '후원하기'였습니다. 사용자가 프로젝트에 후원하는 이 핵심 기능의 성능 테스트를 진행했을 때, 결과는 예상보다 좋지 않았습니다. TPS 61.6, 평균 응답 시간 3850ms. 이 글에서는 이를 TPS 100, 평균 응답 46ms까지 개선한 과정을 공유하려 합니다. 초기 성능, 그리고 문제의 시작처음 부하 테스트를 돌렸을 때의 결과입니다.요청 수: 1000건TPS: 61.6/sec평균 응답 시간: 3850ms최소: 167ms최대: 9462ms표준편차: 1705ms평균 응답 시간이 거의 4초에 가깝습니다. 더 심각한 것은 최대 응답 시간이 9초를 넘어간다는 점이었습니다. 저희는 AWS ECS에 배포할 계획이었고, 제한된 예산 내에서 인프.. 2025. 12. 10.
Spring Boot에서 Redisson을 활용한 분산 락 구현하기 들어가며MSA(Microservices Architecture) 환경에서 개발을 하다 보면 한 가지 자주 마주하게 되는 문제가 있습니다.바로 여러 서버 인스턴스가 동시에 동일한 스케줄러를 실행하는 상황입니다.제가 개발 중인 음악 크라우드펀딩 플랫폼에서도 같은 문제가 발생했습니다.“프로젝트 시작 스케줄러”, “프로젝트 종료 스케줄러”, “Outbox 이벤트 재시도 스케줄러”가 서버 2대 이상에서 동시에 실행되면서, 동일한 프로젝트가 중복 처리되는 상황이 생긴 것입니다. 이런 경우 데이터 정합성이 깨지고, 불필요한 중복 작업으로 인해 시스템 부하가 증가할 수 있습니다.이 문제를 해결하기 위해 다양한 방식을 검토한 끝에, Redis 기반의 Redisson 분산 락을 도입하게 되었습니다. 아래에서는 이를 AOP.. 2025. 12. 3.
분산 트랜잭션에서 Outbox 패턴 사용하기 어떤 문제가 있었냐면...크라우드펀딩 플랫폼을 만들고 있는데요, Funding 서비스가 다른 서비스들(Reward, Payment)이랑 통신해야 하는 상황이었습니다.비즈니스 플로우는 이렇습니다1. 사용자가 후원 버튼 클릭2. Reward 서비스한테 티켓 재고 감소 요청3. Payment 서비스한테 결제 요청4. Funding DB에 저장5. 통계 업데이트간단해 보이죠? 근데 여기서 문제가 생겼는데요, 실제로 겪은 문제들 케이스 1: 티켓은 예약됐는데 결제가 실패함✅ Reward 서비스: 재고 감소됨 (이미 커밋됨)❌ Payment 서비스: 결제 실패❌ Funding DB: 저장 안 됨 (롤백됨)결과: 티켓 재고만 줄어들고 실제 후원은 안 된 상태 💥사용자 입장에선 결제 실패했다고 나오는데, 티켓은 이미.. 2025. 12. 2.
AWS ECS 기반 MSA 구축 시 서비스 디스커버리 방식 비교 안녕하세요. 이번 포스팅에서는 AWS ECS Fargate 환경에서 마이크로서비스 아키텍처(MSA)를 구축할 때 고민했던 서비스 디스커버리 방식에 대해 공유하고자 합니다.프로젝트 배경저희 팀은 음악 크라우드펀딩 플랫폼을 MSA로 구축하고 있으며, 다음과 같은 환경에서 개발하고 있습니다.기술 스택: Spring Boot (Java 21), AWS ECS Fargate서비스 구성: 5개의 마이크로서비스 (Funding, Payment, Reward 등)예상 트래픽: DAU 50,000 ~ 100,000명, 피크 타임 TPS 5,000 ~ 10,000프로젝트 기간: 1개월팀 구성: 4명이러한 환경에서 서비스 간 통신을 어떻게 구성할지 고민하게 되었고, 3가지 방식을 비교 분석했습니다.제가 생각한 우리 서비스가.. 2025. 11. 26.
DDD에서 AbstractAggregateRoot 이해하기 AbstractAggregateRoot1. AbstractAggregateRoot란?Spring Data에서 제공하는 도메인 이벤트 발행을 위한 추상 클래스입니다. 애그리거트 루트가 이를 상속받으면 도메인 이벤트를 쉽게 등록하고 자동으로 발행할 수 있습니다.import org.springframework.data.domain.AbstractAggregateRoot;public class Order extends AbstractAggregateRoot { public void pay() { this.status = PAID; registerEvent(new OrderPaidEvent(this.id)); }}2. 핵심 역할2.1 도메인 이벤트 저장소public abstra.. 2025. 10. 30.
Redis를 활용한 데이터베이스 캐싱 전략 개요이 문서는 Redis를 사용한 데이터베이스 캐싱 전략에 대한 AWS 백서를 정리한 내용입니다. 인메모리 데이터 캐싱은 애플리케이션 성능을 향상시키고 데이터베이스 비용을 절감하는 가장 효과적인 전략 중 하나입니다.1. 데이터베이스의 주요 과제분산 애플리케이션을 구축할 때 디스크 기반 데이터베이스는 여러 문제점을 가지고 있습니다.1.1 느린 쿼리 처리디스크에서 데이터를 검색하는 물리적 시간과 쿼리 처리 시간으로 인해 응답 시간이 밀리초 단위로 증가최적화된 상태에서도 최소 수십 밀리초의 응답 시간 소요1.2 확장 비용NoSQL이든 관계형 데이터베이스든 높은 읽기 성능을 위한 확장 비용이 높음단일 인메모리 캐시 노드가 제공하는 초당 요청 수를 맞추려면 여러 개의 데이터베이스 읽기 복제본이 필요1.3 데이터 .. 2025. 10. 23.
Spring Boot 환경에서의 직렬화(Serialization) 이해하기 직렬화란 무엇인가?직렬화(Serialization)는 프로그램 메모리에 존재하는 데이터를 저장하거나 전송 가능한 형태로 변환하는 과정입니다.프로그램이 실행되는 동안 JVM 메모리에는 다양한 객체들이 생성되고 소멸됩니다. 이러한 객체들은 프로그램이 실행 중일 때만 존재하며, 프로그램이 종료되면 함께 사라집니다. 만약 이 데이터를 파일에 저장하거나, 네트워크를 통해 전송하거나, 다른 프로세스와 공유하려면 메모리 상의 객체를 특정 형식으로 변환해야 합니다.반대로, 저장되거나 전송된 데이터를 다시 메모리의 객체로 복원하는 과정을 **역직렬화(Deserialization)**라고 합니다.왜 직렬화가 필요한가?메모리와 저장소는 서로 다른 언어를 사용한다다음과 같은 자바 객체가 있다고 가정해봅시다.public cla.. 2025. 10. 22.
Docker 완벽 가이드: 컨테이너 기반 개발 환경 구축하기 Docker란 무엇인가?Docker는 애플리케이션을 컨테이너라는 경량화된 패키지로 실행할 수 있게 해주는 플랫폼입니다. 애플리케이션 실행에 필요한 코드, 런타임, 시스템 도구, 라이브러리를 모두 포함하여 어디서든 동일한 환경에서 실행할 수 있습니다. 핵심 특징컨테이너화 애플리케이션과 의존성을 하나의 패키지로 묶어 환경에 구애받지 않고 실행할 수 있습니다. 개발 환경에서 테스트한 애플리케이션을 운영 환경에 그대로 배포할 수 있죠.경량성 가상 머신과 달리 운영체제 커널을 공유하기 때문에 시작 시간이 빠르고 리소스 사용량이 적습니다.이식성 한 번 빌드한 이미지는 Docker가 설치된 어떤 환경에서도 동일하게 동작합니다.핵심 개념 이해하기이미지(Image)애플리케이션 실행에 필요한 모든 요소를 포함한 읽기 전용.. 2025. 10. 21.
JPA @Enumerated 사용 시 Data truncated 에러 해결 에러 상황Spring Boot + JPA 환경에서 회원가입 기능 구현 중 다음과 같은 에러가 발생했습니다.SQL Error: 1265, SQLState: 01000Data truncated for column 'role' at row 1원인 분석1. 엔티티 코드@Entity@Table(name = "p_user")public class User extends BaseEntity { @Enumerated(EnumType.STRING) @Column(nullable = false) // length 속성 없음 private UserRole role = UserRole.USER;}2. Enum 정의public enum UserRole { USER("사용자"), OWNER(".. 2025. 10. 13.
Entity 식별자 전략: UUID vs Auto Increment vs Snowflake 목차들어가며1. Auto Increment (순차적 증가)개념장점단점적합한 사용 사례2. UUID (Universally Unique Identifier)개념장점단점최적화 방법적합한 사용 사례3. Snowflake ID개념구조장점단점구현 예제적합한 사용 사례4. 전략 비교표5. 실무 적용 가이드마치며 들어가며Spring JPA에서 Entity의 식별자(ID)를 어떻게 설계할지는 시스템 아키텍처에서 중요한 결정사항입니다. 이 글에서는 세 가지 주요 식별자 전략(Auto Increment, UUID, Snowflake)의 특징과 장단점을 비교하고, 각 상황에 맞는 최적의 선택 방법을 알아보겠습니다. 1. Auto Increment (순차적 증가)Auto Increment 개념데이터베이스가 자동으로 1씩 증가.. 2025. 10. 1.
배달 플랫폼 데이터베이스 PK 전략: 실무 중심 설계 과정 배경배달 주문 관리 플랫폼을 개발하면서 데이터베이스 설계의 첫 단계인 Primary Key 전략을 결정해야 했습니다. 요구사항 문서에는 "모든 주요 엔티티의 식별자는 UUID를 사용"이라고 명시되어 있었지만, 이것이 정말 최선의 선택일까요?이 글에서는 UUID, BIGINT, 그리고 커스텀 ID 생성 전략을 비교 분석하고, 실제 기업들이 어떻게 문제를 해결하는지 살펴보며, 최종적으로 우리 프로젝트에 적합한 전략을 선택한 과정을 공유합니다.문제 정의초기 요구사항프로젝트 요구사항은 명확했습니다:모든 주요 엔티티(주문, 결제 등)는 UUID를 PK로 사용유저 엔티티는 예외 (username 사용)보안과 확장성을 고려한 설계의문점하지만 개발을 진행하면서 몇 가지 의문이 생겼습니다:"모든 테이블에 UUID를 써야.. 2025. 9. 29.
Java 21 Virtual Thread가 만드는 새로운 조합: MSA 서비스 간 통신의 4가지 선택지 들어가며Java 21의 Virtual Thread 등장으로 마이크로서비스 아키텍처에서 서비스 간 통신에 대한 선택지가 크게 확장되었습니다. 기존에는 HTTP 클라이언트 선택과 스레드 모델이 거의 고정적으로 결합되어 있었지만, 이제는 더 다양한 조합이 가능해졌습니다.- HTTP 클라이언트: OpenFeign vs WebClient- 스레드 모델: Platform Thread vs Virtual Thread이 두 축의 조합으로 총 4가지 접근 방식이 가능해진 것입니다. 각각의 조합이 어떤 특성을 가지며, 언제 어떤 선택을 해야 하는지 분석해보겠습니다.4가지 조합의 전체 그림1. OpenFeign + Platform Thread (전통적 방식)특징: 간단한 API, 제한된 동시성적합한 상황: 단순한 서비스, .. 2025. 9. 18.