

์นด์นด์ค
Kakao Developers
์นด์นด์ค API๋ฅผ ํ์ฉํ์ฌ ๋ค์ํ ์ดํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํด ๋ณด์ธ์. ์นด์นด์ค ๋ก๊ทธ์ธ, ๋ฉ์์ง ๋ณด๋ด๊ธฐ, ์น๊ตฌ API, ์ธ๊ณต์ง๋ฅ API ๋ฑ์ ์ ๊ณตํฉ๋๋ค.
developers.kakao.com
๋ค์ด๋ฒ
https://developers.naver.com/main/
NAVER Developers
๋ค์ด๋ฒ ์คํ API๋ค์ ํ์ฉํด ๊ฐ๋ฐ์๋ค์ด ๋ค์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ์ ์๋๋ก API ๊ฐ์ด๋์ SDK๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ ๊ณต์ค์ธ ์คํ API์๋ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ, ๊ฒ์, ๋จ์ถURL, ์บก์ฐจ๋ฅผ ๋น๋กฏ ๊ธฐ๊ณ๋ฒ์ญ, ์
developers.naver.com
๊ตฌ๊ธ
https://developers.google.com/?hl=ko
Google for Developers - AI์ ํด๋ผ์ฐ๋๋ถํฐ ๋ชจ๋ฐ์ผ๊ณผ ์น๊น์ง
๊ฐ๋ฐ์ ๋ฆฌ์์ค, ์ปค๋ฎค๋ํฐ ์ด๋ฒคํธ, ์๊ฐ์ ์ฃผ๋ ์คํ ๋ฆฌ๋ฅผ ํ์ํ์ฌ ๋ ์ค๋งํธํ๊ฒ ๊ฐ๋ฐํ๊ณ ๋ ๋น ๋ฅด๊ฒ ์ถ์ํ์ธ์.
developers.google.com

์นด์นด์ค๋ง ์๋๊ฒ ๋ง์, api๋ auth๊ฐ ๋ถ์ผ๋ฉด ์๋จ
@gateway/src/main/resources/application.yaml ์ฌ๊ธฐ์ ์ ๋ ์นด์์ ์ค์ ํ ๊ฒ์ฒ๋ผ, ๋ง์ดํฌ๋ก ์๋น์ค ์ปจํ ์ด๋๋ฅผ ๋ก๋๋ฐธ๋ฐ์๋ก ๋ฑ๋กํด์ ์ฒ๋ฆฌํด์ค. /auth๋ ์๋ธ๋ผ์ฐํฐ๋ก ์ฒ๋ฆฌํด์ค.

๊ฒ์ดํธ ์จ์ด ์ผ๋ฏ์์ ์ด๋ฌํ ๋ช ๋ น์ด ์ ๋ ฅํด์ ์คํํ๊ธฐ

@gateway/src/main/resources/application.yaml ์ฌ๊ธฐ์ ์ ๋ ์นด์์ ์ค์ ํ ๊ฒ์ฒ๋ผ, ๋ง์ดํฌ๋ก ์๋น์ค ์ปจํ ์ด๋๋ฅผ ๋ก๋๋ฐธ๋ฐ์๋ก ๋ฑ๋กํด์ ์ฒ๋ฆฌํด์ค. /auth๋ ์๋ธ๋ผ์ฐํฐ๋ก ์ฒ๋ฆฌํด์ค.
-lb๊ฐ ์๊ฒ ํด์ผํจ
์ด์ ํ๋๋ก ์นด์นด์ค ๋ก๊ทธ์ธ ๊ตฌํํ ๊ฒ ์ฒ๋ผ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ์ด๋ ๊ตฌ๊ธ ๋ก๊ทธ์ธ ์ ๊ตฌํํ๋ ค๊ณ ํ๋๋ฐ ๋ฐฑ์๋์ ํ๋ก ํธ์๋๋ก ๋ฐ๋ก ํด์ ์ฐ๊ฒฐํ ๊ฑฐ์ผ

