<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>바른 프로그래밍</title>
    <link>https://barun-programing.tistory.com/</link>
    <description>오늘보다 내일 더 나은 개발자가 되기 위한 노력을 기록하는 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Sat, 13 Jun 2026 19:14:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>r잡초처럼</managingEditor>
    <image>
      <title>바른 프로그래밍</title>
      <url>https://tistory1.daumcdn.net/tistory/3778705/attach/842f8c1833da4ea284532d3c356afd88</url>
      <link>https://barun-programing.tistory.com</link>
    </image>
    <item>
      <title>Router 의존성 주입과 API Func 의존성 주입</title>
      <link>https://barun-programing.tistory.com/183</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;오늘 PR 리뷰를 하다가 현재 User에 대한 validate와 user 정보를 받아오는 함수를 정의해서 api args에서 annotate로 선언해서 썼다. 문제가 없는 코드였다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;API endpoint에 depends 주입한 코드&lt;/h2&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from typing import Annotated

from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=&quot;token&quot;)

def validate_token(token):
	# 토큰이 유효하지 않다면
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	raise HTTPException(status_code=401, detail=&quot;Invalid Token&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 토큰이 유효하다면
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	return token

def get_user_by_token(token):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 1. token 이 유효한지 검사한다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 2. token 이 유효하다면 토큰에 심어져 있는 id 를 통해 db 에서 user 정보를 조회한다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 3. 만약 user 정보가 없다면 raise 를 일으킨다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 4. 해당 user의 active 가 true 인지 확인하고 아니라면 raise 를 일으킨다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 5. 모든 게 통과된다면 user 를 return 한다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user = repository.get_user_by_token(token) # db 에서 user 를 가져온다.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if user is None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise HTTPException(status_code=404, detail=&quot;Invalid User&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not user.active
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise HTTPException(status_code=404, detail=&quot;Invalid User&quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return user


async def get_current_user(token: Annotated[str, Depends(validate_token)]):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user = get_user_by_token(token)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return user


@app.get(&quot;/users/me&quot;)
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return current_user&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;위의 코드는 공식문서에 있는 코드를 변형해서 슈도 코드로 만들었다. read_users_me의 current_user 에 depends를 주입시켰다.&lt;br&gt;문제가 없는 코드이지만 나는 current_user는 인증이 요구되는 api endpoint 함수들에는 다 필요한 요소라고 생각했고, path router에다가 depends를 주입하면 코드 중복성을 제거할 수 있지 않을까 생각했다.&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;APIRouter에 depends를 주입해 보자&lt;/h2&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;login_required_router = APIRouter(
	dependencies=[Depends(get_current_user)],
)


@login_required_router(&quot;/api&quot;)
def get_foo(user: User):
	do_something()&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;위와 같이 할 수 있지 않을까 생각했다. 결론적으로는 역할이 달랐고, return value를 endpoint에서 &lt;u&gt;직접적으로 꺼내는 방법은 없었다&lt;/u&gt;.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/#return-values&quot; target=&quot;_self&quot;&gt;&lt;span&gt;not used return value in path router depends&lt;/span&gt;&lt;/a&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/tiangolo/fastapi/issues/424#issuecomment-518875731&quot; target=&quot;_blank&quot;&gt;&lt;span&gt; include_router:&amp;nbsp;How&amp;nbsp;can&amp;nbsp;I&amp;nbsp;get&amp;nbsp;the&amp;nbsp;return&amp;nbsp;of&amp;nbsp;a&amp;nbsp;dependency&amp;nbsp;callable&amp;nbsp;at&amp;nbsp;the&amp;nbsp;decorator&amp;nbsp;level &lt;/span&gt;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;하지만 내 생각에는 api endpoint 함수들마다 get_current_user를 annotate 인자로 받아서 쓰는 건 모든 api 마다 current user를 중복으로 쓰지 않을까란 생각이 들었다(결국 인증 - 유저가 유효한지- 에 관한 문제이기 때문에)결국엔 login_required 가 필요한 api endpoint에 공통적으로 들어가 있는 게 훨씬 낫다는 생각이었다. 따라서 &lt;a href=&quot;https://www.starlette.io/requests/#other-state&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;request state&lt;/span&gt;&lt;/a&gt;에 담아서 쓰는 건 어떤가라는 생각이 들었다.&lt;br&gt;아래는 router에 depends를 주입하는 코드이다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# router/user.py

async def get_current_user(request: Request, token: Annotated[str, Depends(validate_token)]):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;user = get_user_by_token(token)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;	
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;request.state.current_user = user
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return user
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;

login_required_router = APIRouter(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dependencies=[Depends(get_current_user)],
)


@login_required_router(&quot;/api&quot;)
def get_foo(request: Request):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current_user = request.state.current_user&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do_something()&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;물론 이 코드도 문제는 request.state 에 current_user 가 저장되어 있다는 것을 알아야 하지만 내 생각에는 이게 중복성을 제거할 수 있지 않을까란 생각이 들었다.&lt;br&gt;&lt;br&gt;ps. 글을 쓰고 다시 생각해보니 middleware에다가 현재 로그인한 사용자를 넣는건 어떨까란 생각이 드는데 올바른지 고민해봐야겠다&lt;/p&gt;</description>
      <category>FastAPI</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/183</guid>
      <comments>https://barun-programing.tistory.com/183#entry183comment</comments>
      <pubDate>Mon, 16 Oct 2023 23:51:13 +0900</pubDate>
    </item>
    <item>
      <title>상속 안티 패턴</title>
      <link>https://barun-programing.tistory.com/181</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;원하는 데이터 객체를 만들기 위해 많은 비용을 들이지 말자. 다음 예를 보자&lt;/p&gt;
&lt;pre id=&quot;code_1693840037090&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TransactionalPolicy(collections.UserDict):
    &quot;&quot;&quot;잘못된 상속의 예&quot;&quot;&quot;
    
    def change_in_policy(self, customer_id, **new_policy_data):
    	self[customer_id].update(**new_policy_data)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 클래스는 고객의 정책에 접근해 정책을 바꾼다는 목적에는 부합할 수 있으나,&amp;nbsp; 상속의 안티패턴을 보여주고 있다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;계층 구조가 잘못됐다.&lt;br /&gt;&lt;br /&gt;기본 클래스에서 새 클래스를 만드는 것은 개념적으로 확장되고 세부적인 것이다라는 것을 뜻한다. 하지만 해당 데이터 객체는 Dict를 상속하면서 Dict의 개념을 확장했다기 보단 필요한 몇몇 부분(고객 데이터를 접근하는 부분)만 사용했다는 느낌을 가지게 된다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;결합력이 강하다.&lt;br /&gt;&lt;br /&gt;이 클래스에는 pop() 또는 items()와 같은 메서드가 필요하지 않지만 포함되어 있다. 덕타이핑으로 볼 때 해당 오리는 필요없는 부위가 있는 이상한 오리다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 구현 객체를 도메인 객체와 혼합할 때 발생하는 문제이다. dict는 특정 유형의 작업에 적합한 객체 또는 데이터 구조로서 다른 데이터 구조와 마찬가지로 트레이드오프가 있다. TransactionPolicy는 특정 도메인의 정보를 나타내는 것이므로 해결하려는 문제의 일부분에 사용되는 엔티티여야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올바른 해결책은 컴포지션을 사용하는 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;컴포지션이란?&lt;br /&gt;다른 클래스의 인스턴스를 자신의 인스턴스 변수로 가지고 있는 것이다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1693841290876&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class TransactionalPolicy:
    &quot;&quot;&quot;컴포지션을 사용한 리팩토링 예제&quot;&quot;&quot;
    
    def __init__(self, policy_data, **extra_data):
        self._data = {**policy_data, **extra_data}
        
    def change_in_policy(self, customer_id, **new_policy_data):
        self._data[customer_id].update(**new_policy_data)
        
    def __getitem__(self, customer_id):
    	return self._data[customer_id]
        
    def __len__(self):
    	return len(self._data)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 개념적으로 정확할 뿐만 아니라 확장성도 뛰어나다.&lt;/p&gt;</description>
      <category>Python</category>
      <category>상속 안티 패턴</category>
      <category>상속과 컴포지션</category>
      <category>파이썬 클린 코드</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/181</guid>
      <comments>https://barun-programing.tistory.com/181#entry181comment</comments>
      <pubDate>Tue, 5 Sep 2023 00:29:06 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI - Query Parameter에 Custom Validate 적용하기</title>
      <link>https://barun-programing.tistory.com/179</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제상황&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI에서 Query Parameter에 Validate를 적용하고 싶었다. 기본적인 건 min_length, max_length, pattern, regex 등은 기본적으로 지원해 주지만(&lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/query-params-str-validations/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식문서&lt;/a&gt;), 조금 복잡한 validate를 적용할 때는 마땅한 게 없었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;View에서 물론 처리해도 되겠지만, 같은 코드 블록이 다른 View에서 계속 쓰이는 것은 보기 불편했다.(validate 함수를 지정하고 view에서 계속 호출하는 그게 너무 싫었다)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결 과정&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;처음 생각한 건 BaseModel을 적용해서 @field_validator 를 적용할까 생각했지만, 한 두 개의 query param을 위해 BaseModel을 적용하는 건 &lt;u&gt;필요 이상으로 복잡하게 모델을 구현한다&lt;/u&gt;는 생각이 들었다.&lt;/li&gt;
&lt;li&gt;생각해보다가 FastAPI에서 제공해 주는 &lt;b&gt;Depends&lt;/b&gt;를 활용하면 어떨까란 생각이 들었다. 마침 &lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/dependencies/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Depends에 대한 공식문서&lt;/a&gt;의 설명에서 내가 생각한 게 아주 틀리진 않다는 확신을 가졌다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;Dependency Injection&quot; means, in programming, that there is a way for your code (in this case, your path operation functions) to declare things that it requires to work and use: &quot;dependencies&quot;.&lt;br /&gt;And then, that system (in this case&amp;nbsp;FastAPI) will take care of doing whatever is needed to provide your code with those needed dependencies (&quot;inject&quot; the dependencies).&lt;br /&gt;This is very useful when you need to:&lt;br /&gt;- Have shared logic (the same code logic again and again).&lt;br /&gt;- Share database connections.&lt;br /&gt;- Enforce security, authentication, role requirements, etc.&lt;br /&gt;- And many other things..&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 줄의 &lt;i&gt;Have shared logic&lt;/i&gt; 이란 부분에서 같은 validate를 적용하면 되지 않을까 싶었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현해 보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 비교를 위해 처음 구현되었던 부분을 보자&lt;/p&gt;
&lt;pre id=&quot;code_1693629601954&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/duplicate/validate/query1&quot;)
async def duple_validate_query_1(
    q: Annotated[str, Query(description=&quot;이것은 query string 이다.&quot;)]
):
    if not do_validate_something(q):
        return {&quot;message&quot;: &quot;Cheer Up!!!!&quot;}
    ...



@app.get(&quot;/duplicate/validate/query2&quot;)
async def duple_validate_query_1(
    q: Annotated[str, Query(description=&quot;이것은 query string 이다.&quot;)]
):
    if not do_validate_something(q):
        return {&quot;message&quot;: &quot;Cheer Up!!&quot;}
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 '이게 뭐가 거슬리냐?'라고 말할 수 있다. 하지만 이러한 validate 가 조금씩 늘어난다면 어떻게 될까?&lt;/p&gt;
&lt;pre id=&quot;code_1693630469561&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/duplicate/validate/query1&quot;)
async def duple_validate_query_1(
    q: Annotated[str, Query(description=&quot;이것은 query string 이다.&quot;)],
    qq: Annotated[str, Query(description=&quot;이것은 query string 이다.&quot;)],
):
    if not do_validate_something(q):
        return {&quot;message&quot;: &quot;Cheer Up!!!!&quot;}
    if not do_validate_something(qq):
        return {&quot;message&quot;: &quot;More Cheer Up!!!&quot;}
    ...


