Java-Spring 환경에서 URL을 다루다가, URL 인코딩이 두번 중복해서 수행되는 문제를 접하고, URL 인코딩이 무엇인지 제대로 파헤치기 위해 이 글을 작성합니다.
URL 인코딩이란, URI에서 구분자로 사용되는 예약어들과, 예약어와 동일한 문자이지만 단순한 문자로서 사용되는 것들을 구분하여, 혼동을 없애기 위해 도입된 Spec이다. 예약어와 동일하지만 구분자 역할을 수행하지 않는 문자를 ‘%’ + ‘16진수’ + ‘16진수’ 형태( ex. %25 )로 인코딩한다.
URI에서는 특수한 목적을 위해 사용될 예약어들을 정의해 두었다. 만약 URI 컴포넌트에서 예약어들이 포함될 경우, 해당 문자가 URI 컴포넌트들을 구분하는 ‘구분자’의 역할을 하는 녀석인지, 아니면 단순한 문자인지를 알 수가 없다. 이러한 혼동을 막기위해 도입된 개념이 URL 인코딩이다. 정확하게는 Percent-encoding 이라고 말한다. URI에서 예약어로 사용되는 문자는 아래와 같다.
문자 | 인코딩 |
: (콜론) | %3A |
/ (슬래시) | %2F |
? | %3F |
# | %23 |
[ | %5B |
] | %5D |
@ | %40 |
! | %21 |
$ | %24 |
& | %26 |
‘ (작은 따옴표) | %27 |
( | %28 |
) | %29 |
* | %2A |
+ | %2B |
, (쉼표) | %2C |
; (세미콜론) | %3B |
= | %3D |
% (퍼센트) | %25 |
띄어쓰기 | %20 or + |
예를 들어, URI가 'http://hello.com'(schme과 도메인) 그리고 '/world?/test'(경로) 그리고 'key=123&value=a/b'(쿼리)로 구성되있다고 가정하자. 위에 세 URI 컴포넌트를 URL 인코딩 없이 합치게 되면 아래와 같은 URI가 만들어진다.
http://hello.com/world?/testkey=123&value=a/b
의도는 쿼리 파라미터로 key = 123 과 value = a/b 가 될 것을 예상하였겠지만, 경로 컴포넌트에 포함된 '?'가 URL 인코딩 되지 않아, 경로와 쿼리를 구분하는 구분자로서 인식될 것이다. 즉, /testkey = 123 과 value = a/b 의 쿼리로 인식될 것이다.. URL 인코딩을 수행하지 않으면 이러한 혼동이 발생한다.
URL 인코딩을 수행한 결과는 아래와 같다.
http://hello.com /world%3F/test?key=123&value=a/b
? 가 %3F 로 인코딩되었다. 이때, 인코딩은 URI 컴포넌트들을 모두 조합한 이후 수행하는 것이 아니라, 각각의 컴포넌트에 대해 인코딩을 수행한 이후, 컴포넌트를 조합한다는 점을 유의하자. URL 인코딩을 수행하는 구현체를 사용할 때, 위 사항을 유의하면서 개발해야 한다.
URL 인코딩을 다룰 때 유의할 점!
어떤 문자가 인코딩 되는가?
일반적으로 'ASCII' 문자에 해당되는 알파벳은 인코딩되지 않으며, 예약된 문자 또는 ASCII에 포함되지 않는 문자(ex. 유니코드) 가 URL 인코딩된다. RFC 3986에 따르면, 인코딩 시, UniCode에 해당되는 문자라면 먼저 UTF-8로 인코딩되어야 하고, 이후 UniCode에 포함되지 않으면서 예약어인 문자가 Percent-encoding 되어야 한다고 말한다.
예약어 문자는 각 URI 컴포넌트 마다 다르다
경로 컴포넌트에서 ' '(공백) 은 '+'가 아닌, '%20'으로 인코딩되어야 하며, '+' 문자는 인코딩되지 않는다.
반면, 쿼리 컴포넌트에서 ' '(공백) 은 '+' 또는 '%20'으로 인코딩될 수 있지만, '+'는 '%2B'로 인코딩되어야 한다. 이처럼, 각 컴포넌트 마다, 예약어가 인코딩되는 방식과 인코딩되어야 하는 예약어가 다를 수 있다.
URI 컴포넌트 마다 이스케이프 되지 않아도 되는 예약어가 다르다
- 경로 컴포넌트에서, '+'는 이스케이프 되지 않는다.
- query 컴포넌트에서, '?' , '/' 는 이스케이프 되지 않는다.
- '경로 파라미터' or '쿼리 파라미터의 value' or '경로 세그먼트'에서, '=' 는 이스케이프 되지 않는다.
더 구체적인 사항은 아래와 같다. 아래 Value는 각 컴포넌트에서 인코딩하지 않고 사용가능한 예약어를 나타낸다. (Scheme 과 Host 는 무시하자.)
URL 내부에 예약어가 존재한다고 해서, 무작정 URL 인코딩해야 하는 것이 아니다. 각 URI 컴포넌트에서 이스케이프 되어야하는 예약어 일때 URL 인코딩을 수행해야 한다.
그 외 유의할 점
- 디코딩된 이후, URL은 정상적으로 분석되지 않을 수 있다. 디코딩 이후에는 각 URI 컴포넌트 구분이 달라질 수 있기 떄문이다.
- 디코딩된 URL은, 동일한 형태로 다시 인코딩되지 못할 수 있다.
참고자료
'CS > 네트워크' 카테고리의 다른 글
[네트워크] 컴퓨터 네트워크와 기본 장치들 (0) | 2024.01.22 |
---|---|
[HTTP] 17. 내용 협상과 트랜스코딩 (0) | 2024.01.17 |
[HTTP] 16. 국제화 (0) | 2024.01.17 |
[HTTP] 15. 엔터티와 인코딩 (0) | 2024.01.16 |
[HTTP] 11. 클라이언트 식별과 쿠키 (0) | 2024.01.16 |