주간 리뷰 (Week 1): 백지에서 다시 못 쓰면 아직 내 것이 아니다
Week 1에서 배운 Python 기초(딕셔너리, 클래스, 제너레이터)를 강의 노트나 예전 코드를 보지 않고 백지에서 재작성하는 복습 Day다. 새로운 개념은 배우지 않고, 대신 이번 주에 틀렸던 문제를 다시 풀면서 진짜로 이해했는지 점검한다. 여기서 막히는 부분이 곧 다음 주로 넘어가기 전에 메워야 할 구멍이다.
오늘의 목표
새로운 걸 배우는 날이 아니라 "확인하는 날"이다. Day 1~6 동안 강의 노트를 보면서, 혹은 에러 메시지에 도움을 받으면서 짰던 코드를 이제는 아무것도 안 보고 처음부터 다시 짤 수 있는지 확인한다. 목표는 "예전에 돌아갔던 코드가 남아있다"가 아니라 "지금 이 순간 백지에 다시 쓸 수 있다"이다. 이 둘은 완전히 다르다.
개념과 직관
오늘은 새 학습 항목이 없다. 대신 왜 "백지 재작성"이라는 방식을 쓰는지만 짚고 넘어가자.
비유를 들면, 자전거 타는 법을 유튜브로 보고 "아 저렇게 하는 거구나" 하고 이해하는 것과, 실제로 자전거에 올라타서 넘어지지 않고 페달을 밟는 것은 완전히 다른 능력이다. 코드도 똑같다. 남이 짠 코드를 읽고 "아 이렇게 하는 거구나" 하고 고개를 끄덕이는 것(재인식, recognition)과, 빈 화면 앞에서 문제만 보고 처음부터 타이핑해 내는 것(재생, recall)은 뇌 안에서 쓰이는 회로 자체가 다르다.
Week 1에서 배운 것 중 오늘 다시 짜볼 만한 세 가지를 짚어보면:
- 단어 빈도수 세기: 문자열을 받아서 단어별로 몇 번 나왔는지 딕셔너리(dictionary, 키-값 쌍으로 데이터를 저장하는 자료구조)에 담는 문제. 핵심은 "이미 딕셔너리에 그 단어가 있으면 +1, 없으면 새로 만들기"라는 조건 처리를 코드로 표현하는 것이다.
- 2D 벡터 클래스: 클래스(class, 데이터와 그 데이터를 다루는 함수를 하나로 묶은 설계도)로 x, y 좌표를 가진 벡터를 만들고, 덧셈이나 크기 계산 같은 동작을 메서드(method, 클래스 안에 정의된 함수)로 붙이는 문제. 핵심은
__init__으로 초기 상태를 만들고, 필요하면__add__같은 특수 메서드로+연산자를 직접 정의하는 것이다. - 파일 generator: 파일을 한 줄씩 읽어서 필요할 때마다 하나씩 내놓는 제너레이터(generator, 값을 한 번에 다 만들지 않고 요청할 때마다 하나씩 만들어주는 함수) 문제. 핵심은
return대신yield를 쓰면 함수가 끝나지 않고 "잠깐 멈췄다가" 다음 호출 때 이어서 실행된다는 점이다.
코드로 직접 해보기
아래 순서를 그대로 따라가자. 중요한 건 "먼저 문제만 보고 짜본 다음, 그 다음에 예전 코드와 비교"하는 순서다.
1단계 — 문제만 다시 적어본다. 코드 없이, 아래 세 문제를 노트에 한 줄씩 적는다.
- 문자열 하나를 받아서
{단어: 등장 횟수}형태의 딕셔너리를 반환하는 함수를 만들어라. - x, y 좌표를 가진
Vector2D클래스를 만들고, 두 벡터를 더할 수 있게 하고, 벡터의 길이(크기)를 구하는 메서드를 넣어라. - 파일 경로를 받아서 한 줄씩 읽어 반환하는 제너레이터 함수를 만들어라. 단, 빈 줄은 건너뛰어라.
2단계 — 아무것도 안 보고 짠다. 막혀도 5분은 버텨본다. 그 5분 동안 "어디서 막혔는지"를 기록해두는 게 핵심이다.
# 1. 단어 빈도수 세기
def word_count(text):
counts = {}
for word in text.lower().split():
if word in counts:
counts[word] += 1
else:
counts[word] = 1
return counts
print(word_count("apple banana apple orange banana apple"))
# {'apple': 3, 'banana': 2, 'orange': 1}# 2. 2D 벡터 클래스
import math
class Vector2D:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector2D(self.x + other.x, self.y + other.y)
def magnitude(self):
return math.sqrt(self.x ** 2 + self.y ** 2)
def __repr__(self):
return f"Vector2D({self.x}, {self.y})"
v1 = Vector2D(3, 4)
v2 = Vector2D(1, 2)
print(v1 + v2) # Vector2D(4, 6)
print(v1.magnitude()) # 5.0# 3. 파일 generator
def read_lines(path):
with open(path, "r") as f:
for line in f:
stripped = line.strip()
if stripped: # 빈 줄 건너뛰기
yield stripped
# 사용 예시 (테스트용 파일을 미리 만들어야 함)
# for line in read_lines("sample.txt"):
# print(line)3단계 — 예전 코드와 비교한다. Day 1~6 노트나 파일을 열어서, 오늘 짠 것과 뭐가 다른지 확인한다. 다른 부분이 있다면 "그때는 알았는데 지금은 까먹은 것"인지, "예전 방식보다 지금이 더 나은 것"인지 구분해서 적어둔다.
오늘의 실험
실험은 없다. 대신 이번 주에 틀렸던 알고리즘 문제를 전부 다시 푸는 게 오늘의 실질적인 작업이다. 아래 항목을 정리해서 기록하자.
- 이번 주 알고리즘 문제 중 틀렸거나 못 풀었던 것을 목록으로 뽑는다.
- 다시 풀 때는 예전 풀이를 보지 않고 처음부터 다시 접근한다.
- 두 번째로도 막힌 문제가 있다면, 그건 "아직 개념이 안 잡힌 것"이니 표시해두고 다음 주 시작 전에 별도로 복습한다.
왜 이걸 배우나 (LLM / Post-training 연결)
오늘 다시 짠 세 가지는 전부 나중에 모습을 바꿔서 다시 나온다. 딕셔너리로 단어를 세는 감각은 나중에 토크나이저(tokenizer)의 단어 사전(vocabulary)을 만들 때 그대로 쓰인다. 클래스로 상태와 동작을 묶는 연습은 나중에 신경망 모델을 nn.Module 같은 구조로 짤 때 기본이 된다. 제너레이터로 "한 번에 다 만들지 않고 필요할 때 하나씩 내놓는" 감각은, LLM 학습용 데이터셋이 메모리에 다 못 올라갈 만큼 클 때 데이터를 조금씩 읽어오는 방식으로 다시 등장한다. 지금 기초를 몸에 붙여두면 나중에 "저게 왜 저렇게 생겼지" 하고 헷갈리는 시간이 확 줄어든다.
초보자가 막히는 지점
- "어차피 예전 코드 있는데 왜 다시 짜야 하지"라는 생각. 코드가 저장되어 있는 것과 내가 그걸 다시 만들어낼 수 있는 것은 다르다. 재인식과 재생의 차이를 몸으로 느끼는 게 오늘의 핵심이다.
- `__add__`, `__repr__` 같은 특수 메서드 이름을 까먹는 것. 이름이 안 외워지면 왜 이 이름이어야 하는지(파이썬이
+연산자를 볼 때 내부적으로__add__를 찾아서 호출한다는 규칙)를 다시 짚고 넘어가자. 통째로 암기하기보다 규칙을 이해하는 게 오래 간다. - generator와 일반 함수의 차이가 여전히 헷갈리는 것.
yield가 하나라도 들어있으면 그 함수는 호출해도 바로 실행되지 않고 제너레이터 객체만 돌려준다는 점을 헷갈리기 쉽다.for문으로 돌리거나next()를 직접 호출해봐야 실제로 언제 실행되는지 감이 잡힌다. - 딕셔너리 키 존재 확인을 매번 `if key in dict`로 짜다가 지치는 것. 오늘은 원리를 익히는 게 목적이라 이 방식으로 충분하지만, 나중에
dict.get()이나collections.Counter같은 더 간결한 도구를 만나면 "아 이게 내가 매번 손으로 짜던 그 패턴이구나" 하고 알아볼 수 있어야 한다.
알고리즘 (병행 1시간)
이번 주 동안 풀었던 문제 중 틀렸던 것, 혹은 시간 초과나 막혀서 풀이를 봤던 것을 전부 골라서 다시 푼다. 새 문제를 풀지 않는다. 각 문제마다 아래 세 가지를 반드시 기록한다.
- 접근: 이번엔 어떤 방식으로 풀었는지 한두 줄로.
- 시간복잡도: 대략 어느 정도인지 (예: 반복문이 몇 겹인지 세어보는 정도로 충분하다).
- 틀린 이유: 처음에 왜 못 풀었는지 - 조건을 잘못 읽었는지, 자료구조 선택이 잘못됐는지, 아니면 파이썬 문법 자체를 헷갈렸는지. 이 한 줄이 다음에 같은 실수를 막아준다.