@app.get(&quot;/duplicate/validate/query2&quot;)
async def duple_validate_query_1(
    q: Annotated[str, Query(description=&quot;이것은 query string 이다.&quot;)],
    qq: Annotated[str, Query(description=&quot;이것은 query string 이다.&quot;)],
):
    if not do_validate_something(q):
        return {&quot;message&quot;: &quot;Cheer Up!!!!&quot;}
    if not do_validate_something(qq):
        return {&quot;message&quot;: &quot;More Cheer Up!!!&quot;}
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금씩 코드뭉치가 늘어나는 느낌이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;query param이 많다면 다음과 같은 방법도 괜찮을 것이다.(&lt;a title=&quot;stack overflow - Query parameters from pydantic model&quot; href=&quot;https://stackoverflow.com/questions/62468402/query-parameters-from-pydantic-model&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고자료&lt;/a&gt;)&lt;/p&gt;
&lt;pre id=&quot;code_1693630999616&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;
class QueryParam(BaseModel):
    q: str = Query(description=&quot;이것은 query string 이다.&quot;)
    qq: str = Query(description=&quot;이것은 query string 이다.&quot;)
    qqq: int = Query(description=&quot;이것은 query string 이다.&quot;)

    @field_validator(&quot;q&quot;, &quot;qq&quot;)
    @classmethod
    def q_must_is_goal(cls, v: str):
        if do_validate_something(v):
            return v
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST, detail={&quot;message&quot;: &quot;Cheer Up&quot;}
        )

    @field_validator(&quot;qqq&quot;)
    @classmethod
    def qqq_must_match(cls, v: int):
        expected_value = [10000, 10200, 30000]
        if v in expected_value:
            return v
        raise HTTPException(
            status_code=HTTP_400_BAD_REQUEST,
            detail={&quot;message&quot;: f&quot;qqq is {','.join(map(str, expected_value))}&quot;},
        )


