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

Frye 'de Bacon·2023년 10월 30일
post-thumbnail

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


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


이제 질문의 조회와 답변 등록까지 가능하게 되었지만, 아무래도 화면 모양새가 조금 아쉽다. 이번에는 스타일 시트를 이용해 화면에 디자인을 적용하는 방법을 알아보자.

1. 스타일 시트를 이용한 화면 꾸미기

Detail.svelte 파일에 다음 내용을 추가한다.

...
</form>
<style>
    textarea {
        width: 100%;
    }
    input[type=submit] {
        margin-top: 10px;
    }
</style>

Detail.svelte 파일 가장 아래에 <style> 블록을 추가하고 여기에 CSS 스타일을 적용하였다. 먼저 답변 내용을 입력하는 텍스트 창(textarea)의 너비를 100%로 넓히고, '답변 등록' 버튼의 위쪽으로 마진을 10px 추가하였다. 이때 텍스트 창의 너비에서 100%는 웹 브라우저의 너비를 기준으로 한다.
이제 화면을 확인해보면 다음과 같이 나타난다.


2. 부트스트랩을 이용한 화면 꾸미기

부트스트랩 설치

부트스트랩은 간단하게 화면을 디자인할 수 있도록 도와주는 오픈소스 프로젝트이다. 우선 프론트엔드 폴더에서 터미널을 이용해 부트스트랩을 설치한다.

(practice_1) C:\workspace\fastapi_practice\practice_1\frontend> npm install bootstrap

설치가 정상적으로 진행되면 다음과 같은 메시지가 나타나고, '/practice_1/frontend/node_modules/bootstrap' 디렉토리에 부트스트랩이 설치된 것을 확인할 수 있다.

added 2 packages, and audited 41 packages in 9s

6 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

부트스트랩 적용

설치한 부트스트랩을 화면 전체에 적용해 보자. 우선 frontend/scr 디렉토리의 main.js 파일을 수정한다. 이 파일은 Svelte의 index.html이 참조하는 자바스크립트 파일이다.

import './app.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min.js'
import App from './App.svelte'

const app = new App({
  target: document.getElementById('app'),
})

export default app

node_modules 디렉토리의 파일들은 위와 같이 상대경로를 이용해 import할 수 있다.

질문 목록 화면에 부트스트랩 적용하기

이제 질문 목록 화면에 부트스트랩을 적용해보자. Home.svelte 파일을 다음과 같이 수정한다.

<script>
    import fastapi from "../lib/api"
    import { link } from 'svelte-spa-router'

    let question_list = []
  
    function get_question_list() {
      fastapi('get', '/api/question/list', {}, (json) => {
        question_list = json
      })
    }
  
    get_question_list()
</script>

