배경

예를 들어,

[GET] /some/list

라는 API를 프론트 단에서 보낼 때, 검색, 페이지네이션, 필터같이 여러 기능이 필요할 때가 있을 것이다.

그럼 우리는 아래와 같이 파라미터를 보내서 해결한다.

[GET] /some/list?q=a&page=0 ...

오늘의 주인공은 바로 저 뒤 파라미터 녀석.

 

간혹 query parameter에 + 나 띄어쓰기를 넣으면, 다음과 같이 쿼리가 보내질 때가 있다.

만약 'eng+lish' 를 보냈다고 해보자.

?q=eng%2Blish

+가 %2B 와 같이 변했다.

이렇게 영어를 제외한 특수문자를 안전하게 전송하기 위한 방법을 인코딩이라고 한다.

인코딩

이는 웹에서 데이터를 안전하게 전송하기 위함이다. 예를 들어

الصباح رباح 라는 아랍어를 웹에서 전송하기 위해서는 ا  ل ص ب ا ح   ر ب ا ح 문자 각각을 보내는 것 보다, %D8%A7%D9%84%D8%B5%D8%A8%D8%A7%D8%AD%20%D8%B1%D8%A8%D8%A7%D8%AD 와 같이 정해진 규칙에 맞춰 인코딩해 보내는 것이 더 안전하다.

그리고 ?나 &같은 특수 기호들이, 기호로써 일하는지 아니면 사용자의 입력값에 있는 것인지 구분을 해야한다.

이를 위해 영어를 제외한 특수문자를 인코딩하는 규칙이 있고, 이 규칙에는 여러 종류가 있다.

오늘은 javascript 함수를 기준으로 보자.

 

encodeURI vs encodeURIComponent

멀리 갈 것도 없다. 당장 크롬 개발자 도구를 켜서 확인할 수 있다.

둘다 "한글" 이라는 글자를 encode했을 때, 똑같은 결과가 나오는 것을 알 수 있다.

그럼 둘의 차이는 무엇일까?

바로 띄어쓰기(space)와 +를 대하는 태도이다.

정확히는, 예약 문자를 다루는 태도가 다르다.

mdn(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)에 따르면

 

encodeURI()는 완전한 URI를 형성하는데 필요한 문자는 인코딩 하지 않습니다. 또한, 예약된 목적을 가지지는 않지만 URI가 그대로 포함할 수 있는 몇 가지 문자("비예약 표식")도 인코딩 하지 않습니다.

var set1 = ";,/?:@&=+$#"; // 예약 문자
var set2 = "-_.!~*'()"; // 비예약 표식
var set3 = "ABC abc 123"; // 알파벳 및 숫자, 공백

console.log(encodeURI(set1)); // ;,/?:@&=+$#
console.log(encodeURI(set2)); // -_.!~*'()
console.log(encodeURI(set3)); // ABC%20abc%20123 (공백은 %20으로 인코딩)

console.log(encodeURIComponent(set1)); // %3B%2C%2F%3F%3A%40%26%3D%2B%24%23
console.log(encodeURIComponent(set2)); // -_.!~*'()
console.log(encodeURIComponent(set3)); // ABC%20abc%20123 (공백은 %20으로 인코딩)

 

URI의 Generic Syntax를 구성하기 위한 요소 말고는 변형하지 않는 친구이다.

아, API를 보낼 때는 encodeURIComponent를 써야 제대로 파라미터가 가겠구나!

라는 생각을 할 수 있겠다.

그러던 중 사소한 디테일도 발견했다.

 

사소한 디테일

우리의 코드가 %25ED%2595%259C%252B%25EA%25B8%2580 같은 식으로 파라미터를 보내고 있었다.

이렇게 생긴 애들은 대부분 encode를 두 번 거친 친구들이다. 어딘가 단단히 꼬인 경우인데...

오늘 있었던 일은 이랬다.

한+글 을 보내면

위 사진은 이해를 돕기 위해 제작했다.

어찌된 영문인지 자꾸 %25ED%2595%259C%2520%25EA%25B8%2580 로 보내지는 것이다.

이 곳에는 두 개의 문제가 있었다.

1. 왜 두 번 인코딩 되는가?

2. 왜 +가 띄어쓰기로 인식되는가?

1번 두 번 인코딩 문제는 encode 함수를 전부 검색해서, axios에서도 한 번, 보내줄 때도 한 번 하는 부분을 찾아 금방 해결했지만

2번 문제는 쉽사리 해결되지 않았었는데...

한참을 찾던 중 발견했다.

 

navigate를 확인하자

그렇다. 종종 검색 내역이나 페이지수를 저장하려고 사용자의 URI에 keyword 정보를 넘기는 경우가 꽤 많은데,

구글도 이렇게 남긴다.

이렇게 경우를 넘길 때 encode를 안 해주고 있었던 것!!

window.location.search 를 직접 교체해서 쓰고 있었다.

여러분은 부디 react에서 몹시 잘 제공해주는 useSearchParams hook을 예쁘게 쓰시길 바란다.

 

++ 2025.04.01 추가

놀랍게도, setSearchParams를 써도 encodeURI를 해버린다.

조금 원시적인 방법이지만 어쩔 수 없이 아래처럼 해야겠다.

navigate(pathname + encodeURIComponent(_query), { replace: true });

 

+ Recent posts