@app.get(&quot;/duplicate/validate/query2&quot;)
async def duple_validate_query_base_model(query_param: QueryParam = Depends()):
    return query_param.model_dump()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 이 방법은 너무 적은 수의 Query를 class로 적용하면, 복잡도를 높인다고 생각든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Depends 사용해 보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 Depends를 사용해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1693638290246&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async def depends_query_validate(
    q: Annotated[str, Query(description=&quot;이것은 Depends 로 검사하는 query string&quot;)]
):
    if do_validate_something(q):
        return q
    return &quot;sorry invalid q&quot;


@app.get(&quot;/duplicate/validate/query/depends&quot;)
async def duple_validate_query_depends(
    q: Annotated[str, Depends(depends_query_validate)]
):
    result = q
    return {&quot;message&quot;: result}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;861&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtoFsC/btssSqxh6Pr/wzovapToTAxQJ0poBvW9xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtoFsC/btssSqxh6Pr/wzovapToTAxQJ0poBvW9xK/img.png&quot; data-alt=&quot;성공!!!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtoFsC/btssSqxh6Pr/wzovapToTAxQJ0poBvW9xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtoFsC%2FbtssSqxh6Pr%2FwzovapToTAxQJ0poBvW9xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;306&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;861&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;성공!!!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;850&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceQW75/btssUPbN77x/EUnXQa07AqKz99jVGHfTak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceQW75/btssUPbN77x/EUnXQa07AqKz99jVGHfTak/img.png&quot; data-alt=&quot;실패&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceQW75/btssUPbN77x/EUnXQa07AqKz99jVGHfTak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceQW75%2FbtssUPbN77x%2FEUnXQa07AqKz99jVGHfTak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;313&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;850&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실패&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Validator를 사용하는 방법이 있긴 한데 어째서인지 router로 serving하면 validate가 세 번이 돌고 있어서 사용하지 않았다. 이 글을 쓰면서 계속 이유를 찾고 있어서 만약 해결이 된다면 따로 포스팅할 예정이다.(app으로 serving 한 경우에는 이런 문제가 발생하지 않는다.)&lt;/p&gt;
