DevToolBoxFREE
BlogAdvertise

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

10분 읽기by DevToolBox

URL 인코딩(퍼센트 인코딩이라고도 함)은 웹 개발에서 가장 필수적인 메커니즘 중 하나입니다. 폼을 제출하거나, 쿼리 매개변수가 있는 링크를 클릭하거나, REST API를 호출할 때마다 URL 인코딩/디코딩 작업이 수행됩니다. 이 종합 가이드는 RFC 3986 사양부터 JavaScript, Python, Bash, PHP, Java의 실용적인 코드 예제까지 모든 것을 다룹니다.

무료 온라인 URL 인코딩/디코딩 도구를 사용해 보세요.

URL 인코딩(퍼센트 인코딩)이란?

URL 인코딩은 RFC 3986에 정의되어 있으며, 안전하지 않은 문자를 퍼센트 기호(%)와 2자리 16진수로 대체합니다. 예를 들어, 공백은 %20이 됩니다.

URL에는 제한된 ASCII 문자 세트만 포함될 수 있습니다. 인코딩이 필요 없는 URL 안전 문자는: 영문자, 숫자, 4개의 특수 문자 -, _, ., ~입니다. 다른 모든 문자는 인코딩이 필요합니다.

&, =, ?, # 같은 문자는 URL에서 구조적 의미를 가집니다. 적절한 URL 인코딩 없이는 URL이 손상되거나 잘못 해석됩니다.

URL 인코딩 작동 원리

URL 인코딩 프로세스는 간단한 알고리즘을 따릅니다:

  1. 문자를 UTF-8 바이트로 변환.
  2. 각 바이트에 대해 % 뒤에 16진수 값을 기록.
  3. 비예약 문자는 그대로 유지.
  4. 예약 문자는 모호한 경우에만 인코딩.

예제: Hello World!Hello%20World%21이 됩니다.

UTF-8 멀티바이트 예제: 비 ASCII 문자는 여러 퍼센트 인코딩된 바이트를 생성합니다.

URL 인코딩 참조 테이블

자주 인코딩되는 문자의 참조 테이블:

CharacterEncodedDescription
(space)%20Space
!%21Exclamation mark
#%23Hash / Fragment
$%24Dollar sign
%%25Percent sign
&%26Ampersand
'%27Apostrophe
(%28Left parenthesis
)%29Right parenthesis
*%2AAsterisk
+%2BPlus sign
,%2CComma
/%2FForward slash
:%3AColon
;%3BSemicolon
=%3DEquals sign
?%3FQuestion mark
@%40At sign
[%5BLeft bracket
]%5DRight bracket
{%7BLeft brace
}%7DRight brace
|%7CPipe
<%3CLess than
>%3EGreater than
"%22Double quote

공백 인코딩: %20 vs +

URL에서 공백을 인코딩하는 두 가지 방법이 있습니다:

%20: URI의 RFC 3986 표준 인코딩.

+: application/x-www-form-urlencoded(HTML 폼)에서 사용.

어떤 것을 사용할까? 프로그래밍으로 URL을 구성할 때는 %20을 사용하세요. +는 폼 데이터에서 자동으로 사용됩니다.

// Space encoding comparison

// RFC 3986 (URI standard) — space = %20
"hello world"  →  "hello%20world"
// Used in: URL paths, API endpoints, general URIs

// application/x-www-form-urlencoded — space = +
"hello world"  →  "hello+world"
// Used in: HTML form submissions, URLSearchParams

// JavaScript comparison:
encodeURIComponent("hello world")  // "hello%20world"  (%20)
new URLSearchParams({q: "hello world"}).toString()  // "q=hello+world"  (+)

// Python comparison:
urllib.parse.quote("hello world")      # "hello%20world"  (%20)
urllib.parse.urlencode({"q": "hello world"})  # "q=hello+world"  (+)

// PHP comparison:
rawurlencode("hello world")  // "hello%20world"  (%20)
urlencode("hello world")     // "hello+world"    (+)

URL 인코딩/디코딩 코드 예제

JavaScript URL 인코딩/디코딩

