[Serving] LLM 시대, FastAPI와 Streamlit으로 웹 아키텍처 구축하기

2025. 10. 11. 20:13·기술 견문록/ProductServing

개요

최근 LLM API를 활용한 프로덕트 개발이 폭발적으로 증가하면서, Streamlit을 사용해서 서비스를 만드시는 분들이 크게 늘어난 것 같습니다.  그런데 다른 분들의 프로젝트를 보다보니 Streamlit 애플리케이션 하나에 로직을 넣는 경우가 많이 보였고, 대다수의 튜토리얼 코드에서도 API 호출, 데이터 처리, UI 렌더링까지 모든 것이 하나의 'py'파일에 작성하는 경우도 자주 보였습니다. 이럴 경우, 유지보수나 안정성 측면에서 불안정하지만 이를 알려주는 자료가 많지 않은 것 같다는 생각을 했습니다. 그래서 FastAPI를 백엔드로, Streamlit을 프론트엔드로 하는 분리형 아키텍처를 만들어보고, docker를 활용해 컨테이너를 올려볼 수 있게 코드를 만들어보고자 했습니다.

 

그래서 이번 글에서는 아키텍처 구성 및 코드 작성 이전에 해당 기술들의 사용 이유와 왜 이런 구조가 필요한지 간단히 알아보고, 이를 활용해 구성된 분리형 아키텍처를 구성해보고자 합니다. 그리고 해당 기술을 통해 구현된 아키텍처를 사용해보실 수 있게 관련 코드를 GitHub Repository에 공유하고자 합니다. 

💡 이 글에서 다룰 내용은 다음과 같습니다.

- 왜 백엔드와 프론트엔드를 분리해야 하는 이유
- FastAPI와 Streamlit을 선택한 이유
- Docker 를 사용해야 하는 이유
- FastAPI, Streamlit, Docker로 구성한 아키텍처

 

1. 분리형 아키텍처를 써야하는 이유

문제 상황: 모놀리식 Streamlit의 한계

종종 streamlit으로 작성된 코드나 튜토리얼을 보면 다음과 같이 코드를 작성하십니다. 데이터를 전처리하고, LLM모델을 호출하는 모든 함수를 하나의 코드에서 관리하고, Streamlit을 실행하기도 합니다. 이런 경우를 "모놀리식 아키텍처"라고 합니다.

 

# ❌ 나쁜 예: 모든 것이 app.py에 있는 경우
import streamlit as st
import openai

def call_llm(prompt):
    # 복잡한 프롬프트 엔지니어링
    # Agent 기반 multi-turn 대화
    # 결과 파싱 및 정제
    # 500줄의 LLM 로직...
    pass

def process_data(data):
    # 데이터 전처리
    # 비즈니스 로직
    # 200줄의 처리 로직...
    pass

# UI 렌더링도 여기서...
st.title("My App")
result = call_llm(st.text_input("질문"))
st.write(result)

그러나 이런 구조의 경우 다음과 같은 문제점이 있습니다.

  1. 장애 전파: Streamlit 서버가 다운되면 모든 기능이 중단됩니다.
  2. 확장성 제약: 기능 A를 수정했는데 기능 B, C까지 영향을 받습니다.
  3. 재사용 불가: API 로직을 다른 서비스(모바일 앱, CLI 도구)에서 사용할 수 없습니다.
  4. 테스트 어려움: UI와 로직이 강결합되어 단위 테스트가 불가능합니다.
  5. 팀 협업 난이도: 백엔드 개발자와 프론트엔드 개발자가 같은 파일을 수정해야 합니다.

해결책: 관심사의 분리 (Separation of Concerns)

이를 해결하기 위해서 다음과 같이 서비스들을 분리할 수 있습니다.

분리 시 장점:

  • 배포 독립성: 프론트엔드 변경이 백엔드에 영향 없음
  • 장애 격리: 백엔드 문제가 프론트엔드 서버 다운으로 이어지지 않음
  • 명확한 책임: 각 서비스가 단일 책임 원칙(Single Responsibility Principle, SRP)을 준수
  • 독립적 확장: 백엔드만 스케일 아웃 가능 (Kubernetes HPA 등)
  • 다중 클라이언트 지원: 웹(Streamlit), 모바일, API 직접 호출 모두 가능

