sori.studio

์›Œ๋“œํ”„๋ ˆ์Šค ๋กœ๊ทธ์ธ & ๊ธ€ ์ž‘์„ฑ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ (JWT ์ธ์ฆ)

๐Ÿ“˜ ์›Œ๋“œํ”„๋ ˆ์Šค REST API๋ฅผ ์ด์šฉํ•ด์„œ ๋‚˜๋งŒ์˜ ์‚ฌ์ดํŠธ ๋งŒ๋“ค๊ธฐ - 2ํŽธ

์›Œ๋“œํ”„๋ ˆ์Šค๋กœ ๋กœ๊ทธ์ธ & ๊ธ€์“ฐ๊ธฐ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๊ธฐ (JWT ์ธ์ฆ ์‚ฌ์šฉ)

 

์ง€๋‚œ ๊ธ€์—์„œ๋Š” ์›Œ๋“œํ”„๋ ˆ์Šค REST API๋ฅผ ์ด์šฉํ•ด์„œ ๋“ฑ๋ก๋œ ๊ฒŒ์‹œ๋ฌผ์„ ๊ฐ€์ ธ์™€์„œ ์›น์‚ฌ์ดํŠธ์— ํ‘œ์‹œํ•˜๋Š” ๊ฑธ ํ•ด๋ดค๋‹ค.
REST API๊ฐ€ ์ž˜ ๋™์ž‘ํ•˜๋Š”๊ฒƒ์„ ํ™•์ธํ–ˆ์œผ๋‹ˆ ์ด์ œ ์ด๊ฒƒ์„ ์ด์šฉํ•ด์„œ ์›ํ•˜๋Š” ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋ณผ ์ฐจ๋ก€์ด๋‹ค.

 

๋ฌด์—‡์„ ๋งŒ๋“ค ๊ฒƒ์ธ๊ฐ€

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋Š” ํŠธ์œ„ํ„ฐ์ฒ˜๋Ÿผ ์งง๊ณ  ๊ฐ„๋‹จํ•œ ๊ธ€์„ ๋‚จ๊ธฐ๋Š” ๊ตฌ์กฐ๋ฅผ ๋ชฉํ‘œ๋กœ ํ•˜๊ณ  ์žˆ๋‹ค.
๊ทธ ์ด์œ ๋Š”, ๋‚˜์—๊ฒŒ ์žˆ์—ˆ๋˜ ์ผ์ด๋‚˜ ๊ฒŒ์ž„ ํ”Œ๋ ˆ์ด ๊ธฐ๋ก, ๊ตฌ๋งคํ•œ ๋ฌผ๊ฑด ๋“ฑ ๋‹ค์–‘ํ•œ ์ผ์ƒ๋“ค์„ ๋ธ”๋กœ๊ทธ ๊ธ€์ด๋ผ๊ณ  ํ•˜๊ธฐ์—” ์• ๋งคํ•  ์ •๋„๋กœ ์งง๊ฒŒ ๋‚จ๊ธฐ๊ณ  ์‹ถ์—ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
“์–ธ์ œ, ์–ด๋–ค ์ผ์ด ์žˆ์—ˆ๋‹ค” ์ •๋„๋งŒ ๋น ๋ฅด๊ฒŒ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ๋Š” ํ˜•์‹์„ ๊ณ ๋ฏผํ•˜๋‹ค๊ฐ€, ํŠธ์œ„ํ„ฐ์ฒ˜๋Ÿผ ๊ฐ€๋ณ๊ฒŒ ๊ธ€์„ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๊ฐ€ ๊ฐ€์žฅ ์ ํ•ฉํ•˜๋‹ค๊ณ  ๋А๊ผˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ธฐ์กด ๋ธ”๋กœ๊ทธ์ฒ˜๋Ÿผ “๊ธ€ ์ž‘์„ฑ ํŽ˜์ด์ง€๋กœ ์ด๋™ → ์ œ๋ชฉ ์ž…๋ ฅ → ๋ณธ๋ฌธ ์ž‘์„ฑ” ๊ฐ™์€ ํ๋ฆ„์€ ์•„์˜ˆ ์—†๋‹ค.
ํ•˜๋‚˜์˜ ํ™”๋ฉด(index.html) ์•ˆ์—์„œ, ์ƒ๋‹จ์—๋Š” ๊ธ€ ์ž‘์„ฑ ๊ณต๊ฐ„์ด, ํ•˜๋‹จ์—๋Š” ์ž‘์„ฑ๋œ ๊ธ€ ๋ชฉ๋ก์ด ๋ฐ”๋กœ ์ด์–ด์ง€๋Š” ๊ตฌ์กฐ๋กœ ๋งŒ๋“ค์–ด๋ณผ ๊ฒƒ์ด๋‹ค.
๊ธฐ์™• ํŠธ์œ„ํ„ฐ์ฒ˜๋Ÿผ ๋งŒ๋“ค๊ธฐ๋กœ ํ–ˆ์œผ๋‹ˆ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค๋„ ๊ธ€์„ ์“ธ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด๋ฉด ์–ด๋–จ๊นŒ ํ•œ๋‹ค.

