DevToolBox무료
블로그

URL 인코딩(퍼센트 인코딩) 해설: %20과 %3A의 진짜 의미

10분 읽기by DevToolBox

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    @ → %40

URL 인코딩 문자 참조 테이블

가장 자주 사용되는 퍼센트 인코딩 문자의 빠른 참조 테이블입니다.

문자인코딩이름 / 용도
(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

최신 프로그래밍 언어는 이를 자동으로 처리합니다. 내장 함수 또는 온라인 도구를 사용하세요.

URL 인코더 도구로 인코딩 & 디코딩 →

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 once

2. 전체 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)

Base64 Encoder →URL Parser →

자주 묻는 질문

왜 공백이 %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 인코딩은 모든 개발자가 이해해야 할 기본적인 웹 개념입니다. 이 가이드를 북마크하여 활용하세요.

URL 인코더/디코더 도구를 사용해보세요 →

𝕏 Twitterin LinkedIn
도움이 되었나요?

최신 소식 받기

주간 개발 팁과 새 도구 알림을 받으세요.

스팸 없음. 언제든 구독 해지 가능.

Try These Related Tools

%20URL Encoder/Decoder🔗URL ParserB64Base64 Encoder/Decoder%%Percent Encoding Tool

Related Articles

URL 인코딩 특수문자: 완전한 참조 테이블 & 예제

URL 퍼센트 인코딩 완전 참조. 모든 특수문자 조회표, encodeURIComponent vs encodeURI 사용법.

URL 인코딩 & 디코딩 완전 가이드: 퍼센트 인코딩 해설

무료 온라인 URL 인코더/디코더. JavaScript, Python, Bash, PHP 코드 예제와 퍼센트 인코딩 원리.

ASCII vs Unicode vs UTF-8 인코딩 설명

ASCII, Unicode, UTF-8의 차이를 이해하세요. 문자 인코딩 작동 원리, UTF-8이 웹을 지배하는 이유, 인코딩 문제 해결법.

URL 인코더 디코더 온라인 가이드: 퍼센트 인코딩, RFC 3986, 모범 사례

URL 인코딩(퍼센트 인코딩) 완전 가이드. RFC 3986 예약 및 비예약 문자, encodeURIComponent vs encodeURI, Python urllib.parse, Java URLEncoder, 일반 인코딩 문자, 폼 인코딩, API 쿼리 매개변수, 이중 인코딩 디버깅.