&lt;pre id=&quot;code_1693638536816&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def result_validate(value):
    return value + &quot;1&quot;
    
    
@test_router.get(
    &quot;/TEST&quot;,
    name=&quot;test&quot;,
)
async def test(
    foo: Annotated[
        str | None,
        Query(description=&quot;foo&quot;),
        AfterValidator(result_validate),
    ],
):
    return {&quot;serving_router&quot;: foo}
    
    
@app.get(
    &quot;/TEST&quot;,
    name=&quot;test&quot;,
)
async def test(
    foo: Annotated[
        str | None,
        Query(description=&quot;foo&quot;),
        AfterValidator(result_validate),
    ],
):
    return {&quot;serving_app&quot;: foo}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1691&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beDArX/btssSB6HyXb/be3SeD8DtI5p912jicRy7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beDArX/btssSB6HyXb/be3SeD8DtI5p912jicRy7k/img.png&quot; data-alt=&quot;router로 서빙한 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beDArX/btssSB6HyXb/be3SeD8DtI5p912jicRy7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeDArX%2FbtssSB6HyXb%2Fbe3SeD8DtI5p912jicRy7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;126&quot; data-origin-width=&quot;1691&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;router로 서빙한 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;308&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9DzPx/btssSnObd0p/wnIWyMhF5ni76H3vOUPCz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9DzPx/btssSnObd0p/wnIWyMhF5ni76H3vOUPCz1/img.png&quot; data-alt=&quot;app 으로 서빙한 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9DzPx/btssSnObd0p/wnIWyMhF5ni76H3vOUPCz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9DzPx%2FbtssSnObd0p%2FwnIWyMhF5ni76H3vOUPCz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;697&quot; height=&quot;128&quot; data-origin-width=&quot;1680&quot; data-origin-height=&quot;308&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;app 으로 서빙한 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>FastAPI</category>
      <category>depends</category>
      <category>fastapi</category>
      <category>query param</category>
      <category>validate</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/179</guid>
      <comments>https://barun-programing.tistory.com/179#entry179comment</comments>
      <pubDate>Sat, 2 Sep 2023 14:51:05 +0900</pubDate>
    </item>
    <item>
      <title>FastApi 사용하면서 기록할 것들</title>
      <link>https://barun-programing.tistory.com/178</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Embed&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Body 에서 Request Body 내부에 Key 로 구분지어서 받고 싶다면 Embed=True 로 설정하면 된다. 다음과 같이 나온다&lt;/p&gt;
&lt;pre id=&quot;code_1693386371631&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Annotated

from fastapi import Body, FastAPI
from pydantic import BaseModel

app = FastAPI()


class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None


@app.put(&quot;/items/{item_id}&quot;)
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
    results = {&quot;item_id&quot;: item_id, &quot;item&quot;: item}
    return results&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 아래와 같이 나온다.&lt;/p&gt;