๊ทธ๋ž˜์„œ index.html์„ ์—ด๋ฉด ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋ผ๋ฉด ๋ฐ”๋กœ ๊ธ€์„ ์“ธ ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ณ , ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋ผ๋ฉด ๊ธ€์“ฐ๊ธฐ๋ฅผ ๋ง‰๊ณ  ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑํ•  ์˜ˆ์ •์ด๋‹ค.

 

 

์ธ์ฆ์€ ๊ผญ ํ•„์š”ํ•˜๋‹ค

REST API๋กœ ๊ธ€์„ ์ž‘์„ฑํ•˜๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๋ ค๋ฉด ์ธ์ฆ์ด ํ•„์š”ํ•˜๋‹ค.
๊ทธ๋ž˜์„œ ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” JWT ํ† ํฐ ๊ธฐ๋ฐ˜ ์ธ์ฆ์„ ๋„์ž…ํ•ด์„œ ๋กœ๊ทธ์ธ → ํ† ํฐ ๋ฐœ๊ธ‰ → ๊ธ€ ์ž‘์„ฑ ์š”์ฒญ ๊นŒ์ง€ ์—ฐ๊ฒฐํ•˜๋Š” ํ๋ฆ„์„ ๋งŒ๋“ ๋‹ค.

 

 

ํ”Œ๋Ÿฌ๊ทธ์ธ ์„ค์น˜: JWT Authentication for WP REST API

์›Œ๋“œํ”„๋ ˆ์Šค ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€์—์„œ ์ด ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•œ๋‹ค: JWT Authentication for WP REST API

 

์„ค์น˜ ํ›„ ์•„๋ž˜ ์„ค์ •๋“ค์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

 

wp-config.php์— ์ถ”๊ฐ€:

define('JWT_AUTH_SECRET_KEY', '์ ๋‹นํžˆ_๋ณต์žกํ•œ_๋น„๋ฐ€ํ‚ค');
define('JWT_AUTH_CORS_ENABLE', true);

์—ฌ๊ธฐ์„œ ๋น„๋ฐ€ํ‚ค๋Š” https://randomkeygen.com/ ๊ฐ™์€ ์‚ฌ์ดํŠธ๋ฅผ ์ด์šฉํ•ด์„œ ๊ฐ€์ ธ์™€๋„ ์ข‹๊ณ , ์ง์ ‘ 'e2f7c6b4a8d9e1f0b3c4d5f6e7f8g9h0i1j2k3l4m5n6o7p8'์™€ ๊ฐ™์€ 16์ง„์ˆ˜ ํ˜•์‹์˜ ๋žœ๋คํ•œ ๊ฐ’์„ ์ง€์ •ํ•ด๋„ ๋œ๋‹ค.

 

.htaccess (Apache ์‚ฌ์šฉ ์‹œ):

RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1

 

 

๋กœ๊ทธ์ธ ์š”์ฒญ: ํ† ํฐ ๋ฐ›์•„์˜ค๊ธฐ

๋กœ๊ทธ์ธ ํผ์—์„œ ์ž…๋ ฅํ•œ ์ •๋ณด๋กœ ์•„๋ž˜ API์— ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ๋œ๋‹ค:

POST /wp-json/jwt-auth/v1/token

 

 

์š”์ฒญ ๋ฐ”๋””๋Š” ์ด๋ ‡๊ฒŒ:

