JavaScript URL 인코딩 완전 가이드 | encodeURIComponent

·Front-end & Node.js Developer·검토자Sophie Laurent·게시일

무료 URL 인코더을 브라우저에서 직접 사용하세요 — 설치 불필요.

URL 인코더 온라인으로 사용하기 →

검색 URL을 만들거나, 리다이렉트 경로를 쿼리 파라미터로 전달하거나, OAuth 인가 요청을 구성할 때 &, =, 공백 같은 특수 문자는 URL 인코딩을 하지 않으면 URL을 조용히 손상시킵니다. JavaScript에는 세 가지 내장 방법이 있습니다—— encodeURIComponent(), encodeURI(), 그리고 URLSearchParams——각각 서로 다른 사용 사례를 위해 설계되었으며, 잘못된 것을 선택하는 것이 코드 리뷰에서 제가 만난 대부분의 인코딩 버그의 근본 원인입니다. 코드 없이 빠르게 한 번 인코딩하려면 ToolDeck의 URL Encoder 가 브라우저에서 즉시 처리합니다. 이 가이드는 세 가지 방법 모두를 심층적으로 다룹니다 (JavaScript ES2015+ / Node.js 10+):각각을 언제 사용할지, 공백과 예약 문자 처리 방법의 차이, 실제 Fetch 및 API 패턴, CLI 사용법, 그리고 가장 미묘한 프로덕션 버그를 일으키는 네 가지 실수입니다.

  • encodeURIComponent()는 개별 파라미터 값을 인코딩하는 올바른 선택입니다. A–Z, a–z, 0–9 및 - _ . ! ~ * ' ( )를 제외한 모든 문자를 인코딩합니다
  • encodeURI()는 구조 문자(/ ? # & = :)를 보존하면서 전체 URL을 인코딩합니다. 개별 값에는 절대 사용하지 마세요
  • URLSearchParams는 application/x-www-form-urlencoded 형식을 사용하여 키-값 쌍을 자동 인코딩하며, 공백은 %20이 아닌 +가 됩니다
  • 이중 인코딩은 가장 흔한 프로덕션 버그입니다. encodeURIComponent(encodeURIComponent(value))는 %20을 %2520으로 바꿉니다
  • qs 라이브러리는 쿼리 문자열의 중첩 객체와 배열을 기본으로 처리합니다. 표준 라이브러리 URLSearchParams는 이를 지원하지 않습니다

URL 인코딩이란?

URL 인코딩(공식적으로 퍼센트 인코딩이라 하며, RFC 3986에 정의됨)은 URL에서 허용되지 않거나 특별한 의미를 가진 문자를 안전한 표현으로 변환합니다. 각 안전하지 않은 바이트는 퍼센트 기호와 두 개의 16진수——해당 문자의 ASCII 코드——로 대체됩니다. 공백은 %20이 되고, 앰퍼샌드는 %26이 되며, 슬래시는 %2F가 됩니다.

항상 안전하고 절대 인코딩되지 않는 문자를 비예약 문자라고 합니다: 문자 A–Z와 a–z, 숫자 0–9, 그리고 네 가지 기호 - _ . ~. 그 외 모든 것은 데이터로 사용될 때 인코딩되어야 하거나, URL 자체에서 구조적 역할을 합니다(예: /는 경로 세그먼트를 구분하고&는 쿼리 파라미터를 구분합니다). 실제 결과: 쿼리 파라미터에 "무선 키보드 & 마우스" 같은 제품 이름이 원시 그대로 전달되면 URL 구조가 깨집니다.

Before · text
After · text
https://shop.example.com/search?q=Wireless Keyboard & Mouse&category=peripherals
https://shop.example.com/search?q=Wireless%20Keyboard%20%26%20Mouse&category=peripherals

encodeURIComponent() — 쿼리 파라미터에 올바른 함수

encodeURIComponent()는 JavaScript에서 URL 인코딩의 핵심 함수입니다. 비예약 세트(A–Z, a–z, 0–9, - _ . ! ~ * ' ( ))를제외한 모든 문자를 인코딩합니다. 중요한 것은, URL에서 구조적 의미를 가진 모든 문자를 인코딩한다는 것입니다——&, =, ?, #, /—— 이는 파라미터 에서 사용하기 안전하게 만듭니다. 가져오기가 필요 없으며, 모든 JavaScript 환경에서 전역 함수로 사용 가능합니다.

최소 작동 예제

JavaScript (browser / Node.js)
// Encode a search query parameter that contains special characters
const searchQuery  = 'Wireless Keyboard & Mouse'
const filterStatus = 'in-stock'
const maxPrice     = '149.99'

const searchUrl = `https://shop.example.com/products?` +
  `q=${encodeURIComponent(searchQuery)}` +
  `&status=${encodeURIComponent(filterStatus)}` +
  `&maxPrice=${encodeURIComponent(maxPrice)}`

console.log(searchUrl)
// https://shop.example.com/products?q=Wireless%20Keyboard%20%26%20Mouse&status=in-stock&maxPrice=149.99

리다이렉트 URL을 쿼리 파라미터로 인코딩

JavaScript
// The redirect destination is itself a URL — it must be fully encoded
// or the outer URL parser will misinterpret its ? and & as its own
const redirectAfterLogin = 'https://dashboard.internal/reports?view=weekly&team=platform'

const loginUrl = `https://auth.company.com/login?next=${encodeURIComponent(redirectAfterLogin)}`

console.log(loginUrl)
// https://auth.company.com/login?next=https%3A%2F%2Fdashboard.internal%2Freports%3Fview%3Dweekly%26team%3Dplatform

// Decoding on the receiving end
const params    = new URLSearchParams(window.location.search)
const next      = params.get('next')              // Automatically decoded
const nextUrl   = new URL(next!)                  // Safe to parse
console.log(nextUrl.hostname)                     // dashboard.internal

비 ASCII 및 유니코드 문자 인코딩

JavaScript
// encodeURIComponent handles Unicode natively in all modern environments
// Each UTF-8 byte of the character is percent-encoded
const customerName = '김민준'
const productTitle = '서울 wireless adapter'
const reviewText   = '매우 좋음 — 완벽하게 작동'

console.log(encodeURIComponent(customerName))
// %EA%B9%80%EB%AF%BC%EC%A4%80

console.log(encodeURIComponent(productTitle))
// %EC%84%9C%EC%9A%B8%20wireless%20adapter

console.log(encodeURIComponent(reviewText))
// %EB%A7%A4%EC%9A%B0%20%EC%A2%8B%EC%9D%8C%20%E2%80%94%20%EC%99%84%EB%B2%BD%ED%95%98%EA%B2%8C%20%EC%9E%91%EB%8F%99

// Decoding back
console.log(decodeURIComponent('%EA%B9%80%EB%AF%BC%EC%A4%80'))
// 김민준
참고:encodeURIComponent()는 JavaScript 엔진의 내부 UTF-16 문자열 인코딩을 사용한 후, 문자의 각 UTF-8 바이트를 개별적으로 인코딩합니다. ü(U+00FC)같은 문자는 %C3%BC로 인코딩됩니다. UTF-8 표현이 두 바이트(0xC3과 0xBC)이기 때문입니다. 이는 올바르며 URI 표준을 따릅니다——서버는 UTF-8 바이트 시퀀스를 원래 코드포인트로 디코딩할 것으로 예상됩니다.

JavaScript URL 인코딩 함수——문자 참조

세 가지 네이티브 인코딩 방법은 어떤 문자를 인코딩하는지가 다릅니다. 아래 표는 가장 일반적으로 문제가 되는 문자들의 출력을 보여줍니다:

문자URL에서의 역할encodeURIComponent()encodeURI()URLSearchParams
Space단어 구분자%20%20+
&파라미터 구분자%26kept%26
=키=값%3Dkept%3D
+인코딩된 공백(폼)%2B%2B%2B
?쿼리 시작%3Fkept%3F
#프래그먼트%23kept%23
/경로 구분자%2Fkept%2F
:스킴 / 포트%3Akept%3A
@인증 구분자%40kept%40
%퍼센트 리터럴%25%25%25
~비예약keptkeptkept

핵심 열은 & =에 대한 encodeURIComponent() encodeURI()의 차이입니다:encodeURI()는 이들을 그대로 유지합니다. 이는 전체 URL을 인코딩할 때는 올바르지만, 이런 문자를 포함하는 을 인코딩할 때는 치명적입니다.

encodeURI() — URL 구조를 보존해야 할 때

encodeURI()전체 URL을 인코딩하도록 설계되었습니다—— URI의 유효한 구조적 부분인 모든 문자를 보존합니다:스킴 (https://), 호스트, 경로 구분자, 쿼리 구분자, 프래그먼트 식별자. 경로 세그먼트에 공백이나 비 ASCII 문자를 포함할 수 있는 URL을 받았지만 그 구조를 유지해야 할 때 사용합니다.

사용자 제공 URL 정리

JavaScript
// A URL pasted from a document with spaces in the path and non-ASCII chars
const rawUrl = 'https://cdn.example.com/assets/product images/München chair.png'

const safeUrl = encodeURI(rawUrl)
console.log(safeUrl)
// https://cdn.example.com/assets/product%20images/M%C3%BCnchen%20chair.png

// encodeURIComponent would break it — it encodes the :// and all / characters
const broken = encodeURIComponent(rawUrl)
console.log(broken)
// https%3A%2F%2Fcdn.example.com%2Fassets%2Fproduct%20images%2FM%C3%BCnchen%20chair.png
// ↑ Not a valid URL — the scheme and slashes are destroyed
참고:사용자가 제공한 URL 문자열을 처리할 때는 URL 생성자가 일반적으로 encodeURI()보다 좋은 선택입니다—— URL을 정규화하고, 구조를 검증하며, 각 구성 요소에 액세스하기 위한 깔끔한 API를 제공합니다.encodeURI()는 문자열 결과가 필요하고 입력이 구조적으로 유효한 URL임을 이미 알고 있는 경우를 위해 남겨두세요.

URLSearchParams — 쿼리 문자열의 현대적 접근법

URLSearchParams는 현대 JavaScript에서 쿼리 문자열을 구축하고 파싱하는 관용적인 방법입니다. 모든 현대 브라우저와 Node.js 10+에서 전역으로 사용 가능하며, 인코딩을 자동으로 처리합니다——일반 키-값 쌍으로 작업하면 올바른 출력이 생성됩니다. 중요한 세부 사항: application/x-www-form-urlencoded 사양을 따르며, 공백을 %20이 아닌 +로 인코딩합니다. 이는 올바르고 널리 지원되지만, 서버가 특정 형식을 기대할 때는 주의해야 합니다.

검색 요청 URL 구축

JavaScript (browser / Node.js 10+)
// Building a search API URL with multiple parameters
const filters = {
  query:    'standing desk',
  category: 'office-furniture',
  minPrice: '200',
  maxPrice: '800',
  inStock:  'true',
  sortBy:   'price_asc',
}

const params = new URLSearchParams(filters)

const apiUrl = `https://api.example.com/v2/products?${params}`
console.log(apiUrl)
// https://api.example.com/v2/products?query=standing+desk&category=office-furniture&minPrice=200&maxPrice=800&inStock=true&sortBy=price_asc

// Appending additional params after construction
params.append('page', '2')
params.append('tag', 'ergonomic & adjustable')
console.log(params.toString())
// query=standing+desk&...&tag=ergonomic+%26+adjustable

들어오는 쿼리 문자열 파싱

JavaScript
// Both browser (window.location.search) and Node.js (req.url) scenarios
function parseWebhookCallbackUrl(rawSearch: string) {
  const params = new URLSearchParams(rawSearch)

  return {
    eventId:     params.get('event_id'),       // null if missing
    timestamp:   Number(params.get('ts')),
    signature:   params.get('sig'),
    redirectUrl: params.get('redirect'),       // Automatically decoded
    tags:        params.getAll('tag'),         // Handles repeated keys
  }
}

const callbackQuery = '?event_id=evt_9c2f&ts=1717200000&sig=sha256%3Dabc123&redirect=https%3A%2F%2Fdashboard.internal%2F&tag=payment&tag=webhook'

const parsed = parseWebhookCallbackUrl(callbackQuery)
console.log(parsed.redirectUrl)  // https://dashboard.internal/
console.log(parsed.tags)         // ['payment', 'webhook']

JavaScript Fetch 요청에서 파라미터를 URL 인코딩하는 방법

프로덕션 코드에서 URL 인코딩이 가장 자주 등장하는 곳은 fetch() 호출 내부입니다—— 요청 URL을 구축하거나 요청 본문에 폼 데이터를 전송하는 경우입니다. 각 시나리오에는 올바른 접근법이 있습니다.

GET 요청——쿼리 파라미터 인코딩

JavaScript (browser / Node.js 18+)
async function searchInventory(params: {
  sku?:      string
  warehouse: string
  minStock:  number
  updatedAfter?: Date
}): Promise<{ items: unknown[]; total: number }> {
  const searchParams = new URLSearchParams()

  if (params.sku)          searchParams.set('sku',          params.sku)
  searchParams.set('warehouse',  params.warehouse)
  searchParams.set('min_stock',  String(params.minStock))
  if (params.updatedAfter) searchParams.set('updated_after', params.updatedAfter.toISOString())

  const url = `https://inventory.internal/api/items?${searchParams}`

  const res = await fetch(url, {
    headers: {
      'Authorization': `Bearer ${process.env.INVENTORY_API_KEY}`,
      'Accept':        'application/json',
    },
  })

  if (!res.ok) {
    throw new Error(`Inventory API ${res.status}: ${await res.text()}`)
  }

  return res.json()
}

const results = await searchInventory({
  warehouse:    'eu-west-1',
  minStock:     10,
  updatedAfter: new Date('2025-01-01'),
})

console.log(`Found ${results.total} items`)

POST 요청——폼 본문 URL 인코딩

JavaScript
// application/x-www-form-urlencoded POST — used by OAuth token endpoints,
// legacy form-submission APIs, and some webhook providers
async function requestOAuthToken(
  clientId:     string,
  clientSecret: string,
  code:         string,
  redirectUri:  string,
): Promise<{ access_token: string; expires_in: number }> {
  const body = new URLSearchParams({
    grant_type:    'authorization_code',
    client_id:     clientId,
    client_secret: clientSecret,
    code,
    redirect_uri:  redirectUri,  // URLSearchParams encodes this automatically
  })

  const res = await fetch('https://oauth.provider.com/token', {
    method:  'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body:    body.toString(),
    // body: body — also works directly in modern environments (Fetch spec accepts URLSearchParams)
  })

  if (!res.ok) {
    const err = await res.json()
    throw new Error(`OAuth error: ${err.error_description ?? err.error}`)
  }

  return res.json()
}

스크립트를 설정하지 않고 인코딩된 URL을 테스트하거나 디버그해야 할 때, 원시 값을 직접 URL Encoder 에 붙여넣으세요——브라우저와 서버가 볼 것을 정확히 보여주면서 즉시 인코딩과 디코딩을 합니다. OAuth 리다이렉트 URI, webhook 콜백 URL, CDN 서명된 요청 파라미터를 검사할 때 유용합니다.

Node.js와 Shell을 사용한 커맨드라인 URL 인코딩

셸 스크립트, CI 파이프라인, 또는 빠른 디버깅을 위해 완전한 스크립트를 작성하지 않고도 작동하는 몇 가지 커맨드라인 방법이 있습니다.

bash
# ── Node.js one-liners — cross-platform (macOS, Linux, Windows) ────────

# encodeURIComponent — encode a single value
node -e "console.log(encodeURIComponent(process.argv[1]))" "Wireless Keyboard & Mouse"
# Wireless%20Keyboard%20%26%20Mouse

# Build a complete query string
node -e "console.log(new URLSearchParams(JSON.parse(process.argv[1])).toString())"   '{"q":"standing desk","category":"office-furniture","page":"1"}'
# q=standing+desk&category=office-furniture&page=1

# Decode a percent-encoded string
node -e "console.log(decodeURIComponent(process.argv[1]))" "Wireless%20Keyboard%20%26%20Mouse"
# Wireless Keyboard & Mouse

# ── curl — automatic encoding ───────────────────────────────────────────
# curl --data-urlencode encodes values automatically in GET and POST
curl -G "https://api.example.com/search"   --data-urlencode "q=Wireless Keyboard & Mouse"   --data-urlencode "category=office furniture"

# ── Python one-liner (available on most systems) ─────────────────────────
python3 -c "from urllib.parse import quote; print(quote(input(), safe=''))"
# (type the string and press Enter)

# ── jq + bash: encode every value in a JSON object ───────────────────────
echo '{"q":"hello world","tag":"node & express"}' |   node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));            console.log(new URLSearchParams(d).toString())"
# q=hello+world&tag=node+%26+express

고성능 대안: qs

내장 URLSearchParams는 중첩 객체나 배열을 지원하지 않습니다—— 많은 API와 프레임워크에서 사용하는 쿼리 문자열 형태입니다. qs 라이브러리(주간 npm 다운로드 3000만 이상)는 실제에서 사용되는 전체 범위의 쿼리 문자열 패턴을 처리합니다: 브래킷 표기법으로 중첩된 객체(filters[status]=active), 반복 키, 커스텀 인코더, 설정 가능한 배열 직렬화 형식.

bash
npm install qs
# or
pnpm add qs
JavaScript
import qs from 'qs'

// URLSearchParams cannot represent this structure natively
const reportFilters = {
  dateRange: {
    from: '2025-01-01',
    to:   '2025-03-31',
  },
  status:    ['published', 'archived'],
  author:    { id: 'usr_4f2a9c1b', role: 'editor' },
  workspace: 'ws-platform-eu',
}

// qs produces bracket-notation query strings used by Express, Rails, Django REST
const query = qs.stringify(reportFilters, { encode: true })
console.log(query)
// dateRange%5Bfrom%5D=2025-01-01&dateRange%5Bto%5D=2025-03-31&status%5B0%5D=published&status%5B1%5D=archived&author%5Bid%5D=usr_4f2a9c1b&author%5Brole%5D=editor&workspace=ws-platform-eu

// Human-readable (no encoding) — useful for debugging
console.log(qs.stringify(reportFilters, { encode: false }))
// dateRange[from]=2025-01-01&dateRange[to]=2025-03-31&status[0]=published&status[1]=archived...

// Parsing back
const parsed = qs.parse(query)
console.log(parsed.dateRange)   // { from: '2025-01-01', to: '2025-03-31' }
console.log(parsed.status)      // ['published', 'archived']

평면 키-값 파라미터에는 URLSearchParams가 항상 올바른 선택입니다—— 내장되어 있고, 오버헤드가 없으며, 보편적으로 지원됩니다. 중첩 구조가 필요하거나, 반복 키 외의 배열 직렬화 형식이 필요하거나, 브래킷 표기법 쿼리 문자열을 기대하는 백엔드 프레임워크와 통합할 때만 qs를 사용하세요.

흔한 실수

프로덕션 코드베이스에서 이 네 가지 실수를 반복해서 봤습니다—— 대부분은 값에 특수 문자가 포함될 때만 나타나는 사일런트 실패로, 단위 테스트를 통과하고 실제 사용자 데이터에서만 나타나는 그런 버그입니다.

실수 1 — 쿼리 파라미터 값에 encodeURI() 사용

문제: encodeURI() &, =, 또는 +를 인코딩하지 않습니다. 값에 이러한 문자가 포함되면, 쿼리 문자열 구문으로 해석되어 파라미터를 조용히 분할하거나 덮어씁니다. 수정: 개별 값에는 항상 encodeURIComponent()를 사용하세요.

Before · JavaScript
After · JavaScript
// ❌ encodeURI does not encode & and = in the value
//    "plan=pro&promo=SAVE20" is treated as two separate params
const planName = 'Pro Plan (save=20% & free trial)'
const url = `/checkout?plan=${encodeURI(planName)}`
// /checkout?plan=Pro%20Plan%20(save=20%%20&%20free%20trial)
//                                          ↑ & breaks the query string
// ✅ encodeURIComponent encodes & and = safely
const planName = 'Pro Plan (save=20% & free trial)'
const url = `/checkout?plan=${encodeURIComponent(planName)}`
// /checkout?plan=Pro%20Plan%20(save%3D20%25%20%26%20free%20trial)
//                                   ↑ = and & are both encoded

실수 2 — 이미 인코딩된 문자열을 이중 인코딩

문제: 이미 퍼센트 인코딩된 값에 encodeURIComponent()를 호출하면% %25가 되어,%20 %2520이 됩니다. 서버는 한 번 디코딩하고 공백 대신 리터럴 %20을 받습니다. 수정: 먼저 디코딩한 다음 재인코딩하거나, 값이 두 번 인코딩되지 않도록 하세요.

Before · JavaScript
After · JavaScript
// ❌ encodedParam already contains %20 — encoding again produces %2520
const encodedParam  = 'Berlin%20Office'
const url = `/api/locations/${encodeURIComponent(encodedParam)}`
// /api/locations/Berlin%2520Office
// Server sees: "Berlin%20Office" (a literal percent-twenty, not a space)
// ✅ Decode first if the value may already be encoded
const maybeEncoded = 'Berlin%20Office'
const clean = decodeURIComponent(maybeEncoded)  // 'Berlin Office'
const url   = `/api/locations/${encodeURIComponent(clean)}`
// /api/locations/Berlin%20Office — correct

실수 3 — URLSearchParams와 encodeURIComponent의 + / %20 차이를 고려하지 않음

문제: URLSearchParams는 공백을 +(application/x-www-form-urlencoded)로 인코딩하고,encodeURIComponent() %20을 사용합니다. 같은 URL에서 둘을 혼합하면——예를 들어 URLSearchParams 출력에 미리 인코딩된 문자열을 추가하는 경우—— 일부 파서를 혼란스럽게 하는 불일치 인코딩이 생성됩니다. 수정: 하나의 접근법을 선택하고 URL 구축 함수 전체에서 일관되게 사용하세요.

Before · JavaScript
After · JavaScript
// ❌ Mixed: URLSearchParams uses + for spaces, but we're appending
//    a manually-encoded segment that uses %20
const base   = new URLSearchParams({ category: 'office furniture' })
const extra  = `sort=${encodeURIComponent('price asc')}`
const url    = `/api/products?${base}&${extra}`
// /api/products?category=office+furniture&sort=price%20asc
//              ↑ two different space encodings in the same URL
// ✅ Use URLSearchParams exclusively — consistent encoding throughout
const params = new URLSearchParams({
  category: 'office furniture',
  sort:     'price asc',
})
const url = `/api/products?${params}`
// /api/products?category=office+furniture&sort=price+asc

실수 4 — 슬래시를 포함하는 경로 세그먼트 인코딩을 잊음

문제: REST 경로 세그먼트로 사용되는 리소스 식별자(파일 경로 reports/2025/q1.pdf 같은)에는 서버 라우터가 경로 구분자로 해석하는 / 문자가 포함되어 있어 존재하지 않는 엔드포인트로 라우팅됩니다. 수정: 슬래시를 포함할 수 있는 경로 세그먼트에는 항상 encodeURIComponent()를 사용하세요.

Before · JavaScript
After · JavaScript
// ❌ The file path contains / — the server receives a 3-segment path
//    instead of a single resource ID
const filePath = 'reports/2025/q1-financials.pdf'
const url      = `https://storage.example.com/objects/${filePath}`
// https://storage.example.com/objects/reports/2025/q1-financials.pdf
// → Server routes to /objects/:year/:filename — 404 or wrong resource
// ✅ encodeURIComponent encodes / as %2F — single path segment
const filePath = 'reports/2025/q1-financials.pdf'
const url      = `https://storage.example.com/objects/${encodeURIComponent(filePath)}`
// https://storage.example.com/objects/reports%2F2025%2Fq1-financials.pdf
// → Server receives the full file path as one resource identifier

JavaScript URL 인코딩 메서드——빠른 비교

메서드공백 인코딩& = ? 인코딩# / : 인코딩사용 사례설치 필요
encodeURIComponent()%20✅ yes✅ yes개별 파라미터 값 인코딩No
encodeURI()%20❌ no❌ no전체 URL 문자열 정리No
URLSearchParams+✅ yes✅ yes쿼리 문자열 구축 및 파싱No
URL constructor컴포넌트별 자동autoauto전체 URL 구축 및 정규화No
qs library%20(설정 가능)✅ yes✅ yes쿼리 문자열의 중첩 객체와 배열npm install qs

대부분의 경우 선택은 세 가지 시나리오로 좁혀집니다. 구조화된 데이터에서 다중 파라미터 쿼리 문자열을 구축할 때는 URLSearchParams를 사용하세요—— 가장 안전하고 가독성이 높은 선택입니다. 템플릿 리터럴 URL에서 단일 값을 인코딩하거나, 경로 세그먼트에 사용하거나, 공백에 + 대신 %20을 기대하는 시스템(AWS S3 서명 URL 등)에서는encodeURIComponent()를 사용하세요. 쿼리 문자열이 URLSearchParams로 기본적으로 표현할 수 없는 중첩 객체나 배열을 가질 때만 qs를 사용하세요.

자주 묻는 질문

JavaScript에서 encodeURIComponent()와 encodeURI()의 차이점은 무엇인가요?
encodeURIComponent()는 비예약 세트(A–Z, a–z, 0–9, - _ . ! ~ * ' ( ))를 제외한 모든 문자를 인코딩합니다. & = ? # / : 같은 구조적 문자도 포함됩니다. 개별 쿼리 파라미터 값이나 경로 세그먼트를 인코딩하기 위해 설계되었습니다. encodeURI()는 구조적 URL 문자를 보존합니다——& = ? # / : @를 인코딩하지 않습니다——구조를 깨지 않고 전체 URL을 정리하기 위해 설계되었기 때문입니다. & 또는 =를 포함하는 값에 encodeURI()를 사용하는 것은 조용한 데이터 손상의 일반적인 원인입니다.
URLSearchParams가 공백을 %20이 아닌 +로 인코딩하는 이유는 무엇인가요?
URLSearchParams는 application/x-www-form-urlencoded 사양(원래 HTML 폼 제출에서 파생됨)을 따르며, 공백을 %20이 아닌 +로 인코딩합니다. +와 %20 모두 쿼리 문자열에서 공백의 유효한 표현이며, 대부분의 서버는 둘 다 수락합니다. 하지만 일부 API——특히 AWS Signature v4, Google APIs, 커스텀 REST 백엔드——는 %20을 요구합니다. 그런 경우에는 encodeURIComponent()를 사용하여 쿼리 문자열을 수동으로 구축하거나, URLSearchParams 출력에 params.toString().replace(/\+/g, '%20')을 호출하세요.
JavaScript에서 슬래시가 포함된 경로 세그먼트를 URL 인코딩하려면 어떻게 하나요?
encodeURIComponent()를 사용하세요——/를 %2F로 인코딩하여 전체 경로를 단일 세그먼트로 만듭니다. encodeURI()는 /를 인코딩하지 않아 라우터가 실제 경로 구분자로 처리합니다. 이는 경로 위치에 리소스 식별자를 사용하는 REST API에서 중요합니다:파일 경로(reports/2025/q1.pdf), S3 객체 키, 복합 ID(org/team/member). 인코딩 후:encodeURIComponent('reports/2025/q1.pdf') → 'reports%2F2025%2Fq1.pdf'.
JavaScript에서 퍼센트 인코딩된 URL 파라미터를 디코딩하려면 어떻게 하나요?
개별 파라미터 값에는 decodeURIComponent()를 사용하거나 URLSearchParams가 자동으로 처리하도록 하세요. new URLSearchParams(window.location.search).get('key')를 실행하면 값이 이미 디코딩되어 있습니다——다시 decodeURIComponent()를 호출할 필요가 없습니다. URLSearchParams 외부에서 원시 인코딩된 문자열을 받을 때만 직접 decodeURIComponent()를 호출하세요. 예를 들어 커스텀 헤더나 수동으로 파싱된 URL 프래그먼트에서. 참고: decodeURIComponent()는 베어 % 문자 같은 잘못된 형식의 시퀀스에 URIError를 발생시킵니다——입력이 사용자 데이터에서 온다면 try/catch로 감싸세요.
URLSearchParams를 사용하여 { filters: { status: "active" } } 같은 중첩 객체를 인코딩할 수 있나요?
아니요. URLSearchParams는 평면 키-값 쌍만 지원합니다. 각 값에 .toString()을 호출하여 중첩 객체를 직렬화하는데, 이는 "[object Object]"를 생성합니다——조용히 잘못된 결과입니다. 중첩 구조에는 qs 라이브러리를 사용하세요:qs.stringify({ filters: { status: 'active' } })는 filters%5Bstatus%5D=active(브래킷 표기법)를 생성하며, Express, Rails, Django REST Framework에서 이해합니다. 또는 중첩 데이터를 JSON 문자열로 직렬화하여 단일 파라미터 값으로 전달하세요:params.set('filters', JSON.stringify({ status: 'active' })).
Fetch API는 쿼리 파라미터를 자동으로 URL 인코딩하나요?
아니요. fetch()는 URL 문자열을 받아 그대로 네트워크 레이어에 전달합니다——쿼리 파라미터를 파싱하거나 인코딩하지 않습니다. 인코딩되지 않은 값을 URL 문자열에 연결하면 특수 문자가 요청을 손상시킵니다. 올바른 패턴은 fetch()에 전달하기 전에 URLSearchParams나 encodeURIComponent()로 URL을 구축하는 것입니다:const url = new URL('/api/search', base); url.searchParams.set('q', userInput); const res = await fetch(url). URL 생성자와 URLSearchParams를 함께 사용하면 깔끔한 API로 안전한 자동 인코딩이 실현됩니다.

관련 도구

코드를 작성하지 않고 한 번의 클릭으로 인코딩하거나 디코딩하려면, 문자열을 직접 URL Encoder 에 붙여넣으세요——브라우저에서 퍼센트 인코딩과 디코딩을 즉시 처리하며, 인코딩된 출력을 fetch 호출이나 터미널에 복사할 준비가 되어 있습니다.

AC
Alex ChenFront-end & Node.js Developer

Alex is a front-end and Node.js developer with extensive experience building web applications and developer tooling. He is passionate about web standards, browser APIs, and the JavaScript ecosystem. In his spare time he contributes to open-source projects and writes about modern JavaScript patterns, performance optimisation, and everything related to the web platform.

SL
Sophie Laurent기술 검토자

Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.