21일오블완챌린지_vue.js와fastapi로 만드는 웹 주문 시스템

Day 12: 기본 오류 처리와 유효성 검사 추가

@thiskorea 2024. 11. 18. 09:57

오늘은 클라이언트와 서버에서 오류 처리유효성 검사를 강화하여 사용자 경험을 향상시키고, 잘못된 데이터가 입력되거나 전송되지 않도록 방지하겠습니다. 이를 통해 사용자는 입력 실수를 쉽게 파악하고, 서버에서도 예상치 못한 데이터를 처리하지 않도록 보호할 수 있습니다.


1. 클라이언트 측 유효성 검사

사용자가 데이터를 잘못 입력했을 때, 즉시 오류 메시지를 표시하여 문제를 수정할 수 있도록 합니다.

OrderForm.vue 수정하기

<template>
  <div class="order-form">
    <h2>주문하기</h2>
    <form @submit.prevent="submitOrder">
      <div>
        <label for="name">이름:</label>
        <input type="text" v-model="name" id="name" />
        <span v-if="errors.name" class="error">{{ errors.name }}</span>
      </div>
      <div>
        <label for="item">상품명:</label>
        <input type="text" v-model="item" id="item" />
        <span v-if="errors.item" class="error">{{ errors.item }}</span>
      </div>
      <div>
        <label for="quantity">수량:</label>
        <input type="number" v-model="quantity" id="quantity" min="1" />
        <span v-if="errors.quantity" class="error">{{ errors.quantity }}</span>
      </div>
      <div>
        <label for="notes">주문 메모:</label>
        <textarea v-model="notes" id="notes" placeholder="추가 요청 사항을 입력해 주세요"></textarea>
      </div>
      <button type="submit">주문 제출</button>
    </form>
    <div v-if="orderSubmitted" class="success-message">
      <h3>주문이 성공적으로 접수되었습니다!</h3>
    </div>
  </div>
</template>

<script>
export default {
  name: "OrderForm",
  data() {
    return {
      name: "",
      item: "",
      quantity: 1,
      notes: "",
      orderSubmitted: false,
      errors: {
        name: null,
        item: null,
        quantity: null,
      },
    };
  },
  methods: {
    validateForm() {
      this.errors = { name: null, item: null, quantity: null };
      let isValid = true;

      if (!this.name.trim()) {
        this.errors.name = "이름을 입력해 주세요.";
        isValid = false;
      }

      if (!this.item.trim()) {
        this.errors.item = "상품명을 입력해 주세요.";
        isValid = false;
      }

      if (this.quantity < 1) {
        this.errors.quantity = "수량은 1 이상이어야 합니다.";
        isValid = false;
      }

      return isValid;
    },
    async submitOrder() {
      if (!this.validateForm()) {
        return;
      }

      const orderData = {
        name: this.name,
        item: this.item,
        quantity: this.quantity,
        notes: this.notes,
      };

      try {
        const response = await fetch("http://127.0.0.1:8000/order", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(orderData),
        });

        if (!response.ok) {
          throw new Error("서버 오류가 발생했습니다.");
        }

        const result = await response.json();
        console.log(result);
        this.orderSubmitted = true;
      } catch (error) {
        console.error("주문 전송 중 오류:", error);
        alert("주문 전송 중 문제가 발생했습니다. 다시 시도해 주세요.");
      }
    },
  },
};
</script>

<style scoped>
.order-form {
  max-width: 400px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 5px;
}
.order-form div {
  margin-bottom: 10px;
}
.error {
  color: red;
  font-size: 0.9em;
}
.success-message {
  text-align: center;
  color: green;
}
</style>
  • validateForm 메서드: 사용자가 입력한 데이터를 클라이언트 측에서 확인하고, 유효하지 않은 경우 오류 메시지를 표시합니다.
  • errors 객체: 각 필드에 대한 오류 메시지를 저장하고 화면에 표시합니다.

2. 서버 측 유효성 검사

서버에서도 클라이언트가 잘못된 데이터를 전송했을 때 적절한 오류 메시지를 반환하도록 처리합니다.

main.py 수정하기

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
from typing import List, Optional

app = FastAPI()

# CORS 설정
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5173"],  # Vue.js가 실행 중인 포트
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 주문 데이터 구조 정의
class Order(BaseModel):
    name: str = Field(..., min_length=1, description="이름은 필수입니다.")
    item: str = Field(..., min_length=1, description="상품명은 필수입니다.")
    quantity: int = Field(..., ge=1, description="수량은 1 이상이어야 합니다.")
    notes: Optional[str] = None

# 임시로 주문을 저장할 리스트
orders: List[Order] = []

# 주문 접수 엔드포인트
@app.post("/order")
async def create_order(order: Order):
    if len(order.name.strip()) == 0 or len(order.item.strip()) == 0:
        raise HTTPException(status_code=400, detail="이름과 상품명은 필수입니다.")
    if order.quantity < 1:
        raise HTTPException(status_code=400, detail="수량은 1 이상이어야 합니다.")
    
    orders.append(order)  # 주문 데이터를 리스트에 저장
    return {"message": "Order received", "order": order}

# 모든 주문 조회 엔드포인트
@app.get("/orders")
async def get_orders():
    return {"orders": orders}
  • Field를 통한 유효성 검사: pydantic의 Field를 사용하여 서버에서 유효성을 검사합니다.
  • HTTPException: 클라이언트가 잘못된 데이터를 보낼 경우, 400 상태 코드를 반환하며 적절한 오류 메시지를 제공합니다.

3. 테스트하기

  1. 클라이언트에서 필드가 비어 있는 상태로 제출하거나, 수량이 0 이하로 입력된 경우 오류 메시지가 표시되는지 확인합니다.
  2. 서버 측에서 유효하지 않은 데이터가 전송되었을 때 400 상태 코드와 함께 적절한 오류 메시지가 반환되는지 테스트합니다.

4. 오늘의 마무리

오늘은 클라이언트와 서버 모두에서 오류 처리와 유효성 검사를 강화하여 잘못된 데이터 입력 및 전송을 방지하는 방법을 배웠습니다. 내일은 CSS 또는 CSS 프레임워크를 사용하여 기본 스타일링을 개선하여 더 보기 좋은 사용자 인터페이스를 만들어 보겠습니다.