← 파이썬 강의 목록으로
🧮
실습 프로젝트 (Practice) · ★
메뉴 + 결과 누적 + 이력 · 다중 카테고리 + 양방향 · 우선순위·마감일 + 정렬·필터 · 양방향 + 복습 모드

Lv1 · CLI 계산기 · Lv1 · 단위 변환기 · Lv2 · To-Do List · Lv2 · 단어장 퀴즈

메뉴 선택형 사칙연산 계산기. 결과를 누적해 다음 계산에 사용할 수 있고, 종료 시 전체 이력을 출력합니다. 길이/무게/온도를 양방향으로 변환하는 메뉴 CLI. 변환 함수를 카테고리별로 분리하고 메뉴 핸들러로 매핑합니다. 할 일 추가/조회/완료/삭제 + 우선순위·마감일을 갖춘 메모리 기반 할 일 관리. list 의 dict 컬렉션을 다룹니다. 영-한 사전으로 랜덤 퀴즈. 영→한, 한→영, 랜덤 모드와 틀린 문제만 복습하는 모드까지 구현합니다.

함수메뉴예외dictlist정렬필터random퀴즈
소요 시간
⏱ 1~2시간 + 1~2시간 + 2~3시간 + 2~3시간
난이도
📊 기초 응용
선수 조건
🎯 기초 4, 9주차
결과물
함수 매핑(dict)과 람다 활용 / 도메인별 함수 분리 패턴 / 리스트의 dict 패턴과 명령어 파싱 / 딕셔너리 양방향 활용

이 강의에서 배우는 것

  • 1연산자별 함수를 dict 로 매핑한다
  • 20 나누기 예외를 처리한다
  • 3이전 결과 재사용 옵션을 구현한다
  • 4변환 함수를 단위 쌍별로 정의한다
  • 5메뉴 → 핸들러 매핑을 dict 로 한다
  • 6f-string 으로 깔끔한 결과 출력
  • 7할 일을 dict 로 표현하고 list 에 보관한다
  • 8list 명령에 --pending, --sort priority 옵션을 단다
  • 9번호로 done/del 을 정확히 처리한다
  • 10단어장 dict 에서 랜덤 표본을 뽑는다
  • 11영→한, 한→영, 랜덤 모드를 구현한다
  • 12틀린 문제만 복습 모드로 다시 푼다

Lv1 · CLI 계산기

메뉴 선택형 사칙연산 계산기. 결과를 누적해 다음 계산에 사용할 수 있고, 종료 시 전체 이력을 출력합니다.

프로젝트 개요

  • 메뉴: 1.+ 2.- 3.* 4./ 5.이전 결과 사용 6.종료
  • 0 나누기 예외 처리 + 친절한 메시지
  • 종료 시 전체 이력 한 번에 출력

요구사항

  • OPS dict 에 (기호, 함수) 매핑
  • history 리스트에 모든 연산 누적
  • 직전 결과를 변수에 저장 → '이전결과 사용' 옵션

도전 과제

  • ** (제곱), % (나머지) 추가
  • 수식 문자열 직접 파싱 (eval 금지)

채점 체크리스트

  • 0 나누기 안전
  • 이전 결과 재사용
  • 이력 출력

Lv1 · 단위 변환기

길이/무게/온도를 양방향으로 변환하는 메뉴 CLI. 변환 함수를 카테고리별로 분리하고 메뉴 핸들러로 매핑합니다.

프로젝트 개요

  • 메뉴: 1.길이(cm↔inch) 2.무게(kg↔lb) 3.온도(°C↔°F) 4.종료
  • 각 카테고리 안에서 다시 양방향 선택
  • 변환 함수는 카테고리별로 별도 정의

요구사항

  • cm_to_inch, inch_to_cm 등 6개 함수
  • menu_length / menu_weight / menu_temp 핸들러
  • handlers dict 로 메뉴 라우팅

