ToolPal
네트워크 케이블과 서버 인프라

URL 파서 완벽 가이드 — URL 구조 분석 방법

📷 Jordan Harrison / Pexels

URL 파서 완벽 가이드 — URL 구조 분석 방법

URL을 프로토콜, 호스트명, 포트, 경로, 쿼리 매개변수, 해시로 분해하는 방법을 실용적인 예제와 함께 알아봅니다.

2026년 3월 30일7분 소요

개발하다 보면 URL을 손으로 뜯어보는 상황이 생각보다 자주 온다. 예를 들어 백엔드에서 넘어온 redirect URL에서 특정 파라미터만 뽑아야 할 때, 아니면 GA(Google Analytics)에서 UTM 파라미터가 제대로 붙었는지 확인하고 싶을 때. 이럴 때 URL을 눈으로 파싱하려고 하면 금방 머리가 아파진다.

URL이 복잡해질수록 더 그렇다. 쿼리스트링에 파라미터가 10개 넘어가고, 값에 인코딩된 문자가 섞여 있으면 구분자 어디서 끊기는지도 헷갈리기 시작한다.

이 글에서는 URL의 구조를 차근차근 뜯어보고, 실제 개발 현장에서 어떻게 URL 파싱을 활용하는지 정리해본다.


URL이 뭔지 다시 한번 짚고 가자

URL은 Uniform Resource Locator의 약자다. 말 그대로 "어떤 리소스가 어디에 있는지 알려주는 주소"다. 우리가 매일 브라우저 주소창에 입력하는 그것.

그냥 문자열처럼 보이지만, 사실 URL은 꽤 정교한 규약(RFC 3986)에 따라 구성되어 있다. 각 부분이 명확한 역할을 하고 있고, 이 구조를 모르면 URL 조작이나 디버깅이 어려워진다.


URL의 구성 요소 완전 분해

이런 URL이 있다고 해보자.

https://user:pass@api.example.com:8080/v1/search?q=hello&page=2#results

복잡해 보이지만 쪼개면 다음과 같다.

1. 프로토콜 (Protocol / Scheme)

https:// 부분이다. 이 리소스에 접근할 때 어떤 통신 방식을 쓸지 지정한다. 웹에서는 httphttps가 주류지만, ftp, mailto, ws(WebSocket), file 같은 것도 있다.

httpshttp에 TLS 암호화가 추가된 버전이다. 요즘은 대부분의 사이트가 https를 강제한다.

2. 사용자 정보 (Userinfo)

user:pass@ 부분이다. 인증 정보를 URL에 직접 포함하는 방식인데, 보안상 이유로 현대 웹에서는 거의 쓰지 않는다. 브라우저도 이 부분을 포함한 URL에는 경고를 띄우기도 한다. FTP URL이나 일부 레거시 시스템에서 가끔 볼 수 있다.

3. 호스트명 (Hostname)

api.example.com 부분이다. DNS로 IP 주소로 변환된다. 서브도메인(api), 도메인(example), TLD(.com)로 다시 쪼갤 수 있다.

로컬 개발할 때는 localhost127.0.0.1을 쓰게 된다.

4. 포트 (Port)

:8080 부분이다. 호스트의 어떤 포트로 연결할지 지정한다. 생략하면 프로토콜 기본값이 쓰인다. http는 80, https는 443이 기본이다.

개발 환경에서 Next.js는 3000번, Vite는 5173번을 기본으로 쓰는 식이다.

5. 경로 (Path)

/v1/search 부분이다. 서버에서 어떤 리소스를 요청하는지 나타낸다. REST API에서는 리소스의 계층 구조를 표현하는 데 쓰인다.

6. 쿼리 문자열 (Query String)

?q=hello&page=2 부분이다. ?로 시작하고, 키=값 쌍을 &로 구분한다. 페이지 경로는 그대로 두면서 서버에 추가 정보를 전달하는 용도다. 검색어, 필터, 정렬 기준, 페이지 번호 같은 것들이 여기 들어간다.

값에 특수 문자나 한글이 포함되면 퍼센트 인코딩(%EC%95%88%EB%85%95 같은 형태)으로 변환된다.

7. 해시 (Fragment / Hash)

#results 부분이다. 페이지 내 특정 위치(앵커)로 이동할 때 쓴다. 그런데 여기서 중요한 사실이 하나 있다.

Fragment는 서버로 전달되지 않는다.

이건 꽤 많은 개발자들이 놓치는 포인트다. # 뒤의 내용은 브라우저에서만 처리된다. 서버 사이드에서 fragment 값을 읽을 수 없다는 뜻이다. 서버 로그에서 fragment를 찾아봐도 없는 게 정상이다.

