본문 바로가기
웹 개발/Python : FastAPI

Python - FastAPI 시작하기 (사용법 : 개론 설명 및 설정) [feat. Pycharm (Community)]

by TayLee 2024. 10. 20.
반응형

글의 목표

1) FastAPI (with uvicorn)을 사용하는 이유
2) Python의 GIL 정책과 FastAPI : 개발할때의 방향성 (맛보기)
3) FastAPI 구성 (설치)
  - Fast API 설치 
  - ASGI (uvicorn) 설치 및 설명
    * Web Application에 대한 개론 (feat Web Framework)
4) Pycharm으로 FastAPI 웹 서버 실행 및 구성 
  - Python Project의 가상환경과 System환경에 대해서 알 수 있다.
  - Uvicorn을 실행시킬 수 있는 두 가지 방법에 대해 알 수 있다
  - 내장형 Swagger-UI 실행법을 알 수 있다.

 

1. Why FastAPI? (FastAPI의 당위성)

 FastAPI는 비동기 처리를 통한 높은 성능과 간결한 코드 작성이 장점으로 꼽히고 있다. 특히 높은 성능을 요구하는 경우, 예를 들어 '데이터 분석 플랫폼'처럼 빠른 응답 속도가 생명인 경우 적합한 선택이 될 수 있다.

 하지만, CPU-Bound TaskI/O-Bound Task를 구분할 필요가 있다. FastAPI는 I/O-Bound 작업(예: 데이터베이스 호출, 외부 API 호출 등)에서 성능상의 이점을 제공하지만, CPU-Bound Task가 많은 경우에는 Python의 GIL정책때문에 비동기 처리만으로는 성능 향상이 제한적일 수 있다. 

 

[FastAPI 공식문서]

 

FastAPI

FastAPI framework, high performance, easy to learn, fast to code, ready for production

fastapi.tiangolo.com

 

2. Python GIL Policy & FastAPI

 Python 언어는 GIL(Global Interpreter Lock) 때문에 멀티 스레드(Multi-Threads)를 사용하더라도 실질적인 병렬 처리의 효과를 얻기 어렵다.

 GIL은 한 번에 하나의 스레드만 Python 바이트 코드를 실행할 수 있도록 제한하여, CPU-Bound Task에서는 멀티스레딩의 장점을 충분히 활용하지 못하게 한다 (Race Condition을 막기 위해 GIL을 채택했다고 한다). 따라서, I/O-Bound 작업에서는 FastAPI의 비동기(feat. uvicorn) 기능이 유리하지만, CPU-Bound 작업이 많은 경우에는 멀티프로세싱이나 다른 언어를 사용한 최적화가 필요하다. 

 위의 GIL화제로 다음에 별도의 글을 Posting할 예정이다. 위의 메커니즘을 이해정도에 따라 개발의 방향성이나 코드의 구조가 완전히 달라지기때문에 꼭 이해하고 넘어가야한다. 특히 Java진영의 Spring Boot (MVC)로 개발하던 분들이라면, Python과 Java의 비동기 처리 방식의 차이를 명확히 이해한 후, 개발에 임하는 것이 이후에 코드 수정이나 구조 변경을 줄이는 데 도움이 될 것이다.

 

 


3. FastAPI 설치 

혹시 pip이나 Python 환경변수 설정이 되어 있지 않다면, 아래 글을 참고하고 오시길 바랍니다. 

 

Python 설치 : 환경변수 설정 : 패키지구조 설명

최근에 Java Spring Boot 프로젝트만 수행하다 Python으로 개발할 프로젝트가 생겼다.   이번 글에서는 윈도우상(Linux는 후에 올리도록 해보겠다)에서 Python 관련 환경변수 설정을 하고, 필요한 Library,

taehyuklee.tistory.com

 

Python 패키지관리자인 pip을 통해 fastapi 패키지를 다운로드 한다.

 

3.1 FastAPI 설치

pip install fastapi

 

만약, fastapi가 잘 설치되어 있는 것을 확인하고 싶다면, 파이썬이 설치되어 있는 경로 하에 (저는 D:\Python 경로하에 설치했습니다) Lib\site-packages 경로와 \Scripts 경로 아래 fastapi가 있는지 확인해보면 된다. 

 

 

