← 파이썬 강의 목록으로
🚀
고급 (Advanced)
정적 타입 · typing · dataclass · frozen · @deco · functools.wraps · with · contextmanager · 프로토콜 · yield from · itertools · 코루틴

1~3주차 — 타입 힌트와 dataclass · 데코레이터와 컨텍스트 매니저 · 이터레이터·제너레이터 심화

타입 힌트 문법, typing 모듈(Optional/Union/Callable), 그리고 보일러플레이트를 줄여주는 @dataclass 와 frozen 옵션까지 — 견고한 코드의 출발점입니다. 함수 데코레이터, 인자 받는 데코레이터, 클래스/함수 기반 컨텍스트 매니저까지 — 횡단 관심사를 깔끔히 분리하는 두 도구를 익힙니다. for 루프 뒤에 숨은 __iter__/__next__ 프로토콜, yield from 위임, itertools 의 강력한 함수들, 그리고 코루틴 기초까지 다룹니다.

typingdataclassmypyfielddecoratorwrapswithcontextmanageriteratorgeneratoritertoolscoroutine
소요 시간
⏱ 2시간 + 2시간 + 2시간
난이도
📊 고급
선수 조건
🎯 중급 7~8주차 (OOP)
결과물
정적 타입과 dataclass로 견고한 모델 작성 / 데코레이터·컨텍스트 매니저 직접 구현 / 직접 구현한 이터레이터와 itertools 활용

이 강의에서 배우는 것

  • 1타입 힌트 문법(int, str, list[int], Optional)을 익힌다
  • 2mypy 로 정적 타입을 검사한다
  • 3@dataclass 로 데이터 클래스를 간결히 작성한다
  • 4field(default_factory=list) 의 의의를 안다
  • 5함수 데코레이터를 정의하고 적용한다
  • 6인자 받는 데코레이터를 만든다
  • 7클래스/함수 기반 컨텍스트 매니저를 구현한다
  • 8@functools.wraps 의 역할을 안다
  • 9이터레이터 프로토콜(__iter__, __next__)을 직접 구현한다
  • 10yield from 으로 제너레이터를 위임한다
  • 11itertools 의 함수들을 활용한다
  • 12send 기반 코루틴 기초를 안다

1주차 — 타입 힌트와 dataclass

타입 힌트 문법, typing 모듈(Optional/Union/Callable), 그리고 보일러플레이트를 줄여주는 @dataclass 와 frozen 옵션까지 — 견고한 코드의 출발점입니다.

1. 타입 힌트 기본

런타임에는 영향이 없지만, IDE 자동완성·정적 분석·문서화에 큰 도움이 됩니다.

python
def greet(name: str, age: int = 20) -> str:
    return f"{name}({age})"

count: int = 0
items: list[str] = []

2. typing 모듈

python
from typing import Optional, Union, Any, Callable

def find_user(uid: int) -> Optional[dict]:
    """Optional[X] = X | None"""
    ...

def parse(value: Union[str, int]) -> int:
    """str 또는 int (Python 3.10+ 에서는 str | int)"""
    ...

handler: Callable[[int], str] = lambda x: str(x)

Python 3.10+ 부터는 더 짧게 쓸 수 있습니다.

python
def find_user(uid: int) -> dict | None: ...
def parse(value: str | int) -> int: ...

3. 컨테이너 타입

python
nums: list[int] = [1, 2, 3]
scores: dict[str, int] = {"Alice": 90}
pair: tuple[int, str] = (1, "a")
unique: set[int] = {1, 2, 3}

4. mypy

bash
pip install mypy
mypy main.py
mypy --strict main.py    # 엄격 모드

5. dataclass

__init__, __repr__, __eq__ 가 자동 생성됩니다.

python
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p = Point(1, 2)
print(p)              # Point(x=1, y=2)
print(p == Point(1, 2))   # True

6. dataclass 옵션

python
from dataclasses import dataclass, field

@dataclass
class Book:
    title: str
    author: str
    tags: list[str] = field(default_factory=list)
    price: float = 0.0

b = Book("Python", "Guido")
b.tags.append("language")
⚠️

