Redis 클라이언트 오류 처리

Redis 클라이언트 오류 처리 (Error Handling)

Redis 클라이언트 라이브러리를 쓸 때 오류를 처리하는 방법을 설명합니다.

출처: 공식문서 — Error handling

Redis로 작업할 때 네트워크 문제, 잘못된 명령, 리소스 제약 등 여러 이유로 오류가 발생할 수 있습니다. 이 가이드는 마주칠 수 있는 오류 유형과 이를 효과적으로 처리하는 방법을 설명합니다.

오류의 범주 (Categories of errors)

Redis 오류는 네 가지 주요 범주로 나뉩니다. 아래 표는 각 유형의 개요입니다. 오류 유형을 클릭하면 일반적인 원인·예시·처리 전략·코드 예시가 있는 상세 섹션으로 이동합니다.

오류 유형 일반적인 원인 처리 시점 예시
연결 오류(Connection) 네트워크 문제, 서버 다운, 인증 실패, 타임아웃, 풀 고갈 거의 항상 ConnectionError, TimeoutError, AuthenticationError
명령 오류(Command) 명령 오타, 잘못된 인자, 잘못된 타입, 미지원 명령 드물게(보통 버그) ResponseError, WRONGTYPE, ERR unknown command
데이터 오류(Data) 직렬화 실패, 데이터 손상, 타입 불일치 때때로(데이터 소스에 따라) JSONDecodeError, SerializationError, WRONGTYPE
리소스 오류(Resource) 메모리 한도, 풀 고갈, 연결 과다, 키 퇴출 때때로(일부는 일시적) OOM, 풀 타임아웃, LOADING

연결 오류 (Connection errors)

연결 오류는 애플리케이션이 Redis와 통신할 수 없을 때 발생합니다. 전형적으로 일시적이고 종종 복구 가능합니다.

일반적인 원인:

  • 네트워크 연결 문제
  • Redis 서버 다운 또는 도달 불가
  • 인증 실패
  • 연결 타임아웃
  • 연결 풀 고갈

예시:

  • ConnectionError: 네트워크 실패 또는 서버 도달 불가
  • TimeoutError: 작업이 설정된 타임아웃을 초과
  • AuthenticationError: 잘못된 자격 증명

처리 시점: 거의 항상. 연결 오류는 보통 일시적이므로 재시도 로직이나 폴백 전략을 구현하는 것이 권장됩니다.

예시 전략:

 graph TB A["Try to connect<br/>to Redis"] A -->|Success| B(["Use the result"]) A -->|Failure| C{Error type?} C -->|Timeout| D(["Retry with<br/>exponential backoff"]) C -->|Auth failure| E(["Check credentials<br/>and fail"]) C -->|Network error| F(["Fallback to<br/>alternative data source"])

명령 오류 (Command errors)

명령 오류는 Redis가 잘못된·형식이 잘못된 명령을 받을 때 발생합니다. 전형적으로 코드의 버그를 나타냅니다.

일반적인 원인:

  • 명령 이름 오타
  • 인자 수 오류
  • 잘못된 인자 타입(예: list 명령에 string 키를 공급)
  • Redis 버전에 존재하지 않는 명령 사용

예시:

  • ResponseError: 잘못된 명령 또는 문법 오류
  • WRONGTYPE Operation against a key holding the wrong kind of value
  • ERR unknown command

처리 시점: 드물게. 이는 보통 프로그래밍 오류이므로 런타임에 처리하려 하기보다 코드의 오류를 고쳐야 합니다. 다만 잘못된 사용자 입력 같은 일부 경우는 처리할 가치가 있습니다.

예시:

 graph TB A["User provides<br/>JSONPath expression"] A --> B["Try to execute it"] direction TB B -->|ResponseError| C["Log the error"] C --> D(["Return default value<br/>or error message to user"]) B -->|Success| E(["Use the result"])

데이터 오류 (Data errors)

데이터 오류는 데이터 자체에 문제(직렬화 실패, 데이터 손상)가 있을 때 발생합니다.

