TechFeedTechFeed
Frontend

ERR_INVALID_URL, 잘못된 주소, new URL | 환경 변수가 비었는데 빌드가 주소에서 죽으면?

ERR_INVALID_URL은 new URL이 빈 문자열이나 상대 경로를 절대 주소로 읽지 못할 때 노드가 던지는 타입 오류입니다. 빌드 전에 URL.canParse로 거르고, 카카오 콜백은 베이스를 붙여 만듭니다. 클라이언트 번들에만 값이 없는 NEXT_PUBLIC과는 다른 줄입니다. Next.js, 버셀 프리뷰, 환경 변수, 한국 1인 개발. 2026년 10월 문서.

by

ERR_INVALID_URL은 new URL에 빈 문자열, undefined가 된 글자, 스키마 없는 경로가 들어왔을 때 노드가 던지는 타입 오류입니다. 환경 변수를 읽자마자 생성자에 넣지 말고, 값이 절대 주소인지 확인한 뒤에만 new URL을 부르세요.




로컬 .env에는 https://localhost:3000이 있는데, 버셀 프리뷰 빌드만 TypeError: Invalid URL에서 멈추는 장면이 이 줄입니다. 모듈 맨 위에서 콜백 주소를 만들면, 요청이 오기 전에 빌드가 죽습니다.




클라이언트 번들에 NEXT_PUBLIC이 안 박힌 줄과는 다릅니다. 근거는 Node.js 오류 코드와 URL 생성자 문서에 뒀습니다.


new URL이 거절하는 입력

WHATWG URL 파서는 절대 주소만 단독으로 받습니다. 스키마와 호스트가 없으면 파싱이 실패하고, 노드는 그걸 TypeError [ERR_INVALID_URL]: Invalid URL로 올립니다.


빈 문자열 ''은 실패합니다. 변수가 아예 없으면 자바스크립트가 undefined를 글자 "undefined"로 바꾼 뒤 생성자에 넣고, 그 글자도 주소가 아니라 같은 코드가 납니다. /kakao/callback처럼 슬래시로 시작하는 경로는 베이스가 없으면 실패합니다. localhost:3000도 스키마가 없으면 실패합니다.


베이스를 두 번째 인자로 주면 상대 경로를 붙일 수 있습니다. new URL('/kakao/callback', 'https://preview.example.com')은 통과합니다. 베이스 자체가 비어 있으면 두 번째 인자에서 다시 같은 오류가 납니다. 노드 18.17과 20부터는 URL.canParse가 있어, 던지기 전에 false만 받을 수 있습니다.


로그에 찍히는 입력 | 오류 메시지 기본문은 Invalid URL 한 줄입니다. 어떤 변수가 비었는지는 생성자가 알려 주지 않으니, 호출 전에 변수 이름을 포함한 오류를 던지세요.


환경 변수가 비어 new URL이 Invalid URL을 던지는 빌드 로그 개념
빈 문자열과 스키마 없는 경로는 new URL이 절대 주소로 받지 않는다

빌드 때 죽는 자리와 요청 때 죽는 자리

모듈 최상단에서 new URL(process.env.APP_URL)을 실행하면, 그 파일을 처음 평가하는 순간에 터집니다. 넥스트 빌드가 페이지를 수집할 때 이미 죽으면 프리뷰 URL이 아예 안 나옵니다.


호출 위치언제 죽나화면
모듈 최상단빌드 또는 서버 기동프리뷰 주소가 안 열림
서버 함수 안그 요청만로그인 버튼만 500
클라이언트 클릭브라우저 콘솔버튼은 있는데 이동이 없음
next.config 리다이렉트빌드가 설정을 읽을 때설정 로드 실패

카카오 로그인처럼 주소가 요청마다 필요하면 함수 안으로 내리세요. 최상단에 두면 프리뷰 환경 변수를 빼먹은 배포 전체가 막힙니다. 함수 안이면 그 버튼만 실패하고, 로그에 변수 이름을 남길 수 있습니다.


로컬은 .env가 채워져 통과하는데 프리뷰만 죽는 이유는, 버셀에서 Production에만 값을 넣고 Preview는 비워 둔 경우가 많습니다. 빈 칸으로 저장한 변수는 없는 게 아니라 빈 문자열이라, new URL('')과 같은 길이 됩니다.


절대 주소일 때만 생성자를 부른다
function appOrigin() { const raw = process.env.APP_URL; if (!raw || !URL.canParse(raw)) { throw new Error('APP_URL이 절대 주소가 아닙니다'); } return new URL(raw); } export function kakaoCallback() { return new URL('/kakao/callback', appOrigin()); }