๐ฆ 2. ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ๊ตฌํ ๋ฐฉ๋ฒ
โ 2-1. ๋ค์ด๋ฒ ๋ก๊ทธ์ธ ํค(client id, secret) ์ป๋ ๋ฒ
1) ๋ค์ด๋ฒ ๊ฐ๋ฐ์ ์ผํฐ ์ ์
2) "์ ํ๋ฆฌ์ผ์ด์ ๋ฑ๋ก" ํด๋ฆญ
3) ๋ค์ ํญ๋ชฉ ์ค์
- ์ด๋ฆ: my-app-login (์๋ฌด๊ฑฐ๋)
- ์ฌ์ฉ API: ๋ค์ด๋ฒ ๋ก๊ทธ์ธ
- ํ๊ฒฝ: ์น
- Callback URL(์ค์!)(๋ฐฑ์๋ ์ฃผ์ ๊ธฐ์ค)
-
http://localhost:8080/auth/naver/callback

4) ๋ฑ๋กํ๋ฉด ์๋ ์ ๋ณด๊ฐ ๋์ด
- Client ID
- Client Secret
โก ๋ฐฑ์๋์ ํ๊ฒฝ๋ณ์๋ก ๋ฃ๊ธฐ
๐ฆ 3. ๊ตฌ๊ธ ๋ก๊ทธ์ธ ๊ตฌํ ๋ฐฉ๋ฒ
โ 3-1. ๊ตฌ๊ธ ๋ก๊ทธ์ธ ํค ๋ฐ๋ ๋ฒ
1) Google Cloud Console ์ ์
https://console.cloud.google.com
2) ํ๋ก์ ํธ ์์ฑ
3) API ๋ฐ ์๋น์ค → OAuth ๋์ ํ๋ฉด ์ค์
4) OAuth 2.0 ํด๋ผ์ด์ธํธ ID ๋ง๋ค๊ธฐ
- ์ ํ: ์น ์ ํ๋ฆฌ์ผ์ด์
- ์ด๋ฆ: google-login
- ์น์ธ๋ redirect URI:
-
http://localhost:8080/auth/google/callback
5) ๋ฐ๊ธ๋ ์ ๋ณด ํ์ธ
- Client ID
- Client Secret
โก ๋ฐฑ์๋ ํ๊ฒฝ๋ณ์์ ์ถ๊ฐ

ํ๋ฉด์ด๋ ๋ฐฑ์๋๋ ๊ฒฝ๋ก ์ฐ๊ฒฐํด์ฃผ๊ธฐ

๋ฐฑ์๋์์ ๊ฐ ์์ ๋ก๊ทธ์ธ ์๋ํฌ์ธํธ(kakao/login, naver/login, google/login)๋ฅผ ๊ตฌํํ๋ฉด์ ํด๋น ์๋น์ค์ ํค์ URI๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
์ฆ, ์๋ ค์ค์ผ ํ ๊ฒ
๋ฐฑ์๋ ํ ๋๋ ๋ณธ์ธ์ด ๋ฐฑ์๋๋ฅผ ๊ตฌํํ๋ค๋ฉด: "ํ๋ก ํธ์์ /api/{provider}/login์ ํธ์ถํ ๋ http://localhost:8080/<provider-start>๋ก ๋ณด๋ด๊ณ , ์ฝ๋ฐฑ ๋๋๋ฉด http://localhost:3000/dashboard/{provider}๋ก redirect ํด ์ฃผ์ธ์"๋ผ๊ณ ์ ๋ฆฌํ๋ฉด ๋ฉ๋๋ค

