처음 시작하는 FastAPI

MMM._.MMM·2025년 11월 10일

처음 시작하는 FastAPI 책을 읽으면서 책 내용을 정리한 내용입니다.

PART 2-3

타입 변환

경로함수의 반환 값과 JSON 자동 응답

FastAPI에서 경로 함수(Path Function)는 놀랍게도 무엇이든 반환할 수 있습니다. 딕셔너리, 리스트 또한 Pydantic 모델 인스턴스까지 가능합니다.
이러한 파이썬 객체를 반환할 때 FastAPI는 기본적으로 해당 데이터를 JSONResponse를 사용하여 클라이언트에게 보냅니다. 이 과정에서 서버는 다음 작업을 수행하여 HTTP 응답을 완성합니다.

  • JSON 문자열 변환: 반환된 파이썬 객체를 네트워크를 통해 전송 가능한 JSON 문자열로 반환합니다.
  • 응답 헤더 설정: 변환된 JSON 문자열의 크기에 맞는 Content-Length 헤더와 데이터 형식이 JSON임을 알리는 Content-Type: application/json 헤더를 응답에 포함하여 반환합니다.

    특히 모든 Pydantic 모델 클래스 인스턴스를 반환하며 Pydantic 모델의 구조를 그대로 따르는 JSON객체로 자동 변환되어 응답됩니다.

jsonable_encoder()

파이썬의 표준 json라이브러리를 사용해보았다면 datetime 객체, 사용자 정의 클래스 혹은 UUID와 같은 일부 데이터 타입을 변환할려고 할때 예외(Exception)가 발생하는 것을 보았을 것입니다. 표준 json라이브러리는 모든 파이썬 객체를 JSON형식으로 다룰 수 없기 때문입니다.
FastAPI는 이 문제를 해결하기 위해서 jsonable_encoder()라는 내부 함수를 사용합니다.

- jsonable_encoder()의 역할
jsonable_encoder()는 반환되는 데이터 구조를 재귀적으로 순회하면서 파이썬 객체들을 JSON과 비슷한(JSONable) 파이썬 데이터 구조로 변환하는 역할을 담당합니다.

변환 과정을 거치며 최종적으로 데이터 구조는 파이썬의 표준 json.dump()함수가 아무 문제 없이 처리할 수 있는 딕셔너리, 리스트, 문자열, 숫자, 불리언등으로 구성됩니다.

jsonable_encoder()사용해서 JSON호환 형식으로 변환 → json.dump()를 호출 → 클라이언트에게 전송할 JSON문자열 생성

Starlette

FastAPI와 Starlette는 기존 파이썬 웹 서버 인터페이스인 WSGI(Web Server Gateway Interface)가 아닌 ASGI(Asynchronous Server Gateway Interface) 표준을 따릅니다.

  • ASGI: 동기(Synchronous) 및 비동기(Asynchronous) 애플리케이션 모두를 지원하도록 설계된 파이썬 비동기 웹 표준입니다.
  • Starlette의 역할: Starlette는 이 ASGI 표준을 구현하여 개발자가 비동기 Python코드(async/await)를 사용하여 고성능 웹 애플리케이션을 쉽게 만들 수 있도록 해줍니다.

Python의 동시성 유형

비동기와 동기

파이썬에서 동시성을 구현하는 방식은 크게 비동기(Asynchronous)동기(Synchronous) 두가지로 나뉩니다. Starlette와 FastAPI는 이 두가지를 모두 처리할 수 있습니다.

1. 비동기(Asynchronous)

  • 특징: async와 await 키워드를 사용합니다. 단일스레드 내에서 I/O 작업이 발생할 때 CPU가 놀지 않도록 작업 전환을 통해 효율성을 극대화합니다.
  • 언제 사용하는지: I/O Bound작업에 가장 적합하며 높은 동시 사용자 요청을 효율적으로 처리하여 성능이 우수합니다.

