FastAPI -2

-·2022년 1월 13일
0

강의정리 - MLOps

목록 보기
10/18

목차

  • FastAPI 기본지식
    -- Path Parameter
    -- Query Parameter
    -- Optional Parameter
    -- Request Body
    -- Response Body
    -- From, File

  • Pydantic
    -- Pydantic
    -- Pydantic Validation
    -- Pydantic Config

FastAPI 기본지식

엄청 많이 쓸 내용이니 숙달해보세요 !

Path Parameter한 번 하고 정리하고 ~

Path Parameter, Query Parameter

웹에서 Get Method를 사용해 데이터를 전송할 수 있음
ID가 402인 사용자 정보를 가져오고 싶은 경우 방식

/users/402

  • Path Parameter방식
    -- 서버에 402라는 값을 전달하고 변수로 사용

/users?id=402

  • Query Parameter방식
    -- Query String
    -- API뒤에 입력 데이터를 함께 제공하는 방식으로 사용
    -- Query String은 Key, Value의 쌍으로 이루어지며 &로 연결해 여러 데이터를 넘길 수 있음

언제 어떤 방식을 사용해야할까
관련한 자료를 찾아보면 상황마다 다름

/users/{}: Path
/users?name={} : Query

Path Parameter : 저 경로에 존재하는 내용이 없으므로 404 Error발생
Query Parameter : 데이터가 없는 경우 빈 리스트가 나옴 => 추가로 Error Handling필요

Resource를 식별해야 하는 경우: Path Parameter가 더 적합
정렬, 필터링 해야 하는 경우: Query Parameter가 더 적합

Path Parameter

  • Get Method: 정보를 READ하기 위해 사용

  • 유저 정보에 접근하는 API 만들기

  • FastAPI는 데코레이터로 GET,POST를 표시

  • @app.get @app.post

  • Get Method의 인자로 있는 {user_id}가 함수의 값으로 주입

python test_get.py 로 웹서버를 띄우고 localhost:8000/users/1로 접근
터미널에서 Request 로그가 남음

선행학습의 폐해다. 
reload=True를 사용하여 간편하게 코드를 수정하고 확인하려 하였으나 예상치못한
You must pass the application as an import string to enable 'reload' or 'workers'.
오류와 만남. 

해결방법은 간단했다.
``` uvicorn.run("getMethod:app", host='0.0.0.0', port = 8000, reload=True) ```

Query Parameter

  • 함수의 파라미터가 Query Parameter로 사용됨
    URL뒤에 ?를 붙이고 Key, Value형태로 연결

예시 : localhost:8000/career/?first=0&limit=1

Optional Parameter

from typing import Optional 
  • 특정 파라미터는 Optional(선택적)으로 하고 싶은 경우
    def read_item(item_id:str, q:Optional[str] = None):

q는 있어도 되고 없어도 됨 !!

내멋대로 만들다가 궁금한거.

@app.get("users/{user_id}")로 선언된 두개의 함수가 있다면?!

그냥 위에꺼가 실행되는걸까?

아무튼 Optional parameter까지 잘실행되는것을 볼 수 있었다.

Request Body

  • 클라이언트에서 API에 데이터를 보낼 때, Request Body를 사용함

  • 클라이언트=>API: Request Body

  • API의 Response =>클라이언트: Response Body

  • Request Body에 데이터가 항상 포함되어야 하는것은 아님

  • Request Body에 데이터를 보내고 싶다면 POST Method를 사용

  • 참괴 GET Method는 URL, Request Header로 데이터 전달

  • POST Method는 Request Body에 데이터를 넣어 보냄

  • Body의 데이터를 설명하는 Content-Type이란 Header필드가 존재하고, 어떤 데이터 타입인지 명시해야함.

  • 대표적인 컨텐츠 타입
    -- application/x-www-form-urlencoded : BODY에 Key, Value 사용. &구분자 사용
    -- text/plain : 단순 txt파일
    -- multipartform-data: 데이터를 바이너리 데이터로 전송

  • POST요청으로 item을 생성하는 예제
  • pydantic으로 Request Body 데이터 정의
  • Type Hinting에 위에서 생성한 Class 주입
  • Request Body 데이터를 Validation
class fullstack(BaseModel): 

BaseModel은 pydantic. 후반부에 자세하게 설명. 지금은 그냥 클래스를 만들었구나 알아두기 !

Swagger

  • Schemas에서 pydantic으로 정의한 내용을 볼 수 있음

  • 빨간 * 이 달려있다면 필수, 아니라면 Optional

  • POST쪽을 클릭해도 해당 내용 확인할 수 있음

  • Try it out 클릭

  • 기본 설정 상태에서 Execute누르면 curl명령어, Response가 보임

  • float에 string을 넣어서 execute하면?!
    -- score가 Float이 아니라는 메세지가 출력됨 (validation Check한 것)

