URL
고친 사람 github-actions[bot]
URL 은 인터넷에 있는 것을 어디서 어떻게 가져올지 한 줄의 글자로 알려 줍니다. 브라우저 주소창에 붙여 넣는 그 한 줄이 URL 입니다. 프로그램은 이 한 줄만 받으면 무엇을 어디서 가져올지 스스로 알아냅니다.
쉽고 빠른 이해
무슨 일을 하나 — 가져올 대상과 가져오는 길을 한 줄에 같이 담습니다. https://shop.example.com/items/42 는 어느 서버의 어느 상품을 어떤 방법으로 가져올지를 한 줄로 말합니다.
왜 필요한가 — 주소를 적는 방식이 프로그램마다 다르면 링크를 주고받을 수 없습니다. 모두가 같은 순서로 적으니 브라우저도 서버도 캐시도 같은 줄을 같은 대상으로 읽습니다.
어떻게 도나
- 맨 앞의 이름이 어떤 방법으로 가져올지 정합니다
- 그 뒤의 서버 이름과 포트가 어디에 물을지 정합니다
- 경로와 물음표 뒤의 조건(쿼리 문자열)이 그 서버 안에서 무엇을 달라고 할지 정합니다
대가 — 쓸 수 있는 글자가 정해져 있어 한글과 공백은 % 와 숫자로 바꿔 적어야 합니다. 한 줄에 서버 이름이 박혀 있어서 서버를 옮기면 예전 줄이 끊깁니다.
언제 안 쓰나 — 가져올 길이 필요 없고 이름만 필요하면 URL 대신 이름만 담는 줄을 씁니다. 그런 줄을 URN(Uniform Resource Name, 자원 이름)이라고 부릅니다.
상세
이 절은 주소 한 줄을 쪼개 봅니다.
URL 은 Uniform Resource Locator 의 줄임말입니다. 우리말로는 자원 위치 지정자입니다. 자원은 가리킬 수 있는 무엇이든입니다. 웹 페이지 한 장, 이미지 파일 하나, 서버가 그때그때 만들어 주는 검색 결과 한 덩이가 전부 자원입니다.
이름표 노릇만 하는 식별자와 갈리는 대목은 가져오는 길이 들어 있다는 것입니다. URI(Uniform Resource Identifier, 통합 자원 식별자)는 무언가를 한 줄로 가리키는 이름표를 통틀어 부르는 이름입니다. 그중에서 가져오는 방법까지 담은 것이 URL 입니다. 이름만 담고 어디서 받는지는 말하지 않는 URN이 그 반대편입니다. 가져올 길이 필요 없고 이름만 필요한 곳에서는 URL 대신 이쪽을 씁니다.
적는 순서가 모두에게 같다는 것이 URL 의 값입니다. 주소를 적는 방식이 프로그램마다 다르면 링크 하나를 주고받을 때마다 옮겨 적어야 합니다. 순서가 하나로 정해져 있으니 브라우저가 만든 줄을 서버가 읽습니다. 서버가 보낸 줄은 캐시가 다시 같은 대상으로 읽습니다.
한 줄이 나뉘는 조각
URL 은 정해진 순서로 놓인 조각으로 읽힙니다. 조각마다 앞이나 뒤에 붙는 기호가 정해져 있어서, 읽는 프로그램은 기호만 보고 어디까지가 어느 조각인지 압니다.
아래 그림과 표는 https://shop.example.com:8443/items/42?color=red#reviews 한 줄을 쪼갠
것입니다. 그림은 조각이 어떻게 묶이는지를, 표는 조각마다 무엇을 하는지를 보입니다.
flowchart TD
U["https://shop.example.com:8443/items/42?color=red#reviews"]
subgraph OR["오리진"]
S["스킴 · https"]
subgraph AU["authority"]
H["호스트 · shop.example.com"]
P["포트 · 8443"]
end
end
U -->|"뒤에 :"| S
U -->|"// 뒤"| H
U -->|"앞에 :"| P
U -->|"/ 로 시작"| PT["경로 · /items/42"]
U -->|"앞에 ?"| Q["쿼리 문자열 · color=red"]
U -->|"앞에 #"| FR["프래그먼트 · reviews"]
한 줄은 기호를 경계로 여섯 조각으로 갈립니다. 호스트와 포트를 묶은 부분은 authority 라고 부릅니다. 여기에 스킴까지 더한 셋을 묶은 것이 오리진이고, 브라우저는 이 묶음으로 보안 경계를 긋습니다.
| 조각 | 앞뒤 기호 | 이 예의 값 | 하는 일 |
|---|---|---|---|
| 스킴 | 뒤에 : |
https |
어떤 규칙으로 가져올지 고른다 |
| 호스트 | // 뒤 |
shop.example.com |
어느 서버에 물을지 정한다 |
| 포트 | 앞에 : |
8443 |
그 서버의 어느 문으로 붙을지 정한다 |
| 경로 | / 로 시작 |
/items/42 |
서버 안에서 무엇을 달라고 할지 고른다 |
| 쿼리 문자열 | 앞에 ? |
color=red |
고른 대상에 조건을 덧붙인다 |
| 프래그먼트 | 앞에 # |
reviews |
받아 온 것 안의 한 부분을 가리킨다 |
여섯 조각이 다 있어야 하는 것은 아닙니다. 스킴은 어느 줄에나 있고, 웹 주소라면 호스트도 있어야
합니다. 포트를 안 적으면 스킴이 정해 둔 기본 포트로 붙습니다. 경로가 비면 / 를 적은 것과 같이
읽힙니다.
조각이 요청으로 바뀌는 순서
앞의 https://shop.example.com:8443/items/42?color=red#reviews 를 주소창에 넣었다고 하고, 그
줄이 요청 하나가 되기까지를 봅니다. 아래 그림의 상자 둘은 각 단계가 어디서 벌어지는지를 가릅니다.
flowchart TD
subgraph ME["내 컴퓨터"]
B["URL 한 줄을 조각으로 쪼갠다"]
C["스킴이 어떤 규칙으로 가져올지 정한다"]
D["호스트 이름을 숫자 주소로 바꾼다"]
E["그 주소의 포트로 연결한다"]
F["경로와 쿼리 문자열을 적어 보낸다"]
G["받아 온 문서에서 프래그먼트가 가리킨 곳으로 옮긴다"]
end
subgraph SV["서버"]
R["요청을 받아 돌려준다"]
end
B --> C --> D --> E --> F --> R --> G
앞의 세 조각은 어디에 붙을지를 정합니다. 뒤의 조각들은 붙은 다음 무엇을 달라고 할지를 정합니다. 호스트는 사람이 읽는 이름이라 그대로 연결할 수 없습니다. DNS(Domain Name System, 도메인 이름 체계)가 그 이름을 서버의 숫자 주소로 바꿔 줍니다.
프래그먼트만 차례가 다릅니다. # 뒤는 서버로 보내지 않습니다. 받아 온 문서 안에서 그 부분을
찾아 화면을 옮기는 일은 브라우저가 합니다.
그림에서 마지막 칸이 서버 상자를 안 지나는 까닭입니다. 서버 기록에는 # 뒤가 남지 않습니다.
쓸 수 있는 글자와 퍼센트 인코딩
URL 에 그대로 쓸 수 있는 글자는 영문자와 숫자, 그리고 몇몇 기호뿐입니다. 한글과 공백은 넣을 수 없습니다.
넣을 수 없는 글자는 퍼센트 인코딩으로 바꿔 적습니다. 글자를 먼저 바이트로 바꿉니다. 그
바이트마다 % 와 16진수 두 자리로 적습니다.
바이트를 얻는 규칙이 UTF-8(Unicode Transformation Format 8-bit)입니다. 글자 하나를 바이트 몇 개로 적을지 정해 둔 규칙이고, 글자마다 그 개수가 다릅니다.
flowchart TD
A["한"]
subgraph AB["UTF-8 바이트 셋"]
A1["ED"]
A2["95"]
A3["9C"]
end
A --> AB --> AR["%ED%95%9C"]
S["공백"]
subgraph SB["UTF-8 바이트 하나"]
S1["20"]
end
S --> SB --> SR["%20"]
한 은 바이트가 셋이라 퍼센트가 세 번 붙습니다. 공백은 바이트가 하나라 한 번입니다.
/ ? # : @ 처럼 조각을 가르는 기호는 예약 문자입니다. 값 안에 이런 기호가 들어가야
하면 그때도 퍼센트 인코딩으로 바꿉니다. 쿼리 값에 # 을 그대로 두면 읽는 프로그램은 거기서
프래그먼트가 시작된 것으로 봅니다.
앞부분 셋이 보안 경계가 된다
앞에서 묶은 오리진이 그 경계입니다. 브라우저는 스킴과 호스트와 포트 셋이 모두 같을 때만 같은
곳에서 온 것으로 봅니다. 아래 표는 https://shop.example.com/a 를 기준으로 견준 것입니다.
| 견주는 줄 | 같은 오리진인가 | 갈린 조각 |
|---|---|---|
https://shop.example.com/b/c |
✓ | 없다. 경로는 안 센다 |
http://shop.example.com/a |
✗ | 스킴 |
https://api.shop.example.com/a |
✗ | 호스트 |
https://shop.example.com:8443/a |
✗ | 포트 |
경로는 오리진에 안 들어갑니다. 한 서버가 여러 사람의 페이지를 경로로 갈라 담으면, 그 페이지들은 브라우저가 보기에 전부 한 곳입니다.
동일 출처 정책이 이 경계로 막습니다. 다른 오리진에서 온 문서가 내 문서의 내용을 읽지
못하게 하는 규칙입니다. 위 표의 http://shop.example.com/a 는 스킴 하나만 달라도 다른
오리진이라, 그 줄에서 온 문서는 https 쪽 문서의 내용을 읽지 못합니다.
사이트 격리는 한 걸음 더 나아갑니다. 주소의 앞부분이 다른 문서를 아예 다른 프로세스에 담습니다. 규칙으로 막는 대신 그릇을 갈라 두는 것이라, 브라우저가 규칙을 어기게 되는 허점이 생겨도 읽을 내용이 그 프로세스의 메모리에 없습니다.
규칙이 둘이다
URL 을 어떻게 적고 어떻게 읽을지 정한 문서는 하나가 아닙니다. 널리 따르는 것이 둘입니다.
RFC(Request for Comments) 문서 가운데 RFC 3986 이 URI 문법을 정합니다. 그 문법에 안 맞는 줄은 URI 가 아닙니다.
WHATWG(Web Hypertext Application Technology Working Group)가 내는 WHATWG URL 표준은 다른 각도로 적습니다. 브라우저가 만나는 줄에는 문법에 안 맞는 것이 섞입니다. 그래도 브라우저는 무언가를 열어야 합니다.
그래서 이 표준은 올바른 문법 대신 읽는 절차를 정합니다. 어떤 줄을 넣어도 같은 결과가 나오도록 단계마다 무엇을 고쳐 읽을지가 정해져 있습니다.
두 규칙은 틀린 줄을 다룰 때 갈립니다. http 로 시작하는 줄 안의 역슬래시를 WHATWG 쪽은 / 처럼
읽습니다. RFC 3986 에서 역슬래시는 URI 에 쓸 수 없는 글자입니다.
웹 요청은 서버 한 대만 지나지 않을 때가 많습니다. 앞에 선 중계 프로그램(앞단)이 먼저 받아 검사한 뒤 뒤의 서버(뒷단)로 넘깁니다.
이 갈림은 한 줄을 앞단과 뒷단이 다르게 쪼갤 때 아픕니다. 앞단이 역슬래시를 / 로 읽고 뒷단이
안 그러면, 앞단의 검사를 통과한 요청이 뒷단에서는 다른 호스트를 가리킵니다.
관련 항목
URL 한 줄을 이루는 조각
스킴 · 호스트 · 포트 · 경로 · 쿼리 문자열 · 프래그먼트 · 사용자 정보 · authority
URL 을 품는 상위 이름과 그 갈래
URI · URN · IRI · 데이터 URI · 상대 URL · 절대 URL
URL 을 정하는 표준 문서
RFC 3986 · RFC 1738 · WHATWG URL 표준 · WHATWG · RFC 3987
URL 에 글자를 담는 인코딩 규칙
퍼센트 인코딩 · 예약 문자 · ASCII · UTF-8 · 퓨니코드 · 국제화 도메인 이름
URL 앞부분으로 긋는 보안 경계
오리진 · 동일 출처 정책 · 사이트 격리 · CORS · 공개 접미사 · 오픈 리다이렉트
URL 을 읽어 쓰는 프로그램과 규칙
다른 이름: Uniform Resource Locator · 자원 위치 지정자