FastAPI를 이용한 웹 서비스 구현 연습_9

Frye 'de Bacon·2023년 10월 30일

본 시리즈는 '박응용' 님의 '점프 투 FastAPI'를 바탕으로 학습 및 실습한 내용을 정리한 것입니다.


구현 및 파인튜닝한 모델을 사용한 웹 서비스 구현을 위해 FastAPI의 학습 필요성을 느껴 학습 과정을 정리합니다. 내용의 정확성이나 이론적인 부분은 당연히 원본 페이지를 참조하시는 게 좋고, 본 시리즈에서는 구현 도중 발생하는 문제 등을 해결하는 과정을 함께 기록하여 '처음부터 끝까지 따라 할 수 있는' 시리즈를 만드는 것을 목표로 합니다(물론 제1목표는 학습 내용 기록입니다).


이제 질문을 등록하는 기능을 만들어 보자.

1. 질문 등록 API 만들기

질문 등록 API 명세

[질문 등록 API 명세]

API명URL요청 방법설명
질문 등록api/question/createpost질문을 등록한다.

[질문 등록 API 입력 항목]

  • subject : 등록할 질문의 제목
  • content : 등록할 질문의 내용

[질문 등록 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로 구성되었으며 모두 빈 값은 허용되지 않는다.

질문 등록 CRUD

이제 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 문서에서 다음처럼 테스트 결과를 확인할 수 있다.


2. 질문 등록 화면 만들기

질문 등록 버튼 만들기

이제 질문을 등록하기 위한 버튼을 질문 목록 화면에 추가하자. 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 함수가 실행되면 '/' 경로에 해당하는 질문 목록 화면으로 이동한다.


3. 질문 등록 기능 확인

이제 질문 등록 기능이 잘 동작하는지 확인해보자. 우선 메인 화면에 접속하면 다음과 같이 '질문 등록하기' 버튼이 나타난 것을 확인할 수 있다.

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

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

profile
AI, NLP, Data analysis로 나아가고자 하는 개발자 지망생

0개의 댓글