도전 과제

  • 환율 변환 추가 (외부 API)
  • 자유 입력 형태 (5 km to mile)

채점 체크리스트

  • 변환 함수 분리
  • 메뉴 라우팅
  • 양방향 모두 동작

Lv2 · To-Do List

할 일 추가/조회/완료/삭제 + 우선순위·마감일을 갖춘 메모리 기반 할 일 관리. list 의 dict 컬렉션을 다룹니다.

데이터 모델

python
[{"task": str, "done": bool, "priority": str, "due": str}, ...]

명령

  • add — 할 일/우선순위/마감일 입력
  • list [--pending] [--sort priority]
  • done <번호>
  • del <번호>
  • quit

도전 과제

  • 마감 임박 강조
  • 카테고리(태그)

채점 체크리스트

  • 5개 명령 동작
  • 옵션 처리
  • 잘못된 번호 안전

Lv2 · 단어장 퀴즈

영-한 사전으로 랜덤 퀴즈. 영→한, 한→영, 랜덤 모드와 틀린 문제만 복습하는 모드까지 구현합니다.

프로젝트 개요

  • 단어장 dict (10개 이상)
  • 모드 선택: 영→한 / 한→영 / 랜덤
  • 10문제 출제 후 점수 + 틀린 단어 + 복습 옵션

요구사항

  • random.sample 로 중복 없이 출제
  • 즉시 정답/오답 피드백
  • 복습 모드: 틀린 문제만 다시

도전 과제

  • 단어장 외부 파일 로드
  • 정답률 단어별 누적

채점 체크리스트

  • 3가지 모드
  • 복습 모드
  • 점수 정확

💻 예제 (examples)

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

solution.py— 전체 동작 코드
CODE
"""CLI 계산기 — 메뉴형 + 결과 누적"""

OPS = {
    "1": ("+", lambda a, b: a + b),
    "2": ("-", lambda a, b: a - b),
    "3": ("*", lambda a, b: a * b),
    "4": ("/", lambda a, b: a / b if b != 0 else None),
}

def main():
    history = []
    last = None
    while True:
        print("\n[메뉴] 1.+ 2.- 3.* 4./ 5.이전결과 사용 6.종료")
        choice = input("선택: ").strip()
        if choice == "6":
            break
        if choice not in OPS:
            print("잘못된 선택"); continue
        try:
            if last is not None and input(f"첫 번째 수 (이전 결과 {last}): ").strip() == "":
                a = last
            else:
                a = float(input("첫 번째 수: "))
            b = float(input("두 번째 수: "))
        except ValueError:
            print("숫자만 입력"); continue

        symbol, fn = OPS[choice]
        result = fn(a, b)
        if result is None:
            print("0으로 나눌 수 없습니다")
            continue
        print(f"결과: {a} {symbol} {b} = {result}")
        history.append(f"{a} {symbol} {b} = {result}")
        last = result

    print("\n[연산 이력]")
    for h in history:
        print(f"  {h}")

if __name__ == "__main__":
    main()
▶ 실행 결과
[메뉴] 1.+ 2.- 3.* 4./ 5.이전결과 사용 6.종료
선택: 1
첫 번째 수: 3
두 번째 수: 4
결과: 3.0 + 4.0 = 7.0
선택: 6

[연산 이력]
  3.0 + 4.0 = 7.0
solution.py— 전체 동작 코드
CODE
"""단위 변환기 — 다중 카테고리 + 양방향"""

def cm_to_inch(cm): return cm / 2.54
def inch_to_cm(inch): return inch * 2.54
def kg_to_lb(kg): return kg * 2.2046226218
def lb_to_kg(lb): return lb / 2.2046226218
def c_to_f(c): return c * 9 / 5 + 32
def f_to_c(f): return (f - 32) * 5 / 9

