์๋ํ๋ ์ค ๋ก๊ทธ์ธ & ๊ธ ์์ฑ ์ง์ ๊ตฌํํ๊ธฐ (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"๋ก ๋ณด๋ด๋ ์์ผ๋ก ๊ฒ์๋ฌผ์ ์์ฑํ ์ ์์ง๋ง ์ง๊ธ ๋ด๊ฐ ๋ง๋ค๋ ค๊ณ ํ๋ ์ฌ์ดํธ์์๋ ์ด๋ฐ ๊ธฐ๋ฅ์ด ํ์ ์๊ธฐ์ ์ฌ์ฉํ์ง๋ ์์ ์์ .