통과하는 문자열과 실패하는 문자열

생성자에 넣기 전에 문자열 모양을 나누면, 콘솔의 Invalid URL 한 줄에서 헤매지 않습니다.


입력단독 new URL이유
https://app.example.com통과스키마와 호스트가 있음
빈 문자열ERR_INVALID_URL파싱할 문자가 없음
undefined 변수글자 undefined로 실패문자열이 강제 변환됨
/kakao/callback베이스 없으면 실패상대 경로
localhost:3000실패스키마가 없음

끝에 슬래시가 있든 없든 절대 주소면 생성자는 통과합니다. 카카오 콘솔에 등록한 리다이렉트와 글자가 같은지는 그 다음 문제입니다. 생성자가 성공해도 콘솔 주소와 한 글자만 달라도 로그인은 거절됩니다. 비교는 href를 찍어서 하세요. origin만 보면 경로가 잘립니다.


쿼리를 붙일 때는 문자열 이어 붙이기보다 searchParams.set을 쓰세요. ?를 두 번 붙이면 생성자는 통과하는데 카카오가 code를 이상한 키로 받습니다.


프리뷰 환경 변수는 빈 칸도 값이다

버셀은 Production, Preview, Development를 따로 저장합니다. 로컬 .env.local은 배포 빌드에 올라가지 않습니다. 프리뷰 빌드가 읽는 값은 대시보드의 Preview 칸입니다.


체크할 이름은 코드가 읽는 이름과 같아야 합니다. APP_URL을 읽는데 대시보드에는 NEXT_PUBLIC_APP_URL만 있으면, 서버 함수 쪽 변수는 여전히 비어 있습니다. 반대로 서버 전용 주소를 NEXT_PUBLIC_ 없이 클라이언트에서 읽으면 번들에는 빈 값이 들어갑니다. 그 경우도 new URL까지 가면 같은 코드가 나지만, 원인은 접두사입니다.


프리뷰 도메인이 배포마다 바뀌면 고정 APP_URL 대신 VERCEL_URL에 스키마를 붙여 베이스를 만듭니다. VERCEL_URL에는 https://가 없습니다. 그대로 생성자에 넣으면 스키마 없음으로 실패합니다. https://를 붙인 뒤에 canParse를 통과시키세요.


한국에서 카카오 로그인을 프리뷰에 붙여 쓰는 1인 프로젝트는, 콘솔의 리다이렉트 목록과 이 베이스가 같은 호스트인지까지 봐야 합니다. 생성자가 통과해도 콘솔에 그 호스트가 없으면 로그인 창만 거절합니다. 빌드 로그의 Invalid URL이 사라졌는지를 먼저 가르세요.


VERCEL_URL에는 스키마가 없다
function previewOrigin() { if (process.env.APP_URL && URL.canParse(process.env.APP_URL)) { return process.env.APP_URL; } const host = process.env.VERCEL_URL; const withScheme = host ? 'https://' + host : ''; if (!URL.canParse(withScheme)) { throw new Error('APP_URL 또는 VERCEL_URL이 없습니다'); } return withScheme; }
프리뷰 환경 변수 칸이 비어 빌드가 주소 생성에서 멈추는 장면
Production에만 주소를 넣으면 프리뷰 빌드의 new URL은 빈 문자열을 만난다

NEXT_PUBLIC 미주입과 다른 줄

증상이 비슷해 보여도 로그 한 줄이 다릅니다. NEXT_PUBLIC을 클라이언트에서 읽었는데 빌드에 값이 없으면, 대개 undefined인 채로 화면은 뜨고 버튼만 이상합니다. 서버 빌드가 ERR_INVALID_URL로 중단되면 생성자가 먼저 던진 것입니다.


쿠키를 동기처럼 읽어 넥스트 15가 경고하는 줄, 정적 생성 중에 cookies()를 써서 Dynamic server usage가 나는 줄도 따로입니다. 그 둘은 주소 파서가 아니라 요청 API입니다. 스택에 new URL 또는 ERR_INVALID_URL이 있는지만 보면 됩니다.


리다이렉트 헬퍼가 예외를 삼키는 문제는 주소가 이미 만들어진 뒤의 흐름입니다. 주소 생성에서 빌드가 끝나면 그 코드까지 도달하지 않습니다. 관련 글은 클라이언트에 값이 안 실릴 때, 쿠키를 기다려야 할 때, 정적 빌드가 쿠키에서 멈출 때입니다.