2. 기술 스택 알아보기

2-1. Streamlit

Streamlit은 Python 개발자가 빠르게 UI를 구현할 수 있는 도구입니다. 매우 짧은 시간(30분 이내)에 인터랙티브한 대시보드를 만들 수 있어, MVP나 내부 대시보드, 데모와 같은 용도로 매우 적합합니다. 특히, 데이터 시각화나 머신러닝 모델 결과 표시에도 효율적으로 사용할 수 있습니다.

 

Streamlit도 단점이 존재(복잡한 상태 관리가 어렵고, 커스텀 디자인에 제약 등)합니다만, 이러한 단점을 감수할 만큼 개발 생산성과 접근성이 뛰어나기 때문에 Streamlit을 자주 사용하며 빠르게 성장하고 있는 도구입니다.

# JavaScript 없이 순수 Python으로 UI 구현
import streamlit as st

st.title("🚀 대시보드")
name = st.text_input("이름을 입력하세요")
if st.button("제출"):
    st.success(f"안녕하세요, {name}님!")

2-2. FastAPI

FastAPI는 Python 기반의 최신 웹 프레임워크로, 빠르고 간결한 API 서버 개발에 최적화되어 있습니다. 비동기(Async) 처리를 기본 지원하며, 자동 문서화 등 생산성이 뛰어납니다. 그리고 성능 또한 타 pyhton 기반 웹 프레임워크보다 뛰어납니다.

  • 비동기 처리: 하나의 서버에서 여러 사용자의 요청을 동시에 처리하는 것을 의미하며, FastAPI에서는 "await/async" 키워드로 사용합니다.
  • 자동 API문서 생성: FastAPI는 인터랙티브 API 문서 자동 생성하며, /docs로 접속해 확인이 가능합니다. 
  • Pydantic 기반 데이터 검증: 타입 힌팅(Type Hinting)만으로 자동 검증이 가능해서 잘못된 데이터가 들어올 상황을 방지할 수 있습니다. 

비동기 처리 (Async/Await)

@app.post("/api/v1/llm/generate")
async def generate_text(request: GenerateRequest):
    # LLM API 호출은 I/O bound 작업
    # async로 처리하면 동시 요청 처리량이 10배 이상 증가
    result = await openai_client.chat.completions.create(...)
    return result

자동 API 문서 생성

# FastAPI는 코드만 작성하면 Swagger UI가 자동 생성됨
@app.post("/api/v1/users/", response_model=UserResponse)
async def create_user(user: UserCreate):
    """
    사용자를 생성합니다.

    - **username**: 고유한 사용자명
    - **email**: 이메일 주소
    """
    pass

Pydantic 기반 데이터 검증

from pydantic import BaseModel, EmailStr, validator

class UserCreate(BaseModel):
    username: str
    email: EmailStr
    password: str

    @validator('username')
    def username_alphanumeric(cls, v):
        assert v.isalnum(), 'must be alphanumeric'
        return v

 

2-3. Docker

Docker를 사용하는 이유에 대해서 간단히 짚고 넘어가보겠습니다. 다음과 같은 상황을 가정해보겠습니다. 개발자 A의 컴퓨터에서 구동되던 코드를 개발자 B가 자신의 컴퓨터에서 구동해보고자 합니다. 하지만 개발자 A와 B의 환경이 상이할 수 있습니다. 이 경우, 의존성 충돌, 버전 불일치, OS 차이로 구동이 안 되는 문제가 발생할 수 있습니다. 이런 상황에서 Docker를 사용하면 모든 환경(A/B의 로컬 컴퓨터, 스테이징, 프로덕션) 에서 동일하게 실행이 가능합니다.

문제 상황

# 개발자 A의 환경
Python 3.9, macOS, pip로 설치

# 개발자 B의 환경
Python 3.11, Windows, conda로 설치

# 프로덕션 서버
Python 3.10, Ubuntu, apt로 설치

 

Docker로 문제 해결

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]

 

