대학원 수업/빅데이터서비스모델링

[2주차] FastAPI를 이용한 API 서비스 만들기

Tech코알라 2025. 3. 15. 14:44

안녕하세요. 저는 현재 회사에 다니면서 저녁이나 주말 시간을 활용하여 대학원(박사 과정)에서 수업을 수강하고 있는데요. 

수업 내용을 정리할 곳도 마땅치 않고, 동기도 필요해서 블로그에 간략하게 정리해보고자 합니다. 아마 메모장 느낌으로 저술하는 느낌이다 보니 뒤죽 박죽할 수도 있을 것 같습니다. 이 부분은 양해해 주시길 바라겠습니다. (수업 자료 및 자세한 내용 등은 문제가 될 수 있기에 올리지 않도록 하겠습니다.)

 

이 수업은 기본적으로 파이썬의 fastapi를 활용하여 서비스를 만드는 수업입니다. 저도 일을 하거나, 공부를 할 때 Fast API를 자주 사용하기는 하지만, 복습하는 마음으로 수업을 수강했습니다.

 

Fast API : Python의 API를 빌드하기 위한 웹 프레임워크 # 호스트 시스템에 직접 구동되는 형태로 진행(host-native)

*framework : 여러 기능을 가진 클래스와 라이브러리가 '특정 결과물을 구현하고자' 합쳐진 형태

 

설치 방법

pip install "fastapi[standard]" #optional 패키지의 추가 설치 방법 => 모두 설치시 [all]

 

간단 예시 

from fastapi import FastAPI

app = FastAPI()


@app.get("/") #get 방식으로 호출 => http://localhost:8000/ 으로 호출
async def root():
    return {"message": "Hello World"}

 

* Method  

get : 자원을 받아오기만 사용한다. 

어떠한 방식으로도 자원의 상태를 변경시키지 않으므로, safe method라고 불리기도 한다. GET API는 멱등성의 띈다. 멱등성이란, 동일한 API를 여러번 호출시에도 동일한 결과를 얻을 수 있음을 의미한다.

 

post : 새로운 자원을 추가할 때 사용한다.

이는 서버의 상태를 변경시키며, 때문에 비멱등성 성질을 가진다. 응답 코드로 201(Created)를 받아야 정상적으로 서버에 추가 되었음을 확인할 수 있다.

 

delete : 자원을 삭제할 때 사용한다.

DELETE 메소드는 멱등성 성질을 띈다. DELETE 메소드를 요청하면 자원을 삭제하게 되는데, 반복적으로 DELETE를 호출한 경우 결과는 바뀌지 않는다. 하지만 이미 제거되었으므로404(NOT FOUND)를 반환받는다.

 

put : 존재하는 자원을 변경 할 때 사용한다.
만약 자원이 존재하지 않는 경우, API는 새로운 자원을 생성하도록 할 수 있다. 이 경우 응답코드는 201(Created)를 받게 된다.
존재하는 자원을 변경시킨 경우 200(OK) 또는 204(No Content) 응답코드를 받게 된다

 

patch: 한 자원의 데이터를 부분적으로 변경할 때 사용한다.

PUT도 마찬가지로 자원을 변경할 수 있다. 하지만 조금더 명확하게는 존재하는 자원에 대해 부분적으로 업데이트를 위해서는 PATCH를 사용한다. PUT은 자원을 완전히 대체하는 경우 사용한다.

** PATCH의 경우 모든 브라우저, 서버, 앱 어플리케이션 프레임워크에서 사용할 수 있는 것은 아니다.

 

*** 멱등성 : 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질

$ fastapi dev main.py # 위의 예시를 main.py로 저장했다고 가정

   FastAPI   Starting development server 🚀

             Searching for package file structure from directories
             with __init__.py files
             Importing from /home/user/code/awesomeapp

    module   🐍 main.py

      code   Importing the FastAPI app object from the module with
             the following code:

             from main import app

       app   Using import string: main:app

    server   Server started at http://127.0.0.1:8000
    server   Documentation at http://127.0.0.1:8000/docs

       tip   Running in development mode, for production use:
             fastapi run

             Logs:

      INFO   Will watch for changes in these directories:
             ['/home/user/code/awesomeapp']
      INFO   Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C
             to quit)
      INFO   Started reloader process [383138] using WatchFiles
      INFO   Started server process [383153]
      INFO   Waiting for application startup.
      INFO   Application startup complete.

* swagger : url/docs

이를 해석하면,

1) 진입점

- IP : 127.0.0.1

- Port : 8000

- Method : GET

- Endpoind: /

 

2) 요청 및 응답 규격 

- 요청 규격 : 별고 요청값 없음

- 응답 규격 : {'message' : 'hello World'} # 고정값을 반환한다. 

 

3) run, dev

dev : 나 밖에 접근 못하는 서버 => 개발 모드로 띄었다.

- > 127.0.0.1 로만 접근 가능 

 

run : 실제 서비스가 가능한 서버 => prod 모드 

-> 실제 IP 할당(현재 하드웨어의 ip)

 

실제 서비스 시에는 외부에서 접근할 수 있도록 0.0.0.0이나 서버의 실제 IP 주소에 바인딩하여 실행합니다. 또한, 보안, 성능, 로깅, 오류 처리 등 추가적인 설정과 함께 Uvicorn을 Gunicorn과 같은 프로세스 매니저 및 Nginx 같은 리버스 프록시와 함께 사용하는 것이 일반적입니다.

 

4) uvicorn 

5) port 지정 방법 

uvicorn main:app --host 0.0.0.0 --port 8000

 

6) 공인 IP -> 사설 IP

API 서버가 사설 IP(예: 192.168.x.x, 10.x.x.x 등)에서 동작 중이라면, 외부에서 접근할 수 있도록 공인 IP로 노출시키는 것은 네트워크 설정 및 인프라 환경에서 이루어집니다. ( 포트 포워딩, 역방향 프록시, 터널링 서비스)

=> 0.0.0.0으로 열려 있다면 공인 ip를 통해서 해당 api를 사용할 수 있습니다.

 

7) 요청 규격 
(1) path 

=> api path에 요청 변수를 추가, api 진입점에 변수가 정의 되어 optional parameter 정의는 불가능

=> /flights/{origin}/{destination}/{date}/{fare_class} 와 같이 설정되며

=> curl -X GET 'http://127.0.0.1:8000/fligths/Seoul/Tokyo/2025-03-08/Business' 와 같이 호출 

(2)Query 
=> path 뒤에 ? 를 붙여 요청 변수를 전달

@app.get('flights')
async def get_flights_info(
	origin: str,
    destination: str,
    date: str,
    fare_class: optional[str] = 'Economy'
    ):
    return {"message" : 'hello world!'}
    
   # curl -X 'GET' "http://127.0.0.1:8000?origin=Seoul&destination=Tokyo&date=2025-03-01"

 

(3) Body

class FlightRequest(BaseModel):
	origin: str,
    destination: str,
    date: str,
    fare_class: optional[str] = 'Economy'
    
@app.post('flights')
async def get_flights_info(request: FlightRequest):
    return {"message" : 'hello world!'}
    
   # curl -X 'GET' "http://127.0.0.1:8000/flights" -H 'Content-Type: application/json' -d '(
   		'origin':'Seoul',
        'destination':'Tokyo',
        'date':'2025-03-01',
        'fare_class':'Economy')

 

8) 비동기화 

예시 concurrent-burgers, parrallel burger

 

* 단 api에서 i/o bound operation인 경우 좋지만, cpu bound operation인 경우 동기로 처리하는 것이 더 좋다