이 저장소가 대체 무엇인가.
일반적인 웹 프레임워크에서는 요청 데이터 검증 로직을 직접 짜고, 응답 형식을 직접 맞추고, API 문서는 별도로 관리한다. FastAPI는 이 세 가지를 파이썬 타입 힌트 하나로 통합했다. def create_item(item: Item)라고만 써두면, FastAPI가 런타임에 함수 시그니처를 들여다보고(인트로스펙션) Item이 Pydantic 모델이라는 걸 파악한 뒤 — 요청 본문을 그 모델로 자동 검증하고, 실패하면 422 에러를 자동으로 반환하고, 성공하면 파싱된 객체를 함수에 주입하고, 응답을 다시 JSON으로 직렬화하고, 이 모든 스키마를 OpenAPI 문서로 자동 생성한다.
여기에 Depends()라는 의존성 주입(DI) 시스템을 더해, "이 라우트를 실행하기 전에 DB 세션을 열고, 토큰을 검증하고, 사용자 객체를 조회해줘" 같은 반복 로직을 함수 시그니처 안에서 선언적으로 조립할 수 있게 했다. Starlette 위에서 ASGI(비동기 서버 게이트웨이 인터페이스)를 그대로 활용하므로 async def와 def 핸들러를 자유롭게 섞어 쓸 수 있다.
: int, : str, : Item처럼 타입을 명시하는 문법(PEP 484). 원래는 IDE 자동완성과 정적 분석 도구(mypy)를 돕기 위한 "힌트"일 뿐 런타임에 강제되지 않았다. FastAPI는 이 힌트를 런타임에 실제로 읽어서(inspect.signature) 검증·직렬화·문서화에 활용하는 것이 핵심 아이디어다.Flask · Django REST Framework 대비 장점 · 트렌딩 이유.
2018년 등장 이후 FastAPI는 파이썬 API 프레임워크 생태계의 지각변동을 일으켰다. 기존 강자였던 Flask는 최소주의(microframework) 철학이라 검증·직렬화·문서화를 전부 Flask-RESTful, marshmallow, flasgger 같은 별도 라이브러리로 조립해야 했다. Django REST Framework(DRF)는 기능은 강력하지만 Django 전체(ORM, admin, settings)를 끌고 들어와야 하고 시리얼라이저를 직접 클래스로 작성해야 하는 보일러플레이트가 많다. 두 프레임워크 모두 WSGI 기반이라 네이티브 async를 지원하지 않는다(Django는 3.1+부터 부분 지원).
| 기능 | Flask | Django REST Framework | FastAPI |
|---|---|---|---|
| 비동기(async) 네이티브 지원 | 제한적(확장 필요) | 부분적 | ✓ (ASGI 기반) |
| 요청 검증 | 별도 라이브러리 필요 | Serializer 직접 작성 | ✓ 타입 힌트만으로 자동 |
| 자동 API 문서(OpenAPI) | 확장 필요(flasgger) | 확장 필요(drf-spectacular) | ✓ 기본 내장 |
| 에디터 자동완성/타입체크 | 약함 | 약함 | ✓ 강력(Pydantic 모델 기반) |
| 의존성 주입 시스템 | 없음 | 없음(Mixin 방식) | ✓ Depends() |
| 학습 곡선 | 매우 쉬움 | 가파름 | 쉬움~보통 |
| ORM 종속성 | 없음(자유 선택) | Django ORM 사실상 강제 | 없음(자유 선택) |
Flask는 빠르고 배우기 쉽지만 API용 부가기능이 없다. DRF는 기능이 다 있지만 보일러플레이트가 많고 Django에 묶인다. FastAPI는 "타입 힌트 하나로 세 가지를 자동화"하는 아이디어로 이 둘 사이의 공백을 정확히 채웠다. 특히 에디터에서 item.을 입력했을 때 Pydantic 모델의 필드가 자동완성되는 개발자 경험(DX)이 채택을 가속화한 핵심 요인으로 꼽힌다.
주문서 양식을 설계한다고 하자. Flask라면: 양식을 받고(request.json), 필드가 있는지 하나하나 확인하고(if 'name' not in data), 타입이 맞는지 확인하고, 에러 메시지를 직접 만들고, 마지막으로 API 문서 페이지에 이 양식을 손으로 다시 옮겨 적어야 한다. FastAPI라면: class Order(BaseModel): name: str; qty: int 하나만 정의하면 검증도, 에러 메시지도, 문서 페이지(Swagger UI)도 전부 그 정의에서 자동으로 파생된다. "양식을 한 번만 그리면 검증기와 안내판이 저절로 생기는" 셈이다.
Starlette(ASGI) · Pydantic(검증) · Uvicorn(서버) · 타입 힌트 각각 상세.
| 패키지 | 버전 조건 | 역할 |
|---|---|---|
starlette | >=0.46.0 | ASGI 웹 프레임워크 — 라우팅·미들웨어·요청/응답 객체·WebSocket의 기반. FastAPI는 이를 상속·확장한다. |
pydantic | >=2.9.0 | 데이터 검증/직렬화 라이브러리 — 요청 바디, 쿼리 파라미터, 응답 모델을 정의하는 BaseModel 제공. Rust로 작성된 pydantic-core가 실제 검증을 수행해 매우 빠르다. |
typing-extensions | >=4.8.0 | 표준 typing 모듈에 아직 없는 최신 타입 기능(예: Annotated) 백포트. |
typing-inspection | >=0.4.2 | 타입 힌트를 런타임에 안전하게 분석·순회하는 유틸리티 — Pydantic v2와 FastAPI가 공통으로 의존. |
annotated-doc | >=0.0.2 | Annotated[X, Doc("설명")] 형태로 파라미터 문서 문자열을 부여하는 표준화 라이브러리 — FastAPI 소스 전체(예: applications.py)의 매개변수 docstring에 사용된다. |
pip install "fastapi[standard]"| 패키지 | 역할 |
|---|---|
fastapi-cli[standard] | fastapi dev / fastapi run CLI 명령 제공 — 개발 서버 실행, 프로덕션 실행을 감싼 래퍼 |
uvicorn[standard] | ASGI 서버 — uvloop(고성능 이벤트 루프) + httptools 포함 버전 |
httpx | TestClient가 내부적으로 사용하는 HTTP 클라이언트 — 테스트 코드에서 앱을 직접 호출 |
jinja2 | 서버사이드 HTML 템플릿 렌더링(Jinja2Templates) |
python-multipart | multipart/form-data 파싱 — 폼 데이터·파일 업로드 처리에 필수 |
email-validator | Pydantic의 EmailStr 타입 검증에 사용 |
pydantic-settings | 환경변수 기반 설정 관리(BaseSettings) |
pydantic-extra-types | 색상·좌표 등 추가 Pydantic 타입 |
itsdangerous (all만) | Starlette SessionMiddleware용 서명 쿠키 |
pyyaml (all만) | OpenAPI 스키마 YAML 출력 등 |
FastAPI 클래스 자체가 class FastAPI(Starlette):로 Starlette를 상속한다. 라우팅 트리, 미들웨어 체인, Request/Response 객체, WebSocket 지원, 예외 처리 미들웨어(ExceptionMiddleware, ServerErrorMiddleware)까지 전부 Starlette가 제공하는 것을 FastAPI가 그대로 쓰거나 살짝 확장한다. FastAPI 문서가 "우리는 Starlette 위의 얇은 레이어"라고 자주 표현하는 이유다.
BaseModel을 상속한 클래스가 요청 바디, 응답 모델, 설정값의 스키마 역할을 한다. Pydantic v2부터 핵심 검증 로직이 Rust로 작성된 pydantic-core로 이전되어 순수 파이썬 대비 수배~수십 배 빠르다. FastAPI는 이 모델에서 JSON Schema를 뽑아 OpenAPI 문서를 만들고, 요청이 들어올 때 이 모델로 model_validate()를 호출해 검증한다.
실제로 TCP 소켓을 열고 HTTP 요청을 받아 ASGI scope/receive/send 3종 세트로 변환해 FastAPI 앱(callable)에 넘기는 것이 Uvicorn의 역할이다. uvicorn[standard]는 libuv 기반 uvloop(이벤트 루프 가속)와 httptools(C로 작성된 HTTP 파서)를 포함해 순수 asyncio 대비 빠르다. fastapi dev main.py를 실행하면 내부적으로 Uvicorn을 자동 리로드 모드로 띄운다.
async def app(scope, receive, send) 형태의 콜러블로 통일해, HTTP뿐 아니라 WebSocket·수명주기(lifespan) 이벤트까지 하나의 프로토콜로 다룬다. Starlette와 FastAPI 모두 ASGI 애플리케이션이다.요청 처리 흐름도 + OpenAPI 자동생성 파이프라인 + 핵심 설계 패턴.
라우트 함수를 등록하는 시점에 FastAPI는 inspect.signature()로 함수의 모든 매개변수를 읽고, 각 매개변수의 타입 힌트와 기본값(Depends(), Query(), Body() 등)을 분석해 Dependant라는 데이터 구조를 만든다(fastapi/dependencies/models.py). 이 Dependant는 "이 함수를 실행하려면 어떤 하위 의존성들을 먼저 해결해야 하는지"를 트리 형태로 표현한다. 이 분석은 요청이 오기 전, 앱 시작 시 한 번만 수행되고, 이후 요청마다는 이미 만들어진 그래프를 순회하기만 하므로 빠르다.
Depends(callable)는 "이 콜러블을 먼저 실행해서 나온 값을 이 매개변수에 넣어줘"라는 선언이다. 콜러블은 함수든 클래스든 상관없고, 그 콜러블 자체도 다시 Depends()를 매개변수로 가질 수 있어 의존성이 의존성을 부르는 트리 구조가 만들어진다. 요청마다 solve_dependencies()(fastapi/dependencies/utils.py)가 이 트리를 리프 노드부터 재귀적으로 해결하며, 기본적으로 같은 요청 안에서 동일한 의존성은 한 번만 호출하고 캐시한다(use_cache=True). yield를 쓰는 의존성은 컨텍스트 매니저처럼 동작해 응답 후 정리 코드(DB 세션 종료 등)를 실행한다.
# 의존성이 의존성을 부르는 전형적인 체인
def get_db():
db = SessionLocal()
try:
yield db # 여기까지가 "생성"
finally:
db.close() # 응답 후 자동 실행되는 "정리"
def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[Session, Depends(get_db)],
):
... # token과 db는 이미 해결된 상태로 주입됨
return user
@app.get("/users/me")
def read_current_user(
user: Annotated[User, Depends(get_current_user)]
):
return user # oauth2_scheme → get_db, get_current_user 순으로 자동 해결
fastapi/ 패키지 내부 주요 모듈 역할.
fastapi/encoders.py에 있는 함수로, Pydantic 모델뿐 아니라 datetime, Enum, dataclass, set, 커스텀 객체 등 "JSON으로 바로 못 바꾸는" 파이썬 객체를 재귀적으로 순회하며 dict/list/str/숫자 같은 JSON 호환 타입으로 변환한다. 응답을 직렬화하는 마지막 단계에서 항상 거치는 관문이다.이 저장소에서 무엇을 배울 수 있는가.
FastAPI의 가장 근본적인 아이디어는 "타입 힌트를 정적 분석 도구용 장식이 아니라 런타임 동작을 결정하는 데이터로 쓴다"는 것이다. fastapi/dependencies/utils.py의 get_dependant()는 inspect.signature()와 typing.get_type_hints()를 조합해 함수의 각 매개변수 타입을 분석하고, 그것이 Pydantic 모델인지, Annotated[X, Query()]인지, Depends()인지 분류한다.
배울 것: inspect 모듈로 함수 시그니처 읽기, typing.Annotated로 타입에 메타데이터 부착하기, Pydantic BaseModel의 model_validate()/model_dump() 활용, pydantic-core(Rust 백엔드)가 왜 빠른지.
Java의 Spring이나 .NET의 DI 컨테이너 같은 무거운 프레임워크 없이, 파이썬 함수 매개변수 문법만으로 DI를 구현한 사례다. fastapi/dependencies/models.py의 Dependant 클래스와 fastapi/dependencies/utils.py의 solve_dependencies()를 함께 읽으면, "선언적 DI"를 데코레이터·클래스 없이 순수 함수 합성으로 구현하는 방법을 배울 수 있다.
배울 것: 의존성 그래프의 재귀적 해결, yield 기반 컨텍스트 매니저 패턴(생성-사용-정리), 요청 스코프 캐싱(use_cache), 서브 의존성 오버라이드(app.dependency_overrides)로 테스트 시 실제 DB 대신 목(mock) 주입하기.
FastAPI는 Starlette를 상속만 한 게 아니라, WSGI/ASGI의 차이, 미들웨어 체인이 요청을 어떻게 감싸는지, lifespan 이벤트(시작/종료 훅)가 어떻게 동작하는지를 배우는 좋은 교재다. fastapi/applications.py의 FastAPI.__init__가 ServerErrorMiddleware, ExceptionMiddleware, AsyncExitStackMiddleware를 쌓는 순서를 보면 미들웨어 체인 설계를 이해할 수 있다.
배울 것: ASGI 3-tuple(scope/receive/send) 프로토콜, 미들웨어 onion(양파) 구조, async def와 def 핸들러가 스레드풀로 자동 분기되는 이유, WebSocket과 HTTP를 하나의 인터페이스로 통합하는 설계.
fastapi/openapi/utils.py의 get_openapi()는 "코드에서 문서를 역생성"하는 대표 사례다. 라우트마다 이미 존재하는 Dependant(파라미터 정보)와 Pydantic 모델의 JSON Schema를 조합해 OpenAPI 3.1 스펙 전체를 조립한다. 문서를 별도로 유지보수할 필요 없이 코드 자체가 단일 진실원(source of truth)이 되는 설계를 배울 수 있다.
배울 것: OpenAPI 스펙 구조(paths/components/schemas), Pydantic 모델 → JSON Schema 변환, Swagger UI/ReDoc이 스펙을 어떻게 소비하는지, response_model로 응답 스키마와 실제 반환 객체를 분리하는 이유(민감 필드 제외 등).
fastapi/security/oauth2.py의 OAuth2PasswordBearer는 "보안 스킴도 결국 하나의 의존성"이라는 것을 보여준다. 이 클래스의 인스턴스 자체가 callable이라 Depends(oauth2_scheme)로 쓰이며, 동시에 OpenAPI 문서에 자물쇠 아이콘과 인증 방식을 자동으로 표시하는 메타데이터 역할도 겸한다.
배울 것: OAuth2 Password/Authorization Code 플로우의 파이썬 구현, __call__을 구현한 클래스를 Depends()에 넣는 패턴, JWT 토큰 검증을 의존성 체인으로 표현하기, Swagger UI의 "Authorize" 버튼이 동작하는 원리.
실행 환경 · 설치 조건.
| 항목 | 요구사항 |
|---|---|
| Python 버전 | 3.10 이상 (pyproject.toml requires-python = ">=3.10", 3.10~3.14 공식 지원) |
| 필수 의존성 | starlette>=0.46.0, pydantic>=2.9.0, typing-extensions, typing-inspection, annotated-doc |
| ASGI 서버 (프로덕션 실행 시 필요) | Uvicorn(권장), Hypercorn, Daphne 등 — fastapi[standard] 설치 시 Uvicorn 자동 포함 |
| 메모리 사용 | 순수 파이썬 프레임워크 수준(수십 MB) — 실제 사용량은 애플리케이션 코드·워커 수에 좌우 |
| 동시성 모델 | 단일 프로세스 내 asyncio 이벤트 루프 — 수평 확장은 Uvicorn --workers 또는 Gunicorn+Uvicorn 워커, 컨테이너 복제 |
| OS | Linux/macOS/Windows 모두 지원 (Python이 동작하는 모든 곳) |
| 선택적 시스템 의존성 | python-multipart(파일 업로드 시), email-validator(EmailStr 검증 시) |
FastAPI는 라우트 핸들러가 async def인지 일반 def인지 자동으로 감지한다. async def는 이벤트 루프에서 직접 await되고, 일반 def는 run_in_threadpool로 별도 스레드풀에서 실행되어 블로킹 코드(동기 DB 드라이버 등)가 이벤트 루프 전체를 멈추지 않도록 한다. 이 덕분에 레거시 동기 라이브러리를 async 코드베이스에 안전하게 섞어 쓸 수 있다.
async def 핸들러 안에서 time.sleep()이나 동기 requests 라이브러리 같은 블로킹 호출을 그대로 쓰면, 이벤트 루프 자체가 멈춰 그 순간 다른 모든 요청도 함께 지연된다. 블로킹 작업이 필요하면 일반 def로 선언하거나 run_in_threadpool로 명시적으로 감싸야 한다.
난이도별 5개.
FastAPI를 설치하고 최소 앱을 실행한 뒤 자동 생성된 Swagger UI를 직접 눈으로 확인한다.
pip install "fastapi[standard]"
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello World"}
@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
return {"item_id": item_id, "q": q}
fastapi dev main.py
# http://127.0.0.1:8000/docs 에서 Swagger UI 확인
# http://127.0.0.1:8000/items/5?q=test 호출해보기
# http://127.0.0.1:8000/items/abc 호출 → 422 검증 에러 확인 (int가 아니라서)
확인 포인트: item_id: int인데 문자열을 넣으면 자동으로 422 에러가 나는 것을 관찰. /docs에서 각 파라미터 타입이 어떻게 표시되는지 확인.
Pydantic BaseModel을 정의하고 POST 요청 바디로 받아, 검증 실패 시 에러 응답 형태를 관찰한다.
from pydantic import BaseModel
from fastapi import FastAPI
app = FastAPI()
class Item(BaseModel):
name: str
price: float
is_offer: bool | None = None
@app.post("/items/")
def create_item(item: Item):
return {"name": item.name, "price_with_tax": item.price * 1.1}
확인 포인트: /docs에서 "Try it out"으로 price에 문자열("abc")을 넣어 422 에러의 상세 구조(loc, msg, type)를 확인. response_model=Item을 추가해 응답 스키마도 검증되는지 비교.
여러 라우트에서 재사용되는 공통 로직(페이지네이션 파라미터, 가짜 DB 세션)을 Depends()로 뽑아내 본다.
from typing import Annotated
from fastapi import Depends, FastAPI
app = FastAPI()
async def common_pagination(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
def get_fake_db():
db = {"users": ["alice", "bob", "carol"]}
try:
yield db
finally:
print("DB 연결 정리 완료") # 응답 후 자동 실행
@app.get("/users/")
def list_users(
pagination: Annotated[dict, Depends(common_pagination)],
db: Annotated[dict, Depends(get_fake_db)],
):
users = db["users"][pagination["skip"]: pagination["skip"] + pagination["limit"]]
return {"users": users, **pagination}
확인 포인트: 같은 common_pagination 의존성을 다른 라우트(/items/)에도 재사용해보고, yield 이후 코드가 응답 전송 이후 실행되는 시점을 print로 확인.
FastAPI 보안 유틸리티로 로그인 → 토큰 발급 → 보호된 라우트 접근 흐름을 구현한다.
from typing import Annotated
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
if token != "valid-token":
raise HTTPException(status_code=401, detail="Invalid token")
return {"username": "demo_user"}
@app.get("/users/me")
def read_users_me(
current_user: Annotated[dict, Depends(get_current_user)]
):
return current_user
확인 포인트: /docs에 자물쇠 아이콘이 자동으로 생기는 것을 확인. Authorization 헤더 없이 호출 시 401, 잘못된 토큰일 때의 에러 구조 비교. pyjwt를 추가해 실제 JWT 서명·검증으로 확장해본다.
저장소를 직접 클론하고, 디버거로 solve_dependencies()가 실제로 호출되는 시점을 추적한다.
git clone https://github.com/fastapi/fastapi
cd fastapi
pip install -e ".[all]"
# fastapi/dependencies/utils.py의 solve_dependencies에 breakpoint() 삽입
# 아무 예제 앱이나 실행 후 요청을 보내 콜스택 관찰
python -m pytest tests/test_dependency_cache.py -v # 의존성 캐싱 테스트 확인
확인 포인트: get_dependant()가 앱 시작(라우트 등록) 시 한 번만 호출되고, solve_dependencies()는 매 요청마다 호출된다는 시점 차이를 콜스택으로 직접 확인. app.dependency_overrides로 테스트에서 실제 의존성을 목으로 교체하는 코드(tests/ 디렉토리 다수)를 찾아 분석.
FastAPI 이해·기여를 위한 주차별 학습 경로.
목표: PEP 484 타입 힌트 문법, Optional/Union/Annotated 이해.
자료: 파이썬 공식 typing 문서(docs.python.org/3/library/typing.html), FastAPI 공식 튜토리얼의 "Python Types Intro" 챕터.
실습: mypy로 타입 체크가 되는 간단한 함수 여러 개 작성.
목표: BaseModel, validator, model_dump()/model_validate(), JSON Schema 생성 이해.
자료: Pydantic 공식 문서(docs.pydantic.dev), pydantic-core가 Rust로 작성된 이유에 대한 블로그 글.
실습: 중첩 모델, 커스텀 검증자(@field_validator)로 이메일/전화번호 검증 로직 작성.
목표: 경로 연산, 쿼리/경로/바디 파라미터, response_model, 상태 코드 관리.
자료: FastAPI 공식 튜토리얼(fastapi.tiangolo.com/tutorial/) 전체.
실습: CRUD API 하나를 처음부터 끝까지 구현 (인메모리 dict 저장소로 충분).
목표: Depends(), 서브 의존성, yield 의존성, 클래스 기반 의존성, dependency_overrides.
자료: "Dependencies" 튜토리얼 섹션, fastapi/dependencies/utils.py 소스 직접 읽기.
실습: 실습 3~4를 확장해 DB 세션+인증+권한 검사를 의존성 체인으로 조합.
목표: ASGI 스펙, 미들웨어, lifespan, WebSocket, BackgroundTasks.
자료: Starlette 공식 문서(starlette.dev), ASGI 스펙(asgi.readthedocs.io).
실습: 커스텀 미들웨어 작성(요청 처리 시간 로깅), WebSocket 채팅 엔드포인트 구현.
목표: OAuth2/JWT, CORS, Uvicorn 워커 설정, Docker 배포.
자료: "Security" 튜토리얼 섹션, FastAPI 공식 "Deployment" 가이드.
실습: JWT 로그인 API를 Docker 컨테이너로 패키징하고 Gunicorn+Uvicorn 워커로 실행.
목표: FastAPI 저장소 구조 파악, 테스트 작성법 익히기, PR 프로세스 이해.
자료: CONTRIBUTING.md, tests/ 디렉토리의 기존 테스트 패턴.
실습: 문서 번역/개선 PR, 또는 이슈 트래커의 good-first-issue 라벨 항목에 도전.
이 저장소를 이해하는 데 필요한 핵심 용어.
Depends(callable)를 매개변수 기본값처럼 선언하면, 요청마다 그 callable이 먼저 실행되어 반환값이 자동으로 주입된다. 의존성끼리 중첩되면 트리 구조의 해결 순서가 자동으로 결정된다./docs(Swagger UI)와 /redoc(ReDoc) 페이지에서 대화형으로 렌더링한다.name: str, age: int처럼 변수·매개변수·반환값에 타입을 명시하는 문법. 원래는 정적 분석 도구를 위한 것이었으나, FastAPI는 이를 런타임에 실제로 읽어 검증·직렬화·문서 생성의 근거로 삼는다는 점이 독창적이다.async def로 정의한 함수는 await로 다른 비동기 작업이 끝나길 "양보"하며 기다리는 동안 이벤트 루프가 다른 요청을 처리할 수 있다. FastAPI는 async def 핸들러를 이벤트 루프에서 직접 실행하고, 일반 def 핸들러는 스레드풀에서 실행해 블로킹을 방지한다.fastapi/dependencies/models.py에 정의된 데이터 클래스. 라우트 함수 하나당 하나씩 만들어지며, 그 함수가 필요로 하는 경로/쿼리/헤더/바디 파라미터 목록과 하위 Depends() 의존성 목록을 트리 형태로 담는다. 앱 시작 시 get_dependant()가 생성하고, 요청마다 solve_dependencies()가 이를 순회해 실제 값을 채운다.fastapi dev/fastapi run CLI가 내부적으로 Uvicorn을 실행한다.더 깊이 파고들기 위한 공식 자료.