- I/O: I/O는 데이터를 입/출력하는 모든 작업을 통칭하며 컴퓨터 내부(CPU, 메모리)와 외부 장치(디스크, 네트워크 카드, 키보드, 마우스등)간의 데이터 전송을 의미합니다.
- I/O Bound: 특정 작업이나 프로그램이 실행되는 데 걸리는 전체 시간의 대부분을 I/O작업이 차지하는 경우를 일컫는 용어입니다.
즉 해당 작업의 속도를 결정하는 주된 병목이 CPU의 계산 속도가 아니라 디스크 읽기/쓰기 또는 네트워크통신 속도와 같이 느린 I/O작업이라는 의미입니다.

**2. 동기(Synchronous)

  • 특징: 일반적인 def함수를 사용합니다. 코드가 순서대로 실행되며 하나의 작업이 완료될 때까지 다음 작업은 대기(Blocking)합니다.
  • 언제 사용하는지: CPU Bound작업에 적합합니다. 비동기로 구현해도 성능 이득이 없고 오히려 오버헤드가 발생할 수 있습니다.

- CPU Bound: 특정 작업이 실행되는 데 걸리는 전체 시간의 대부분을 CPU의 연산작업이 차지하는 경우를 말합니다.

운영체제 프로세스(OS Peocess)

프로세스는 실행 중인 프로그램을 의미합니다. 운영체제가 자원을 할당하는 독립적인 실행 단위입니다.

  • 독립성: 각 프로세스는 완전히 독립적인 메모리 공간(adress space)을 가집니다. 한 프로세스가 다른 프로세스의 메모리를 침범하거나 훼손하는 것을 방지합니다.
  • 자원(메모리 구조): 프로세스는 자체적으로 아래와 같은 영역으로 구성됩니다.
    • Code Section (코드 영역): 실행할 명령어가 저장
    • Data Section (데이터 영역): 전역 변수, 정적 변수
    • Heap (힙): 동적 할당 메모리
    • Stack (스택): 함수 호출시 지역변수, 매개변수 저장
  • 통신 (프로세스 간 통신<IPC, Inter-Process Communication>): 프로세스는 서로 독립되어 있기 때문에 데이터 공유가 어렵습니다. 따라서 아래와 같은 특수한 통신 매커니즘이 필요합니다.
    • 파이프(Pipe)
    • 소켓(Socket)
    • 메시지 큐(Message Queue)
    • 공유 메모리(Shared Memory)
  • 오버헤드: 프로세스 생성 및 전환(Context Switching) 시에는 CPU상태(레지스터, 프로그램 카운터 등)를 저장하고 복원해야 하므로 오버헤드가 큽니다.

운영체제 스레드(OS Thread)

운영체제 스레드는 프로세스 내에서 실행되는 실행의 단위입니다. 한 프로세스는 하나 이상의 스레드를 가질 수 있습니다.

  • 자원 공유: 같은 프로세스에 속한 스레드들은 힙 영역, 코드 영역, 데이터 영역을 공유 합니다.
  • 개별 자원: 각 스레드는 독립적인 스텍(Stack)레지스터 상태를 가집니다.
  • 경량화: 프로세스보다 생성 및 전환 오버헤드가 훨씬 작습니다. 자원을 공유하므로 프로세스 간 전환보다 빠릅니다.
  • 동시성: OS가 직접 관리하며 병렬성(Parallelism)을 구현하기 적합합니다. 멀티코어 CPU 환경에서 여러 스레드가 동시에 실행될 수 있습니다.

그린 스레드(Green Thread)

그린 스레드는 사용자 레벨 스레드의 일종으로 운영체제가 아닌 애플리케이션이나 특정 라이브러리(런타임 라이브러리)에 의해 직접 스케줄링하고 관리하는 스레드입니다.

특징

  • 관리 주체: OS 커널이 아닌 런타임 시스템 또는 라이브러리가 아닌 스레드 생성과 스케줄링을 담당합니다.
  • 가벼움(경량성): OS 커널 스레드를 생성하지 않기 때문에 생성/전환(Context Switching), 소멸의 오버헤드가 매우 작습니다.
  • 스케줄링 방식: 주로 협력적인(Cooperative) 멀티태스킹을 사용합니다.
    (ex. 한 스레드가 자발적으로 제어권을 반납(yield, await등) 해야 다른 스레드가 실행됩니다.)

