๐Ÿ”
โ† ๊ฐ€์ด๋“œ ๋ชฉ๋ก์œผ๋กœ

Base64 URL ์•ˆ์ „ ์ธ์ฝ”๋”ฉ: ์ •์˜์™€ ์ค‘์š”์„ฑ

ยท ํƒœ๊ทธ: base64, url-safe, base64url, jwt, encoding, web-development, api

Base64 URL ์•ˆ์ „ ์ธ์ฝ”๋”ฉ์ด๋ž€?

Base64 URL ์•ˆ์ „ ์ธ์ฝ”๋”ฉ(Base64url์ด๋ผ๊ณ ๋„ ํ•จ)์€ URL๊ณผ ํŒŒ์ผ ์ด๋ฆ„์—์„œ ํŠน๋ณ„ํ•œ ์˜๋ฏธ๋ฅผ ๊ฐ€์ง€๋Š” ๋ฌธ์ž๋“ค์„ ์•ˆ์ „ํ•œ ๋Œ€์ฒด ๋ฌธ์ž๋กœ ๋ฐ”๊พผ ํ‘œ์ค€ Base64์˜ ๋ณ€ํ˜•์ž…๋‹ˆ๋‹ค. ํ‘œ์ค€ Base64๋Š” 64์ž ์•ŒํŒŒ๋ฒณ์˜ ์ผ๋ถ€๋กœ +์™€ /๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ํŒจ๋”ฉ์œผ๋กœ =๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์ด ์„ธ ๋ฌธ์ž ๋ชจ๋‘ URL, ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด, ํŒŒ์ผ ๊ฒฝ๋กœ์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

Base64url์€ ๋‘ ๊ฐ€์ง€ ๊ฐ„๋‹จํ•œ ์น˜ํ™˜์œผ๋กœ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค:

| ํ‘œ์ค€ Base64 | URL ์•ˆ์ „ Base64 | |-----------------|-----------------| | + (๋”ํ•˜๊ธฐ) | - (๋นผ๊ธฐ) | | / (์Šฌ๋ž˜์‹œ) | _ (๋ฐ‘์ค„) | | = (ํŒจ๋”ฉ) | ์ƒ๋žต (์ผ๋ฐ˜์ ) |

ํ‘œ์ค€ Base64๊ฐ€ URL์„ ๊นจ๋œจ๋ฆฌ๋Š” ์ด์œ 

+๋‚˜ /๊ฐ€ ํฌํ•จ๋œ Base64 ์ธ์ฝ”๋”ฉ ๋ฌธ์ž์—ด์ด URL์— ๋‚˜ํƒ€๋‚˜๋ฉด, ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„์˜ ๋™์ž‘์ด ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

+ ๋ฌธ์ œ

URL ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์—์„œ + ๋ฌธ์ž๋Š” ๊ณต๋ฐฑ์œผ๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค. ์ด ๋™์ž‘์€ application/x-www-form-urlencoded ๋ช…์„ธ์—์„œ ๋น„๋กฏ๋˜๋ฉฐ, ๋””์ฝ”๋”ฉ๋œ ๋ฐ์ดํ„ฐ์— ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๊ณต๋ฐฑ์ด ์ƒ๊ธฐ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, Pj4+Pz8/Pw==์™€ ๊ฐ™์€ Base64 ๋ฌธ์ž์—ด์—๋Š” + ๋ฌธ์ž๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด URL ๋งค๊ฐœ๋ณ€์ˆ˜ ?token=Pj4+Pz8/Pw==์— ๋‚˜ํƒ€๋‚˜๋ฉด, ์„œ๋ฒ„๋Š” ?token=Pj4 Pz8/Pw==๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค โ€” +๊ฐ€ ๊ณต๋ฐฑ์ด ๋ฉ๋‹ˆ๋‹ค.

/ ๋ฌธ์ œ

URL ๊ฒฝ๋กœ์—์„œ / ๋ฌธ์ž๋Š” ๊ฒฝ๋กœ ๊ตฌ๋ถ„์ž์ž…๋‹ˆ๋‹ค. ab/CD+Ef==์™€ ๊ฐ™์€ Base64 ๋ฌธ์ž์—ด์—๋Š” /๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ๋””๋ ‰ํ„ฐ๋ฆฌ ๋ ˆ๋ฒจ๋กœ ํ•ด์„๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด URL ์„ธ๊ทธ๋จผํŠธ์— ๋‚˜ํƒ€๋‚˜๋ฉด ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„ ๋ชจ๋‘ ๊ตฌ์กฐ๋ฅผ ์ž˜๋ชป ํ•ด์„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

