junyeokk
Blog
Next.js·2025. 09. 04

Next.js allowedDevOrigins

Next.js 개발 서버는 기본적으로 localhost에서 실행된다. 대부분의 경우 이걸로 충분하지만, 실제 프로젝트에서는 커스텀 도메인으로 개발해야 하는 상황이 생긴다.

예를 들어 OAuth 소셜 로그인을 구현할 때, Google이나 Apple의 콜백 URL에 localhost를 등록할 수 없는 경우가 있다. 또는 HTTPS가 필수인 API를 호출해야 하거나, 쿠키의 Secure 속성 때문에 HTTPS 환경이 필요한 경우도 있다. 이런 상황에서 youvico.dev 같은 커스텀 도메인을 /etc/hosts에 등록하고 mkcert로 로컬 인증서를 만들어서 HTTPS 개발 서버를 띄우면, 브라우저에서 cross-origin 관련 경고가 뜨면서 HMR(Hot Module Replacement)이 동작하지 않는 문제가 발생한다.


왜 이런 문제가 생기는가

Next.js 개발 서버는 내부적으로 WebSocket을 사용해서 HMR, 에러 오버레이, Fast Refresh 등의 기능을 제공한다. 이 WebSocket 연결은 서버가 시작된 hostname을 기준으로 origin 검증을 수행한다.

기본적으로 Next.js는 localhost에서 시작되므로, localhost에서 오는 요청만 허용한다. 그런데 브라우저에서 https://youvico.dev:3000으로 접속하면, WebSocket 연결의 origin이 youvico.dev가 되고, 이것이 서버가 기대하는 localhost와 다르기 때문에 cross-origin 요청으로 간주된다.

text
브라우저 접속: https://youvico.dev:3000
WebSocket 요청 origin: https://youvico.dev:3000
서버 기대 origin: http://localhost:3000
→ origin 불일치 → cross-origin 차단
→ HMR 연결 실패 → 코드 변경해도 브라우저가 안 바뀜

이 제한은 보안을 위한 것이다. 개발 서버에는 소스맵, 내부 API 엔드포인트, 에러 스택 트레이스 등 민감한 정보가 노출되어 있다. 아무 origin에서나 이 정보에 접근할 수 있으면, 같은 네트워크에 있는 악의적인 사이트가 개발 서버의 내부 정보를 탈취할 수 있다. Next.js는 향후 메이저 버전에서 이 cross-origin 차단을 기본 동작으로 강화할 예정이다.


allowedDevOrigins 설정

allowedDevOrigins는 이 문제를 해결하기 위한 설정이다. 개발 서버가 허용할 추가 origin을 명시적으로 지정한다.

typescript
// next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  allowedDevOrigins: ["youvico.dev"],
};

export default nextConfig;

이렇게 설정하면 youvico.dev에서 오는 WebSocket 연결이 허용되어 HMR이 정상 동작한다.

여러 도메인 허용

배열이기 때문에 여러 도메인을 동시에 허용할 수 있다.

typescript
allowedDevOrigins: [
  "youvico.dev",
  "local.youvico.com",
  "192.168.0.10",  // LAN IP로 모바일 테스트할 때
],

모바일 디바이스에서 같은 네트워크를 통해 개발 서버에 접속할 때도 동일한 문제가 발생한다. 이 경우 개발 머신의 LAN IP를 allowedDevOrigins에 추가하면 된다.

와일드카드 서브도메인

서브도메인 패턴도 지원한다.

typescript
allowedDevOrigins: ["*.localhost"],

이렇게 하면 app.localhost, admin.localhost 등 모든 서브도메인에서 접속할 수 있다. 멀티 테넌트 앱에서 서브도메인별로 다른 워크스페이스를 보여주는 구조라면 유용하다.


실제 사용 시나리오: HTTPS 개발 환경 구축

allowedDevOrigins가 필요한 가장 흔한 시나리오는 로컬 HTTPS 개발 환경이다. 전체 과정을 정리하면 이렇다.

1. 호스트 파일 설정

text
# /etc/hosts (macOS/Linux) 또는 C:\Windows\System32\drivers\etc\hosts (Windows)
127.0.0.1 youvico.dev

2. mkcert로 로컬 인증서 생성

bash
# mkcert 설치 (macOS)
brew install mkcert
mkcert -install  # 로컬 CA 설치