tags: list[str] = [] 는 모든 인스턴스가 공유합니다. 절대 안 됨 — 반드시 field(default_factory=list).

7. frozen dataclass

불변 객체. 해시 가능 → set/dict 키로 사용 가능합니다.

python
@dataclass(frozen=True)
class Point:
    x: int
    y: int

p = Point(1, 2)
# p.x = 99   # FrozenInstanceError
{Point(1, 2)}   # OK

자주 하는 실수

  1. 가변 기본값을 그대로 — field(default_factory=list) 사용
  2. Optional 의미 오해 — Optional[int] = int | None. '선택적 인자'가 아님
  3. 타입 힌트가 런타임에 검증된다고 오해 — 그냥 힌트일 뿐. 런타임 검증은 pydantic

FAQ

Q1. 타입 힌트를 다 달아야 하나요? — 공개 API(함수 시그니처)는 강력 권장. 내부 변수는 추론으로 충분.

Q2. dataclass vs pydantic.BaseModel — dataclass는 표준, pydantic은 런타임 검증·직렬화 강력. 데이터 처리 많으면 pydantic.

Q3. Any 를 쓰면? — mypy가 검사를 포기. 가능하면 더 구체적인 타입.

2주차 — 데코레이터와 컨텍스트 매니저

함수 데코레이터, 인자 받는 데코레이터, 클래스/함수 기반 컨텍스트 매니저까지 — 횡단 관심사를 깔끔히 분리하는 두 도구를 익힙니다.

1. 데코레이터란

함수를 인자로 받아 새로운 함수를 반환합니다. 로깅·캐싱·인증 같은 기능을 횡단으로 추가.

python
def loud(func):
    def wrapper(*args, **kwargs):
        print(f">>> {func.__name__} 호출")
        result = func(*args, **kwargs)
        print(f"<<< 결과: {result}")
        return result
    return wrapper

@loud
def add(a, b):
    return a + b

add(3, 5)
# >>> add 호출
# <<< 결과: 8

@loud 는 add = loud(add) 의 문법 설탕입니다.

2. functools.wraps

데코레이터로 감싸도 원본 함수 이름·docstring 을 유지합니다.

python
from functools import wraps

def loud(func):
    @wraps(func)            # 이거 없으면 .__name__ 이 'wrapper' 됨
    def wrapper(*args, **kwargs):
        ...
    return wrapper

3. 인자 받는 데코레이터

데코레이터를 만드는 함수 — 3중 함수 구조.

python
def retry(times):
    def deco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"재시도 {i+1}/{times}: {e}")
            raise
        return wrapper
    return deco

@retry(3)
def fragile():
    ...

4. 컨텍스트 매니저

with 문에서 동작합니다. 자원 획득·해제를 안전하게.

클래스 기반:

python
class FileLock:
    def __init__(self, path):
        self.path = path
    def __enter__(self):
        print(f"잠금: {self.path}")
        return self
    def __exit__(self, exc_type, exc, tb):
        print("해제")

with FileLock("data.txt"):
    print("작업 중")

함수 기반 (@contextmanager):

python
from contextlib import contextmanager
import time

@contextmanager
def timer(label):
    start = time.perf_counter()
    yield
    print(f"{label}: {time.perf_counter() - start:.4f}s")

with timer("작업"):
    sum(range(1_000_000))

5. 실전 — 메모이제이션

python
def memoize(func):
    cache = {}
    @wraps(func)
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@memoize
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

fib(100)   # 빠름

자주 하는 실수

  1. @wraps 누락 — 디버깅·문서가 망가짐. 항상 붙이기
  2. 데코레이터가 return 안 함 — 결과가 None
  3. @contextmanager 함수에 yield 가 두 개 이상 — 한 번만
  4. __exit__ 가 True 반환 — 예외를 삼킴

FAQ

Q1. 데코레이터는 언제? — 여러 함수에 같은 부가 동작(로깅/인증/캐싱)을 넣을 때.

Q2. with 와 try-finally 의 차이? — with 는 정형화된 try-finally. 가독성·실수 방지.

