웹 상의 모든 리소스는 URI로 가르킬 수 있다. 또한 HTTP로 조작할 수 있다. HTTP와 URI는 웹을 성공으로 이끈 요인 중 하나이다. 지금부터는 URI의 스펙과 더 나은 URI의 설계를 위한 방법에 대해 알아보자.
웹의 핵심 기술인 URI의 스펙에 대해 알아본다. URI의 스펙을 바르게 이해하는 것은 쓰기 편한 웹 서비스와 웹 API로 가는 첫걸음이다.
Uniform Resource Identifier,
직역시 '유니폼 리소스 식별자'인 URI는 '리소스를 통일적으로 식별하는 ID'를 말한다. 통일적이란 모두가 같은 규칙을 따르고 있다는 것을 말하고, 식별자란 어떤 것을 다른 것과 구별하기 위한 이름 또는 Id를 말한다.
URI를 사용하면 웹상의 모든 리소스를 통일된 방식으로 보여줄 수 있으며, URI가 있으면, 모든 리소스에 간단하게 접속할 수 있게 된다. 이를 실현하기 위한 비밀이 바로 URI 구문에 있다.
구문을 살펴보자, 참고로 예시의 URI의 스펙은 RFC3986이다.
http://blog.example.com/entries/1
이 URI를 구성하는 파트는 다음과 같다
URI는 URI 스키마로 시작된다. 이는 그 URI가 이용하는 프로토콜을 나타낸다. 이 예에서는 HTTP로 접근할 수 있다는 것을 나타낸다.
다음은 ://
로 구분된다. (스키마와 사용자 정보, 호스트명의 시작을 구분한다.)
그 다음은 호스트명이 나타나는데 호스트명은 DNS(Domain Name System)에서 이름을 해석할 수 있는 도메인 명이나 IP어드레스로 인터넷에서 반드시 일의성을 가지게 된다.
호스트명 뒤엔 계층화를 나타내는 경로가 이어지고 경로는 호스트 안에서 오로지 하나의 리소스를 가르킨다.
이처럼 넷상에서 반드시 유일한 호스트명의 구조와 호스트 내에서 유일한 계층적인 경로를 결합함으로써, 어떤 리소스의 URI가 전 세계의 다른 리소스와 절대로 중복되지 않도록 되어있다.
http://yohei:pass@blog.example.com:8000/search?q=test&debug=true#n10
이 URI또한 다음과 같이 나눌 수 있다.
앞의 예에선 없던 부분을 차례대로 살펴보자
우선, URI 스키마 다음에 사용자 정보가 들어있다. 이는 이 리소스에 접근할 때 이용할 사용자 이름과 패스워드로 구성된다. 이름과 패스워드는 :
로 구분한다.
사용자 정보 다음에 구분 문자인 @
가 있고, 그 뒤에 호스트 정보가 이어진다. 호스트 정보는 호스트명과 포트번호로 구성되며, 이 둘은 :
로 구분된다. 포트번호는 이 호스트에 엑세스 할 때, 프로토콜이 사용할 TCP의 포트번호를 나타낸다. 포트번호를 생략하면 각 프로토콜의 디폴트 값이 사용된다. (예 : HTTP - 80)
패스 뒤엔 ?
구분문자가 오고, 이름 = 값
형식으로 된 쿼리가 이어진다. 이 예에서는 q=test
와 debug=true
가 각각 쿼리이다. 쿼리가 여러 개 존재할 때는 &
로 연결한다. 이 하나 이상의 쿼리의 집합을 '쿼리 파라미터' 또는 '쿼리 문자열' 이라고 부른다. 쿼리 파라미터는 검색 서비스에 검색 키워드를 전달 할 때 등 클라이언트에서 동적으로 URI를 생성할 때 사용한다.
마지막으로 #
으로 시작되는 문자열은 URI 프래그먼트 라고 한다. 이는 #
앞에 문자열로 표현한 URI가 가르키는 리소스 내부에서 더 세세한 부분을 특정할 때 이용한다. 예를 들어 이 리소스가 HTML문서인 경우에는 id 속성 값이 'n10'인 요소를 나타내게 된다.
OS의 파일 시스템처럼 URI에도 절대 URI와 상대 URI가 있다. 상대 URI는 URI 스키마와 호스트명을 생략하고, 경로만으로 표현한다.
절대 URI
http://example.com/foo/bar
상대 URI
foo/bar
상대 URI의 기준이 되는 URI가 어디인지 알 수 없어서 상대 URI만 가지고는 클라이언트가 바르게 해석할 수 없다.
때문에 기점이 되는 URI를 지정하는 것이 Base URI이다. 예를 들어, Base URI를 http://example.com/foo/bar라고 가정해보자.
상대 경로와 마찬가지로 .
와 ..
를 사용할 수 있을 뿐아니라, 쿼리 파라미터와 URI 프래그먼트도 상대 URI로서 사용할 수 있다. 또한, /
로 시작되는 상대 URI는 호스트 명부터 시작되는 경로로 해석한다. (/hoge/fuga 의 예)
이처럼, 상대 URI를 절대 URI로 변환하기 위해서는 Base URI가 필요하다.
이제 대표적인 2가지의 URI 부여 방식을 살펴보자.
상대 URI가 출현하는 리소스의 URI를 Base URI로 부여하는 것은 직관적이다. 어떤 리소스를 가져왔을 때 상대 URI가 등장하면, 그 리소스의 URI를 Base URI로 하고 상대 URI를 해석한다.
이해가 쉬운 장점이 있지만 Base URI가 되는 리소스의 URI를 클라이언트 측에서 가지고 있어야만 한다는 문제가 있다.
예를 들어, 웹 페이지를 HTML로 가지고 있을 때, 그 HTML 파일이 원래 어느 URI이었는지 알 수 없다. 이말은 즉, 그 HTML 파일에 포함되어 있는 상대 를 해석할 수 없다는 의미이다.
위 방법의 문제를 해결하는 방법 중 하나가 HTML과 XML 안에서 명시적으로 Base URI를 지정하는 방법이다.
HTML의 경우 <head>
요소 안에 <base>
요소를 삽입한다.
XML 문서는 xml:base 속성을 이용하면, 어느 요소에서나 Base URL를 지정할 수 있다.
URI 스펙에 따르면 다음과 같은 문자를 URI 경로에 사용할 수 있다.
이 문자열은 소위 ASCII 문자다. 즉, URI에는 그 밖의 문자는 직접 입력할 수 없다.
한글이나 일어 등 ASCII 이외의 문자를 URI에 넣을 때는 %인코딩이라는 방식을 사용한다.
URI 스펙에서 허용하는 문자 이외의 것을 입력하기 위해서는 %인코딩으로 그 문자를 인코딩 해야 한다.
http://ko.wikipedia.org/wiki/가
http://ko.wikipedia.org/wiki/%EA%B0%80
한글이 포함된 URI는 실제로는 다음의 문자열로 전개되어 브라우저와 서버 사이에 전송된다. 단 한글자였던 '가'가 '%EA%B0%80'이 되었다. 이것은 '가'라는 문자가 UTF-8 에서는 0xEA, 0xB0, 0x80의 3바이트로 이루어지는 것에 기인한다.
%인코딩에서는 UTF-8 문자를 구성하는 각 바이트를 '%xx(xx는 16진수)'로 기술하여 URI에 사용할 수 없는 문자를 표현한다.
일반적으로 알파벳 대소문자를 구별해 사용하지만, %인코딩에 사용하는 문자는 대소문자가 같은 의미를 가지게 되어 있다. 하지만 대문자 사용이 권장되어진다.
덧붙여 '%'를 사용하려면 '%25'라고 표기해야 한다.
서버가 제공하는 URI를 그대로 다루는 경우는 서버가 %인코딩하고 있으므로 문제가 없지만, 클라이언트 쪽에서 폼을 사용해 URI를 생성하는 경우엔 문제가 생긴다. 유저가 입력한 문자를 어느 문자 인코딩을 사용해 %인코딩 할 것인지를 브라우저 같은 클라이언트가 판단할 수 없기 때문이다.
일반적으로는 그 폼을 제공한고 있는 웹 페이지의 문자 인코딩을 사용함으로 해결한다. 즉, UTF-8로 기술한 폼이라면 UTF-8로, EUC-KR로 기술한 폼이라면 EUC-KR로 URI를 만든다. 단, 이 방식은 프록시에서 문자 인코딩을 변환하는 경우나 근본이 되는 웹페이지의 문자 인코딩 범위 외의 문자가 주어진 경우 등에선 오류가 난다.
스펙상으로 URI의 길이 제한은 없다. 그러나 구현상으로는 제한이 존재한다. Internet Explorer는 2038바이트라는 제한이 있어 이 길이에 맞추어 구현하는 일이 많아지고 있다.
크롬은 2MB (2048 바이트) 제한을 둔다.
URI 스키마의 공식적 목록은 IANA(Internet Assigned Numbers Authority)에 있고, 2010년 70개 현 기준(2022.3)으로는 여기에서 살펴볼 수 있다.
역사적으로는 HTTP에 대응한 http 스키마가 먼저 탄생했고, 그 뒤를 다양한 프로토콜에 대응한 스키마가 등록되어 왔다.
URI 스키마를 새로 발명하는 것은 어리석다. 기존의 스키마를 지정하면 되기 때문이다. 그래서 URI 스키마를 새로 등록해야만 하는 경우는 기존에 없는 프로토콜을 발명했을 때 정도다.
웹 서비스와 웹 API를 구현함에 있어서 URI의 스펙상 주의해야 할 것은 상대 URI의 해석과 %인코딩을 다루는 2가지 사항이다.
클라이언트에서 상대 URI를 해석하기 위해 번거로운 처리가 필요하기 때문에, 웹 서비스와 웹 API를 구현하는 경우에는 가능한 절대 URI를 사용하는 편이 클라이언트에 도움이 된다.
또한, URI에 ASCII 이외의 문자를 넣을 경우에는 문자 인코딩의 혼란을 피하기위해 %인코딩의 문자 인코딩으로 될 수 있는 한 UTR-8을 이용한다.