본문 바로가기

Web.d

[Web][Network] About URI Encoding & Decoding

반응형
목차
1️⃣ 서론
2️⃣ URI와 URL
3️⃣ Encoding은 어떤 동작인가요?
4️⃣ JavaScript에서의 URI Encoding(, Decoding)
5️⃣ 글을 줄이며

1️⃣  서론

URL은 웹 개발에 빼놓을 수 없는 페이지 정보의 주소값입니다.

URL에 우리가 설정하는 Domain이 녹아있을 수 있고, 라우팅을 위한 path 정보, Permalink를 위한 query string 등 페이지 정보의 이름표 역할을 하죠.

 

로그인 로직을 처리하는 웹 서버에서 query string에 다음 페이지 링크인 next 정보를 담으며 Redirect를 위한 URL을 주고받는 로직을 구현하였습니다. (예를 들어, "https://www.domain.com/login?next=%2Fproducts%2F8LN9SRrhDMunA3dJ9Wjcbx2DM9Z8YKjKC" 와 같은 permalink를 구성하기 위한 작업입니다.)

이 과정에서 한껏 알아보고 쌓아둔 Encoding 동작에 대해 알아보고자 합니다.

먼저, URL과 URI에 대해 간단히 짚어보며 시작하겠습니다.

 

2️⃣  URI와 URL

개념에 대해서는 설명이 잘 되어있는 🔗타링크 또한 첨부합니다.

 

> URI; Uniform resource Identifier

네트워크 상에서 자원 위치를 알려주기 위한 규약입니다. 이는 인터넷에서 요구되는 기본 조건입니다.

URL(~ locator), URN(~ name)의 상위개념입니다.

즉, 자원의 이름과 주소 두 정보를 모두 포함할 수 있습니다.

 

> URL; Unirom Resouce Locator

인터넷에 있는 자원을 나타내는 유일한 (물리적인) 주소입니다.

달리 보면, 비영구적인 URI의 종류라고 볼 수 있습니다.

항상 인터넷 프로토콜에 붙어 있습니다. URI보다 더 정확한 개념으로, 혼란이 없기 때문에 가장 흔히 사용합니다.

 

하지만, URL 주소가 바뀌면 리소스를 사용하지 못한다는 단점에서 착안되어, URN이라는 표준 작업이 착수되고, PURL(Persistent URL) 개념 등이 나오고 있습니다. 하지만 URL → URN 주소 체계를 바꾸는 것은 쉬운 작업이 아니기 때문에, 당분간은 URL이 계속해서 주로 사용될 전망입니다.

 

[Fig.1]   URL Component can have scheme, authority, path, query, fragment, …
[Fig.2] URL Components

 

예시를 들어 알아볼까요?

  1. https://example.com/foo 
    이는 URL이기도, URI이기도 합니다.
  2. https://example.com/123  (URI, no URL)
    URL은 "https://example.com" 까지입니다.
    내가 원하는 정보에 도달하기 위해 123이라는 식별자가 필요하기 때문에 URI이지만 URL은 아닌 것이죠.
  3. https://example.com/one?id=123  (URI, no URL)
    URL은 "https://example.com/one" 까지입니다.
    같은 이유입니다. 내가 원하는 정보에 도달하기 위해서는 ?id=123이라는 식별자가 필요하기 때문입니다.

 

3️⃣  Encode는 어떤 동작인가요?

다음 두 문자열의 차이점을 보면,

encoding이 어떤 것인지, encoding을 안 했을 때에 어떤 문제점이 있을지 감이 오시리라 생각합니다.

 

const serverError = `Authentication backend has internal error: {"error":"아이디가 존재하지 않습니다."}`;

1. not encoded
`Authentication%20backend%20has%20internal%20error:%20{"error":"아이디가%20존재하지%20않습니다."}`

2. encoded
`Authentication%20backend%20has%20internal%20error%3A%20%7B"error"%3A"아이디가%20존재하지%20않습니다."%7D`

 

> Encode?

신뢰성 있는 자료들을 인용하며 살펴보겠습니다.

