import pdb; pdb.set_trace() → 프로그램 멈춤 → 변수값 확인 → 버그 발견! 😊
“왜 이 변수가 None이지?” print 100번 찍지 말고, 한 줄로 멈춰서 확인! 프로 개발자처럼 pdb 디버거로 버그를 추적합니다!
(45-55분 완독 ⭐⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
🎯 학습 목표 1: 디버깅의 개념과 방법 이해하기
1.1 디버깅이란?
디버깅(Debugging): 프로그램의 오류(버그)를 찾아 수정하는 과정
1
2
3
4
5
6
7
8
9
10
11
| # 버그가 있는 코드
def calculate_average(numbers):
total = sum(numbers)
return total / len(numbers) # 빈 리스트면 ZeroDivisionError!
# 버그를 찾아 수정
def calculate_average(numbers):
if not numbers: # 빈 리스트 체크
return 0
total = sum(numbers)
return total / len(numbers)
|
1.2 디버깅 방법 종류
| 방법 | 설명 | 적합한 상황 |
| print 디버깅 | 변수 값을 출력해서 확인 | 간단한 버그, 빠른 확인 |
| 로깅 | logging 모듈로 기록 | 운영 환경, 복잡한 흐름 |
| 디버거 (pdb) | 코드를 멈추고 단계별 실행 | 복잡한 로직, 정밀 분석 |
| 프로파일링 | 실행 시간/성능 측정 | 성능 문제, 병목 구간 |
1.3 버그의 종류
1
2
3
4
5
6
7
8
9
10
11
| # 1. 구문 오류 (Syntax Error) - 실행 전에 발견
print("Hello" # SyntaxError: 괄호 누락
# 2. 런타임 오류 (Runtime Error) - 실행 중 발생
numbers = [1, 2, 3]
print(numbers[10]) # IndexError
# 3. 논리 오류 (Logic Error) - 가장 찾기 어려움!
def is_adult(age):
return age > 18 # 버그: 18세도 성인인데 False 반환
# 올바른 코드: return age >= 18
|
💡 논리 오류가 가장 찾기 어렵습니다. 프로그램은 정상 실행되지만 결과가 틀립니다!
🎯 학습 목표 2: print 디버깅과 로깅 활용하기
2.1 기본 print 디버깅
1
2
3
4
5
6
7
8
9
10
| def calculate_total(prices):
print(f"입력: {prices}") # 디버깅
total = sum(prices)
print(f"결과: {total}") # 디버깅
return total
calculate_total([100, 200, 300])
# 출력:
# 입력: [100, 200, 300]
# 결과: 600
|
2.2 향상된 print 디버깅
1
2
3
4
5
6
7
8
9
10
11
| def debug_print(var_name, value):
"""디버그 출력 - 타입까지 표시"""
print(f"[DEBUG] {var_name} = {value} (type: {type(value).__name__})")
numbers = [1, 2, 3]
debug_print("numbers", numbers)
# [DEBUG] numbers = [1, 2, 3] (type: list)
name = "Alice"
debug_print("name", name)
# [DEBUG] name = Alice (type: str)
|
2.3 로깅을 활용한 디버깅
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| import logging
# 로깅 설정
logging.basicConfig(level=logging.DEBUG)
def process_order(order_id, items):
logging.debug(f"주문 처리 시작: {order_id}")
logging.debug(f"상품 목록: {items}")
total = sum(item['price'] for item in items)
logging.info(f"총액 계산 완료: {total}원")
return total
# 사용
items = [{'name': '사과', 'price': 1000}, {'name': '배', 'price': 2000}]
process_order("ORD-001", items)
|
💡 print vs logging: 개발 중에는 print가 편리하지만, 운영 환경에서는 logging을 사용하세요!
🎯 학습 목표 3: pdb 디버거 사용하기
3.1 기본 사용법
1
2
3
4
5
6
7
8
9
| import pdb
def buggy_function(x, y):
result = x + y
pdb.set_trace() # 여기서 프로그램이 멈춤!
final = result * 2
return final
buggy_function(10, 20)
|
실행하면 터미널에 이렇게 표시됩니다:
1
2
3
| > /path/to/script.py(6)buggy_function()
-> final = result * 2
(Pdb) _
|
여기서 (Pdb) 프롬프트가 나타나면 명령어를 입력할 수 있습니다:
p result 입력 → 30 출력 (result 변수 값 확인) p x, y 입력 → (10, 20) 출력 n 입력 → 다음 줄로 이동 c 입력 → 프로그램 계속 실행
3.2 pdb 명령어
| 명령어 | 설명 | 예시 |
n (next) | 다음 줄 실행 | n |
s (step) | 함수 안으로 들어감 | s |
c (continue) | 다음 중단점까지 실행 | c |
p 변수 | 변수 값 출력 | p result |
pp 변수 | 변수 예쁘게 출력 | pp items |
l (list) | 현재 위치 코드 보기 | l |
w (where) | 호출 스택 보기 | w |
q (quit) | 디버거 종료 | q |
pdb 세션 예시 (실제 터미널 화면):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| $ python debug_example.py
> /home/user/debug_example.py(6)buggy_function()
-> final = result * 2
(Pdb) p result
30
(Pdb) p x, y
(10, 20)
(Pdb) n
> /home/user/debug_example.py(7)buggy_function()
-> return final
(Pdb) p final
60
(Pdb) c
$
|
💡 pdb 종료: q 또는 quit을 입력하면 프로그램이 즉시 종료됩니다. c (continue)는 프로그램을 끝까지 실행합니다.
3.3 breakpoint() (Python 3.7+)
1
2
3
4
5
6
7
| def calculate(x, y):
result = x + y
breakpoint() # pdb.set_trace()와 동일하지만 더 간편!
return result * 2
# 환경 변수로 비활성화 가능
# PYTHONBREAKPOINT=0 python script.py
|
💡 breakpoint() 는 Python 3.7+에서 사용 가능하며, pdb.set_trace()보다 짧고 편리합니다!
🎯 학습 목표 4: 디버깅 베스트 프랙티스 익히기
4.1 전략 1: 이진 탐색
문제가 있는 구간을 절반씩 좁혀가는 방법입니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 문제 있는 구간을 절반씩 좁혀가기
def process_data(data):
step1 = clean_data(data)
print("Step 1 OK") # ✅ 여기까지는 정상
step2 = transform_data(step1)
print("Step 2 OK") # ❌ 여기서 문제 발생!
step3 = save_data(step2)
print("Step 3 OK")
return step3
# Step 1과 Step 2 사이에서 문제 발생 → transform_data() 집중 분석
|
4.2 전략 2: 최소 재현 예제
복잡한 코드에서 버그의 핵심만 추출합니다.
1
2
3
4
5
6
7
8
| # ❌ 복잡한 코드 (100줄)에서 버그 찾기 어려움
def complex_function(a, b, c, d, e):
# 100줄의 코드...
pass
# ✅ 최소 예제로 축소해서 문제 파악
def simple_test():
result = 10 / 0 # 문제 발견! ZeroDivisionError
|
4.3 프로파일링 - 실행 시간 측정
1
2
3
4
5
6
7
8
9
10
| import time
start = time.time()
# 측정할 코드
time.sleep(1)
result = sum(range(1000000))
end = time.time()
print(f"실행 시간: {end - start:.2f}초")
# 실행 시간: 1.05초
|
4.4 프로파일링 - cProfile
1
2
3
4
5
6
7
8
9
10
11
| import cProfile
def slow_function():
total = 0
for i in range(1000000):
total += i
return total
# 프로파일링 실행
cProfile.run('slow_function()')
# 출력: 함수별 호출 횟수, 총 실행 시간, 개별 실행 시간 등
|
💡 프로파일링은 “어디가 느린지” 찾는 것입니다. 최적화 전에 항상 측정하세요!
🎯 학습 목표 5: assert 문과 방어적 프로그래밍
5.1 assert 문이란?
assert 문: “이 조건이 반드시 참이어야 한다”고 선언하는 문장. 조건이 거짓이면 프로그램이 즉시 중단됩니다.
1
2
3
4
5
6
7
8
9
10
| def calculate_average(numbers):
assert len(numbers) > 0, "빈 리스트는 평균을 계산할 수 없습니다"
return sum(numbers) / len(numbers)
# 정상 케이스
print(calculate_average([1, 2, 3, 4, 5])) # 3.0
# 오류 케이스
print(calculate_average([]))
# AssertionError: 빈 리스트는 평균을 계산할 수 없습니다
|
5.2 assert vs if문
| 구분 | assert | if문 |
| 목적 | 개발 중 버그 검출 | 런타임 오류 처리 |
| 운영 환경 | 비활성화 가능 (python -O) | 항상 실행 |
| 사용 시점 | “이건 절대 발생하면 안 됨” | “이건 발생할 수 있음” |
1
2
3
4
5
6
7
8
9
| # assert 사용: 개발자 실수 검출 (내부 오류)
def process_data(data):
assert isinstance(data, list), "data는 리스트여야 합니다"
# ...
# if 사용: 사용자 입력 검증 (외부 오류)
def get_user_age(age_input):
if age_input < 0:
raise ValueError("나이는 음수일 수 없습니다")
|
5.3 assert 활용 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 1. 함수 입력 검증
def divide(a, b):
assert b != 0, "0으로 나눌 수 없습니다"
return a / b
# 2. 반환값 검증
def get_positive_number():
result = some_calculation()
assert result > 0, f"양수가 반환되어야 하는데 {result}가 반환됨"
return result
# 3. 상태 검증
class Order:
def ship(self):
assert self.status == "paid", f"결제 완료 상태여야 하는데 {self.status}"
self.status = "shipped"
|
💡 assert는 운영 환경에서 비활성화될 수 있습니다! (python -O 옵션) 따라서 사용자 입력 검증에는 if문과 예외 처리를 사용하세요.
🎯 학습 목표 6: 자주 발생하는 버그 패턴
6.1 변수 참조 오류
1
2
3
4
5
6
7
| # ❌ 버그: 변수명 오타
usernmae = "Alice" # 오타!
print(username) # NameError: name 'username' is not defined
# ✅ 해결: 변수명 주의, IDE 자동완성 활용
username = "Alice"
print(username)
|
6.2 리스트/딕셔너리 인덱스 오류
1
2
3
4
5
6
7
8
9
10
11
| # ❌ 버그: 인덱스 범위 초과
items = ["a", "b", "c"]
print(items[3]) # IndexError: list index out of range
# ✅ 해결: 범위 확인 또는 get() 사용
if len(items) > 3:
print(items[3])
# 딕셔너리에서는 get() 활용
user = {"name": "Alice"}
print(user.get("age", "정보 없음")) # "정보 없음" (KeyError 방지)
|
6.3 None 값 처리 오류
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # ❌ 버그: None에 메서드 호출
def find_user(user_id):
# 사용자를 찾지 못하면 None 반환
return None
user = find_user(123)
print(user.name) # AttributeError: 'NoneType' object has no attribute 'name'
# ✅ 해결: None 체크
user = find_user(123)
if user is not None:
print(user.name)
else:
print("사용자를 찾을 수 없습니다")
# 또는 간단히
if user:
print(user.name)
|
6.4 변경 가능한 기본 인자 버그
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # ❌ 버그: 리스트를 기본값으로 사용
def add_item(item, items=[]): # 버그! 모든 호출이 같은 리스트 공유
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['a', 'b'] ← 이전 호출 결과가 남아있음!
# ✅ 해결: None을 기본값으로 사용
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['b'] ✅
|
6.5 비교 연산 오류
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # ❌ 버그: == vs = 혼동 (문법 오류로 잡힘)
# if x = 5: # SyntaxError
# ❌ 버그: == vs is 혼동
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True (값이 같음)
print(a is b) # False (다른 객체)
# ✅ 해결: 값 비교는 ==, None 비교는 is
if user is None: # ✅ None과 비교할 때는 is
pass
if score == 100: # ✅ 값 비교할 때는 ==
pass
|
6.6 루프 변수 범위 오류
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # ❌ 버그: 범위가 1 모자람
for i in range(5): # 0, 1, 2, 3, 4 (5번 반복)
print(i)
# 1부터 5까지 출력하고 싶었다면?
for i in range(1, 6): # 1, 2, 3, 4, 5 ✅
print(i)
# ❌ 버그: 리스트 수정하면서 순회
items = [1, 2, 3, 4, 5]
for item in items:
if item % 2 == 0:
items.remove(item) # 버그! 순회 중 리스트 수정
print(items) # [1, 3, 5] 가 아니라 예상치 못한 결과
# ✅ 해결: 복사본 순회 또는 리스트 컴프리헨션
items = [1, 2, 3, 4, 5]
items = [item for item in items if item % 2 != 0]
print(items) # [1, 3, 5] ✅
|
🎯 학습 목표 7: IDE 디버거 활용 (VS Code / PyCharm)
7.1 IDE 디버거 vs pdb
| 기능 | pdb (터미널) | IDE 디버거 |
| 시각화 | 텍스트만 | 그래픽 UI |
| 중단점 설정 | 코드에 직접 작성 | 클릭으로 설정 |
| 변수 확인 | 명령어 입력 | 자동 표시 |
| 호출 스택 | w 명령어 | 항상 표시 |
| 학습 곡선 | 높음 | 낮음 |
💡 입문자에게는 IDE 디버거 추천! 시각적으로 변수 값을 확인하고, 클릭으로 중단점을 설정할 수 있습니다.
7.2 VS Code 디버깅
설정 방법:
- Python 확장 프로그램 설치
- 중단점 설정: 줄 번호 왼쪽 클릭 (빨간 점 표시)
- F5 키로 디버깅 시작
- 디버그 패널에서 변수 값 확인
1
2
3
4
5
6
7
| # example.py
def calculate(x, y):
result = x + y # ← 여기에 중단점 설정 (줄 번호 클릭)
final = result * 2
return final
print(calculate(10, 20))
|
디버그 조작:
- F5: 디버깅 시작/계속
- F10: 다음 줄로 (Step Over)
- F11: 함수 안으로 (Step Into)
- Shift+F11: 함수 밖으로 (Step Out)
- Shift+F5: 디버깅 중지
7.3 PyCharm 디버깅
설정 방법:
- 줄 번호 클릭으로 중단점 설정
- 우클릭 → “Debug” 선택
- Debug 패널에서 변수, 호출 스택 확인
디버그 조작:
- F8: 다음 줄로 (Step Over)
- F7: 함수 안으로 (Step Into)
- Shift+F8: 함수 밖으로 (Step Out)
- F9: 계속 실행 (Resume)
💡 조건부 중단점: 특정 조건에서만 멈추게 할 수 있습니다! (예: i == 100일 때만)
- VS Code: 중단점 우클릭 → “Edit Breakpoint” → 조건 입력
- PyCharm: 중단점 우클릭 → Condition 입력
🎯 학습 목표 8: 실전 디버깅 시나리오
8.1 시나리오: 쇼핑몰 할인 계산 버그
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # 버그가 있는 코드
def calculate_final_price(price, discount_percent, coupon_discount):
"""
최종 가격 계산
- price: 원래 가격
- discount_percent: 할인율 (예: 20 = 20%)
- coupon_discount: 쿠폰 할인 금액
"""
discounted = price * discount_percent # 버그 1
after_coupon = discounted - coupon_discount # 버그 2
return after_coupon
# 테스트: 10000원, 20% 할인, 1000원 쿠폰
result = calculate_final_price(10000, 20, 1000)
print(f"최종 가격: {result}원")
# 예상: 7000원 (10000 * 0.8 - 1000)
# 실제: 199000원 ← 뭔가 잘못됨!
|
디버깅 과정:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| # Step 1: print로 중간값 확인
def calculate_final_price(price, discount_percent, coupon_discount):
print(f"[DEBUG] price={price}, discount_percent={discount_percent}")
discounted = price * discount_percent
print(f"[DEBUG] discounted={discounted}") # 200000 ← 이상함!
after_coupon = discounted - coupon_discount
print(f"[DEBUG] after_coupon={after_coupon}")
return after_coupon
# Step 2: 버그 발견
# discounted = price * discount_percent = 10000 * 20 = 200000
# 버그 1: 퍼센트를 비율로 변환하지 않음 (20 → 0.2)
# 버그 2: 할인된 가격에서 쿠폰을 빼야 하는데, 할인 금액에서 빼고 있음
# Step 3: 수정
def calculate_final_price(price, discount_percent, coupon_discount):
discount_rate = discount_percent / 100 # 20 → 0.2
discounted = price * (1 - discount_rate) # 10000 * 0.8 = 8000
after_coupon = discounted - coupon_discount # 8000 - 1000 = 7000
return after_coupon
result = calculate_final_price(10000, 20, 1000)
print(f"최종 가격: {result}원") # 7000원 ✅
|
8.2 시나리오: 사용자 목록 필터링 버그
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # 버그가 있는 코드
def filter_adults(users):
"""18세 이상 사용자만 필터링"""
adults = []
for user in users:
if user['age'] > 18: # 버그: 18세도 성인인데 제외됨
adults.append(user)
return adults
users = [
{"name": "Alice", "age": 18},
{"name": "Bob", "age": 20},
{"name": "Charlie", "age": 15}
]
adults = filter_adults(users)
print(adults)
# 예상: Alice, Bob
# 실제: Bob만 포함 ← 18세 Alice가 빠짐!
|
디버깅 과정:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| # pdb로 디버깅
import pdb
def filter_adults(users):
adults = []
for user in users:
pdb.set_trace() # 각 반복에서 멈춤
if user['age'] > 18:
adults.append(user)
return adults
# 터미널에서:
# (Pdb) p user
# {'name': 'Alice', 'age': 18}
# (Pdb) p user['age'] > 18
# False ← 18은 18보다 크지 않음!
# (Pdb) q
# 수정: > 를 >= 로 변경
def filter_adults(users):
adults = []
for user in users:
if user['age'] >= 18: # 수정됨!
adults.append(user)
return adults
|
8.3 시나리오: API 응답 처리 버그
1
2
3
4
5
6
7
8
9
10
11
12
13
| # 버그가 있는 코드
def process_api_response(response):
"""API 응답에서 사용자 이름 추출"""
user = response['data']['user']
return user['name']
# 테스트
response1 = {"data": {"user": {"name": "Alice"}}}
print(process_api_response(response1)) # Alice ✅
response2 = {"data": {"user": None}} # 사용자가 없는 경우
print(process_api_response(response2))
# TypeError: 'NoneType' object is not subscriptable ← 버그!
|
디버깅 및 수정:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| def process_api_response(response):
"""API 응답에서 사용자 이름 추출 (안전 버전)"""
# 방어적 프로그래밍
data = response.get('data')
if data is None:
return None
user = data.get('user')
if user is None:
return None
return user.get('name')
# 테스트
print(process_api_response({"data": {"user": {"name": "Alice"}}})) # Alice
print(process_api_response({"data": {"user": None}})) # None
print(process_api_response({"data": None})) # None
print(process_api_response({})) # None
|
💡 실전 팁 & 주의사항
✅ 효율적인 디버깅 습관
- 문제 재현 먼저
- 문제를 일관되게 재현할 수 있어야 합니다
- 최소 재현 예제 만들기
- 가설 세우고 검증
- “이 변수가 None일 것이다” → 확인
- 추측 대신 증거 기반 디버깅
- 변경 사항 추적
⚠️ 주의사항
pdb.set_trace()는 배포 전에 제거하세요 - 성능 측정은 여러 번 반복 후 평균값 사용
- print 디버깅 후 코드 정리 필수
🧪 연습 문제
문제 1: 디버깅 도우미 함수
목표: 변수의 이름, 값, 타입을 한 줄로 출력하는 간단한 디버깅 함수를 작성하세요.
요구사항:
- 변수명과 값을 받아서 출력
- 타입도 함께 표시
[DEBUG] 접두어 사용
해답 보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| def debug(name, value):
"""간단한 디버그 출력"""
print(f"[DEBUG] {name} = {value} (type: {type(value).__name__})")
# 테스트
x = [1, 2, 3]
debug("x", x)
# [DEBUG] x = [1, 2, 3] (type: list)
y = "hello"
debug("y", y)
# [DEBUG] y = hello (type: str)
z = 3.14
debug("z", z)
# [DEBUG] z = 3.14 (type: float)
# 실제 디버깅 예시
def calculate_total(items):
debug("items", items) # 입력 확인
total = sum(item['price'] for item in items)
debug("total", total) # 중간 결과 확인
return total
|
문제 2: 버그 찾기
목표: 아래 코드에서 버그를 찾고 수정하세요.
1
2
3
4
5
6
7
8
9
10
11
| def calculate_discount(price, discount_percent):
"""할인 가격 계산"""
discount = price * discount_percent
final_price = price - discount
return final_price
# 테스트: 10000원에서 20% 할인
result = calculate_discount(10000, 20)
print(f"할인 후 가격: {result}원")
# 예상: 8000원
# 실제: -190000원 (버그!)
|
해답 보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 버그 원인: discount_percent가 20이면 price * 20 = 200000이 됨
# 퍼센트를 비율로 변환하지 않음!
# 수정된 코드
def calculate_discount(price, discount_percent):
"""할인 가격 계산"""
discount = price * (discount_percent / 100) # 퍼센트를 비율로 변환
final_price = price - discount
return final_price
# 테스트
result = calculate_discount(10000, 20)
print(f"할인 후 가격: {result}원")
# 출력: 할인 후 가격: 8000.0원 ✅
|
디버깅 과정:
- print로 중간값 확인:
print(f"discount = {discount}") - discount가 200000인 것을 발견
price * discount_percent에서 퍼센트 변환 누락 확인 /100 추가로 해결
📝 오늘 배운 내용 정리
| 개념 | 설명 | 예시 |
| print 디버깅 | 변수 값 출력으로 확인 | print(f"x = {x}") |
| pdb | Python 내장 디버거 | import pdb; pdb.set_trace() |
| breakpoint() | Python 3.7+ 디버깅 | breakpoint() |
| assert | 조건 검증 문 | assert x > 0, "양수여야 함" |
| IDE 디버거 | 시각적 디버깅 도구 | VS Code, PyCharm |
| cProfile | 성능 프로파일링 | cProfile.run('func()') |
| 자주 발생하는 버그 | None, 인덱스, 비교 오류 | dict.get(), is None |
| 디버깅 전략 | 이진 탐색, 최소 재현 | - |
핵심 코드 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # pdb 디버깅
import pdb
def buggy_function(x, y):
result = x + y
pdb.set_trace() # 여기서 멈춤
return result * 2
# breakpoint (Python 3.7+)
def my_function():
breakpoint() # 디버거 시작
# 코드...
# 시간 측정
import time
start = time.time()
# 작업
elapsed = time.time() - start
|
🔗 관련 자료
📚 이전 학습
Day 47: 로깅 시스템 ⭐⭐⭐
어제는 logging 모듈로 프로그램의 실행 기록을 파일로 남기는 방법을 배웠습니다!
📚 다음 학습
Day 49: 파일 시스템 고급 ⭐⭐⭐
내일은 pathlib와 os 모듈로 파일과 디렉토리를 생성, 삭제, 이동하는 방법을 배웁니다!
“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀
| Day 48/100 | Phase 5: 파일 처리와 예외 처리 | #100DaysOfPython |
이제와서 시작하는 Python 마스터하기 - Day 48 완료! 🎉