
가장 기본적인 연결을 수행해보는 예제다.
내 작업 환경에는 이미 Rust와 Python이 설치되어 있으니
언어에 대한 개발 환경 구축은 생략한다.
~/workspace$ mkdir fastapi_rust_bridge && cd fastapi_rust_bridge ~/workspace/fastapi_rust_bridge$ python3 -m venv venv ~/workspace/fastapi_rust_bridge$ source venv/bin/activate ~/workspace/fastapi_rust_bridge$ pip install maturin fastapi uvicorn ~/workspace/fastapi_rust_bridge$ maturin init ~/workspace/fastapi_rust_bridge$ # 선택지 중 기본값인 PyO3 선택 ~/workspace/fastapi_rust_bridge$ # Cargo.toml과 src/lib.rs가 자동 생성된다 ~/workspace/fastapi_rust_bridge$ # Python 코드는 직접 생성해 주어야 한다 ~/workspace/fastapi_rust_bridge$ mkdir app && touch app/main.py ~/workspace/fastapi_rust_bridge$ tree -I venv . ├── app │ └── main.py ├── Cargo.toml ├── pyproject.toml └── src └── lib.rs
Cargo.toml 파일을 열어 라이브러리 이름을 수정해 주겠다.
앞으로 모든 실습에서 패키지 이름은 프로젝트 이름을 그대로 사용하고
라이브러리 이름은 rust_engine 으로 통일할 것이다.
Cargo.toml[package] name = "fastapi-rust-bridge" # pip list 할 때 뜨는 패키지명 version = "0.1.0" edition = "2024" [lib] name = "rust_engine" # Python에 import 할 때 사용하는 이름 crate-type = ["cdylib"] [dependencies] pyo3 = "0.28.0"
만약 한 번 빌드한 이후에 패키지 이름을 변경하게 될 경우
pip uninstall [기존 패키지 이름] 으로 정리해주는 것이 깔끔하다.
Python에서 사용할 모든 함수에 #[pyfunction] 속성을 붙여야 하며
#[pymodule] 속성이 붙은 함수에서 add_function 함수를 통해 등록해 주어야 한다.
이 때, #[pymodule] 속성이 붙은 함수 이름은 Cargo.toml 파일의 라이브러리 이름과 일치해야 한다.
src/lib.rsuse pyo3::prelude::*; #[pyfunction] fn compute_sum_fast(n: u64) -> PyResult<u64> { Ok((0..n).sum()) } #[pyfunction] fn calculate_square(n: u64) -> PyResult<u64> { Ok(n * n) } #[pymodule] fn rust_engine(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(compute_sum_fast, m)?)?; m.add_function(wrap_pyfunction!(calculate_square, m)?)?; Ok(()) }
마찬가지로 Cargo.toml 파일의 라이브러리 이름으로 모듈을 import 해야 한다.
app/main.pyfrom fastapi import FastAPI import rust_engine app = FastAPI() @app.get("/") def index(): return { "status": "running", "engine": "Rust + PyO3 0.27", "message": "FastAPI with Rust Engine" } @app.get("/sum/{number}") async def get_sum(number: int): result = rust_engine.compute_sum_fast(number) return { "input": number, "result": result, "message": "Computed by Rust" } @app.get("/square/{number}") async def get_square(number: int): result = rust_engine.calculate_square(number) return { "input": number, "result": result, "message": "Computed by Rust" }
Maturin 라이브러리를 통해 Rust 코드를 Python에서 호출 가능한 형태로 컴파일한다.
컴파일 후 pip list 명령어를 사용해 보면 Cargo.toml 파일에 작성한 패키지 이름을 확인할 수 있다.
uvicorn 라이브러리를 통해 FastAPI를 실행한다.
~/workspace/fastapi_rust_bridge$ maturin develop ~/workspace/fastapi_rust_bridge$ uvicorn app.main:app --reload
curl 명령어 또는 브라우저를 통해 다음과 같은 테스트를 해볼 수 있다.
http://127.0.0.1:8000/square/12http://127.0.0.1:8000/sum/10