카테고리 없음

thingo 프로젝트 성능 개선 2

bora-ovo 2026. 3. 22. 21:46

2. 문제 후보 카테고리

문제 정의가 끝났다면 그 다음은 “그 문제가 어디서 생기는지“ 보아야 한다.

성능 문제는 겉으로는 전부 “느림”으로 보일 수 있지만 실제 원인은 매우 다르기 때문이다.

예를 들어 어떤 기능은 인덱스 부족 때문일 수 있고, 어떤 기능은 JPA fetch 문제일 수 있으며, 어떤 기능은 애초에 구조가 부적절하여 느릴 수도 있다.

PostgresSQL 문서에서도 적절한 쿼리 계획과 인덱스 선택이 성능에 매우 중요하다고 설명하고, Hibernate 문서에서는 fetching 전략에 따라 N+1 같은 비효율이 발생할 수 있다고 설명했다.

따라서 thingo 프로젝트의 성능 문제 후보에 대해 아래와 같이 카테고리화를 진행하였다.


2-1. DB 인덱싱 문제

이는 인덱스 설계 문제이다.

PostgreSQL은 인덱스가 WHERE, JOIN 조건에 쓰이는 컬럼을 빠르게 찾는 게 중요하다고 설명한다.

반대로 자주 조회되는 조건 컬럼에 인덱스가 없으면 전체 스캔 비용이 커질 수 있다.

인덱스는 비교적 구조적으로 분석하기에 쉽기도 하고 실행 계획으로 근거를 제시하기에 용이하다.

PostgreSQL의 EXPLAIN 같은 경우에도 쿼리 플랜을 분석하여 어떤 접근 방식이 선택됐는지 보여주기 때문에 인덱싱 문제는 성능개선 파트에서 가장 먼저 점검하기 좋은 항목이다.


2-2. 쿼리 작성 및 ORM 사용 문제

이는 쿼리 자체가 비효율 적인 경우이다.

인덱스가 있더라도 쿼리 구조가 좋지 않으면 성능은 여전히 나쁠 수 있다.

Hibernate의 문서에서는 연관 객체 로딩 방식에 따라 N+1 문제를 유의해야 한다고 말한다.

 

우리 프로젝트에서는 N+1 조회 문제, 불필요한 join을 다회 사용하는 문제, 필요한 컬럼보다 더 많은 데이터를 조회하는 문제, 목록 조회에서 pagination의 비효율 문제, 같은 데이터를 반복 조회하는 문제, fetch 전략이 실제 사용 패턴과 맞지 않는 문제 등이 있을 수 있다.

 

해당 카테고리는 성능 개선에 대해 알아보기 전부터 중요함을 알고 있었다.

많은 Spring/JPA 기반 프로젝트에서는 성능 저하가 체감되는 부분이 DB 자체보다는 ORM 사용 방식에서 발생하기 쉽기 때문이다.

즉 DB가 느리다기 보다는 DB를 느리게 쓰고 있다의 문제일 수 있다는 것이다.

Hibernate는 N+1과 같은 문제를 join fetch, batch fetching, subselect fetching 등으로 줄일 수 있다고 설명했다.


2-3. 애플리케이션 로직 및 코드 구조 문제

서비스 계층에서의 로직, 반복 계산, 불필요한 데이터 변환 등 애플리케이션 코드 자체의 비효율을 말한다.

이 카테고리는 DB와 연관이 있기 보다는 Java & Spring 코드에서의 문제를 말한다.

예를 들어 동일한 요청에서 같은 계산을 반복하거나, 한 번만 해도 되는 데이터를 여러 번 가공하거나, 외부 호출을 순차적으로 묶어놓아 전체 시간이 길어지는 경우 등이 있을 것이다.

 

우리 프로젝트에서는 한 요청 안에서 같은 서비스or레포지토리 호출이 반복되는 경우, 불필요한 DTO 변환이나 리스트 재가공이 많은 경우, 순차 처리 때문에 전체 응답시간이 늘어나는 경우 등이 있을 수 있다.

 

이 문제에서는 성능문제가 항상 SQL, DB와 관련되어서만 발생하는 것이 아니라는 것을 상기시킨다.

실제로는 DB 시간 보다 서비스 로직 시간이 더 길 수 있기 때문에 유의해야한다.


2-4. 요청 - 응답 흐름 및 병목 구간 문제

요청이 들어와서 응답이 나가기까지의 전체 파이프라인 중 특정 구간이 병목인 경우이다.

