본 시리즈는 '박응용' 님의 '점프 투 FastAPI'를 바탕으로 학습 및 실습한 내용을 정리한 것입니다.
구현 및 파인튜닝한 모델을 사용한 웹 서비스 구현을 위해 FastAPI의 학습 필요성을 느껴 학습 과정을 정리합니다. 내용의 정확성이나 이론적인 부분은 당연히 원본 페이지를 참조하시는 게 좋고, 본 시리즈에서는 구현 도중 발생하는 문제 등을 해결하는 과정을 함께 기록하여 '처음부터 끝까지 따라 할 수 있는' 시리즈를 만드는 것을 목표로 합니다(물론 제1목표는 학습 내용 기록입니다).
이제 질문을 등록하는 기능을 만들어 보자.
[질문 등록 API 명세]
| API명 | URL | 요청 방법 | 설명 |
|---|---|---|---|
| 질문 등록 | api/question/create | post | 질문을 등록한다. |
[질문 등록 API 입력 항목]
[질문 등록 API 출력 항목]
없음
질문 등록 API의 입력 항목은 subject와 content이다. 따라서 question_schema.py 파일을 수정하여 다음과 같은 QuestionCreate 스키마를 만든다.
import datetime
from pydantic import BaseModel, validator
from domain.answer.answer_schema import Answer
class QuestionCreate(BaseModel):
subject: str
content: str
@validator('subject', 'content')
def not_empty(cls, v):
if not v or not v.strip():
raise ValueError("빈 값은 허용되지 않습니다.")
return v
class Question(BaseModel):
...
이전에 AnswerCreate 스키마를 만들었던 방식과 유사하게 QuestionCreate를 만들었다. subject와 content로 구성되었으며 모두 빈 값은 허용되지 않는다.
이제 question_crud.py 파일에 질문 데이터를 저장하는 create_question 함수를 작성한다.
from datetime import datetime
from domain.question.question_schema import QuestionCreate
...
def get_question(db: Session, question_id: int):
question = db.query(Question).get(question_id)
return question
def create_question(db: Session, question_create: QuestionCreate):
db_question = Question(subject=question_create.subject,
content=question_create.content,
create_date=datetime.now())
db.add(db_question)
db.commit()
그리고 question_router.py 파일에 다음처럼 question_create 함수를 추가한다.
...
from starlette import status
...
@router.post("/create", status_code=status.HTTP_204_NO_CONTENT)
def question_create(_question_create: question_schema.QuestionCreate,
db: Session = Depends(get_db)):
question_crud.create_question(db=db, question_create=_question_create)
answer_create 함수와 마찬가지로 라우터 함수의 응답으로서 'status_code=status.HTTP_204_NO_CONTENT'를 사용함으로써 "응답 없음"을 리턴할 수 있다.
잘 작성했다면 FastAPI의 docs 문서에서 다음처럼 테스트 결과를 확인할 수 있다.

이제 질문을 등록하기 위한 버튼을 질문 목록 화면에 추가하자. Home.svelte 파일의 </table> 태그 아래에 질문 등록 버튼을 생성하기 위한 코드를 추가한다.
...
</table>
<a use:link href="/question-create" class="btn btn-primary">질문 등록하기</a>
</div>
이제 App.svelte 파일에 /question-create 경로에 대응되는 QuestionCreate 컴포넌트를 등록한다.
<script>
import Router from 'svelte-spa-router'
import Home from "./routes/Home.svelte"
import Detail from "./routes/Detail.svelte"
import QuestionCreate from "./routes/QuestionCreate.svelte"
const routes = {
'/': Home,
'/detail/:question_id': Detail,
'/question-create': QuestionCreate
}
</script>
<Router {routes}/>
※ 현재 QuestionCreate.svelte 파일은 존재하지 않는다. 뒤이어 해당 파일을 만들 것이다.
이제 질문 등록 화면을 만든다. '/frontend/src/routes' 디렉토리에 QuestionCreate.svelte 파일을 만들고 다음과 같이 코드를 작성한다.
<script>
import { push } from 'svelte-spa-router'
import fastapi from '../lib/api';
import Error from '../components/Error.svelte';
let error = {detail:[]}
let subject = ""
let content = ""
function post_question(event) {
event.preventDefault()
let url = "/api/question/create"
let params = {
subject: subject,
content: content,
}
fastapi('post', url, params,
(json) => {
push("/")
},
(json_error) => {
error = json_error
}
)
}
</script>
<div class="container">
<h5 class="my-3 border-bottom pb-2">질문 등록</h5>
<Error error={error} />
<form method="post" class="my-3">
<div class="mb-3">
<label for="subject">제목</label>
<input type="text" class="form-control" bind:value="{subject}">
</div>
<div class="mb-3">
<label for="content">내용</label>
<textarea class="form-control" rows="10" bind:value="{content}"></textarea>
</div>
<button class="btn btn-primary" on:click="{post_question}">저장하기</button>
</form>
</div>
우선 질문과 등록을 위해 질문의 '제목'과 '내용'이라는 입력 항목이 필요하므로 각각 subject와 content라는 이름으로 바인딩하고, '저장하기' 버튼을 누르면 post_question 함수가 실행되도록 한다. 앞서 답변 등록 부분과 거의 동일하다.
질문 등록이 성공하면 질문 목록 화면으로 이동해야 한다. 이를 위해 success_callback 함수에서 push 함수를 사용했다. push('/')는 App.svelte 파일에 등록된 라우터 중 '/' 경로에 해당하는 화면으로 이동하라는 의미이다. 따라서 success_callback 함수가 실행되면 '/' 경로에 해당하는 질문 목록 화면으로 이동한다.
이제 질문 등록 기능이 잘 동작하는지 확인해보자. 우선 메인 화면에 접속하면 다음과 같이 '질문 등록하기' 버튼이 나타난 것을 확인할 수 있다.

해당 버튼을 누르면 질문 등록 화면으로 이동한다.

제목과 내용에 값을 입력하고 '저장하기' 버튼을 누르면 질문이 정상적으로 등록되고 다음처럼 질문 목록에 등록한 질문이 표시된다.