&lt;pre id=&quot;code_1693387172551&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;item&quot;: {
    &quot;name&quot;: &quot;string&quot;,
    &quot;description&quot;: &quot;string&quot;,
    &quot;price&quot;: 0,
    &quot;tax&quot;: 0
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;BaseModel로 QueryParam 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;br /&gt;1. &lt;a href=&quot;https://github.com/tiangolo/fastapi/discussions/6495&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/tiangolo/fastapi/discussions/6495&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;a href=&quot;https://stackoverflow.com/questions/62468402/query-parameters-from-pydantic-model&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/62468402/query-parameters-from-pydantic-model&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1693388650207&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Query parameters from pydantic model&quot; data-og-description=&quot;Is there a way to convert a pydantic model to query parameters in fastapi? Some of my endpoints pass parameters via the body, but some others pass them directly in the query. All this endpoints s...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/62468402/query-parameters-from-pydantic-model&quot; data-og-url=&quot;https://stackoverflow.com/questions/62468402/query-parameters-from-pydantic-model&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bN8hUv/hyTL8JZTWB/828E32kIyxcGfA7pkgEcQ0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/62468402/query-parameters-from-pydantic-model&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/62468402/query-parameters-from-pydantic-model&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bN8hUv/hyTL8JZTWB/828E32kIyxcGfA7pkgEcQ0/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Query parameters from pydantic model&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Is there a way to convert a pydantic model to query parameters in fastapi? Some of my endpoints pass parameters via the body, but some others pass them directly in the query. All this endpoints s...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 query param의 개수가 많다면 BaseModel로 선언하고, 아래와 같이 사용하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1693389142870&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Foo(BaseModel):
	num: int
    char: str


@app.put(&quot;/items/{item_id}&quot;)
async def get_item(
    query_parm: Foo = Depends(),
):
	...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 Field와 Query를 추가하면 metadata도 추가할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1693389283574&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Foo(BaseModel):
	num: int = Field(
        Query(
            ...,
            description=&quot;숫자&quot;          
            example=1,
        ),
    )
    char: str = Field(
        Query(
            ...,
            description=&quot;문자&quot;          
            example=&quot;hello, world.&quot;,
            min_length=1,
        ),
    )&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #36464e; text-align: start;&quot;&gt;response_model_exclude_unset&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #36464e; text-align: start;&quot;&gt;True&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #36464e; text-align: start;&quot;&gt;값이 할당 되지 않은 예를 들면 Key가 없는 건 response 모델에서 뺀다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1693391145649&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float = 10.5
    tags: list[str] = []


items = {
    &quot;foo&quot;: {&quot;name&quot;: &quot;Foo&quot;, &quot;price&quot;: 50.2},
    &quot;bar&quot;: {&quot;name&quot;: &quot;Bar&quot;, &quot;description&quot;: &quot;The bartenders&quot;, &quot;price&quot;: 62, &quot;tax&quot;: 20.2},
    &quot;baz&quot;: {&quot;name&quot;: &quot;Baz&quot;, &quot;description&quot;: None, &quot;price&quot;: 50.2, &quot;tax&quot;: 10.5, &quot;tags&quot;: []},
}


@app.get(&quot;/items/{item_id}&quot;, response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
    return items[item_id]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response 모델은 Item으로 정의되어 있지만, response를 보면 key 가 없는 foo의 경우엔 보이지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1693391207418&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;Foo&quot;,
  &quot;price&quot;: 50.2
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Response Model&amp;nbsp; Union&lt;/h3&gt;
&lt;pre id=&quot;code_1693391436589&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class BaseItem(BaseModel):
    description: str
    type: str


class CarItem(BaseItem):
    type: str = &quot;car&quot;


class PlaneItem(BaseItem):
    type: str = &quot;plane&quot;
    size: int


items = {
    &quot;item1&quot;: {&quot;description&quot;: &quot;All my friends drive a low rider&quot;, &quot;type&quot;: &quot;car&quot;},
    &quot;item2&quot;: {
        &quot;description&quot;: &quot;Music is my aeroplane, it's my aeroplane&quot;,
        &quot;type&quot;: &quot;plane&quot;,
        &quot;size&quot;: 5,
    },
}


@app.get(&quot;/items/{item_id}&quot;, response_model=Union[PlaneItem, CarItem])
async def read_item(item_id: str):
    return items[item_id]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 response_model을 선언하면 schema에선 다음과 같이 보인다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;659&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj2Piz/btssGAskAWF/KADoAh4NWwrj8Ha4kkYjOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj2Piz/btssGAskAWF/KADoAh4NWwrj8Ha4kkYjOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj2Piz/btssGAskAWF/KADoAh4NWwrj8Ha4kkYjOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj2Piz%2FbtssGAskAWF%2FKADoAh4NWwrj8Ha4kkYjOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;548&quot; height=&quot;355&quot; data-origin-width=&quot;1018&quot; data-origin-height=&quot;659&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>FastAPI</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/178</guid>
      <comments>https://barun-programing.tistory.com/178#entry178comment</comments>
      <pubDate>Wed, 30 Aug 2023 19:36:43 +0900</pubDate>
    </item>
    <item>
      <title>[ERROR] Pycharm Interpreter package install 시 'cp949' Encode error</title>
      <link>https://barun-programing.tistory.com/176</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;pycharm에서 poetry로 가상환경을 세팅했다. 하지만 패키지를 설치하려고 할 때마다 `cp949` 에러가 났다.&lt;br&gt;사실 큰 문제는 아니었다. terminal에서 poetry 직접 add를 하면 됐다.&lt;br&gt;하지만 mypy를 plugin 으로 설치하고, 작동을 하려니까 계속 install mypy라는 noti가 뜨고(global 하게 mypy를 설치한 python을 써도 같은 오류를 뱉었다... virtualenv로 가상환경을 구성했을 땐 잘만되던 게... 왜 poetry에서만...ㅠ) 설치하려니 해당 오류를 뱉으니, 더 이상 무시할 수 없게 되었다.&lt;br&gt;&amp;nbsp;&lt;br&gt;Pycharm file encoding 문제인가 싶어 UTF-8로 설정했지만, 에러 로그에는 stream_out.py 에서 `&lt;b&gt;&lt;i&gt;\u2022`를&lt;/i&gt;&lt;/b&gt; encode 하다가 에러가 발생했다는 메시지만 보여줬다.&lt;br&gt;&amp;nbsp;&lt;br&gt;아무 생각 없이 stream_out.py 파일을 건드려서 해당 문자열을 오류 핸들링 했다&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# stream_out.py

def _write(self, message: str, new_line: bool = False) -&amp;gt; None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if new_line:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;message += &quot;\n&quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# custom 코드
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 하지만 깨름칙하다. python release 버전 소스코드를 내맘대로 커스텀 하는게..
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if &quot;\u2022&quot; in message:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;message = message.replace(&quot;\u2022&quot;, '*')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self._stream.write(message)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self._stream.flush()&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;그러다 문득 든 생각이 '이럴 거면 그냥 window default encoding 옵션을 utf-8로 하면 되는 게 아닐까?'란 생각이 들었다.&amp;nbsp;&lt;br&gt;(참고: &lt;a href=&quot;https://bangtal.bosornd.com/posts/windows-utf-8/&quot; target=&quot;_blank&quot;&gt;&lt;span&gt;https://bangtal.bosornd.com/posts/windows-utf-8/&lt;/span&gt;&lt;/a&gt;)&lt;br&gt;&amp;nbsp;&lt;br&gt;다행히 생각은 주효했고 다른 오류를 만났다&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;error: Microsoft Visual C++ 14.0 or greater is required. Get it with &quot;Microsoft C++ Build Tools&quot;: https://visualstudio.microsoft.com/visual-cpp-build-tools/&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;이건 C++ 를 설치하면서 해결했다&lt;/p&gt;</description>
      <category>기타 팁들</category>
      <category>cp949</category>
      <category>encoding</category>
      <category>poetry</category>
      <category>pycharm</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/176</guid>
      <comments>https://barun-programing.tistory.com/176#entry176comment</comments>
      <pubDate>Mon, 14 Aug 2023 16:22:10 +0900</pubDate>
    </item>
    <item>
      <title>Annotated</title>
      <link>https://barun-programing.tistory.com/175</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Annotated를 활용해 Type hint에 대한 Metadata를 설정해 보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;정의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typing.Annotated란 해당 타입에 대한 메타데이터를 제공해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용법&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;예를 들어 다음과 같이 사용할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1691028213301&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;T1 = Annotated[int, ValueRange(-10, 5)]
T2 = Annotated[T1, ValueRange(-20, 3)]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Annotated는 첫 번째 인자는 유효한 자료형이어야 한다. T2와 같이 가변인자도 지원한다.&lt;/li&gt;
&lt;li&gt;또한 위와 같이 여러 개의 형 주석이 지원된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1691028275515&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Annotated[int, ValueRange(3, 10), ctype(&quot;char&quot;)]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Annotated는 최소한 두 개의 인자로 호출해야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1691028383345&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# error
num: Annotated[int]

# Good
num: Annotated[int, &quot;숫자&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FastAPI에선 &lt;i&gt;Query&lt;/i&gt;와 같이 사용하여 다음과 같이 validate도 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1691028569502&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()


@app.get(&quot;/items/&quot;)
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
	...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &lt;a href=&quot;https://docs.python.org/ko/3.9/library/typing.html#typing.Annotated&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.python.org/ko/3.9/library/typing.html#typing.Annotated&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1691028676829&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;typing &amp;mdash; 형 힌트 지원 &amp;mdash; Python 3.9.17 문서&quot; data-og-description=&quot;typing &amp;mdash; 형 힌트 지원 소스 코드: Lib/typing.py 참고 파이썬 런타임은 함수와 변수 형 어노테이션을 강제하지 않습니다. 형 어노테이션은 형 검사기, IDE, 린터(linter) 등과 같은 제삼자 도구에서 사용&quot; data-og-host=&quot;docs.python.org&quot; data-og-source-url=&quot;https://docs.python.org/ko/3.9/library/typing.html#typing.Annotated&quot; data-og-url=&quot;https://docs.python.org/ko/3.9/library/typing.html#typing.Annotated&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://docs.python.org/ko/3.9/library/typing.html#typing.Annotated&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.python.org/ko/3.9/library/typing.html#typing.Annotated&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;typing &amp;mdash; 형 힌트 지원 &amp;mdash; Python 3.9.17 문서&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;typing &amp;mdash; 형 힌트 지원 소스 코드: Lib/typing.py 참고 파이썬 런타임은 함수와 변수 형 어노테이션을 강제하지 않습니다. 형 어노테이션은 형 검사기, IDE, 린터(linter) 등과 같은 제삼자 도구에서 사용&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.python.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;a href=&quot;https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#__tabbed_2_1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#__tabbed_2_1&lt;/a&gt;&lt;/p&gt;</description>
      <category>FastAPI</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/175</guid>
      <comments>https://barun-programing.tistory.com/175#entry175comment</comments>
      <pubDate>Thu, 3 Aug 2023 11:10:27 +0900</pubDate>
    </item>
    <item>
      <title>Slack reminder 조회 및 삭제</title>
      <link>https://barun-programing.tistory.com/174</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;slack 에서 채널에 계속 공지하다가 삭제할 일이 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. remind 를 조회하자&lt;/p&gt;
&lt;pre id=&quot;code_1691024806320&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/remind list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 목록에서 삭제를 누르면 된다&lt;/p&gt;</description>
      <category>기타 팁들</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/174</guid>
      <comments>https://barun-programing.tistory.com/174#entry174comment</comments>
      <pubDate>Thu, 3 Aug 2023 10:08:28 +0900</pubDate>
    </item>
    <item>
      <title>QueryParameter</title>
      <link>https://barun-programing.tistory.com/173</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;1. kwargs 와 type 힌트를 통해 queryparameter 를 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1690946714469&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fake_items_db = [{&quot;item_name&quot;: &quot;Foo&quot;}, {&quot;item_name&quot;: &quot;Bar&quot;}, {&quot;item_name&quot;: &quot;Baz&quot;}]


@app.get(&quot;/items/&quot;)
async def read_item(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Optional의 경우엔 어떻게?&lt;/p&gt;
&lt;pre id=&quot;code_1690946733799&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id: str, q: str | None = None):
    if q:
        return {&quot;item_id&quot;: item_id, &quot;q&quot;: q}
    return {&quot;item_id&quot;: item_id}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. multiple path and query parameter 는 특정 순서로 선언할 필요는 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1690952538704&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/users/{user_id}/items/{item_id}&quot;)
async def read_user_item(
    user_id: int, item_id: str, q: str | None = None, short: bool = False
):
    item = {&quot;item_id&quot;: item_id, &quot;owner_id&quot;: user_id}
    if q:
        item.update({&quot;q&quot;: q})
    if not short:
        item.update(
            {&quot;description&quot;: &quot;This is an amazing item that has a long description&quot;}
        )
    return item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. required query parameter는 default value 를 설정하지 않으면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1690952921603&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
async def read_user_item(item_id: str, needy: str):
    item = {&quot;item_id&quot;: item_id, &quot;needy&quot;: needy}
    return item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 어떤 param 은 필수값이고 어떤 건 옵셔널하다면 다음과 같이 선언해도 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1690952962309&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
async def read_user_item(
    item_id: str, needy: str, skip: int = 0, limit: int | None = None
):
    item = {&quot;item_id&quot;: item_id, &quot;needy&quot;: needy, &quot;skip&quot;: skip, &quot;limit&quot;: limit}
    return item&lt;/code&gt;&lt;/pre&gt;</description>
      <category>FastAPI</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/173</guid>
      <comments>https://barun-programing.tistory.com/173#entry173comment</comments>
      <pubDate>Wed, 2 Aug 2023 14:09:30 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI - PathParameter</title>
      <link>https://barun-programing.tistory.com/172</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;PathParameter&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. '{}' 를 쓰고 인자로 받으면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1690945029690&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/items/{item_id}&quot;)
async def read_item(item_id: int):
    return {&quot;item_id&quot;: item_id}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 만약 sub path 가 같고 뒤에 오는게 다르다면?&lt;/p&gt;
&lt;pre id=&quot;code_1690945055542&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/users/me&quot;)
async def read_user_me():
    return {&quot;user_id&quot;: &quot;the current user&quot;}


@app.get(&quot;/users/{user_id}&quot;)
async def read_user(user_id: str):
    return {&quot;user_id&quot;: user_id}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 경우엔 path param이 &lt;i&gt;me&lt;/i&gt;가 오는 경우 read_user_me()가 호출 되고, 다른 경우엔 read_user가 호출된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 만약 path param 에서 받아오는 값에 &quot;/&quot; 가 붙어있는 경우에는 어떻게 해야할까? 이때는 &lt;i&gt;:path&lt;/i&gt; 를 붙여준다.&lt;/p&gt;
&lt;pre id=&quot;code_1690945277123&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@app.get(&quot;/files/{file_path:path}&quot;)  # :path 예를 들어 1/1.txt 로 요청해도 404 가 안뜬다
async def read_file(file_path: str):
    return {&quot;file_path&quot;: file_path}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 만약 path param 으로 받아오는 값을 choice하게 해야된다면? Enum을 이용하자&lt;/p&gt;
&lt;pre id=&quot;code_1690945332506&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ModelName(str, Enum):
    alexnet = &quot;alexnet&quot;
    resnet = &quot;resnet&quot;
    lenet = &quot;lenet&quot;


@app.get(&quot;/models/{model_name}&quot;)
async def get_model(model_name: ModelName):
    if model_name is ModelName.alexnet:
        return {&quot;model_name&quot;: model_name, &quot;message&quot;: &quot;Deep Learning FTW!&quot;}

    if model_name.value == &quot;lenet&quot;:
        return {&quot;model_name&quot;: model_name, &quot;message&quot;: &quot;LeCNN all the images&quot;}

    return {&quot;model_name&quot;: model_name, &quot;message&quot;: &quot;Have some residuals&quot;}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>FastAPI</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/172</guid>
      <comments>https://barun-programing.tistory.com/172#entry172comment</comments>
      <pubDate>Wed, 2 Aug 2023 12:02:43 +0900</pubDate>
    </item>
    <item>
      <title>FastAPI 시작하기 - 개발환경 세팅</title>
      <link>https://barun-programing.tistory.com/171</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FastAPI로 개발하게 될 거 같아서 공부를 해보자. 일단 개발환경부터 세팅하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발환경은 pyenv와 poetry로 가상환경을 설정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;pyenv&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pyenv 는 Python의 버전을 관리할 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;여러 개의 프로젝트에서 다양한 python 버전으로 개발환경을 세팅할 수 있다.&lt;/li&gt;
&lt;li&gt;시스템 전체에 영향을 주지 않고 버전을 바꿀 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 버전의 Python 설치&lt;/li&gt;
&lt;li&gt;가상 환경 관리&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pyenv를 사용하여 가상 환경을 생성하고 관리할 수 있다.&lt;/li&gt;
&lt;li&gt;각각의 프로젝트에 맞게 가상 환경을 생성하여 의존성을 격리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;python 설치하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690875812993&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Python 3.9.6 버전 설치
pyenv install 3.9.6

# Python 3.8.12 버전 설치
pyenv install 3.8.12&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;설치한 버전 확인하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690875904948&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pyenv versions&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트에서만 해당 python 버전 사용하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690875945545&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;cd my_project

pyenv local 3.8.12&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역으로 python 버전 설정하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690875975681&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pyenv global 3.10.10&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Poetry&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Poetry는 Python 패키지 관리 도구&lt;/li&gt;
&lt;li&gt;Group 옵션을 통해 다양한 환경에서 의존성을 관리할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가상 환경 생성과 관리&lt;/li&gt;
&lt;li&gt;패키지 의존성 관리&lt;/li&gt;
&lt;li&gt;프로젝트 정보 관리
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pyproject.toml 파일을 사용하여 프로젝트의 정보를 정의한다.&lt;/li&gt;
&lt;li&gt;프로젝트의 이름, 버전, 저자 등의 정보를 설정할 수 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로젝트 생성과 의존성 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690876439004&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 새 프로젝트 생성
poetry new my_project

# pyproject.toml 생성
cd my_project
poetry init

# 의존성 추가
poetry add requests
poetry add numpy --dev  # dev group에 numpy 설치&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;라이브러리 및 파일 실행하기(black, pytest 또는 소스 코드)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690876688060&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 소스 코드 실행
poetry run python your_script.py

# pytest 실행
poetry run pytest&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;의존성 패키지 설치하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690876805956&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;poetry install&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;FastAPI&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특징&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ASGI 서버와 호환되어 비동기 방식으로 동작한다&lt;/li&gt;
&lt;li&gt;안정적이고 빠른 개발환경을 제공한다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 기능&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빠른 성능&lt;/li&gt;
&lt;li&gt;강력한 타입 힌팅&lt;/li&gt;
&lt;li&gt;API 문서 자동화&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용 예시&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;fastapi 및 서버 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690876983510&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;poetry add fastapi

poetry add &quot;uvicorn[standard]&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;간단한 api 예시&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1690877011317&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from fastapi import FastAPI

app = FastAPI()


@app.get(&quot;/&quot;)
async def root():
    return {&quot;message&quot;: &quot;Hello World&quot;}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>FastAPI</category>
      <author>r잡초처럼</author>
      <guid isPermaLink="true">https://barun-programing.tistory.com/171</guid>
      <comments>https://barun-programing.tistory.com/171#entry171comment</comments>
      <pubDate>Tue, 1 Aug 2023 17:03:51 +0900</pubDate>
    </item>
  </channel>
</rss>