In a URI or a URL, characters that have a special purpose in the context of one or more URI or URL components are known as reserved characters. For example, the characters /, ?, &, and : are used as delimiters for various components. Machine interpreters might misinterpret the URI or URL if the reserved characters are used for any reason. Also, certain characters are disallowed, or excluded, from use anywhere in a URI or URL, either because they are a potential cause of confusion for machine or human users, or because they are known to cause problems for some machine interpreters. For example, the space character is not permitted in a URL.
- IBM

 

=

/, ?, &, : 문자와 같은 문자는 URI, URL 컨텍스트에서 "예약 문자"로 정의됩니다.

예약 문자를 사용하게 된다면, 기계 또는 사용자에게 혼란을 줄 수 있거나 URL, URI, 혹은 그 너머의 해석기에 문제를 일으킬 수 있습니다. 이와 같은 이유로 공백 문자가 URL에서 허용되지 않습니다.

 

The space character is excluded because significant spaces may disappear and insignificant spaces may be introduced when URI are transcribed or typeset or subjected to the treatment of word- processing programs. Whitespace is also used to delimit URI in many contexts.
- IETF(국제 인터넷 표준화 기구) RFC 2936, section 2.4.3:

 

=

우리가 URI를 조작하거나 혹은 워드 프로세싱과 같은 다른 프로그램의 처리를 거칠 때, 중요한 공백이 사라지거나 추가될 수가 있습니다.

공백은 여러 곳에서 URI을 구분하는 데에도 사용됩니다.

 

URI(URL)은 인터넷에 있는 모든 리소스가 여러 프로토콜을 통해 전달될 수 있도록, 어떤 인터넷 Protocol을 통해서든 안전하게 전송될 수 있도록 설계되어야 했습니다. 예를 들어 이메일에 사용되는 SMTP는 특정 문자를 제거하며 전송을 하기 때문에, 안전한 알파벳 문자(7비트의 US-ASCII) 만 포함하도록, URL은 설계되었습니다. 하지만 사람들이 URL에 Binary Data, 특수기호 등을 포함하려고 했고, 이를 회피하기 위해 Escape 개념이 생겨났습니다. ••••••. 결과적으로 안전한 문자 표현의 한계를 넘기 위해, 지금의 인코딩 방식이 고안되었고, 이는 % 기호를 사용해 ASCII 코드로 표현되는 2개의 16진수 숫자. 이스케이프 문자로 바꿉니다.
- <HTTP 완벽 가이드>

 

이처럼 ASCII 문자로 구성된 URI의 특정한 문자를, UTF-8로 인코딩해 1~4개의 연속된 이스케이프 문자로 나타냅니다. (a.k.a percent-encoding)

 

이런 것을 편히 하기 위해 인코딩 방식을 애플리케이션이나, 브라우저가 모든 문자를 인코딩할 수 있겠지만,

이는 되려 위험하고 극단적인 이야기입니다. 통신 과정 혹은 해석 과정 등에서의 다른 Side Effect 위험성이 큽니다.

그전에 클라이언트 단에서 안전하지 않거나 제한된 문자를 변환하는 것이 좋다는 의견입니다. 안전하지 않은 모든 문자를 그때마다 인코딩하기만 하면, 다른 애플리케이션으로부터 특별한 의미를 가지는 문자를 받았을 때 혼동할 걱정 없이, 애플리케이션 간에 공유할 수 있는 URL의 원형을 유지할 수 있습니다.

 

4️⃣  JavaScript에서의 URI Encoding(, Decoding)

이와 같은 uri 관련 문자열을 인코딩하고 디코딩하는 JavaScript built-in API는 다음과 같습니다.

  • encodeURI() decodeURI()
  • encodeURIComponent() decodeURIComponent()

 

> encodeURI() ↔ decodeURI()

예약 문자는 인코딩하지 않습니다. (URI가 그대로 포함할 수 있는 몇 가지 문자도 인코딩 하지 않습니다.)

이를 통해 완전한 URI를 형성할 수 있습니다.

하지만 그 이유로, whole url가 아니라면, 사용에 부적합할 수 있습니다.

 

console.log(
  encodeURI('https://domain.com/path to a document.pdf')
);

