{"componentChunkName":"component---src-templates-blog-post-js","path":"/python/FastAPI-완전정복-고성능-비동기-웹-API-개발-가이드/","result":{"data":{"site":{"siteMetadata":{"title":"Bottlehs Tech Blog","description":"웹·앱·AI 개발자 전병훈(bottlehs)의 기술 블로그. AI·LLM, JavaScript·Vue, Spring Boot, 알고리즘, 블록체인 실무 가이드를 다룹니다","siteUrl":"https://www.bottlehs.dev","author":{"name":"Jeon Byung Hun","summary":"개발을 즐기는 bottlehs - Engineer, MS, AI, FE, BE, OS, IOT, Blockchain, 설계, 테스트"},"social":{"github":"bottlehs","codepen":"bottlehs"}}},"markdownRemark":{"id":"37e8e7cc-e3f8-5876-b12d-cfcd5df24276","excerpt":"FastAPI 완전정복 - 고성능 비동기 웹 API 개발 실전 가이드 FastAPI는 Python으로 고성능 웹 API를 빠르게 개발할 수 있게 해주는 현대적 웹 프레임워크다. Starlette와 Pydantic을 기반으로 하며, 자동 API…","html":"<h1 id=\"fastapi-완전정복---고성능-비동기-웹-api-개발-실전-가이드\" style=\"position:relative;\"><a href=\"#fastapi-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5---%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-api-%EA%B0%9C%EB%B0%9C-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C\" aria-label=\"fastapi 완전정복   고성능 비동기 웹 api 개발 실전 가이드 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>FastAPI 완전정복 - 고성능 비동기 웹 API 개발 실전 가이드</h1>\n<p>FastAPI는 Python으로 고성능 웹 API를 빠르게 개발할 수 있게 해주는 현대적 웹 프레임워크다. Starlette와 Pydantic을 기반으로 하며, 자동 API 문서 생성, 타입 힌팅, 비동기 지원 등 강력한 기능을 제공한다. 이 글은 FastAPI를 활용한 실전 웹 API 개발의 모든 것을 다룬다.</p>\n<h2 id=\"1-fastapi-개요와-시작하기\" style=\"position:relative;\"><a href=\"#1-fastapi-%EA%B0%9C%EC%9A%94%EC%99%80-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0\" aria-label=\"1 fastapi 개요와 시작하기 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1. FastAPI 개요와 시작하기</h2>\n<h3 id=\"1-1-fastapi의-특징\" style=\"position:relative;\"><a href=\"#1-1-fastapi%EC%9D%98-%ED%8A%B9%EC%A7%95\" aria-label=\"1 1 fastapi의 특징 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1-1. FastAPI의 특징</h3>\n<ul>\n<li><strong>높은 성능</strong>: NodeJS 및 Go와 비슷한 수준의 성능</li>\n<li><strong>빠른 개발</strong>: 타입 힌팅과 자동 검증으로 개발 속도 향상</li>\n<li><strong>자동 문서화</strong>: OpenAPI/Swagger UI 자동 생성</li>\n<li><strong>비동기 지원</strong>: asyncio 기반 비동기 프로그래밍</li>\n<li><strong>표준 기반</strong>: OpenAPI, JSON Schema 기반</li>\n</ul>\n<h3 id=\"1-2-기본-설치와-hello-world\" style=\"position:relative;\"><a href=\"#1-2-%EA%B8%B0%EB%B3%B8-%EC%84%A4%EC%B9%98%EC%99%80-hello-world\" aria-label=\"1 2 기본 설치와 hello world permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1-2. 기본 설치와 Hello World</h3>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">pip <span class=\"token function\">install</span> fastapi uvicorn<span class=\"token punctuation\">[</span>standard<span class=\"token punctuation\">]</span></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token comment\"># main.py</span>\n<span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> FastAPI\n<span class=\"token keyword\">from</span> fastapi<span class=\"token punctuation\">.</span>responses <span class=\"token keyword\">import</span> JSONResponse\n\napp <span class=\"token operator\">=</span> FastAPI<span class=\"token punctuation\">(</span>\n    title<span class=\"token operator\">=</span><span class=\"token string\">\"My API\"</span><span class=\"token punctuation\">,</span>\n    description<span class=\"token operator\">=</span><span class=\"token string\">\"FastAPI 완전정복 가이드\"</span><span class=\"token punctuation\">,</span>\n    version<span class=\"token operator\">=</span><span class=\"token string\">\"1.0.0\"</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">root</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"message\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Hello World\"</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/health\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">health_check</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> JSONResponse<span class=\"token punctuation\">(</span>\n        content<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"status\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"healthy\"</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        status_code<span class=\"token operator\">=</span><span class=\"token number\">200</span>\n    <span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># 서버 실행</span>\n<span class=\"token comment\"># uvicorn main:app --reload</span></code></pre></div>\n<h3 id=\"1-3-경로-매개변수와-쿼리-매개변수\" style=\"position:relative;\"><a href=\"#1-3-%EA%B2%BD%EB%A1%9C-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EC%99%80-%EC%BF%BC%EB%A6%AC-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98\" aria-label=\"1 3 경로 매개변수와 쿼리 매개변수 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1-3. 경로 매개변수와 쿼리 매개변수</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> FastAPI<span class=\"token punctuation\">,</span> Query<span class=\"token punctuation\">,</span> Path\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> Optional\n<span class=\"token keyword\">from</span> pydantic <span class=\"token keyword\">import</span> BaseModel\n\napp <span class=\"token operator\">=</span> FastAPI<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># 경로 매개변수</span>\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/{user_id}\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_user</span><span class=\"token punctuation\">(</span>user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"user_id\"</span><span class=\"token punctuation\">:</span> user_id<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\"># 쿼리 매개변수</span>\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/items/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">read_items</span><span class=\"token punctuation\">(</span>\n    skip<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n    limit<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> Query<span class=\"token punctuation\">(</span><span class=\"token number\">10</span><span class=\"token punctuation\">,</span> ge<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> le<span class=\"token operator\">=</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    q<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> Query<span class=\"token punctuation\">(</span><span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span> min_length<span class=\"token operator\">=</span><span class=\"token number\">3</span><span class=\"token punctuation\">,</span> max_length<span class=\"token operator\">=</span><span class=\"token number\">50</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string\">\"skip\"</span><span class=\"token punctuation\">:</span> skip<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"limit\"</span><span class=\"token punctuation\">:</span> limit<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"q\"</span><span class=\"token punctuation\">:</span> q\n    <span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\"># 경로와 쿼리 조합</span>\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/{user_id}/items/{item_id}\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_user_item</span><span class=\"token punctuation\">(</span>\n    user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> Path<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span> gt<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    item_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> Path<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span> gt<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    q<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span>\n    short<span class=\"token punctuation\">:</span> <span class=\"token builtin\">bool</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">False</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string\">\"user_id\"</span><span class=\"token punctuation\">:</span> user_id<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"item_id\"</span><span class=\"token punctuation\">:</span> item_id<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"q\"</span><span class=\"token punctuation\">:</span> q<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"short\"</span><span class=\"token punctuation\">:</span> short\n    <span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"2-pydantic-모델과-데이터-검증\" style=\"position:relative;\"><a href=\"#2-pydantic-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B2%80%EC%A6%9D\" aria-label=\"2 pydantic 모델과 데이터 검증 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2. Pydantic 모델과 데이터 검증</h2>\n<h3 id=\"2-1-기본-모델-정의\" style=\"position:relative;\"><a href=\"#2-1-%EA%B8%B0%EB%B3%B8-%EB%AA%A8%EB%8D%B8-%EC%A0%95%EC%9D%98\" aria-label=\"2 1 기본 모델 정의 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2-1. 기본 모델 정의</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> pydantic <span class=\"token keyword\">import</span> BaseModel<span class=\"token punctuation\">,</span> Field<span class=\"token punctuation\">,</span> EmailStr<span class=\"token punctuation\">,</span> validator\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> Optional<span class=\"token punctuation\">,</span> List\n<span class=\"token keyword\">from</span> datetime <span class=\"token keyword\">import</span> datetime\n<span class=\"token keyword\">from</span> enum <span class=\"token keyword\">import</span> Enum\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">UserRole</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> Enum<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    ADMIN <span class=\"token operator\">=</span> <span class=\"token string\">\"admin\"</span>\n    USER <span class=\"token operator\">=</span> <span class=\"token string\">\"user\"</span>\n    GUEST <span class=\"token operator\">=</span> <span class=\"token string\">\"guest\"</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">UserBase</span><span class=\"token punctuation\">(</span>BaseModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    email<span class=\"token punctuation\">:</span> EmailStr\n    full_name<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span> min_length<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> max_length<span class=\"token operator\">=</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n    age<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span> ge<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> le<span class=\"token operator\">=</span><span class=\"token number\">120</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">UserCreate</span><span class=\"token punctuation\">(</span>UserBase<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    password<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span> min_length<span class=\"token operator\">=</span><span class=\"token number\">8</span><span class=\"token punctuation\">,</span> max_length<span class=\"token operator\">=</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n    \n    <span class=\"token decorator annotation punctuation\">@validator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'password'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">validate_password</span><span class=\"token punctuation\">(</span>cls<span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">(</span>char<span class=\"token punctuation\">.</span>isdigit<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">for</span> char <span class=\"token keyword\">in</span> v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">raise</span> ValueError<span class=\"token punctuation\">(</span><span class=\"token string\">'Password must contain at least one digit'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> <span class=\"token builtin\">any</span><span class=\"token punctuation\">(</span>char<span class=\"token punctuation\">.</span>isupper<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">for</span> char <span class=\"token keyword\">in</span> v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">raise</span> ValueError<span class=\"token punctuation\">(</span><span class=\"token string\">'Password must contain at least one uppercase letter'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> v\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">UserResponse</span><span class=\"token punctuation\">(</span>UserBase<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token builtin\">id</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span>\n    role<span class=\"token punctuation\">:</span> UserRole\n    created_at<span class=\"token punctuation\">:</span> datetime\n    is_active<span class=\"token punctuation\">:</span> <span class=\"token builtin\">bool</span>\n    \n    <span class=\"token keyword\">class</span> <span class=\"token class-name\">Config</span><span class=\"token punctuation\">:</span>\n        from_attributes <span class=\"token operator\">=</span> <span class=\"token boolean\">True</span>  <span class=\"token comment\"># Pydantic v2 (이전에는 orm_mode=True)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">UserUpdate</span><span class=\"token punctuation\">(</span>BaseModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    email<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span>EmailStr<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">None</span>\n    full_name<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span> min_length<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> max_length<span class=\"token operator\">=</span><span class=\"token number\">100</span><span class=\"token punctuation\">)</span>\n    age<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span><span class=\"token builtin\">int</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span> ge<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> le<span class=\"token operator\">=</span><span class=\"token number\">120</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"2-2-중첩-모델과-리스트\" style=\"position:relative;\"><a href=\"#2-2-%EC%A4%91%EC%B2%A9-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EB%A6%AC%EC%8A%A4%ED%8A%B8\" aria-label=\"2 2 중첩 모델과 리스트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2-2. 중첩 모델과 리스트</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> List<span class=\"token punctuation\">,</span> Optional\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Item</span><span class=\"token punctuation\">(</span>BaseModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    name<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span>\n    description<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">None</span>\n    price<span class=\"token punctuation\">:</span> <span class=\"token builtin\">float</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span> gt<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">)</span>\n    tax<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span><span class=\"token builtin\">float</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token boolean\">None</span><span class=\"token punctuation\">,</span> ge<span class=\"token operator\">=</span><span class=\"token number\">0</span><span class=\"token punctuation\">,</span> le<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderCreate</span><span class=\"token punctuation\">(</span>BaseModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span>\n    items<span class=\"token punctuation\">:</span> List<span class=\"token punctuation\">[</span>Item<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> Field<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">,</span> min_items<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n    shipping_address<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">OrderResponse</span><span class=\"token punctuation\">(</span>BaseModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token builtin\">id</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span>\n    user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span>\n    items<span class=\"token punctuation\">:</span> List<span class=\"token punctuation\">[</span>Item<span class=\"token punctuation\">]</span>\n    total<span class=\"token punctuation\">:</span> <span class=\"token builtin\">float</span>\n    created_at<span class=\"token punctuation\">:</span> datetime\n    \n    <span class=\"token keyword\">class</span> <span class=\"token class-name\">Config</span><span class=\"token punctuation\">:</span>\n        from_attributes <span class=\"token operator\">=</span> <span class=\"token boolean\">True</span></code></pre></div>\n<h3 id=\"2-3-커스텀-검증자\" style=\"position:relative;\"><a href=\"#2-3-%EC%BB%A4%EC%8A%A4%ED%85%80-%EA%B2%80%EC%A6%9D%EC%9E%90\" aria-label=\"2 3 커스텀 검증자 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2-3. 커스텀 검증자</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> pydantic <span class=\"token keyword\">import</span> validator<span class=\"token punctuation\">,</span> root_validator\n<span class=\"token keyword\">import</span> re\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">UserRegistration</span><span class=\"token punctuation\">(</span>BaseModel<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    username<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span>\n    email<span class=\"token punctuation\">:</span> EmailStr\n    password<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span>\n    confirm_password<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span>\n    phone<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">None</span>\n    \n    <span class=\"token decorator annotation punctuation\">@validator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'username'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">validate_username</span><span class=\"token punctuation\">(</span>cls<span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> re<span class=\"token punctuation\">.</span><span class=\"token keyword\">match</span><span class=\"token punctuation\">(</span><span class=\"token string\">r'^[a-zA-Z0-9_]{3,20}$'</span><span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">raise</span> ValueError<span class=\"token punctuation\">(</span><span class=\"token string\">'Username must be 3-20 alphanumeric characters or underscores'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> v\n    \n    <span class=\"token decorator annotation punctuation\">@validator</span><span class=\"token punctuation\">(</span><span class=\"token string\">'phone'</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">validate_phone</span><span class=\"token punctuation\">(</span>cls<span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> v <span class=\"token keyword\">and</span> <span class=\"token keyword\">not</span> re<span class=\"token punctuation\">.</span><span class=\"token keyword\">match</span><span class=\"token punctuation\">(</span><span class=\"token string\">r'^\\+?[1-9]\\d{1,14}$'</span><span class=\"token punctuation\">,</span> v<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">raise</span> ValueError<span class=\"token punctuation\">(</span><span class=\"token string\">'Invalid phone number format'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> v\n    \n    <span class=\"token decorator annotation punctuation\">@root_validator</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">validate_passwords_match</span><span class=\"token punctuation\">(</span>cls<span class=\"token punctuation\">,</span> values<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        password <span class=\"token operator\">=</span> values<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">'password'</span><span class=\"token punctuation\">)</span>\n        confirm_password <span class=\"token operator\">=</span> values<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">'confirm_password'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">if</span> password <span class=\"token operator\">!=</span> confirm_password<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">raise</span> ValueError<span class=\"token punctuation\">(</span><span class=\"token string\">'Passwords do not match'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> values</code></pre></div>\n<h2 id=\"3-의존성-주입-dependency-injection\" style=\"position:relative;\"><a href=\"#3-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-dependency-injection\" aria-label=\"3 의존성 주입 dependency injection permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3. 의존성 주입 (Dependency Injection)</h2>\n<h3 id=\"3-1-기본-의존성\" style=\"position:relative;\"><a href=\"#3-1-%EA%B8%B0%EB%B3%B8-%EC%9D%98%EC%A1%B4%EC%84%B1\" aria-label=\"3 1 기본 의존성 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3-1. 기본 의존성</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> Depends<span class=\"token punctuation\">,</span> FastAPI<span class=\"token punctuation\">,</span> HTTPException\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> Optional\n\napp <span class=\"token operator\">=</span> FastAPI<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># 간단한 의존성</span>\n<span class=\"token keyword\">def</span> <span class=\"token function\">get_db</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    db <span class=\"token operator\">=</span> <span class=\"token string\">\"database_connection\"</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">yield</span> db\n    <span class=\"token keyword\">finally</span><span class=\"token punctuation\">:</span>\n        <span class=\"token comment\"># 클린업 코드</span>\n        <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Closing database connection\"</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/items/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">read_items</span><span class=\"token punctuation\">(</span>db<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span> <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"db\"</span><span class=\"token punctuation\">:</span> db<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\"># 의존성 체인</span>\n<span class=\"token keyword\">def</span> <span class=\"token function\">get_query_token</span><span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span> <span class=\"token operator\">=</span> Query<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> token\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">get_token_header</span><span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span> <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_query_token<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">if</span> token <span class=\"token operator\">!=</span> <span class=\"token string\">\"secret-token\"</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> HTTPException<span class=\"token punctuation\">(</span>status_code<span class=\"token operator\">=</span><span class=\"token number\">403</span><span class=\"token punctuation\">,</span> detail<span class=\"token operator\">=</span><span class=\"token string\">\"Invalid token\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> token\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/protected/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">protected_route</span><span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span> <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_token_header<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"message\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Access granted\"</span><span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"3-2-클래스-기반-의존성\" style=\"position:relative;\"><a href=\"#3-2-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B8%B0%EB%B0%98-%EC%9D%98%EC%A1%B4%EC%84%B1\" aria-label=\"3 2 클래스 기반 의존성 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3-2. 클래스 기반 의존성</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> Depends\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> Optional\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">PaginationParams</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">__init__</span><span class=\"token punctuation\">(</span>\n        self<span class=\"token punctuation\">,</span>\n        skip<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n        limit<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> <span class=\"token number\">10</span>\n    <span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        self<span class=\"token punctuation\">.</span>skip <span class=\"token operator\">=</span> skip\n        self<span class=\"token punctuation\">.</span>limit <span class=\"token operator\">=</span> limit\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/items/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">read_items</span><span class=\"token punctuation\">(</span>pagination<span class=\"token punctuation\">:</span> PaginationParams <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string\">\"skip\"</span><span class=\"token punctuation\">:</span> pagination<span class=\"token punctuation\">.</span>skip<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"limit\"</span><span class=\"token punctuation\">:</span> pagination<span class=\"token punctuation\">.</span>limit\n    <span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"3-3-데이터베이스-세션-의존성\" style=\"position:relative;\"><a href=\"#3-3-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%B8%EC%85%98-%EC%9D%98%EC%A1%B4%EC%84%B1\" aria-label=\"3 3 데이터베이스 세션 의존성 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3-3. 데이터베이스 세션 의존성</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> sqlalchemy<span class=\"token punctuation\">.</span>ext<span class=\"token punctuation\">.</span>asyncio <span class=\"token keyword\">import</span> AsyncSession<span class=\"token punctuation\">,</span> create_async_engine\n<span class=\"token keyword\">from</span> sqlalchemy<span class=\"token punctuation\">.</span>orm <span class=\"token keyword\">import</span> sessionmaker\n<span class=\"token keyword\">from</span> contextlib <span class=\"token keyword\">import</span> asynccontextmanager\n\n<span class=\"token comment\"># 데이터베이스 설정</span>\nDATABASE_URL <span class=\"token operator\">=</span> <span class=\"token string\">\"postgresql+asyncpg://user:password@localhost/dbname\"</span>\n\nengine <span class=\"token operator\">=</span> create_async_engine<span class=\"token punctuation\">(</span>DATABASE_URL<span class=\"token punctuation\">,</span> echo<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\nAsyncSessionLocal <span class=\"token operator\">=</span> sessionmaker<span class=\"token punctuation\">(</span>\n    engine<span class=\"token punctuation\">,</span> class_<span class=\"token operator\">=</span>AsyncSession<span class=\"token punctuation\">,</span> expire_on_commit<span class=\"token operator\">=</span><span class=\"token boolean\">False</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_db</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> AsyncSession<span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">with</span> AsyncSessionLocal<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> session<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">yield</span> session\n            <span class=\"token keyword\">await</span> session<span class=\"token punctuation\">.</span>commit<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">except</span> Exception<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">await</span> session<span class=\"token punctuation\">.</span>rollback<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token keyword\">raise</span>\n        <span class=\"token keyword\">finally</span><span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">await</span> session<span class=\"token punctuation\">.</span>close<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_users</span><span class=\"token punctuation\">(</span>db<span class=\"token punctuation\">:</span> AsyncSession <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> db<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>select<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    users <span class=\"token operator\">=</span> result<span class=\"token punctuation\">.</span>scalars<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token builtin\">all</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> users</code></pre></div>\n<h2 id=\"4-비동기-프로그래밍-심화\" style=\"position:relative;\"><a href=\"#4-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%AC%ED%99%94\" aria-label=\"4 비동기 프로그래밍 심화 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4. 비동기 프로그래밍 심화</h2>\n<h3 id=\"4-1-비동기-함수와-await\" style=\"position:relative;\"><a href=\"#4-1-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98%EC%99%80-await\" aria-label=\"4 1 비동기 함수와 await permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4-1. 비동기 함수와 await</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> asyncio\n<span class=\"token keyword\">import</span> aiohttp\n<span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> FastAPI\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> List\n\napp <span class=\"token operator\">=</span> FastAPI<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">fetch_data</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> <span class=\"token builtin\">dict</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">with</span> aiohttp<span class=\"token punctuation\">.</span>ClientSession<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> session<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">async</span> <span class=\"token keyword\">with</span> session<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> response<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span>json<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/fetch-multiple/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">fetch_multiple_urls</span><span class=\"token punctuation\">(</span>urls<span class=\"token punctuation\">:</span> List<span class=\"token punctuation\">[</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token comment\"># 병렬 실행</span>\n    tasks <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span>fetch_data<span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span> <span class=\"token keyword\">for</span> url <span class=\"token keyword\">in</span> urls<span class=\"token punctuation\">]</span>\n    results <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> asyncio<span class=\"token punctuation\">.</span>gather<span class=\"token punctuation\">(</span><span class=\"token operator\">*</span>tasks<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> results\n\n<span class=\"token comment\"># 타임아웃 처리</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">fetch_with_timeout</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> timeout<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> <span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> asyncio<span class=\"token punctuation\">.</span>wait_for<span class=\"token punctuation\">(</span>fetch_data<span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> timeout<span class=\"token operator\">=</span>timeout<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">except</span> asyncio<span class=\"token punctuation\">.</span>TimeoutError<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"error\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Request timeout\"</span><span class=\"token punctuation\">}</span></code></pre></div>\n<h3 id=\"4-2-백그라운드-작업\" style=\"position:relative;\"><a href=\"#4-2-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%9E%91%EC%97%85\" aria-label=\"4 2 백그라운드 작업 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4-2. 백그라운드 작업</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> BackgroundTasks\n<span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> FastAPI\n\napp <span class=\"token operator\">=</span> FastAPI<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">send_email</span><span class=\"token punctuation\">(</span>email<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> message<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token comment\"># 이메일 전송 로직</span>\n    <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"Sending email to </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>email<span class=\"token punctuation\">}</span></span><span class=\"token string\">: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>message<span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">log_activity</span><span class=\"token punctuation\">(</span>user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">,</span> action<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token comment\"># 활동 로깅</span>\n    <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"User </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>user_id<span class=\"token punctuation\">}</span></span><span class=\"token string\"> performed </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>action<span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/\"</span><span class=\"token punctuation\">,</span> response_model<span class=\"token operator\">=</span>UserResponse<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">create_user</span><span class=\"token punctuation\">(</span>\n    user<span class=\"token punctuation\">:</span> UserCreate<span class=\"token punctuation\">,</span>\n    background_tasks<span class=\"token punctuation\">:</span> BackgroundTasks<span class=\"token punctuation\">,</span>\n    db<span class=\"token punctuation\">:</span> AsyncSession <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token comment\"># 사용자 생성</span>\n    db_user <span class=\"token operator\">=</span> User<span class=\"token punctuation\">(</span><span class=\"token operator\">**</span>user<span class=\"token punctuation\">.</span><span class=\"token builtin\">dict</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    db<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">(</span>db_user<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">await</span> db<span class=\"token punctuation\">.</span>commit<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">await</span> db<span class=\"token punctuation\">.</span>refresh<span class=\"token punctuation\">(</span>db_user<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token comment\"># 백그라운드 작업 추가</span>\n    background_tasks<span class=\"token punctuation\">.</span>add_task<span class=\"token punctuation\">(</span>send_email<span class=\"token punctuation\">,</span> user<span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">,</span> <span class=\"token string\">\"Welcome!\"</span><span class=\"token punctuation\">)</span>\n    background_tasks<span class=\"token punctuation\">.</span>add_task<span class=\"token punctuation\">(</span>log_activity<span class=\"token punctuation\">,</span> db_user<span class=\"token punctuation\">.</span><span class=\"token builtin\">id</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"user_created\"</span><span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">return</span> db_user</code></pre></div>\n<h3 id=\"4-3-비동기-이벤트-핸들러\" style=\"position:relative;\"><a href=\"#4-3-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%9F%AC\" aria-label=\"4 3 비동기 이벤트 핸들러 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4-3. 비동기 이벤트 핸들러</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> contextlib <span class=\"token keyword\">import</span> asynccontextmanager\n\n<span class=\"token decorator annotation punctuation\">@asynccontextmanager</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">lifespan</span><span class=\"token punctuation\">(</span>app<span class=\"token punctuation\">:</span> FastAPI<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token comment\"># 시작 시 실행</span>\n    <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Starting up...\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token comment\"># 초기화 작업 (DB 연결, 캐시 설정 등)</span>\n    <span class=\"token keyword\">yield</span>\n    <span class=\"token comment\"># 종료 시 실행</span>\n    <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Shutting down...\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token comment\"># 클린업 작업</span>\n\napp <span class=\"token operator\">=</span> FastAPI<span class=\"token punctuation\">(</span>lifespan<span class=\"token operator\">=</span>lifespan<span class=\"token punctuation\">)</span></code></pre></div>\n<h2 id=\"5-데이터베이스-연동\" style=\"position:relative;\"><a href=\"#5-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EB%8F%99\" aria-label=\"5 데이터베이스 연동 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>5. 데이터베이스 연동</h2>\n<h3 id=\"5-1-sqlalchemy-비동기-설정\" style=\"position:relative;\"><a href=\"#5-1-sqlalchemy-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%84%A4%EC%A0%95\" aria-label=\"5 1 sqlalchemy 비동기 설정 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>5-1. SQLAlchemy 비동기 설정</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> sqlalchemy<span class=\"token punctuation\">.</span>ext<span class=\"token punctuation\">.</span>asyncio <span class=\"token keyword\">import</span> AsyncSession<span class=\"token punctuation\">,</span> create_async_engine\n<span class=\"token keyword\">from</span> sqlalchemy<span class=\"token punctuation\">.</span>ext<span class=\"token punctuation\">.</span>declarative <span class=\"token keyword\">import</span> declarative_base\n<span class=\"token keyword\">from</span> sqlalchemy<span class=\"token punctuation\">.</span>orm <span class=\"token keyword\">import</span> sessionmaker\n<span class=\"token keyword\">from</span> sqlalchemy <span class=\"token keyword\">import</span> Column<span class=\"token punctuation\">,</span> Integer<span class=\"token punctuation\">,</span> String<span class=\"token punctuation\">,</span> DateTime<span class=\"token punctuation\">,</span> Boolean\n<span class=\"token keyword\">from</span> datetime <span class=\"token keyword\">import</span> datetime\n\nBase <span class=\"token operator\">=</span> declarative_base<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">User</span><span class=\"token punctuation\">(</span>Base<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    __tablename__ <span class=\"token operator\">=</span> <span class=\"token string\">\"users\"</span>\n    \n    <span class=\"token builtin\">id</span> <span class=\"token operator\">=</span> Column<span class=\"token punctuation\">(</span>Integer<span class=\"token punctuation\">,</span> primary_key<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">,</span> index<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n    email <span class=\"token operator\">=</span> Column<span class=\"token punctuation\">(</span>String<span class=\"token punctuation\">,</span> unique<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">,</span> index<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">,</span> nullable<span class=\"token operator\">=</span><span class=\"token boolean\">False</span><span class=\"token punctuation\">)</span>\n    full_name <span class=\"token operator\">=</span> Column<span class=\"token punctuation\">(</span>String<span class=\"token punctuation\">,</span> nullable<span class=\"token operator\">=</span><span class=\"token boolean\">False</span><span class=\"token punctuation\">)</span>\n    hashed_password <span class=\"token operator\">=</span> Column<span class=\"token punctuation\">(</span>String<span class=\"token punctuation\">,</span> nullable<span class=\"token operator\">=</span><span class=\"token boolean\">False</span><span class=\"token punctuation\">)</span>\n    is_active <span class=\"token operator\">=</span> Column<span class=\"token punctuation\">(</span>Boolean<span class=\"token punctuation\">,</span> default<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n    created_at <span class=\"token operator\">=</span> Column<span class=\"token punctuation\">(</span>DateTime<span class=\"token punctuation\">,</span> default<span class=\"token operator\">=</span>datetime<span class=\"token punctuation\">.</span>utcnow<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># 엔진과 세션 생성</span>\nDATABASE_URL <span class=\"token operator\">=</span> <span class=\"token string\">\"postgresql+asyncpg://user:password@localhost/dbname\"</span>\n\nengine <span class=\"token operator\">=</span> create_async_engine<span class=\"token punctuation\">(</span>\n    DATABASE_URL<span class=\"token punctuation\">,</span>\n    echo<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">,</span>\n    future<span class=\"token operator\">=</span><span class=\"token boolean\">True</span>\n<span class=\"token punctuation\">)</span>\n\nAsyncSessionLocal <span class=\"token operator\">=</span> sessionmaker<span class=\"token punctuation\">(</span>\n    engine<span class=\"token punctuation\">,</span>\n    class_<span class=\"token operator\">=</span>AsyncSession<span class=\"token punctuation\">,</span>\n    expire_on_commit<span class=\"token operator\">=</span><span class=\"token boolean\">False</span>\n<span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\"># 테이블 생성</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">init_db</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">with</span> engine<span class=\"token punctuation\">.</span>begin<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> conn<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">await</span> conn<span class=\"token punctuation\">.</span>run_sync<span class=\"token punctuation\">(</span>Base<span class=\"token punctuation\">.</span>metadata<span class=\"token punctuation\">.</span>create_all<span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"5-2-crud-작업\" style=\"position:relative;\"><a href=\"#5-2-crud-%EC%9E%91%EC%97%85\" aria-label=\"5 2 crud 작업 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>5-2. CRUD 작업</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> sqlalchemy <span class=\"token keyword\">import</span> select<span class=\"token punctuation\">,</span> update<span class=\"token punctuation\">,</span> delete\n<span class=\"token keyword\">from</span> sqlalchemy<span class=\"token punctuation\">.</span>ext<span class=\"token punctuation\">.</span>asyncio <span class=\"token keyword\">import</span> AsyncSession\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> List<span class=\"token punctuation\">,</span> Optional\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">UserRepository</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">__init__</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> db<span class=\"token punctuation\">:</span> AsyncSession<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        self<span class=\"token punctuation\">.</span>db <span class=\"token operator\">=</span> db\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">create</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> user_data<span class=\"token punctuation\">:</span> UserCreate<span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> User<span class=\"token punctuation\">:</span>\n        hashed_password <span class=\"token operator\">=</span> get_password_hash<span class=\"token punctuation\">(</span>user_data<span class=\"token punctuation\">.</span>password<span class=\"token punctuation\">)</span>\n        db_user <span class=\"token operator\">=</span> User<span class=\"token punctuation\">(</span>\n            email<span class=\"token operator\">=</span>user_data<span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">,</span>\n            full_name<span class=\"token operator\">=</span>user_data<span class=\"token punctuation\">.</span>full_name<span class=\"token punctuation\">,</span>\n            hashed_password<span class=\"token operator\">=</span>hashed_password\n        <span class=\"token punctuation\">)</span>\n        self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>add<span class=\"token punctuation\">(</span>db_user<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>commit<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>refresh<span class=\"token punctuation\">(</span>db_user<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> db_user\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_by_id</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> Optional<span class=\"token punctuation\">[</span>User<span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span>\n        result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>\n            select<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>where<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">.</span><span class=\"token builtin\">id</span> <span class=\"token operator\">==</span> user_id<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">.</span>scalar_one_or_none<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_by_email</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> email<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> Optional<span class=\"token punctuation\">[</span>User<span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span>\n        result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>\n            select<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>where<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">.</span>email <span class=\"token operator\">==</span> email<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">.</span>scalar_one_or_none<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_all</span><span class=\"token punctuation\">(</span>\n        self<span class=\"token punctuation\">,</span>\n        skip<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> <span class=\"token number\">0</span><span class=\"token punctuation\">,</span>\n        limit<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> <span class=\"token number\">100</span>\n    <span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> List<span class=\"token punctuation\">[</span>User<span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span>\n        result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>\n            select<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span>offset<span class=\"token punctuation\">(</span>skip<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span>limit<span class=\"token punctuation\">(</span>limit<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">.</span>scalars<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token builtin\">all</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">update</span><span class=\"token punctuation\">(</span>\n        self<span class=\"token punctuation\">,</span>\n        user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">,</span>\n        user_update<span class=\"token punctuation\">:</span> UserUpdate\n    <span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> Optional<span class=\"token punctuation\">[</span>User<span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span>\n        update_data <span class=\"token operator\">=</span> user_update<span class=\"token punctuation\">.</span><span class=\"token builtin\">dict</span><span class=\"token punctuation\">(</span>exclude_unset<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> update_data<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>get_by_id<span class=\"token punctuation\">(</span>user_id<span class=\"token punctuation\">)</span>\n        \n        <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>\n            update<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span>where<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">.</span><span class=\"token builtin\">id</span> <span class=\"token operator\">==</span> user_id<span class=\"token punctuation\">)</span>\n            <span class=\"token punctuation\">.</span>values<span class=\"token punctuation\">(</span><span class=\"token operator\">**</span>update_data<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>commit<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>get_by_id<span class=\"token punctuation\">(</span>user_id<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">delete</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> <span class=\"token builtin\">bool</span><span class=\"token punctuation\">:</span>\n        result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>\n            delete<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>where<span class=\"token punctuation\">(</span>User<span class=\"token punctuation\">.</span><span class=\"token builtin\">id</span> <span class=\"token operator\">==</span> user_id<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">await</span> self<span class=\"token punctuation\">.</span>db<span class=\"token punctuation\">.</span>commit<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">.</span>rowcount <span class=\"token operator\">></span> <span class=\"token number\">0</span>\n\n<span class=\"token comment\"># API 엔드포인트</span>\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/\"</span><span class=\"token punctuation\">,</span> response_model<span class=\"token operator\">=</span>UserResponse<span class=\"token punctuation\">,</span> status_code<span class=\"token operator\">=</span><span class=\"token number\">201</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">create_user</span><span class=\"token punctuation\">(</span>\n    user<span class=\"token punctuation\">:</span> UserCreate<span class=\"token punctuation\">,</span>\n    db<span class=\"token punctuation\">:</span> AsyncSession <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    repository <span class=\"token operator\">=</span> UserRepository<span class=\"token punctuation\">(</span>db<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token comment\"># 이메일 중복 확인</span>\n    existing_user <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> repository<span class=\"token punctuation\">.</span>get_by_email<span class=\"token punctuation\">(</span>user<span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> existing_user<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> HTTPException<span class=\"token punctuation\">(</span>\n            status_code<span class=\"token operator\">=</span><span class=\"token number\">400</span><span class=\"token punctuation\">,</span>\n            detail<span class=\"token operator\">=</span><span class=\"token string\">\"Email already registered\"</span>\n        <span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">return</span> <span class=\"token keyword\">await</span> repository<span class=\"token punctuation\">.</span>create<span class=\"token punctuation\">(</span>user<span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/{user_id}\"</span><span class=\"token punctuation\">,</span> response_model<span class=\"token operator\">=</span>UserResponse<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_user</span><span class=\"token punctuation\">(</span>\n    user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">,</span>\n    db<span class=\"token punctuation\">:</span> AsyncSession <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    repository <span class=\"token operator\">=</span> UserRepository<span class=\"token punctuation\">(</span>db<span class=\"token punctuation\">)</span>\n    user <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> repository<span class=\"token punctuation\">.</span>get_by_id<span class=\"token punctuation\">(</span>user_id<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> user<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> HTTPException<span class=\"token punctuation\">(</span>status_code<span class=\"token operator\">=</span><span class=\"token number\">404</span><span class=\"token punctuation\">,</span> detail<span class=\"token operator\">=</span><span class=\"token string\">\"User not found\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> user</code></pre></div>\n<h2 id=\"6-인증과-인가\" style=\"position:relative;\"><a href=\"#6-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80\" aria-label=\"6 인증과 인가 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6. 인증과 인가</h2>\n<h3 id=\"6-1-jwt-인증-구현\" style=\"position:relative;\"><a href=\"#6-1-jwt-%EC%9D%B8%EC%A6%9D-%EA%B5%AC%ED%98%84\" aria-label=\"6 1 jwt 인증 구현 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6-1. JWT 인증 구현</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> datetime <span class=\"token keyword\">import</span> datetime<span class=\"token punctuation\">,</span> timedelta\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> Optional\n<span class=\"token keyword\">from</span> jose <span class=\"token keyword\">import</span> JWTError<span class=\"token punctuation\">,</span> jwt\n<span class=\"token keyword\">from</span> passlib<span class=\"token punctuation\">.</span>context <span class=\"token keyword\">import</span> CryptContext\n<span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> Depends<span class=\"token punctuation\">,</span> HTTPException<span class=\"token punctuation\">,</span> status\n<span class=\"token keyword\">from</span> fastapi<span class=\"token punctuation\">.</span>security <span class=\"token keyword\">import</span> OAuth2PasswordBearer<span class=\"token punctuation\">,</span> OAuth2PasswordRequestForm\n\n<span class=\"token comment\"># 설정</span>\nSECRET_KEY <span class=\"token operator\">=</span> <span class=\"token string\">\"your-secret-key\"</span>\nALGORITHM <span class=\"token operator\">=</span> <span class=\"token string\">\"HS256\"</span>\nACCESS_TOKEN_EXPIRE_MINUTES <span class=\"token operator\">=</span> <span class=\"token number\">30</span>\n\npwd_context <span class=\"token operator\">=</span> CryptContext<span class=\"token punctuation\">(</span>schemes<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"bcrypt\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> deprecated<span class=\"token operator\">=</span><span class=\"token string\">\"auto\"</span><span class=\"token punctuation\">)</span>\noauth2_scheme <span class=\"token operator\">=</span> OAuth2PasswordBearer<span class=\"token punctuation\">(</span>tokenUrl<span class=\"token operator\">=</span><span class=\"token string\">\"token\"</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">verify_password</span><span class=\"token punctuation\">(</span>plain_password<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> hashed_password<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> <span class=\"token builtin\">bool</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> pwd_context<span class=\"token punctuation\">.</span>verify<span class=\"token punctuation\">(</span>plain_password<span class=\"token punctuation\">,</span> hashed_password<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">get_password_hash</span><span class=\"token punctuation\">(</span>password<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> pwd_context<span class=\"token punctuation\">.</span><span class=\"token builtin\">hash</span><span class=\"token punctuation\">(</span>password<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">create_access_token</span><span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">:</span> <span class=\"token builtin\">dict</span><span class=\"token punctuation\">,</span> expires_delta<span class=\"token punctuation\">:</span> Optional<span class=\"token punctuation\">[</span>timedelta<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token boolean\">None</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    to_encode <span class=\"token operator\">=</span> data<span class=\"token punctuation\">.</span>copy<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> expires_delta<span class=\"token punctuation\">:</span>\n        expire <span class=\"token operator\">=</span> datetime<span class=\"token punctuation\">.</span>utcnow<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> expires_delta\n    <span class=\"token keyword\">else</span><span class=\"token punctuation\">:</span>\n        expire <span class=\"token operator\">=</span> datetime<span class=\"token punctuation\">.</span>utcnow<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">+</span> timedelta<span class=\"token punctuation\">(</span>minutes<span class=\"token operator\">=</span><span class=\"token number\">15</span><span class=\"token punctuation\">)</span>\n    to_encode<span class=\"token punctuation\">.</span>update<span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"exp\"</span><span class=\"token punctuation\">:</span> expire<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    encoded_jwt <span class=\"token operator\">=</span> jwt<span class=\"token punctuation\">.</span>encode<span class=\"token punctuation\">(</span>to_encode<span class=\"token punctuation\">,</span> SECRET_KEY<span class=\"token punctuation\">,</span> algorithm<span class=\"token operator\">=</span>ALGORITHM<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> encoded_jwt\n\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">get_current_user</span><span class=\"token punctuation\">(</span>\n    token<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span> <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>oauth2_scheme<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    db<span class=\"token punctuation\">:</span> AsyncSession <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span><span class=\"token operator\">></span> User<span class=\"token punctuation\">:</span>\n    credentials_exception <span class=\"token operator\">=</span> HTTPException<span class=\"token punctuation\">(</span>\n        status_code<span class=\"token operator\">=</span>status<span class=\"token punctuation\">.</span>HTTP_401_UNAUTHORIZED<span class=\"token punctuation\">,</span>\n        detail<span class=\"token operator\">=</span><span class=\"token string\">\"Could not validate credentials\"</span><span class=\"token punctuation\">,</span>\n        headers<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"WWW-Authenticate\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Bearer\"</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        payload <span class=\"token operator\">=</span> jwt<span class=\"token punctuation\">.</span>decode<span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">,</span> SECRET_KEY<span class=\"token punctuation\">,</span> algorithms<span class=\"token operator\">=</span><span class=\"token punctuation\">[</span>ALGORITHM<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> payload<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">\"sub\"</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">if</span> user_id <span class=\"token keyword\">is</span> <span class=\"token boolean\">None</span><span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">raise</span> credentials_exception\n    <span class=\"token keyword\">except</span> JWTError<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> credentials_exception\n    \n    repository <span class=\"token operator\">=</span> UserRepository<span class=\"token punctuation\">(</span>db<span class=\"token punctuation\">)</span>\n    user <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> repository<span class=\"token punctuation\">.</span>get_by_id<span class=\"token punctuation\">(</span>user_id<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> user <span class=\"token keyword\">is</span> <span class=\"token boolean\">None</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> credentials_exception\n    <span class=\"token keyword\">return</span> user\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/token\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">login</span><span class=\"token punctuation\">(</span>\n    form_data<span class=\"token punctuation\">:</span> OAuth2PasswordRequestForm <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    db<span class=\"token punctuation\">:</span> AsyncSession <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    repository <span class=\"token operator\">=</span> UserRepository<span class=\"token punctuation\">(</span>db<span class=\"token punctuation\">)</span>\n    user <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> repository<span class=\"token punctuation\">.</span>get_by_email<span class=\"token punctuation\">(</span>form_data<span class=\"token punctuation\">.</span>username<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> user <span class=\"token keyword\">or</span> <span class=\"token keyword\">not</span> verify_password<span class=\"token punctuation\">(</span>form_data<span class=\"token punctuation\">.</span>password<span class=\"token punctuation\">,</span> user<span class=\"token punctuation\">.</span>hashed_password<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> HTTPException<span class=\"token punctuation\">(</span>\n            status_code<span class=\"token operator\">=</span>status<span class=\"token punctuation\">.</span>HTTP_401_UNAUTHORIZED<span class=\"token punctuation\">,</span>\n            detail<span class=\"token operator\">=</span><span class=\"token string\">\"Incorrect username or password\"</span><span class=\"token punctuation\">,</span>\n            headers<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"WWW-Authenticate\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Bearer\"</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">)</span>\n    access_token_expires <span class=\"token operator\">=</span> timedelta<span class=\"token punctuation\">(</span>minutes<span class=\"token operator\">=</span>ACCESS_TOKEN_EXPIRE_MINUTES<span class=\"token punctuation\">)</span>\n    access_token <span class=\"token operator\">=</span> create_access_token<span class=\"token punctuation\">(</span>\n        data<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"sub\"</span><span class=\"token punctuation\">:</span> user<span class=\"token punctuation\">.</span><span class=\"token builtin\">id</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> expires_delta<span class=\"token operator\">=</span>access_token_expires\n    <span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"access_token\"</span><span class=\"token punctuation\">:</span> access_token<span class=\"token punctuation\">,</span> <span class=\"token string\">\"token_type\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"bearer\"</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/me/\"</span><span class=\"token punctuation\">,</span> response_model<span class=\"token operator\">=</span>UserResponse<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">read_users_me</span><span class=\"token punctuation\">(</span>current_user<span class=\"token punctuation\">:</span> User <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_current_user<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> current_user</code></pre></div>\n<h3 id=\"6-2-역할-기반-접근-제어-rbac\" style=\"position:relative;\"><a href=\"#6-2-%EC%97%AD%ED%95%A0-%EA%B8%B0%EB%B0%98-%EC%A0%91%EA%B7%BC-%EC%A0%9C%EC%96%B4-rbac\" aria-label=\"6 2 역할 기반 접근 제어 rbac permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6-2. 역할 기반 접근 제어 (RBAC)</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> enum <span class=\"token keyword\">import</span> Enum\n<span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> Depends<span class=\"token punctuation\">,</span> HTTPException<span class=\"token punctuation\">,</span> status\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">Role</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> Enum<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    ADMIN <span class=\"token operator\">=</span> <span class=\"token string\">\"admin\"</span>\n    USER <span class=\"token operator\">=</span> <span class=\"token string\">\"user\"</span>\n    GUEST <span class=\"token operator\">=</span> <span class=\"token string\">\"guest\"</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">require_role</span><span class=\"token punctuation\">(</span>required_role<span class=\"token punctuation\">:</span> Role<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">role_checker</span><span class=\"token punctuation\">(</span>current_user<span class=\"token punctuation\">:</span> User <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_current_user<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> current_user<span class=\"token punctuation\">.</span>role <span class=\"token operator\">!=</span> required_role<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">raise</span> HTTPException<span class=\"token punctuation\">(</span>\n                status_code<span class=\"token operator\">=</span>status<span class=\"token punctuation\">.</span>HTTP_403_FORBIDDEN<span class=\"token punctuation\">,</span>\n                detail<span class=\"token operator\">=</span><span class=\"token string\">\"Not enough permissions\"</span>\n            <span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> current_user\n    <span class=\"token keyword\">return</span> role_checker\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>delete</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/{user_id}\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">delete_user</span><span class=\"token punctuation\">(</span>\n    user_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">,</span>\n    current_user<span class=\"token punctuation\">:</span> User <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>require_role<span class=\"token punctuation\">(</span>Role<span class=\"token punctuation\">.</span>ADMIN<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    db<span class=\"token punctuation\">:</span> AsyncSession <span class=\"token operator\">=</span> Depends<span class=\"token punctuation\">(</span>get_db<span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    repository <span class=\"token operator\">=</span> UserRepository<span class=\"token punctuation\">(</span>db<span class=\"token punctuation\">)</span>\n    success <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> repository<span class=\"token punctuation\">.</span>delete<span class=\"token punctuation\">(</span>user_id<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> <span class=\"token keyword\">not</span> success<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">raise</span> HTTPException<span class=\"token punctuation\">(</span>status_code<span class=\"token operator\">=</span><span class=\"token number\">404</span><span class=\"token punctuation\">,</span> detail<span class=\"token operator\">=</span><span class=\"token string\">\"User not found\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"message\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"User deleted\"</span><span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"7-websocket과-실시간-통신\" style=\"position:relative;\"><a href=\"#7-websocket%EA%B3%BC-%EC%8B%A4%EC%8B%9C%EA%B0%84-%ED%86%B5%EC%8B%A0\" aria-label=\"7 websocket과 실시간 통신 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>7. WebSocket과 실시간 통신</h2>\n<h3 id=\"7-1-기본-websocket\" style=\"position:relative;\"><a href=\"#7-1-%EA%B8%B0%EB%B3%B8-websocket\" aria-label=\"7 1 기본 websocket permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>7-1. 기본 WebSocket</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> WebSocket<span class=\"token punctuation\">,</span> WebSocketDisconnect\n<span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> List\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ConnectionManager</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">__init__</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        self<span class=\"token punctuation\">.</span>active_connections<span class=\"token punctuation\">:</span> List<span class=\"token punctuation\">[</span>WebSocket<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">connect</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> websocket<span class=\"token punctuation\">:</span> WebSocket<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">await</span> websocket<span class=\"token punctuation\">.</span>accept<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        self<span class=\"token punctuation\">.</span>active_connections<span class=\"token punctuation\">.</span>append<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">def</span> <span class=\"token function\">disconnect</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> websocket<span class=\"token punctuation\">:</span> WebSocket<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        self<span class=\"token punctuation\">.</span>active_connections<span class=\"token punctuation\">.</span>remove<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">send_personal_message</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> message<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> websocket<span class=\"token punctuation\">:</span> WebSocket<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">await</span> websocket<span class=\"token punctuation\">.</span>send_text<span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">broadcast</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> message<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">for</span> connection <span class=\"token keyword\">in</span> self<span class=\"token punctuation\">.</span>active_connections<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">await</span> connection<span class=\"token punctuation\">.</span>send_text<span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span>\n\nmanager <span class=\"token operator\">=</span> ConnectionManager<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>websocket</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/ws/{client_id}\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">websocket_endpoint</span><span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">:</span> WebSocket<span class=\"token punctuation\">,</span> client_id<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">await</span> manager<span class=\"token punctuation\">.</span>connect<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">while</span> <span class=\"token boolean\">True</span><span class=\"token punctuation\">:</span>\n            data <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> websocket<span class=\"token punctuation\">.</span>receive_text<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token keyword\">await</span> manager<span class=\"token punctuation\">.</span>send_personal_message<span class=\"token punctuation\">(</span>\n                <span class=\"token string-interpolation\"><span class=\"token string\">f\"Message text was: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>data<span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">,</span> websocket\n            <span class=\"token punctuation\">)</span>\n            <span class=\"token keyword\">await</span> manager<span class=\"token punctuation\">.</span>broadcast<span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"Client #</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>client_id<span class=\"token punctuation\">}</span></span><span class=\"token string\"> says: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>data<span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">except</span> WebSocketDisconnect<span class=\"token punctuation\">:</span>\n        manager<span class=\"token punctuation\">.</span>disconnect<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">await</span> manager<span class=\"token punctuation\">.</span>broadcast<span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"Client #</span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>client_id<span class=\"token punctuation\">}</span></span><span class=\"token string\"> left the chat\"</span></span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"7-2-실시간-채팅-애플리케이션\" style=\"position:relative;\"><a href=\"#7-2-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%B1%84%ED%8C%85-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98\" aria-label=\"7 2 실시간 채팅 애플리케이션 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>7-2. 실시간 채팅 애플리케이션</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> Dict\n<span class=\"token keyword\">import</span> json\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">ChatManager</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">__init__</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">:</span> Dict<span class=\"token punctuation\">[</span><span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> List<span class=\"token punctuation\">[</span>WebSocket<span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">connect</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> websocket<span class=\"token punctuation\">:</span> WebSocket<span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">await</span> websocket<span class=\"token punctuation\">.</span>accept<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">if</span> room <span class=\"token keyword\">not</span> <span class=\"token keyword\">in</span> self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">:</span>\n            self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n        self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>append<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">def</span> <span class=\"token function\">disconnect</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> websocket<span class=\"token punctuation\">:</span> WebSocket<span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> room <span class=\"token keyword\">in</span> self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">:</span>\n            self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>remove<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">send_message</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> message<span class=\"token punctuation\">:</span> <span class=\"token builtin\">dict</span><span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> room <span class=\"token keyword\">in</span> self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">:</span>\n            <span class=\"token keyword\">for</span> connection <span class=\"token keyword\">in</span> self<span class=\"token punctuation\">.</span>rooms<span class=\"token punctuation\">[</span>room<span class=\"token punctuation\">]</span><span class=\"token punctuation\">:</span>\n                <span class=\"token keyword\">await</span> connection<span class=\"token punctuation\">.</span>send_json<span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">)</span>\n\nchat_manager <span class=\"token operator\">=</span> ChatManager<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>websocket</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/ws/chat/{room}\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">chat_endpoint</span><span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">:</span> WebSocket<span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">await</span> chat_manager<span class=\"token punctuation\">.</span>connect<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">try</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">while</span> <span class=\"token boolean\">True</span><span class=\"token punctuation\">:</span>\n            data <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> websocket<span class=\"token punctuation\">.</span>receive_text<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            message <span class=\"token operator\">=</span> json<span class=\"token punctuation\">.</span>loads<span class=\"token punctuation\">(</span>data<span class=\"token punctuation\">)</span>\n            <span class=\"token keyword\">await</span> chat_manager<span class=\"token punctuation\">.</span>send_message<span class=\"token punctuation\">(</span>message<span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">except</span> WebSocketDisconnect<span class=\"token punctuation\">:</span>\n        chat_manager<span class=\"token punctuation\">.</span>disconnect<span class=\"token punctuation\">(</span>websocket<span class=\"token punctuation\">,</span> room<span class=\"token punctuation\">)</span></code></pre></div>\n<h2 id=\"8-파일-업로드와-처리\" style=\"position:relative;\"><a href=\"#8-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C%EC%99%80-%EC%B2%98%EB%A6%AC\" aria-label=\"8 파일 업로드와 처리 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>8. 파일 업로드와 처리</h2>\n<h3 id=\"8-1-단일-파일-업로드\" style=\"position:relative;\"><a href=\"#8-1-%EB%8B%A8%EC%9D%BC-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C\" aria-label=\"8 1 단일 파일 업로드 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>8-1. 단일 파일 업로드</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> UploadFile<span class=\"token punctuation\">,</span> File\n<span class=\"token keyword\">from</span> fastapi<span class=\"token punctuation\">.</span>responses <span class=\"token keyword\">import</span> FileResponse\n<span class=\"token keyword\">import</span> aiofiles\n<span class=\"token keyword\">import</span> os\n\nUPLOAD_DIR <span class=\"token operator\">=</span> <span class=\"token string\">\"uploads\"</span>\nos<span class=\"token punctuation\">.</span>makedirs<span class=\"token punctuation\">(</span>UPLOAD_DIR<span class=\"token punctuation\">,</span> exist_ok<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/upload/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">upload_file</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">file</span><span class=\"token punctuation\">:</span> UploadFile <span class=\"token operator\">=</span> File<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    file_path <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span>UPLOAD_DIR<span class=\"token punctuation\">,</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">.</span>filename<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">with</span> aiofiles<span class=\"token punctuation\">.</span><span class=\"token builtin\">open</span><span class=\"token punctuation\">(</span>file_path<span class=\"token punctuation\">,</span> <span class=\"token string\">'wb'</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> f<span class=\"token punctuation\">:</span>\n        content <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">.</span>read<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">await</span> f<span class=\"token punctuation\">.</span>write<span class=\"token punctuation\">(</span>content<span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token string\">\"filename\"</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">.</span>filename<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"content_type\"</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">.</span>content_type<span class=\"token punctuation\">,</span>\n        <span class=\"token string\">\"size\"</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">len</span><span class=\"token punctuation\">(</span>content<span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">}</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/download/{filename}\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">download_file</span><span class=\"token punctuation\">(</span>filename<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    file_path <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span>UPLOAD_DIR<span class=\"token punctuation\">,</span> filename<span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">if</span> os<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">.</span>exists<span class=\"token punctuation\">(</span>file_path<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">return</span> FileResponse<span class=\"token punctuation\">(</span>\n            file_path<span class=\"token punctuation\">,</span>\n            media_type<span class=\"token operator\">=</span><span class=\"token string\">'application/octet-stream'</span><span class=\"token punctuation\">,</span>\n            filename<span class=\"token operator\">=</span>filename\n        <span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">raise</span> HTTPException<span class=\"token punctuation\">(</span>status_code<span class=\"token operator\">=</span><span class=\"token number\">404</span><span class=\"token punctuation\">,</span> detail<span class=\"token operator\">=</span><span class=\"token string\">\"File not found\"</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"8-2-다중-파일-업로드\" style=\"position:relative;\"><a href=\"#8-2-%EB%8B%A4%EC%A4%91-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C\" aria-label=\"8 2 다중 파일 업로드 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>8-2. 다중 파일 업로드</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> typing <span class=\"token keyword\">import</span> List\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>post</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/upload-multiple/\"</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">upload_multiple_files</span><span class=\"token punctuation\">(</span>files<span class=\"token punctuation\">:</span> List<span class=\"token punctuation\">[</span>UploadFile<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> File<span class=\"token punctuation\">(</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    uploaded_files <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n    \n    <span class=\"token keyword\">for</span> <span class=\"token builtin\">file</span> <span class=\"token keyword\">in</span> files<span class=\"token punctuation\">:</span>\n        file_path <span class=\"token operator\">=</span> os<span class=\"token punctuation\">.</span>path<span class=\"token punctuation\">.</span>join<span class=\"token punctuation\">(</span>UPLOAD_DIR<span class=\"token punctuation\">,</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">.</span>filename<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">async</span> <span class=\"token keyword\">with</span> aiofiles<span class=\"token punctuation\">.</span><span class=\"token builtin\">open</span><span class=\"token punctuation\">(</span>file_path<span class=\"token punctuation\">,</span> <span class=\"token string\">'wb'</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> f<span class=\"token punctuation\">:</span>\n            content <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">.</span>read<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n            <span class=\"token keyword\">await</span> f<span class=\"token punctuation\">.</span>write<span class=\"token punctuation\">(</span>content<span class=\"token punctuation\">)</span>\n        \n        uploaded_files<span class=\"token punctuation\">.</span>append<span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n            <span class=\"token string\">\"filename\"</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">.</span>filename<span class=\"token punctuation\">,</span>\n            <span class=\"token string\">\"size\"</span><span class=\"token punctuation\">:</span> <span class=\"token builtin\">len</span><span class=\"token punctuation\">(</span>content<span class=\"token punctuation\">)</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    \n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"uploaded_files\"</span><span class=\"token punctuation\">:</span> uploaded_files<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"9-에러-처리와-미들웨어\" style=\"position:relative;\"><a href=\"#9-%EC%97%90%EB%9F%AC-%EC%B2%98%EB%A6%AC%EC%99%80-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4\" aria-label=\"9 에러 처리와 미들웨어 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>9. 에러 처리와 미들웨어</h2>\n<h3 id=\"9-1-커스텀-예외-처리\" style=\"position:relative;\"><a href=\"#9-1-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC\" aria-label=\"9 1 커스텀 예외 처리 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>9-1. 커스텀 예외 처리</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> FastAPI<span class=\"token punctuation\">,</span> Request<span class=\"token punctuation\">,</span> status\n<span class=\"token keyword\">from</span> fastapi<span class=\"token punctuation\">.</span>responses <span class=\"token keyword\">import</span> JSONResponse\n<span class=\"token keyword\">from</span> fastapi<span class=\"token punctuation\">.</span>exceptions <span class=\"token keyword\">import</span> RequestValidationError\n<span class=\"token keyword\">from</span> starlette<span class=\"token punctuation\">.</span>exceptions <span class=\"token keyword\">import</span> HTTPException <span class=\"token keyword\">as</span> StarletteHTTPException\n\napp <span class=\"token operator\">=</span> FastAPI<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">CustomException</span><span class=\"token punctuation\">(</span>Exception<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">__init__</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> message<span class=\"token punctuation\">:</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">,</span> status_code<span class=\"token punctuation\">:</span> <span class=\"token builtin\">int</span> <span class=\"token operator\">=</span> <span class=\"token number\">400</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        self<span class=\"token punctuation\">.</span>message <span class=\"token operator\">=</span> message\n        self<span class=\"token punctuation\">.</span>status_code <span class=\"token operator\">=</span> status_code\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>exception_handler</span><span class=\"token punctuation\">(</span>CustomException<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">custom_exception_handler</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">:</span> Request<span class=\"token punctuation\">,</span> exc<span class=\"token punctuation\">:</span> CustomException<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> JSONResponse<span class=\"token punctuation\">(</span>\n        status_code<span class=\"token operator\">=</span>exc<span class=\"token punctuation\">.</span>status_code<span class=\"token punctuation\">,</span>\n        content<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"detail\"</span><span class=\"token punctuation\">:</span> exc<span class=\"token punctuation\">.</span>message<span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>exception_handler</span><span class=\"token punctuation\">(</span>StarletteHTTPException<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">http_exception_handler</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">:</span> Request<span class=\"token punctuation\">,</span> exc<span class=\"token punctuation\">:</span> StarletteHTTPException<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> JSONResponse<span class=\"token punctuation\">(</span>\n        status_code<span class=\"token operator\">=</span>exc<span class=\"token punctuation\">.</span>status_code<span class=\"token punctuation\">,</span>\n        content<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"detail\"</span><span class=\"token punctuation\">:</span> exc<span class=\"token punctuation\">.</span>detail<span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">)</span>\n\n<span class=\"token decorator annotation punctuation\">@app<span class=\"token punctuation\">.</span>exception_handler</span><span class=\"token punctuation\">(</span>RequestValidationError<span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">validation_exception_handler</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">:</span> Request<span class=\"token punctuation\">,</span> exc<span class=\"token punctuation\">:</span> RequestValidationError<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> JSONResponse<span class=\"token punctuation\">(</span>\n        status_code<span class=\"token operator\">=</span>status<span class=\"token punctuation\">.</span>HTTP_422_UNPROCESSABLE_ENTITY<span class=\"token punctuation\">,</span>\n        content<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span><span class=\"token string\">\"detail\"</span><span class=\"token punctuation\">:</span> exc<span class=\"token punctuation\">.</span>errors<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"9-2-미들웨어\" style=\"position:relative;\"><a href=\"#9-2-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4\" aria-label=\"9 2 미들웨어 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>9-2. 미들웨어</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> fastapi <span class=\"token keyword\">import</span> Request\n<span class=\"token keyword\">from</span> starlette<span class=\"token punctuation\">.</span>middleware<span class=\"token punctuation\">.</span>base <span class=\"token keyword\">import</span> BaseHTTPMiddleware\n<span class=\"token keyword\">import</span> time\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TimingMiddleware</span><span class=\"token punctuation\">(</span>BaseHTTPMiddleware<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">dispatch</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> request<span class=\"token punctuation\">:</span> Request<span class=\"token punctuation\">,</span> call_next<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        start_time <span class=\"token operator\">=</span> time<span class=\"token punctuation\">.</span>time<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> call_next<span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">)</span>\n        process_time <span class=\"token operator\">=</span> time<span class=\"token punctuation\">.</span>time<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">-</span> start_time\n        response<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">[</span><span class=\"token string\">\"X-Process-Time\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token builtin\">str</span><span class=\"token punctuation\">(</span>process_time<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> response\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">LoggingMiddleware</span><span class=\"token punctuation\">(</span>BaseHTTPMiddleware<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">dispatch</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> request<span class=\"token punctuation\">:</span> Request<span class=\"token punctuation\">,</span> call_next<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"Request: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>request<span class=\"token punctuation\">.</span>method<span class=\"token punctuation\">}</span></span><span class=\"token string\"> </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>request<span class=\"token punctuation\">.</span>url<span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span>\n        response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> call_next<span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span><span class=\"token string-interpolation\"><span class=\"token string\">f\"Response: </span><span class=\"token interpolation\"><span class=\"token punctuation\">{</span>response<span class=\"token punctuation\">.</span>status_code<span class=\"token punctuation\">}</span></span><span class=\"token string\">\"</span></span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> response\n\napp<span class=\"token punctuation\">.</span>add_middleware<span class=\"token punctuation\">(</span>TimingMiddleware<span class=\"token punctuation\">)</span>\napp<span class=\"token punctuation\">.</span>add_middleware<span class=\"token punctuation\">(</span>LoggingMiddleware<span class=\"token punctuation\">)</span></code></pre></div>\n<h2 id=\"10-테스트\" style=\"position:relative;\"><a href=\"#10-%ED%85%8C%EC%8A%A4%ED%8A%B8\" aria-label=\"10 테스트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>10. 테스트</h2>\n<h3 id=\"10-1-단위-테스트\" style=\"position:relative;\"><a href=\"#10-1-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8\" aria-label=\"10 1 단위 테스트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>10-1. 단위 테스트</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token comment\"># test_main.py</span>\n<span class=\"token keyword\">from</span> fastapi<span class=\"token punctuation\">.</span>testclient <span class=\"token keyword\">import</span> TestClient\n<span class=\"token keyword\">from</span> main <span class=\"token keyword\">import</span> app\n\nclient <span class=\"token operator\">=</span> TestClient<span class=\"token punctuation\">(</span>app<span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">test_root</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    response <span class=\"token operator\">=</span> client<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">\"/\"</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">assert</span> response<span class=\"token punctuation\">.</span>status_code <span class=\"token operator\">==</span> <span class=\"token number\">200</span>\n    <span class=\"token keyword\">assert</span> response<span class=\"token punctuation\">.</span>json<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">==</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"message\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Hello World\"</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">test_create_user</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    response <span class=\"token operator\">=</span> client<span class=\"token punctuation\">.</span>post<span class=\"token punctuation\">(</span>\n        <span class=\"token string\">\"/users/\"</span><span class=\"token punctuation\">,</span>\n        json<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>\n            <span class=\"token string\">\"email\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"test@example.com\"</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">\"full_name\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Test User\"</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">\"age\"</span><span class=\"token punctuation\">:</span> <span class=\"token number\">25</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">\"password\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"Test1234\"</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">assert</span> response<span class=\"token punctuation\">.</span>status_code <span class=\"token operator\">==</span> <span class=\"token number\">201</span>\n    data <span class=\"token operator\">=</span> response<span class=\"token punctuation\">.</span>json<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">assert</span> data<span class=\"token punctuation\">[</span><span class=\"token string\">\"email\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">==</span> <span class=\"token string\">\"test@example.com\"</span></code></pre></div>\n<h3 id=\"10-2-비동기-테스트\" style=\"position:relative;\"><a href=\"#10-2-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%85%8C%EC%8A%A4%ED%8A%B8\" aria-label=\"10 2 비동기 테스트 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>10-2. 비동기 테스트</h3>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> pytest\n<span class=\"token keyword\">from</span> httpx <span class=\"token keyword\">import</span> AsyncClient\n<span class=\"token keyword\">from</span> main <span class=\"token keyword\">import</span> app\n\n<span class=\"token decorator annotation punctuation\">@pytest<span class=\"token punctuation\">.</span>mark<span class=\"token punctuation\">.</span>asyncio</span>\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">def</span> <span class=\"token function\">test_async_endpoint</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">async</span> <span class=\"token keyword\">with</span> AsyncClient<span class=\"token punctuation\">(</span>app<span class=\"token operator\">=</span>app<span class=\"token punctuation\">,</span> base_url<span class=\"token operator\">=</span><span class=\"token string\">\"http://test\"</span><span class=\"token punctuation\">)</span> <span class=\"token keyword\">as</span> ac<span class=\"token punctuation\">:</span>\n        response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> ac<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">\"/users/\"</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">assert</span> response<span class=\"token punctuation\">.</span>status_code <span class=\"token operator\">==</span> <span class=\"token number\">200</span></code></pre></div>\n<h2 id=\"11-프로덕션-배포\" style=\"position:relative;\"><a href=\"#11-%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%EB%B0%B0%ED%8F%AC\" aria-label=\"11 프로덕션 배포 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>11. 프로덕션 배포</h2>\n<h3 id=\"11-1-gunicorn과-uvicorn-workers\" style=\"position:relative;\"><a href=\"#11-1-gunicorn%EA%B3%BC-uvicorn-workers\" aria-label=\"11 1 gunicorn과 uvicorn workers permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>11-1. Gunicorn과 Uvicorn Workers</h3>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token comment\"># gunicorn_config.py</span>\n<span class=\"token builtin class-name\">bind</span> <span class=\"token operator\">=</span> <span class=\"token string\">\"0.0.0.0:8000\"</span>\nworkers <span class=\"token operator\">=</span> <span class=\"token number\">4</span>\nworker_class <span class=\"token operator\">=</span> <span class=\"token string\">\"uvicorn.workers.UvicornWorker\"</span>\n<span class=\"token function\">timeout</span> <span class=\"token operator\">=</span> <span class=\"token number\">120</span>\nkeepalive <span class=\"token operator\">=</span> <span class=\"token number\">5</span>\n\n<span class=\"token comment\"># 실행</span>\ngunicorn main:app <span class=\"token parameter variable\">-c</span> gunicorn_config.py</code></pre></div>\n<h3 id=\"11-2-docker-배포\" style=\"position:relative;\"><a href=\"#11-2-docker-%EB%B0%B0%ED%8F%AC\" aria-label=\"11 2 docker 배포 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>11-2. Docker 배포</h3>\n<div class=\"gatsby-highlight\" data-language=\"dockerfile\"><pre class=\"language-dockerfile\"><code class=\"language-dockerfile\"><span class=\"token comment\"># Dockerfile</span>\n<span class=\"token instruction\"><span class=\"token keyword\">FROM</span> python:3.11-slim</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">WORKDIR</span> /app</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> requirements.txt .</span>\n<span class=\"token instruction\"><span class=\"token keyword\">RUN</span> pip install --no-cache-dir -r requirements.txt</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">COPY</span> . .</span>\n\n<span class=\"token instruction\"><span class=\"token keyword\">CMD</span> [<span class=\"token string\">\"uvicorn\"</span>, <span class=\"token string\">\"main:app\"</span>, <span class=\"token string\">\"--host\"</span>, <span class=\"token string\">\"0.0.0.0\"</span>, <span class=\"token string\">\"--port\"</span>, <span class=\"token string\">\"8000\"</span>]</span></code></pre></div>\n<h2 id=\"12-결론\" style=\"position:relative;\"><a href=\"#12-%EA%B2%B0%EB%A1%A0\" aria-label=\"12 결론 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>12. 결론</h2>\n<p>FastAPI는 현대적이고 고성능인 Python 웹 프레임워크다. 이 글에서 다룬 내용:</p>\n<ol>\n<li><strong>기본 사용법</strong>: 경로, 쿼리, 요청/응답 모델</li>\n<li><strong>Pydantic 검증</strong>: 데이터 검증과 변환</li>\n<li><strong>의존성 주입</strong>: 재사용 가능한 로직 분리</li>\n<li><strong>비동기 프로그래밍</strong>: asyncio를 활용한 고성능 처리</li>\n<li><strong>데이터베이스 연동</strong>: SQLAlchemy 비동기 사용</li>\n<li><strong>인증/인가</strong>: JWT와 RBAC 구현</li>\n<li><strong>WebSocket</strong>: 실시간 통신</li>\n<li><strong>파일 처리</strong>: 업로드/다운로드</li>\n<li><strong>테스트</strong>: 단위 및 통합 테스트</li>\n<li><strong>배포</strong>: 프로덕션 환경 설정</li>\n</ol>\n<p>이러한 기능들을 조합하면 확장 가능하고 유지보수하기 쉬운 API를 빠르게 개발할 수 있다.</p>\n<h2 id=\"참고-자료\" style=\"position:relative;\"><a href=\"#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C\" aria-label=\"참고 자료 permalink\" class=\"anchor-header before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>참고 자료</h2>\n<ul>\n<li><a href=\"https://fastapi.tiangolo.com/\">FastAPI 공식 문서</a></li>\n<li><a href=\"https://docs.pydantic.dev/\">Pydantic 문서</a></li>\n<li><a href=\"https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html\">SQLAlchemy 비동기 가이드</a></li>\n</ul>","tableOfContents":"<ul>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#fastapi-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5---%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-api-%EA%B0%9C%EB%B0%9C-%EC%8B%A4%EC%A0%84-%EA%B0%80%EC%9D%B4%EB%93%9C\">FastAPI 완전정복 - 고성능 비동기 웹 API 개발 실전 가이드</a></p>\n<ul>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-fastapi-%EA%B0%9C%EC%9A%94%EC%99%80-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0\">1. FastAPI 개요와 시작하기</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-1-fastapi%EC%9D%98-%ED%8A%B9%EC%A7%95\">1-1. FastAPI의 특징</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-2-%EA%B8%B0%EB%B3%B8-%EC%84%A4%EC%B9%98%EC%99%80-hello-world\">1-2. 기본 설치와 Hello World</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#1-3-%EA%B2%BD%EB%A1%9C-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EC%99%80-%EC%BF%BC%EB%A6%AC-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98\">1-3. 경로 매개변수와 쿼리 매개변수</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#2-pydantic-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B2%80%EC%A6%9D\">2. Pydantic 모델과 데이터 검증</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#2-1-%EA%B8%B0%EB%B3%B8-%EB%AA%A8%EB%8D%B8-%EC%A0%95%EC%9D%98\">2-1. 기본 모델 정의</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#2-2-%EC%A4%91%EC%B2%A9-%EB%AA%A8%EB%8D%B8%EA%B3%BC-%EB%A6%AC%EC%8A%A4%ED%8A%B8\">2-2. 중첩 모델과 리스트</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#2-3-%EC%BB%A4%EC%8A%A4%ED%85%80-%EA%B2%80%EC%A6%9D%EC%9E%90\">2-3. 커스텀 검증자</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-dependency-injection\">3. 의존성 주입 (Dependency Injection)</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-1-%EA%B8%B0%EB%B3%B8-%EC%9D%98%EC%A1%B4%EC%84%B1\">3-1. 기본 의존성</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-2-%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B8%B0%EB%B0%98-%EC%9D%98%EC%A1%B4%EC%84%B1\">3-2. 클래스 기반 의존성</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#3-3-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%84%B8%EC%85%98-%EC%9D%98%EC%A1%B4%EC%84%B1\">3-3. 데이터베이스 세션 의존성</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%8B%AC%ED%99%94\">4. 비동기 프로그래밍 심화</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-1-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%95%A8%EC%88%98%EC%99%80-await\">4-1. 비동기 함수와 await</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-2-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%9E%91%EC%97%85\">4-2. 백그라운드 작업</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#4-3-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%9F%AC\">4-3. 비동기 이벤트 핸들러</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#5-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EB%8F%99\">5. 데이터베이스 연동</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#5-1-sqlalchemy-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%84%A4%EC%A0%95\">5-1. SQLAlchemy 비동기 설정</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#5-2-crud-%EC%9E%91%EC%97%85\">5-2. CRUD 작업</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#6-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80\">6. 인증과 인가</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#6-1-jwt-%EC%9D%B8%EC%A6%9D-%EA%B5%AC%ED%98%84\">6-1. JWT 인증 구현</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#6-2-%EC%97%AD%ED%95%A0-%EA%B8%B0%EB%B0%98-%EC%A0%91%EA%B7%BC-%EC%A0%9C%EC%96%B4-rbac\">6-2. 역할 기반 접근 제어 (RBAC)</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#7-websocket%EA%B3%BC-%EC%8B%A4%EC%8B%9C%EA%B0%84-%ED%86%B5%EC%8B%A0\">7. WebSocket과 실시간 통신</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#7-1-%EA%B8%B0%EB%B3%B8-websocket\">7-1. 기본 WebSocket</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#7-2-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%B1%84%ED%8C%85-%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98\">7-2. 실시간 채팅 애플리케이션</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#8-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C%EC%99%80-%EC%B2%98%EB%A6%AC\">8. 파일 업로드와 처리</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#8-1-%EB%8B%A8%EC%9D%BC-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C\">8-1. 단일 파일 업로드</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#8-2-%EB%8B%A4%EC%A4%91-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C\">8-2. 다중 파일 업로드</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#9-%EC%97%90%EB%9F%AC-%EC%B2%98%EB%A6%AC%EC%99%80-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4\">9. 에러 처리와 미들웨어</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#9-1-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC\">9-1. 커스텀 예외 처리</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#9-2-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4\">9-2. 미들웨어</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#10-%ED%85%8C%EC%8A%A4%ED%8A%B8\">10. 테스트</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#10-1-%EB%8B%A8%EC%9C%84-%ED%85%8C%EC%8A%A4%ED%8A%B8\">10-1. 단위 테스트</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#10-2-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%85%8C%EC%8A%A4%ED%8A%B8\">10-2. 비동기 테스트</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#11-%ED%94%84%EB%A1%9C%EB%8D%95%EC%85%98-%EB%B0%B0%ED%8F%AC\">11. 프로덕션 배포</a></p>\n<ul>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#11-1-gunicorn%EA%B3%BC-uvicorn-workers\">11-1. Gunicorn과 Uvicorn Workers</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#11-2-docker-%EB%B0%B0%ED%8F%AC\">11-2. Docker 배포</a></li>\n</ul>\n</li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#12-%EA%B2%B0%EB%A1%A0\">12. 결론</a></li>\n<li><a href=\"/python/FastAPI-%EC%99%84%EC%A0%84%EC%A0%95%EB%B3%B5-%EA%B3%A0%EC%84%B1%EB%8A%A5-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%9B%B9-API-%EA%B0%9C%EB%B0%9C-%EA%B0%80%EC%9D%B4%EB%93%9C/#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C\">참고 자료</a></li>\n</ul>\n</li>\n</ul>","wordCount":{"words":305},"fields":{"slug":"/python/FastAPI-완전정복-고성능-비동기-웹-API-개발-가이드/","image":null,"faq":[]},"frontmatter":{"title":"FastAPI 완전정복 - 고성능 비동기 웹 API 개발 실전 가이드","date":"2025년 11월 15일","dateISO":"2025-11-15T00:00:00.000Z","updatedISO":null,"category":"python","description":"FastAPI 기초부터 고급 활용까지. 비동기 프로그래밍, 의존성 주입, Pydantic 검증, WebSocket, 백그라운드 작업, 데이터베이스 연동, 인증/인가, 테스트까지 실무에서 바로 사용할 수 있는 완벽한 가이드를 제공합니다.","tags":["FastAPI","Python","비동기 프로그래밍","웹 API","REST API","asyncio","Pydantic","WebSocket","데이터베이스","백엔드 개발"]}}},"pageContext":{"slug":"/python/FastAPI-완전정복-고성능-비동기-웹-API-개발-가이드/","previous":{"fields":{"slug":"/springboot/마이크로서비스-아키텍처-완전정복-실전-구축-가이드/"},"frontmatter":{"title":"마이크로서비스 아키텍처 완전정복 - 실전 구축 가이드","tags":["마이크로서비스","Microservices","Service Mesh","API Gateway","분산 시스템","Istio","Kong","Jaeger","Kubernetes","이벤트 기반 아키텍처","Spring Cloud"]}},"next":{"fields":{"slug":"/javascript/React-Next.js-완전정복-현대적-프론트엔드-개발-가이드/"},"frontmatter":{"title":"React와 Next.js 완전정복 - 현대적 프론트엔드 개발 실전 가이드","tags":["React","Next.js","React Hooks","Server Components","React Query","프론트엔드","웹 개발","JavaScript","TypeScript","상태 관리"]}}}},"staticQueryHashes":["213619243","3262363727"]}