JavaScript는 encodeURIComponentencodeURI를 제공합니다. 각각의 사용 시점을 이해하는 것이 중요합니다:

// ===== encodeURIComponent vs encodeURI =====

// encodeURIComponent — encodes EVERYTHING except: A-Z a-z 0-9 - _ . ~
// Use for: individual query parameter values, path segments
encodeURIComponent("hello world & goodbye");
// "hello%20world%20%26%20goodbye"

encodeURIComponent("price=100&category=books");
// "price%3D100%26category%3Dbooks"

// encodeURI — preserves URL structure characters: : / ? # & = @ + $
// Use for: encoding a complete URL
encodeURI("https://example.com/search?q=hello world&lang=en");
// "https://example.com/search?q=hello%20world&lang=en"

// WARNING: Do NOT use encodeURIComponent on full URLs!
encodeURIComponent("https://example.com/path");
// "https%3A%2F%2Fexample.com%2Fpath"  ← BROKEN URL!

// ===== Decoding =====
decodeURIComponent("hello%20world%20%26%20goodbye");
// "hello world & goodbye"

decodeURI("https://example.com/search?q=hello%20world");
// "https://example.com/search?q=hello world"

// ===== URLSearchParams (handles form encoding automatically) =====
const params = new URLSearchParams({
  query: "cats & dogs",
  page: "1",
  filter: "price > 50"
});
params.toString();
// "query=cats+%26+dogs&page=1&filter=price+%3E+50"
// Note: spaces become + (form encoding), & in values becomes %26

// Parse query string
const parsed = new URLSearchParams("?q=hello+world&lang=en");
parsed.get("q");  // "hello world" (+ decoded to space)
parsed.get("lang");  // "en"

// ===== Building URLs with the URL API =====
const url = new URL("https://api.example.com/search");
url.searchParams.set("q", "hello world & more");
url.searchParams.set("limit", "10");
url.toString();
// "https://api.example.com/search?q=hello+world+%26+more&limit=10"

Python URL 인코딩/디코딩

Python의 urllib.parse 모듈은 포괄적인 URL 인코딩 함수를 제공합니다:

import urllib.parse

# ===== quote / unquote (RFC 3986 percent-encoding) =====

# Encode a string (spaces become %20)
urllib.parse.quote("hello world & goodbye")
# "hello%20world%20%26%20goodbye"

# By default, / is NOT encoded (safe="/")
urllib.parse.quote("/path/to/file name.txt")
# "/path/to/file%20name.txt"

# Encode everything including /
urllib.parse.quote("/path/to/file", safe="")
# "%2Fpath%2Fto%2Ffile"

# Decode
urllib.parse.unquote("hello%20world%20%26%20goodbye")
# "hello world & goodbye"

# ===== urlencode (for query strings, uses + for spaces) =====
params = {"q": "cats & dogs", "page": 1, "filter": "price > 50"}
urllib.parse.urlencode(params)
# "q=cats+%26+dogs&page=1&filter=price+%3E+50"

# ===== quote_plus (form encoding, spaces become +) =====
urllib.parse.quote_plus("hello world")
# "hello+world"

urllib.parse.unquote_plus("hello+world")
# "hello world"

# ===== With the requests library =====
import requests

# requests handles URL encoding automatically
response = requests.get(
    "https://api.example.com/search",
    params={"q": "hello world", "lang": "en"}
)
# Actual URL: https://api.example.com/search?q=hello+world&lang=en

# For path segments, encode manually
username = "john@example.com"
url = f"https://api.example.com/users/{urllib.parse.quote(username, safe='')}"
# "https://api.example.com/users/john%40example.com"

Bash / curl URL 인코딩

curl의 --data-urlencode 플래그가 가장 간단한 방법입니다:

# ===== curl --data-urlencode =====

# GET request with URL-encoded query parameter
curl -G "https://api.example.com/search" \
  --data-urlencode "q=hello world & more" \
  --data-urlencode "lang=en"
# Request URL: /search?q=hello%20world%20%26%20more&lang=en

# POST with form-encoded body
curl -X POST "https://api.example.com/submit" \
  --data-urlencode "name=John Doe" \
  --data-urlencode "message=Hello! How are you?"

