문자 인코딩
- 문자 인코딩(Character encoding) 혹은 텍스트 인코딩(Text encoding) 또는 줄여서 인코딩이라 한다.
- 한국어로는 암호화(=부호화)를 의미하며, 반대말은 복호화(decoding)이라 부른다.
- 사용자가 입력한 문자나 기호들을 컴퓨터가 이용할 수 있는 신호로 만드는 것을 의미한다.
인코딩은 보통 인터넷에서 파일을 다운로드 받거나
혹은 웹 페이지 검색이나 페이지 이동 시에 주소창에 이상한 글씨들로 주소가 되어 있는 것을 본 경험이 있을 것이다.
이는 대개 표현할 수 없는 기호나 공백들을 처리할 수 있도록 인코딩을 한 형태이다.
대부분의 사용자는 인코딩의 형식을 위와 같은 경우에서 많이 보았을 것이다.
예를 들어 보자. "가 나"라는 문자열이 있다. 이 문자열의 공백을 처리하기 위해 인코딩을 진행한다면,
"%ED%95%98%EB%82%98%20%EB%91%98" (encodeURIComponent) 의 형태로 변형된다.
위의 인코딩 문자가 무엇을 뜻하는지는 모르지만 다들 한 번쯤은 보았을 것이다.
문자열을 여러 환경에서 표현하기 위해서 자바스크립트에서는 문자열을 인코딩해주는 함수가 있다.
인코딩을 왜 해야하는가에 대한 질문은 다음과 같다.
- 보안과 개인 정보 보호
- 인코딩을 통해 민감한 정보를 숨기고 보호하는 것을 목적으로 인코딩을 사용한다.
- 데이터를 암호화하고 복호화할 때에만 원래 데이터를 얻을 수 있도록 한다.
- 즉, 문자열을 다른 암호화된 문자로 변경하여 사용자가 알아볼 수 없도록 한다.
- 다양한 문자 집합 지원
- 인코딩을 통해 다양한 문자 집합을 지원한다.
- 가끔 웹 페이지에서 보는 깨진 글자들은 모두 인코딩이 제대로 이루어지지 않아서 나타나는 현상이다.
- 즉, 서로 다른 환경에서 문자열의 형식을 통일시켜주는 역할을 한다. 이는 아래 예제에서 자세히 다루도록 하겠다.
- 데이터 형식 변환
- 인코딩을 통해 데이터를 서로 다른 형식으로 변환할 수 있다.
- 예를 들어서 똑같은 동영상 파일이더라도 인코딩 방식에 따라 코덱별로 재생 여부가 다르게 지원되는 경우가 있다. 이와 같이 웹 페이지에 표시되지 않을 수 있는 데이터 형식을 변환하게 쉽게 멀티미디어를 표시할 수도 있다.
escape()
우선 escape 부터 알아보자.
지금은 오래된 구식 인코딩 방식이며, 실제로 코딩 시 escape 함수를 쓰면 가운데에 줄이 그어지며 권장하지 않는 함수라고 뜬다.
- Visual Studio Code에서 escape 함수를 사용했을 때의 화면
설명
- 문자를 인코딩할 때 사용하는 구식 인코딩 방식이다.
- RFC 1738 인코딩을 사용하기 때문에 현대 브라우저에서는 거의 사용되지 않는다.
- RFC 1738 인코딩은 HTTP URL에서 더하기 '+' 기호를 공백 ' ' 기호로 인식한다.
- 최신 브라우저에서는 escape 함수를 호출할 때 브라우저가 RFC 3986 인코딩을 적용한다.
- 아스키 코드 문자(A~Z, a~z, 0~9, @\*+-_./)를 제외한 모든 문자를 인코딩 한다.
- 인코딩 시 16진수로 인코딩되며 두 자리 %xx 혹은 4자리 %uXXXX 로 표현된다.
구문 그리고 예시
// 인코딩 (=암호화,Encoding)
escape(내용);
// 디코딩 (=복호화,Decoding)
unescape(내용);
- 예시 (html 구성 소스코드는 생략한다.)
let str = "ABC/abc/123/@\\*+-_./";
let addStr = ";?:&=$,#!~'()"
let url = "https://luvris2.tistory.com/"
html += "<h2> 원본 : </h2>" + str + "<br>";
html += "<h2> escape 아스키 문자열 : </h2>" + escape(str) + "<br>";
html += "<h2> escape 다른 특수 기호 : </h2>" + escape(addStr) + "<br>";
html += "<h2> escape URL 주소 : </h2>" + escape(url) + "<br><hr>";
- 결과 화면
- escape를 사용하여 문자열을 인코딩 한 것을 확인할 수 있다.
- unescape를 사용하여 문자열을 디코딩하여 다시 원래의 문자열로 되돌릴 수 있다.
아래의 결과 화면은 최신 브라우저를 통하여 RFC 3986 인코딩을 적용되었지만 일부 환경에서는 여전히 RFC 1738 인코딩이 적용될 수 있다.
그렇다면 왜 지금은 권장하지 않는 함수일까?
사실 이 것이 내가 지금 포스팅을 작성하는 이유이다.
실제로 회사에서 사용자가 입력한 값이 정상적으로 입력이 되지 않아 오류가 발생된다는 오류 수정 요청이 들어왔는데,
입력한 값 사이에 더하기 기호가 있으면 정상 입력이 되지 않는다는 내용이였다.
(예를 들자면 "1+1"의 경우 "1 1"로 들어왔다.)
DB의 SQL Server 쿼리, 웹 페이지의 소스 코드를 확인해보았지만
전달되는 값은 똑같음에도 불구하고 계속 다른 값이 들어갔다.
디버깅을 통해 한참을 확인해본 결과 인코딩 오류라는 것을 알아냈다.
특정 환경에서 escape 함수를 쓰면 RFC 1788 인코딩을 이용하여 더하기 '+' 기호를 공백으로 인코딩한다.
현재 사용하는 최신 브라우저들은 RFC 3986 인코딩을 사용하여 상관은 없지만 위에서 언급했듯이 특정 환경에서 문제가 발생될 수 있기 때문에 사양을 지양한다.
이 부분이 내게 문제가 되어서 수정 요청이 들어왔었던 부분이다. (이런 것을 누가 알겠냐고...)
이 문제를 escape 함수가 아닌 encodeURIComponent 함수를 사용하고 나서야 해결할 수 있었다.
escape 함수를 사용하지 않는 이유는 대표적으로 다음과 같다.
일부 특수 문자들을 인코딩하지 않는다.
- 아스키 코드 문자 집합에 포함되는 특수 기호들은(@\*+-_./) 인코딩을 하지 않는다.
- 이는 즉, 다른 환경에서의 문자열 처리가 완벽하지 못 할 수 있으며 이는 곧 오류를 초래한다.
- 가장 가까운 예로 URL에 사용되는 특수 문자들이 포함된다.
일부 유니코드 문자를 지원하지 않는다.
- 인코딩 시 아스키 코드 문자 집합을 사용하여 변환한다.
- 그렇기 때문에 부분적으로 일부 문자들은 유니 코드가 정확히 매핑되지 않을 수 있다.
- 이는 즉, 몇몇 유니코드 문자로 하여금 문제가 발생할 수 있다.
- (이해하기 쉽게 말하자면, 아스키 코드와 유니 코드가 다르다는 것으로 이해하면 편할 것이다.)
보안에 취약하다.
- 일부 특수문자를 인코딩하지 않는다는 것은 보안이 취약할 수 있음을 의미한다.
- encodeURIComponent() 함수는 모든 문자를 인코딩하지만, escape() 함수는 그렇지 않다.
이는 곧 보안상의 취약점이 발생할 수 있음을 의미한다.
위와 같은 이유들로 현재에서는 권장하지 않고,
encodeURI() 함수나 encodeURIComponent() 함수를 상황에 맞게 쓰도록 권장하고 있다.
encodeURI()
설명
- URL 문자열의 특정 부분만 인코딩한다.
- URL의 쿼리 문자열을 인코딩하지 않고 경로(path)나 해시(fragment) 등에 해당하는 부분만 인코딩을 한다.
- 전체 URL 문자열을 만들 때 주로 사용한다.
- 즉, 이해하기 쉽게 말하자면 URL 주소의 문자에서 공백을 제외하면 거의 인코딩을 하지 않는다고 볼 수 있다.
- 제외되는 문자 목록은 아래와 같다. (아래의 문자 외 모두 인코딩)
A-Z
a-z
0-9
- _ . ! ~ * ' ( )
; / ? : @ & = + $ , #
구문 그리고 예시
// 인코딩 (=암호화,Encoding)
encodeURI(내용);
// 디코딩 (=복호화,Decoding)
decodeURI(내용);
- 예시 (html 구성 소스코드는 생략한다.)
let str = "ABC/abc/123/@\\*+-_./";
let addStr = ";?:&=$,#!~'()"
let url = "https://luvris2.tistory.com/?hello=world&hi=bye#note-1"
html += "<h2> 원본 : </h2>" + str + "<br>" + addStr + "<br>" + url;
html += "<h2> encodeURI 아스키 문자열 : </h2>" + deencodeURI(str) + "<br>";
html += "<h2> encodeURI 다른 특수 기호 : </h2>" + encodeURI(addStr) + "<br>";
html += "<h2> encodeURI URL 주소 : </h2>" + encodeURI(url) + "<br><hr>";
- 결과 화면
- encodeURI를 사용하여 문자열을 인코딩 한 것을 확인할 수 있다.
- decodeURI를 사용하여 문자열을 디코딩하여 다시 원래의 문자열로 되돌릴 수 있다.
encodeURIComponent()
설명
- 경로, 쿼리 문자열, 해시(fragment)의 모든 특수 문자들을 인코딩한다.
- 인코딩된 문자열을 보다 안전하게 URL을 생성할 수 있도록 해준다.
- 즉, URL 주소에서 자주 사용되는 특수 문자들을 인코딩하여 서버로 데이터를 전송할 때 주로 사용한다.
- 제외되는 문자 목록은 아래와 같다. (아래의 문자 외 모두 인코딩)
A-Z
a-z
0-9
- _ . ! ~ * ' ( )
구문 그리고 예시
// 인코딩 (=암호화,Encoding)
encodeURIComponent(내용);
// 디코딩 (=복호화,Decoding)
decodeURIComponent(내용);
- 예시 (html 구성 소스코드는 생략한다.)
let str = "ABC/abc/123/@\\*+-_./";
let addStr = ";?:&=$,#!~'()"
let url = "https://luvris2.tistory.com/?hello=world&hi=bye#note-1"
html += "<h2> 원본 : </h2>" + str + "<br>" + addStr + "<br>" + url;
html += "<h2> encodeURIComponent 아스키 문자열 : </h2>" + encodeURIComponent(str) + "<br>";
html += "<h2> encodeURIComponent 다른 특수 기호 : </h2>" + encodeURIComponent(addStr) + "<br>";
html += "<h2> encodeURIComponent URL 주소 : </h2>" + encodeURIComponent(url) + "<br><hr>";
- 결과 화면
- encodeURIComponent를 사용하여 문자열을 인코딩 한 것을 확인할 수 있다.
- decodeURIComponent를 사용하여 문자열을 디코딩하여 다시 원래의 문자열로 되돌릴 수 있다.
- 예시 화면에서 볼 수 있듯이 mozilla 가이드에서는 인코딩한다고 하였지만 ! ~ ' ( ) 기호는 인코딩 하지 않는다.
(이 부분에 자세히 아시는 분 있으시면 댓글 달아주세요...)
인코딩 함수 비교 및 결론
URL 인코딩 기준으로 낸 자체적 결론입니다.
escape()
- 아스키 문자 기호 외 모든 기호 인코딩
- 옛날 인코딩 방식이니 가능한 쓰지 말자
encodeURI
- 전체 URL을 만들 때 사용하므로 인코딩을 거의 하지 않는다
encodeURIComponent
- 서버 전송용 인코딩
- URL 구성 특수 문자들을 인코딩한다
예를 들자면, 쿼리 문자열의 물음표 '?'로 시작하여 '키=밸류' 형태들을 인코딩한다
참고
위키백과 - 문자 인코딩
나무위키 - 인코딩
mozilla MDN - escape()
rfc-editor.org - rfc1738
mozilla MDN - encodeURI()
틀린 부분이 있으면 댓글 달아주세요. 함께 배워갑시다 :)