{
  "username": "์•„์ด๋””",
  "password": "๋น„๋ฐ€๋ฒˆํ˜ธ"
}

 

์‘๋‹ต์œผ๋กœ๋Š” ์ด๋ ‡๊ฒŒ ์ƒ๊ธด ํ† ํฐ์ด ์˜จ๋‹ค:

{
  "token": "eyJ0eXAiOiJKV1QiLCJhbGci...",
  "user_email": "example@email.com",
  "user_nicename": "ํ™๊ธธ๋™",
  ...
}

 

์—ฌ๊ธฐ์„œ ์ด token๋งŒ ๋ฝ‘์•„์„œ localStorage์— ์ €์žฅํ•ด๋‘๋ฉด ๋œ๋‹ค.

 

 

๋กœ๊ทธ์ธ ํ™”๋ฉด(login.html)์˜ ์Šคํฌ๋ฆฝํŠธ:

async function login() {
  const username = usernameInput.value;
  const password = passwordInput.value;

  const response = await fetch(
    'https://myDomain.com/wp-json/jwt-auth/v1/token',
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    },
  );

  const result = await response.json();

  if (result.token) {
    localStorage.setItem('jwt_token', result.token); // ํ† ํฐ ์ €์žฅ
    document.getElementById('message').innerText = '๋กœ๊ทธ์ธ ์„ฑ๊ณต!';
    window.location.href = 'index.html'; // ๋ฉ”์ธ ํŽ˜์ด์ง€๋กœ ์ด๋™
  } else {
    document.getElementById('message').innerText = '๋กœ๊ทธ์ธ ์‹คํŒจ: ' + result.message;
  }
}

 

 

index.html์—์„œ ํ† ํฐ ๊ฒ€์‚ฌํ•˜๊ธฐ

index.html์„ ์—ด์—ˆ์„ ๋•Œ, ํ† ํฐ์ด ์—†๋‹ค๋ฉด ๋ฐ”๋กœ login.html๋กœ ๋ณด๋‚ด๋ฒ„๋ฆฐ๋‹ค.
HTML๋กœ ์›น์„ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด ๋งค ํŽ˜์ด์ง€ ์ด ๋™์ž‘์ด ํ•„์š”ํ•˜๋‹ค.

const token = localStorage.getItem('jwt_token');
if (!token) {
  window.location.href = 'login.html';
}

 

 

๊ฒŒ์‹œ๋ฌผ ์ž‘์„ฑํ•˜๊ธฐ

๋‹ค์‹œํ•œ๋ฒˆ ์ •๋ฆฌํ•˜๋Š” ๋ชฉํ‘œ๋กœ ํ•˜๊ณ ์žˆ๋Š” index.html ๊ตฌ์กฐ๋ฅผ ์š”์•ฝํ•ด๋ณธ๋‹ค.

  • ๊ธ€ ์ž‘์„ฑ ์˜์—ญ: ์ƒ๋‹จ์— ๋ฐ”๋กœ ์ž…๋ ฅ
  • ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ๋„ ์—ฌ๊ธฐ์„œ ์ฒ˜๋ฆฌ (์ด๋ฒˆ ๋‚ด์šฉ์—์„œ๋Š” ์ œ์™ธ)
  • ๊ฒŒ์‹œํ•˜๊ธฐ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด REST API๋กœ POST ์š”์ฒญ

๊ธ€ ์ž‘์„ฑ์„ ์œ„ํ•œ ๋ณ„๋„ ํŽ˜์ด์ง€๋Š” ์—†๋‹ค.
ํŠธ์œ„ํ„ฐ๊ฐ™์€ ๋А๋‚Œ์˜ ๋ฉ”์ธ ํ™”๋ฉด์—์„œ ๋ชจ๋“  ๊ฑธ ์ฒ˜๋ฆฌํ•œ๋‹ค.

 

 

 

๊ธ€ ์ž‘์„ฑ ์˜์—ญ(index.html):

<textarea 
  id="postContent" placeholder="๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋‚˜์š”?" 
  rows="3" style="width:100%;">
</textarea>
<button onclick="submitPost()">๊ฒŒ์‹œํ•˜๊ธฐ</button>

 

๊ธ€ ์ž‘์„ฑ ์š”์ฒญ(index.js) ํ•จ์ˆ˜:

