복잡한 데이터 다루기

Pt J·2026년 3월 20일
post-thumbnail

복잡한 데이터 다루기

복잡한 데이터라고 했지만 아주 복잡한 녀석은 아니고,
ListDict 를 다루는 예제다.

Python의 List 는 Rust의 Vec 으로 작성되며
Python의 Dict 는 Rust의 HashMap 으로 작성된다.

작업공간 생성 및 구조 확인

~/workspace$ mkdir complex-data && cd complex-data
~/workspace/complex-data$ python3 -m venv venv
~/workspace/complex-data$ source venv/bin/activate
~/workspace/complex-data$ pip install maturin fastapi uvicorn
~/workspace/complex-data$ maturin init
~/workspace/complex-data$ # 선택지 중 기본값인 PyO3 선택
~/workspace/complex-data$ # Cargo.toml과 src/lib.rs가 자동 생성된다
~/workspace/complex-data$ # Python 코드는 직접 생성해 주어야 한다
~/workspace/complex-data$ mkdir app && touch app/main.py
~/workspace/complex-data$ tree -I venv
.
├── app
│   └── main.py
├── Cargo.toml
├── pyproject.toml
└── src
    └── lib.rs

Cargo.toml 파일을 열어 라이브러리 이름을 수정해 주겠다.

Cargo.toml

[package]
name = "complex-data"
version = "0.1.0"
edition = "2024"

[lib]
name = "rust_engine"
crate-type = ["cdylib"]

[dependencies]
pyo3 = "0.28.0"

코드 작성

Rust 코드

List 역할을 하는 Vec 는 Rust의 기본 자료형 중 하나이므로
따로 불러오지 않아도 사용할 수 있지만
Dict 역할을 하는 HashMap 의 경우 std::collections::HashMap 을 불러와야 한다.

Python에서 List 로 숫자를 받아 Rust에 전해주면
Rust가 그것을 Vec 로서 처리하여 HashMap 에 결과값을 넣고 반환하며
Python이 반환받은 값을 Dict 로서 처리하는 코드를 작성할 것이다.

src/lib.rs

use pyo3::prelude::*;
use std::collections::HashMap;

#[pyfunction]
fn process_data(data: Vec<i32>) -> PyResult<HashMap<String, f64>> {
    let sum: i32 = data.iter().sum();
    let count = data.len();

    let avg = if count > 0 {
        sum as f64 / count as f64
    } else {
        0.0
    };

    let mut stats = HashMap::new();
    stats.insert("sum".to_string(), sum as f64);
    stats.insert("average".to_string(), avg);
    stats.insert("count".to_string(), count as f64);

    Ok(stats)
}

#[pymodule]
fn rust_engine(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(process_data, m)?)?;

    Ok(())
}

Python 코드

여기서는 정수 값을 담는 List 를 속성으로 가진 DataInput 클래스를 만들어 사용할 것이다.
이것은 데이터 검증 라이브러리 pydanticBaseModel 클래스를 상속받아 생성한다.

과거 버전의 경우 from typing import List 를 통해 List 를 가져와야
자료형을 명시하여 사용할 수 있었지만
Python 3.9부터는 기본 list 에도 타입 힌트를 포함할 수 있게 되었다.
이 실습에서는 3.9+ 버전의 Python을 상정하고 코드를 작성한다.

app/main.py

from fastapi import FastAPI
from pydantic import BaseModel
import rust_engine

app = FastAPI()

class DataInput(BaseModel):
    numbers: list[int]

@app.post("/analyze")
def analyze_data(input_data: DataInput):
    stats = rust_engine.process_data(input_data.numbers)

    return {
        "source": "FastAPI",
        "engine": "Rust",
        "analysis": stats
    }

빌드 및 실행

Maturin 라이브러리를 통해 Rust 코드를 Python에서 호출 가능한 형태로 컴파일한다.
컴파일 후 pip list 명령어를 사용해 보면 Cargo.toml 파일에 작성한 패키지 이름을 확인할 수 있다.

uvicorn 라이브러리를 통해 FastAPI를 실행한다.

~/workspace/complex-data$ maturin develop
~/workspace/complex-data$ uvicorn app.main:app --reload

curl 명령어 또는 브라우저를 통해 테스트를 해볼 수 있다.

이 예제의 경우 GET 메서드가 아닌 POST 메서드로 통신하므로
브라우저를 통한 테스트 시 Swagger UI를 사용해야 한다.
FastAPI의 경우 다음과 같은 주소로 Swagger UI가 내장되어 있다.

  • http://127.0.0.1:8000/docs
profile
Peter J Online Space - since July 2020 | 아무데서나 채용해줬으면 좋겠다

0개의 댓글