= ๋ฌธ์ œ

= ๋ฌธ์ž๋Š” ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์—์„œ ํ‚ค-๊ฐ’ ์Œ์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ?data=SGVsbG8=์˜ ๋งˆ์ง€๋ง‰ =์€ ๋ชจํ˜ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค โ€” =์ด Base64 ํŒจ๋”ฉ์˜ ์ผ๋ถ€์ธ๊ฐ€์š”, ์•„๋‹ˆ๋ฉด ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜ ๊ตฌ๋ฌธ์ธ๊ฐ€์š”?

ํผ์„ผํŠธ ์ธ์ฝ”๋”ฉ์œผ๋กœ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์ง€๋งŒ(+๋Š” %2B๋กœ, /๋Š” %2F๋กœ), ๋ฌธ์ž์—ด ๊ธธ์ด๊ฐ€ ๋Š˜์–ด๋‚˜๊ณ  ๋””๋ฒ„๊น…์ด ๋” ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค. Base64url์ด ๋” ๊น”๋”ํ•œ ํ•ด๊ฒฐ์ฑ…์ž…๋‹ˆ๋‹ค.

Base64url์˜ ์ž‘๋™ ๋ฐฉ์‹

Base64url์€ RFC 4648 ์„น์…˜ 5์— ๊ณต์‹์ ์œผ๋กœ ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ‘œ์ค€ Base64์™€ ์ •ํ™•ํžˆ ๋™์ผํ•œ ์•Œ๊ณ ๋ฆฌ์ฆ˜(3๋ฐ”์ดํŠธ๊ฐ€ 4๋ฌธ์ž๊ฐ€ ๋จ)์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์•ŒํŒŒ๋ฒณ์˜ 62๋ฒˆ๊ณผ 63๋ฒˆ ์œ„์น˜์—์„œ ํ•œ ๋ฌธ์ž์”ฉ ์น˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค:

  • ์•ŒํŒŒ๋ฒณ ์ธ๋ฑ์Šค 62: + โ†’ - (๋นผ๊ธฐ)
  • ์•ŒํŒŒ๋ฒณ ์ธ๋ฑ์Šค 63: / โ†’ _ (๋ฐ‘์ค„)

ํŒจ๋”ฉ(=)์€ ์ผ๋ฐ˜์ ์œผ๋กœ ์ œ๊ฑฐ๋˜๋Š”๋ฐ, ๋””์ฝ”๋”ฉ ์‹œ ๋ฌธ์ž์—ด ๊ธธ์ด๋กœ๋ถ€ํ„ฐ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. Base64url ๋ฌธ์ž์—ด์€ ์ถ”๊ฐ€ ์ด์Šค์ผ€์ดํ”„ ์—†์ด ํ•ญ์ƒ ์œ ํšจํ•œ URL ๊ตฌ์„ฑ ์š”์†Œ์ž…๋‹ˆ๋‹ค.

๋ณ€ํ™˜ ์ „ํ›„

๋‹ค์Œ์€ ๋™์ผํ•œ ์ด์ง„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‘ ํ˜•์‹์—์„œ ์–ด๋–ป๊ฒŒ ๋ณด์ด๋Š”์ง€ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค:

ํ‘œ์ค€:    Pj4+Pz8/Pw==
URL ์•ˆ์ „: Pj4-Pz8_Pw

Base64url์ด ์‚ฌ์šฉ๋˜๋Š” ๊ณณ

JSON Web Token (JWT)

JWT๋Š” Base64url์˜ ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ์‚ฌ์šฉ์ฒ˜์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  JWT๋Š” ํ—ค๋”, ํŽ˜์ด๋กœ๋“œ, ์„œ๋ช…์˜ ์„ธ ๋ถ€๋ถ„์œผ๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ, ๊ฐ๊ฐ URL ์•ˆ์ „ Base64๋กœ ์ธ์ฝ”๋”ฉ๋˜๊ณ  ์ ์œผ๋กœ ๊ตฌ๋ถ„๋ฉ๋‹ˆ๋‹ค:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNqP0m2iTgCjD0

