AWS

https://sso-y.tistory.com/137

 

[개발-배포-자동화] 1. 개발 : FastAPI + Svelte + MySQL 환경구성

FastAPI + svelte + MySQL을 사용해 간단한 메모장 애플리케이션을 만들고 jenkins를 사용해 AWS에 자동배포(CICD) 하려함 컨테이너 구성 python:3.8.16-slim-buster : FastApi node:16 : svelte mysql : mysql # memoApp/docker-compo

sso-y.tistory.com

지난 글에서 개발 환경 구성에 이어 본격적인 개발을 시작한다.

 

우선 FastAPI와 DB를 연동하여 메모 CRUD를 개발하고

이후 Svelte와 연동할 것이다.

 

최종 결과

 

폴더 구조

 


 

FastAPI ⇄ MySQL

아래는 전부 FastAPI를 위해 띄운 python 컨테이너에서 실행을 전제로 한다.

 

DB 연결부터 시작하자.

# app/Backend/config/config.py

# Database configuration
DB_CONFIG = {
    'host': 'mysql',
    'user': 'root',
    'password': '비밀번호',
    'database': 'memo_db',
}

여기서 host를 'mysql'로 쓸 수 있는 이유는 docker-compose.yml에서 service를

'FastAPiApp', 'svelteApp', 'mysql'로 정의했기 때문이다.

 

# app/Backend/database.py

import pymysql.cursors
from config.config import DB_CONFIG

connection = pymysql.connect(
    host=DB_CONFIG['host'],
    user=DB_CONFIG['user'],
    password=DB_CONFIG['password'],
    db=DB_CONFIG['database'],
    cursorclass=pymysql.cursors.DictCursor
)

FastAPI로 돌아가 api를 만들어보자.

# app/Backend/app.py

from fastapi import FastAPI, HTTPException
from database import create_memo, read_memo, update_memo, delete_memo
import os

app = FastAPI()


# 메모 생성
@app.post("/memos/")
async def create_memo_api(new_memo: dict):
    content = new_memo.get('content')
    create_memo(content)
    return {"message": "Memo created successfully"}

# 메모 조회
@app.get("/memos/all")
async def read_memo_api():
    return read_memo()

# 메모 수정
@app.put("/memos/{memo_id}")
async def update_memo_api(modify_memo: dict):
    id = modify_memo.get('id')
    content = modify_memo.get('content')
    update_memo(id, content)
    return {"message": "Memo updated successfully"}

# 메모 삭제
@app.delete("/memos/{memo_id}")
async def delete_memo_api(memo_id: int):
    delete_memo(memo_id)
    return {"message": "Memo deleted successfully"}


if __name__ == "__main__":
    os.system('uvicorn app:app --host 0.0.0.0 --port 8000 --reload')

코드 가독성을 위해 DB를 다루는 부분은 앞선 database.py에 추가할 것이다.

간단하게 api만 정의해두고 다시 DB로 넘어가자.

 

# app/Backend/database.py

import pymysql.cursors
from fastapi import HTTPException
from config.config import DB_CONFIG

connection = pymysql.connect(
    host=DB_CONFIG['host'],
    user=DB_CONFIG['user'],
    password=DB_CONFIG['password'],
    db=DB_CONFIG['database'],
    cursorclass=pymysql.cursors.DictCursor
)

# 메모 생성
def create_memo(content: str):
    with connection.cursor() as cursor:
        sql = "INSERT INTO memos (content) VALUES (%s)"
        cursor.execute(sql, (content))
        connection.commit()

# 메모 조회
def read_memo():
    with connection.cursor() as cursor:
        sql = "SELECT * FROM memos"
        cursor.execute(sql)
        result = cursor.fetchall()

        if result is None:
            raise HTTPException(status_code=404, detail="Memo not found")

        return result

# 메모 수정
def update_memo(memo_id: int, content: str):
    with connection.cursor() as cursor:
        sql = "UPDATE memos SET content = %s WHERE id = %s"
        cursor.execute(sql, (content, memo_id))
        connection.commit()

        if cursor.rowcount == 0:
            raise HTTPException(status_code=404, detail="Memo not found")

# 메모 삭제
def delete_memo(memo_id: int):
    with connection.cursor() as cursor:
        sql = "DELETE FROM memos WHERE id = %s"
        cursor.execute(sql, (memo_id))
        connection.commit()

        if cursor.rowcount == 0:
            raise HTTPException(status_code=404, detail="Memo not found")

 

끝이다!