콜백(Callback)

콜백은 특정 작업이 끝난 뒤 호출되도록 다른 함수에 인수로 전달되는 함수 입니다.

과거에는 비동기 작업(파일 I/O, 네트워크 통신 등)의 완료 시점을 알리고 결과를 처리하기 위해 사용되는 기본적인 패턴이었습니다. 하지만 콜백 함수가 여러 단계로 중첩될 경우 코드가 복잡해져 콜백 지옥(Callback Hell)을 유발한다는 단점이 있습니다.

파이썬 제너레이터(Generator)

제너레이터는 반복 가능한 객채(lterable)를 생성하는 특별한 유형의 함수입니다. 이는 async/await 패턴의 기초를 다진 중요한 개념입니다.

  • 개념 및 yield 키워드
    일반 함수가 return을 만나면 완전히 종료되는 것과 달리 제너레이터 함수는 yield키워드를 사용합니다.
    - yield의 역할: 실행을 잠시 멈추고 값을 호출하는자에게 반환합니다. 그리고 함수의 현재 상태(지역변수, 명령어 포인터 등)를 메모리에 저장해 둡니다.
    - 호출자가 다음에 값을 요청하면(ex.next()함수), 제너레이터는 저장된 상태부터 실행을 재개(Resume)합니다.
  • 동시성으로의 연결
    제너레이터의 '잠시 멈추고 상태를 저장했다가 재개하는' 능력은 비동기 프로그래밍에서 I/O대기시 제어권을 넘겼다가 작업 완료 후 다시 돌아오는 코루틴(Coroutine)의 작동 방식과 유사합니다.

파이썬의 async/await

async와 await는 파이썬에서 비동기 프로그래밍을 명확하고 가독성 높게작성하도록 도입된 구문입니다.

  • async def(코루틴 정의)
    - 함수 앞에 async키워드를 붙이면 코루틴(Coroutine)함수를 정의합니다.
    • 코루틴 함수를 호출해도 즉시 실행되지 않고 실행 준비가 된 코루틴 객체(Coroutine Object)를 반환합니다.
      (ex.제너레이터 함수를 호출했을 때 제너레이터 객체가 반환되는 것과 유사합니다.)
  • await(제어권 반납)
    • await은 코루틴 내에서 I/O작업과 같이 대기 시간이 필요한 작업을 호출할 때 키워드를 사용합니다.

await을 만나면 해당 코루틴은 I/O 작업이 완료될 때까지 잠시 실행을 멈추고(Suspended), 제어권이벤트 루프에게 자발적으로 반납(Yield)합니다.
제어권을 받은 이벤트 루프는 대기하고 있던 시간동안 다른 코루틴을 실행하여 단일 스레드 내에서 동시성을 확보합니다.
I/O작업이 완료되면 이벤트 루프는 다시 해당 코루틴의 await 지점으로 돌아와 실행을 재개합니다.

asyncio(비동기 엔진)

asyncio는파이썬의 비동기 프로그래밍을 위한 표준 라이브러리이자 프레임워크입니다. async와 await로 정의된 코루틴들을 실제로 실행하고 관리하는 엔진 역할을 합니다.

이벤트 루프(Event Loop)
asyncio의 핵심은 이벤트 루프(Event Loop)입니다.

  • 이벤트 루프는 모든 코루틴 작업을 등록하고 어느 코루틴이 현재 실행되어야 하는지 어느 코루틴이 I/O 대기중인지 등을 스케줄링하고 관리합니다.
  • await를 통해 제어권이 반납됩니다. 이벤트 루프는 대기 목록에 있는 준비된 다른 코루틴을 선택해 실행합니다.
profile
아....평화롭게 오카네모찌 되고JOB다...

0개의 댓글