ํ๋ก ํธ์๋์์ ์งํํ ์์
- app/(auth)/login/page.tsx
- ์นด์นด์ค/๋ค์ด๋ฒ/๊ตฌ๊ธ ๋ฒํผ์ด ๊ฐ๊ฐ /api/auth/{provider}๋ก ์ด๋ํ๋๋ก ์ ๋ฆฌํ์ต๋๋ค.
- ์ค๋ณต๋๋ ๋ฆฌ๋ค์ด๋ ํธ ๋ก์ง์ redirectToProvider ํฌํผ๋ก ๋ฌถ์ด ๊น๋ํ๊ฒ ์ ์งํ์ต๋๋ค.
- Next.js API ๋ผ์ฐํธ ์ถ๊ฐ
- app/api/auth/kakao/route.ts
- app/api/auth/naver/route.ts
- app/api/auth/google/route.ts
- ๊ฐ ๋ผ์ฐํธ๋ ์๋ฒ ์ฌ์ด๋์์ ๋ฐฑ์๋ ๋ก๊ทธ์ธ ์์ URL๋ก ๋ฐ๋ก ๋ฆฌ๋ค์ด๋ ํธํฉ๋๋ค.
- URL์ ํ๊ฒฝ๋ณ์(BACKEND_KAKAO_LOGIN_URL, BACKEND_NAVER_LOGIN_URL, BACKEND_GOOGLE_LOGIN_URL)์์ ์ฝ์ผ๋ฉฐ, ๋ฏธ์ค์ ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก http://localhost:8080/...๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๋ฐฑ์๋ ํ์ ์ ๋ฌํ ๋ด์ฉ
- ์๋์ ๊ฐ์ “๋ก๊ทธ์ธ ์์ URL”์ ๋ ธ์ถํด ์ฃผ์ธ์(์ค์ ๊ฒฝ๋ก์ ๋ง์ถฐ ์กฐ์ ๊ฐ๋ฅ).
- ์นด์นด์ค: http://localhost:8080/oauth2/kakao/login
- ๋ค์ด๋ฒ: http://localhost:8080/auth/naver/login
- ๊ตฌ๊ธ: http://localhost:8080/auth/google/login
- ์ URL์ ํ๋ก ํธ .env.local์ ๋ค์์ฒ๋ผ ์ง์ ํ๋ฉด ๋ฉ๋๋ค.
- ์์ ์ธ์ฆ ์๋ฃ ํ ์ฝ๋ฐฑ์์ ํ๋ก ํธ ์ฑ๊ณต ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธํด ์ฃผ์ธ์.
- ์นด์นด์ค: http://localhost:3000/dashboard/kakao
- ๋ค์ด๋ฒ: http://localhost:3000/dashboard/naver
- ๊ตฌ๊ธ: http://localhost:3000/dashboard/google
์ด์ ํ๋ก ํธ์์๋ ๋ฒํผ → Next API → ๋ฐฑ์๋ ๋ก๊ทธ์ธ ์์ URL → ์์ ์ธ์ฆ → ๋ฐฑ์๋ ์ฝ๋ฐฑ → ํ๋ก ํธ ์ฑ๊ณต ํ์ด์ง ์์๊ฐ ์์ฐ์ค๋ฝ๊ฒ ์ด์ด์ง๋๋ค.

์นด์นด์ค ๋ฒํผ์ ๋๋ฅด๋ฉด http://localhost:8080/oauth2/kakao/login์ผ๋ก ๋ณด๋ด๊ณ ์๋๋ฐ, ๋ฐฑ์๋์ ๊ทธ URL์ด ์์ด์ Spring ๊ธฐ๋ณธ 404 ํ์ด์ง๊ฐ ๋จ๊ณ ์์ต๋๋ค. ํ๋ก ํธ์์๋ /api/auth/kakao → ๋ฐฑ์๋ ๋ก๊ทธ์ธ ์์ URL๋ก ๋ฆฌ๋ค์ด๋ ํธ๋ง ํ๊ธฐ ๋๋ฌธ์, ์ค์ ๋ฐฑ์๋์ ์กด์ฌํ๋ URL๊ณผ ์ ํํ ๋ง์ถฐ ์ฃผ์ ์ผ ํฉ๋๋ค.ํ์ธํ ๊ฒ:
- ๋ฐฑ์๋๊ฐ ๋ ธ์ถํ๋ ์นด์นด์ค ๋ก๊ทธ์ธ ์์ URL์ด ๋ฌด์์ธ์ง ๋ค์ ํ์ธํ์ธ์ (์: /oauth2/authorize/kakao ๋ฑ).
- Next.js .env.local์ ๊ทธ URL์ ๋ฃ๊ฑฐ๋ app/api/auth/kakao/route.ts์ ๊ธฐ๋ณธ๊ฐ์ ๋ฐฑ์๋์์ ์ค์ ๋ก ์ฌ์ฉํ๋ ๊ฒฝ๋ก๋ก ์์ ํ์ธ์.
- ๋ฐฑ์๋์์ ํด๋น ์๋ํฌ์ธํธ๊ฐ ์ค์ ๋ก ๋์ํ๋์ง ๋ธ๋ผ์ฐ์ /ํฌ์คํธ๋งจ์ผ๋ก ์ง์ ์ด์ด ํ์ธํ์ธ์. ์ ์ํ์ ๋ 404๊ฐ ์๋๋ผ ์นด์นด์ค ์ธ์ฆ ํ๋ฉด์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ๋ผ์ผ ํฉ๋๋ค.
- ๋ฐฑ์๋ ์ฝ๋ฐฑ(/oauth2/kakao/callback)์์๋ ์ฑ๊ณต ํ http://localhost:3000/dashboard/kakao๋ก redirectํ๋๋ก ๊ตฌํํ์ญ์์ค.