Q3. 데코레이터를 여러 개 쌓으면? — 아래쪽이 먼저 적용. @A @B def f → f = A(B(f)).

3주차 — 이터레이터·제너레이터 심화

for 루프 뒤에 숨은 __iter__/__next__ 프로토콜, yield from 위임, itertools 의 강력한 함수들, 그리고 코루틴 기초까지 다룹니다.

1. 이터레이터 프로토콜

for ... in x 는 사실 iter(x) → next(it) 반복입니다.

python
class Range:
    def __init__(self, start, stop):
        self.cur = start
        self.stop = stop

    def __iter__(self):
        return self

    def __next__(self):
        if self.cur >= self.stop:
            raise StopIteration
        self.cur += 1
        return self.cur - 1

print(list(Range(1, 5)))   # [1, 2, 3, 4]

2. yield from

다른 이터러블을 그대로 yield. 평탄화에 유용합니다.

python
def chain(*iters):
    for it in iters:
        yield from it

print(list(chain([1, 2], (3, 4), "ab")))   # [1, 2, 3, 4, 'a', 'b']

3. itertools

표준 라이브러리의 보석.

python
import itertools as it

# 무한 시퀀스
counter = it.count(1, 2)        # 1, 3, 5, 7, ...
print(list(it.islice(counter, 5)))  # [1, 3, 5, 7, 9]

# 조합/순열
print(list(it.combinations([1, 2, 3, 4], 2)))
print(list(it.permutations([1, 2, 3], 2)))

# 그룹
data = [("a", 1), ("a", 2), ("b", 3), ("b", 4)]
for key, group in it.groupby(data, key=lambda x: x[0]):
    print(key, list(group))

# 사전 곱
print(list(it.product([1, 2], "ab")))

# 누적
print(list(it.accumulate([1, 2, 3, 4])))   # [1, 3, 6, 10]

4. 코루틴 기초

yield 가 양방향 통신 — send(값) 으로 값 주입.

python
def echo():
    while True:
        x = yield
        print(f"받음: {x}")

co = echo()
next(co)            # 첫 yield까지 진행 (필수)
co.send("hello")
co.send("world")

Python 3.5+ 부터는 async/await 가 더 일반적 — 다음 주차에서.

5. 제너레이터 vs 리스트

제너레이터리스트
메모리항목 1개씩전체
길이모름len()
재사용한 번만여러 번
인덱싱안 됨됨

자주 하는 실수

  1. 이터레이터를 두 번 순회 — 두 번째는 빈 결과. 다시 생성 필요
  2. __next__ 에서 StopIteration 누락 — 무한 루프
  3. groupby 전 정렬 안 함 — groupby는 연속된 같은 키만 묶음

FAQ

Q1. 이터레이터와 제너레이터의 차이? — 제너레이터는 이터레이터의 한 종류. yield 로 만든 것.

Q2. itertools 보다 more-itertools 가 좋다는데? — 표준은 아니지만 강력. 실무에서 자주 사용.

💻 예제 (examples)

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

01_type_hints.py— 기본 타입 힌트
CODE
def greet(name: str, age: int = 20) -> str:
    return f"{name}({age})"

count: int = 0
items: list[str] = ["사과", "바나나"]

print(greet("홍길동"))
print(greet("김파이", 28))
print(count, items)
▶ 실행 결과
홍길동(20)
김파이(28)
0 ['사과', '바나나']
02_typing_module.py— Optional/Union/Callable
CODE
from typing import Optional, Callable

def find_user(uid: int) -> Optional[dict]:
    return {"id": 1, "name": "Alice"} if uid == 1 else None

def apply(f: Callable[[int], int], x: int) -> int:
    return f(x)

print(find_user(1))
print(find_user(99))
print(apply(lambda n: n * n, 5))
▶ 실행 결과
{'id': 1, 'name': 'Alice'}
None
25
03_dataclass.py— dataclass 기본
CODE
from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int

p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1)
print(p1 == p2)
▶ 실행 결과
Point(x=1, y=2)
True
04_dataclass_advanced.py— field, frozen
CODE
from dataclasses import dataclass, field