# ===== Pure Bash URL encoding (using printf) =====
urlencode() {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o
  for (( pos=0 ; pos<strlen ; pos++ )); do
    c=${string:$pos:1}
    case "$c" in
      [-_.~a-zA-Z0-9]) o="${c}" ;;
      *) printf -v o '%%%02X' "'$c" ;;
    esac
    encoded+="${o}"
  done
  echo "${encoded}"
}

urlencode "hello world & goodbye"
# "hello%20world%20%26%20goodbye"

# ===== Using Python one-liner in Bash =====
python3 -c "import urllib.parse; print(urllib.parse.quote('hello world'))"
# "hello%20world"

# ===== Decode URL-encoded strings =====
python3 -c "import urllib.parse; print(urllib.parse.unquote('hello%20world'))"
# "hello world"

PHP URL 인코딩/디코딩

PHP는 urlencode()rawurlencode()를 제공합니다:

<?php
// ===== urlencode / urldecode =====
// Uses + for spaces (application/x-www-form-urlencoded)

echo urlencode("hello world & goodbye");
// "hello+world+%26+goodbye"

echo urldecode("hello+world+%26+goodbye");
// "hello world & goodbye"

// ===== rawurlencode / rawurldecode =====
// Uses %20 for spaces (RFC 3986)

echo rawurlencode("hello world & goodbye");
// "hello%20world%20%26%20goodbye"

echo rawurldecode("hello%20world%20%26%20goodbye");
// "hello world & goodbye"

// ===== When to use which? =====
// urlencode()    → for query string values (form-style)
// rawurlencode() → for URL path segments (RFC 3986)

// Building a URL with encoded path and query
$path = rawurlencode("my file.pdf");
$query = urlencode("search term & more");
$url = "https://example.com/files/{$path}?q={$query}";
// "https://example.com/files/my%20file.pdf?q=search+term+%26+more"

// ===== http_build_query (encode arrays as query strings) =====
$params = [
    "q" => "cats & dogs",
    "page" => 1,
    "tags" => ["php", "url encoding"]
];
echo http_build_query($params);
// "q=cats+%26+dogs&page=1&tags%5B0%5D=php&tags%5B1%5D=url+encoding"
?>

Java URL 인코딩/디코딩

Java의 URLEncoderURLDecoder 클래스가 URL 인코딩을 처리합니다:

import java.net.URLEncoder;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

// ===== URLEncoder.encode (uses + for spaces) =====

// Always specify UTF-8 charset!
String encoded = URLEncoder.encode(
    "hello world & goodbye", StandardCharsets.UTF_8
);
// "hello+world+%26+goodbye"

// Decode
String decoded = URLDecoder.decode(
    "hello+world+%26+goodbye", StandardCharsets.UTF_8
);
// "hello world & goodbye"

// ===== Convert + to %20 for RFC 3986 compliance =====
String rfc3986 = URLEncoder.encode("hello world", StandardCharsets.UTF_8)
    .replace("+", "%20");
// "hello%20world"

// ===== Java 11+ URI encoding =====
import java.net.URI;

URI uri = URI.create("https://example.com/search");
// For building URLs with encoded parameters:
String query = "q=" + URLEncoder.encode("hello world", StandardCharsets.UTF_8);
URI fullUri = URI.create("https://example.com/search?" + query);
// https://example.com/search?q=hello+world

// ===== Encoding path segments =====
String pathSegment = URLEncoder.encode("my file.pdf", StandardCharsets.UTF_8)
    .replace("+", "%20");
String url = "https://example.com/files/" + pathSegment;
// "https://example.com/files/my%20file.pdf"

일반적인 URL 인코딩 실수

피해야 할 가장 흔한 함정들:

이중 인코딩: 이미 인코딩된 문자열을 다시 인코딩. %20%2520이 됩니다.

// Double encoding example — a common bug!

const value = "hello world";

// Correct: encode once
const correct = encodeURIComponent(value);
// "hello%20world" ✓

// Bug: encode twice
const doubleEncoded = encodeURIComponent(encodeURIComponent(value));
// "hello%2520world" ✗  (%25 is the encoded form of %)

