Nuxt.js 를 배워보자/⚙️ 3. Nuxt의 서버 사이드 기능 활용: 서버 API와 미들웨어

⚙️ 로컬 API 서버 구축 실습: POST 요청으로 데이터 저장하기

_Blue_Sky_ 2025. 12. 5. 18:12
728x90

우리가 지금까지 만든 Nuxt 애플리케이션은 클라이언트(브라우저)에서 실행되는 프론트엔드 코드였습니다. 하지만 Nuxt는 Nitro 엔진 덕분에 자체적으로 서버 API를 구동할 수 있습니다. 이는 복잡한 백엔드 설정 없이도 사용자 인증, 데이터 유효성 검사 등 서버 로직을 Nuxt 프로젝트 내에서 처리할 수 있게 해줍니다.

이번 실습에서는 server/api/ 디렉토리를 사용하여 POST 요청을 받아 데이터를 처리하는 API 엔드포인트를 만들어 봅시다.

1. 🚀 Nuxt 서버 API의 특징

  • 자동 라우팅: server/api/ 폴더 내에 파일을 만들면, 파일명에 따라 자동으로 /api/... 엔드포인트가 생성됩니다.
  • 파일 기반 메소드 지정: 파일명에 .post, .get 등을 붙여 해당 HTTP 메소드만 처리하도록 지정할 수 있습니다. (예: user.post.ts는 POST 요청만 받음)
  • H3 기반: Nuxt의 서버는 빠르고 가벼운 H3 프레임워크를 기반으로 하며, defineEventHandler를 사용하여 서버 함수를 정의합니다.

2. 👩‍💻 실습 1: POST 요청을 받는 서버 API 구축

가상의 사용자 정보를 등록(POST)하는 API를 만들어 봅시다.

2.1. POST API 파일 생성

  1. 프로젝트 루트의 server/api 폴더 안에 register.post.ts 파일을 생성합니다.
    // server/api/register.post.ts
    
    // defineEventHandler를 사용하여 서버에서 실행될 함수를 정의합니다.
    export default defineEventHandler(async (event) => {
      // 1. readBody(event)를 사용하여 클라이언트가 전송한 POST 데이터를 가져옵니다.
      const body = await readBody(event)
    
      // 2. 데이터 유효성 검사: 이름과 이메일이 필수인지 체크합니다.
      if (!body.name || !body.email) {
        // 필수 필드가 누락되면, 에러 객체를 생성하여 클라이언트에 400 Bad Request 응답을 보냅니다.
        throw createError({
          statusCode: 400,
          statusMessage: '이름(name)과 이메일(email)은 필수입니다.',
        })
      }
    
      // 3. 가상의 데이터 저장 로직 (실제 DB 저장 로직이 들어갈 부분)
      const newUser = {
        id: Date.now(),
        name: body.name,
        email: body.email,
        status: 'registered',
      }
    
      // 4. 처리 결과를 JSON 형태로 클라이언트에 반환합니다.
      return {
        success: true,
        user: newUser,
        message: `${newUser.name} 님의 등록이 완료되었습니다.`,
      }
    })
    

2.2. 서버 확인

서버가 실행 중인 상태에서 (dev 모드), 외부 툴(Postman, Thunder Client 등)을 사용하여 POST 요청을 http://localhost:3000/api/register로 보내 테스트할 수 있습니다.

  • 성공 시: Body에 { "name": "철수", "email": "chulsu@test.com" } 전송 시, 성공 응답이 반환됩니다.
  • 실패 시: Body에 { "name": "영희" }만 전송 시, status: 400 에러와 함께 statusMessage가 반환됩니다.

3. 실습 2: 클라이언트 페이지에서 로컬 POST API 호출

이제 클라이언트 페이지를 만들어 사용자의 입력을 받아 방금 만든 서버 API로 데이터를 전송해 봅시다.

  1. 페이지 파일 생성: pages/signup.vue 파일을 생성합니다.
    <template>
      <div>
        <AppHeader />
        <h1>사용자 회원가입</h1>
    
        <form @submit.prevent="submitForm" style="display: flex; flex-direction: column; max-width: 300px;">
          <input type="text" v-model="form.name" placeholder="이름" required style="margin-bottom: 10px; padding: 8px;">
          <input type="email" v-model="form.email" placeholder="이메일" required style="margin-bottom: 10px; padding: 8px;">
    
          <button type="submit" :disabled="isPending" style="padding: 10px; background-color: #3b82f6; color: white; border: none; cursor: pointer;">
            {{ isPending ? '처리 중...' : '가입하기' }}
          </button>
        </form>
    
        <div v-if="successMsg" style="color: green; margin-top: 15px;">
          ✅ {{ successMsg }}
        </div>
        <div v-if="errorMsg" style="color: red; margin-top: 15px;">
          🚨 {{ errorMsg }}
        </div>
      </div>
    </template>
    
    <script setup>
    const form = reactive({
      name: '',
      email: ''
    })
    const isPending = ref(false)
    const successMsg = ref('')
    const errorMsg = ref('')
    
    async function submitForm() {
      // 상태 초기화
      isPending.value = true
      successMsg.value = ''
      errorMsg.value = ''
    
      try {
        // useFetch를 사용하여 로컬 API 엔드포인트에 POST 요청
        const { data, error } = await useFetch('/api/register', {
          method: 'POST',
          body: form // reactive 객체를 body에 전달하면 Nuxt가 JSON으로 자동 변환
        })
    
        if (error.value) {
          // 서버에서 발생한 에러 메시지(400 Bad Request 등) 처리
          errorMsg.value = error.value.statusMessage || '서버 오류 발생'
        } else if (data.value && data.value.success) {
          successMsg.value = data.value.message
          // 성공 후 폼 초기화
          form.name = ''
          form.email = ''
        }
    
      } catch (e) {
        errorMsg.value = '알 수 없는 클라이언트 오류가 발생했습니다.'
      } finally {
        isPending.value = false
      }
    }
    </script>
    

결과 확인: http://localhost:3000/signup에 접속하여 이름과 이메일을 입력하고 가입 버튼을 눌러보세요. 성공 메시지가 뜨면 Nuxt의 서버 API가 클라이언트 요청을 성공적으로 처리한 것입니다.


📝 블로그 마무리: 풀스택 통합 개발 환경

이번 실습을 통해 Nuxt가 제공하는 클라이언트와 서버의 완벽한 통합을 경험했습니다. 별도의 서버 환경 구축 없이도 server/api 폴더를 사용하여 복잡한 서버 로직(데이터 유효성 검사, 저장 처리)을 구현할 수 있게 되었습니다.

728x90