Response Body

  • API의 Response => 클라이언트: Response Body

  • Decorator의 response_model 인자로 주입가능

  • 역할
    -- Output Data를 해당 정의에 맞게 변형
    -- 데이터 Validation
    -- Response에 대한 Json Schema 추가
    -- 자동으로 문서화

  • python postMethod.py 웹서버 실행한 후, docs확인

  • Try it out을 누르고 Execute를 실행

  • Request 데이터와 Response 데이터가 다름

Form

  • Form(입력) 형태로 데이터를 받고 싶은경우
  • Form을 사용하려면 python-multipart를 설치해야함

pip install python-multipart

스승님.. poetry는여???
poetry add python-multipart 

프론트도 간단히 만들기 위해 Jinja2 설치 ~~ㅋㅋ이름이 진짜 진자네 ~~

poetry add Jinja2

login_form.html은 Boostcamp model serving에서 가져옴.ㅎㅎ
  • Form 클래스를 사용하면 Request의 Form Data에서 값을 가져옴

Method Not Allow Error 5xx
get과 post방식의 차이를 자세히 알아야됨

  • localhost:8000/login으로 접근하면(URL로 접근하면) -> GET
python postMethod_Form.py를 통해 웹 서버를 실행한후 localhost:8000/login/으로 이동하면
login_form.html을 통해 간단한 login form이 생성됨 
제출을 누르면 login 함수가 실행됨(POST 요청) 

Form(...): ...는 무엇일까?

  • Python ellipsis : Required(꼭 필수 요소)를 의미

  • FastAPI 웹 서버를 실행한 후 Swagger로 이동하면 Required(...)를 볼 수 있음

File

  • File을 업로드하고 싶은 경우
  • File을 사용할 때도 python-multipart를 설치해야 함
  • from fastapi import UploadFile
  • "/"로 접근할 때 보여줄 HTML 코드
@app.post('/files/')
def create_f~~