@dataclass
class Book:
    title: str
    tags: list[str] = field(default_factory=list)

@dataclass(frozen=True)
class Coord:
    x: int
    y: int

b = Book("Python")
b.tags.append("language")
print(b)
print({Coord(1, 2), Coord(1, 2), Coord(3, 4)})
▶ 실행 결과
Book(title='Python', tags=['language'])
{Coord(x=1, y=2), Coord(x=3, y=4)}
01_basic_decorator.py— 함수 데코레이터 + wraps
CODE
from functools import wraps

def loud(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f">>> {func.__name__} 호출")
        result = func(*args, **kwargs)
        print(f"<<< 결과: {result}")
        return result
    return wrapper

@loud
def add(a, b):
    return a + b

add(3, 5)
print("이름:", add.__name__)
▶ 실행 결과
>>> add 호출
<<< 결과: 8
이름: add
02_param_decorator.py— 인자 받는 데코레이터 (retry)
CODE
from functools import wraps

def retry(times):
    def deco(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for i in range(times):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    print(f"재시도 {i+1}/{times}: {e}")
            raise
        return wrapper
    return deco

count = 0

@retry(3)
def fragile():
    global count
    count += 1
    if count < 3:
        raise RuntimeError("일시 오류")
    return "성공"

print(fragile())
▶ 실행 결과
재시도 1/3: 일시 오류
재시도 2/3: 일시 오류
성공
03_context_class.py— 클래스 기반 컨텍스트 매니저
CODE
class FileLock:
    def __init__(self, path):
        self.path = path
    def __enter__(self):
        print(f"잠금: {self.path}")
        return self
    def __exit__(self, exc_type, exc, tb):
        print("해제")

with FileLock("data.txt"):
    print("작업 중")
▶ 실행 결과
잠금: data.txt
작업 중
해제
04_contextmanager.py— @contextmanager 함수 기반
CODE
from contextlib import contextmanager
import time

@contextmanager
def timer(label):
    start = time.perf_counter()
    yield
    print(f"{label}: {time.perf_counter() - start:.4f}s")

with timer("계산"):
    sum(i * i for i in range(1_000_000))
▶ 실행 결과
계산: 0.0521s
01_iterator.py— 직접 이터레이터 구현
CODE
class Range:
    def __init__(self, start, stop):
        self.cur = start
        self.stop = stop
    def __iter__(self):
        return self
    def __next__(self):
        if self.cur >= self.stop:
            raise StopIteration
        self.cur += 1
        return self.cur - 1

print(list(Range(1, 5)))
▶ 실행 결과
[1, 2, 3, 4]
02_yield_from.py— yield from
CODE
def chain(*iters):
    for it in iters:
        yield from it

def flatten(nested):
    for item in nested:
        if isinstance(item, list):
            yield from flatten(item)
        else:
            yield item

print(list(chain([1, 2], (3, 4), "ab")))
print(list(flatten([1, [2, [3, [4, 5]]], 6])))
▶ 실행 결과
[1, 2, 3, 4, 'a', 'b']
[1, 2, 3, 4, 5, 6]
03_itertools.py— combinations, groupby, accumulate
CODE
import itertools as it

print(list(it.islice(it.count(1, 2), 5)))
print(list(it.combinations([1, 2, 3, 4], 2)))
data = sorted([("b", 3), ("a", 1), ("a", 2)], key=lambda x: x[0])
for key, group in it.groupby(data, key=lambda x: x[0]):
    print(key, list(group))
print(list(it.accumulate([1, 2, 3, 4])))
▶ 실행 결과
[1, 3, 5, 7, 9]
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
a [('a', 1), ('a', 2)]
b [('b', 3)]
[1, 3, 6, 10]
04_coroutine.py— send 기반 코루틴
CODE
def echo():
    while True:
        x = yield
        print(f"받음: {x}")

co = echo()
next(co)
co.send("hello")
co.send("world")
co.close()
▶ 실행 결과
받음: hello
받음: world

📝 과제 (exercises)

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

과제 1

함수에 타입 힌트

목표: 기본 함수 시그니처에 타입 힌트를 추가한다.

요구사항
  • add(a: int, b: int) -> int 타입 힌트 부여
  • join(words: list[str], sep: str = ', ') -> str 타입 힌트 부여
💡 힌트

기본값이 있어도 타입 힌트는 콜론 뒤에 그대로 쓴다

입출력 예시
5
사과, 바나나, 포도
채점
  • · mypy main.py 가 오류 없이 통과
  • · 함수가 정상 동작
▶정답 코드 펼치기 / 접기
SOLUTION
def add(a: int, b: int) -> int:
    return a + b

def join(words: list[str], sep: str = ", ") -> str:
    return sep.join(words)

print(add(2, 3))
print(join(["사과", "바나나", "포도"]))
▶ 실행 결과
5
사과, 바나나, 포도
과제 2

Book/Member dataclass

목표: 도서관 도메인을 dataclass 로 모델링한다.

요구사항
  • @dataclass Book(title, author, tags: list[str] = field(default_factory=list))
  • @dataclass Member(name, age: int)
  • 두 인스턴스를 만들어 출력
💡 힌트

가변 기본값은 field(default_factory=list)

입출력 예시
Book(title='파이썬', author='홍길동', tags=['언어'])
Member(name='Alice', age=25)
채점
  • · dataclass 데코레이터 사용
  • · field 사용 정확
  • · 출력 일치
▶정답 코드 펼치기 / 접기
SOLUTION
from dataclasses import dataclass, field

@dataclass
class Book:
    title: str
    author: str
    tags: list[str] = field(default_factory=list)

@dataclass
class Member:
    name: str
    age: int

b = Book("파이썬", "홍길동")
b.tags.append("언어")
m = Member("Alice", 25)
print(b)
print(m)
▶ 실행 결과
Book(title='파이썬', author='홍길동', tags=['언어'])
Member(name='Alice', age=25)
과제 3

Optional 시그니처

목표: Optional 을 사용하는 함수 5개를 작성한다.

요구사항
  • find_user(uid: int) -> Optional[dict]
  • first(items: list[int]) -> Optional[int]
  • div(a: int, b: int) -> Optional[float]
  • get_age(name: str) -> Optional[int]
  • parse_int(s: str) -> Optional[int]
💡 힌트

반환할 값이 없으면 None 반환

입출력 예시
{'id': 1}
1
None
None
42
채점
  • · 5개 모두 Optional 반환 타입
  • · None 반환 케이스 처리
▶정답 코드 펼치기 / 접기
SOLUTION
from typing import Optional

DB = {1: {"id": 1}, 2: {"id": 2}}
AGES = {"Alice": 25}

def find_user(uid: int) -> Optional[dict]:
    return DB.get(uid)

def first(items: list[int]) -> Optional[int]:
    return items[0] if items else None

def div(a: int, b: int) -> Optional[float]:
    return None if b == 0 else a / b

def get_age(name: str) -> Optional[int]:
    return AGES.get(name)

def parse_int(s: str) -> Optional[int]:
    try:
        return int(s)
    except ValueError:
        return None

print(find_user(1))
print(first([1, 2, 3]))
print(div(10, 0))
print(get_age("Bob"))
print(parse_int("42"))
▶ 실행 결과
{'id': 1}
1
None
None
42
과제 4

@logged 데코레이터

목표: 함수 호출과 결과를 로깅하는 데코레이터를 만든다.

요구사항
  • @logged 적용 시 인자와 결과를 한 줄에 print
  • @wraps 로 메타데이터 보존
💡 힌트

wrapper 안에서 args, kwargs 출력 후 결과 반환

입출력 예시
add(2, 3) -> 5
add(10, 20) -> 30
채점
  • · wraps 사용
  • · 출력 형식 일치
▶정답 코드 펼치기 / 접기
SOLUTION
from functools import wraps

def logged(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        print(f"{func.__name__}{args} -> {result}")
        return result
    return wrapper

@logged
def add(a, b):
    return a + b

add(2, 3)
add(10, 20)
▶ 실행 결과
add(2, 3) -> 5
add(10, 20) -> 30
과제 5

@cached 메모이제이션

목표: 결과를 캐시하는 데코레이터를 직접 구현한다.

요구사항
  • @cached 가 같은 인자에 대해 함수 본체를 재실행하지 않는다
  • fib(30) 등 큰 입력에서 즉시 반환
💡 힌트

dict 로 args 키 → 결과 저장

입출력 예시
832040
채점
  • · 인자 동일하면 캐시 사용
  • · 정확한 값 반환
▶정답 코드 펼치기 / 접기
SOLUTION
from functools import wraps

def cached(func):
    store = {}
    @wraps(func)
    def wrapper(*args):
        if args not in store:
            store[args] = func(*args)
        return store[args]
    return wrapper

@cached
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(30))
▶ 실행 결과
832040
과제 6

with timer 컨텍스트 매니저

목표: @contextmanager 로 실행 시간을 측정한다.

요구사항
  • with timer('label'): ... 형식
  • 블록 종료 시 경과 초를 출력
💡 힌트

time.perf_counter()

yield 전후로 시각 측정

입출력 예시
합계: 0.05s 정도
채점
  • · @contextmanager 사용
  • · 예외 시에도 시간 출력 (선택)
▶정답 코드 펼치기 / 접기
SOLUTION
from contextlib import contextmanager
import time

@contextmanager
def timer(label):
    start = time.perf_counter()
    try:
        yield
    finally:
        print(f"{label}: {time.perf_counter() - start:.2f}s")

with timer("합계"):
    s = sum(i for i in range(5_000_000))
print(s)
▶ 실행 결과
합계: 0.21s
12499997500000
과제 7

chunked 제너레이터

목표: 리스트를 n개씩 잘라서 yield 하는 제너레이터를 만든다.

요구사항
  • chunked(iterable, n) -> generator
  • 마지막 청크는 n보다 작아도 됨
💡 힌트

슬라이싱 또는 itertools.islice 사용

입출력 예시
[1, 2, 3]
[4, 5, 6]
[7]
채점
  • · 제너레이터 함수
  • · 정확한 청크 분할
▶정답 코드 펼치기 / 접기
SOLUTION
def chunked(iterable, n):
    buf = []
    for x in iterable:
        buf.append(x)
        if len(buf) == n:
            yield buf
            buf = []
    if buf:
        yield buf

for c in chunked([1, 2, 3, 4, 5, 6, 7], 3):
    print(c)
▶ 실행 결과
[1, 2, 3]
[4, 5, 6]
[7]
과제 8

groupby 로 그룹화

목표: (date, message) 튜플 리스트를 날짜별로 그룹화한다.

요구사항
  • itertools.groupby 사용
  • 그룹 전 정렬 필수
💡 힌트

sorted(data, key=lambda x: x[0])

입출력 예시
2026-05-09: 2건
2026-05-10: 1건
채점
  • · groupby 사용
  • · 정확한 카운트
▶정답 코드 펼치기 / 접기
SOLUTION
import itertools as it

logs = [
    ("2026-05-10", "INFO"),
    ("2026-05-09", "ERROR"),
    ("2026-05-09", "WARN"),
]

logs.sort(key=lambda x: x[0])
for date, group in it.groupby(logs, key=lambda x: x[0]):
    print(f"{date}: {len(list(group))}건")
▶ 실행 결과
2026-05-09: 2건
2026-05-10: 1건
과제 9

무한 피보나치 + islice

목표: 무한 피보나치 제너레이터에서 처음 N개를 꺼낸다.

요구사항
  • fibs() 가 무한 제너레이터
  • itertools.islice 로 N개 추출
💡 힌트

a, b = b, a + b 패턴

입출력 예시
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
채점
  • · 무한 yield
  • · islice 사용
  • · 정확한 N개
▶정답 코드 펼치기 / 접기
SOLUTION
import itertools as it

def fibs():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

print(list(it.islice(fibs(), 10)))
▶ 실행 결과
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
예제 코드 / 강의 자료

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

GitHub에서 보기 ↗

댓글 0

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

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

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