// How to detect double encoding:
// If you see %25 in your URLs, you likely have a double encoding issue
// %2520 = double-encoded space (%25 = %, 20 = space)
// %253A = double-encoded colon (%25 = %, 3A = :)

전체 URL 인코딩: 전체 URL에 encodeURIComponent()를 사용하면 URL 구조가 깨집니다.

// Encoding the entire URL — WRONG!
const url = "https://example.com/path?q=hello world";

// WRONG: breaks URL structure
encodeURIComponent(url);
// "https%3A%2F%2Fexample.com%2Fpath%3Fq%3Dhello%20world"

// CORRECT option 1: use encodeURI for full URLs
encodeURI(url);
// "https://example.com/path?q=hello%20world"

// CORRECT option 2: encode only the value
const base = "https://example.com/path";
const query = encodeURIComponent("hello world");
const fullUrl = base + "?q=" + query;
// "https://example.com/path?q=hello%20world"

UTF-8 누락: URL 인코딩에는 항상 UTF-8을 사용하세요.

경로 세그먼트 인코딩 누락: 공백이 포함된 경로도 인코딩이 필요합니다.

+ 기호 잘못 처리: +는 폼에서 공백을 의미하지만 일반 URL에서는 리터럴입니다.

API에서의 URL 인코딩

API에서 올바른 URL 인코딩은 중요합니다:

쿼리 매개변수: 각 매개변수 이름과 값을 개별적으로 인코딩해야 합니다.

경로 매개변수: URL 경로에 포함된 값도 인코딩이 필요합니다.

폼 데이터: application/x-www-form-urlencoded는 공백에 +를 사용합니다.

JSON 페이로드: 요청 본문의 JSON 콘텐츠는 URL 인코딩이 필요 없습니다.

// API URL encoding examples

// Query parameters — encode each value individually
const searchTerm = 'price > 100 & category = "electronics"';
const apiUrl = `https://api.example.com/search?q=${encodeURIComponent(searchTerm)}&limit=10`;
// "https://api.example.com/search?q=price%20%3E%20100%20%26%20...&limit=10"

// Path parameters — encode values embedded in paths
const username = "john@example.com";
const profileUrl = `https://api.example.com/users/${encodeURIComponent(username)}/profile`;
// "https://api.example.com/users/john%40example.com/profile"

// Form data (POST) — uses + for spaces
const formBody = new URLSearchParams({
  username: "john doe",
  password: "p@ss w0rd!"
}).toString();
// "username=john+doe&password=p%40ss+w0rd%21"

// JSON as query parameter — encode the entire JSON string
const filters = JSON.stringify({ price: { min: 10, max: 100 } });
const url = `/api/products?filters=${encodeURIComponent(filters)}`;
// "/api/products?filters=%7B%22price%22%3A%7B%22min%22%3A10%2C%22max%22%3A100%7D%7D"

자주 묻는 질문

URL 인코딩이란 무엇이며 왜 필요한가요?

URL 인코딩은 안전하지 않은 문자를 퍼센트-16진수 시퀀스로 대체하여 URL에서 안전하게 전송하기 위한 프로세스입니다. &, =, ?, # 같은 특수 문자는 구조적 의미를 가지며 데이터로 사용할 때 인코딩이 필요합니다.

encodeURI와 encodeURIComponent의 차이점은?

encodeURI()는 URL 구조 문자(:, /, ?, #)를 보존합니다. encodeURIComponent()는 비예약 문자를 제외한 모든 것을 인코딩합니다. 개별 매개변수 값에는 encodeURIComponent를 사용하세요.

공백에 %20과 + 중 어떤 것을 사용해야 하나요?

URL(RFC 3986)에는 %20을 사용하세요. application/x-www-form-urlencoded에만 +를 사용하세요. 확실하지 않으면 %20이 더 안전한 선택입니다.

URL 인코딩은 모든 개발자가 매일 접하는 기본적인 웹 개발 기술입니다. 무료 온라인 도구로 즉시 인코딩과 디코딩을 수행하세요.

무료 온라인 도구로 URL을 즉시 인코딩하고 디코딩하세요.

도움이 되었나요?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy