[Next.js] SSR에서 API Routes CORS 에러 해결

[Next.js] SSR에서 API Routes CORS 에러 해결

·

2 min read

지난 포스트에서 독서 페이지를 클라이언트 컴포넌트에서 서버 컴포넌트로 리팩터링하는 작업을 했다. 이번에는 독서 추가 페이지에 대해 동일한 작업을 하였다.

그러나 지난번과는 달리 API 호출이 제대로 되지 않았다.

🔴 문제

// app/book/add/[id]/page.tsx

async function page({ params }: { params: { id: string } }) {
  const { id } = params
  const info = await fetchAladinBookInfo({ isbn: id })

  return (
    <Suspense fallback={<Skeleton />}>
      <PageTitle route={info.title} />
      ...
    </Suspense>
  )
}

fetchAladinBookInfo 함수에서 데이터를 반환하지 않아 info 값을 찾을 수 없어 에러가 발생한 것이다.

export const fetchAladinBookInfo = async ({ isbn }: AladinBookInfoRequest) => {
  try {
    const response = await fetch(`/api/book/info?isbn=${isbn}`)
    if (!response.ok) {
      throw new Error('Network response was not ok')
    }
    const data = await response.json()

    if (data.errorCode && data.errorCode === 3) {
      return null
    } else {
      const list = data.item
      return list[0]
    }
  } catch (error) {
    console.error(error)
  }
}

fetchAladinBookInfo 함수는 위와 같이 구성되어 있다. 에러가 catch 되었으니 api를 패치하는 데에 문제가 있을 것이다.

중요한 점은 이전 포스트에서 사용한 API는 firebase 데이터베이스를 가져온 것이고, fetchAladinBookInfo에서 사용된 API는 Next.js API Routes에서 정의된 것이다.

그리고 이 문제는 클라이언트와 서버 컴포넌트의 차이점을 통해 해결할 수 있을 것이다.

🔵 클라이언트 vs 서버

클라이언트 컴포넌트는 브라우저에서 실행된다. 브라우저는 기본적으로 현재 페이지의 도메인과 경로를 기준으로 상태 경로를 처리할 수 있다. 따라서 /api/book/... 과 같은 상대 경로를 사용하면, 브라우저는 이를 현재 호스트를 기준으로 절대 경로로 변환하여 http://localhost:3000/api/book/... 으로 변환하여 API 요청을 보낸다.

반면, 서버 컴포넌트는 서버에서 실행된다. 서버에서는 클라이언트와 같은 맥락이 없기 때문에 상대 경로를 절대 경로로 변환할 수 없다. 따라서 서버 컴포넌트에서 API를 호출할 땐 절대 경로를 사용해야 한다.

🟢 해결

상대 경로로 정의한 API를 절대 경로로 바꿔주면 문제를 해결할 수 있다. 개발 환경과 배포 환경에서 모두 원활하게 동작할 수 있도록 env로 호스트를 정의하였다.

// .env.local
NEXT_PUBLIC_API_BASE_URL=http://localhost:3000

배포 환경에선 NEXT_PUBLIC_API_BASE_URLhttps://libookrary.vercel.app으로 정의하였다.

const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL

export const fetchNewSpecial = async ({ onSuccess, onError }: FuncType) => {
  try {
    const response = await fetch(`${baseUrl}/api/book/newSpecial`)
    ...
  } catch (error) {
    ...
  }
}

이제 서버 환경에서 Next.js API Routes에 접근하여 데이터를 가져올 수 있다.