[에러 해결] [Python PyCharm] PyCharm type-hint warning: ‘None’ does not have attribute ‘__getitem__’ 원인과 해결 방법

안녕하세요, 개발자 여러분! PyCharm을 사용하며 파이썬 코드를 작성하다 보면, 코드 자체는 문제없이 동작하지만 타입 힌트와 관련된 경고(Warning)가 발생하는 경우가 있습니다. 이번 포스팅에서는 shelve 모듈을 사용하다 마주칠 수 있는 “Member ‘None’ of ‘Any | None’ does not have attribute ‘__getitem__'” 경고의 원인을 분석하고, PyCharm의 타입 검사기를 만족시키면서 코드의 안정성을 높이는 해결 방법을 제시해 드리고자 합니다. 이 글은 구글 SEO에 최적화되어 있으며, 개발자 여러분의 사용자 경험을 고려하여 작성되었습니다.

1. 에러 발생 상황

이 경고는 주로 None이 될 가능성이 있는 변수에 대해 인덱스 접근(예: [0])을 시도할 때 발생합니다. 스택오버플로우에서 제시된 예시 코드는 다음과 같습니다.

import shelve

with shelve.open("file.db") as f:
    sess_data = f.get("xxx")
    if sess_data:
        username = sess_data[0] # 이 줄에서 경고 발생

위 코드에서 username = sess_data[0] 줄에 다음과 같은 PyCharm 경고가 나타납니다.

Member ‘None’ of ‘Any | None’ does not have attribute ‘__getitem__’

개발자는 이미 if sess_data: 조건문을 통해 sess_data가 None이 아닐 때만 다음 코드가 실행되도록 명시했지만, PyCharm은 여전히 None일 가능성을 경고하고 있습니다. PyCharm 버전 2026.2.2에서도 동일한 경고가 발생한다고 보고되었습니다.

2. 명확한 발생 원인

이 경고의 근본적인 원인은 파이썬의 동적 타이핑 특성과 PyCharm의 정적 타입 분석기(Static Type Checker) 사이의 이해 차이에서 비롯됩니다.

  • shelve.get()의 반환 타입: shelve 모듈의 get() 메서드는 해당 키가 쉘브 파일에 존재하지 않을 경우 None을 반환합니다. 키가 존재할 경우에는 저장된 객체를 반환하죠. 하지만 shelve는 어떤 타입의 객체든 저장할 수 있기 때문에, 타입 힌트 관점에서는 반환 타입이 Any | None (Any 또는 None)으로 추론될 수 있습니다.
  • if sess_data: 조건문의 한계: 파이썬에서 if sess_data:와 같은 조건문은 sess_data가 ‘Falsy’ 값(None, 0, 빈 문자열 '', 빈 리스트 [], 빈 딕셔너리 {} 등)이 아닐 경우 True로 평가됩니다. 런타임에서는 이 조건문이 sess_data가 None인 경우를 효과적으로 방지하여 TypeError를 막아줍니다.
  • PyCharm의 정적 타입 분석: 그러나 PyCharm의 타입 검사기(또는 MyPy 같은 외부 타입 검사기)는 런타임 로직과 별개로 코드의 잠재적인 타입 문제를 미리 분석합니다. sess_data의 타입이 Any | None으로 추론된 상황에서, if sess_data: 조건만으로는 sess_data가 None이 아님을 완전히 확신하지 못합니다. 특히 Any 타입이 포함되어 있기 때문에, PyCharm은 sess_data가 여전히 None일 가능성을 열어두고 __getitem__ (인덱스 접근을 위한 특수 메서드)이 없는 None 타입에 대한 접근을 경고하는 것입니다.

즉, 런타임에는 문제가 없지만, PyCharm은 ‘미리’ 발생할 수 있는 타입 관련 잠재적 오류를 개발자에게 알려주는 것이 목적입니다.

3. 해결 방법 및 코드 예시

이 경고를 해결하고 코드의 명확성을 높이려면, PyCharm의 타입 검사기에게 sess_data가 None이 아니며 예상하는 특정 타입임을 명시적으로 알려줘야 합니다.

3.1. 명시적인 None 체크와 타입 캐스팅(typing.cast) 활용 (권장)

가장 강력하고 명확한 해결책은 sess_data is not None으로 명시적으로 None을 체크하고, 이 변수가 특정 타입임을 타입 검사기에게 알려주는 것입니다. typing 모듈의 cast 함수를 사용하면 런타임에는 아무런 영향을 주지 않으면서(타입 변환을 시도하지 않음), 정적 타입 검사기에게만 특정 타입으로 간주하라고 지시할 수 있습니다.

import shelve
from typing import Optional, Any, cast

