Vercel Serverless 적용하기

이전에 서버 주소를 숨기고자 .env파일에 넣어놨는데 

네... 다 보이고 있습니다.

 

네트워크 요청 주소를 숨길 수 있는 방법이 여러 개 존재하는데 

  1. Vercel Serverless 사용하기
  2. AWS API Gateway 사용하기
  3. SSR 사용하기

등 여러가지가 있는데 그중 Vercel Serverless를 적용해 보겠습니다.

 

Vercel CLI 설치

일단 Vercel Serverless를 이용하게 되면 기존의 vite 명령어로 로컬에서 동작하는 걸 볼 수 없기 때문에 Vercel CLI를 설치해 줍니다.

npm install -g vercel

 

개발 서버 실행 명령어는 다음과 같습니다.

vercel dev

 

root/api/index.js 파일 만들고 기존의 api 변경하기

클라이언트에서 서버로 바로 요청하던 것을 Vercel로 우회해 요청하는 것이 가능하도록 하는 파일입니다. 

중요합니다!

 

저는 다음과 같이 만들었습니다.

// api/index.js
import fetch from "node-fetch";
import dotenv from "dotenv";

dotenv.config();
const API_END_POINT = process.env.VITE_API_URL;
const USERNAME = process.env.VITE_USERNAME;

export default async function handler(req, res) {
  const { id } = req.query;
  const url = id ? `/${id}` : req.url.replace("/api/index", "");

  try {
    const fetchOptions = {
      method: req.method,
      headers: {
        "x-username": USERNAME,
        "Content-Type": "application/json",
      },
    };

    if (req.method !== "GET") {
      fetchOptions.body = JSON.stringify(req.body);
    }

    const fetchUrl = `${API_END_POINT}${url}`;

    const response = await fetch(fetchUrl, fetchOptions);

    const responseText = await response.text();
    
    if (response.ok) {
      try {
        const data = JSON.parse(responseText);
        return res.status(200).json({ data }); 
      } catch (jsonError) {
        console.error("JSON parsing error:", jsonError.message);
        throw new Error("응답 데이터 파싱 중 이상이 발생했습니다.");
      }
    } else {
      console.error("Response not OK:", responseText);
      return res.status(response.status).json({ message: responseText });
    }
  } catch (error) {
    console.error("Error occurred:", error.message);
    return res.status(500).json({ message: error.message });
  }
}

그리고 기존의 api 파일은

Before

const API_END_POINT = import.meta.env.VITE_API_URL;
const USERNAME = import.meta.env.VITE_USERNAME;

export const request = async (url = "", options = {}) => {
  try {
    const res = await fetch(`${API_END_POINT}${url}`, {
      ...options,
      headers: {
        "x-username": USERNAME,
        "Content-Type": "application/json",
      },
    });
    if (res.ok) {
      return await res.json();
    }
    throw new Error("API 처리중 이상이 발생했습니다.");
  } catch (event) {
    alert(event.message);
    window.location = "https://vanilla-js-editor.vercel.app/";
  }
};

After 

// src/api.js
export const request = async (url = "", options = {}) => {
  try {
    const fullUrl = `/api${url}`; //상대 경로 가능
    const res = await fetch(fullUrl, {
      ...options,
      headers: {
        ...options.headers, // 기존 헤더 포함
        "Content-Type": "application/json",
      },
    });

    const responseText = await res.text();

    if (res.ok) {
      if (responseText) {
        try {
          const data = JSON.parse(responseText);
          return data;
        } catch (jsonError) {
          console.error("JSON parsing error:", jsonError.message);
          throw new Error("응답 데이터 파싱 중 이상이 발생했습니다.");
        }
      } else {
        throw new Error("빈 응답을 받았습니다.");
      }
    } else {
      console.error(`Error response: ${responseText}`);
      throw new Error(`API 요청 실패: ${res.status} - ${responseText}`);
    }
  } catch (event) {
    console.error("Error occurred:", event.message);
    alert(event.message);
    window.location.href = "/main";
  }
};

다음과 같이 변경되었고 요청을 보내야 하는 주소가 달라졌기 때문에 data.js도 변경이 생겼습니다.

 

너무 길기 때문에 간단하게 설명하면, 기존의 주소와 다르게  `/api/${url}을 추가해야 하기 때문에 url부분인 /index를 추가해 줬습니다.

 

예를 들면

Before

  getDocumentStructure = async () => {
    const getDocumentStructure = await request("", {
      method: "GET",
    });
    return getDocumentStructure;
  };

After

  getDocumentStructure = async () => {
    const getDocumentStructure = await request("/index", { //index 추가됨
      method: "GET",
    });
    return getDocumentStructure;
  };

이런 방식으로 변경되었습니다.

 

문제 발생

저는 서버에서 분명 제대로 된 응답을 주고 있는데 클라이언트에서 받지 못하는 문제를 겪었습니다.

무조건 res.status(200)을 보내도 404로 받아 너무 답답했는데 문제는 바로 vercel.json 설정을 제가 빠트렸던 것이었습니다. 다른 글을 찾아봐도 이런 얘기는 없던데 어떻게 하신 건지 너무 궁금.. 찾다 찾다 결국 stackoverflow 가서 발견했습니다.

 

저는 약간 다르게 설정하긴 했는데 결은 비슷합니다. 꼭 잊지 말고 입력해 주세요.

{
  "version": 2,
  "builds": [
    {
      "src": "api/index.js",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/api/(.*)",
      "dest": "api/index.js"
    }
  ]
}

 

마무리

요청이 변경되어 그런지 처음에 저는 데이터를 가공해서 썼었는데, 변경 후 초기값으로 들어오는 문제가 있었습니다.

이에 맞춰 코드 변경해주시면 되겠습니다.

 

 

현재 이 코드는 로컬에서만 동작합니다. 실제 배포에서는 설정을 좀 다르게 해야하는 것 같습니다.