2-4. Docker Compose

Docker Compose의 경우, 컨테이너와 컨테이너 간 연결을 수월하게 만들어 주고 컨테이너 배포를 편리하게 도와줍니다. 예를 들어, 백엔드(Fastapi)는 localhost:8000에서 실행하고, 프론트엔드(Streamlit)는 localhost:8501에서 각각 실행한다고 가정해보겠습니다. 여기서 프론트엔드 컨테이너 내부에서 "localhost:8000"으로 호출하면 어떻게 될까요?

 

이때, 호출은 "연결 실패"가 됩니다. 왜냐하면, 컨테이너 내부의 localhost는 자기 자신(프론트엔드 컨테이너)을 가리키게 되기 때문입니다 (Docker에 대한 이해가 충분치 않으시면, 해당 부분은 완벽히 이해가 어려우실 수도 있으니 넘어가셔도 괜찮습니다). 이러한 상황을 해결하기 위해 docker-compose를 사용할 수 있습니다.

마이크로서비스 간 네트워킹

services:
  backend:
    build: ./backend
    networks:
      - app_network

  frontend:
    build: ./frontend
    environment:
      - API_HOST=http://backend:8000  # 서비스명으로 통신!
    networks:
      - app_network
    depends_on:
      - backend

networks:
  app_network:
    driver: bridge

Docker Compose는 다음과 같은 기능을 제공합니다. 

  1. 자동 DNS 제공: 서비스명(backend)이 IP 주소로 자동 변환
  2. 의존성 관리: depends_on으로 시작 순서 제어
  3. 네트워크 격리: 외부와 격리된 안전한 내부 네트워크

배포 간소화

무엇보다 좋은 것은, 한 줄의 명령어로 전체 시스템 실행이 가능하다는 것입니다. 저희 상황에서 Backend와 Frontend에 해당하는 서비스를 2개 올리기 위해서는 개별적으로 하나씩 올려줘야 합니다 (네트워크까지 연결될 경우 관리가 더 어려워집니다). 그러나 이러한 상황에서 docker-compose를 사용하면 아래처럼 한 줄의 코드로 간단하게 배포가 가능합니다.

# 로컬 개발
docker-compose up

3. 아키텍처

 

Docker 기반 FastAPI + Streamlit 아키텍처 구조

4. 보이러 플레이트 프로젝트 구조

해당 프로젝트에서는 간단하게 "유저 정보와 상품 정보를 등록하고 검색하는 서비스"를 구현해보고자 합니다. Backend에서는 유저 정보에 해당하는 서비스는 'users' 모듈로 상품 정보에 해당하는 서비스는 'items' 모듈로 분리하고, 이에 대한 API를 구축합니다. Frontend에서는 해당 API들을 호출할 수 있게, User의 입력을 받고 이를 출력해주는 UI로 구성합니다. 

 

프로젝트 구조는 크게 Frontend와 Backend로 나눠져 있으며, Docker-compose를 사용해서 컨테이너를 올리는 구조로 되어 있습니다. 설계 시 고려한 사항은 다음과 같습니다.

  • 모듈화: 각 라우터가 독립적인 파일 (users.py, items.py)
  • 환경 분리: 설정은 config.py에서 환경 변수로 관리
  • 데이터 검증: Pydantic 모델을 별도 파일로 분리
  • 컨테이너 독립성: 각 서비스가 자체 Dockerfile 보유
fastapi-streamlit-boilerplate/
├── backend/                    # FastAPI 백엔드
│   ├── main.py                # 앱 진입점, 라우터 등록
│   ├── config.py              # 환경 변수 관리
│   ├── models.py              # Pydantic 모델
│   ├── routers/               # API 엔드포인트
│   │   ├── users.py           # 사용자 API
│   │   └── items.py           # 아이템 API
│   ├── Dockerfile             # 백엔드 컨테이너 이미지
│   └── requirements.txt
│
├── frontend/                   # Streamlit 프론트엔드
│   ├── app.py                 # Streamlit 메인 앱
│   ├── Dockerfile             # 프론트엔드 컨테이너 이미지
│   └── requirements.txt
│
├── docker-compose.yml         # 멀티 컨테이너 오케스트레이션
├── .dockerignore
└── README.md