3.2 ASGI(Async Server Gateway Interface) 서버 설치

pip install "uvicorn[standard]"

 

 ASGI(Asynchronous Server Gateway Interface)는 비동기 웹 애플리케이션을 위한 인터페이스이다. WSGI가 동기 기반 웹 애플리케이션을 지원하는 표준이라면, ASGI는 비동기 애플리케이션을 지원하도록 확장된 표준이라 생각하면 된다.

 

ASGI  : Uvicorn, Daphne
Framework : FastAPI와 함께 사용된다.

 

WSGI Gunicorn, uWSGI, Waitress
 Framework : Django, Flask 등에서 많이 사용된다.

 

 * 사실 위와같이 공식문서에서 나온대로 추상적인 설명을 하면, 하나도 와닿지 않을거라는걸 압니다. 해당 부분의 메커니즘에 대해서는 딥다이브해서 따로 글을 포스팅할 예정입니다. ASGI, WSGI 그리고 FastAPI와 같은 framework들의 구조와 메커니즘에 대해서는 이후에 정리할 예정이니 기대해주셔도 좋을 것 같습니다. 

 

 

3.3 (참고) Java개발자들을 위한 개념 Mapping (feat. Tomcat, Netty)

 역할로 따지면 Spring Boot(MVC)에서의 Tomcat이 하는 역할과, Spring WebFlux에서 Netty 서버가 하는 역할이 각각 WSGI와 ASGI 서버와 유사하다고 볼 수 있다. 이러한 유사성 때문에 서버 게이트웨이를 WAS라고 부를 수도 있겠지만, 역할은 비슷해도 엄밀히 말해 WAS라고 지칭하는 것은 정확하지 않다.

 

많이 들어봤는데, 뭔지 잘 모르겠다고 생각하는 사람들을 위한 직관적 정의 :

 Tomcat, Netty, WSGI, ASGI 등과 같은 용어들을 분명 많이 들어봤는데, 정확히 뭔지 모르겠다 하는 사람들을 위해 역할을 한 문장으로 요약하자면, 다음과 같다. 

 위와 같은 애플리케이션을 위한 프로세스(서버)는 모두 HTTP요청을 수신하고 처리하기 위해 내부적으로 루프를 돌면서 들어오는 요청을 처리하는 역할을 한다.
(Reuqest Parsing 등 요청을 처리하기 위한 과정 포함)

 

 개발자들은 이러한 역할을 수행하는 애플리케이션 서버에 맞춰, Spring Boot, Flask, FastAPI와 같은 프레임워크를 선택하여 내부 서비스 로직을 구현합니다. 이를 통해 요청을 처리하고, 필요한 비즈니스 로직을 수행하는 애플리케이션을 구축하게 된다. 따라서 Java진영의 Tomcat같은 것들은 Java로 만들어져 있고 Uvicorn은 Cython으로 만들어진걸로 알려져 있다.

 

 

3.4 (중요) Web Framework, Web Application : 관계 및 역할 (구조)

여기서 아래와 같은 의문이 들수 있고 해당 의문을 갖는 것이 당연하다고 본다.

Fast API나 Spring같은 웹 프레임워크들은 Uvicorn, Gunicorn, Tomcat, Netty, Jetty 등과 같은 Web Application이 없으면 서버역할을 하지 못하는 것인가?

 

fig1
그림1. 웹 프레임워크와 웹 애플리케이션의 간단한 관계도

 

 내가 내린 답은 본인이 직접 While Loop돌리는 서버를 만들지 않는 이상, 웹 프레임워크만으로는 서버가 되지 못한다. 웹 프레임워크들은 웹 애플리케이션 개발을 위해 설계된 프레임워크이기 때문에, 웹 서버로 사용하려면 웹 애플리케이션이 필요하다. 다시 말해, 이들 프레임워크는 웹 서버 그 자체라기보다는, 웹 서버 위에서 동작하는 애플리케이션을 쉽게 개발할 수 있도록 도와주는 도구라 할 수 있다.

 간략하게 포함관계를 그리기 위해 그림1과 같이 그려보았다. 네트워크 통신, 스레드 관리 등과 같은 저수준의 시스템 관련 작업은 웹 애플리케이션이 담당하며, 웹 프레임워크는 이러한 작업을 보다 쉽게 처리할 수 있도록 지원한다.

 

 결론을 요약하자면, 웹 애플리케이션 서버는 클라이언트와의 통신을 관리하고, 다중 사용자 요청을 효율적으로 처리하기 위해 스레드를 관리하며, 애플리케이션 로직을 실행할 수 있는 환경을 제공한다. 심지어 Python환경의 uvicorn의 경우 부모-자식 프로세스 관리까지 하게 된다.

 

