URL에서 공백 인코딩: %20 vs + (언제 무엇을 써야 할까)
%20은 공백 문자를 나타내는 RFC 3986 표준 퍼센트 인코딩으로, URL 경로를 비롯한 대부분의 상황에서 사용해야 합니다. + 기호는 HTML 폼 전송과 일부 쿼리 문자열에 쓰이는 application/x-www-form-urlencoded 형식에서만 공백을 의미합니다.
%20은 무슨 의미인가요?
%20은 RFC 3986에서 정의한, 공백 문자(ASCII 코드 32, 16진수 0x20)를 퍼센트 인코딩한 표현입니다. 경로 세그먼트, 쿼리 문자열, 프래그먼트 식별자를 포함해 URI의 어느 부분에서든 공백을 나타내는 표준 방식입니다.
URL에서 %20을 보게 되면 이는 단순히 "여기에 공백이 있다"는 뜻입니다. 예를 들어 https://example.com/my%20file.pdf는 "my file.pdf"라는 이름의 리소스를 가리킵니다. %20은 모든 웹 서버, 브라우저, HTTP 라이브러리가 보편적으로 이해하는 표현입니다.
URL에서 +는 무슨 의미인가요?
공백을 나타내는 + 기호는 폼 데이터를 인코딩하기 위해 HTML 명세에서 정의한 application/x-www-form-urlencoded 콘텐츠 타입에서 유래했습니다. HTML 폼을 GET 메서드로 전송하면 브라우저는 결과 쿼리 문자열에서 공백을 +로 인코딩합니다.
예를 들어 검색 폼에 "hello world"를 입력해 전송하면 브라우저는 https://example.com/search?q=hello+world 같은 URL을 생성합니다. 여기서 +는 "공백"을 의미하는데, 이는 오로지 해당 쿼리 문자열이 form-urlencoded 형식을 사용하기 때문입니다.
여기서 중요한 점은 +가 application/x-www-form-urlencoded 형식 밖에서는 특별한 의미를 갖지 않는다는 것입니다. /files/my+file.pdf 같은 URL 경로에서 +는 공백이 아니라 문자 그대로의 더하기 기호입니다. 참조되는 파일의 이름은 말 그대로 "my+file.pdf"입니다.
핵심 차이
근본적인 차이는 각각이 속한 명세에 있습니다. %20은 RFC 3986(URI)에서 정의되며 URL의 어디에서나 동작합니다. 반면 +를 공백으로 취급하는 관례는 폼 데이터를 위한 HTML/WHATWG 명세에서 정의된 것으로, form-urlencoded 데이터로 명시적으로 다뤄지는 쿼리 문자열에서만 동작합니다.
%20은 경로, 쿼리 문자열, 프래그먼트 등 모든 URI 상황에서 동작합니다+는application/x-www-form-urlencoded쿼리 문자열에서만 "공백"을 의미합니다- URL 경로에서
+는 공백이 아니라 문자 그대로의 더하기 기호입니다 %20은 항상 안전하지만,+는 상황에 따라 달라집니다- 폼 데이터에서 문자 그대로의
+를 인코딩하면%2B가 됩니다
어느 것을 사용해야 하나요?
URL을 프로그래밍 방식으로 구성하거나, REST API 요청을 만들거나, 경로 세그먼트를 인코딩하거나, 혹은 확신이 서지 않을 때는 %20을 사용하세요. URI에서 공백을 나타내는 보편적으로 올바른 인코딩입니다.
HTML 폼 전송을 흉내 내는 쿼리 문자열을 만들거나 API가 이 형식을 명시적으로 요구하는 경우처럼, 의도적으로 application/x-www-form-urlencoded 데이터를 생성할 때만 +를 사용하세요.
대부분의 최신 API와 프레임워크는 쿼리 문자열에서 %20과 +를 모두 허용합니다. 다만 %20을 사용하는 편이 이식성이 높고 모호함을 피할 수 있으며, 특히 쿼리 문자열이 폼 전송 이외의 상황에서 사용될 수 있을 때 유리합니다.
언어별 공백 처리 방식
프로그래밍 언어와 그 표준 라이브러리마다 공백을 인코딩할 때 기본값으로 %20이나 + 중 하나를 사용할 수 있습니다. 사용하는 언어의 기본 동작을 알아두면 예상치 못한 문제를 피할 수 있습니다.
// JavaScript
encodeURIComponent('hello world'); // "hello%20world" (%20)
new URLSearchParams({q: 'hello world'}).toString(); // "q=hello+world" (+)
// 쿼리 문자열에서 %20을 강제하려면:
new URLSearchParams({q: 'hello world'}).toString().replace(/\+/g, '%20');
# Python
from urllib.parse import quote, quote_plus, urlencode
quote('hello world', safe='') # "hello%20world" (%20)
quote_plus('hello world') # "hello+world" (+)
urlencode({'q': 'hello world'}) # "q=hello+world" (기본값이 +)
# %20을 강제하려면:
urlencode({'q': 'hello world'}, quote_via=quote) # "q=hello%20world"
// PHP
rawurlencode('hello world'); // "hello%20world" (%20)
urlencode('hello world'); // "hello+world" (+)
// rawurlencode는 RFC 3986을 따릅니다
// urlencode는 application/x-www-form-urlencoded를 따릅니다