'http://localhost:9000/docs'로 접속해 API를 테스트할 수 있다.

FastAPI ⇄ Svelte 

우선 FastAPI와 Svelte의 통신을 위해 CORS를 추가하자.

# app/Backend/app.py

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:9173"],  # Svelte 앱의 주소
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

 

이제 Frontend로 넘어가자.

Svelte를 위해 띄운 nodejs 컨테이너 내에서 실행한다.

 

'App.svelte'와 'Memo.svelte'로 구분했다.

'App.svelte'에서는 메모를 조회하고 추가할 수 있으며

'Memo.svelte'에서는 메모의 수정과 삭제를 할 수 있도록 했다.

 

svelte에 익숙하지 않아 검색을 많이 했지만 코드는 그렇게 어렵지 않다.

처음이라면 getMemo(조회), createMemo(생성), deleteMemo(삭제), updateMemo(수정) 순으로 봐보자.

 

# app/Frontend/memo/src/App.svelte

<script>
  import Memo from './Memo.svelte'

  let memoText = '';
  let memos = [];

  async function getMemo(){
    const response = await fetch('http://localhost:9000/memos/all');
    const data = await response.json();
    memos = data;
  }

  async function createMemo() {
    const response = await fetch('http://localhost:9000/memos/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ content: memoText })
    });

    if (response.status === 200) {
      memoText = ""; // 입력 필드 초기화
      getMemo(); // 메모 목록 갱신
    }
  }

  getMemo()
</script>

<div>
  <h1> Memo List </h1>
  <form on:submit|preventDefault={createMemo}>
    <input size="40" placeholder="메모를 입력하세요" bind:value={memoText}/>
    <button disabled={!memoText}>Add</button>
  </form>
  <ul>
    {#each memos as memo}
        <Memo memo={memo} on:delete={getMemo}/>
    {/each}
  </ul>
</div>

<style>
  button {
    margin-left: 1rem;
  }
	
	h1 {
    margin-top: 0;
	}
	
  ul {
    list-style: none; /* removes bullets */
    margin-left: 0;
    padding-left: 0;
  }
</style>
# app/Frontend/memo/src/Memo.svelte

<script>
    import { createEventDispatcher } from 'svelte';
    import { fade } from 'svelte/transition';

    const dispatch = createEventDispatcher();
    export let memo;

    let isEditing = false;
    let editText = memo.content;
    let originalText = memo.content;

    function enableEdit() {
        isEditing = true;
        editText = memo.content;
        originalText = memo.content; // 수정하기 전의 내용 보관
    }

    function saveEdit() {
        isEditing = false;
        if (editText !== originalText) {
            updateMemo(); // 수정 내용이 변경된 경우에만 업데이트
        }
    }

    async function updateMemo() {
        const response = await fetch(`http://localhost:9000/memos/${memo.id}`, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ id: memo.id, content: editText }),
        });

        if (response.status === 200) {
            memo.content = editText; // 메모 내용 업데이트
            dispatch('update'); // 수정 완료 이벤트 디스패치
        }
    }

    async function deleteMemo() {
        const response = await fetch(`http://localhost:9000/memos/${memo.id}`, {
            method: 'DELETE',
        });

        if (response.status === 200) {
            dispatch('delete');
        }
    }
</script>

