분명히 주석 처리했는데 왜 429가 나오지?

토토
2026.01.14 16:46조회 17
토토·2026.01.14 16:46·조회 17

오늘의 삽질: "분명히 주석 처리했는데 왜 429가 나오지?"

TL;DR

Spring Boot에서 @Component가 붙은 필터는 SecurityConfig에서 주석 처리해도 서블릿 필터로 자동 등록됩니다. 저처럼 삽질하지 마세요.


문제 상황

제가 운영하는 서비스(fullstackfamily.com)에서 이상한 일이 벌어졌습니다.

혼자 사용하는데도 가끔 429 Too Many Requests 에러가 떴어요. 처음엔 "Cloud Run이 이상한가?" 싶었는데, 조금 빠르게 페이지를 이동하면 거의 확정적으로 429가 나오더라고요.

GET /api/home/stats → 429 GET /api/news → 429 GET /api/lessons → 429

혼자 쓰는데 왜...?


원인 추적

1단계: 일단 테스트해보자

k6라는 부하 테스트 도구로 테스트를 돌려봤습니다.

k6 run tests/stress/basic-load.js

결과가 충격적이었어요.

429 에러율: 96.43%

???

50명 가상 사용자로 테스트했는데 96%가 429로 실패했습니다. 이건 뭔가 심각하게 잘못된 거였어요.

2단계: 범인 찾기

Cloud Run 설정을 확인해봤는데 정상이었어요.

--max-instances 10 --concurrency 80

이론상 800개 동시 요청 처리 가능한데, 50명도 못 버티다니?

그래서 코드를 뒤져봤습니다. 그랬더니...

@Component  // ← 이놈!!! public class RateLimitFilter extends OncePerRequestFilter {    private static final int MAX_REQUESTS_PER_MINUTE = 60;    // ... }

아... IP당 1분에 60번만 허용하는 Rate Limit 필터가 있었네요.

3단계: 근데 이거 분명히 비활성화했는데?

SecurityConfig를 보니까...

// 필터 순서: BotDetectionFilter → JwtAuthenticationFilter → Controller // RateLimitFilter 비활성화 (2026-01-11) - 트래픽 차단 문제로 임시 비활성화 .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .addFilterBefore(botDetectionFilter, JwtAuthenticationFilter.class) // .addFilterBefore(rateLimitFilter, BotDetectionFilter.class)  ← 주석 처리함!

분명히 주석 처리했거든요! 근데 왜 429가 나오는 거야?


원인: @Component의 배신

여기서 제가 몰랐던 Spring Boot의 동작이 있었습니다.

@Component가 붙은 OncePerRequestFilter는 Spring Boot가 자동으로 서블릿 필터로 등록합니다.

SecurityConfig에서 등록 안 해도요!

Spring Security Filter Chain:  주석 처리해서 여기엔 없음                              ↓ Servlet Filter Chain:         @Component라서 여기에 자동 등록됨 ← 문제!                              ↓ Controller

그러니까 SecurityConfig에서 아무리 주석 처리해도, @Component 때문에 서블릿 필터로 먼저 실행되고 있었던 거예요.

저는 "주석 처리했으니까 안 돌아가겠지~" 하고 있었는데, Spring Boot는 열심히 Rate Limiting을 하고 있었던 거죠.


해결

간단합니다. 그냥 파일을 삭제했어요.

rm RateLimitFilter.java rm RateLimitFilterTest.java

SecurityConfig에서 import랑 필드도 제거하고요.


결과

다시 k6로 테스트해봤습니다.

============================================================ Phase 57 성능 테스트 결과 ============================================================ 최대 동시 사용자: 50 총 HTTP 요청: 12,010건 평균 응답 시간: 370.91ms P95 응답 시간: 2234.27ms 429 에러율: 0.00%  ← 완전 해결! ============================================================

429 에러가 96%에서 0%로!

평균 응답 시간이 13ms에서 370ms로 늘어난 건, 이전엔 Rate Limit에서 바로 차단당해서 응답이 빨랐던 거고, 이제는 실제로 서버가 처리하니까 정상적인 수치입니다.


교훈

1. @Component는 강력하다

Spring Boot의 자동 설정은 편리하지만, 가끔 의도치 않은 동작을 만들어요.

필터를 비활성화하고 싶으면:

  • ❌ 주석 처리만 하기 (다른 곳에서 자동 등록될 수 있음)
  • ✅ 파일 자체를 삭제하거나, @Component 제거

2. 테스트는 중요하다

"혼자 쓰는데 가끔 429가 나와요"라는 증상만으로는 원인 찾기 어려웠을 거예요.

k6로 부하 테스트 돌리니까 96% 실패라는 명확한 증거가 나왔고, 그제서야 "이건 뭔가 근본적으로 잘못됐구나"를 알 수 있었습니다.

3. P95를 보자

테스트 결과에서 평균만 보면 안 돼요.

  • 평균 370ms → "괜찮네!"
  • P95 2234ms → "어... 20명 중 1명은 2초 넘게 기다리네?"

P95는 "100명 중 95명의 경험"을 보여주는 지표입니다. 실제 서비스 품질을 판단할 때 평균보다 P95가 더 중요해요.


오늘의 삽질 요약

항목내용
증상혼자 써도 429 에러 발생
원인@Component 필터가 자동 등록되어 Rate Limiting 적용
삽질 시간약 2시간
해결RateLimitFilter 파일 삭제
결과429 에러 96% → 0%

결론: Spring Boot 자동 설정 무서워요. 필터 비활성화할 거면 그냥 삭제하세요.


사용한 도구

  • k6: 부하 테스트 도구 (https://k6.io)
  • Cloud Run: Google Cloud의 서버리스 컨테이너 플랫폼

혹시 비슷한 삽질 중이신 분 계시면 참고하세요!

댓글

불러오는 중...