def menu_length():
    direction = input("1.cm→inch 2.inch→cm: ").strip()
    val = float(input("값: "))
    if direction == "1":
        print(f"{val} cm = {cm_to_inch(val):.2f} inch")
    else:
        print(f"{val} inch = {inch_to_cm(val):.2f} cm")

def menu_weight():
    direction = input("1.kg→lb 2.lb→kg: ").strip()
    val = float(input("값: "))
    if direction == "1":
        print(f"{val} kg = {kg_to_lb(val):.2f} lb")
    else:
        print(f"{val} lb = {lb_to_kg(val):.2f} kg")

def menu_temp():
    direction = input("1.°C→°F 2.°F→°C: ").strip()
    val = float(input("값: "))
    if direction == "1":
        print(f"{val}°C = {c_to_f(val):.2f}°F")
    else:
        print(f"{val}°F = {f_to_c(val):.2f}°C")

def main():
    handlers = {"1": menu_length, "2": menu_weight, "3": menu_temp}
    while True:
        choice = input("\n[메뉴] 1.길이 2.무게 3.온도 4.종료: ").strip()
        if choice == "4":
            break
        if choice in handlers:
            handlers[choice]()

if __name__ == "__main__":
    main()
▶ 실행 결과
[메뉴] 1.길이 2.무게 3.온도 4.종료: 1
1.cm→inch 2.inch→cm: 1
값: 100
100.0 cm = 39.37 inch
solution.py— 전체 동작 코드
CODE
"""To-Do List — 우선순위·마감일 지원"""

PRIORITY_ORDER = {"높음": 0, "보통": 1, "낮음": 2}
todos = []

def add():
    task = input("할 일: ").strip()
    if not task: return
    p = input("우선순위 (높음/보통/낮음, 기본 보통): ").strip() or "보통"
    if p not in PRIORITY_ORDER:
        p = "보통"
    due = input("마감일 (YYYY-MM-DD, 없으면 엔터): ").strip()
    todos.append({"task": task, "done": False, "priority": p, "due": due})
    print("추가됨")

def show(args=""):
    items = list(enumerate(todos, 1))
    if "--pending" in args:
        items = [(i, t) for i, t in items if not t["done"]]
    if "--sort priority" in args:
        items.sort(key=lambda x: PRIORITY_ORDER[x[1]["priority"]])
    if not items:
        print("(비어있음)"); return
    for i, t in items:
        mark = "[x]" if t["done"] else "[ ]"
        due = f"~{t['due']}" if t["due"] else ""
        print(f"{i}. {mark}[{t['priority']}] {t['task']} {due}".rstrip())

def main():
    while True:
        raw = input("\n> ").strip()
        if not raw: continue
        parts = raw.split(maxsplit=1)
        cmd = parts[0]
        arg = parts[1] if len(parts) > 1 else ""
        if cmd == "quit": break
        elif cmd == "add": add()
        elif cmd == "list": show(arg)
        elif cmd == "done":
            try:
                idx = int(arg) - 1
                if 0 <= idx < len(todos):
                    todos[idx]["done"] = True
                    print("완료")
                else:
                    print("번호 없음")
            except ValueError:
                print("번호를 입력하세요")
        elif cmd == "del":
            try:
                idx = int(arg) - 1
                if 0 <= idx < len(todos):
                    del todos[idx]
                    print("삭제")
            except ValueError:
                print("번호를 입력하세요")
        else:
            print("알 수 없는 명령")

if __name__ == "__main__":
    main()
▶ 실행 결과
> add
할 일: 우유 사기
우선순위: 높음
마감일: 2026-05-12
추가됨
> list
1. [ ][높음] 우유 사기 ~2026-05-12
> done 1
완료
solution.py— 전체 동작 코드
CODE
"""단어장 퀴즈 — 양방향 + 복습 모드"""
import random