๋Œ€์‹œ์™€ ๋ฐ‘์ค„์ด ๋ณด์ด๊ณ  ๋”ํ•˜๊ธฐ ๊ธฐํ˜ธ๋‚˜ ์Šฌ๋ž˜์‹œ๊ฐ€ ์—†๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. JWT๊ฐ€ ํ‘œ์ค€ Base64๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด, ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ ๋ฐฉ์‹์ธ URL ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ๋  ๋•Œ ํ† ํฐ์ด ๊นจ์งˆ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

OAuth ๋ฐ OpenID Connect

OAuth 2.0๊ณผ OpenID Connect ๋ชจ๋‘ ์•ก์„ธ์Šค ํ† ํฐ๊ณผ ID ํ† ํฐ์— JWT๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, Base64url ์ธ์ฝ”๋”ฉ์€ ํ˜„๋Œ€ ์ธ์ฆ์˜ ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค. ์ธ์ฆ ์ฝ”๋“œ ํ”Œ๋กœ์šฐ, ์•”์‹œ์  ํ”Œ๋กœ์šฐ, ํด๋ผ์ด์–ธํŠธ ์ž๊ฒฉ ์ฆ๋ช… ํ”Œ๋กœ์šฐ ๋ชจ๋‘ Base64url์„ ์‚ฌ์šฉํ•˜๋Š” ํ† ํฐ์„ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

์›น API ๋ฐ ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜

๋งŽ์€ REST ๋ฐ GraphQL API๊ฐ€ ํŽ˜์ด์ง€๋„ค์ด์…˜ ์ปค์„œ, ํ•„ํ„ฐ ๋˜๋Š” ์ธ์ฝ”๋”ฉ๋œ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์œ„ํ•ด ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜์— Base64 ์ธ์ฝ”๋”ฉ ๋ฐ์ดํ„ฐ๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. Base64url์€ ์ด๋Ÿฌํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์ถ”๊ฐ€ URL ์ธ์ฝ”๋”ฉ ์—†์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

์‹ค์ œ ์‚ฌ๋ก€ โ€” API ์ปค์„œ ํŽ˜์ด์ง€๋„ค์ด์…˜:

GET /api/users?cursor=MjAyNC0wMS0xNVQxMDozMDowMFo

GitHub์˜ API๋Š” ๋Œ€๋Ÿ‰ ๊ฒฐ๊ณผ ์ง‘ํ•ฉ์„ ํŽ˜์ด์ง€๋„ค์ด์…˜ํ•  ๋•Œ ์ด์™€ ์ •ํ™•ํžˆ ๋™์ผํ•œ ํŒจํ„ด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์ผ ์ด๋ฆ„ ๋ฐ ํŒŒ์ผ ์ €์žฅ์†Œ

Base64url ๋ฌธ์ž์—ด์€ ๊ฒฝ๋กœ ๊ตฌ๋ถ„์ž(/)๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š๊ณ , ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ํ˜ผ๋ž€์Šค๋Ÿฝ๊ฒŒ ํ•˜๋Š” ํŠน์ˆ˜ ๋ฌธ์ž๋„ ์—†์œผ๋ฉฐ, Windows์—์„œ ์œ ํšจํ•˜์ง€ ์•Š์€ ๋ฌธ์ž(:, *, ? ๋“ฑ)๋„ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์•ˆ์ „ํ•œ ํŒŒ์ผ ์ด๋ฆ„์ด ๋ฉ๋‹ˆ๋‹ค.

์ฝ˜ํ…์ธ  ์ „์†ก ๋„คํŠธ์›Œํฌ ๋ฐ ์บ์‹œ ํ‚ค

CDN๊ณผ ์บ์‹ฑ ๊ณ„์ธต์€ ์ข…์ข… Base64url๋กœ ์ธ์ฝ”๋”ฉ๋œ ๊ฐ’์„ ์บ์‹œ ํ‚ค๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. /๊ฐ€ ํฌํ•จ๋œ ํ‘œ์ค€ Base64๋Š” ์บ์‹œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ๊ฐ€์งœ ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ณ„์ธต ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด ๋ชจํ˜ธํ•œ ํ‚ค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ‘œ์ค€ Base64์™€ Base64url ๊ฐ„ ๋ณ€ํ™˜

