문제 상황최근 입찰가보다 1000원 이상 높은 입찰가로 입찰해야 한다는 제약 조건이 있다.그러나 1000개의 스레드로 동시에 같은 금액으로 입찰했을 때 10건이 중복 저장되는 문제가 발생했다. 해결 1 - 비관적 락 적용경매 조회 시 비관적 락을 적용해 트랜잭션이 종료될 때까지 유지되도록 하였다.@Lock(LockModeType.PESSIMISTIC_WRITE)@Query("select a from Auction a where a.id = :id")Optional findByIdWithPessimisticLock(Long id); 한 번에 하나의 요청만 접근할 수 있으므로 입찰이 중복으로 저장되는 문제가 해결되었다. 한계경매는 핵심 엔티티로 수정할 일이 많았다.비관적 락을 걸면 조회 시점부터 업데이트를 ..
스프링 프로젝트/데브코스
복합 인덱스가 적용 안되었던 이유를 실험을 통해 알게 되어 공유하고자 한다. 상황경매 목록 조회 쿼리 성능을 개선해보고자 하였다.필터링 조건: 카테고리 목록정렬 조건: 북마크 수쿼리는 아래와 같다.select ...from auction_search as1_0 where as1_0.category in ('디지털 기기','패션/잡화')order by as1_0.bookmark_count desc limit 100; 조회 성능을 개선하기 위해 (카테고리, 북마크 수)를 복합 인덱스를 생성하였다.create index idx_category_bookmark_cnt on auction_search (category, bookmark_count desc); 문제실행 계획을 확인해 보니 복합 인덱스를 타지 않고..
추천 경매 목록 조회 API 조회 성능을 개선한 과정에 대해 정리해보고자 한다. 요구사항경매와 상품이 1:1, 상품과 상품 이미지가 1:N으로 연결되어 있다.경매 추천 목록 조회 API는 필터링과 정렬 수행 후 조건에 만족하는 경매 리스트를 반환한다.필터링 조건: 경매 상태(진행중), 사용자 거주지동적 정렬 조건: 북마크순, 입찰수순, 최신순, 경매 마감일 임박 순 문제조회할 때마다 경매 테이블과 상품 테이블을 조인하여 성능이 저하되었다.select ..from auction a1_0join product p1_0 on p1_0.product_id = a1_0.product_idwhere a1_0.auction_status = 'BIDDING' and a1_0.si = '서울시' and a1_..
배경기존에는 애플리케이션만 Docker 이미지로 EC2 서버에서 실행하고, Redis는 EC2 서버에 인메모리로 설치하였다.하지만 Redis도 컨테이너로 관리하는게 환경 구성에 편리할 것 같다고 생각했다.이에 Docker-Compose를 사용하여 두 컨테이너들이 통합 관리되도록 변경하였다.추후에도 배포 자동화시 이 방법을 계속 사용할 거라 잊어버리기 전에 기록해두려고 한다.cd.yml 파일 수정더보기name: CD Backendon: push: branches: [ "main", "dev" ]permissions: contents: readjobs: build-and-push-image: runs-on: ubuntu-latest steps: - name: Checkout c..
기존에는 CI만 구현되어 있는 상태였고, PR merge 후 EC2 서버에 수동 배포했다.매번 배포할 때마다 우분투 서버에서 아래 과정들을 반복했는데, 시간이 오래 걸리고 번거로웠다.깃허브 프로젝트 클론 받기gitignore 파일 직접 추가프로젝트 빌드해 jar 파일 생성기존에 실행 중인 프로세스 종료jar 파일 백그라운드에서 실행이에 Github Actions와 Docker를 활용해 배포를 자동화하였다.이번 글에서는 어떻게 배포를 자동화했는지 정리해보려고 한다. cd.yml을 작성하기 전 Dockerfile 추가, EC2 패스워드 연결 활성화, Github Secrets 환경 변수 추가가 선행되어야 한다.Dockerfile 추가DockerFile은 자바 Docker 이미지를 생성하기 위한 스크립트이다...
들어가기최종 프로젝트에서 판매자와 구매자 간 일대일 채팅을 구현하기로 하였다.처음에는 클라이언트가 서버에게 지속적으로 요청을 해 메시지를 받으면 되지 않을까 생각했다.하지만, 이런 polling 방식은 매번 HTTP 연결을 생성하고 끊기 때문에 서버에 부담을 준다. 때문에 실제로 채팅, 주식 거래 등 실시간성 데이터를 송수신할 때는 웹 소켓 방식을 활용한다고 한다.이번 글에서는 웹 소켓과 웹 소켓 위에서 쓰이는 STOMP를 알아보고, 코드에 적용해 볼 것이다. 웹 소켓이란?웹 소켓은 서버와 클라이언트 간 양방향 통신이 가능한 프로토콜이다.웹 소켓 과정은 Open Handshake, Data Transfer, Close Handshake로 나눌 수 있다. [Open Handshake]클라이언트는 HTTP에..
엔티티의 필드 값을 수정하고 반영하는데 변경 감지 혹은 벌크 수정 쿼리를 사용할 수 있다.예시로 Waiting 엔티티의 필드를 수정해 보며 둘의 차이점을 알아보자.1. 영속성 컨텍스트와 DB 간 불일치 가능성변경 감지에서는 영속성 컨텍스트와 DB가 항상 일치한다.반면 벌크 수정 쿼리는 상황에 따라 영속성 컨텍스트와 DB가 불일치 할 수 있다. 변경 감지엔티티를 repository에 저장하면 해당 엔티티가 영속화되어 영속성 컨텍스트에서 관리된다. 따라서 setStatus()로 status 필드를 PROGRESS에서 CANCELED로 수정했을 때영속성 컨텍스트는 변경 감지를 하여 엔티티의 수정 사항을 반영한다.참고로 update 쿼리와 h2 필드를 확인하기 위해 @Rollback(false)를 추가하였다...
도입 배경웨이팅 로직을 구현하며 웨이팅 취소, 지연, 입장에 따라 웨이팅 순서를 갱신해줘야 했다.초기에는 웨이팅 순서를 DB에 저장하고, 아래의 상황들에서 벌크 연산으로 웨이팅 순서를 업데이트하고자 했다. 웨이팅 순서를 rank라고 했을 때특정 고객이 웨이팅을 취소하거나 대기를 맨 뒤로 미룸 -> 해당 고객 뒤 고객들의 rank 1 감소시키기고객 입장 -> 다른 고객들의 rank 1 감소시키기 rank는 실시간으로 변화하는 데이터라 매번 벌크 연산을 진행하고 DB에 반영하면 테이블 부하가 심해지는 문제가 있었다. 이에 Redis의 list 자료 구조를 활용해 대기열 큐를 구현하기로 하였다.Redis는 인메모리 데이터베이스로 빠른 읽기, 쓰기 작업이 가능하다는 장점이 있다. 설정Build.gradleSpr..
2차 프로젝트를 진행하며 Redis가 동시성 처리 등 다방면에 활용될 수 있다는 것을 알게 되었다.나는 Redis를 활용해 대기 생성, 지연, 입장을 처리하고 싶었으나 Redis의 이해가 선행되어야 했다. 그래서 Redis의 기본 개념인 데이터 타입과 해당 데이터 타입이 어디에 활용되는지부터 살펴보고자 하였다. 위 두 가지를 살펴보기에 앞서, Redis가 무엇인지 간략하게 살펴보자.Redis(Remote Dictionary Server)Redis는 다수의 서버가 공유하는 해시 테이블이다.해시맵처럼 key-value 형태로 되어 있다.특징인메모리에 모든 데이터를 저장한다.기본적으로 휘발성 데이터지만 영속성 옵션(RDB, AOF)을 통해 데이터를 영속적으로 관리할 수 있다.단일 스레드에서 모든 작업을 수행하..
도입 API 명세서는 각 API가 어떻게 동작하는지 설명하는 문서로, 프론트엔드와 서버 간 협업에서 필수적이다. 나는 기존 프로젝트들에서 명세서 작성 도구로 Swagger를 사용했었다. 그러다 최근에 Spring Rest Docs로도 API 명세서를 작성할 수 있음을 알게 되었다. Spring Rest Docs는 스프링 프레임워크에서 제공하는 API 문서 자동화 도구이다. Swagger VS Rest Docs Swagger와 Rest Docs는 각각 장단점이 존재한다. Swagger는 적용이 쉽고, 해당 문서에서 API 호출 테스트를 직접 해볼 수 있다. 그러나 프로덕션 코드에 Swagger 문서 관련 코드가 포함되어 가독성이 떨어진다. Rest Docs는 Swagger에 비해 적용이 어렵지만 프로덕션 ..