<li transition:fade>
    {#if isEditing}
        <input bind:value={editText} on:keydown={e => e.key === 'Enter' && saveEdit()} />
        <button on:click={saveEdit}>Modify</button>
    {:else}
        <span on:dblclick={enableEdit}>{memo.content}</span>
        <button on:click={deleteMemo}>Delete</button>
    {/if}
</li>

<style>
    li {
        margin-top: 0.8rem;
    }
</style>

 

/app/Frontend/memo/npm run dev로 서버를 실행한 후 

"localhost:9173"으로 접속해 테스트할 수 있다.

(당연히 FastAPI도 실행되어 있어야 한다.)

 


결과

1. 첫 화면

 

2. 저장 화면

 

3. 수정 화면

 

4. 삭제 화면

 

 

 

 

개발 환경 구성과 간단한 Memo App 개발을 끝냈다.

다음 글은 배포다.

FastAPI + svelte + MySQL을 사용해 간단한 메모장 애플리케이션을 만들고

jenkins를 사용해 AWS에 자동배포(CICD) 하려함

 

컨테이너 구성

python:3.8.16-slim-buster : FastApi 

node:16 : svelte

mysql : mysql

# memoApp/docker-compose.yml

version: "3"
services:
  FastApiApp:
    image: python:3.8.16-slim-buster
    ports:
      - "9000:8000"
    volumes:
      - ./Backend:/app/Backend
    stdin_open: true
    tty: true
    depends_on:
      - mysql
  svelteApp:
    image: node:16
    ports:
      - "9173:5173"
    volumes:
      - ./Frontend:/app/Frontend
    stdin_open: true
    tty: true
    depends_on:
      - mysql
  mysql:
    image: mysql:latest
    environment:
      MYSQL_ROOT_PASSWORD: 비밀번호
      MYSQL_DATABASE: memo_db
    ports:
      - "9306:3306"

 

Frontend - Svelte

개발환경 구성

 

* Node.js *

프론트엔드 서버가 필요한 이유는

Svelte로 작성한 코드를 브라우저가 인식할 수 있는 형태로 변환하고 실시간으로 테스트하기 위함

이 역할을 Node.js가 함

 

Node.js는 JavaScript 런타임 환경이다.

프론트엔드 서버 역할을 하며 Svelte코드를 HTML, CSS, JavaScript로 변환하고

이를 브라우저에서 실행할 수 있게 한다.

또한 개발 중에 수정 사항을 즉시 확인할 수 있는 개발 서버 역할도 한다.

 

* Svelte *

Svelte는 프론트엔드 웹 애플리케이션을 만들기 위한 JavaScrit 프레임워크 또는 라이브러리이다.

Svelte 코드는 컴파일되어 HTML, CSS, JavaScript로 변환된다.

 

* Vite *

Vite는 개발 환경을 향상시키기 위한 툴이다.

개발 중에 코드 변환 및 개발 서버 역할을 수행한다.

주로 개발 중에 사용되며, 수정사항을 실시간으로 반영하고,

개발 서버를 제공하며, 빠른 빌드를 지원한다.

프론트엔드 개발을 편하게 만들어주는 역할이다.

 

운영 환경에서는 Svelte 코드를 빌드하여 순수 HTML, CSS, JavaScript로 변환하고

이를 서버(ex_ Node.js) 또는 웹 호스팅 서비스를 통해 호스팅하는 것이 일반적이다.

 

? Vite를 사용하지 않아도 되지 않을까 ?

그렇다.

Vite를 사용하지 않는 경우에는 Node.js 개발 서버가 Svelte 코드 변환 및 개발 서버 역할을 수행할 것이다.

 

하지만 Vite를 사용하지 않는다면, 직접 Wepback 또는 Rollup과 같은 번들링 도구와 개발 서버를 설정해야 한다.

이러한 초기 설정은 Vite가 간단화하고 최적화하는 데 도와주기 때문에 사용한다.

 

# svelteApp 컨테이너 내부

cd /app/Frontend
npm create vite@latest memo -- --template svelte
cd memo
npm install

# 서버 실행
npm run dev

 

여기서 바로 localhost:9173을하면 접속할 수 없다.

# package.json

"scripts": {
    "dev": "vite --host 0.0.0.0 --port 5173",
    "build": "vite build",
    "preview": "vite preview"
},

 script > dev 설정을 위와 같이 바꿔줘야 한다.

'localhost'는 도커 컨테이너 내부에서만 서비스를 사용할 수 있고, 외부에서 접근할 수 없다.

 

따라서 도커 컨테이너에서 '0.0.0.0'로 서비스를 노출하면 호스트 머신 외부에서도 접근할 수 있다.

 

"http://localhost:9173" 접속 (9173 -> 5173)


Backend - FastAPI

from fastapi import FastAPI
import os

app = FastAPI()

if __name__ == "__main__":
    os.system('uvicorn app:app --host 0.0.0.0 --port 8000 --reload')

 

"http://localhost:9000" 접속 (9000 -> 8000)


DataBase - MySQL

container 접속 후

mysql -u root -p > 비밀번호 입력 > show databases; (memo_db 생성되어 있음)

 

USE memo_db; >

CREATE TABLE memos (id INT AUTO_INCREMENT PRIMARY KEY, content TEXT); >

show tables;

테이블 조회시 한글깨짐은 /etc/my.cnf 수정 필요

 

 

 

개발 환경 구성 끝!

 

다음 글에서는 FastAPI ⇄ Svelte와 FastAPI ⇄ MySQL 통신과 

Memo App 개발에 대해서 다루자.

'AWS' 카테고리의 다른 글

[개발-배포-자동화] 1. 개발 : Memo App (FastApi, MySQL, Svelte)  (0) 2023.11.02