개발 slecs

쇼핑몰 Redis 접속 오류 수정과 블랙리스트 자동 차단 구조 도입

목차

도커 컨테이너 안에서 돌아가는 Spring 앱이 Redis에 접속할 때 localhost를 쓰면 당연히 안 된다 - 컨테이너 입장에서 localhost는 컨테이너 자신이니까. 근데 이걸 처음 세팅할 때 놓치는 경우가 생각보다 많다. application-쇼핑몰 플랫폼.yml에 Redis host가 localhost로 박혀 있었고, 로컬 개발 환경에선 Redis를 직접 깔고 쓰다 보니 문제가 없었다. 컨테이너로 올리는 순간 접속 실패.

수정 자체는 한 줄이다.

# before
spring:
  redis:
    host: localhost
    port: 6379

# after
spring:
  redis:
    host: host.docker.internal
    port: 6379

host.docker.internal은 Docker Desktop(Mac/Windows) 환경에서 호스트 머신을 가리키는 DNS 이름이다. Linux 환경에서는 --add-host=host.docker.internal:host-gateway 옵션이나 docker-compose.ymlextra_hosts를 명시해야 같은 효과가 난다. 배포 타겟이 Linux 서버라면 이 부분을 별도로 챙겨야 함. 환경별로 override 파일을 나누는 방식도 있지만, 구조가 단순할 땐 파일이 늘어나는 게 오히려 더 복잡해진다. 이번엔 단일 YAML로 관리하고 있어서 host 값만 바꾸는 게 가장 깔끔한 선택이었다.

재현 → 원인 파악 → 최소 범위 수정 → 배포 순서로 처리했는데, 이 루틴이 잡혀가고 있다는 게 요즘 느끼는 거다. 처음엔 이 순서가 당연한 것 같아도 실제로는 원인 파악을 건너뛰고 이것저것 바꿔보는 방식으로 디버깅하는 경우가 많다.

자주 반복되는 버그 패턴

수정 작업이 몇 번 쌓이다 보니 패턴이 보이기 시작했다. 매번 처음부터 디버깅하는 것보다, 이런 패턴 리스트가 머릿속에 있으면 로그 보는 시간이 훨씬 줄어든다.

패턴 증상 빠른 확인 포인트
null 체크 순서 오류 NPE (NullPointerException) 체이닝 순서, Optional 미처리
경로 예외 처리 누락 필터에서 정상 요청 차단 SecurityConfig permitAll 목록
YAML 공백 혼용 설정값 파싱 실패 탭/스페이스 혼용, 들여쓰기 깊이
import 누락 컴파일 에러 IDE 자동 import가 안 된 경우
환경별 host 불일치 접속 거부, 타임아웃 localhost vs 컨테이너 DNS

이번 Redis 케이스는 마지막 항목이었다. 로그에 Connection refused가 찍히면 host 설정부터 보는 게 빠르다. 에러 메시지 자체는 꽤 친절한 편이라 익숙해지면 원인 찾는 데 5분도 안 걸린다.

YAML 공백 혼용은 진짜 조심해야 하는 게, 에러 메시지가 직관적이지 않아서 원인을 잘못 짚기 쉽다. could not find acceptable representation 같은 메시지 보고 설정 값이 틀렸나 싶어 한참 헤매다가 들여쓰기에 탭이 하나 끼어 있었던 적이 있다. 에디터에서 공백 표시 켜두는 게 습관이 됐음.

블랙리스트 자동 등록과 Rate Limit 구조

공격성 요청 대응을 수동으로 하면 속도에서 진다. IP를 보고, 확인하고, DB에 넣고 - 그 사이에 이미 요청이 수백 건씩 들어온다. 감지 즉시 DB에 자동 등록하고, 다음 요청부터 필터 단에서 바로 차단하는 구조가 필요하다. 대략적인 흐름은 이렇다.

요청 인입
  → Rate Limit 체크 (Redis 슬라이딩 윈도우)
      → 임계값 초과 시 블랙리스트 DB 자동 등록
          → 이후 요청은 필터 진입 시점에 블랙리스트 조회 후 즉시 차단

Redis 슬라이딩 윈도우 방식이 고정 윈도우보다 나은 이유는 경계 구간 버스팅 때문이다. 고정 윈도우는 윈도우가 리셋되는 직전/직후 구간에 요청을 몰아서 실질적으로 두 배 가까운 트래픽을 허용하는 허점이 있다. 슬라이딩 윈도우는 매 요청마다 직전 N초를 동적으로 계산하기 때문에 이 허점이 없다. 대신 연산 비용이 조금 더 든다.

자동 등록 구조 만들 때 챙겨야 할 것들:

  • 오탐 예외 처리: 내부 로드 테스트, 배치 작업, CI 파이프라인 등은 IP 혹은 헤더로 화이트리스트를 잡아둬야 한다. 이걸 빠뜨리면 자기 시스템을 자기가 차단하는 상황이 생김.
  • TTL 설정: 영구 차단보다는 TTL을 두는 게 낫다. 오탐이 있을 때 복구가 되고, 차단 DB가 무한정 불어나지도 않는다. 트래픽 패턴 보고 1시간-24시간 사이에서 결정하면 됨.
  • 알림 연동: 자동 차단 발생 시 슬랙이나 디스코드로 알림이 가도록 연결해 두는 게 좋다. 자동화가 목적이지만, 어떤 IP가 차단됐는지 팀이 인지하고 있어야 이상 징후를 빠르게 파악할 수 있다.

Rate Limit 임계값 설정이 생각보다 까다로운 게, 너무 타이트하면 정상 사용자가 걸리고 너무 느슨하면 의미가 없다. 적절한 기준은 결국 실측 데이터에서 나온다. 평상시 요청 패턴을 로그로 뽑아서 정상 피크의 2-3배 수준에서 임계값을 잡는 게 통상적인 방법이다. 처음부터 완벽한 값을 넣으려 하지 말고, 관대하게 시작해서 로그 보면서 조여 나가는 게 현실적임.

Redis가 단일 장애점이 되는 것도 별도로 챙겨야 할 부분이다. Rate Limit과 블랙리스트 조회가 모두 Redis에 의존하면, Redis가 죽거나 응답이 느려질 때 전체 요청 처리에 영향이 간다. 연결 풀 크기, 커맨드 타임아웃, Redis 장애 시 폴백 전략 - 이 세 가지를 미리 정해두지 않으면 Redis 문제가 서비스 문제로 번진다. 타임아웃은 짧게 잡고, 장애 시엔 Rate Limit을 우회해서 요청을 통과시키는 쪽이 차단하는 것보다 낫다. 차단 방향으로 폴백하면 Redis 장애가 서비스 장애처럼 보이니까.

작업 규모 자체는 크지 않았지만, 이런 수정들이 하나씩 쌓이면서 전체 시스템의 신뢰도가 올라간다는 걸 계속 체감한다. 버그 하나 잡고, 구조 하나 개선하는 사이클이 반복되면서 대응 루틴도 같이 쌓인다.

댓글 0

첫 댓글 달아줘.