SPA(Single Page Application)에서 해시 라우팅(/#/about 같은 방식)을 쓰는 이유도 여기에 있다. 서버에는 항상 /만 도달하고, 클라이언트에서 # 뒤를 보고 어떤 뷰를 렌더링할지 결정한다.


JavaScript로 URL 파싱하기

브라우저와 Node.js 모두 내장 URL API를 제공한다. 외부 라이브러리 없이 URL을 파싱할 수 있다.

const url = new URL('https://api.example.com:8080/v1/search?q=hello&page=2#results');

console.log(url.protocol);  // "https:"
console.log(url.hostname);  // "api.example.com"
console.log(url.port);      // "8080"
console.log(url.pathname);  // "/v1/search"
console.log(url.search);    // "?q=hello&page=2"
console.log(url.hash);      // "#results"

searchParams를 쓰면 쿼리 파라미터를 개별적으로 다룰 수 있다.

const url = new URL('https://example.com/search?q=hello&page=2&sort=desc');

// 특정 파라미터 값 가져오기
console.log(url.searchParams.get('q'));     // "hello"
console.log(url.searchParams.get('page')); // "2"

// 모든 파라미터 순회
url.searchParams.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});
// q: hello
// page: 2
// sort: desc

// 파라미터 추가/수정
url.searchParams.set('page', '3');
url.searchParams.append('filter', 'new');

console.log(url.toString());
// "https://example.com/search?q=hello&page=3&sort=desc&filter=new"

옛날에는 이런 거 하려면 location.search를 직접 문자열로 쪼개거나, 정규식을 썼는데 요즘은 URL API가 워낙 잘 되어 있어서 그럴 필요가 없다.


실용적인 사용 사례

API 디버깅

REST API를 호출했는데 서버에서 예상과 다른 응답이 돌아올 때, 요청 URL이 제대로 구성됐는지 확인하는 게 첫 번째 디버깅 단계다. URL 파서를 쓰면 쿼리 파라미터가 어떻게 인코딩됐는지, 경로가 올바른지 한눈에 볼 수 있다.

특히 API 클라이언트 라이브러리를 쓸 때 내부적으로 URL을 어떻게 조립하는지 확인하고 싶을 때 유용하다. axiosfetch에 전달한 파라미터가 실제로 URL에 어떻게 붙는지 검증할 수 있다.

UTM 파라미터 추출

마케팅에서 UTM 파라미터는 트래픽 소스를 추적하는 데 쓰인다. 이런 URL이 있다고 하자.

https://example.com/landing?utm_source=newsletter&utm_medium=email&utm_campaign=spring2026&utm_content=cta-button

GA나 다른 분석 도구가 이 파라미터를 자동으로 수집해주지만, 가끔 직접 파싱해서 처리해야 할 때가 있다. 예를 들어 특정 캠페인에서 온 사용자에게 다른 UI를 보여준다거나, 서버 사이드에서 UTM 데이터를 DB에 저장해야 할 때.

function extractUtmParams(urlString) {
  const url = new URL(urlString);
  const utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];

  return utmKeys.reduce((acc, key) => {
    const value = url.searchParams.get(key);
    if (value) acc[key] = value;
    return acc;
  }, {});
}

const result = extractUtmParams('https://example.com/landing?utm_source=newsletter&utm_medium=email&utm_campaign=spring2026');
console.log(result);
// { utm_source: 'newsletter', utm_medium: 'email', utm_campaign: 'spring2026' }

리다이렉트 URL 분석

OAuth 인증 플로우나 소셜 로그인을 구현할 때, 콜백 URL에 authorization code나 에러 정보가 쿼리 파라미터로 붙어 온다. 이걸 파싱해서 다음 단계를 처리해야 한다.

// OAuth 콜백 처리 예시
const callbackUrl = 'https://myapp.com/auth/callback?code=abc123&state=xyz789';
const url = new URL(callbackUrl);

const code = url.searchParams.get('code');
const state = url.searchParams.get('state');
const error = url.searchParams.get('error');

if (error) {
  console.error('인증 실패:', error);
} else {
  console.log('인증 코드:', code);
  // 서버에 code 전달하여 access token 교환
}

상대 경로를 절대 경로로 변환

URL 생성자의 두 번째 인수로 base URL을 넣으면 상대 경로를 절대 경로로 변환할 수 있다.

const base = 'https://example.com/blog/post/123';
const relative = '../images/thumbnail.jpg';

const absolute = new URL(relative, base);
console.log(absolute.href);
// "https://example.com/blog/images/thumbnail.jpg"

URL 인코딩과 디코딩

URL에서 사용할 수 없는 문자(한글, 공백, 특수 문자 등)는 퍼센트 인코딩으로 변환해야 한다. JavaScript에는 이를 위한 함수가 여러 개 있는데, 뭘 써야 할지 헷갈리는 경우가 많다.

// encodeURIComponent — 쿼리 파라미터 값 인코딩에 적합
console.log(encodeURIComponent('안녕 world!'));
// "%EC%95%88%EB%85%95%20world!"

// decodeURIComponent — 위의 반대
console.log(decodeURIComponent('%EC%95%88%EB%85%95%20world!'));
// "안녕 world!"

