codingstairs
노트에듀라이프연락
⌕검색⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

연락하기

로그인 없이도 보낼 수 있어요. 답변이 필요하면 이메일을 함께 적어 주세요.

  • 익명 폼으로 의견 남기기 →
  • ✉ warragon112@gmail.com
  • 카카오톡 오픈채팅 ↗

© 2026 codingstairs

  • 노트
  • 에듀
  • 검색
  • 라이프
  • 연락
  • 약관
  • RSS
  • GitHub
노트›languages

Python 과 비동기 (asyncio)

2026-04-28 게시· 2026-05-18 갱신·0회 조회

Python 과 비동기 (asyncio)

Python 은 1991 년에 처음 공개된 동적 스크립트 언어. 이 글은 Python 의 흐름 · GIL · asyncio 등장 배경. 동시성과 병렬성의 차이 · ASGI 와 FastAPI 의 위치 · trio · anyio 같은 대안.

1. Python 에 대한 이야기

네덜란드 출신 Guido van Rossum 이 1989 년 크리스마스 휴가에 시작해 1991 년 2월 0.9.0 으로 처음 공개. 1.0 은 1994 년, 2.0 은 2000 년, 3.0 은 2008 년. 2.x 와 3.x 가 호환되지 않아 한동안 두 갈래로 굴러갔고 2020 년 1월 1일자로 2.7 의 공식 지원이 끝남.

언어 자체는 PSF (Python Software Foundation) 가 관리하고, 변경은 PEP (Python Enhancement Proposal) 절차를 통합니다.

2. GIL

CPython (레퍼런스 구현) 의 인터프리터는 한 번에 한 스레드만 Python 바이트코드를 실행하도록 GIL 을 잡음. 1992 년 무렵부터 있었던 설계이며, 단일 스레드 성능과 C 확장 작성의 단순성을 위한 절충.

결과:

  • CPU 바운드 작업 은 멀티스레드로 병렬화되지 않음 (같은 프로세스 안에서).
  • I/O 바운드 작업 은 GIL 을 자주 놓기 때문에 멀티스레드가 의미.
  • 진짜 병렬을 원하면 multiprocessing 으로 프로세스 분리.

PEP 703 (2023, Sam Gross) 가 GIL 을 선택적으로 끄는 빌드 모드를 제안했고, 3.13 (2024-10) 에서 실험적 free-threaded 빌드 (--disable-gil) 가 제공되기 시작. 정식화는 단계적으로 진행 중.

3. 동시성 vs 병렬성

구분 의미 도구
동시성 (concurrency) 여러 작업을 같은 시간대에 진행하는 구조. 한 코어로도 가능. asyncio · threading
병렬성 (parallelism) 같은 순간에 여러 코어가 동시에 실행. multiprocessing · C 확장

GIL 때문에 Python 의 멀티스레드는 동시성에는 쓸모가 있어도 CPU 병렬에는 한계. 이 빈자리에 multiprocessing (3.0, 2008) 과 concurrent.futures (3.2, 2011).

4. asyncio 의 등장

버전 사건
3.4 (2014) asyncio 표준 라이브러리 추가. generator 기반 @asyncio.coroutine.
3.5 (2015) PEP 492. async def · await 키워드.
3.6 (2016) async generator · async comprehension.
3.7 (2018) asyncio.run() · ContextVar.
3.11 (2022) TaskGroup · except*.

asyncio 는 이벤트 루프 위에서 코루틴을 협력적으로 스케줄. await 를 만나면 코루틴이 자발적으로 양보하고, 이벤트 루프는 다른 준비된 코루틴을 깨움:

import asyncio
import httpx

async def fetch(url: str) -> int:
    async with httpx.AsyncClient() as c:
        r = await c.get(url)
        return r.status_code

async def main():
    async with asyncio.TaskGroup() as tg:        # 3.11+
        t1 = tg.create_task(fetch("https://example.com"))
        t2 = tg.create_task(fetch("https://www.python.org"))
    print(t1.result(), t2.result())

asyncio.run(main())

TaskGroup 은 한 작업이 실패하면 형제 작업을 자동으로 취소하고 모든 예외를 ExceptionGroup 으로 모음. "구조적 동시성 (structured concurrency)" 이라 부름.

5. ASGI 의 위치