5. 핵심 코드

⚠️ 콘텐츠 분량 초과로 인해 전체 코드는 GitHub Repository를 참고해주시기 바랍니다. (필요 시, 추후 별도 포스팅)

 

6. 결론

이번 글에서는 FastAPI와 Streamlit을 분리한 아키텍처를 Docker-Compose로 묶어 일관된 실행 환경에서 동작하도록 설계하는 방법을 살펴봤습니다. 이 구조는 모놀리식 Streamlit이 가지는 확장성과 유지보수성의 한계를 해소하고, API 재사용성을 크게 높여줄 수 있습니다.

핵심 요약
  - 프론트엔드와 백엔드의 명확한 분리로 장애 격리와 독립 배포 가능
  - FastAPI의 비동기 처리·자동 문서화·데이터 검증으로 생산성과 신뢰성 향상
  - Streamlit으로 빠른 UI 개발, 실험→프로덕션 전환을 매끄럽게 지원
  - docker-compose로 환경 일치, 간편한 멀티 서비스 오케스트레이션

 

물론, 모든 프로젝트를 다음과 같이 관리하실 필요는 없습니다. 간단한 테스트 서비스나 기능이 많지 않을 경우 오히려 오버엔지니어링이 될 수도 있습니다(저 또한 초기에는 모놀리식으로 구현하기도 합니다). 그러나 성장 가능성이 있는 프로젝트나 기능이 많아지는 서비스들의 경우, 추후 소요되는 과정의 비용을 크게 줄일 수 있을 것입니다. 모놀리식으로 시작하셨더라도 보일러플레이트 코드(전체 코드)를 참고해 점진적으로 분리해 보는 연습을 해보시는 것을 추천드립니다.

저작자표시 비영리 변경금지 (새창열림)

'기술 견문록 > ProductServing' 카테고리의 다른 글

[FastAPI] FastAPI 기초 지식  (0) 2022.05.25
[Backend] 백엔드 기초 지식  (0) 2022.05.24
[Streamlit] Streamlit 명령어  (2) 2022.05.18
[Ipywidget] Ipywidget 명령어  (0) 2022.05.18
'기술 견문록/ProductServing' 카테고리의 다른 글
  • [FastAPI] FastAPI 기초 지식
  • [Backend] 백엔드 기초 지식
  • [Streamlit] Streamlit 명령어
  • [Ipywidget] Ipywidget 명령어
논곰
논곰
현재 2년 유목하고, 3년 이상 리테일 쪽에서 머신러닝 엔지니어로 잠시 정착 중인 AI 엔지니어입니다.
  • 논곰
    에이아이 유목민
    논곰
  • 전체
    오늘
    어제
    • 분류 전체보기 (200)
      • 기술 견문록 (22)
        • MLOps (8)
        • ProductServing (5)
        • 협업 툴 (3)
        • Error Collecting (2)
        • 컨퍼런스 (1)
        • 자격증 (1)
      • IT 견문록 (10)
        • 추가 학습 정리 (10)
      • 알고리즘_코딩테스트 (162)
        • 프로그래머스_Level1 (40)
        • 백준코딩테스트_단계별문제풀이 (14)
        • 이것이 코딩테스트다 (63)
        • 2021_알고리즘 스터디 (30일) (28)
        • 주간코딩 스터디 (주코스) (17)
      • 독서 견문록 (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Level1
    글또
    그래프이론
    알고리즘_스터디
    부스트캠프_AITech3기
    다시보기
    백트랙킹
    파이썬 3
    프로그래머스
    단계별문제풀이
    이코테
    주간회고
    정렬
    mrc
    이진탐색
    dp
    구현
    python3
    dfs
    Level2
    U_stage
    ODQA
    그리디
    알고리즘스터디
    기술면접
    최단경로
    MLFlow
    백준
    부스트캠프_AITech_3기
    Level2_PStage
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
논곰
[Serving] LLM 시대, FastAPI와 Streamlit으로 웹 아키텍처 구축하기
상단으로

티스토리툴바