URL/URI 구조 완벽 해부

scheme·authority(userinfo/host/port)·path·query·fragment 구성요소, 퍼센트 인코딩, reserved vs unreserved 문자, IDN/punycode, 상대 vs 절대, 정규화를 해부합니다.

URL은 웹 자원의 주소이지만, 사실은 잘 정의된 여러 구성요소가 정해진 순서로 조립된 구조체입니다. 그 문법은 RFC 3986이 규정하며, 정확히 이해하면 라우팅·인코딩·보안 문제 대부분이 명료해집니다. 먼저 용어부터 정리하면, URI는 자원을 식별하는 상위 개념이고 URL은 그중 '위치까지 알려주는' 것입니다. 일상에서는 대체로 같은 의미로 씁니다.

다섯 구성요소

RFC 3986의 일반 문법은 scheme://authority/path?query#fragment 입니다. 아래 예시로 각 부분을 짚어 보겠습니다.

https://user:pass@api.example.com:8443/v1/items?sort=asc&page=2#section-3
\___/   \_______/ \_____________/ \__/ \_______/ \_____________/ \_______/
scheme  userinfo      host        port    path        query       fragment
              \__________________________/
                     authority

퍼센트 인코딩과 문자 집합

URL에는 아무 문자나 넣을 수 없습니다. RFC 3986은 문자를 두 부류로 나눕니다. unreserved 문자(A-Z a-z 0-9 - . _ ~)는 어디서든 그대로 써도 됩니다. reserved 문자(: / ? # [ ] @ ! $ & ' ( ) * + , ; =)는 구분자로서 특별한 의미를 가지므로, 데이터로서 그 문자를 넣으려면 퍼센트 인코딩해야 합니다. 예를 들어 공백은 %20, &%26, 한글 '한'은 UTF-8 바이트를 인코딩한 %ED%95%9C이 됩니다.

핵심은 문맥에 따라 인코딩 규칙이 다르다는 점입니다. 같은 /라도 path 구분자로 쓰면 그대로 두지만, 쿼리 값 안에 데이터로 넣으려면 %2F로 인코딩해야 합니다. 그래서 브라우저·언어 표준 라이브러리는 '경로 세그먼트용'과 '쿼리 컴포넌트용' 인코딩 함수를 따로 제공합니다. 잘못된 함수를 쓰면 +(쿼리에서 공백을 뜻하기도 함)나 /가 오해석되는 버그가 생깁니다.

IDN과 punycode

도메인에 비ASCII 문자(한글·한자·이모지 등)를 쓰는 것이 국제화 도메인 이름(IDN) 입니다. 그런데 DNS는 전통적으로 ASCII만 다루므로, 유니코드 도메인은 punycode라는 방식으로 ASCII 표현으로 변환됩니다. 변환된 라벨에는 xn-- 접두사가 붙습니다. 예를 들어 한국.example의 한글 라벨은 xn--3e0b707e처럼 인코딩됩니다. 시각적으로 비슷한 문자를 섞어 가짜 도메인을 만드는 호모그래프 공격의 위험 때문에, 브라우저는 상황에 따라 주소창에 유니코드 대신 punycode를 표시하기도 합니다.

상대 vs 절대, 그리고 정규화

절대 URL은 스킴부터 다 갖춘 완전한 주소이고, 상대 URL(../images/logo.png, ?page=2)은 현재 문서의 기준(base) URL에 대해 해석됩니다. 브라우저는 RFC 3986의 참조 해석(reference resolution) 규칙으로 상대 URL을 절대 URL로 풉니다.