← 파이썬 강의 목록으로
⚙️
중급 (Intermediate)
datetime · random · pathlib · collections · try/except/else/finally · raise · 사용자 정의 예외 · with open · csv · json · encoding utf-8

4~6주차 — 표준 라이브러리 · 예외 처리 · 파일 입출력

추가 설치 없이 import만으로 쓸 수 있는 4가지 핵심 표준 모듈 — 날짜·시간(datetime), 무작위(random), 경로(pathlib), Counter/defaultdict(collections). try/except의 4개 절(try·except·else·finally), 여러 예외 분기 처리, raise로 의도적 발생, Exception 상속으로 사용자 정의 예외, 예외 체인을 익힙니다. with open 으로 안전하게 텍스트 파일 읽고 쓰기, csv 모듈로 표 형식 데이터 처리, json 으로 구조화 데이터 직렬화·역직렬화를 익힙니다.

datetimerandompathlibcollectionsexceptiontryraisecustomfilecsvjsonencoding
소요 시간
⏱ 2시간 + 2시간 + 2시간
난이도
📊 중급
선수 조건
🎯 3주차
결과물
D-Day·로또·파일 통계 같은 도구 작성 / 안전하게 회복 가능한 입력·파일·네트워크 처리 / 메모장·CSV 통계·JSON 영구 저장 도구

이 강의에서 배우는 것

  • 1datetime 으로 날짜·시간을 다룬다
  • 2random 으로 무작위 값·샘플링
  • 3pathlib 으로 파일·폴더 경로
  • 4collections 의 Counter, defaultdict
  • 5try/except/else/finally 의 각 절을 안다
  • 6예외 종류를 구분해 처리한다
  • 7사용자 정의 예외를 만든다
  • 8예외를 의도적으로 발생시킨다(raise)
  • 9텍스트 파일을 with open 으로 안전하게 다룬다
  • 10CSV 파일을 csv 모듈로 읽고 쓴다
  • 11JSON으로 구조화된 데이터를 저장·로드한다

4주차 — 표준 라이브러리

추가 설치 없이 import만으로 쓸 수 있는 4가지 핵심 표준 모듈 — 날짜·시간(datetime), 무작위(random), 경로(pathlib), Counter/defaultdict(collections).

1. datetime

python
from datetime import datetime, date, timedelta

now = datetime.now()
print(now)
print(now.strftime("%Y-%m-%d %H:%M"))

today = date.today()
birthday = date(2000, 5, 9)
days_lived = (today - birthday).days

next_week = now + timedelta(days=7)

2. random

python
import random

print(random.random())                  # 0.0 ~ 1.0
print(random.randint(1, 100))           # 1 ~ 100 정수
print(random.choice(["가위","바위","보"]))
print(random.sample(range(1, 46), 6))   # 로또
random.shuffle(my_list)                 # 제자리 섞기

3. pathlib

문자열보다 깔끔한 경로 다루기.

python
from pathlib import Path

p = Path("data") / "report.txt"        # data/report.txt
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text("hello", encoding="utf-8")
print(p.read_text(encoding="utf-8"))

print(p.exists(), p.suffix, p.name, p.stem)

# 폴더 내 파일 목록
for f in Path(".").glob("*.py"):
    print(f.name, f.stat().st_size)

4. collections

python
from collections import Counter, defaultdict, deque

# Counter — 빈도 계산 한 줄
text = "to be or not to be"
print(Counter(text.split()))    # {'to': 2, 'be': 2, ...}
print(Counter(text.split()).most_common(2))

# defaultdict — 키가 없으면 기본값
groups = defaultdict(list)
for name, age in [("A", 20), ("B", 25), ("C", 20)]:
    groups[age].append(name)
print(dict(groups))

# deque — 양방향 큐
dq = deque([1, 2, 3])
dq.appendleft(0)
dq.append(4)
print(dq)

