루프 종료후 for 루프 변수를 사용하지 않기

Tasker_Jang·2026년 8월 13일
post-thumbnail

🤔 이게 왜 문제인가

파이썬의 for 루프 변수는 루프 안에서만 사는 존재가 아닙니다. 루프가 끝난 뒤에도 마지막 값을 그대로 품은 채 바깥 스코프에 남아 있습니다. C나 자바에서 블록 안에 선언한 변수가 블록을 벗어나면 사라지는 것과는 정반대의 동작입니다.

이 성질을 알고 나면 유혹이 따라옵니다. 조건에 맞는 값을 찾을 때까지 루프를 돌리고 break로 빠져나온 뒤, 루프 변수를 그대로 결과처럼 쓰는 코드가 자연스럽게 나옵니다. 별도의 변수를 선언하지 않아도 되니 짧아 보이기까지 합니다. 하지만 이 코드는 두 가지 상황에서 무너집니다. 순회할 대상이 비어 있으면 변수 자체가 만들어지지 않아 NameError가 나고, 조건에 맞는 값이 하나도 없으면 마지막 원소가 마치 정답인 것처럼 반환됩니다. 앞의 경우는 그래도 요란하게 터지니 다행이지만, 뒤의 경우는 조용히 틀린 값을 흘려보냅니다.

💣 흔한 실수

def find_first_negative(numbers):
    for number in numbers:
        if number < 0:
            break

    return number  # 루프 변수를 결과로 재활용합니다


print(find_first_negative([1, 3, -5, 7]))  # -5  (여기까지는 잘 동작합니다)
print(find_first_negative([1, 3, 5, 7]))   # 7   (음수가 없는데 7을 반환합니다)
print(find_first_negative([]))             # UnboundLocalError

정상 경로에서만 맞고 나머지 두 경로에서는 각각 다른 방식으로 실패합니다. 특히 두 번째 호출은 예외도 없이 잘못된 값을 반환하기 때문에, 문제가 드러나는 시점은 이 함수를 호출한 곳이 아니라 한참 뒤의 어딘가입니다. 테스트 데이터에 음수가 늘 들어 있었다면 배포 후에야 발견될 종류의 버그입니다.

✅ 파이썬다운 방법

def find_first_negative(numbers):
    for number in numbers:
        if number < 0:
            return number  # 찾은 즉시 반환하므로 루프 밖에서 쓸 일이 없습니다

    return None  # 못 찾았을 때의 값을 명시합니다


print(find_first_negative([1, 3, -5, 7]))  # -5
print(find_first_negative([1, 3, 5, 7]))   # None
print(find_first_negative([]))             # None

세 경로가 모두 명확해졌습니다. "못 찾았다"는 상태를 None으로 못 박아 두었기 때문에 호출하는 쪽에서도 처리해야 할 경우의 수가 눈에 보입니다. 함수로 빼기 어려운 상황이라면 결과를 담을 변수를 루프 앞에서 미리 초기화하는 방식으로 같은 안전성을 얻을 수 있습니다.

참고로 이 누수는 루프에서만 일어납니다. 컴프리헨션(comprehension)과 제너레이터 식(generator expression)은 자기만의 스코프를 가지기 때문에 변수가 바깥으로 새지 않습니다.

for i in range(3):
    pass

print(i)  # 2  - 루프 변수는 밖에 남습니다

squares = [j * j for j in range(3)]
print(j)  # NameError - 컴프리헨션 변수는 남지 않습니다

📎 기억할 것

  • for 루프 변수는 루프가 끝난 뒤에도 마지막 값을 유지한 채 바깥 스코프에 남습니다.
  • 순회 대상이 비어 있으면 변수가 아예 만들어지지 않아 실행 시점에 예외가 발생합니다.
  • break 없이 루프가 끝난 경우와 조건을 만족한 경우를 루프 변수만으로는 구분할 수 없습니다.
  • 탐색 결과가 필요하다면 함수로 감싸 즉시 반환하거나 별도의 변수를 미리 초기화합니다.
  • 컴프리헨션과 제너레이터 식의 변수는 자체 스코프를 가지므로 바깥으로 새지 않습니다.

🛠 실무에서 어디 쓸까

설정 파일에서 조건에 맞는 첫 항목을 고르는 코드가 대표적인 위험 지대입니다. 여러 데이터베이스 후보 중 살아 있는 첫 연결을 선택하는 로직 같은 것이 여기에 해당하며, select_healthy_host()처럼 이름을 붙여 함수로 빼고 실패 시 None을 반환하도록 만들면 호출부에서 예외 처리를 빠뜨릴 여지가 줄어듭니다.

리뷰 관점에서도 확인하기 쉬운 신호입니다. 루프 블록이 끝난 뒤 들여쓰기가 풀린 자리에서 루프 변수 이름이 다시 등장한다면 일단 의심해 볼 만합니다. 그 자리에 "리스트가 비어 있으면 어떻게 됩니까?"라는 질문 하나만 던져도 대부분 문제가 드러납니다.

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

0개의 댓글