이는 endpoint 하나를 전체로 보지말고, Controller → Service → Repository → DB → 외부 API → 응답 직렬화 로 이어져 있다는 기본적인 것을 상기시켜야한다.

 

우리 프로젝트에서는 외부 API 호출이 느려 전체 응답이 지연되는 경우, 응답 데이터가 너무 커서 직렬화 또는 전송의 비용이 증가하는 경우, 파일 또는 대용량 JSON 처리에서 병목이 발생하는 경우 등이 있을 수 있다.

 

단순히 DB 문제일 것 같다는 추측보다는 이 과정에서 실제 병목되는 구간을 더 정확히 찾을 수 있도록 해야한다.


2-5. 시스템 아키텍쳐 및 작업 분리 문제

특정 역할을 한 곳에 과도하게 몰아둔 경우이다.

 

우리 프로젝트에서는 사용자 요청 처리와 배치&크롤링이 같은 인스턴스에서 발생하는 경우, 읽기 비중이 높은데 읽기 최적화 구조가 부족한 경우, 동기로 처리할 피룡가 없는 작업임에도 요청 흐름 안에 포함되어있는 경우, 확장성을 고려하지 않고 단일 구조로 개발한 탓에 부하에 취약한 경우 등이 있을 수 있따.

 

해당 문제는 큰 구조를 바꿔야하는 문제이기 때문에 바로 전체 구조를 바꾸자라는 느낌의 진행방식 보다는 현재 구조가 성능적으로 병목을 만들고 있는 구조인지 점검을 하는 항목으로 두는 것이 좋을 듯 하다.

만약 무리한 아키텍쳐 리팩토링이 필요하다면 장기적으로 진행해야할 수 있다.


2-6. 배치&크롤링&주기 작업 문제

크롤링 스케줄러, 배치, 데이터 수집 작업 관련 성능 문제이다.

이건 사용자 API와는 별도로 진행되는데, 이유는 내부 작업이 눈에 보이지 않더라도 CPU, 메모리, 네트워크, DB를 차지 하기 때문에 사용자 요청 성능에서 영향을 끼칠 우려가 있기 때문이다.

 

우리 프로젝트에서는 크롤링 주기가 필요 이상으로 짧은 경우, 중복 수집or저장으로 자원을 낭비하는 경우, 스케줄링 시간이 사용자 활동 시간과 겹치는데 해당 시간에 성능저하가 발생하는 경우, 크롤링 실패or재시도가 과도하게 발생하는 경우, 사용자 요청과 내부 배치가 같은 DB/서버를 동시에 사용하는 경우 등이 있을 수 있다.

 

사용자 화면은 느린데 코드만 봐서는 원인이 안보이는 경우, 백그라운드 작업이 몰려서 일 수 있기 때문에 우리프로젝트처럼 데이터 수집이나 주기 작업이 있는 서비스에서는 꼭 점검해보아야하는 문제이다.


2-7. 관측/측정 체계 부족 문제

성능 저하 그 자체는 아니지만 성능 문제를 확인할 수 없는 상태를 뜻한다.

측정이 없기에 개선도 성능 저하도 증명할 수 없는 상황이다.

 

예를 들어 우리의 공지사항 API가 느린데, 로그가 없으면 DB가 느린지, DTO 변환이 느린지, 정렬이 느린지 등등 알 수 없기 때문이다.

이는 문제가 있을 때 문제를 모른다 라고 말하기 보다는 문제를 진단할 도구가 부족한 상황이라는 상황을 제대로 전달하기 위해 넣어둔 카테고리 이다.


2-8. 문제 카테고리 우선순위

우리와 같은 일반적인 웹 백엔드 프로젝트에서는 아래와 같이 우선적으로 점검하는 것으로 결정하였다.

해당 이유는 쿼리/인덱스는 비교적 적은 변경으로도 큰 효과를 낼 수 있고, 실행계획과 로그를 근거로 남기기 쉽기 때문이다. 하지만 아키텍쳐 변경은 파급이 크기 때문에, 먼저 작은 단위의 병목부터 검증을 하는 접근 방식으로 진행하고자 한다.

  1. 요청-응답 병목 구간 문제
  2. 쿼리/ORM 문제
  3. 인덱싱 문제
  4. 배치·크롤링 문제
  5. 애플리케이션 로직 문제
  6. 아키텍처 문제
  7. 관측 체계 부족 문제