리스트 컴프리헨션은 반복·필터·변환을 한 줄에 표현할 수 있어 편하지만, 한 줄이라는 이유로 모든 로직을 넣으면 나중에 읽는 사람이 멈춘다. 단순한 데이터 변환에만 쓰는 기준이 좋다.
names = ["mina", "", "jun"]
upper_names = [name.upper() for name in names if name]표현식은 결과 값, for는 반복, 끝의 if는 포함 조건을 뜻한다. 위 코드는 비어 있지 않은 이름만 대문자로 새 리스트에 담는다.
assert upper_names == ["MINA", "JUN"]
assert names == ["mina", "", "jun"]처음 읽을 때 순서가 헷갈린다면 같은 코드를 일반 반복문으로 풀어 보자. for로 원소를 받고, if로 남길지 결정한 다음, 표현식의 값을 결과에 추가한다.
result = []
for name in names:
if name:
result.append(name.upper())
assert result == upper_names원본은 바뀌지 않는다
컴프리헨션은 새 리스트를 만든다. 원본을 유지해야 하는 화면 데이터나 함수 입력을 다룰 때 유용하다. 단, 표현식 안에서 객체 속성을 수정하면 새 리스트를 만들더라도 원본 객체는 바뀔 수 있으므로 부수 효과를 넣지 말자.
조건과 예외 처리가 길면 for문이 낫다
검증·로그·예외 처리·여러 단계 분기가 필요하면 일반 for문이 더 명확하다. 성능을 이유로 난해한 한 줄을 만들기 전에 데이터 크기와 병목을 측정하자. 대부분의 코드에서 읽기 쉬운 흐름이 더 큰 이득이다.
예를 들어 입력에 문자열이 아닌 값이 섞이면 name.upper()에서 실패한다. 이때 무조건 예외를 삼키거나 str(name)으로 바꾸면 잘못된 입력이 통과한다. 허용할 입력 타입을 먼저 정하고, 오류가 난 원소를 재현할 수 있는 작은 목록으로 테스트하자. 대량 데이터를 다룬다면 모든 결과를 리스트로 한꺼번에 만드는 비용도 고려해야 한다.
한 줄에서 의미가 흐려지면 일반 반복문으로 되돌린다
records = [" api ", "", "DB", None]
normalized = []
for record in records:
if not isinstance(record, str):
continue
name = record.strip()
if name:
normalized.append(name.upper())
assert normalized == ["API", "DB"]타입 검사, 공백 제거, 빈 값 제외처럼 판단이 여러 단계면 일반 반복문이 중간 상태를 보여 주기 쉽다. 이것을 한 줄로 줄일 수는 있어도 코드 리뷰에서 각 조건의 순서와 누락된 입력의 처리 방식이 바로 보이지 않으면 이득이 작다. 입력 목록과 기대 결과를 나란히 두고, 조건·변환이 한 번에 읽히는 경우에만 컴프리헨션을 쓴다.
큰 입력은 새 리스트의 메모리 비용도 확인한다
리스트 컴프리헨션은 결과 전체를 메모리에 만든다. 대량 데이터를 한 번만 순회해 파일이나 네트워크로 보낸다면 generator 표현식이나 배치 처리가 더 맞을 수 있다. 반대로 결과를 여러 번 순회하거나 길이·인덱스가 필요하다면 리스트가 단순하다.
성능 판단은 문법 길이로 하지 않는다. 대표 크기의 입력으로 처리 시간과 최대 메모리를 측정하고, 다음 소비자가 전체 결과를 필요로 하는지 먼저 본다. 컴프리헨션 안에서 출력·저장 같은 부수 효과를 내면 실패 지점과 재시도 범위가 불분명해지므로 값 변환과 외부 작업을 분리한다.
핵심 요약
리스트 컴프리헨션은 '각 값을 변환해 새 목록을 만든다'는 단순한 작업에 적합하다. 변환식·반복·필터의 순서를 읽을 수 있게 유지하고, 여러 분기나 부수 효과가 필요하면 for문으로 풀어 쓰자.