@app.post('/uploadfiles/')
def create_upload_files(~


content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
    """

html의 form action="/files/' , form action="/uploadfiles/" 등은
위에서 정의된 @app.post('/files/')와 @app.post('/uploadfiles/')의 함수를 실행한다.
위의 html은 파일업로드와 제출 버튼이 두개씩 존재하게 된다.

Pydantic

  • FastAPI에서 Class사용할 때 보이던 Pydantic // BaseModel
  • Data Validation / Settion Management라이브러리
  • Type Hint를 런타임에서 강제해 안전하게 데이터 핸들링
  • 파이썬 기본 타입(String,Int 등) + List, Dict, Tuple에 대한 Validation 지원
  • 기존 Validation 라이브러리보다 빠름(Benchmark)
  • Config를 효과적으로 관리하도록 도와줌
  • 머신러닝 Feature Data Validation으로도 활용 가능

Pydantic의 두가지 기능을 알아보자

  • Validation
  • Config 관리
  • Validation
    -- Machine Learning Model Input Validation
    -- Online Serving에서 Input 데이터를 Validation하는 Case

  • Validation Check Logic
    -- 조건 1: 올바른 url을 입력 받음(url)
    -- 조건 2: 1-10 사이의 정수 입력 받음(rate)
    -- 조건 3: 올바른 폴더 이름을 입력 받음(target_dir)

사용할 수 있는 방법

  • 1 : 일반 Python Class를 활용한 Input Definition 및 Validation

  • 2 : Dataclass를 (python 3.7 이상 필요) 활용한 Input Definition 및 Validation

  • 3 : Pydantic을 활용한 Input Definition 및 Validation

Pydantic Validation - Python Class 활용시 !

class ModelInput01:
	url: str
	rate: int
	target_dir: str

	def __init__(sef,url:str, rate:int, target_dir: str):
		self.url = url
        self.rate = rate
        self.target_dir = target_dir 
        
	def validation(self)-> bool:
    """
    클래스 필드가 올바른지 검증
	Returns:
		bool: 검증 성공/실패 여부
	"""
	validation_results=[self._validate_url(self.url), 1 , self.rate<=10, self._validate_directory(self.target_dir)]
	return all(validation_results)

if __name__ == '__main__':
	VALID_INPUT ={
    	'url' : "https://~~~",
        'rate': 4,
        'target_dir': '/users/~~~'
        }
	
    INVALID_INPUT = {
    	'url': 'wrong_url',
        'rate': 11, 
        'target_dir': 'wrong_dir' 
		}
	
	valid_python_class_model_input = ModelInput01(**VALID_INPUT)
    assert valid_python_class_model_input.validate() is True
    
    invalid_python_class_model_input = ModelInput01(**INVALID_INPUT)
    assert invalid_python_class_model_input.validate() is False 

의미 없이 길어지는 코드량과 장황한 검증 로직이 눈에 보인다.

사실 pydantic을 접하기 전이라 그런지 아니 뭐.. 이정도면 괜찮지 않나? 싶기도 하다.
  • Python Class로 Input Definition 및 Validation => 의미 없는 코드가 많아짐
  • 복잡한 검증 로직엔 Class Method가 복잡해지기 쉬움
  • Exception Handling을 어떻게 할지 등 커스텀하게 제어할 수는 있지만 메인 로직(Input을 받아서 Inferece를 수행하는)에 집중하기 어려워짐

Pydantic Validation - Dataclass 활용시

  • dataclass decorator사용으로 init method를 따로 작성할 필요가 없어짐

  • post init메서드 같은 편의매직메서드 사용 가능

  • 하지만 여전히 validate method를 따로 만들어야함(길어지는 검증 로직을 분리하기 위해)

  • post init method 사용으로 따로 validate메서드를 호출하지 않아도 생성 시점에서 validation

  • 인스턴스 생성 시점에서 Validation을 수행하기 쉬움

  • 여전히 Validation 로직들을 직접 작성해야 함

  • Validation 로직을 따로 작성하지 않으면, 런타임에서 type checking을 지원하지 않음

Pydantic Validation

from pydantic import BaseModel, HttpUrl, Field, DirectoryPath 

class ModelInput03(BaseModel):
	url: HttpUrl
    rate: int = Field(ge=1, le=10)
    target_dir: DirectoryPath
  • 훨씬 간결해진 코드(6라인)(vs 52라인 Python Class, vs 50라인 data class)

  • 주로 쓰이는 타입들(http url, db url, enum등)에 대한 Validation이 만들어져 있음

  • 런타임에서 Type Hint에 따라서 Validation Error 발생

  • Custom Type에 대한 Validation도 쉽게 사용 가능

  • 어디서 에러가 발생했는지

  • location, type, message 등을 알려줌

우리가 알기로 이건 1~1000까지 들어와야되는데 2000이 들어왔다?
저장해둘껀가?버릴껀가?메세지를 날려줄까? 등의 처리가 필요함. 저장해두고 잘못된 데이터가 얼마나 들어오는지
확인해보는것이 좋음

Pydantic Config

  • Pydantic은 Config을 체계적으로 관리할 방법을 제공

  • 기존에 다른 라이브러리들은 어떻게 Config를 설정하고 있을까?

  • 애플리케이션은 종종 설정을 상수로 코드에 저장함 ==하드코딩

  • 이것은 Twelve-Factor를 위반

  • Twelve-Factor는 성정을 코드에서 엄격하게 분리하는 것을 요구함

  • Twelve-Factor App은 설정을 환경 변수(envvars나 env라고도 불림)에 저장함

  • 환경 변수는 코드 변경 없이 쉽게 배포 때마다 쉽게 변경할 수 있음

  • 코드랑 환경변수는 나눌 수 있다

  • The Twelve-Factor App이라는 Saas(Software as a Service)를 만들기 위한 방법론을 정리한 규칙들에 따르면, 환경설정은 애플리케이션 코드에서 분리되어 관리되어야 함

12-factor

  1. .ini, .yaml파일 등으로 config 설정하기
    yaml로 환경 설정을 관리할 경우 쉽게 환경을 설정할 수 있지만, 환경에 대한 설정을 코드 하드코딩하는 형태
    때문에 변경사항이 생일 때 유연하게 코드를 변경하기 어려움

  2. Flask-style config.py

  • Config클래스에서 yaml, ini 파일을 불러와 python class필드로 주입하는 과정을 구현
  • Config를 상속한 클래스에서는 Config클래스의 정보를 오버라이딩해서 사용
  • 하지만 해당파일의 데이터가 정상적인지 체크하거나(Validation)또는 환경 변수로부터 해당 필드를 오버라이딩하려면 코드량이 늘어난다.
  1. pydantic base settings
  • Validation처럼 Pydantic은 BaseSettings를 상속한 클래스에서 Type Hint로 주입된 설정 데이터를 검증할 수 있음
  • Field클래스의 env인자로, 환경변수로부터 해당 필드를 오버라이딩 할 수 있음
  • yaml, ini파일들을 추가적으로 만들지 않고, .env파일들을 환경별로 만들어두거나, 실행환경에서 유연하게 오버라이딩 할 수 있음

위의 세 방법 모두 틀린 방법은 아님

실무에서는 무조건 pydantic을 활용하기 보다는, 각 팀에서 맞는 방법을 따라가는 것이 좋음

하지만 실무에서 여러 사람과 협업하는 환경에서 Human Error를 줄여주는 Pydantic의 기능들은 유용!

profile
-

0개의 댓글