URL에서 %20, %3A, %2F를 본 적 있나요? 이것들은 퍼센트 인코딩된 문자입니다 — URL이 특수 문자를 안전하게 전달할 수 있게 하는 메커니즘입니다. 이 가이드는 바이트 수준의 동작 원리부터 각 언어의 실용적인 코드 예제까지 설명합니다.
URL 인코더/디코더 도구로 즉시 인코딩 및 디코딩하세요 →
URL 인코딩(퍼센트 인코딩)이란?
URL 인코딩(정식 명칭: 퍼센트 인코딩)은 RFC 3986에서 정의된 URI에서 허용되지 않거나 특별한 의미를 가진 문자를 표현하는 메커니즘입니다.
URL에는 제한된 ASCII 문자만 포함할 수 있습니다. 문자는 예약 문자(/, ?, & 같은 구조적 의미)와 비예약 문자(영숫자, -, _, ., ~)로 나뉩니다.
공백(ASCII 32, 16진수 0x20)은 %20이 됩니다. 콜론(ASCII 58, 16진수 0x3A)은 %3A가 됩니다.
// URL encoding in action
Original: https://example.com/search?q=hello world&lang=en
Encoded: https://example.com/search?q=hello%20world&lang=en
// Reserved characters and their encoded forms:
// space → %20 : → %3A / → %2F
// ? → %3F & → %26 = → %3D
// # → %23 + → %2B @ → %40URL 인코딩 문자 참조 테이블
가장 자주 사용되는 퍼센트 인코딩 문자의 빠른 참조 테이블입니다.
| 문자 | 인코딩 | 이름 / 용도 |
|---|---|---|
(space) | %20 | 공백 |
& | %26 | 파라미터 구분자 |
? | %3F | 쿼리 문자열 시작 |
/ | %2F | 경로 구분자 |
: | %3A | 콜론(스킴 구분자) |
= | %3D | 키-값 구분자 |
# | %23 | 프래그먼트 식별자 |
+ | %2B | 더하기(폼에서 공백) |
@ | %40 | 골뱅이 |
% | %25 | 퍼센트(이스케이프 문자) |
! | %21 | 느낌표 |
" | %22 | 큰따옴표 |
< | %3C | 작음 기호 |
> | %3E | 큼 기호 |
{ | %7B | 왼쪽 중괄호 |
} | %7D | 오른쪽 중괄호 |
[ | %5B | 왼쪽 대괄호 |
] | %5D | 오른쪽 대괄호 |
| | %7C | 파이프 |
\ | %5C | 백슬래시 |
URL 인코딩 동작 원리 (단계별)
인코딩 과정은 세 단계를 따릅니다: (1) 문자 가져오기, (2) UTF-8 바이트 시퀀스로 변환, (3) 각 바이트를 %XX로 인코딩.
여러 예제를 살펴보겠습니다:
// Step-by-step: How characters become percent-encoded
// Example 1: ASCII character (space)
// Character: " " (space)
// ASCII code: 32 → hex: 0x20
// Encoded: %20
// Example 2: ASCII character (ampersand)
// Character: "&"
// ASCII code: 38 → hex: 0x26
// Encoded: %26
// Example 3: Multi-byte UTF-8 (accented letter)
// Character: "é"
// Unicode: U+00E9
// UTF-8 bytes: 0xC3 0xA9 (2 bytes)
// Encoded: %C3%A9
// Example 4: Multi-byte UTF-8 (Chinese character)
// Character: "中"
// Unicode: U+4E2D
// UTF-8 bytes: 0xE4 0xB8 0xAD (3 bytes)
// Encoded: %E4%B8%AD
// Example 5: Multi-byte UTF-8 (emoji)
// Character: "🚀"
// Unicode: U+1F680
// UTF-8 bytes: 0xF0 0x9F 0x9A 0x80 (4 bytes)
// Encoded: %F0%9F%9A%80최신 프로그래밍 언어는 이를 자동으로 처리합니다. 내장 함수 또는 온라인 도구를 사용하세요.
JavaScript의 encodeURI vs encodeURIComponent
JavaScript는 두 가지 URL 인코딩 함수를 제공합니다. 잘못된 것을 선택하면 버그가 발생합니다.
encodeURI()는 전체 URI를 인코딩하며 구조적 문자를 보존합니다.
// encodeURI() — preserves URL structure
const url = 'https://example.com/path name?q=hello world';
encodeURI(url);
// → "https://example.com/path%20name?q=hello%20world"
// ✓ :// / ? = are NOT encoded (structure preserved)encodeURIComponent()는 URI 컴포넌트를 인코딩하며 & = + / 같은 구조적 문자도 인코딩합니다.
// encodeURIComponent() — encodes everything for use as a value
const value = 'price=100&discount=20%';
encodeURIComponent(value);
// → "price%3D100%26discount%3D20%25"
// ✓ = & % ARE encoded (they're data, not structure)
// Correct pattern: build URL piece by piece
const base = 'https://api.example.com/search';
const query = 'Tom & Jerry: The Movie';
const fullUrl = base + '?q=' + encodeURIComponent(query);
// → "https://api.example.com/search?q=Tom%20%26%20Jerry%3A%20The%20Movie"원칙: 쿼리 파라미터 값에는 항상 encodeURIComponent()를 사용하세요.
// Comparison table — which characters get encoded?
//
// Character encodeURI() encodeURIComponent()
// ───────── ─────────── ────────────────────
// space %20 %20
// / / (kept) %2F (encoded)
// ? ? (kept) %3F (encoded)
// & & (kept) %26 (encoded)
// = = (kept) %3D (encoded)
// # # (kept) %23 (encoded)
// : : (kept) %3A (encoded)
// + + (kept) %2B (encoded)
// @ @ (kept) %40 (encoded)
// é %C3%A9 %C3%A9
// 中 %E4%B8%AD %E4%B8%AD각 프로그래밍 언어의 URL 인코딩
주요 언어에는 URL 인코딩 함수가 내장되어 있습니다:
JavaScript
// JavaScript — encodeURIComponent / URLSearchParams
// Encode a query parameter value
encodeURIComponent('hello world&key=val');
// → "hello%20world%26key%3Dval"
// Decode
decodeURIComponent('hello%20world%26key%3Dval');
// → "hello world&key=val"
// URLSearchParams (auto-encodes, uses + for spaces)
const params = new URLSearchParams({ q: 'Tom & Jerry', page: '1' });
params.toString(); // → "q=Tom+%26+Jerry&page=1"Python
# Python 3 — urllib.parse
from urllib.parse import quote, quote_plus, unquote, urlencode
# Path encoding (spaces → %20)
quote('hello world/file') # → "hello%20world/file"
quote('hello world/file', safe='') # → "hello%20world%2Ffile"
# Query value encoding (spaces → +)
quote_plus('hello world&key=val') # → "hello+world%26key%3Dval"
# Build query string from dict
urlencode({'q': 'Tom & Jerry', 'page': '1'})
# → "q=Tom+%26+Jerry&page=1"
# Decode
unquote('%E4%B8%AD%E6%96%87') # → "中文"Java
// Java — URLEncoder (spaces → +)
import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
String encoded = URLEncoder.encode(
"hello world&key=val", StandardCharsets.UTF_8
);
// → "hello+world%26key%3Dval"
String decoded = URLDecoder.decode(encoded, StandardCharsets.UTF_8);
// → "hello world&key=val"Go
// Go — net/url package
package main
import "net/url"
// Query value encoding (spaces → +)
url.QueryEscape("hello world&key=val")
// → "hello+world%26key%3Dval"
// Path segment encoding (spaces → %20)
url.PathEscape("hello world/file")
// → "hello%20world%2Ffile"
// Build URL safely with url.Values
vals := url.Values{}
vals.Set("q", "Tom & Jerry")
vals.Encode() // → "q=Tom+%26+Jerry"PHP
<?php
// PHP — urlencode (spaces → +) / rawurlencode (spaces → %20)
urlencode('hello world&key=val');
// → "hello+world%26key%3Dval"
rawurlencode('hello world&key=val');
// → "hello%20world%26key%3Dval"
http_build_query(['q' => 'Tom & Jerry', 'page' => 1]);
// → "q=Tom+%26+Jerry&page=1"
urldecode('hello%20world'); // → "hello world"
?>Ruby
# Ruby — CGI / URI
require 'cgi'
require 'uri'
CGI.escape('hello world&key=val')
# → "hello+world%26key%3Dval"
URI.encode_www_form('q' => 'Tom & Jerry', 'page' => '1')
# → "q=Tom+%26+Jerry&page=1"
CGI.unescape('hello%20world') # → "hello world"흔한 URL 인코딩 실수
1. 이중 인코딩
이미 인코딩된 문자열을 다시 인코딩하면 발생합니다. %20이 %2520이 됩니다.
// Double encoding — a very common bug
const value = 'hello world';
const encoded = encodeURIComponent(value); // "hello%20world"
// BUG: encoding again
encodeURIComponent(encoded);
// → "hello%2520world" (%20 → %25 + 20)
// Server decodes to "%20" (literal) instead of " " (space)
// FIX: encode raw values exactly once2. 전체 URL 인코딩
전체 URL을 encodeURIComponent()에 전달하면 URL 구조가 깨집니다.
// WRONG: encoding entire URL
encodeURIComponent('https://example.com/api?q=test');
// → "https%3A%2F%2Fexample.com%2Fapi%3Fq%3Dtest" (broken!)
// CORRECT: encode only the parameter value
'https://example.com/api?q=' + encodeURIComponent('test value');
// → "https://example.com/api?q=test%20value"3. 공백: + vs %20
HTML 폼은 +, RFC 3986은 %20을 사용합니다. API에는 %20을 권장합니다.
// Space encoding: + vs %20
// HTML form submission (application/x-www-form-urlencoded):
// "hello world" → "hello+world"
// RFC 3986 (URI standard):
// "hello world" → "hello%20world"
// JavaScript functions:
encodeURIComponent('hello world'); // → "hello%20world" (RFC 3986)
new URLSearchParams({q: 'hello world'}).toString(); // → "q=hello+world" (form)4. 쿼리 파라미터 값 미인코딩
사용자 입력값을 인코딩하지 않으면 보안 위험이 있고 URL이 깨집니다.
// Missing encoding breaks URLs
const search = 'Tom & Jerry';
// WRONG: unencoded & splits the value
'/search?q=' + search;
// → "/search?q=Tom & Jerry"
// Server sees: q="Tom " and Jerry="" (two params!)
// CORRECT: encode the value
'/search?q=' + encodeURIComponent(search);
// → "/search?q=Tom%20%26%20Jerry"
// Server sees: q="Tom & Jerry" (one param)URL 인코딩 모범 사례
사용자 입력은 반드시 인코딩한 후 쿼리 문자열이나 경로 세그먼트에 삽입하세요.
수동 치환 대신 라이브러리 함수를 사용하세요.
특수 문자로 테스트: & = ? / # + % 및 비ASCII 문자.
바이너리 데이터에는 URL 안전 Base64를 사용하세요.
// Best practice: use URL / URLSearchParams API
const url = new URL('https://api.example.com/search');
url.searchParams.set('q', 'Tom & Jerry: The Movie');
url.searchParams.set('lang', 'en');
url.toString();
// → "https://api.example.com/search?q=Tom+%26+Jerry%3A+The+Movie&lang=en"
// All encoding handled automatically!
// URL-safe Base64 (avoids + and / in URLs)
// Standard: "SGVsbG8gV29ybGQ=" (may contain + / =)
// URL-safe: "SGVsbG8gV29ybGQ" (uses - _ instead)자주 묻는 질문
왜 공백이 %20으로 인코딩되나요?
공백의 ASCII 코드는 32이고, 16진수로는 0x20입니다. 퍼센트 인코딩은 바이트를 % + 16진수 값으로 표현합니다.
+와 %20의 차이는?
%20은 RFC 3986에서 유래. +는 폼 형식에서 유래. %20이 더 범용적입니다.
전체 URL을 인코딩해야 하나요?
아니요. 개별 컴포넌트만 인코딩하세요. 구조적 문자는 인코딩하지 마세요.
encodeURI와 encodeURIComponent의 차이는?
encodeURI()는 구조적 문자를 보존. encodeURIComponent()는 그것들도 인코딩. 파라미터 값에는 encodeURIComponent()를 사용.
URL 인코딩된 문자열을 디코딩하려면?
JavaScript: decodeURIComponent(). Python: urllib.parse.unquote(). PHP: urldecode().
URL 인코딩은 모든 개발자가 이해해야 할 기본적인 웹 개념입니다. 이 가이드를 북마크하여 활용하세요.