# sess_data의 실제 예상 타입을 지정해야 합니다.
# 예제에서는 인덱싱이 가능하므로 list[Any] 또는 tuple[Any, ...]로 가정합니다.
# 실제 데이터의 구조에 따라 정확한 타입을 명시하는 것이 중요합니다.
ExpectedSessDataType = list[Any] # 예를 들어, list[str] 또는 tuple[str, int] 등

with shelve.open("file.db") as f:
    # shelve.get()은 Any 또는 None을 반환할 수 있으므로 Optional[Any]로 명시합니다.
    sess_data: Optional[Any] = f.get("xxx")

    # 1. 'is not None'을 사용하여 sess_data가 None이 아님을 명확히 합니다.
    # PyCharm의 타입 추론을 돕기 위해 'if sess_data:'보다 'if sess_data is not None:'을 권장합니다.
    if sess_data is not None:
        # 2. PyCharm에게 sess_data가 ExpectedSessDataType 타입임을 알려줍니다.
        # cast()는 런타임에는 아무 동작도 하지 않으며, 오직 타입 검사기에게만 힌트를 줍니다.
        # 실제 데이터가 예상 타입과 다를 경우 런타임 오류(예: TypeError)가 발생할 수 있습니다.
        valid_sess_data = cast(ExpectedSessDataType, sess_data)
        
        # 이제 valid_sess_data는 ExpectedSessDataType으로 인식되어 경고가 사라집니다.
        username = valid_sess_data[0]
        print(f"Username: {username}")
    else:
        print("세션 데이터 'xxx'를 찾을 수 없습니다.")

3.2. # type: ignore 주석 (최후의 수단)

만약 위에 제시된 해결책이 적용하기 어렵거나, 특정 상황에서 타입 검사기의 경고를 의도적으로 무시하고 싶다면 # type: ignore 주석을 사용할 수 있습니다. 그러나 이 방법은 경고의 원인을 근본적으로 해결하지 않고 숨기는 것이므로, 신중하게 사용해야 합니다. 남용할 경우 코드의 잠재적 버그를 놓치게 될 수 있습니다.

import shelve

with shelve.open("file.db") as f:
    sess_data = f.get("xxx")
    if sess_data:
        username = sess_data[0] # type: ignore

4. 향후 예방을 위한 팁

이러한 타입 힌트 관련 경고를 미리 방지하고 더 견고한 코드를 작성하기 위한 몇 가지 팁입니다.

  • 명확한 타입 힌트 사용 습관화: 함수 인자, 반환 값, 변수 등에 Optional[T] (Union[T, None]의 축약형)와 같은 타입 힌트를 적극적으로 사용하세요. 이는 코드의 가독성을 높이고 다른 개발자가 코드를 이해하는 데 도움을 줍니다.
  • None 체크는 is not None으로: None 값을 체크할 때는 if value: 대신 if value is not None:을 사용하는 것이 명확하고 타입 검사기에게도 더 정확한 힌트를 제공합니다. if value:는 None뿐만 아니라 빈 리스트, 빈 문자열, 0 등 ‘Falsy’한 모든 값들을 걸러내므로, 오직 None만을 대상으로 할 때는 is not None이 정확합니다.
  • typing.cast의 현명한 활용: cast는 런타임에 아무런 영향을 주지 않으므로, 타입 검사기에게만 정보를 제공하고 싶을 때 유용합니다. 하지만 cast는 실제 타입 불일치로 인한 런타임 에러를 막아주지 않으므로, 개발자가 데이터의 실제 타입을 정확히 알고 있다는 확신이 있을 때만 사용해야 합니다.
  • 데이터 직렬화/역직렬화에 대한 이해: shelve와 같이 임의의 객체를 저장하고 불러오는 모듈을 사용할 때는, 데이터가 어떤 형태로 저장되고 어떤 타입으로 불러와질지 예측하는 것이 중요합니다. 가능하면 명확한 데이터 구조(예: 항상 리스트 또는 딕셔너리)를 유지하도록 설계하는 것이 좋습니다.
  • 테스트 코드 작성: 정적 타입 검사가 모든 런타임 오류를 잡아낼 수는 없습니다. 예상치 못한 데이터 타입이나 None 값을 처리하는 로직에 대한 단위 테스트를 작성하여 코드의 견고성을 확보하는 것이 중요합니다.

PyCharm의 타입 힌트 경고는 때때로 성가시게 느껴질 수 있지만, 이를 해결하는 과정은 코드의 안정성과 품질을 높이는 좋은 기회가 됩니다. 위 해결책들을 통해 여러분의 PyCharm 경고를 해결하고, 더욱 견고한 파이썬 코드를 작성하시길 바랍니다.

댓글 남기기