본문 바로가기
Python/FastAPI

FastAPI & Svelte - 질문 목록 화면

by 깐테 2024. 1. 27.

 

https://wikidocs.net/176225

 

2-05-1 질문 목록 화면 구현하기

* `[완성 소스]` : [https://github.com/pahkey/fastapi-book/tree/v2.05.1](https://github.com/pahkey/fasta…

wikidocs.net

 

* 해당 프로젝트는 위키독스의 모든 내용을 포함하고 있지 않습니다.

이전 또는 자세한 프로젝트 내용은 위키독스를 참조해주시기 바랍니다.


FastAPI & Svelte - 질문 목록 화면

질문 목록 화면 구현하기

// proj/myapi/frontend/src/App.svelte
<script>
  let question_list = []

  function  get_question_list() {
    fetch("http://localhost:8000/api/question/list").then((response) => {
      response.json().then((json) =>{
        question_list = json
      })
    })
}

  get_question_list()
</script>

<ul>
  {#each question_list as question}
    <li>{question.subject}</li>
  {/each}
</ul>
  • get_question_list() : 질문 목록 API를 호출하는 함수.
    호출하여 얻은 question_list()는 Svelte의 each문을 순회하여 제목을 표시.
  • question_list를 빈 값으로 초기값을 주지 않을 경우 fetch가 비동기로 동작되기 때문에 HTML의 each 문이 실행되고 question_list의 값을 찾지 못해 오류가 발생한다.
    따라서 반복문과 함께 사용할 경우 빈 리스트로 초기화하거나 해당 값이 있는지 체크하는 로직을 each문 앞에 사용한다.
  • question_list를 State 변수로 지정하지 않아도 question_list 값이 변경되는 순간 그 값이 화면에 실시간으로 반영된다. 이는 Svelte의 특징 중 하나인 Truly reactive

 

Svelte 필수 문법

분기문, 반복문에서 태그는 반드시 닫아주어야 한다.

 

1. 분기문

{#if 조건문1}
    <p>조건문1에 해당하면 실행</p>
{:else if 조건문2}
    <p>조건문2에 해당하면 실행</p>
{:else}
    <p>조건문1, 2 모두 해당하지 않으면 실행</p>
{/if}

 

2. 반복문

{#each list as item, index}
    <p>순서: {index} </p>
    <p>{item}</p>
{/each}

 

3. 객체 표시

중괄호를 좌우로 감싸서 표시할 수 있다.

{객체}

 

객체의 속성은 . 으로 이어서 출력할 수 있다.

{객체.속성}

 

질문 목록 확인하기

 

vite + svelte 서버 실행: npm run dev (powershell 터미널 기준. proj\myapi\frontend)

FastAPI 서버 실행: uvicorn main:app --reload (prompt(myapi) 터미널 기준. proj\myapi)

브라우저 접속: http://localhost:5173 또는 http://127.0.0.1:5173

질문 목록의 제목이 출력.

 

Svelte 라우터

필요 화면

  • 질문 목록 - 질문의 목록을 표시하는 화면
  • 질문 상세 - 질문의 상세 내용을 확인하고 답변을 작성하는 화면
  • 질문 작성 - 질문을 작성하는 화면
  • 질문 수정 - 질문을 수정하는 화면
  • 답변 수정 - 답변을 수정하는 화면
  • 회원 가입 - 회원 가입을 위한 화면
  • 로그인 - 로그인을 위한 화면

Svlete는 SPA이기 때문에 하나의 페이지에서만 내용을 달리 해서 표시한다.

SPA(Single Page Application)란 웹 사이트의 전체 페이지를 하나의 페이지에 담아 동적으로 화면을 바꿔가며 표현하는 것을 말한다.

 

svelte-spa-router 설치

npm install svelte-spa-router

 

라우터 적용

라우터를 적용하려면 필요 화면 각각에 대한 URL 주소 네이밍이 필요하다.

URL 파일명 화면명
/ Home.svelte 질문 목록
/detail/:question_id Detail.svelte 질문 상세
/question-create QuestionCreate.svelte 질문 작성
/question-modify/:question_id QuestionModify.svlete 질문 수정
/user-login UserLogin.svelte 로그인
/user-create UserCreate.svelte 회원 가입
/answer-modify/:answer_id AnswerModify.svelte 답변 수정

 

routes 디렉터리 생성

 

위치: proj/myapi/frontend/src/routes

 

Home.svelte

// proj/myapi/frontend/src/routes/Home.svelte
<script>
  let question_list = []

  function  get_question_list() {
    fetch("http://localhost:8000/api/question/list").then((response) => {
      response.json().then((json) =>{
        question_list = json
      })
    })
  }

  get_question_list()
</script>

<ul>
  {#each question_list as question}
    <li>{question.subject}</li>
  {/each}
</ul>

 

App.svelte

<script>
  import Router from 'svelte-spa-router'
  import Home from './routes/Home.svelte'

  const routes = {
    '/': Home,
  }
</script>

<Router {routes}></Router>

route 주소에 매핑되는 컴포넌트로 Home 등록.

<Home /> 은 새로 작성한 Home.svelte 파일의 내용을 의미.

이후 추가되는 URL 규칙들도 routes 변수에 추가.

 

동작 확인

 

API 호출 라이브러리

질문 목록처럼 데이터를 조회하기 위해서는 항상 백엔드 서버에 요청하여 데이터를 가져와야 한다.

앞으로 만들 대부분의 기능들도 데이터 처리를 위해서는 아래처럼 fetch 함수를 사용해야 한다.

데이터를 요청하는 함수를 공통 라이브러리로 만들어 사용하면 편리.

fetch("http://localhost:8000/api/question/list").then((response) => {
    response.json().then((json) => {
        question_list = json
    })
})

 

fastapi 라이브러리

proj/myapi/frontend/src/lib/api.js

// proj/myapi/frontend/src/lib/api.js
const fastapi = (operation, url, params, success_callback, failure_callback) => {
    let method = operation
    let content_type = 'application/json'
    let body = JSON.stringify(params)

    let _url = 'http://127.0.0.1:8000'+url
    if(method === 'get') {
        _url += "?" + new URLSearchParams(params)
    }

    let options = {
        method: method,
        headers: {
            "Content-Type": content_type
        }
    }

    if (method !== 'get') {
        options['body'] = body
    }

    fetch(_url, options)
        .then(response => {
            response.json()
                .then(json => {
                    if(response.status >= 200 && response.status < 300) {  // 200 ~ 299
                        if(success_callback) {
                            success_callback(json)
                        }
                    }else {
                        if (failure_callback) {
                            failure_callback(json)
                        }else {
                            alert(JSON.stringify(json))
                        }
                    }
                })
                .catch(error => {
                    alert(JSON.stringify(error))
                })
        })
}

export default fastapi
매개변수 설명
operation 데이터를 처리하는 방법, 소문자만 사용 get, post, put, delete
url 요청 URL. 단 백엔드 서버의 호스트명 이후의 URL만 전달 /api/question/list
params 요청 데이터 {page: 1, kw: “마크다운”}
success_callback API 호출 성공시 수행할 함수. 전달된 함수에는 API 호출 시 리턴되는 json이 입력으로 주어진다.  
failure_callback API 호출 실패시 수행할 함수. 전달된 함수에는 오류 값이 입력으로 주어진다.  
  • operation이 get 인 경우 파라미터를 GET 방식에 맞게끔 URLSearchParams를 사용하여 파라미터 조립. get이 아닌 경우에만 options['body'] 항목에 전달 받은 파라미터 값을 설정하게 함.
  • body 항목에 값을 설정할 때는 JSON.stringify(params) 처럼 params를 JSON 문자열로 변환해야 한다.
  • API 호출 주소는 호스트명에 전달받은 url 값을 더하여 만든다. fastapi 함수를 사용할 때는 호스트명을 생략하고 그 뒷 부분만 전달한다.
  • HTTP 프로토콜 응답 코드가 200~299 까지인 경우 매개변수로 전달받은 success_callback을 실행 success_callback 함수에는 호출한 API의 리턴값을 입력으로 전달하여 호출한다.

 

호스트명 환경 파일에서 불러오기

현재 하드코딩된 호스트명을 변경하여 환경 파일에 저장하고 그 값을 불러와 사용할 수 있도록 수정한다.

proj/myapi/frontend

 

VITE_SERVER_URL=http://localhost:8000
  • Svelte 파일에서 .env 파일의 항목을 읽기 위해서는 반드시 VITE_ 로 시작하도록 환경변수명을 등록해야 한다.

 

api.js

// proj/myapi/frontend/src/lib/api.js
const fastapi = (operation, url, params, success_callback, failure_callback) => {
    let method = operation
    let content_type = 'application/json'
    let body = JSON.stringify(params)

    let _url = import.meta.env.VITE_SERVER_URL+url
    if(method === 'get') {
        _url += "?" + new URLSearchParams(params)
    }
...
  • .env 파일에 등록한 VITE_SERVER_URL 변수는 import.meta.env.VITE_SERVER_URL 처럼 불러올 수 있다.

 

fastapi 함수 사용하기

//proj/myapi/frontend/src/routes/Home.svelte
<script>
    import fastapi from "../lib/api"
    let question_list = []

    function get_question_list() {
        fastapi('get', '/api/question/list', {}, (json) => {
            question_list = json
        })
    }

    get_question_list()
</script>

<ul>
    {#each question_list as question}
        <li>{question.subject}</li>
    {/each}
</ul>
  • fastapi를 사용할 수 있도록 import
  • 질문 목록 API는 GET 방식이므로 operation 항목에 get을 전달. 추가로 전달할 파라미터 값은 아직 없기 때문에 params 항목은 빈 값({}) 전달.
  • success_callback 함수는 다음과 같이 화살표 함수로 전달
    화살표 함수의 내용은 응답으로 받은 json 데이터를 question_list에 대입.
(json) => {
    question_list = json
}
  • failure 함수를 전달하지 않더라도 fastapi 함수는 오류 발생시 오류의 내용을 alert로 표시하게 되어 있다.

 

오류 예시

반응형