아닌데요!? Spring Boot나 fastapi[standard]로 설치하면, 그냥 실행하면 서버 실행되던데요?

 아! 그건 Spring Boot는 내장형 톰캣이 이미 존재하고, fastapi[standard] (공식 문서 참고) 로 설치해서 fastapi dev main.py로 실행하는 것은 내장된 uvicorn이 실행되는 것을 확인할 수 있을 것이다.

 

 

 

uvicorn 공식문서에서 

 

Uvicorn

An ASGI web server, for Python. Introduction Uvicorn is an ASGI web server implementation for Python. Until recently Python has lacked a minimal low-level server/application interface for async frameworks. The ASGI specification fills this gap, and means w

www.uvicorn.org

 

uvicorn을 수동으로 실행하는 방법 (출처 : uvicorn 공식문서)

import asyncio
import uvicorn

async def app(scope, receive, send):
    ...

async def main():
    config = uvicorn.Config("main:app", port=5000, log_level="info")
    server = uvicorn.Server(config)
    await server.serve()

if __name__ == "__main__":
    asyncio.run(main())

 위의 코드 조각을 보면, uvicorn에서 fastapi관련 app을 서버로 실행하는 방법중 하나로 표기하고 있다. 여기서 보면, uvicorn에 "main:app"을 감싸서 asyncio.run(main())으로 비동기 방식으로 main함수를 실행하는 것을 확인할 수 있다.

 

 

 

Pycharm에서 FastAPI - uvicorn으로 실행하는 방법


3.5 Pycharm (Community) 설정 : Python Pycharm community 2024.2.3

 위에서 FastAPI와 uvnicorn의 개론에 대해서는 충분히 봤으니, 실제로 pycharm으로 어떻게 구성하고 실행하는지 보도록 하자. 이전에 pip 파이썬 패키지 관리자를 이용해 global site-packages에 fastapi, uvicorn[standard]를 설치해 놓은 상태이다. 3.1과정과 3.2과정을 진행한 상태.

 

(용어정리)

global site-packages(system-wide site-packages) vs
local site-packages (virtual environment site-packages)? 
 Python을 시스템에 설치하면, 해당 Python 설치 디렉터리 내에 라이브러리들이 저장되는 폴더가 있다. 이 폴더는 해당 Python 인터프리터를 사용하는 모든 프로젝트에서 공통으로 참조될 수 있기 때문에 global site-packages라고 불린다. 예를 들어, 본인이 D:\Python\Python.3.11.8 경로에 Python을 설치했다면, 해당 경로 안의 D:\Python\Python.3.11.8\Lib\site-packages 폴더가 바로 global site-packages이다.
 반면, 프로젝트마다 독립적인 환경을 구성할 수 있는 방법도 있다. 이러한 환경에서는 각 프로젝트의 폴더 내부에만 설치된 패키지들이 사용된다. 이는 프로젝트마다 서로 다른 버전의 라이브러리나 패키지를 사용해야 할 때 유용하며, 이 때문에 가상 환경(venv) 또는 local site-packages라는 이름으로 독립적인 환경을 구성하게 된다.

 

3.5-1) 프로젝트 생성 (가상환경 / system 환경)

fig2
그림2. 새로운 프로젝트 생성

 

 Pycharm에서 프로젝트 생성을 하면 위와 같은 창이 뜨게 된다. 이때 각 인터프리터 타입에 프로젝트 vnv, 기본 conda, 사용자 지정 환경이 있는데 위 부분들은 어떤 인터프리터를 가져올지를 결정하는 부분이다. 우리는 우리가 설치했던 파이썬 인터프리터를 가져올 예정이다. 그리고 global site-packages를 참조하여 library를 사용할 예정이다.

 