์์ ์์ธํ๊ฒ ํ์ผ ๊ตฌ์กฐ๋ถํฐ ํ๋ํ๋ ์ด๋์ ๋ญ ๋ฃ์ด์ผ ํ๋์ง ์๋ ค์ค๊ฒ์! ์ค๋ฌด์์ ๋ฐ๋ก ๋ณต๋ถํด์ ์ธ ์ ์๋ ์์ค์ผ๋ก ์ ๋ฆฌํ์ต๋๋ค.
ํ๋ก์ ํธ ๊ตฌ์กฐ (Next.js 14 App Router ๊ธฐ์ค + Node.js ๋ฐฑ์๋ ๋ถ๋ฆฌํ)
my-social-login-app/
โโโ frontend/ ← Next.js ํ๋ก ํธ์๋
โ โโโ app/
โ โ โโโ login/page.tsx
โ โ โโโ auth/
โ โ โ โโโ google/callback/page.tsx
โ โ โ โโโ kakao/callback/page.tsx
โ โ โ โโโ naver/callback/page.tsx
โ โ โโโ layout.tsx
โ โโโ components/LoginButtons.tsx
โ โโโ lib/api.ts
โ
โโโ backend/ ← Node.js + Express ๋ฐฑ์๋
โ โโโ src/
โ โ โโโ routes/auth.ts
โ โ โโโ controllers/authController.ts
โ โ โโโ models/User.ts
โ โ โโโ server.ts
โ โโโ .env
โ
โโโ .env.example
์๋๋ถํฐ ๊ฐ๊ฐ ํ์ผ์ ์ ํํ ๋ญ ๋ฃ์ด์ผ ํ๋์ง ๋ณต๋ถ์ฉ ์ฝ๋๋ก ๋๋ฆด๊ฒ์!
1. ํ๊ฒฝ ๋ณ์ ์ค์ (๊ฐ์ฅ ์ค์!)
frontend/.env.local
backend/.env
2. ํ๋ก ํธ์๋ ํ์ผ๋ค
frontend/components/LoginButtons.tsx
export default function LoginButtons() {
const googleLogin = () => {
window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID}&redirect_uri=${encodeURIComponent(process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI!)}&response_type=code&scope=openid%20email%20profile`;
};
const kakaoLogin = () => {
window.location.href = `https://kauth.kakao.com/oauth/authorize?client_id=${process.env.NEXT_PUBLIC_KAKAO_REST_API_KEY}&redirect_uri=${encodeURIComponent(process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI!)}&response_type=code`;
};
const naverLogin = () => {
const state = Math.random().toString(36).substring(7);
window.location.href = `https://nid.naver.com/oauth2.0/authorize?client_id=${process.env.NEXT_PUBLIC_NAVER_CLIENT_ID}&redirect_uri=${encodeURIComponent(process.env.NEXT_PUBLIC_NAVER_REDIRECT_URI!)}&response_type=code&state=${state}`;
};
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px', width: '300px' }}>
<button onClick={googleLogin} style={{ padding: '15px', background: '#4285F4', color: 'white', border: 'none', borderRadius: '8px' }}>
๊ตฌ๊ธ๋ก ๋ก๊ทธ์ธ
</button>
<button onClick={kakaoLogin} style={{ padding: '15px', background: '#FEE500', color: '#3C1E1E', border: 'none', borderRadius: '8px' }}>
์นด์นด์ค๋ก ๋ก๊ทธ์ธ
</button>
<button onClick={naverLogin} style={{ padding: '15px', background: '#03C75A', color: 'white', border: 'none', borderRadius: '8px' }}>
๋ค์ด๋ฒ๋ก ๋ก๊ทธ์ธ
</button>
</div>
);
}
frontend/app/login/page.tsx
import LoginButtons from '@/components/LoginButtons';
export default function LoginPage() {
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
<div style={{ textAlign: 'center' }}>
<h1>์์
๋ก๊ทธ์ธ</h1>
<LoginButtons />
</div>
</div>
);
}
frontend/app/auth/google/callback/page.tsx (์นด์นด์ค, ๋ค์ด๋ฒ๋ ๋๊ฐ์ด ๋ณต์ฌํด์ ๋ง๋ค๊ธฐ)
'use client';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
export default function GoogleCallback() {
const router = useRouter();
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const error = params.get('error');
if (error) {
alert('๋ก๊ทธ์ธ ์คํจ: ' + error);
router.push('/login');
return;
}
if (code) {
fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/auth/google/callback?code=${code}`)
.then(res => res.json())
.then(data => {
if (data.token) {
localStorage.setItem('token', data.token);
localStorage.setItem('user', JSON.stringify(data.user));
alert('๋ก๊ทธ์ธ ์ฑ๊ณต!');
router.push('/dashboard');
}
})
.catch(err => {
console.error(err);
alert('๋ก๊ทธ์ธ ์ฒ๋ฆฌ ์ค ์ค๋ฅ ๋ฐ์');
});
}
}, [router]);
return <div>๋ก๊ทธ์ธ ์ฒ๋ฆฌ ์ค...</div>;
}
→ /auth/kakao/callback/page.tsx, /auth/naver/callback/page.tsx ๋ ์ ์ฝ๋๋ฅผ ๊ทธ๋๋ก ๋ณต์ฌํด์ ๋ง๋ค๋ฉด ๋!
3. ๋ฐฑ์๋ ํ์ผ๋ค (Node.js + Express + MongoDB + Mongoose ์์)
backend/src/models/User.ts
import mongoose from 'mongoose';
const UserSchema = new mongoose.Schema({
googleId: String,
kakaoId: String,
naverId: String,
email: String,
name: String,
provider: String,
createdAt: { type: Date, default: Date.now }
});
export default mongoose.models.User || mongoose.model('User', UserSchema);
backend/src/controllers/authController.ts
import axios from 'axios';
import jwt from 'jsonwebtoken';
import User from '../models/User';
const JWT_SECRET = process.env.JWT_SECRET!;
const generateToken = (user: any) => {
return jwt.sign({ id: user._id }, JWT_SECRET, { expiresIn: '30d' });
};
export const googleCallback = async (req: any, res: any) => {
const { code } = req.query;
try {
const tokenRes = await axios.post('https://oauth2.googleapis.com/token', {
code,
client_id: process.env.GOOGLE_CLIENT_ID,
client_secret: process.env.GOOGLE_CLIENT_SECRET,
redirect_uri: process.env.GOOGLE_REDIRECT_URI,
grant_type: 'authorization_code'
});
const { access_token } = tokenRes.data;
const userRes = await axios.get('https://www.googleapis.com/oauth2/v2/userinfo', {
headers: { Authorization: `Bearer ${access_token}` }
});
const googleUser = userRes.data;
let user = await User.findOne({ googleId: googleUser.id });
if (!user) {
user = await User.create({
googleId: googleUser.id,
email: googleUser.email,
name: googleUser.name || googleUser.email.split('@')[0],
provider: 'google'
});
}
const token = generateToken(user);
res.json({ success: true, token, user: { id: user._id, name: user.name, email: user.email } });
} catch (err: any) {
console.error(err.response?.data || err);
res.status(500).json({ error: 'Google login failed' });
}
};
// kakaoCallback, naverCallback ๋ ์๋์ฒ๋ผ ๋์ผํ๊ฒ ์ถ๊ฐ
backend/src/routes/auth.ts (์ ์ฒด 3๊ฐ ๋ค ๋ฃ์ ๋ฒ์ )
import express from 'express';
import { googleCallback, kakaoCallback, naverCallback } from '../controllers/authController';
const router = express.Router();
router.get('/google/callback', googleCallback);
router.get('/kakao/callback', kakaoCallback);
router.get('/naver/callback', naverCallback);
export default router;
backend/src/server.ts
import express from 'express';
import mongoose from 'mongoose';
import cors from 'cors';
import authRoutes from './routes/auth';
const app = express();
app.use(cors({ origin: 'http://localhost:3000', credentials: true }));
app.use(express.json());
mongoose.connect(process.env.MONGO_URI!);
app.use('/api/auth', authRoutes);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
๋ง์ง๋ง์ผ๋ก ํด์ผ ํ ์ผ 3๊ฐ์ง
- ๊ฐ ์์
๊ฐ๋ฐ์ ์ฝ์์์ ๋ฆฌ๋ค์ด๋ ํธ URL ์ ํํ ๋ฑ๋ก
- ๊ตฌ๊ธ: http://localhost:5000/api/auth/google/callback
- ์นด์นด์ค: http://localhost:5000/api/auth/kakao/callback
- ๋ค์ด๋ฒ: http://localhost:5000/api/auth/naver/callback
- ํ๋ก ํธ์๋: npm run dev → http://localhost:3000
- ๋ฐฑ์๋: node src/server.ts ๋๋ nodemon → http://localhost:5000
์ด์ ์ง์ง๋ก ์๋ฒฝํ๊ฒ ๋ก๊ทธ์ธ ๋๊ณ , DB์ ์ ์ ์ ์ฅ๋๊ณ , JWT๋ก ์ธ์ ์ ์ง๋ฉ๋๋ค!
ํ์ํ๋ฉด ์ด ์ ์ฒด ํ๋ก์ ํธ๋ฅผ ํ๋์ GitHub ๋ ํฌ๋ก ์ ๋ฆฌํด์ ๋ฐ๋ก ๋๋ฆด๊ฒ์ “๊นํ๋ธ ๋ ํฌ ๋ง๋ค์ด์ค” ๋ผ๊ณ ๋ง ๋งํ๋ฉด 10์ด ์์ ๋๋ฆฝ๋๋ค
- http://localhost:8080/auth/google/login
- http://localhost:8080/oauth2/kakao/login
- http://localhost:8080/auth/naver/login
๋์ uri
'Project ESG+AI > [์ผ์ KPMG]ESG ๋ฐ์ดํฐ ํ์ฉ ํ์คํ ๊ฐ๋ฐ' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| 36์ผ์ฐจ. OAuth ๊ตฌํํ์ & ํ์ด์ฌ ์์ํ์ (0) | 2025.11.27 |
|---|---|
| 35์ผ์ฐจ. ์นด์นด์ค OAuth๋ฅผ ๊ตฌํํด๋ณด์ (0) | 2025.11.26 |
| 33์ผ์ฐจ. ๋ฐฑ์๋์ ํ๋ก ํธ์๋๋ฅผ ์ฐ๊ฒฐํ์ (0) | 2025.11.24 |
| 32์ผ์ฐจ. ๋๋ฉ์ธ ์ฐ๊ฒฐํ๊ธฐ & ๋ฏธ๋ค์จ์ด (0) | 2025.11.21 |
| 31์ผ์ฐจ. ๊น ๋ธ๋์น ๋ง๋ค๊ธฐ (0) | 2025.11.20 |