// 'https://domain.com/path%20to%20a%20document.pdf'

 

> endcodeURIComponent() ↔ decodeURIComponent()

;,/?:@&=+$ 등의 예약 문자도 escape 합니다.

 

console.log(
  `http://domain.com/?search=${encodeURIComponent('encode & decode param')}`
);

// 'http://domain.com/?search=encode%20%26%20decode%20param'

 

다만, _.!~*'() 의 character는 encode 하지 않습니다.

이 이유는, encodeURIComponent는 “encode 동작”의 위함이 아니라, “URI의 구성요소로 encode 하기” 위함이기 때문입니다.

encode 되지 않는 문자들은, URI를 구성할 때의 예약어가 아니기 때문에 encode 할 필요가 없게 됩니다.

 

> What is "URI Component"?

URI Component를 위한 API는 어떤 동작을 위함일까요?

어떠한 메시지 혹은 JSON 객체의 stringify 된 문자열 등을 URI라고는 할 수 없습니다.

그렇다면 과연 query string에 넣어줄, key value 값들은  encodeURIComponent() API를 사용하는 것이 맞을까요?

위에서 살펴봤었던, 아래 코드처럼 말이죠

 

const serverError = `Authentication backend has internal error: {"error":"아이디가 존재하지 않습니다."}`;

// encoded
`Authentication%20backend%20has%20internal%20error%3A%20%7B"error"%3A"아이디가%20존재하지%20않습니다."%7D`

// -> Is it correct?
history.replace(`/fail?error=${encodeURIComponent(serverError)}`)

 

URI가 아닌 값인데, URI Component로 인코딩 해도 되는 것일까요? 라는 의문이 피어올랐습니다.

 

하지만 어원을 다시 생각해 보니, 적절한 의미일 수 있겠다는 생각이 들었습니다.

Component란 부분, 요소, 성분, 부품을 말합니다.

 

[Fig.3] uri components? (https://uri.thephpleague.com/uri/4.0/components/overview/)

 

URI Component라는 어원의 정의는 명확하게 나와있지 않습니다.

다만 은유적으로 위 사진과 같이 URI에 대한 조각조각을 URI Component라고 표현할 수 있음을 알 수 있습니다.

GPT도 괜찮다고 하더군요.

Yes, you can use "URI Component" as the value in a query string. "URI Component" is a string used in URLs, and to handle it safely, you should URL-encode special characters and spaces, among other things.
- ChatGPT

 

> 캡슐화 ❗️

URI Component를 인코딩하며 더 나아간 개념을 상상할 수 있습니다.

uri를 구성하는 정보 자체(어떠한 메시지 혹은 JSON 객체의 stringify 된 문자열 등)를 캡슐화할 수 있다는 것입니다.

예를 들어 ?next=(?next=[?next=——]~~~)  와 같이 구성한다면, (~)  로 이동한 이후에, [-]  로 이동하는 캡슐화가 가능한 것이죠. uri가 어떤 정보인지는 모른 채, redirect 하여 decode 하니, next 정보를 가지는 구성의 동작이 가능합니다. 즉, 하나의 permalink로써 순서를 가지는 next logic이 동작하게끔 구성할 수 있습니다.

 

5️⃣ 글을 줄이며

사실 해당 인코딩 및 디코딩 내용은, 우리가 웹개발하며 접할 일이 많이 없습니다.

URLSearchParams API 에서 제공하는 toString() 메소드는 인코딩하는 로직이 내부에 있고,

qs와 같이 우리가 흔히 쓰는 라이브러리에서도 인코딩을 대신 해주기 때문입니다.

 

그러나 이번에 클라이언트에서 url을 구성하고, 웹서버에서 이를 받아 특정 동작을 처리한 후에 다시 클라이언트로 넘겨줄 url을 직접 구성하다보니 해당 개념을 접하게 되었습니다.

이런 게 라이브러리에 의존하지 않을 때 얻을 수 있는 인사이트라고 생각하고, 소중한 경험이었다고 생각합니다.

한가지씩 지식을 쌓아나가는 것에 흥미를 느낍니다. 앞으로도 많은 경험을 하고 싶다는 마음이 듭니다.

반응형