<div class="container my-3">
  <table class="table">
    <thead>
      <tr class="table-dark">
        <th>번호</th>
        <th>제목</th>
        <th>작성 일시</th>
      </tr>
    </thead>
    <tbody>
      {#each question_list as question, i}
      <tr>
        <td>{i+1}</td>
        <td>
          <a use:link href="/detail/{question.id}">{question.subject}</a>
        </td>
        <td>{question.create_date}</td>
      </tr>
      {/each}
    </tbody>
  </table>
</div>

기존 ul로 표시되었던 질문 목록을 table 엘리먼트로 수정했다. table 태그 및 하위 태그에 대해 부트스트랩을 적용했으며, 여기에 사용된 class="container my-3", class=:"table", class="table-dark" 등이 바로 부트스트랩에서 제공하는 클래스이다.
부트스트랩에 대한 더 많은 정보는 공식 문서에서 확인할 수 있다.

이렇게 수정한 뒤 다시 스벨트 서버를 구동하면 질문 목록 화면에 부트스트랩이 적용된 것을 확인할 수 있다.

질문 상세 화면에 부트스트랩 적용하기

Detail.svelte 파일을 다음과 같이 수정해 질문 상세 화면에도 부트스트랩을 적용해 보자.

...
</script>

<div class="container my-3">
    <!-- 질문 -->
    <h2 class="border-botom py-2">{question.subject}</h2>
    <div class="card my-3">
        <div class="card-body">
            <div class="card-text" style="white-space: pre-line;">{question.content}</div>
            <div class="d-flex justify-content-end">
                <div class="badge bglight text-dark p-2">
                    {question.create_date}
                </div>
            </div>
        </div>
    </div>
    <!-- 답변 목록 -->
    <h5 class="border-bottom my-3 py-2">{question.answers.length}개의 답변이 있습니다.</h5>
    {#each question.answers as answer}
    <div class="card my-3">
        <div class="card-body">
            <div class="card-text" style="white-space: pre-line">{answer.content}</div>
            <div class="d-flex justify-content-end">
                <div class="badge bglight text-dark p-2">
                    {answer.create_date}
                </div>
            </div>
        </div>
    </div>
    {/each}
    <!-- 답변 등록 -->
    <Error error={error} />
    <form method="post" class="my-3">
        <div class="mb=3">
            <textarea rows="10" bind:value={content} class="form-control" />
        </div>
        <input type="submit" value="답변 등록" class="btn btn-primary" on:click="{post_answer}" />
    </form>
</div>

<!-- 이하 내용 삭제 
<h1>{question.subject}</h1>
<div>
    {question.content}
</div>
<ul>
    {#each question.answers as answer}
        <li>{answer.content}</li>
    {/each}
</ul>
<Error error={error} />
<form method="post">
    <textarea rows="15" bind:value={content}></textarea>
    <input type="submit" value="답변 등록" on:click="{post_answer}">
</form>

<style>
    textarea {
        width: 100%;
    }
    input[type=submit] {
        margin-top: 10px;
    }
</style> -->

질문이나 답변은 하나의 뭉치에 해당하므로 부트스트랩의 card 컴포넌트를 사용했다. 사용한 부트스트랩 클래스는 다음과 같다.

부트스트랩 클래스설명
card, card-body, card-text부트스트랩 Card 컴포넌트
badge부트스트랩 Badge 컴포넌트
form-control, form-label부트스트랩 Form 컴포넌트
border-bottom아래 방향 테두리 선
my-3상하 마진값 3
py-2상하 패딩값 2
p-2상하좌우 패딩값 2
d-flex justify-content-end컴포넌트의 우측 정렬
bg-light연회색 배경
text-dark검은색 글씨
text-start좌측 정렬
btn btn-primary부트스트랩 버튼 컴포넌트

그리고 질문 내용과 답변 내용에는 <style="white-space: pre-line;" 스타일을 지정하여 글 내용의 줄바꿈을 정상적으로 보여줄 수 있도록 하였다.

상기 내용을 모두 성공적으로 적용했다면 다음과 같은 화면을 볼 수 있다.

오류 컴포넌트에 부트스트랩 적용하기

이제 frontend/src/components 디렉토리의 Error.svelte 파일도 다음과 같이 부트스트랩을 적용하자.

<script>
    export let error  // 전달받은 오류
</script>

{#if typeof error.detail === 'string'}
    <div class="alert alert-danger" role="alert">
        <div>
            {error.detail}
        </div>
    </div>
{:else if typeof error.detail === 'object' && error.detail.length > 0}
    <div class="alert alert-danger" role="alert">
        {#each error.detail as err, i}
        <div>
            <strong>{err.loc[1]}</strong> : {err.msg}
        </div>
        {/each}
    </div>
{/if}

부트스트랩의 Alerts 컴포넌트를 활용해 오류가 예쁘게 표시되도록 수정하였다.
이제 질문 상세 화면에서 빈 값으로 답변을 작성하려고 하면 다음처럼 오류가 나타날 것이다.

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

0개의 댓글