FastAPI로 자신만의 포럼 구축하기: 1단계 - 최소 포럼
Takashi Yamamoto
Infrastructure Engineer · Leapcell

시중에 이미 많은 포럼 제품이 있지만, 마음에 드는 것이 없어 여전히 답답하신가요? 그렇다면 자신만의 포럼을 처음부터 만들어 보는 것은 어떨까요?
Discourse와 같은 거대한 포럼 SaaS 플랫폼에 겁먹지 마세요. 포럼을 만드는 것은 생각보다 어렵지 않습니다.
앞으로의 시리즈에서는 인기 있는 Python 웹 프레임워크인 FastAPI를 사용하여 처음부터 완전히 기능하는 최신 포럼 웹사이트를 단계별로 구축하는 방법을 안내할 것입니다.
이 튜토리얼 시리즈는 초보자를 위해 맞춤 제작되었습니다. 이 첫 번째 기사의 끝에는 아래와 같이 작동하는 미니 포럼이 완성될 것입니다.
더 이상 지체하지 말고 시작해 보겠습니다.
1단계: 환경 설정
먼저 개발 환경을 준비해 보겠습니다.
프로젝트 디렉토리 및 가상 환경 만들기
프로젝트를 위한 전용 폴더를 만들고 그 안에 가상 환경을 만드세요.
간단하게 하기 위해 Python의 내장 venv
모듈을 사용하겠습니다. poetry
와 같이 직접 탐색해 볼 수 있는 훌륭한 다른 가상 환경 도구들도 많이 있습니다.
# 프로젝트 디렉토리 생성 및 진입 mkdir fastapi-forum cd fastapi-forum # 가상 환경 생성 python3 -m venv venv # 가상 환경 활성화 # Windows: # venv\Scripts\activate # macOS / Linux: source venv/bin/activate
성공적으로 활성화되면 명령줄 프롬프트 앞에 (venv)
가 붙는 것을 볼 수 있습니다.
FastAPI 및 Uvicorn 설치
다음으로, 이 기사에 필요한 핵심 라이브러리인 fastapi
와 uvicorn
을 설치합니다.
fastapi
: 프레임워크 자체.uvicorn
: FastAPI 애플리케이션을 실행하는 데 사용되는 ASGI 서버.
pip install fastapi "uvicorn[standard]"
"uvicorn[standard]"
는 최적의 성능을 위해 권장 종속성과 함께 uvicorn을 설치합니다.
2단계: "안녕하세요, 포럼!" FastAPI 앱 실행하기
환경이 준비되었으니 첫 번째 FastAPI 코드를 작성해 보겠습니다. fastapi-forum
디렉토리에 main.py
라는 파일을 만들고 다음 내용을 추가합니다.
main.py
from fastapi import FastAPI app = FastAPI() @app.get("/") def read_root(): return {"message": "Hello, Forum!"}
이 코드는 무엇을 하나요?
FastAPI
인스턴스를 생성하고app
이라고 이름 붙입니다.@app.get("/")
는 데코레이터입니다. 이는 아래의read_root
함수가/
경로로 들어오는GET
요청을 처리한다는 것을 FastAPI에 알려줍니다.- 루트 경로로 요청이 오면, 함수는 Python 딕셔너리를 반환하며, FastAPI는 이를 자동으로 JSON 응답으로 변환합니다.
다음으로 터미널에서 애플리케이션을 실행합니다.
uvicorn main:app --reload
main
:main.py
파일을 참조합니다.app
:main.py
에서 생성한FastAPI
인스턴스app
을 참조합니다.--reload
: 코드 변경 후 서버가 자동으로 다시 시작되도록 하는 매우 유용한 매개변수입니다.
이제 브라우저를 열고 http://127.0.0.1:8000
으로 이동합니다. 다음을 볼 수 있습니다.
{ "message": "Hello, Forum!" }
축하합니다! 첫 번째 FastAPI 애플리케이션이 성공적으로 실행되었습니다!
3단계: 핵심 데이터 구조 정의
우리의 포럼은 게시물을 발행하기 위한 것이므로, "게시물"이 어떻게 생겼는지를 정의하는 명확한 데이터 구조가 필요합니다. FastAPI는 데이터 정의 및 유효성 검사를 위해 Pydantic
사용을 권장합니다. 이는 FastAPI에 내장되어 있으며 매우 강력합니다.
main.py
에 Post
모델을 정의해 보겠습니다.
main.py
(업데이트됨)
from fastapi import FastAPI from pydantic import BaseModel from typing import List # 1. 데이터 모델 정의 class Post(BaseModel): id: int title: str content: str app = FastAPI() # 2. 메모리 내 데이터베이스로 리스트 사용 # 참고: 이 기능은 시연용입니다. 서버를 다시 시작하면 데이터가 손실됩니다! db: List[Post] = [ Post(id=1, title="What is FastAPI?", content="A modern, high-performance Python web framework..."), Post(id=2, title="Introduction to Pydantic", content="Pydantic is a library for data validation and settings management..."), ] @app.get("/") def read_root(): return {"message": "Welcome to my forum!"} # 후속 API는 여기에 추가됩니다...
pydantic.BaseModel
을 상속하는 Post
클래스를 만들었습니다. 게시물이 id
(정수), title
(문자열), content
(문자열)의 세 가지 필드를 가져야 함을 정의했습니다.
db
라는 리스트를 생성하고 두 개의 Post
객체로 미리 채워 우리의 데이터베이스를 시뮬레이션했습니다.
4단계: 핵심 API 구현
기본 포럼에는 두 가지 기능이 필요합니다. 게시물을 보고 게시물을 만드는 것입니다. 해당 API 엔드포인트를 구현해 보겠습니다.
1. 모든 게시물 가져오기
db
리스트의 모든 게시물을 반환하는 엔드포인트가 필요합니다. main.py
에 다음 코드를 추가하세요.
main.py
(계속)
... # 이전 코드는 변경되지 않음 # 3. 게시물 목록을 가져오는 API @app.get("/api/posts", response_model=List[Post]) def get_posts(): return db
@app.get("/api/posts")
:GET
요청을 처리하는 새로운 경로/api/posts
를 정의합니다.response_model=List[Post]
: 이 엔드포인트의 응답 본문이Post
객체의 리스트임을 FastAPI에 알립니다. FastAPI는 이를 데이터 유효성 검사, 변환 및 API 문서에서의 명확한 문서화를 위해 사용합니다.
2. 새 게시물 만들기
다음은 새 게시물을 만드는 엔드포인트입니다.
먼저 폼 데이터 처리를 지원하기 위해 python-multipart
를 설치합니다.
pip install python-multipart
main.py
(계속)
# ... 이전 코드는 변경되지 않음 # 4. 새 게시물을 만드는 API @app.post("/api/posts", response_model=Post) def create_post(title: str = Form(...), content: str = Form(...)): new_id = len(db) + 1 new_post = Post( id=new_id, title=title, content=content ) db.append(new_post) return new_post
title: str = Form(...)
와 같은 매개변수 형식은 폼 필드에서 데이터를 추출하도록 FastAPI에 지시합니다.
입력/출력 형식이 폼 필드인 이유는 무엇인가요? 나중에 사용자가 폼을 통해 게시물 내용을 제출할 수 있는 HTML 페이지를 만들 것이기 때문입니다.
5단계: 간단한 대화형 페이지
순수 API는 실제로 포럼이라고 불릴 없습니다. FastAPI의 HTMLResponse
를 사용하여 사용자가 브라우저에서 직접 게시물을 만들고 볼 수 있는 사용 가능한 HTML 페이지를 빠르게 만들 수 있습니다.
이 페이지를 표시하기 위해 GET /posts
라는 새 경로를 만들 것입니다.
main.py
(최종 전체 버전)
from fastapi import FastAPI, Form from fastapi.responses import HTMLResponse, RedirectResponse from pydantic import BaseModel from typing import List # --- 데이터 모델 --- class Post(BaseModel): id: int title: str content: str # --- 메모리 내 데이터베이스 --- db: List[Post] = [ Post(id=1, title="What is FastAPI?", content="A modern, high-performance Python web framework..."), Post(id=2, title="Introduction to Pydantic", content="Pydantic is a library for data validation and settings management..."), ] app = FastAPI() # --- HTML 템플릿 --- def generate_html_response(): posts_html = "" for post in reversed(db): # 새 게시물을 위쪽에 표시 posts_html += f""" <div style="border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;"> <h3>{post.title} (ID: {post.id})</h3> <p>{post.content}</p> </div> """ html_content = f""" <html> <head> <title>My FastAPI Forum</title> <style> body {{ font-family: sans-serif; margin: 2em; }} input, textarea {{ width: 100%; padding: 8px; margin-bottom: 10px; box-sizing: border-box; }} button {{ padding: 10px 15px; background-color: #007BFF; color: white; border: none; cursor: pointer; }} button:hover {{ background-color: #0056b3; }} </style> </head> <body> <h1>Welcome to My Forum</h1> <h2>Create a New Post</h2> <form action="/api/posts" method="post"> <input type="text" name="title" placeholder="Post Title" required><br> <textarea name="content" rows="4" placeholder="Post Content" required></textarea><br> <button type="submit">Post</button> </form> <hr> <h2>Post List</h2> {posts_html} </body> </html> """ return HTMLResponse(content=html_content, status_code=200) # --- 라우트 --- @app.get("/", response_class=RedirectResponse) def read_root(): # 루트 경로를 보기 페이지로 리다이렉션 return "/posts" # 페이지를 표시하는 라우트 @app.get("/posts", response_class=HTMLResponse) async def view_posts(): return generate_html_response() @app.post("/api/posts") def create_post(title: str = Form(...), content: str = Form(...)): new_id = len(db) + 1 new_post = Post( id=new_id, title=title, content=content ) db.append(new_post) # 리디렉션을 통해 게시 후 새로고침 효과를 위해 메인 페이지로 다시 리다이렉션 return RedirectResponse(url="/posts", status_code=303)
generate_html_response
함수를 사용하여 폼과 게시물 목록이 포함된 완전한 HTML 페이지를 동적으로 생성합니다. 조잡하지만 이 단계에서는 완벽하게 충분합니다.
GET /posts
는 생성된 HTML 페이지를 직접 반환하여 사용자가 /posts
를 방문할 때 포럼 인터페이스를 볼 수 있게 합니다. 또한 루트 경로 /
는 /posts
로 리다이렉션되므로 루트 URL을 방문하는 사용자는 자동으로 포럼 페이지로 이동합니다.
GET /api/posts
가 제거되었음을 알 수 있습니다. 이는 게시물 목록을 가져오기 위한 별도의 API가 더 이상 필요하지 않기 때문입니다. 페이지는 메모리 내 db
에서 직접 데이터를 읽습니다.
POST /api/posts
에 대한 로직도 수정했습니다. Post
객체를 반환하는 대신 /posts
페이지로 다시 리다이렉션하여 사용자가 제출 후 업데이트된 게시물 목록을 즉시 볼 수 있도록 합니다.
포럼 실행하기
이제 uvicorn 서버가 계속 실행 중인지 확인합니다. http://127.0.0.1:8000
을 새로고침하거나 다시 방문하면 기존 게시물과 새 게시물을 만들 수 있는 폼이 있는 포럼 페이지가 표시됩니다.
폼에 내용을 입력하고 제출하면 게시물 목록에 새 게시물이 나타나는 것을 볼 수 있습니다.
프로젝트 온라인 배포하기
포럼은 모두가 사용할 수 있도록 만들어졌으므로 로컬에서 실행하는 것만으로는 충분하지 않습니다. 다음으로 온라인에 배포할 수 있습니다.
간단한 배포 옵션은 Leapcell을 사용하는 것입니다. FastAPI를 포함한 다양한 언어와 프레임워크의 프로젝트를 호스팅할 수 있는 웹 앱 호스팅 플랫폼입니다.
아래 단계를 따르세요.
- 웹사이트에서 계정을 등록합니다.
- 프로젝트를 GitHub에 커밋합니다. GitHub 공식 문서를 참고하여 단계를 수행할 수 있습니다. Leapcell은 나중에 GitHub 리포지토리에서 코드를 가져올 것입니다.
- Leapcell 페이지에서 "Create Service"를 클릭합니다.
- FastAPI 리포를 선택하면 Leapcell이 필요한 구성을 자동으로 채워줍니다.
- 하단의 "Submit"을 클릭하여 배포합니다. 배포는 빠르게 완료되고 배포 홈페이지로 돌아갑니다. 여기서 Leapcell이 도메인을 제공했음을 알 수 있습니다. 이것이 블로그의 온라인 주소입니다.
결론
몇 가지 짧은 단계만으로 FastAPI를 사용하여 가장 필수적인 기능을 갖춘 포럼 프로토타입을 처음부터 구축했습니다.
이 포럼은 아직 매우 불완전합니다. 예를 들어, 서버를 다시 시작하면 모든 새 게시물이 사라집니다. 이것은 분명히 용납할 수 없습니다.
다음 기사에서는 지속적인 데이터 저장을 가능하게 하도록 포럼에 실제 데이터베이스를 도입할 것입니다.