WSGI (2003, PEP 333 / 3333) 는 동기 인터페이스. 비동기 프레임워크가 등장하면서 ASGI (Asynchronous Server Gateway Interface) 가 정의. Tom Christie 가 2018 년 무렵 Django Channels · Starlette 작업 중 다듬음.

  • Starlette (Tom Christie, 2018) — 가벼운 ASGI 프레임워크.
  • FastAPI (Sebastián Ramírez, 2018) — Starlette 위에 Pydantic 으로 타입 · 검증 · OpenAPI 자동 생성을 더한 프레임워크.
  • Uvicorn (2017) · Hypercorn — ASGI 서버 구현.

6. 다른 길

비동기 모델 자체에 다른 시도:

도구 시작 특징
asyncio 2014, 표준 가장 넓게 쓰임.
trio 2017, Nathaniel J. Smith 구조적 동시성을 처음부터 설계 원칙으로. nursery 개념.
anyio 2018, Alex Grönholm trio 와 asyncio 양쪽 위에서 같은 코드를 돌릴 수 있는 추상층. Starlette · FastAPI 가 내부에서 사용.
curio 2016, David Beazley 실험적. asyncio 의 대안 설계 탐구.

asyncio.TaskGroup 은 trio 의 nursery 영향을 받은 구조.

동기 프레임워크와의 비교:

프레임워크 모델 비고
Django WSGI (동기) + ASGI (부분) 2005. 가장 큰 생태계.
Flask WSGI (동기) 2010. 마이크로 프레임워크의 원형.
FastAPI ASGI (비동기) 2018. 타입 힌트 기반 자동 검증 · 문서화.
Sanic ASGI (비동기) 2016. 초기 비동기 시도 중 하나.

비동기가 만능은 아닙니다. 라이브러리 · 드라이버가 비동기 인터페이스를 제공하지 않으면 이벤트 루프를 막아 오히려 느려짐. CPU 바운드 작업도 마찬가지. 이런 자리에는 asyncio.to_thread (3.9+) 또는 별도 워커 프로세스.

7. 자주 쓰는 모양

import asyncio, httpx

async def fetch_all(urls: list[str]) -> list[int]:
    async with httpx.AsyncClient(timeout=10) as c:
        async with asyncio.TaskGroup() as tg:
            tasks = [tg.create_task(c.get(u)) for u in urls]
    return [t.result().status_code for t in tasks]

8. 자주 걸리는 자리

동기 코드를 async 함수 안에서 그대로 호출 — CPU 작업 또는 동기 DB 드라이버가 이벤트 루프를 막음. asyncio.to_thread(fn, *args) 또는 loop.run_in_executor 로 풀어냄.

time.sleep vs asyncio.sleep — 전자는 루프 전체를 멈춤. async 함수 안에서는 await asyncio.sleep(...).

누락된 await — f() 만 호출하면 코루틴 객체만 만들어지고 실행되지 않음. 경고가 뜨지만 놓치기 쉬움.

이벤트 루프 정책 — Windows 의 기본 루프는 한동안 ProactorEventLoop. 라이브러리에 따라 SelectorEventLoop 가 필요할 수 있음.

GIL 의존성을 잊은 멀티스레드 CPU 코드 — 코어 수만큼 스레드를 띄워도 빠르지 않음. multiprocessing 또는 NumPy 같은 GIL-releasing C 확장이 필요.

CancelledError — asyncio 의 취소는 예외로 전달. except Exception 으로 삼키면 취소가 무력해짐. except 시 CancelledError 를 다시 raise 하는 패턴이 안전.

하고픈 말

Python 의 비동기는 GIL 의 한계를 이벤트 루프와 협력적 스케줄링으로 우회하는 길. asyncio + FastAPI + Pydantic + httpx 의 조합이 모던 Python 비동기의 기본 짝. CPU 바운드는 여전히 multiprocessing 또는 C 확장의 자리. PEP 703 의 free-threaded 빌드가 정식화되면 그림이 다시 한 번 바뀔 자리.

Next

  • rust-for-tauri
  • (languages 끝)

Python 공식 사이트 · Python Documentation asyncio · PEPs Index · PEP 492 Coroutines with async and await · PEP 703 Making the GIL Optional in CPython · trio · anyio · ASGI 명세 · FastAPI 를 참고합니다.

languages 카테고리의 다른 글

카테고리 전체 보기 →
  • Rust — Tauri 가 Rust 인 이유
  • Java 21 의 모습
  • TypeScript 와 strict 모드