async function submitPost() {
  const content = document.getElementById('postContent').value;
  if (!content.trim()) return;

  const response = await fetch(apiUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}` // JWT ์ธ์ฆ ์ถ”๊ฐ€
    },
    body: JSON.stringify({
      title: '์ƒˆ ๊ฒŒ์‹œ๋ฌผ',
      content: content,
      status: 'publish'
    })
  });

  if (response.ok) {
    document.getElementById('postContent').value = '';
    fetchPosts();
  } else {
    alert('๊ฒŒ์‹œ ์‹คํŒจ! ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜์„ธ์š”.');
  }
}

 

 

๐Ÿ”– ์ฐธ๊ณ : ์›Œ๋“œํ”„๋ ˆ์Šค status ๊ฐ’ ์ข…๋ฅ˜

 

  • publish: ๊ณต๊ฐœ ๊ธ€. ๋ˆ„๊ตฌ๋‚˜ ๋ณผ ์ˆ˜ ์žˆ์Œ.
  • draft: ์ž„์‹œ ๊ธ€. ๋กœ๊ทธ์ธํ•œ ์ž‘์„ฑ์ž๋งŒ ๋ณผ ์ˆ˜ ์žˆ์Œ.
  • pending: ๊ฒ€ํ†  ๋Œ€๊ธฐ ๊ธ€. ๊ด€๋ฆฌ์ž ์Šน์ธ ํ›„ ๊ณต๊ฐœ๋˜๋Š” ๊ตฌ์กฐ์— ์‚ฌ์šฉ๋จ.
  • private: ๋น„๊ณต๊ฐœ ๊ธ€. ๋กœ๊ทธ์ธํ•œ ์ž‘์„ฑ์ž ๋˜๋Š” ๊ด€๋ฆฌ์ž๋งŒ ๋ณผ ์ˆ˜ ์žˆ์Œ.
  • future: ์˜ˆ์•ฝ ๋ฐœํ–‰. date ํ•„๋“œ๋ฅผ ํ•จ๊ป˜ ์„ค์ •ํ•ด์•ผ ํ•จ.
  • trash: ํœด์ง€ํ†ต์œผ๋กœ ์ด๋™๋œ ๊ธ€.
  • inherit: ์ฒจ๋ถ€ ํŒŒ์ผ ๋“ฑ์— ์‚ฌ์šฉ๋˜๋Š” ๋‚ด๋ถ€์šฉ ์ƒํƒœ. ์ง์ ‘ ์“ธ ์ผ์€ ๊ฑฐ์˜ ์—†์Œ.
  • auto-draft: ์ž๋™ ์ €์žฅ๋œ ์ดˆ์•ˆ. ์›Œ๋“œํ”„๋ ˆ์Šค ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ. ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ ์•ˆ ํ•จ.

 

์˜ˆ์‹œ: ์˜ˆ์•ฝ๋ฐœํ–‰

{
  "title": "์˜ˆ์•ฝ๋œ ๊ธ€",
  "content": "์ด ๊ธ€์€ ๋‚˜์ค‘์— ์ž๋™์œผ๋กœ ๊ณต๊ฐœ๋ฉ๋‹ˆ๋‹ค.",
  "status": "future",
  "date": "2025-04-10T10:00:00"
}

 

ํ•„์š”์— ๋”ฐ๋ผ ๋ฒ„ํŠผ UI๋ฅผ ๋‹ค๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•˜์—ฌ "์ž„์‹œ ์ €์žฅ" ๋ฒ„ํŠผ ๋ˆ„๋ฅด๋ฉด "status": "draft"๋กœ ๋ณด๋‚ด๊ณ , "๋ฐœํ–‰ํ•˜๊ธฐ" ๋ฒ„ํŠผ ๋ˆ„๋ฅด๋ฉด "publish"๋กœ ๋ณด๋‚ด๋Š” ์‹์œผ๋กœ ๊ฒŒ์‹œ๋ฌผ์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ง€๊ธˆ ๋‚ด๊ฐ€ ๋งŒ๋“ค๋ ค๊ณ ํ•˜๋Š” ์‚ฌ์ดํŠธ์—์„œ๋Š” ์ด๋Ÿฐ ๊ธฐ๋Šฅ์ด ํ•„์š” ์—†๊ธฐ์— ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์„ ์˜ˆ์ •.