// encodeURI — URL 전체 인코딩 (/ : ? 같은 구조 문자는 건드리지 않음)
console.log(encodeURI('https://example.com/search?q=안녕'));
// "https://example.com/search?q=%EC%95%88%EB%85%95"

실무에서 주의할 점은 encodeURIComponentencodeURI의 차이다. 쿼리 파라미터 값을 인코딩할 때는 encodeURIComponent를 써야 &= 같은 구분자도 인코딩된다. encodeURI는 URL 전체를 인코딩할 때 쓰는데, 구조 문자는 그대로 두기 때문에 파라미터 값에 쓰면 예상치 못한 결과가 나올 수 있다.


URL 파서 도구 활용하기

코드로 파싱하는 게 항상 편한 건 아니다. 개발 중에 특정 URL이 어떻게 구성되어 있는지 빠르게 확인하고 싶을 때, 매번 콘솔에 코드 치는 것도 번거롭다.

toolboxhubs.com/ko/tools/url-parser에서 URL을 입력하면 각 구성 요소를 시각적으로 분리해서 보여준다. 쿼리 파라미터가 키-값 쌍으로 정리되고, 인코딩된 값도 디코딩해서 보여주기 때문에 복잡한 URL을 분석할 때 특히 편하다.

서버로 데이터가 전송되지 않고 브라우저 내장 URL API를 사용해서 처리되기 때문에, API 키나 토큰이 포함된 URL도 안심하고 붙여넣을 수 있다.


흔한 실수와 주의사항

1. Fragment는 서버로 안 간다

앞에서도 언급했지만, 이건 정말 중요하다. 서버 사이드 렌더링이나 API에서 # 뒤의 값을 읽으려는 시도는 절대 작동하지 않는다. Fragment 처리는 무조건 클라이언트 사이드에서 해야 한다.

2. 상대 URL에 new URL()을 직접 쓰면 에러 난다

// 이건 에러
const url = new URL('/search?q=hello');

// 이렇게 써야 한다
const url = new URL('/search?q=hello', 'https://example.com');
// 또는 window.location.origin을 base로 쓰기
const url = new URL('/search?q=hello', window.location.origin);

3. 쿼리 파라미터에 같은 키가 여러 번 나올 수 있다

?color=red&color=blue&color=green 같은 URL은 완전히 유효하다. 이 경우 searchParams.get('color')는 첫 번째 값만 반환한다. 모든 값을 가져오려면 searchParams.getAll('color')를 써야 한다.

const url = new URL('https://example.com?color=red&color=blue&color=green');
console.log(url.searchParams.get('color'));     // "red"
console.log(url.searchParams.getAll('color'));  // ["red", "blue", "green"]

4. URL 비교는 직접 문자열로 하면 안 된다

https://example.comhttps://example.com/은 다른 문자열이지만 사실상 같은 URL이다. 비교할 때는 URL 객체로 파싱한 뒤 origin, pathname 같은 속성을 비교하는 게 안전하다.

5. 포트가 기본값인 경우 생략된다

https://example.com:443/path를 파싱하면 url.port는 빈 문자열을 반환한다. 기본 포트(http는 80, https는 443)는 명시적으로 지정해도 URL 파서가 생략하기 때문이다.


Node.js에서의 URL 파싱

Node.js에서도 동일한 URL 클래스를 사용할 수 있다. 예전에 많이 쓰던 require('url').parse()는 레거시 API라서 새 코드에서는 쓰지 않는 게 좋다.

// Node.js — 최신 방식
const { URL } = require('url'); // Node 10 이전에는 필요, 이후는 전역으로 사용 가능

const url = new URL('https://api.example.com/users?role=admin&page=1');
console.log(url.hostname);               // "api.example.com"
console.log(url.searchParams.get('role')); // "admin"

Express.js에서 요청 URL을 파싱할 때는 req.query가 이미 파싱된 파라미터를 제공하지만, 전체 URL 구조를 보고 싶을 때는 new URL(req.url, 'http://localhost')처럼 쓸 수 있다.


정리

URL은 단순한 문자열이 아니다. 프로토콜, 인증 정보, 호스트, 포트, 경로, 쿼리, 해시라는 7가지 구성 요소가 각자의 역할을 맡고 있는 구조화된 데이터다.

개발 중 URL 관련 버그는 대부분 이 구조를 제대로 이해하지 못하거나, 인코딩 처리를 빠뜨릴 때 생긴다. 특히 fragment는 서버로 전달되지 않는다는 점, 같은 키의 파라미터가 여러 개일 수 있다는 점을 기억해두면 디버깅 시간을 많이 아낄 수 있다.

JavaScript의 내장 URL API는 생각보다 강력하다. 외부 라이브러리 없이도 URL 파싱, 조작, 인코딩 처리를 깔끔하게 할 수 있으니 적극 활용하자.

자주 묻는 질문

이 글 공유하기

XLinkedIn

관련 글