일반적인 원인:

  • 객체를 JSON으로 직렬화할 수 없음
  • 캐시된 데이터가 손상됨
  • 잘못된 데이터 역직렬화 시도

예시:

  • JSONDecodeError: JSON 데이터를 역직렬화할 수 없음
  • SerializationError: 객체를 직렬화할 수 없음

처리 시점: 때때로. 사용자 입력·외부 데이터 때문에 오류가 발생하면 우아하게 처리합니다. 코드 탓이면 코드를 고칩니다.

예시:

 graph TB A["Read cached data"] A --> B["Try to deserialize"] B -->|Success| C(["Use the data"]) B -->|Deserialization fails| D["Log the error"] D --> E["Delete corrupted<br/>cache entry"] E --> F(["Fetch fresh data<br/>from source"])

리소스 오류 (Resource errors)

리소스 오류는 Redis가 리소스가 부족하거나 한도에 닿을 때 발생합니다.

일반적인 원인:

  • 메모리 한도 도달
  • 연결 풀 고갈
  • 연결 과다
  • 메모리 압력으로 인한 키 퇴출

예시:

  • OOM command not allowed when used memory > 'maxmemory'
  • 연결 풀 타임아웃
  • LOADING Redis is loading the dataset in memory

처리 시점: 때때로. 일부 리소스 오류(REDIS 로딩)는 일시적이지만, 다른 것은 구성 문제를 나타냅니다.

예시:

 graph TB A{Resource error<br/>occurred?} A -->|Redis loading| B(["Retry after<br/>a delay"]) A -->|Memory full| C(["Check Redis<br/>configuration and data"]) A -->|Pool exhausted| D(["Increase pool size<br/>or reduce concurrency"])

오류 처리 패턴 (Error handling patterns)

패턴 1: Fail fast

오류가 복구 불가능하거나 코드의 버그를 나타낼 때 사용합니다.

사용 시점:

  • 명령 오류(잘못된 문법)
  • 인증 오류
  • 프로그래밍 오류

예시:

 try : result = r . get ( key ) except redis . ResponseError as e : # This indicates a bug in our code raise # Re-raise the exception

패턴 2: 우아한 저하 (Graceful degradation)

필요한 데이터를 얻을 다른 방법이 있어, 선호 코드 대신 대안으로 폴백할 수 있을 때 사용합니다.

사용 시점:

  • 캐시 읽기(데이터베이스로 폴백)
  • 세션 읽기(기본값으로 폴백)
  • 선택적 데이터(불가 시 건너뛰기)

예시:

 try : cached_value = r . get ( key ) if cached_value : return cached_value except redis . ConnectionError : logger . warning ( "Cache unavailable, using database" ) # Fallback to database return database . get ( key )

패턴 3: 백오프로 재시도 (Retry with backoff)

오류가 네트워크 부하나 다른 일시적 조건 때문일 수 있을 때 사용합니다.

사용 시점:

  • 연결 타임아웃
  • 일시적 네트워크 문제
  • Redis가 데이터를 로딩 중

예시:

 import time max_retries = 3 retry_delay = 0.1 for attempt in range ( max_retries ): try : return r . get ( key ) except redis . TimeoutError : if attempt < max_retries - 1 : time . sleep ( retry_delay ) retry_delay *= 2 # Exponential backoff else : raise

클라이언트 라이브러리가 종종 재시도 로직을 직접 구현하므로, 재시도를 직접 구현하기보다 올바른 구성을 제공하기만 하면 될 수도 있습니다. 각 클라이언트 라이브러리의 재시도 구성을 설명하는 페이지 링크는 아래 '클라이언트별 오류 처리'를 참고하세요.

패턴 4: 기록하고 계속 (Log and continue)

작업이 애플리케이션에 중요하지 않을 때 사용합니다.

사용 시점:

  • 캐시 쓰기(데이터 손실 허용)
  • 중요하지 않은 갱신
  • 메트릭 수집

예시:

 try : r . setex ( key , 3600 , value ) except redis . ConnectionError : logger . warning ( f "Failed to cache { key } , continuing without cache" ) # Application continues normally