인터프리터 타입 : 사용자 지정 환경 (선택)

환경 : 새로 생성 (vevn) 인터프리터는 내가 설치한 파이썬 인터프리터를 사용하지만, Library는 가상환경에 설치된 것만 사용하겠다는 의미이다. 따라서 기존 항목 선택을 선택 하여 Library를 global site-packages를 참조하게 해야한다.

 

fig3
그림3. (위의 그림) 가상환경 (아래 그림) global-stie packages

 

 그림 3을 보면, 위의 그림은 가상 환경을 사용한 프로젝트를, 아래 그림은 global site-packages를 참조하는 프로젝트를 보여준다.

 가상 환경을 사용하는 경우, 프로젝트 내부에 LibScripts 폴더가 생성되며, 외부 라이브러리 또한 해당 가상 환경의 Scripts 폴더를 참조하고 있는 것을 확인할 수 있다. 반면, global site-packages를 사용하는 프로젝트는 Python이 설치된 경로를 참조하고 있다. 만약 해당 venv환경에서 fastapi를 설치하고 싶다면, 해당 환경의 터미널에서 pip install fastapi를 다시 하면 된다. 그럼 독립된 프로젝트 가상환경에서 fastapi가 다운로드 되어 사용할 수 있을 것이다. 뿐만 아니라 다른 프로젝트에서 해당 프로젝트에서 다운 받은 환경을 참조해서 사용하는 것 또한 가능하다.

 물론, 프로젝트를 생성한 후 인터프리터를 변경하여 참조되는 패키지를 변경할 수도 있지만, 여기서는 global site-packages를 참조하는 방식에 대해 설명한다. 

 

 

3.5-2) main.py 생성 : FastAPI 프레임워크 생성

fig4
그림4. fastapi 인식이 가능해진다

 

프로젝트 하위에 main.py를 만들고 fastapi 공식문서상에 있는 예제를 쳐보도록 한다.

# 출처: fastapi 공식문서
from typing import Union
from fastapi import FastAPI

app = FastAPI()


@app.get("/sync/say-hello")
def read_root():
    return {"Hello": "World"}

@app.get("/async/say-hello")
async def async_read_root():
    return {"async Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}

 

 app = FastAPI(): FastAPI 프레임워크의 인스턴스를 app으로 생성한다. 이후, 이 app 인스턴스를 uvicorn을 통해 웹 애플리케이션을 실행할 때 전달하게 된다.

 

 

 

3.5-2) uvicorn (ASGI) : 웹 애플리케이션 실행 방법 두 가지

(첫 번째 방법 - 터미널에 uvicorn을 직접 실행시키기)

uvicorn main:app --reload

 위의 명령어를 해석해보면, uvicorn을 실행하는데, main.py안에 app객체를 넘기도록 하겠다. 라고 해석하면 된다.

--reload 항은 코드를 수정할 때마다 서버를 자동으로 재시작해 주는 기능이다. 개발 중에 유용한 옵션으로, 코드 변경 사항을 즉시 반영할 수 있어 수동으로 서버를 재시작할 필요가 없다. 하지만, 배포 환경에서는 권장되지 않는다.

 

 reload말고도 여러 옵션들이 uvicorn 공식문서에 적혀져 있다. 추후에 --worker라는 자식 프로세스 개수를 조절하는 옵션이 있는데 이는 파이썬 환경에서 매우 중요한 역할을 하므로, GIL과 비동기를 포스팅할때 다룰 예정이다. (포트 또한 --port 옵션으로 바꿀수 있다)

 

fig5
그림5. uvicorn을 실행한 상태

 

 uvicorn을 실행하게 되면, reloader와 uvicorn 서버가 각 각 프로세스 ID 9256, 23828로 돌아가고 있는 것을 확인할 수 있다. reloader 또한 별도의 Process로 돌아가고 있음을 알 수 있다.

 

 

(두 번째 방법 - 코드에 uvicorn을 넣어 직접 실행시키기 : 좀 더 직관적인 방법)