WORDS = {
    "apple": "사과", "book": "책", "table": "탁자", "door": "문",
    "water": "물", "fire": "불", "tree": "나무", "sky": "하늘",
    "love": "사랑", "time": "시간", "house": "집", "car": "자동차",
}

def quiz(pairs, mode):
    score = 0
    wrong = []
    for i, (en, ko) in enumerate(pairs, 1):
        if mode == "en2ko":
            q, a = en, ko
        elif mode == "ko2en":
            q, a = ko, en
        else:  # random
            if random.random() < 0.5:
                q, a = en, ko
            else:
                q, a = ko, en
        ans = input(f"[{i}/{len(pairs)}] {q}? ").strip()
        if ans == a:
            print("✓ 정답")
            score += 1
        else:
            print(f"✗ 오답 (정답: {a})")
            wrong.append((q, a))
    return score, wrong

def main():
    print("[모드] 1.영→한 2.한→영 3.랜덤")
    mode_map = {"1": "en2ko", "2": "ko2en", "3": "random"}
    mode = mode_map.get(input("선택: ").strip(), "en2ko")

    pairs = random.sample(list(WORDS.items()), min(10, len(WORDS)))
    score, wrong = quiz(pairs, mode)
    print(f"\n총점: {score} / {len(pairs)}")

    if wrong and input("\n틀린 문제만 복습? (y/n): ").strip().lower() == "y":
        score2, _ = quiz(wrong, mode)
        print(f"복습 점수: {score2} / {len(wrong)}")

if __name__ == "__main__":
    main()
▶ 실행 결과
[모드] 1.영→한 2.한→영 3.랜덤
선택: 1
[1/10] apple? 사과
✓ 정답
...
총점: 8 / 10
틀린 문제만 복습? (y/n): y
[1/2] water? 물
✓ 정답
복습 점수: 2 / 2

📝 과제 (exercises)

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

과제 1

프로젝트 구현

목표: 위 사양대로 직접 작성한다.

요구사항
  • OPS dict + 람다
  • 0 나누기 처리
  • 이력 출력
💡 힌트

lambda a, b: a / b if b != 0 else None

입출력 예시
결과: 3.0 + 4.0 = 7.0
채점
  • · 메뉴 동작
  • · 예외 안전
▶정답 코드 펼치기 / 접기
SOLUTION
# 위 solution.py 와 동일
▶ 실행 결과
(예제 참고)
과제 2

프로젝트 구현

목표: 위 사양대로 직접 작성한다.

요구사항
  • 6개 변환 함수
  • 3개 메뉴 핸들러
  • handlers dict 라우팅
💡 힌트

f-string 의 :.2f

입출력 예시
100.0 cm = 39.37 inch
채점
  • · 함수 분리
  • · 메뉴 동작
▶정답 코드 펼치기 / 접기
SOLUTION
# 위 solution.py 와 동일
▶ 실행 결과
(예제 참고)
과제 3

프로젝트 구현

목표: 위 사양대로 직접 작성한다.

요구사항
  • dict 리스트
  • list/add/done/del/quit
  • 옵션 처리
💡 힌트

enumerate(todos, 1) 로 번호 매기기

입출력 예시
1. [x][높음] 우유 사기
채점
  • · 5개 명령
  • · 옵션 동작
▶정답 코드 펼치기 / 접기
SOLUTION
# 위 solution.py 와 동일
▶ 실행 결과
(예제 참고)
과제 4

프로젝트 구현

목표: 위 사양대로 직접 작성한다.

요구사항
  • WORDS dict
  • quiz 함수
  • 복습 모드
💡 힌트

random.sample(list(WORDS.items()), 10)

입출력 예시
총점: 8 / 10
채점
  • · 3가지 모드
  • · 복습
▶정답 코드 펼치기 / 접기
SOLUTION
# 위 solution.py 와 동일
▶ 실행 결과
(예제 참고)
예제 코드 / 강의 자료

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

GitHub에서 보기 ↗

댓글 0

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

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

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