# 인증서 생성
mkcert youvico.dev localhost 127.0.0.1 ::1
# → youvico.dev+3.pem, youvico.dev+3-key.pem 생성됨

mkcert는 로컬 개발용 인증서를 만들어주는 도구다. 자체 서명 인증서와 달리, 로컬 CA를 시스템에 등록하기 때문에 브라우저에서 신뢰할 수 있는 인증서로 인식된다.

3. 커스텀 개발 서버 작성

javascript
// server.dev.js
const { createServer } = require("https");
const { parse } = require("url");
const next = require("next");
const fs = require("fs");
const path = require("path");

const app = next({ dev: true });
const handle = app.getRequestHandler();

const httpsOptions = {
  key: fs.readFileSync(path.join(__dirname, "certificates/youvico.dev+3-key.pem")),
  cert: fs.readFileSync(path.join(__dirname, "certificates/youvico.dev+3.pem")),
};

app.prepare().then(() => {
  createServer(httpsOptions, (req, res) => {
    const parsedUrl = parse(req.url, true);
    handle(req, res, parsedUrl);
  }).listen(3000, "youvico.dev", () => {
    console.log("Ready on https://youvico.dev:3000");
  });
});

Next.js의 기본 next dev 대신, Node.js의 https.createServer로 HTTPS 서버를 직접 띄운다. next() 함수로 Next.js 앱 인스턴스를 만들고, 모든 요청을 getRequestHandler()로 위임한다.

4. Next.js 설정

typescript
// next.config.ts
const nextConfig: NextConfig = {
  allowedDevOrigins: ["youvico.dev"],
};

5. package.json 스크립트

json
{
  "scripts": {
    "dev": "node server.dev.js",
    "dev:http": "next dev --turbopack"
  }
}

HTTPS가 필요 없는 일반 개발에는 dev:http를 쓰고, OAuth 테스트 등 HTTPS가 필요한 경우 dev를 쓰는 식으로 나눌 수 있다.


allowedDevOrigins vs experimental.serverActions.allowedOrigins

비슷해 보이지만 완전히 다른 설정이다.

설정대상용도
allowedDevOrigins개발 서버 전체 (HMR, WebSocket 등)개발 중 cross-origin 접속 허용
serverActions.allowedOriginsServer Actions (POST 요청)프로덕션 포함, Server Actions의 CSRF 보호

serverActions.allowedOrigins는 Server Actions가 허용할 origin을 지정하는 것으로, 프로덕션 환경에서도 적용된다. 리버스 프록시 뒤에서 origin이 달라지는 경우에 사용한다.

typescript
const nextConfig: NextConfig = {
  allowedDevOrigins: ["youvico.dev"],  // 개발 전용
  experimental: {
    serverActions: {
      allowedOrigins: ["youvico.com"],  // 프로덕션 포함
    },
  },
};

주의사항

개발 환경 전용

allowedDevOriginsnext dev에서만 동작한다. next build + next start로 실행하는 프로덕션 환경에서는 아무 효과가 없다. 프로덕션에서 CORS를 제어하려면 미들웨어나 API Route에서 직접 헤더를 설정해야 한다.

포트 번호 포함 여부

origin에 포트 번호를 포함할 수도 있고 생략할 수도 있다. 도메인만 적으면 해당 도메인의 모든 포트에서 접속을 허용한다.

typescript
// 도메인만 (모든 포트 허용)
allowedDevOrigins: ["youvico.dev"]

// 특정 포트만 허용
allowedDevOrigins: ["youvico.dev:3000"]

Next.js 버전 호환성

allowedDevOrigins는 Next.js 14.2부터 도입되었다. 그 이전 버전에서는 이 옵션이 없기 때문에, cross-origin 관련 경고가 떠도 HMR은 차단되지 않았다. 즉, 보안 강화의 일환으로 나중에 추가된 옵션이다.


정리

  • allowedDevOrigins는 커스텀 도메인이나 LAN IP로 개발 서버에 접속할 때 WebSocket origin 불일치로 HMR이 깨지는 문제를 해결한다
  • 개발 환경 전용 설정이며, 프로덕션의 Server Actions CSRF 보호인 serverActions.allowedOrigins와는 별개다
  • 와일드카드 서브도메인을 지원하고, Next.js 14.2부터 도입된 보안 강화 옵션이다

관련 문서