오류 처리 결정 트리 (Decision tree)

 graph LR Start{Error occurred?} Start -->|Connection error| C1{Operation type?} C1 -->|Read| C2["Graceful degradation<br/>fallback"] C1 -->|Write| C3["Log and continue<br/>or retry"] C1 -->|Critical| C4["Retry with backoff"] Start -->|Command error| Cmd1{Error source?} Cmd1 -->|User input| Cmd2["Log and return<br/>error to user"] Cmd1 -->|Your code| Cmd3["Fail fast<br/>fix the bug"] Start -->|Data error| D1{Operation type?} D1 -->|Read| D2["Log, invalidate,<br/>fallback"] D1 -->|Write| D3["Log and fail<br/>data is invalid"] Start -->|Resource error| R1{Error type?} R1 -->|Redis loading| R2["Retry with backoff"] R1 -->|Pool exhausted| R3["Increase pool size"] R1 -->|Memory full| R4["Check configuration"]

로깅과 모니터링 (Logging and monitoring)

프로덕션에서는 오류 발생 시 로깅하고 패턴 파악을 위해 로그를 모니터링하는 것이 유용할 수 있습니다. 이는 어떤 오류가 가장 흔한지, 재시도·폴백 전략이 효과적인지 식별하는 데 도움이 됩니다. 일부 Redis 클라이언트 라이브러리는 이 정보를 제공하는 내장 인스트루먼테이션을 갖고 있습니다(전체 설명은 Observability 참고).

무엇을 로깅할 것인가

  • 오류 유형과 메시지: 무엇이 잘못됐는가?
  • 컨텍스트: 어떤 키? 어떤 작업?
  • 타임스탬프: 언제 발생했는가?
  • 재시도 정보: 재시도인가? 몇 번 시도했는가?

예시:

 logger . error ( "Redis operation failed" , extra = { "error_type" : type ( e ) . __name__ , "operation" : "get" , "key" : key , "attempt" : attempt , "timestamp" : datetime . now () . isoformat (), } )

무엇을 모니터링할 것인가

  • 오류율: 분당 몇 개의 오류가 발생하는가?
  • 오류 유형: 어떤 오류가 가장 흔한가?
  • 복구 성공: 몇 번의 재시도가 성공하는가?
  • 폴백 사용: 폴백 전략을 얼마나 자주 사용하는가?

이 메트릭은 패턴과 잠재적 문제를 식별하는 데 도움이 됩니다.

흔한 실수 (Common mistakes)

모든 예외 잡기

문제: 모든 예외를 잡으면 예상치 못한 오류를 잡아 버그를 숨길 수 있습니다.

예시(틀림):

 try : result = r . get ( key ) except Exception : # Too broad - some errors indicate code problems. pass

더 나은 접근: 특정 예외 유형을 잡습니다.

예시(맞음):

 try : result = r . get ( key ) except redis . ConnectionError : # Handle connection error pass

오류 유형 구분하지 않기

문제: 오류마다 다른 처리가 필요합니다. 예를 들어 문법 오류를 재시도해도 소용없습니다.

예시(틀림):

 try : result = r . get ( key ) except redis . ResponseError : # Retry? This won't help if it's a syntax error. retry ()

더 나은 접근: 오류가 복구 가능한지에 따라 각 오류 유형을 다르게 처리합니다.

예시(맞음):

 try : result = r . get ( key ) except redis . TimeoutError : retry () # Retry on timeout except redis . ResponseError : raise # Fail on syntax error

연결 풀 오류 무시하기

문제: 연결 풀 오류는 해결해야 할 구성·동시성 문제를 나타냅니다.

예시(틀림):

 # Pool is exhausted, but we don't handle it result = r . get ( key ) # Might timeout waiting for connection

더 나은 접근: 풀 사용을 모니터링하고 필요하면 크기를 늘립니다.

클라이언트별 오류 처리 (Client-specific error handling)

클라이언트 라이브러리의 예외에 대한 자세한 정보는 다음을 참고하세요.

더 알아보기 (Learn more)