5. 자주 하는 실수

  1. datetime vs date — datetime 은 시간까지, date 는 날짜만
  2. random.seed() 모름 — 같은 결과 재현하려면 random.seed(42) 처럼 시드 설정
  3. 경로 문자열 더하기 — "data" + "/" + "x" 보다 Path("data") / "x" 가 안전

6. FAQ

Q1. time 과 datetime 의 차이?

time 은 저수준(타임스탬프), datetime 은 고수준(객체). 보통 datetime 이면 충분.

Q2. os.path 와 pathlib 중 뭐?

신규 코드는 pathlib 권장. 더 OOP스럽고 가독성 좋음.

5주차 — 예외 처리

try/except의 4개 절(try·except·else·finally), 여러 예외 분기 처리, raise로 의도적 발생, Exception 상속으로 사용자 정의 예외, 예외 체인을 익힙니다.

1. 예외란

실행 중 발생하는 오류. 처리하지 않으면 프로그램이 종료됨.

python
print(10 / 0)         # ZeroDivisionError
print(int("abc"))     # ValueError
print([1,2][5])       # IndexError

2. try / except

python
try:
    n = int(input("숫자: "))
    print(10 / n)
except ValueError:
    print("숫자가 아닙니다")
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다")
except Exception as e:
    print(f"기타 에러: {e}")

3. else / finally

  • else: try가 예외 없이 끝났을 때
  • finally: 예외 여부와 무관하게 항상 실행 (정리 작업)
python
try:
    f = open("data.txt")
except FileNotFoundError:
    print("파일 없음")
else:
    print(f.read())
    f.close()
finally:
    print("정리 완료")

4. raise

예외를 의도적으로 발생.

python
def withdraw(balance, amount):
    if amount > balance:
        raise ValueError("잔액 부족")
    return balance - amount

5. 사용자 정의 예외

Exception 상속.

python
class InsufficientFundsError(Exception):
    pass

def transfer(account, amount):
    if account.balance < amount:
        raise InsufficientFundsError(f"잔액 {account.balance} < 요청 {amount}")

6. 예외 체인

기존 예외를 감싸서 새 예외 발생.

python
try:
    int("abc")
except ValueError as e:
    raise RuntimeError("입력 처리 실패") from e

7. 자주 하는 실수

  1. 너무 광범위한 except (except: pass) — 위험. 가능한 좁은 예외 클래스 명시
  2. except ... as e: pass — 에러를 숨김. 최소한 로그라도
  3. finally 에서 return — try의 return 값을 덮어쓸 수 있음
  4. except 순서 — 좁은 예외(FileNotFoundError) 먼저, 넓은 것(OSError) 뒤에

8. FAQ

Q1. 모든 함수에 try를 둘러싸야 하나요?

아닙니다. 회복할 수 있는 곳에서만. 보통 사용자 입력, 파일 I/O, 네트워크 호출.

Q2. Exception 과 BaseException 의 차이?

BaseException 이 최상위. KeyboardInterrupt, SystemExit 도 포함. 일반적으로는 Exception 까지만 잡음.

Q3. 예외 vs 반환값 (None / -1)?

예외는 "정상 흐름이 아님" 을 명확히. 검색에서 "없음" 같은 정상 상황은 None 권장.

6주차 — 파일 입출력 (텍스트·CSV·JSON)

with open 으로 안전하게 텍스트 파일 읽고 쓰기, csv 모듈로 표 형식 데이터 처리, json 으로 구조화 데이터 직렬화·역직렬화를 익힙니다.

1. 텍스트 파일 — with 문

파일을 자동으로 닫아줍니다.

python
# 쓰기
with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("첫 줄\n")
    f.writelines(["A\n", "B\n", "C\n"])

# 읽기
with open("notes.txt", encoding="utf-8") as f:
    content = f.read()      # 전체 한 번에
    # 또는
    for line in f:           # 한 줄씩
        print(line.rstrip())

모드

모드의미
r읽기 (기본)
w쓰기 (기존 내용 삭제)
a추가
r+읽기/쓰기
b바이너리 (예: rb, wb)
⚠️

항상 encoding="utf-8" 명시 (한글 안전).

2. CSV

쉼표로 구분된 표 형식.

python
import csv

# 쓰기
with open("scores.csv", "w", encoding="utf-8", newline="") as f:
    w = csv.writer(f)
    w.writerow(["name", "score"])
    w.writerows([["Alice", 92], ["Bob", 78]])

# 읽기
with open("scores.csv", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        print(row["name"], row["score"])
⚠️

newline="" 빠뜨리면 빈 줄 생김 (Windows).

3. JSON

구조화된 데이터를 텍스트로 직렬화.

python
import json

data = {
    "name": "홍길동",
    "skills": ["Python", "SQL"],
    "active": True,
}

# 저장
with open("profile.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

# 로드
with open("profile.json", encoding="utf-8") as f:
    loaded = json.load(f)
print(loaded["skills"])
  • ensure_ascii=False — 한글을 그대로
  • indent=2 — 사람이 읽기 쉽게 들여쓰기

4. 자주 하는 실수

  1. open 후 close 누락 — with 사용으로 자동 처리
  2. encoding 누락 — Windows에서 한글 깨짐
  3. json.dump vs json.dumps — dump 는 파일에, dumps 는 문자열로
  4. CSV newline="" 누락 — Windows에서 빈 줄 추가됨
  5. JSON으로 저장 안 되는 타입 — datetime, 사용자 클래스. 변환 필요

5. FAQ

Q1. CSV 와 JSON 중 뭘 쓰나요?

표 형태 데이터는 CSV, 중첩·구조화 데이터는 JSON.

Q2. 큰 파일도 한 번에 읽어도 되나요?

메모리에 올라가므로 GB 단위는 위험. 라인 단위 순회 또는 제너레이터 사용.

Q3. pickle 은 뭔가요?

파이썬 객체를 그대로 저장. 단, 다른 언어에서 못 읽고 보안 위험. JSON 권장.

💻 예제 (examples)

실제로 실행해 결과를 확인할 수 있는 예제 코드입니다.

01_datetime.py— datetime / timedelta
CODE
from datetime import datetime, date, timedelta

now = datetime.now()
print(now.strftime("%Y-%m-%d %H:%M"))

today = date.today()
print("오늘:", today)
print("일주일 후:", today + timedelta(days=7))

birthday = date(2000, 5, 9)
print(f"태어난 후 {(today - birthday).days}일")
▶ 실행 결과
2026-05-10 14:32
오늘: 2026-05-10
일주일 후: 2026-05-17
태어난 후 9498일
02_random.py— random — 5가지 패턴
CODE
import random
random.seed(42)   # 재현 가능

print(random.random())
print(random.randint(1, 100))
print(random.choice(["가위", "바위", "보"]))
print(random.sample(range(1, 46), 6))

xs = [1, 2, 3, 4, 5]
random.shuffle(xs)
print(xs)
▶ 실행 결과
0.6394267984578837
82
바위
[8, 14, 21, 27, 36, 41]
[3, 1, 4, 5, 2]
03_pathlib.py— pathlib — 경로/폴더/파일 한 번에
CODE
from pathlib import Path

p = Path("data") / "hello.txt"
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text("Hello!", encoding="utf-8")
print(p.read_text(encoding="utf-8"))
print(p.exists(), p.suffix, p.name, p.stem)

# 현재 폴더의 .py 파일 목록
for f in Path(".").glob("*.py"):
    print(f.name)
▶ 실행 결과
Hello!
True .txt hello.txt hello
01_datetime.py
02_random.py
03_pathlib.py
04_collections.py
04_collections.py— Counter / defaultdict / deque
CODE
from collections import Counter, defaultdict, deque

text = "to be or not to be that is the question to be"
c = Counter(text.split())
print(c.most_common(3))

groups = defaultdict(list)
for name, age in [("A", 20), ("B", 25), ("C", 20)]:
    groups[age].append(name)
print(dict(groups))

dq = deque([1, 2, 3])
dq.appendleft(0)
dq.append(4)
print(dq)
▶ 실행 결과
[('to', 3), ('be', 3), ('or', 1)]
{20: ['A', 'C'], 25: ['B']}
deque([0, 1, 2, 3, 4])
01_try_except.py— 기본 try/except + 여러 예외
CODE
try:
    n = int(input("숫자: "))
    print(10 / n)
except ValueError:
    print("숫자가 아닙니다")
except ZeroDivisionError:
    print("0으로 나눌 수 없습니다")
except Exception as e:
    print(f"기타 에러: {e}")
▶ 실행 결과
숫자: abc
숫자가 아닙니다

숫자: 0
0으로 나눌 수 없습니다

숫자: 5
2.0
02_else_finally.py— else, finally
CODE
try:
    f = open("hello.txt", encoding="utf-8")
except FileNotFoundError:
    print("파일 없음")
else:
    print(f.read())
    f.close()
finally:
    print("정리 완료")
▶ 실행 결과
# 파일 있을 때
Hello, World!
정리 완료

# 파일 없을 때
파일 없음
정리 완료
03_raise.py— raise + 입력 검증
CODE
def withdraw(balance, amount):
    if amount <= 0:
        raise ValueError("금액은 양수여야 합니다")
    if amount > balance:
        raise ValueError("잔액 부족")
    return balance - amount

try:
    print(withdraw(1000, 500))
    print(withdraw(500, 1000))
except ValueError as e:
    print("에러:", e)
▶ 실행 결과
500
에러: 잔액 부족
04_custom.py— 사용자 정의 예외
CODE
class InsufficientFundsError(Exception):
    pass

class Account:
    def __init__(self, balance):
        self.balance = balance

    def withdraw(self, amount):
        if amount > self.balance:
            raise InsufficientFundsError(
                f"잔액 {self.balance} < 요청 {amount}"
            )
        self.balance -= amount

try:
    a = Account(1000)
    a.withdraw(2000)
except InsufficientFundsError as e:
    print("거래 거부:", e)
▶ 실행 결과
거래 거부: 잔액 1000 < 요청 2000
01_text.py— 텍스트 파일 읽기/쓰기
CODE
# 쓰기
with open("notes.txt", "w", encoding="utf-8") as f:
    f.write("첫 줄\n")
    f.writelines(["A\n", "B\n", "C\n"])

# 읽기 (한 줄씩)
with open("notes.txt", encoding="utf-8") as f:
    for line in f:
        print(line.rstrip())
▶ 실행 결과
첫 줄
A
B
C
02_csv_write.py— CSV 쓰기
CODE
import csv

with open("scores.csv", "w", encoding="utf-8", newline="") as f:
    w = csv.writer(f)
    w.writerow(["name", "score"])
    w.writerows([
        ["Alice", 92],
        ["Bob", 78],
        ["Charlie", 88],
    ])

print("저장 완료")
▶ 실행 결과
저장 완료

# scores.csv 내용:
name,score
Alice,92
Bob,78
Charlie,88
03_csv_read.py— CSV 읽기 (DictReader)
CODE
import csv

with open("scores.csv", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        name = row["name"]
        score = int(row["score"])
        print(f"{name}: {score}")
▶ 실행 결과
Alice: 92
Bob: 78
Charlie: 88
04_json.py— JSON dump / load
CODE
import json

data = {
    "name": "홍길동",
    "skills": ["Python", "SQL"],
    "active": True,
}

with open("profile.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)

with open("profile.json", encoding="utf-8") as f:
    loaded = json.load(f)

print(loaded["name"])
print(loaded["skills"])
▶ 실행 결과
홍길동
['Python', 'SQL']

📝 과제 (exercises)

직접 풀어보고, 막힐 때 정답을 펼쳐 비교해보세요.

과제 1

D-Day 계산기

목표: 기념일까지의 D-Day를 계산해 출력.

요구사항
  • 기념일 입력 (YYYY-MM-DD)
  • 오늘과의 차이 계산 후 D-N 또는 D+N 형태로 출력
입출력 예시
기념일: 2026-12-25
D-229
▶정답 코드 펼치기 / 접기
SOLUTION
from datetime import date

target = date.fromisoformat(input("기념일: "))
today = date.today()
diff = (target - today).days

if diff > 0:
    print(f"D-{diff}")
elif diff < 0:
    print(f"D+{-diff}")
else:
    print("D-DAY!")
▶ 실행 결과
기념일: 2026-12-25
D-229
과제 2

폴더 내 .txt 파일 크기 출력

목표: 현재 폴더의 모든 .txt 파일 이름과 크기를 한 줄씩.

요구사항
  • pathlib.Path(".").glob("*.txt") 사용
  • f.stat().st_size 로 크기
입출력 예시
notes.txt: 1234 bytes
todo.txt: 567 bytes
▶정답 코드 펼치기 / 접기
SOLUTION
from pathlib import Path

for f in Path(".").glob("*.txt"):
    print(f"{f.name}: {f.stat().st_size} bytes")
▶ 실행 결과
notes.txt: 1234 bytes
todo.txt: 567 bytes
과제 3

단어 빈도 Top 5 (Counter)

목표: Counter로 가장 자주 등장한 단어 5개.

요구사항
  • text.split() + Counter + most_common(5)
입출력 예시
apple: 5
banana: 4
cherry: 3
date: 2
egg: 2
▶정답 코드 펼치기 / 접기
SOLUTION
from collections import Counter

text = "apple banana apple cherry banana apple cherry banana apple cherry apple date egg banana date egg"
words = text.split()

for word, n in Counter(words).most_common(5):
    print(f"{word}: {n}")
▶ 실행 결과
apple: 5
banana: 4
cherry: 3
date: 2
egg: 2
과제 4

안전한 정수 입력 (read_int)

목표: 잘못된 입력이면 다시 받는 read_int(prompt) 함수.

요구사항
  • ValueError 잡아서 다시 입력 받기
  • while True 로 정상 입력될 때까지 반복
입출력 예시
숫자: abc
숫자가 아닙니다. 다시 입력하세요.
숫자: 1.5
숫자가 아닙니다. 다시 입력하세요.
숫자: 42
입력값: 42
▶정답 코드 펼치기 / 접기
SOLUTION
def read_int(prompt):
    while True:
        try:
            return int(input(prompt))
        except ValueError:
            print("숫자가 아닙니다. 다시 입력하세요.")

n = read_int("숫자: ")
print(f"입력값: {n}")
▶ 실행 결과
숫자: abc
숫자가 아닙니다. 다시 입력하세요.
숫자: 42
입력값: 42
과제 5

사용자 정의 예외 InvalidScoreError

목표: 0~100 범위가 아닌 점수면 InvalidScoreError.

요구사항
  • InvalidScoreError(Exception) 정의
  • validate_score(s) 함수 — 잘못되면 raise
  • 테스트로 try/except 처리
입출력 예시
유효: 85
거부: 점수는 0~100 사이여야 합니다 (입력: 150)
▶정답 코드 펼치기 / 접기
SOLUTION
class InvalidScoreError(Exception):
    pass

def validate_score(s):
    if not (0 <= s <= 100):
        raise InvalidScoreError(f"점수는 0~100 사이여야 합니다 (입력: {s})")
    return s

for v in [85, 150]:
    try:
        validate_score(v)
        print(f"유효: {v}")
    except InvalidScoreError as e:
        print(f"거부: {e}")
▶ 실행 결과
유효: 85
거부: 점수는 0~100 사이여야 합니다 (입력: 150)
과제 6

파일 읽기 예외 처리

목표: 파일이 없으면 친절한 메시지, 인코딩 오류면 다른 메시지.

요구사항
  • FileNotFoundError → "파일 없음"
  • UnicodeDecodeError → "인코딩 오류"
  • 정상 — 첫 5줄만 출력
입출력 예시
파일 없음: data.txt
▶정답 코드 펼치기 / 접기
SOLUTION
def show_first_lines(path, n=5):
    try:
        with open(path, encoding="utf-8") as f:
            for i, line in enumerate(f):
                if i >= n:
                    break
                print(line.rstrip())
    except FileNotFoundError:
        print(f"파일 없음: {path}")
    except UnicodeDecodeError:
        print(f"인코딩 오류: {path} (UTF-8 아님)")

show_first_lines("data.txt")
▶ 실행 결과
파일 없음: data.txt
과제 7

메모장 (텍스트 추가)

목표: 사용자 입력을 한 줄씩 notes.txt 에 추가.

요구사항
  • "a" 모드로 열기
  • quit 입력 시 종료
  • 각 줄에 시간 prefix
입출력 예시
메모: 우유 사기
메모: 책 읽기
메모: quit

# notes.txt
[14:32] 우유 사기
[14:33] 책 읽기
▶정답 코드 펼치기 / 접기
SOLUTION
from datetime import datetime

while True:
    text = input("메모: ")
    if text == "quit":
        break
    with open("notes.txt", "a", encoding="utf-8") as f:
        ts = datetime.now().strftime("%H:%M")
        f.write(f"[{ts}] {text}\n")
▶ 실행 결과
메모: 우유 사기
메모: 책 읽기
메모: quit

# notes.txt
[14:32] 우유 사기
[14:33] 책 읽기
과제 8

CSV 점수 통계

목표: scores.csv 를 읽어 평균/최고/최저 점수와 인원 수.

요구사항
  • csv.DictReader 사용
  • score 컬럼을 int로 변환
  • 평균 소수점 1자리
입출력 예시
인원: 3
평균: 86.0
최고: 92
최저: 78
▶정답 코드 펼치기 / 접기
SOLUTION
import csv

scores = []
with open("scores.csv", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        scores.append(int(row["score"]))

print(f"인원: {len(scores)}")
print(f"평균: {sum(scores)/len(scores):.1f}")
print(f"최고: {max(scores)}")
print(f"최저: {min(scores)}")
▶ 실행 결과
인원: 3
평균: 86.0
최고: 92
최저: 78
과제 9

To-Do JSON 영구 저장

목표: 할 일을 추가/조회/저장하고 재시작 시에도 유지.

요구사항
  • todos.json 에 [{ id, text, done }] 형태로 저장
  • 프로그램 시작 시 로드, 종료 시 저장
  • 명령: add/list/done <id>/quit
입출력 예시
> add 우유 사기
> list
1. [ ] 우유 사기
> done 1
> quit
저장 완료
▶정답 코드 펼치기 / 접기
SOLUTION
import json
from pathlib import Path

PATH = Path("todos.json")

def load():
    if PATH.exists():
        return json.loads(PATH.read_text(encoding="utf-8"))
    return []

def save(todos):
    PATH.write_text(json.dumps(todos, ensure_ascii=False, indent=2), encoding="utf-8")

todos = load()
next_id = max([t["id"] for t in todos], default=0) + 1

while True:
    cmd = input("> ").strip()
    if cmd == "quit":
        save(todos)
        print("저장 완료")
        break
    elif cmd == "list":
        for t in todos:
            mark = "x" if t["done"] else " "
            print(f"{t['id']}. [{mark}] {t['text']}")
    elif cmd.startswith("add "):
        todos.append({"id": next_id, "text": cmd[4:], "done": False})
        next_id += 1
    elif cmd.startswith("done "):
        i = int(cmd[5:])
        for t in todos:
            if t["id"] == i:
                t["done"] = True
▶ 실행 결과
> add 우유 사기
> list
1. [ ] 우유 사기
> done 1
> quit
저장 완료
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗

댓글 0

아직 댓글이 없습니다. 첫 댓글을 남겨 주세요.

댓글은 회원만 쓸 수 있습니다.

광고·욕설·비방·개인정보·불법 내용이 담긴 댓글은 숨기거나 지웁니다. 댓글은 닉네임과 함께 공개됩니다. 개인정보처리방침