한국 서비스에서 카카오 로그인을 프리뷰에 붙이면, 콘솔에 등록하는 주소와 서버가 만드는 주소가 한 글자만 달라도 로그인이 거절됩니다. 그 거절은 빌드가 끝난 뒤의 일이고, 이번 오류는 그 주소를 만들기도 전에 납니다. 프리뷰 환경의 값에 스키마가 있는지, 빈 칸으로 저장하지는 않았는지부터 보세요. 로컬 파일에만 주소가 있고 배포 칸이 비어 있으면 집 맥에서는 버튼이 되고 프리뷰에서는 빌드가 죽습니다.


값을 로그에 전부 찍지 마세요. 주소 안에 토큰이나 질의 문자열이 섞여 있으면 빌드 로그에 남습니다. 길이와 앞 여덟 글자만 남기면 빈 문자열인지, 스키마가 빠졌는지, 호스트가 잘렸는지를 구분할 수 있습니다. 그 한 줄이 있으면 대시보드를 열어 어느 칸이 비었는지 바로 찾을 수 있습니다.


  • [ ] 스택에 ERR_INVALID_URL 또는 new URL이 있는지 봤다
  • [ ] 입력이 빈 문자열인지 undefined인지 로그에 남겼다
  • [ ] URL.canParse를 통과한 뒤에만 생성자를 불렀다
  • [ ] 프리뷰 칸과 변수 이름이 코드와 같은지 봤다
  • [ ] VERCEL_URL에는 https:// 를 붙였다

절대 주소와 상대 경로를 나누어 콜백 URL을 만드는 개발 화면
상대 경로는 베이스가 있을 때만 new URL의 두 번째 인자로 붙인다

참고 자료


내부 연계: 클라이언트에 환경 변수가 안 실릴 때, cookies와 headers를 await 할 때, 정적 빌드가 쿠키에서 멈출 때.


인용한 생성자 동작은 2026년 10월 Node.js와 넥스트 공개 문서 기준입니다. URL.canParse는 노드 18.17, 19.9, 20 이상입니다.


자주 묻는 질문

localhost:3000 만 넣어도 되나요?

스키마가 없으면 실패합니다. http://localhost:3000 처럼 스키마와 호스트가 같이 있어야 단독 new URL이 통과합니다. 포트만 있는 문자열은 상대 참조로 거절됩니다.


변수는 있는데도 Invalid URL이 납니다.

빈 칸으로 저장된 값은 있는 변수이지만 빈 문자열입니다. 앞뒤 공백, 따옴표가 값에 포함된 경우도 파서가 거절합니다. 로그에는 길이만 찍고 값 전체는 찍지 마세요.


try로 감싸면 빌드가 통과하나요?

최상단 try는 오류를 삼키고 주소가 빈 채로 남습니다. 로그인 주소가 조용히 깨집니다. 함수 안에서 변수 이름이 있는 오류로 실패시키는 편이 다음 배포에서 찾습니다.


카카오 콘솔까지 고쳐야 하나요?

빌드가 Invalid URL에서 끝나면 콘솔에 도달하기 전입니다. 생성자가 통과한 뒤 로그인만 거절되면, 그때 리다이렉트 문자열이 콘솔 목록과 같은지 봅니다.


URL.canParse가 없다고 나옵니다.

노드 18.17 미만에는 그 정적 메서드가 없습니다. 노드 20으로 올리거나, try 안에서 new URL을 부르고 실패하면 변수 이름을 다시 던지세요. 빈 catch로 끝내면 안 됩니다.


NEXT_PUBLIC을 붙이면 해결되나요?

접두사는 클라이언트 번들에 넣을지만 정합니다. 값이 빈 문자열이면 생성자는 그대로 실패합니다. 서버 콜백 주소는 접두사 없이 두고, 프리뷰 칸에 절대 주소를 채우세요.


ERR_INVALID_URL은 new URL이 절대 주소를 받지 못했을 때의 타입 오류입니다. 빈 프리뷰 변수와 스키마 없는 VERCEL_URL을 먼저 고치고, 생성자는 URL.canParse 뒤에만 부르세요. 번들에 값이 없는 증상은 NEXT_PUBLIC 글을 보시면 됩니다.


ERR_INVALID_URLnew URL환경변수Next.js프리뷰콜백빌드프론트엔드

함께 보면 좋은 문제 해결

EXPLORE / Frontend

이어서 읽어보기

전체 토픽 둘러보기 →