in을 쓰고 딕셔너리 키가 없는 경우를 KeyError로 처리하지 말고 get을 사용할 것

Tasker_Jang·2026년 8월 14일
post-thumbnail

🤔 이게 왜 문제인가

딕셔너리로 하는 일의 대부분은 결국 세 가지입니다. 값에 접근하고, 대입하고, 삭제하는 것입니다. 이 중에서 유독 코드가 지저분해지는 지점이 "키가 아직 없을 때"의 처리입니다. 투표 집계, 태그별 그룹핑, 사용자별 이벤트 누적처럼 흔한 작업마다 같은 분기가 반복됩니다.

파이썬에는 이 상황을 다루는 방법이 네 가지 있습니다. in 식으로 미리 확인하는 방법, KeyError를 잡는 방법, get 메서드를 쓰는 방법, 그리고 setdefault를 쓰는 방법입니다. 네 가지가 다 동작하기 때문에 아무거나 골라도 될 것 같지만, 값의 타입과 생성 비용에 따라 명확한 우열이 생깁니다.

💣 흔한 실수

counters = {}
key = 'wheat'

if key in counters:  # 해시 조회가 두 번 일어납니다
    count = counters[key]
else:
    count = 0

counters[key] = count + 1

votes = {}
name = 'brioche'
who = 'Elmer'

try:  # 예외 흐름으로 정상 로직을 표현하고 있습니다
    names = votes[name]
except KeyError:
    votes[name] = names = []

names.append(who)

in 방식은 키를 두 번 조회하고 분기가 세 줄로 늘어납니다. KeyError 방식은 예외를 정상적인 제어 흐름으로 쓰는 셈이라 의도가 잘 드러나지 않습니다. 둘 다 같은 패턴이 코드베이스 곳곳에 복사되기 시작하면 관리 비용이 빠르게 불어납니다.

✅ 파이썬다운 방법

from collections import defaultdict

counters = {}
key = 'wheat'

counters[key] = counters.get(key, 0) + 1  # 조회 한 번으로 끝납니다
print(counters)  # {'wheat': 1}

votes = {}

if (names := votes.get('brioche')) is None:  # 대입식으로 중복 조회를 막습니다
    votes['brioche'] = names = []

names.append('Elmer')

# 값 생성이 반복되는 구조라면 defaultdict가 더 깔끔합니다
grouped = defaultdict(list)
grouped['brioche'].append('Elmer')

카운터처럼 값이 정수나 문자열 같은 기본 타입이면 get에 디폴트를 넘기는 한 줄이 가장 짧고 빠릅니다. 값을 만드는 비용이 비싸거나 생성 도중 예외가 날 수 있다면 get과 대입식(walrus operator)을 함께 써서 필요할 때만 만들면 됩니다. setdefault는 키 존재 여부와 무관하게 디폴트 값을 매번 생성하므로, 그 패턴이 자주 보인다면 defaultdict로 넘어가는 편이 낫습니다. 가독성과 성능이 동시에 개선되는 선택입니다.

📎 기억할 것

  • 키가 없는 경우를 다루는 방법은 in, KeyError, get, setdefault 네 가지입니다.
  • get의 두 번째 인자는 키가 없을 때 돌려줄 디폴트 값입니다.
  • 값이 기본 타입인 카운터류에는 get이 가장 짧고 빠릅니다.
  • 값 생성 비용이 크거나 예외가 날 수 있으면 get과 대입식을 조합합니다.
  • setdefault는 이름이 헷갈리고 디폴트 값을 항상 만들어 내므로 우선순위가 낮습니다.
  • setdefault가 적합해 보이는 상황이라면 defaultdict 사용을 먼저 검토해야 합니다.

🛠 실무에서 어디 쓸까

로그나 이벤트 집계 코드에서 곧바로 쓰입니다. 요청 로그를 훑으며 상태 코드별 건수를 세는 작업은 stats[code] = stats.get(code, 0) + 1 한 줄이면 충분하고, 사용자별 이벤트를 리스트로 모으는 작업은 defaultdict(list)로 분기 자체를 없앨 수 있습니다.

외부 API 응답을 파싱할 때도 유용합니다. 선택적 필드가 많은 JSON에서 payload.get('meta', {}).get('locale', 'ko')처럼 체이닝하면 중첩된 if 없이 안전한 기본값을 확보할 수 있습니다. 다만 값 생성이 비싼 경우에는 get의 인자가 항상 평가된다는 점을 기억하고 대입식 쪽으로 방향을 잡아야 합니다.

profile
ML Engineer 🧠 | AI 모델 개발과 최적화 경험을 기록하며 성장하는 개발자 🚀 The light that burns twice as bright burns half as long ✨

0개의 댓글