최신 글
개요20,000 TPS 환경에서 평균 500ms의 응답속도를 보장하는쿠폰 서비스를 설계하며 고민했던 내용을 정리하려 한다. 기능은 이벤트성 선착순 쿠폰 발급과 쿠폰 관리 기능으로 나뉘며,특히 선착순 쿠폰 발급의 동시성 문제와 대량 트래픽을 안정적으로 처리하는 설계가 주요 도전 과제이다.   쿠폰 서비스 설계쿠폰이라는 도메인은 코드의 변동 가능성, 타 서비스와의 연동 및 호출 빈도를 기준으로아래와 같이 두 기능으로 나눌 수 있었다. 선착순 쿠폰 발급 이벤트쿠폰 사용 / 관리운영 성격단기간 집중 이벤트 중심장기적이고 안정적인 기능 운영요구 사항 변경 빈도높음낮음트래픽 특성단시간에 급격한 트래픽 증가비교적 일정하고 안정적인 트래픽시스템 자원 사용량순간적으로 높은 리소스 소비일관된 자원 사용외부 시스템 연동상대..
개요지금까지 클러스터링 인덱스(Clustering Index)는 테이블의 데이터가 디스크에 물리적으로 저장되는 순서, 정렬을 담당하는 인덱스라고 생각했다. [Real MySQL 8.0] 에서는 “물리적으로 정렬하여 저장한다.” 라는 문구가 존재했기에 오해가 생겼던 것 같다. 추가적으로 지금까지 멘토링, 면접을 진행하며 아무런 태클을 받지 못했기에 잘못된 지식이 굳어진 것 같다. 오해가 생긴 정확한 이유는 클러스터링 인덱스를 사용하면 테이블의 데이터가‘단순 배열처럼 연속된 디스크 블록에 정렬되어 저장되는 구조가 된다’ 라고 생각했기 때문이다.실제로는 B+Tree라는 자료구조를 사용하여 “논리적으로 정렬된 형태”로 저장된다. 이번에는 클러스터링 인덱스에 대해 잘못알고 있었던 지식을 재정리 해보려 한다.해당 ..
개요웹 서버를 개발하다보면 비슷한 형태를 가지는 코드가 반복되는 경우가 더러 있다. 이번 프로젝트에서는 JPA를 통해 낙관적 락을 간편하게 구현했는데,업데이트 쿼리 실행을 실패하면 예외(OptiimisticLockingFailureException)가 발생한다. 즉, 낙관적 락을 사용하는 모든 기능은 동시성 문제로 인해 쿼리 실행 실패 시,예외 트래킹, 디버깅, 로깅을 위해 위의 예외를 조금 더 상세하게 핸들링하는 코드가 필요하다. 예외를 핸들링하는 중복코드를 템플릿 메서드 패턴을 사용하여 가독성을 높이고, 관리 포인트를 줄여보려 한다.  원인앞서 언급했듯 낙관적 락 관련 예외를 핸들링하는 코드가 다수의 메소드에 중복 선언되어 있다.public Product update(Product targetProd..
개요이전에 DB 타입의 결정으로 조회 성능을 대폭 향상시킬 수 있었다.이번에는 페이지네이션의 방식 변경으로 조회 성능을 개선시켜보려 한다. 현재 프로젝트에는 상점을 별점, 생성일 등의 순서대로 정렬하거나 필터링하는 기능이 존재한다. 해당 기능의 결과는 페이지네이션을 통해 일부분의 데이터만 응답하게 된다. 현재는 구현이 간단한 offset 기반의 페이지네이션 방식을 사용하여 구현하고 있다. 인덱스를 생성하여 간단하게 조회 성능을 향상 시킬 수는 있지만 커서 기반 페이지네이션이라는 방식을 새로이 알게 되어 성능 비교를 해보려 한다.    Offset vs Cursor페이지네이션을 구현하는 대표적인 방식에는 offset 기반, cursor 기반 페이지네이션이 존재한다.  Offset 기반 페이지네이션offse..
개요 데이터베이스 테이블 PK 타입 변경에 대한 안건 · Issue #10 · IDLE-Sparta/order-management-serverContent 현재 데이터베이스 테이블의 PK 타입은 varchar, 값은 UUID 입니다. PK 타입을 varchar로 지정한 이유는 추후 PK 생성 방식의 변경을 대비하여 확장성을 고려했기 때문입니다. PK의 타입을 변경하github.com 주문관리 애플리케이션의 데이터베이스 테이블에 내부식별자와 외부식별자를 분리하는 안건에 대해 고민한 내용을 정리한다.내부, 외부 식별자의 차이는 다음과 같다.내부 식별자: 애플리케이션 서버 내부, DB 내부에서 사용하는 식별자외부 식별자: 사용자에게 보여지는 식별자 현재 데이터베이스 테이블의 PK 타입은 varchar이며, 값..
1. 개요쿠폰의 사용 기간 정책을 저장할 방식에 대해 고민한 내용을 정리한다. 조건부 속성 문제를 DDD + Factory Method Pattern 을 사용하여DB 레벨에서는 nullable하게 application 레벨에서는 null을 허용하지 않게 설계하여 해결했다. 쿠폰 사용 기간에 대한 요구사항은 아래와 같다.1. 쿠폰은 사용할 수 있는 유효 기간을 가진다.2. 쿠폰 사용 기간 정책은 3가지로 나뉜다.     2.1. FIXED: 정해진 시작일과 종료일사이에만 사용할 수 있는 정책.    2.2. AFTER: 발급일로부터 특정일 이후까지 사용할 수 있는 정책    2.3. MIXED: 발급일로부터 특정일 이후까지, 고정된 기간 내에 사용할 수 있는 정책 2. 데이터 추출위의 요구사항을 데이터 관..
1. 문제Kafka를 사용하는 이번 프로젝트에서 데이터의 일관성을 보장하기 위해 Transactional Outbox Pattern을 적용하였다. Outbox에 새로운 데이터가 커밋되는 것을 트리거로 동작하는 Kafka 이벤트 발행 로직을 구현하기 위해 Spring Event와 @TransactionalEventListener를 사용하였다. 그러나 Spring Event를 발행하는 작업은 정상적으로 동작하였으나, @TransactionalEventListener가 이벤트를 인식하지 못하는 문제가 발생하였다.  1.1. 소스코드문제가 발생한 코드이다. ApplicationEventPublisher를 통해 DomainEventEnvelop 객체를 이벤트로 발행한다.@RequiredArgsConstructor..
관련 프로젝트 Github GitHub - maru-KK/sparta-logistics: MSA 물류 유통・관리 서버MSA 물류 유통・관리 서버. Contribute to maru-KK/sparta-logistics development by creating an account on GitHub.github.com  개요OrderService와 DeliveryService 간의 통신을 Event 기반으로 전환하여 기존 동기 호출에서 발생하는 물리적 의존성을 제거하려고 한다. 현재 주문 생성은 사용자가 호출하는 API로, 응답 시간이 길어질 경우 사용자 편의성이 저하된다. 하지만 DeliveryService는 주문 생성 과정에서 동기 방식으로 호출되며, 여러 서비스와 추가로 동기 통신을 수행해 배송 데이터..
문제RedisTemplate를 통해 데이터를 저장하기에는 성공했으나, 읽어오는 과정에서 역직렬화에 실패했다.Redis에 저장할dto에 존재하지 않는 필드가 함께 저장되어 역직렬화에 실패한 것인데 이유를 알아보자. Redis에 String 타입의 Json 데이터를 저장했고 직/역직렬화에는 Jackson 2.18.1 라이브러리를 사용했다.  Redis에 StringType으로 저정할 Dto는 아래와 같다.@Getter@NoArgsConstructor@AllArgsConstructorpublic class ProductCache implements Serializable { @Serial private static final long serialVersionUID = 1L; private st..
· 일기
졸업 후 약 1년 반동안 백엔드 개발자로의 길을 끊임 없이 달려오다 본의 아니게 약 8주의 휴식 기간을 가졌다.취업 경쟁에서 잠시 벗어나고자 한 회피성 휴식 기간이었고, 이제 반성하며 다시 마음을 다잡으려 한다. 취업 준비를 마쳤다고 생각한 이후, 처음으로 네이버에 지원했고 아쉽게도 면접에서 탈락했다. 이후 10여곳의 기업 공채에 지원했고, 모두 불합격한 뒤 자신감이 떨어졌다. 성격상 어떤 일을 앞두고 완벽한 준비를 하기에 결과가 따라주지 않는다면 다소 큰 타격이 있는 건 당연했다. 8주라는 시간을 제대로 쉰 것도 아니다. 머리는 쉬어야 한다고 생각하지만, 마음은 취업 문제에 묶여있었다. 이도저도 아닌 허송세월을 보낸 것이 자신에게 미안하다. 브라우저 북마크에 있는 내 블로그와 깃허브 클릭을 하루에도 몇..
개요500만개의 레코드에서 키워드 기반 문자열 검색을 수행했을 때, 속도가 저하되는 문제를 MySQL의 Full-Text Search를 통해 해결해보려 한다. 개선 결과 기존 API 처리 시간 1185ms → 564ms로 약 52% 성능 개선이 이뤄졌다.  기존 쿼리의 문제점아래는 기존에 사용하던 쿼리이다. 키워드를 기반으로 post 테이블을 검색한다.explain select *from post pjoin category c on c.category_id = p.category_category_idwhere p.title like '%**:keyword**%' and p.region_region_id in (**:region_ids**) and p.status not in ('HIDE', ..
이슈 내용개발자가 제어할 수 없는 요소에 대한 테스트 용이성과 직접적으로 의존하면 안 되는 모듈의 클래스를 의존하고 있진 않은지 테스트하기 위해 인터페이스를 두고 테스트 더블을 직접 구현할 수 있도록 설계했다. 코드의 크기가 작을 때는 아무런 문제가 되지 않았지만, 기능과 의존성이 추가될 때마다 직접 구현할 테스트 더블이 증가했고 이는 생산성 저하와 더불어 테스트 전체의 크기가 커지는 영향을 미쳤다. 해당 문제가 발생한 이유와 해결방법에 대해 정리해보려 한다.  테스트 더블을 직접 구현한 이유Mockito 라이브러리를 사용하면 쉽게 테스트 더블을 생성할 수 있다. 하지만 이전 프로젝트에서 적용해본 결과 비즈니스 로직에서 bootstrap, framework 모듈의 클래스를 직접 의존하진 않는지 파악할 수..
· CS
여러 서비스에서의 Message기반 비동기 호출 아키텍처를 구성할 때, Message Driven Architecture, Event Driven Architecture의 선택지가 존재한다. 여기서 Message와 Event의 차이를 정리해보려 한다. 메시징 메시징은 서비스가 메시지를 서로 비동기적(메시지 브로커를 사용하는 경우와 통상적인 경우)으로 주고받는 통신 방식이다. 보통 메시징 기반 애플리케이션은 서비스 사이에 Message Brocker(대표적으로 Kafka, RebbitMQ)를 두어 비동기 통신을 구현하지만, 서비스가 직접 서로 통신하는 BrockerLess 아키텍처도 존재한다. BrockerLess 아키텍처는 통신할 애플리케이션이 가용중이 아니라면 내부적으로 큐를 두는 방식 등을 통해 메시..
문제JAVA 17, Spring Boot 3.2 멀티 모듈 구조에서 각 모듈간의 의존성을 제대로 선언해놓고 실행했음에도 모듈에서 다른 모듈의 소스 정보를 못 읽어오는 이슈가 발생했다. 해당 문제를 해결하기 위해 애플리케이션 실행 지점에 다른 모듈의 객체를 생성하는 간단한 코드를 넣어 확인해보았다. 소스코드와 IDE 상에는 분명히 domain 모듈의 Product 클래스가 import 되어 있는데, 컴파일러가 Product 클래스를 import 하는 시점에 오류가 발생한다.해당 프로젝트는 내가 예전에 작성하고 테스트까지 끝마쳤던 소스코드를 그대로 가져와서 동일한 환경으로 재구성 한 것이라 완벽하게 동일한 소스코드와 의존성을 가지고 있다. 이전의 프로젝트는 오류없이 정상적으로 실행되니 참 답답한 상황이 아닐..
🥕 이번 당근 클론 프로젝트에서는 기존에 자주 사용하던 MVC 구조가 아닌 헥사고날 아키텍처를 적용해보려 한다. 따라서 먼저 배경 지식을 습득후 간단한 예제 코드를 통해 헥사고날 아키텍처에 대해 익숙해지려 한다. 이전 포스팅과 이어집니다. MVC 구조에서 헥사고날 아키텍처로 🥕 이번 당근 클론 프로젝트에서는 기존에 자주 사용하던 MVC 구조가 아닌 헥사고날 아키텍처를 적용해보려 한다. 따라서 먼저 배경 지식을 습득후 간단한 예제 코드를 통해 헥사고날 아키텍처 hyunsb.tistory.com 이번에는 이전 포스팅에서 설명한 MVC 구조에서 싱글 모듈 헥사고날 아키텍처를 적용한 API 서버를 멀티 모듈로 마이그레이션 해보려 한다. 싱글 모듈의 한계점은 명확하게 존재한다. 모든 로직이 하나의 dependen..
hyunsb
개발일기