from typing import Union
from fastapi import FastAPI
import asyncio
import uvicorn

app = FastAPI()


@app.get("/sync/say-hello")
def read_root():
    return {"Hello": "World"}

@app.get("/async/say-hello")
async def async_read_root():
    return {"async Hello": "World"}


@app.get("/items/{item_id}")
def read_item(item_id: int, q: Union[str, None] = None):
    return {"item_id": item_id, "q": q}


async def main():
    config = uvicorn.Config("main:app", port=8000, log_level="info")
    server = uvicorn.Server(config)
    await server.serve()

if __name__ == "__main__":
    asyncio.run(main())

 

 위 코드는 uvicorn.Config에 main.py의 app 인스턴스를 전달하여 서버를 직접 실행하고, main 함수를 비동기(asyncio)로 실행하는 방식으로 구성되어 있다. 이렇게 작성하면 구조가 직관적이어서 이해하기 더 쉬울 것으로 생각된다.

fig6
그림6. pycharm에서 main을 실행시키는 방법

 

 코드는 터미널에서 main.py를 python으로 실행하거나, PyCharm에서 Run을 사용해 main.py를 실행해도 된다.

fig7
그림7. 실행 결과

 

 실행 결과는 터미널에서 uvicorn으로 실행하는 것과 동일하다. 다만 --reload 프로세스는 따로 띄우지 않았기 때문에 uvicorn 웹 애플리케이션의 프로세스 ID [11888]만 뜬 것을 확인 할 수 있다.

 

 

3.5-3) 요청 보내보기

fig8
그림8. 요청 보내보기

 

 위에서 서버가 잘 작동하는지 확인하기 위해 request를 하나 보내본다. http://127.0.0.1:8000/sync/say-hello로 get요청을 보냈을때 답변을 잘 받아오는 것을 확인할 수 있었다.

 

3.5-4) Swagger-UI in FastAPI

fig9
그림9. FastAPI내장 Swagger-UI

 

 또한, Java 진영의 Spring Boot와 달리 FastAPI는 Swagger UI를 별도로 Maven Repository에서 의존성을 가져오지 않고도 내장하고 있는 것이 특징이다. 예를 들어, 서버 도메인이 http://127.0.0.1:8000인 경우, 경로에 /docs를 추가하면 해당 서버에서 제공하는 API를 확인할 수 있는 Swagger UI가 나타난다.

 
 

맺음말

 이번 포스팅에서는 FastAPI의 당위성(장점)과 설치 방법, 그리고 웹 프레임워크와 웹 애플리케이션 간의 관계에 대해 개론적으로 알아보았다. 또한, PyCharm을 이용해 FastAPI 웹 서버를 실행하는 과정을 단계별로 설명해 보았다.

 FastAPI는 비동기 처리를 통해 높은 성능을 제공하는 강력한 웹 프레임워크로, 빠른 응답 속도가 요구되는 애플리케이션에 적합합니다. 이와 함께 Python의 GIL(Global Interpreter Lock) 정책을 이해하는 것이 중요하다는 점도 강조했었다.

 

 FastAPI를 제대로 이해하기 위한 큰 그림을 그리는 데 초점을 맞추었다. 앞으로는 DB 설정, 라우터, 서비스 레이어 등 API 서버로서 FastAPI를 어떻게 구성할 수 있는지 등 에대해 단계적으로 설명할 예정이다.

 또한, uvicorn이 OS와 내부적으로 어떻게 상호작용하는지와 그 작동 방식에 대한 이해도 다룰 계획이다. 특히, GIL(Global Interpreter Lock) 정책으로 인한 제약을 극복하기 위해 부모-자식 프로세스 관계를 활용한 멀티프로세싱 관리 방법도 자세히 설명하려고 한다.

 

 

이번 글은 길다면 길고, 내용의 중요도 측면에서 짧다고도 할 수 있는 글인데, 읽어주셔서 감사합니다.

 

 

참고 자료

1. Uvicorn 공식문서 

2. Devocean (SK) 블로그 (동의하지 않는 개념도 있긴 하지만, 전반적인 개념이 잘 정리되어 있다)
3. fastapi 공식문서

4. GIL이란 - wiki-Python 

반응형