Python IndexError 방지 및 해결 가이드

IndexError: list index out of range 이해하기

Python 개발 시 자주 마주치는 IndexError: list index out of range는 시퀀스 자료형의 범위를 벗어난 위치에 접근할 때 발생합니다. 리스트의 인덱스는 0부터 시작하며, 마지막 요소의 인덱스는 len(리스트) - 1입니다.

오류 발생 예시

data = ['apple', 'banana', 'cherry']
print(data[5])  # IndexError 발생

위 코드에서 data는 3개 요소만 존재하므로 유효한 인덱스는 0, 1, 2입니다. 인덱스 5에 접근하면 예외가 발생합니다.

주요 발생 원인

1. 하드코딩된 인덱스 사용

코드에 고정된 숫자를 인덱스로 사용하면 데이터 변경 시 오류가 발생할 수 있습니다.

records = fetch_data()  # 반환 데이터 수가 변동 가능
print(records[10])      # 데이터가 10개 미만이면 오류

2. 잘못된 음수 인덱스

음수 인덱스는 끝에서부터 역순으로 접근합니다. -len(리스트)보다 작은 값은 오류를 유발합니다.

items = [10, 20]
print(items[-5])  # -2까지만 유효, -5는 오류

3. 반복문에서의 범위 초과

while 루프나 잘못된 range 설정으로 인덱스가 벗어날 수 있습니다.

values = [1, 2, 3]
idx = 0
while idx <= len(values):  # 등호로 인해 마지막에서 오류
    print(values[idx])
    idx += 1

4. 런타임 중 리스트 수정

순회 중 요소 삭제로 인해 인덱스가 무효화되는 경우입니다.

nums = [1, 2, 3, 4]
for i in range(len(nums)):
    if nums[i] % 2 == 0:
        del nums[i]  # 삭제 후 인덱스 꼬임

방어적 코딩 기법

방법 1: 조건문으로 사전 검증

def fetch_element(container, position):
    length = len(container)
    if not container:
        return None
    if -length <= position < length:
        return container[position]
    return None

matrix = [[1, 2], [3, 4]]
result = fetch_element(matrix, 5)  # 안전하게 None 반환

방법 2: 예외 처리 구조 활용

def process_items(data_source):
    try:
        target = data_source[2]['detail']
    except IndexError:
        target = "기본값"
    except (KeyError, TypeError):
        target = "구조 불일치"
    return target

방법 3: 안전한 반복 패턴

인덱스 대신 직접 순회하거나 enumerate를 활용합니다.

# 권장 방식 1: 직접 순회
for entry in collection:
    print(entry)

# 권장 방식 2: enumerate 활용
for offset, entry in enumerate(collection):
    print(f"{offset}: {entry}")

# 권장 방식 3: zip으로 병렬 처리
names = ['kim', 'lee']
scores = [85, 92]
for n, s in zip(names, scores):
    print(f"{n}: {s}")

방법 4: 슬라이싱으로 안전 접근

슬라이싱은 범위를 벗어나도 빈 결과를 반환하여 예외를 방지합니다.

sequence = [10, 20, 30]

# 안전한 부분 추출
subset = sequence[5:10]   # 빈 리스트 [], 오류 없음
tail = sequence[-10:]      # 전체 리스트 반환

방법 5: 수정 중인 컬렉션 처리

필터링 시 리스트 컴프리헨션이나 filter를 사용하여 원본을 직접 변경하지 않습니다.

raw_data = [1, 2, 3, 4, 5, 6]

# 안전한 필터링
evens = [x for x in raw_data if x % 2 == 0]

# 또는
from itertools import filterfalse
odds = list(filter(lambda x: x % 2, raw_data))

실전 적용 패턴

원형 큐 구현 시 인덱스 계산

class CircularBuffer:
    def __init__(self, capacity):
        self.capacity = capacity
        self.storage = [None] * capacity
        self.head = 0
        self.tail = 0
    
    def _normalize(self, index):
        return index % self.capacity
    
    def read(self, offset):
        actual = self._normalize(self.head + offset)
        if self.storage[actual] is None:
            raise ValueError("빈 슬롯 접근")
        return self.storage[actual]

다차원 배열 안전 접근

def safe_matrix_get(grid, row, col, default=None):
    if not isinstance(grid, list) or not grid:
        return default
    if row < 0 or row >= len(grid):
        return default
    if col < 0 or col >= len(grid[row]):
        return default
    return grid[row][col]

board = [
    ['X', 'O', 'X'],
    ['O', 'X', 'O']
]
cell = safe_matrix_get(board, 5, 1, '?')  # '?' 반환

디버깅 전략

상황확인 사항도구
예상치 못한 오류변수 값, 리스트 길이 출력print(), logging
복잡한 로직조건부 중단점 설정pdb, IDE debugger
정적 분석잠재적 인덱스 문제 탐지mypy, pyright
테스트 커버리지경계값 테스트pytest, unittest

요약

IndexError는 대부분 범위 검증 부재로 인해 발생합니다. 핵심 원칙은 다음과 같습니다:

  • 인덱스 사용 전 길이 확인 또는 예외 처리
  • 가능한 경우 인덱스 없는 순회 방식 채택
  • 슬라이싱을 활용한 안전한 부분 접근
  • 컬렉션 수정 시 별도 복사본 생성
  • 정적 타입 검사 도구 도입

태그: python IndexError list Exception Handling debugging

6월 15일 00:02에 게시됨