Spring Boot 성능 튜닝 방법 — 응답 시간 50% 줄이기
Spring Boot 애플리케이션의 응답 시간을 줄이는 실전 튜닝 방법. DB 커넥션 풀, JPA 최적화, 캐시, JVM 설정까지 단계별 가이드.
TestForge Team ·
성능 측정부터 시작
튜닝 전 반드시 **기준선(baseline)**을 측정하세요. 느낌이 아닌 숫자로.
# Spring Boot Actuator 활성화
management.endpoints.web.exposure.include=health,metrics,prometheus
# k6로 부하 테스트
k6 run --vus 50 --duration 60s script.js
1. DB 커넥션 풀 최적화 (HikariCP)
Spring Boot 기본 커넥션 풀은 HikariCP입니다. 기본값이 생각보다 작습니다.
spring:
datasource:
hikari:
maximum-pool-size: 20 # 기본값 10 → 서버 코어수 × 2~4
minimum-idle: 5
connection-timeout: 3000 # 3초 이내 커넥션 획득 실패 시 예외
idle-timeout: 600000 # 10분 미사용 커넥션 반환
max-lifetime: 1800000 # 30분 최대 수명
leak-detection-threshold: 5000 # 5초 이상 반환 안 되면 경고
주의: maximum-pool-size를 무작정 늘리면 오히려 DB 과부하.
DB 서버 max_connections 의 80%를 넘지 않도록 합니다.
2. JPA N+1 문제 제거
가장 흔한 성능 저하 원인입니다.
// 문제: Order 1개 조회 후 items 쿼리 N번 추가 실행
List<Order> orders = orderRepository.findAll();
orders.forEach(o -> o.getItems().size()); // N+1!
// 해결 1: Fetch Join
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.userId = :userId")
List<Order> findWithItems(@Param("userId") Long userId);
// 해결 2: @EntityGraph
@EntityGraph(attributePaths = {"items", "user"})
List<Order> findByStatus(String status);
// 해결 3: Batch Size (컬렉션 IN절로 묶기)
@BatchSize(size = 100)
@OneToMany(mappedBy = "order")
private List<OrderItem> items;
3. 캐시 레이어 추가
// Spring Cache + Redis
@EnableCaching
@Configuration
public class CacheConfig {
@Bean
public RedisCacheConfiguration cacheConfig() {
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeValuesWith(
RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer())
);
}
}
// 사용
@Cacheable(value = "product", key = "#id")
public ProductDto getProduct(Long id) {
return productRepository.findById(id)
.map(ProductDto::from)
.orElseThrow();
}
@CacheEvict(value = "product", key = "#id")
public void updateProduct(Long id, UpdateRequest req) { ... }
4. 비동기 처리
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
// 이메일 발송, 알림 등 비크리티컬 작업 비동기화
@Async
public CompletableFuture<Void> sendNotification(Long userId, String message) {
notificationService.send(userId, message);
return CompletableFuture.completedFuture(null);
}
5. 응답 압축
server:
compression:
enabled: true
mime-types: application/json,application/xml,text/html
min-response-size: 1024 # 1KB 이상만 압축
JSON 응답 기준 40~70% 용량 감소 효과.
6. JVM 튜닝
java -jar app.jar \
-Xms512m -Xmx1g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=100 \
-XX:G1HeapRegionSize=16m \
-XX:+ParallelRefProcEnabled \
-XX:+DisableExplicitGC
7. Slow Query 자동 감지
spring:
jpa:
properties:
hibernate:
generate_statistics: true
session.events.log.LOG_QUERIES_SLOWER_THAN_MS: 100 # 100ms 이상 쿼리 로깅
logging:
level:
org.hibernate.stat: DEBUG
튜닝 우선순위
- DB 쿼리 최적화 (N+1, 인덱스) — 효과 최대
- 커넥션 풀 조정 — 즉각 효과
- 캐시 도입 — 반복 조회 제거
- 비동기 처리 — 응답 시간 단축
- 응답 압축 — 네트워크 비용 절감
- JVM 튜닝 — 미세 조정
대부분의 성능 문제는 DB 레이어에서 발생합니다.
JVM 튜닝은 1~5를 모두 적용한 후 마지막에 합니다.