본문 바로가기

분류 전체보기

(388)
[프로젝트 이슈] API 호출량 제한 들어가기 전이번 프로젝트에서는 사용자의 위치를 기반으로 주변 스터디룸을 탐색하는 기능을 제공했습니다. 처음에는 사용자가 조회할 때마다 외부 API를 호출하여 실시간 데이터를 가져오는 방식으로 구현했지만, 곧 예상되는 문제가 존재했습니다. 사용자가 늘어날수록 API 호출 횟수가 기하급수적으로 증가하면서 비용이 커질 것이고, 특히 카카오 API의 호출 제한 때문에 안정적으로 데이터를 가져오기 어려운 상황도 발생할 수 있다고 생각했습니다. 이를 해결하기 위해 지난 번 지오해시(GeoHash)를 활용한 캐싱 전략을 도입했었습니다. 각 스터디룸 데이터에 지오해시 값을 포함해 저장하고, 사용자가 특정 위치를 조회할 때는 해당 지점이 속한 격자와 인근 8개 격자를 함께 조회하도록 설계했습니다. 이후, 매번 외부 AP..
[프로젝트 이슈] 무중단 배포 자동화 들어가기 전지난번 배포 과정에서는 GitHub Actions와 CodeDeploy를 활용해 빌드와 서버 배포를 대부분 자동화했기 때문에, 사람이 직접 파일을 복사하고 서버를 재시작하며 발생할 수 있는 오류는 크게 줄일 수 있었습니다. 또한 배포 실패 시 CodeDeploy의 롤백 기능을 통해 이전 버전으로 빠르게 되돌릴 수 있어, 서비스가 완전히 중단되는 상황은 최소화할 수 있었습니다. 하지만 단일 EC2 인스턴스 환경에서는 배포 중 잠시 서비스가 끊길 가능성을 완전히 제거할 수 없었고, 진정한 의미의 무중단 배포까지 구현한 것은 아니었습니다. 이 한계를 보완하고자, 배포 과정에서 서비스 중단을 최소화할 수 있는 전략을 찾아보았습니다.롤링(Rolling) 배포 방식카나리 (Canary) 배포 방식블루/그..
[프로젝트 이슈] CI/CD 구축기 들어가기 전코드를 완성하고 나면, 그다음 단계는 바로 배포입니다. 처음에는 단순하다고 생각했습니다. 로컬에서 빌드한 JAR 파일을 서버에 올리고, SSH로 접속해서 실행하면 끝났죠. 한두 번 정도는 금방 끝나는 일이었고, 이 정도면 충분하다라고 생각하기도 했습니다. 제가 처음 배포를 시작했을 때는, 로컬에서 서버로 JAR 파일을 옮기는 과정도 직접 실행하여야 했습니다. 일반적으로는 SCP나 SFTP를 이용해서 파일을 서버에 업로드했습니다. 예를 들어, 터미널에서 다음과 같이 명령어를 입력하면 됩니다.scp build/libs/app.jar ubuntu@서버IP:/home/ubuntu/app/ 이 한 줄로 로컬에서 빌드한 JAR 파일이 원격 서버의 지정 디렉토리로 복사됩니다. 처음에는 단순히 파일을 복사하..
[프로젝트 이슈] 사용자 로그인 처리(인증, 인가) 0. 들어가며API 서버를 설계하면서 가장 먼저 고민한 부분 중 하나는 사용자의 로그인 상태를 어떻게 관리할 것인가? 였습니다. 대표적인 방식으로는 세션 방식과 토큰방식이 있으며 관련 레퍼런스를 살펴보니 각각의 방식은 장단점이 존재했습니다. 세션 방식은 서버가 로그인한 사용자 정보를 서버 측 세션 저장소에 유지하고, 클라이언트는 세션 ID를 쿠키에 담아 요청마다 전달하는 구조입니다. 이 방식은 비교적 구현이 간단하지만, 서버가 상태를 유지(stateful)해야 하므로 확장성과 유연성에 제약이 있었습니다. 특히 서버가 여러 대로 구성되는 분산 환경에서는 세션 동기화 또는 공유 저장소를 구성해야 하므로 복잡도가 증가하는 문제점이 있었습니다. 반면, 토큰 기반 인증은 서버가 사용자 정보를 상태로 관리하지 않고..
[프로젝트 이슈] 테스트 커버리지 과정 들어가기 전처음에는 테스트 코드를 꼭 작성해야 한다는 사실은 알고 있었지만, 그 중요성을 크게 느끼진 못했습니다. 하지만 프로젝트가 진행될수록 요구사항이 늘어나고 비즈니스 로직이 점점 복잡해지면서, 코드 변경이 기존 기능에 어떤 영향을 줄지 불안해졌고, 점점 내가 작성한 코드에 확신이 서지 않는 경험을 하게 되었습니다.이러한 불안감을 해소하고 코드의 안정성을 확보하기 위해 서비스 레이어를 중심으로 단위 테스트를 작성하기 시작했습니다.서비스 레이어는 단순히 데이터를 CRUD 하는 수준을 넘어, 예외 상황에 대한 처리, 외부 의존성과의 협력, 조건 분기 등의 핵심 비즈니스 로직이 밀집된 계층이기 때문에 테스트 대상 중 가장 우선순위가 높다고 판단했습니다.그래서 이번에는 서비스 레이어 위주로 단위 테스트를 집..
[JPA] JPA의 동시성 제어 0. 들어가며데이터베이스 기반 애플리케이션을 개발하다 보면, 여러 사용자가 동시에 같은 데이터를 읽고 수정하려는 상황을 자주 마주하게 됩니다. 이런 상황에서 적절한 동시성 제어가 이루어지지 않으면, 데이터가 꼬이거나 무결성이 깨지는 문제로 이어질 수 있습니다. 예를 들어, 인기 있는 온라인 쇼핑몰에서 마지막으로 남은 상품을 여러 명의 사용자가 동시에 구매하려 한다면 어떻게 될까요?혹은 은행 계좌에서 같은 시간에 두 건의 이체 요청이 들어온다면요? 이런 혼란을 방지하기 위해 도입된 개념이 바로 락(Lock)입니다. 자바에서는 synchronized나 ReentrantLock 같은 키워드를 활용해 애플리케이션 레벨에서 동기화를 처리할 수 있습니다.이와 마찬가지로, JPA 또한 트랜잭션과 연계된 동시성 제어 ..
[프로젝트 이슈] 동시성 문제 원인 및 해결 들어가며현재 진행 중인 프로젝트에서는 사용자가 스터디방에 참여하거나 나갈 수 있으며, 동시에 여러 사용자가 같은 방에 입장하는 상황도 빈번히 발생할 수 있습니다. 이러한 기능은 다음과 같은 핵심 요구사항을 포함하고 있습니다.사용자는 하나의 방에만 참여할 수 있어야 한다.방의 최대 인원 수를 초과해서는 안 된다.동시에 여러 사용자가 같은 방에 참여 요청을 보내더라도, 최대 인원 수까지만 입장이 허용되어야 한다.초기 구현에서는 단일 사용자 관점에서의 로직만을 고려하여 currentCount(현재 인원)을 1만큼 증가시키고 Participation(참여 기록)을 생성하는 방식으로 참여 기능을 구현했습니다. 단위 테스트나 일반적인 시나리오에서는 문제가 없어 보였지만, 실제 운영 환경과 유사한 조건에서 여러 스레..
[JAVA] ExecutorService - graceful shutdown 보호되어 있는 글입니다.
[JAVA] Executor 프레임워크 0. 들어가기 전실무에서 스레드를 직접 생성해서 사용하면 다음과 같은 3가지 문제가 있습니다.스레드 생성 시간으로 인한 성능 문제스레드 관리 문제Runnable 인터페이스의 불편함1. 스레드 생성 비용으로 인한 성능 문제스레드를 사용하는 데 있어 가장 큰 문제 중 하나는 생성 비용이 매우 크다는 점입니다. 스레드를 생성하면 단순히 자바 객체를 하나 만드는 수준이 아니라, 각 스레드는 자신만의 호출 스택(call stack)을 가지며 이 메모리를 별도로 할당해야 합니다. 또한 운영체제 커널 수준에서 시스템 콜(system call)을 통해 스레드를 생성하기 때문에 CPU와 메모리 리소스를 많이 사용하게 됩니다. 생성된 스레드는 운영체제의 스케줄러에 등록되어 관리되며, 실행 순서를 조정받는 과정에서도 추가적..
[JAVA] 동시성 컬렉션 0. 들어가기 전멀티스레드 환경에서 하나의 컬렉션 인스턴스를 여러 스레드가 동시에 사용해야 할 때가 많습니다. 예를 들어, 여러 사용자의 요청을 하나의 ArrayList 에 추가하거나, 공유된 큐에서 작업을 꺼내는 구조가 있을 수 있습니다. 그런데 여기서 중요한 질문이 하나 생깁니다.바로 "자바의 컬렉션들은 기본적으로 스레드 세이프할까?" 라는 점입니다. 특히 java.util 패키지에 포함된 대표적인 컬렉션들인 ArrayList, HashMap, LinkedList 같은 클래스들은 여러 스레드가 동시에 접근해도 문제가 없을까요?참고로 여러 스레드가 동시에 접근해도 괜찮은 경우를 스레드 세이프(Thread Safe)하다고 합니다. 하지만 컬렉션 프레임워크가 제공하는 대부분의 연산은 원자적이지 않습니다. ..