초보자에게 자료구조는 종종 이름 싸움처럼 느껴진다. 배열, 리스트, 맵, 딕셔너리라는 단어가 쏟아지면 무엇이 다른지보다 무엇이 더 어려운지가 먼저 들어온다.
하지만 입문 단계에서 중요한 것은 내부 구현보다 나중에 이 값을 어떤 방식으로 꺼내고 싶은가다.
이름이 언어마다 다른 것도 혼란을 키운다. 같은 개념을 파이썬은 딕셔너리, 자바는 맵, 자바스크립트는 객체라고 부른다. 이름을 외우려 들면 끝이 없고, 쓰임을 잡으면 이름은 따라온다.
순서로 꺼낼지, 이름으로 꺼낼지
리스트나 배열은 순서대로 값을 다루고 싶을 때 편하다. 딕셔너리나 맵은 이름표를 붙여 꺼내고 싶을 때 편하다. 초보자는 이 구분만 명확해도 데이터 구조 선택이 많이 쉬워진다.
결국 자료구조는 저장보다 조회 방식과 더 가깝다. 나중에 이 값을 어떻게 찾을까를 먼저 생각하면 된다.
DATA
fruits = ["apple", "banana", "orange"]
user = {"name": "mina", "level": "beginner"}
fruits는 순서로, user는 이름표로 값을 꺼내는 구조다.
잘못 고르면 코드 모양이 먼저 이상해진다
구조를 잘못 고르면 성능이 아니라 코드의 모양에서 먼저 신호가 온다. 학생 이름으로 점수를 찾는 상황을 리스트로 짜면 이렇게 된다.
리스트로 이름을 찾을 때
students = [["mina", 82], ["jun", 91], ["sora", 77]]
def score_of(name):
for row in students:
if row[0] == name:
return row[1]
return None
이름을 찾을 때마다 처음부터 훑는다. row[0], row[1]이 무엇인지도 코드만 봐서는 모른다.
같은 데이터를 딕셔너리로 바꾸면 함수 자체가 사라진다.
딕셔너리로 바꾼 뒤
students = {"mina": 82, "jun": 91, "sora": 77}
students["mina"] # 82
students.get("taeho") # None
찾는 코드가 없어졌다. 구조가 질문에 맞으면 코드가 짧아진다.
여기서 배울 것은 딕셔너리가 더 좋다는 결론이 아니다. 반복문이 자꾸 길어지고 인덱스 번호가 코드에 흩어지기 시작하면, 그것이 구조가 질문과 안 맞는다는 신호라는 점이다. 코드를 더 잘 쓰려고 애쓰기 전에 데이터의 모양을 먼저 의심해 보는 편이 빠르다.
이 값을 나중에 몇 번째로 찾을 것인가, 아니면 무엇이라는 이름으로 찾을 것인가. 앞이면 리스트, 뒤면 딕셔너리입니다.
처음에는 이렇게 구분하면 된다
- 순서가 중요하면 리스트 또는 배열
- 이름표가 중요하면 딕셔너리 또는 맵
- 중복 없이 있는지만 확인할 거면 집합
- 인덱스 숫자가 코드에 자꾸 등장하면 구조를 다시 본다.
- 처음엔 복잡한 구조보다 단순한 구조 두세 개를 자주 써 본다.
섞어 쓰는 것이 보통이다
실제 데이터는 둘 중 하나로 딱 떨어지지 않는다. 대부분은 리스트 안에 딕셔너리가 들어 있는 모양이다. 순서도 있어야 하고 각 항목에 이름표도 필요하기 때문이다. API에서 받아 오는 데이터가 거의 이 모양인 것도 같은 이유다.
가장 흔한 조합
records = [
{"name": "mina", "score": 82},
{"name": "jun", "score": 91},
]
for r in records:
print(r["name"], r["score"])
바깥은 순서, 안쪽은 이름표. 두 성질이 모두 필요할 때의 기본형이다.
이 구조가 눈에 익으면 남의 코드를 읽는 속도가 달라진다. 대괄호가 열리면 순서, 중괄호가 열리면 이름표라는 것만으로 데이터의 성격이 절반은 읽히기 때문이다.
중첩이 세 겹, 네 겹으로 깊어지면 그때부터는 눈으로 따라가기 어려워진다. 그런 데이터를 만났을 때는 한 번에 이해하려 하지 말고, 바깥부터 한 겹씩 벗겨 가며 출력해 보는 편이 빠르다. 전체를 한 번 찍고, 첫 항목만 찍고, 그 안의 키 하나를 찍는 순서다. 세 번 찍으면 대개 구조가 보인다.
키를 무엇으로 삼을지가 설계다
딕셔너리를 쓰기로 정했다면 다음 결정이 남는다. 무엇을 키로 삼을 것인가. 이 선택이 나중에 코드의 절반을 좌우한다. 앞의 예에서 이름을 키로 삼았는데, 동명이인이 생기는 순간 한 명의 점수가 조용히 사라진다. 에러도 나지 않는다. 같은 키에 다시 넣으면 덮어쓰기 때문이다.
그래서 키를 고를 때는 두 가지를 확인한다. 중복되지 않는가, 그리고 바뀌지 않는가. 학번이나 아이디처럼 사람이 붙인 고유 번호가 안전하고, 이름이나 이메일처럼 바뀔 수 있는 값은 위험하다. 이 판단은 자료구조 문법과 무관해 보이지만, 실제로 데이터를 다루면서 가장 자주 후회하는 지점이다.
키가 마땅치 않다면 그것 자체가 신호다. 이 데이터는 이름표로 찾는 성격이 아니라 순서대로 훑는 성격이라는 뜻이고, 그러면 리스트로 돌아가는 편이 맞다. 억지로 키를 만들어 붙인 딕셔너리는 대개 나중에 다시 리스트로 풀리게 된다.
이 구분이 흐려지는 지점
다만 순서 대 이름표라는 이분법이 모든 상황에 통하지는 않는다. 언어마다 사정이 다르기 때문이다. 자바스크립트에서는 배열도 사실 객체이고, 파이썬 딕셔너리는 입력한 순서를 유지한다. 그래서 이름으로 꺼내면서 순서도 그대로 쓰는 코드가 자연스럽게 나온다. 이 구분은 개념을 잡는 도구지 언어의 규칙이 아니다.
또 하나, 구분에 너무 집착해서 미리 최적화하려는 것도 초보 단계에서는 손해다. 항목이 열 개인 목록에서는 리스트를 훑든 딕셔너리로 찾든 체감 차이가 없다. 데이터가 커지고 코드가 실제로 느려졌을 때 바꿔도 늦지 않다. 지금 단계에서 중요한 것은 읽기 쉬운 구조를 고르는 것이다.
정리하면 이 글의 구분은 규칙이 아니라 출발점이다. 익숙해지고 나면 언어별 차이와 성능 특성이 그 위에 얹히는데, 그건 필요해졌을 때 배우면 된다. 지금 필요한 것은 데이터를 앞에 두고 나는 이걸 어떻게 꺼낼 것인가를 먼저 묻는 습관 하나다.
C나 자바의 배열은 크기가 고정이고, 파이썬 리스트는 크기가 늘어납니다. 둘 다 배열이라고 부르는 글이 많으니, 언어를 옮길 때는 이름이 같아도 성질을 다시 확인하십시오.
자료구조 입문은 정의 암기보다, 값을 어떻게 찾고 싶은지 스스로 묻는 데서 시작한다.
더 볼 것
언어마다 이 구조들의 이름과 문법이 어떻게 갈리는지는 리스트 비교와 맵 비교에 나란히 정리해 두었습니다. 한 언어 안에서 더 깊이 보려면 파이썬 컬렉션 가이드가 다음 단계로 적당합니다.
이 글의 포인트
- 초보자는 내부 구현보다 조회 방식 차이를 먼저 익히는 편이 낫다.
- 순서 중심 데이터와 이름표 중심 데이터를 구분하면 이해가 빨라진다.
- 구조가 안 맞으면 성능보다 코드 모양에서 먼저 신호가 온다.
- 실무 데이터는 대개 리스트 안에 딕셔너리가 든 모양이다.
- 단순한 구조를 자주 써 보는 것이 복잡한 구조를 빨리 외우는 것보다 중요하다.