Backend
home
🤔

대규모 트래픽을 다루기 위한 Java Spring 백엔드 개발자의 고민

생성 일시
2025/09/13 11:32
태그
Database
게시일
2025/09/13
최종 편집 일시
2025/09/13 11:59

1. 실제 서비스 운영에서 발생할 수 있는 문제

DB 쿼리 병목 → 수 ms 쿼리도 수만 건이 모이면 대형 장애로 이어짐
캐싱 전략 부재 → 같은 요청이 반복되어 리소스 낭비
비효율적인 아키텍처 → 트래픽이 몰릴 경우 서비스 전체가 다운될 수 있음

2. Spring 기반 서비스에서 자주 접하는 문제

2.1. DB 쿼리 최적화

N+1 문제
JPA/Hibernate 사용 시 fetch 전략을 잘못 설정하면 쿼리가 폭발할 수 있다.
해결: fetch join, @EntityGraph, QueryDSL을 활용한 최적화.
인덱스 부재
카디널리티가 높은 컬럼에 인덱스가 없으면 풀스캔(full-scan) 발생
해결: 실행 계획(`EXPLAIN`) 분석 후 적절한 인덱스 설계.
Batch Insert/Update
단건 insert를 루프로 돌리면 성능이 급격이 저하됨.
해결: Spring Batch 또는 JPA saveAll + JDBC batch 옵션 활용.

2.2. 캐싱 전략

Redis 캐싱
Hot data(ex. 인기 게시글, 랭킹, 추천 결과)를 Redis에 저장하여 DB 부하 감소
TTL(Time to Live) 설정으로 state data 최소화
Local Cache + Global Cache 조합
자주 조회되는 config 값은 Caffeine(local cache)
다수의 서버 간 공유되는 데이터는 Redis(Global cache)

2.3. 대규모 트래픽 아키텍처 패턴

CQRS(Command Query Responsibility Segregation)
flowchart TB
    subgraph Write DB
        Master[(Master DB)]
    end
    subgraph Read DB
        Replica1[(Replica DB #1)]
        Replica2[(Replica DB #2)]
    end

    API[Spring API 서버] -->|쓰기 요청| Master
    API -->|읽기 요청| Replica1
    API -->|읽기 요청| Replica2
    Master --> Replica1
    Master --> Replica2
Mermaid
복사
쓰기와 읽기를 분리해 DB 부하 분산
읽기는 replica DB, 쓰기는 master DB 활용
Event Driven Architecture (Kafka 활용)
flowchart LR
    Client[사용자 요청] --> LB[로드밸런서]
    LB --> App1[Spring Boot App #1]
    LB --> App2[Spring Boot App #2]
    App1 & App2 --> Redis[(Redis 캐시)]
    App1 & App2 --> Kafka[(Kafka 브로커)]
    App1 & App2 --> DB[(PostgreSQL/MongoDB)]
    Kafka --> Consumer[비동기 Consumer 서비스]
    DB --> ES[(Elasticsearch - 검색/분석)]
Mermaid
복사
실시간 로그/이벤트 처리를 DB가 아닌 Kafka에 적재
소비자(Consumer)가 비동기로 처리 → 시스템 안정성 향상
서킷 브레이커 & Rate Limiting
외부 API 장애 시 전체 서비스 전파 방지(Resilience4j, Spring Cloud CircuitBreaker)
특정 사용자/서비스의 과도한 요청 차단(Redis + Bucket4j)

3. DB 성능 최적화

sequenceDiagram
    participant Client
    participant API
    participant Redis
    participant DB

    Client->>API: 데이터 요청
    API->>Redis: 캐시 조회
    alt Cache Hit
        Redis-->>API: 캐시 데이터 반환
        API-->>Client: 응답 (빠른 응답)
    else Cache Miss
        API->>DB: 쿼리 실행
        DB-->>API: 결과 반환
        API->>Redis: 캐시에 저장 (TTL)
        API-->>Client: 응답 (조금 느림)
    end
Mermaid
복사

3.1. 쿼리 튜닝 프로세스

1.
문제 탐지: APM(New Relic, Pinpoint, Datadog)으로 느린 쿼리 확인
2.
실행 계획 분석: EXPLAIN ANALYZE 로 인덱스 사용 여부 확인
3.
쿼리 리팩토링: 불필요한 JOIN 제거, SELECT 절 최소화
4.
결과 검증: JMeter, Locust 등으로 부하 테스트

3.2. Tip

Top-N 쿼리(랭킹)는 인덱스 + LIMIT 조합으로 구성
Full Text Search는 Elasticsearch 도입 고려
Batch job은 오프 피크 시간대 실행

4. 실무에서 중요한

“빠른 코드보다 중요한 건 안정적인 구조”
단순히 쿼리 속도만 줄이는 게 아니라, 장애 방지를 잘할 수 있는 아키텍처가 필요하다.
관찰 가능성(Observability), 모니터링이 곧 생존력
Grafana + Prometheus + ELK(stack) 세팅으로 장애를 빠르게 인지할 수 있어야 한다.
코드 품질보다 중요한 건 팀의 협업 프로세스
PR 리뷰, DB Schema Migration Tool(Flyway, Liquibase), CI/CD 파이프라인은 필수.