FastAPI 개발일지/개발하기

FastAPI 개발일지10 - 중간 점검

hccode0419 2024. 10. 10. 16:13

지금까지 fastapi의 기본적인 get, post, put, delete와 APIRouter까지 배웠다.

 

지금까지 작성한 코드로 한 번 웹사이트와 연결하여 작동이 잘 되는지 확인해 보자. 

 

1. main.py

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from item.item_router import router as item_router

app = FastAPI()

origins = ["*"]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(item_router, tags=["item"])

if __name__ == "__main__":
    import uvicorn

    uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)

 

프론트와 연결을 할 땐 CORS 에러가 뜬다. 이를 방지하기 위해 middleware를 선언해 줬다.

 

 

2. Front 생성

front는 중간점검을 위해 기능이 작동하는 것만 보면 되니, css로 많이 꾸미지 않았다.

 

먼저 디렉토리를 다음과 같이 생성한다.

 

└─project-fastapi
    │  database.py
    │  main.py
    │  models.py
    │  README.md
    │
    ├─front
    │      app.js
    │      create-item.html
    │      create-item.js
    │      styles.css
    │      test.html
    |
    ├─history
    │      history_crud.py
    │      history_router.py
    │      history_schema.py
    │
    ├─item
    │      item_crud.py
    │      item_router.py
    │      item_schema.py
    │
    └─user
           user_crud.py
           user_router.py
           user_schema.py

 

지금 볼 front는 나중에 전부 바뀔 예정이니 크게 신경 쓰지 않아도 된다. 

 

2-1) test.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>UNToc Shop</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <header>
        <div class="top-bar">
            <div class="logo">UNTOC SHOP</div>
            <div class="search-bar">
                <input type="text" placeholder="검색어를 입력하세요">
                <button>🔍</button>
            </div>
            <div class="user-menu">
                <a href="#">로그인</a>
                <a href="#">회원가입</a>
            </div>
        </div>
    </header>

    <nav class="navigation">
        <button>☰</button>
        <div class="cart-account">
            <a href="#">장바구니</a>
            <a href="#">내 정보</a>
            <a href="#">고객센터</a>
            <a href="create-item.html">상품등록</a> 
        </div>
    </nav>
    
    <main>
        <div class="ad-banner">광고 배너</div>

        <section class="product-list" id="product-list">
            <!-- 상품 아이템이 여기에 동적으로 추가됩니다. -->
        </section>
    </main>

    <script src="app.js"></script>
</body>
</html>

 

UNOTC Shop의 메인페이지이다. 

 

2-2) styles.css

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: Arial, sans-serif;
}

.top-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 20px;
    background-color: #f5f5f5;
}

.logo {
    font-size: 24px;
    font-weight: bold;
}

.search-bar {
    display: flex;
    align-items: center;
}

.search-bar input {
    padding: 5px;
    font-size: 16px;
}

.search-bar button {
    margin-left: 5px;
    padding: 5px 10px;
    background-color: #ddd;
    border: none;
    cursor: pointer;
}

.user-menu a {
    margin-left: 20px;
    text-decoration: none;
    color: #333;
}

.navigation {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 20px;
    background-color: #eee;
}

.ad-banner {
    margin: 20px 0;
    padding: 50px;
    background-color: #ddd;
    text-align: center;
    font-size: 24px;
}

.product-list {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 20px;
    padding: 20px;
}

.product-item {
    text-align: center;
}

.product-image {
    width: 100%;
    height: 200px;
    background-color: #f0f0f0;
    margin-bottom: 10px;
}

.product-name, .product-price {
    margin-bottom: 5px;
}

 

2-3) app.js

// FastAPI로부터 상품 목록을 가져와서 HTML에 추가하는 함수
async function fetchItems(skip = 0, limit = 10) {
    try {
        const response = await fetch(`http://127.0.0.1:8000/item/get_items`, {
            method: 'GET'  // 명시적으로 GET 메서드를 사용
        });
        const items = await response.json();

        // 상품 리스트가 들어갈 요소
        const productList = document.getElementById('product-list');

        // 받은 데이터를 기반으로 상품 요소 생성
        items.forEach(item => {
            console.log(item);
            const productItem = document.createElement('div');
            productItem.classList.add('product-item');

            const productImage = document.createElement('div');
            productImage.classList.add('product-image');
            productImage.style.backgroundColor = '#f0f0f0'; // 기본 이미지 배경
            if (item.image) {
                productImage.style.backgroundImage = `url(${item.image})`;
            }

            const productName = document.createElement('div');
            productName.classList.add('product-name');
            productName.textContent = item.item_name;

            const productPrice = document.createElement('div');
            productPrice.classList.add('product-price');
            productPrice.textContent = `${item.item_price}원`;

            // 생성한 요소들을 productItem에 추가
            productItem.appendChild(productImage);
            productItem.appendChild(productName);
            productItem.appendChild(productPrice);

            // productList에 추가
            productList.appendChild(productItem);
        });
    } catch (error) {
        console.error('Failed to fetch items:', error);
    }
}

