본문으로 건너뛰기
yceffort
PostsSeriesTagsAbout🧪 Research
EN

Tweaks

theme
accent palette
film grain
minimal mode
BACK TO INDEX
◆ ESSAY
--min
--year
KOoriginal

mailMail icongithubtwitter
yceffort
•
© 2026
•
https://yceffort.kr
BACK TO INDEX
◆ ESSAY

nextjs 서버사이드에서 absolute url 가져오기

avatar
yceffort
2021-10-29 · 4분
4min
2021year
KOoriginal
nextjsbackend

nextjs에서 fetch api를 사용하다가 깨닫는 점 하나는, (당연하지만) 서버와 클라이언트에서 fetch를 하는 방식을 다르게 가져가야 한다는 것이다. 무심코 평소에 하듯이 fetch('/api/some/info')를 하다보면, getServerSideProps나 getInitialProps에서 에러가 날 수 있다. fetch 를 할 때 놓치지 말아야 할 이 에러를 살펴보자.

Absolute URL

서버사이드에서 절대 경로가 아닌 상대경로로 fetch 요청을 하면 (/api/some/info) Absolute URL이 필요하다는 에러가 뜬다. 클라이언트에서는 origin을 추론할 수 있기 때문에 상대경로로 요청을 해도 상관없지만, 서버사이드에서는 현재 주소가 무엇인지 알리가 없기 때문에 상대경로로 요청할 수 없다.

그렇다면 아래와 같이 처리해도 되는 것인가?

async function getUser(id: number) {
  const response = await fetch(
    // 서버라면 강제로 우리가 알고 있는 absolute url을 주입
    typeof window === 'undefined'
      ? 'https://yceffort.kr'
      : '' + `/api/user/${id}`,
  )
  const result = await response.json()
  return result
}

absolute url, origin이 고정되어 있는 경우라면 괜찮겠지만 그렇지 않다면 이렇게 처리하는건 안전하지 못하다. 따라서 우리는 absolute URL을 추론해야 한다. 추론할 수 있는 가장 좋은 방법은, ctx.req 즉 IncomingMessage를 사용하는 것이다. 여기에는 요청과 관련한 정보가 포함되어 있는데, 이 요청이 날라온 곳이 absolute url이라고 가정하고 코딩하는 것이다.

import {IncomingMessage} from 'http'

function getAbsoluteURL(req?: IncomingMessage) {
  // 로컬은 http, 프로덕션은 https 라는 가정
  const protocol = req ? 'https:' : 'http:'
  let host = req
    ? req.headers['x-forwarded-host'] || req.headers['host']
    : window.location.host

  // 주소에 local이라는 문자열이 들어가 있다면,
  // 또는 별도의 환경변수를 주입하고 있다면 그것을 사용해도된다.
  // process.env.RECT_APP_PROFILES === 'local'
  if ((host || '').toString().indexOf('local') > -1) {
    // 개발자 머신에서 실행했을 때 로컬
    host = 'localhost:3000'
  }

  return {
    protocol: protocol,
    host: host,
    origin: protocol + '//' + host,
  }
}

물론 이 방법도 완전하지는 못하다. 프로젝트의 상황에 따라 조금씩 다를 수 있다. 일단 프로토콜은 서버에서 추론할 수가 없는 부분이기 때문에 하드 코딩 형태의 추론이 필요하다.

그리고 host가 아닌 x-forwarded-host를 사용한 이유는 설명에도 나와있듯, 요청을 처리하는 원래 사용된 host를 확인하기 위해 사용하였다.

그리고 이제 fetch 함수는 getAbsoluteURL를 사용해야 한다.

type FetchOptions = {
  req?: IncomingMessage
}

async function getUser(id: number, options?: FetchOptions) {
  const absoluteURL = getAbsoluteURL(options?.req).origin
  const response = await fetch(
    options?.req ? absoluteURL : '' + `/api/user/${id}`,
  )
  const result = await response.json()
  return result
}
export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const {req} = ctx
  const userId = ctx.query?.userId

  const user = await getUser(userId, {req})
  return {
    props: {
      user,
    },
  }
}

이제 서버에서 요청을 할때는 req 정보를 넘겨줘야 한다. 서버와 클라이언트 요청은 명확히 구별할 수 있으므로 크게 어렵지 않을 것이다.

또다른 방법

아무래도 하드 코딩이 들어가 있기 때문에, absolute url을 알아낼 수 있는 또다른 방법은 실행시에 환경변수로 주입하고, 이를 가져오는 것이다. 이 방법을 쓰고 있는 것이 VERCEL이다. NEXT_PUBLIC_VERCEL_URL를 사용하면 이 빌드가 실행되는 곳의 주소를 알아낼 수 있다.

물론 이는 devpos나 개발 환경에서 이러한 정보를 제공할 수 있는 환경 구축이 선행되어야 한다.

관련 글

  • ◆ 웹 서비스 성능 분석 · 3편
    #web-performance#nextjs#backend

    웹 서비스 성능 분석 (3)

    관심 가져주셔서 감사합니다. 🙇🏻‍♂️

    2025-07-01·56분
  • #nextjs#serverless#backend

    서버리스로 블로그 포스트 썸네일 생성하기

    어차피 나만 볼거임 ㅋㅅㅋ

    2020-12-08·6분
  • ◆ 블로그 성능 개선하기 · 3편
    #performance#web-vitals#nextjs

    블로그를 고친 뒤 첫 화면을 다시 재봤다

    블로그 마이그레이션 뒤 수식 글 LCP가 5.6초로 늘어난 원인을 추적했다. 일반 웹 글꼴을 실제 코드에서 제거하고 다시 빌드하자 수식 글 LCP는 5,650ms에서 2,352ms, FCP는 1,514ms에서 758ms로 줄었다. LCP 대상은 본문 문단에서 배너로 바뀌었다. 수식 전용 글꼴은 쓰는 글리프만 남긴 서브셋으로 바꿔 334KiB에서 26KiB가 됐다. 최초 비교 48회, 원인 대조 15회, 수정 전후 24회, 서브셋 전후 8회의 결과를 기록했다.

    2026-09-14·44분
  • ◆ 블로그 성능 개선하기 · 2편
    #rust#wasm#markdown

    블로그의 마크다운 파이프라인을 Rust/WASM으로 옮기기

    블로그의 remark/rehype 체인을 Rust로 옮기고 WASM으로 빌드해 Next.js 서버에 붙였다. 파싱과 HAST 생성에 이어 Oniguruma 하이라이트, MathML 수식, 이미지 크기와 MDX 속성 처리까지 한 호출로 묶었다. 메모리 전달과 해제, 기존 글의 호환성, WASI와 바이너리 배포를 구성하며 얻은 것과 감수한 비용을 기록했다.

    2026-09-14·61분

새 글을 놓치고 싶지 않으시다면 RSS로 구독해 주세요.

RSS 구독 →

yceffort — 프론트엔드 엔지니어입니다.

← Back to the blogIssue on GitHub →