๋‘ ๋ณ€ํ˜• ๊ฐ„ ๋ณ€ํ™˜์€ ์–ด๋–ค ์–ธ์–ด์—์„œ๋“  ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค:

JavaScript

// Standard Base64 to URL-safe Base64
function base64ToBase64url(base64) {
  return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// URL-safe Base64 to Standard Base64
function base64urlToBase64(base64url) {
  let result = base64url.replace(/-/g, '+').replace(/_/g, '/');
  // Restore padding
  while (result.length % 4) {
    result += '=';
  }
  return result;
}

Node.js

Node.js๋Š” ๋ฒ„์ „ 15.7.0๋ถ€ํ„ฐ ๋„ค์ดํ‹ฐ๋ธŒ Base64url ์ง€์›์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

// Encode to Base64url
const encodedUrl = Buffer.from('Hello, world!').toString('base64url');
console.log(encodedUrl);
// Output: SGVsbG8sIHdvcmxkIQ

// Decode from Base64url
const decoded = Buffer.from(encodedUrl, 'base64url').toString('utf-8');
console.log(decoded);
// Output: Hello, world!

Python

import base64

# Encode to Base64url
encoded = base64.urlsafe_b64encode(b"Hello, world!")
print(encoded.decode())
# Output: SGVsbG8sIHdvcmxkIQ==

# Decode from Base64url
decoded = base64.urlsafe_b64decode(encoded)
print(decoded.decode())
# Output: Hello, world!

ํ‘œ์ค€ Base64 vs Base64url ์‚ฌ์šฉ ์‹œ๊ธฐ

ํ‘œ์ค€ Base64๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ:

  • ์ด๋ฉ”์ผ ์ฒจ๋ถ€ ํŒŒ์ผ ์ธ์ฝ”๋”ฉ (MIME ํ‘œ์ค€์—์„œ ์š”๊ตฌ)
  • HTML/CSS ์ž„๋ฒ ๋”ฉ์šฉ ๋ฐ์ดํ„ฐ URL ์ƒ์„ฑ
  • URL ์•ˆ์ „์„ฑ์ด ์ค‘์š”ํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋ฐ์ดํ„ฐ ์ €์žฅ
  • ํ‘œ์ค€ Base64๋ฅผ ๊ธฐ๋Œ€ํ•˜๋Š” ๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ๊ณผ์˜ ์ƒํ˜ธ ์ž‘์šฉ

Base64url์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ:

  • JWT ํ† ํฐ ์ƒ์„ฑ ๋˜๋Š” ํŒŒ์‹ฑ
  • URL ์ฟผ๋ฆฌ ๋งค๊ฐœ๋ณ€์ˆ˜๋‚˜ ๊ฒฝ๋กœ ์„ธ๊ทธ๋จผํŠธ์— ์ธ์ฝ”๋”ฉ๋œ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
  • ์ด์ง„ ๋ฐ์ดํ„ฐ๋กœ๋ถ€ํ„ฐ ํŒŒ์ผ ์ด๋ฆ„ ์ƒ์„ฑ
  • ์บ์‹œ ํ‚ค๋‚˜ ์‹๋ณ„์ž ์ƒ์„ฑ
  • OAuth ํ† ํฐ ๋˜๋Š” OpenID Connect ์ž‘์—…

์˜จ๋ผ์ธ์œผ๋กœ Base64 URL ์•ˆ์ „ ์ธ์ฝ”๋”ฉ ์ฒดํ—˜ํ•˜๊ธฐ

๋ฌด๋ฃŒ ์˜จ๋ผ์ธ Base64 ์ธ์ฝ”๋” ๋ฐ ๋””์ฝ”๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ‘œ์ค€ Base64์™€ URL ์•ˆ์ „ Base64 ํ˜•์‹ ๊ฐ„์— ๋ณ€ํ™˜ํ•˜์„ธ์š”. ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ™์—ฌ๋„ฃ๊ณ  ์›ํ•˜๋Š” ๋ณ€ํ˜•์„ ์„ ํƒํ•˜๋ฉด ์ฆ‰์‹œ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Base64 URL ์•ˆ์ „ ์ธ์ฝ”๋”ฉ: ์ •์˜์™€ ์ค‘์š”์„ฑ - CoolTool