// 페이지가 로드되면 상품 목록을 가져옴
document.addEventListener('DOMContentLoaded', () => {
    fetchItems();
});

 

메인 페이지에서 상품의 모든 정보를 불러오는 코드이다. 

"http://127.0.0.1:8000/item/get_items" 주소로 접근하여 모든 아이템을 불러온다.

 

get_items는 우리가 모든 아이템을 return 하였기 때문에 response에 그 정보가 들어간다. 

 

2-4) create-item.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>상품 등록</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <header>
        <h1>상품 등록</h1>
    </header>

    <main>
        <form id="item-form">
            <label for="item_id">상품 ID:</label>
            <input type="number" id="item_id" name="item_id" required><br><br>

            <label for="item_name">상품 이름:</label>
            <input type="text" id="item_name" name="item_name" required><br><br>

            <label for="item_price">상품 가격:</label>
            <input type="number" id="item_price" name="item_price" required><br><br>

            <button type="submit">상품 등록</button>
        </form>

        <div id="result"></div>
    </main>

    <script src="create-item.js"></script>
</body>
</html>

 

item 생성 페이지다.

 

2-5) create-item.js

document.getElementById('item-form').addEventListener('submit', async function (event) {
    event.preventDefault(); // 폼이 제출될 때 페이지가 새로고침되지 않도록 막음

    // 입력된 폼 데이터 가져오기
    const itemId = document.getElementById('item_id').value;
    const itemName = document.getElementById('item_name').value;
    const itemPrice = document.getElementById('item_price').value;

    // FastAPI로 POST 요청 보내기
    try {
        const response = await fetch(`http://127.0.0.1:8000/item/create_item`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            }         
            ,
            body: JSON.stringify({
                item_id: parseInt(itemId),
                item_name: itemName,
                item_price: parseInt(itemPrice),
            }),
        });

        const result = await response.json();
        
        // 결과를 화면에 출력
        if (response.ok) {
            document.getElementById('result').textContent = '상품이 성공적으로 등록되었습니다.';
        } else {
            document.getElementById('result').textContent = `에러: ${result["error message"]}`;
        }
    } catch (error) {
        console.error('Error:', error);
        document.getElementById('result').textContent = '상품 등록 중 오류가 발생했습니다.';
    }
});

 

item 생성을 위해 백엔드에서 API를 받아오는 코드이다.

 

FastAPI에서 작성한 주소를 불러오고 body를 통해 작성한 request body형식을 그대로 들고 온다.


 

front의 코드는 이렇게 구성되어 있다.

코드가 너무 길어지니 보기 힘들어서... 다음부터 코드가 길어지면 깃허브에 저장에서 보여줘야겠다...

 

3. 예시화면

작성한 코드를 실행시키기 위해 먼저 uvicorn main:app --reload를 실행시킨 후 front를 실행시킨다.

 

3-1) 메인페이지

 

app.js에서 모든 아이템을 불러오는 "item/get_items"로 접근했기 때문에 모든 아이템이 보이는 것을 확인할 수 있다. 

 

3-2) 상품 등록 페이지

메인페이지의 우측 상단의 "상품등록"을 클릭하면 상품 등록 페이지로 이동할 수 있다. 

 

상품 등록페이지에서는 create-item.js에서 작성한 url을 바탕으로 상품 id, 이름, 가격을 작성한 후 상품 등록버튼을 클릭하면 등록된다.

 

이후 메인페이지로 가서 확인해 보면 생성이 돼있을 것이다.

 

 

 

 

지금까지 작성한 코드로 상품을 등록하고 조회할 수 있는 웹 사이트를 개발하였다. 지금까지 잘 따라왔으면 뒤에도 문제없이 할 수 있을 것이다!

 

다음 포스트부터는 database를 연결하여 상품을 DB에서 관리해 보겠다. 

 

 

자세한 코드는 GitHub에서 확인해주세요!

https://github.com/hccode0419/project-fastapi/tree/중간점검