This commit is contained in:
BIN
public/bg.png
Normal file
BIN
public/bg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 429 KiB |
68
src/App.js
68
src/App.js
@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
// src/App.js
|
||||
import React, { useEffect } from "react";
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||
import Navbar from "./components/Navbar";
|
||||
import Footer from "./components/Footer";
|
||||
@ -12,38 +13,53 @@ import PostCategory from "./pages/PostCategory";
|
||||
import Login from "./pages/Login";
|
||||
import Register from './pages/Register';
|
||||
import Profile from "./pages/Profile";
|
||||
import TaskPage from "./pages/TaskPage";
|
||||
import TaskPage from "./pages/TaskPage";
|
||||
import BoardsPage from "./pages/BoardsPage";
|
||||
import AnsiblePage from "./pages/Ansible";
|
||||
import Layout from "./components/Layout/Layout";
|
||||
|
||||
import { AuthProvider } from "./context/AuthContext";
|
||||
import { AuthProvider, useAuth } from "./context/AuthContext";
|
||||
import authApi from "./api/authApi";
|
||||
import { attachAuthInterceptors } from "./api/attachInterceptors";
|
||||
|
||||
function AppContent() {
|
||||
const { logout } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
attachAuthInterceptors(authApi, logout); // ✅ axios 인터셉터에 logout 연결
|
||||
}, [logout]);
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Navbar />
|
||||
<Layout>
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/posts" element={<PostList />} />
|
||||
<Route path="/posts/:id" element={<PostDetail />} />
|
||||
<Route path="/postCreate" element={<PostCreate />} />
|
||||
<Route path="/posts/:id/edit" element={<PostEdit />} />
|
||||
<Route path="/post/:category" element={<PostCategory />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
<Route path="/tasks" element={<TaskPage />} />
|
||||
<Route path="/boards" element={<BoardsPage />} />
|
||||
<Route path="/ansible" element={<AnsiblePage />} />
|
||||
</Routes>
|
||||
</Layout>
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<Router>
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Navbar />
|
||||
<main className="flex-grow pt-16">
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route path="/posts" element={<PostList />} />
|
||||
<Route path="/posts/:id" element={<PostDetail />} />
|
||||
<Route path="/postCreate" element={<PostCreate />} />
|
||||
<Route path="/posts/:id/edit" element={<PostEdit />} />
|
||||
<Route path="/post/:category" element={<PostCategory />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
<Route path="/register" element={<Register />} />
|
||||
<Route path="/profile" element={<Profile />} />
|
||||
<Route path="/tasks" element={<TaskPage />} />
|
||||
<Route path="/boards" element={<BoardsPage />} />
|
||||
<Route path="/ansible" element={<AnsiblePage />} />
|
||||
</Routes>
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
</Router>
|
||||
<AppContent /> {/* ✅ useAuth는 이 내부에서만 사용 가능 */}
|
||||
</AuthProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// src/api/attachInterceptors.js
|
||||
import { refreshAccessToken } from './tokenUtils';
|
||||
|
||||
export const attachAuthInterceptors = (axiosInstance) => {
|
||||
// 요청 시 access token 자동 첨부
|
||||
export const attachAuthInterceptors = (axiosInstance, logout) => {
|
||||
axiosInstance.interceptors.request.use(config => {
|
||||
const token = localStorage.getItem('access');
|
||||
if (token) {
|
||||
@ -10,7 +10,6 @@ export const attachAuthInterceptors = (axiosInstance) => {
|
||||
return config;
|
||||
});
|
||||
|
||||
// 응답 시 access 만료 → refresh 재시도
|
||||
axiosInstance.interceptors.response.use(
|
||||
res => res,
|
||||
async err => {
|
||||
@ -29,9 +28,8 @@ export const attachAuthInterceptors = (axiosInstance) => {
|
||||
return axiosInstance(originalRequest);
|
||||
} catch (refreshError) {
|
||||
console.error('토큰 갱신 실패', refreshError);
|
||||
localStorage.removeItem('access');
|
||||
localStorage.removeItem('refresh');
|
||||
window.location.href = '/login'; // 또는 useNavigate 사용 가능
|
||||
if (logout) logout(); // ✅ Context logout 반영
|
||||
window.location.href = '/login';
|
||||
}
|
||||
}
|
||||
|
||||
|
16
src/components/Layout/Layout.js
Normal file
16
src/components/Layout/Layout.js
Normal file
@ -0,0 +1,16 @@
|
||||
// src/components/Layout/Layout.js
|
||||
import React from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
const Layout = ({ children }) => {
|
||||
const location = useLocation();
|
||||
const isHome = location.pathname === "/";
|
||||
|
||||
return (
|
||||
<main className={`${isHome ? "pt-0" : "pt-16"} flex-grow`}>
|
||||
{children}
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
@ -1,80 +1,77 @@
|
||||
import React from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { Link, useNavigate, useLocation } from "react-router-dom";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
|
||||
const Navbar = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { isLoggedIn, logout, user } = useAuth();
|
||||
|
||||
// 로그인 상태에 따라 메뉴 구성
|
||||
const isHome = location.pathname === "/";
|
||||
|
||||
const menuItems = [
|
||||
{ name: "home", path: "/" },
|
||||
{ name: "about", path: "/about" },
|
||||
{ name: "posts", path: "/posts" },
|
||||
{ name: "boards", path: "/boards" },
|
||||
...(isLoggedIn ? [
|
||||
{ name: "tasks", path: "/tasks" },
|
||||
{ name: "ansible", path: "/ansible" },
|
||||
] : []),
|
||||
];
|
||||
|
||||
// 로그인 상태일 때 추가되는 메뉴
|
||||
if (isLoggedIn) {
|
||||
menuItems.push({ name: "tasks", path: "/tasks" });
|
||||
menuItems.push({ name: "ansible", path: "/ansible" });
|
||||
// menuItems.push({ name: "boards", path: "/boards" });
|
||||
}
|
||||
|
||||
return (
|
||||
<nav className="fixed w-full z-50 bg-white/90 backdrop-blur-md shadow-sm">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="flex justify-between items-center h-16">
|
||||
<Link to="/" className="text-2xl font-bold text-[#3B82F6]">
|
||||
Dev
|
||||
</Link>
|
||||
|
||||
<div className="hidden md:flex items-center space-x-8">
|
||||
{menuItems.map((item) => (
|
||||
<nav className={`fixed top-0 w-full z-50 h-16 ${isHome ? "text-white bg-transparent" : "text-black bg-white shadow"}`}>
|
||||
<div className="container mx-auto px-4 py-3 flex justify-between items-center">
|
||||
<Link to="/" className="text-xl font-bold tracking-wide">
|
||||
ICURFER
|
||||
</Link>
|
||||
<div className="hidden md:flex space-x-6 font-semibold">
|
||||
{menuItems.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.path}
|
||||
className={`hover:text-yellow-400 transition capitalize`}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
<div className="space-x-2 flex items-center ">
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<Link
|
||||
key={item.name}
|
||||
to={item.path}
|
||||
className="text-gray-600 hover:text-[#D4AF37]"
|
||||
to="/profile"
|
||||
className={`px-4 py-2 rounded-full shadow ${isHome ? "bg-white text-black" : "bg-gray-200 text-gray-800"}`}
|
||||
>
|
||||
{item.name.charAt(0).toUpperCase() + item.name.slice(1)}
|
||||
내 정보 | 등급: {user?.grade && `(${user.grade})`}
|
||||
</Link>
|
||||
))}
|
||||
|
||||
{isLoggedIn ? (
|
||||
<>
|
||||
<Link
|
||||
to="/profile"
|
||||
className="bg-[#8b0000] text-white px-4 py-2 rounded hover:bg-gray-300"
|
||||
>
|
||||
내 정보 {user?.grade && `(${user.grade})`}
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => {
|
||||
logout();
|
||||
navigate("/login");
|
||||
}}
|
||||
className="bg-gray-200 text-gray-700 px-4 py-2 rounded hover:bg-gray-300"
|
||||
>
|
||||
로그아웃
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
to="/login"
|
||||
className="bg-[#3B82F6] text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||
>
|
||||
로그인
|
||||
</Link>
|
||||
<Link
|
||||
to="/register"
|
||||
className="bg-gray-200 text-gray-700 px-4 py-2 rounded hover:bg-gray-300"
|
||||
>
|
||||
회원가입
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
logout();
|
||||
navigate("/login");
|
||||
}}
|
||||
className={`px-4 py-2 rounded-full ${isHome ? "bg-white text-black" : "bg-gray-200 text-gray-800"} hover:bg-gray-300`}
|
||||
>
|
||||
로그아웃
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
to="/login"
|
||||
className={`px-4 py-2 rounded-full ${isHome ? "bg-gray-200 text-gray-700 hover:bg-gray-300" : "bg-blue-600 text-white"}`}
|
||||
>
|
||||
로그인
|
||||
</Link>
|
||||
<Link
|
||||
to="/register"
|
||||
className={`px-4 py-2 rounded-full ${isHome ? "bg-gray-700 text-white hover:bg-gray-300" : "bg-gray-200 text-gray-800"}`}
|
||||
>
|
||||
회원가입
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -6,16 +6,19 @@ const Navbar = () => {
|
||||
const navigate = useNavigate();
|
||||
const { isLoggedIn, logout, user } = useAuth();
|
||||
|
||||
// ✅ 로그인 상태에 따라 메뉴 구성
|
||||
// 로그인 상태에 따라 메뉴 구성
|
||||
const menuItems = [
|
||||
{ name: "home", path: "/" },
|
||||
{ name: "about", path: "/about" },
|
||||
{ name: "posts", path: "/posts" },
|
||||
{ name: "boards", path: "/boards" },
|
||||
];
|
||||
|
||||
// 로그인 상태일 때 추가되는 메뉴
|
||||
if (isLoggedIn) {
|
||||
menuItems.push({ name: "tasks", path: "/tasks" });
|
||||
menuItems.push({ name: "ansible", path: "/ansible" });
|
||||
// menuItems.push({ name: "boards", path: "/boards" });
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,11 +1,15 @@
|
||||
// src/index.js
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import { AuthProvider } from './context/AuthContext'; // ✅ 추가
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
<AuthProvider> {/* ✅ 전역 로그인 상태 관리 */}
|
||||
<App />
|
||||
</AuthProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
);
|
||||
|
@ -10,10 +10,10 @@ const About = () => {
|
||||
animation: false,
|
||||
radar: {
|
||||
indicator: [
|
||||
{ name: '클라우드', max: 100 },
|
||||
{ name: '리눅스', max: 100 },
|
||||
{ name: '데브옵스', max: 100 },
|
||||
{ name: 'CI/CD', max: 100 },
|
||||
{ name: '클라우드', max: 120 },
|
||||
{ name: '리눅스(우분투)', max: 130 },
|
||||
{ name: '쿠버네티스', max: 110 },
|
||||
{ name: 'CI/CD', max: 120 },
|
||||
{ name: '기타', max: 100 }
|
||||
]
|
||||
},
|
||||
@ -39,18 +39,26 @@ const About = () => {
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 mb-20">
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold mb-6">우리의 비전</h3>
|
||||
<p className="text-gray-600 leading-relaxed">
|
||||
엔지니어에게 필요한 관리 포탈을 만듭니다.
|
||||
</p>
|
||||
<ul className="space-y-4">
|
||||
<li className="flex items-center">
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>실무에 적용 가능한 교육</span>
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>시스템 엔지니어를 위한 관리 도구 제작</span>
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>-</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold mb-6">핵심 가치</h3>
|
||||
<ul className="space-y-4">
|
||||
<li className="flex items-center">
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>시스템 엔지니어를 위한 관리 도구</span>
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>가성비</span>
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>-</span>
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>효율</span>
|
||||
</li>
|
||||
<li className="flex items-center">
|
||||
<i className="fas fa-check text-[#3B82F6] mr-3"></i><span>-</span>
|
||||
@ -59,7 +67,7 @@ const About = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-20">
|
||||
<h3 className="text-2xl font-bold mb-8 text-center">전문 역량</h3>
|
||||
<h3 className="text-2xl font-bold mb-8 text-center">교육 역량</h3>
|
||||
<div ref={chartRef} className="w-full h-[400px]"></div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
|
@ -13,7 +13,7 @@ const Board = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 py-20">
|
||||
<div className="min-h-screen bg-gray-50 pt-20">
|
||||
<div className="container mx-auto px-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="mb-8">
|
||||
|
@ -1,60 +1,81 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import blogApi from '../api/blogApi';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
// src/pages/Home.js
|
||||
import React, { useEffect, useState } from "react";
|
||||
import blogApi from "../api/blogApi";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
|
||||
const Home = () => {
|
||||
const [posts, setPosts] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
blogApi.get('/api/blog/posts/')
|
||||
.then(res => setPosts(res.data))
|
||||
.catch(err => console.error('게시글 목록 조회 실패:', err));
|
||||
blogApi
|
||||
.get("/api/blog/posts/")
|
||||
.then((res) => setPosts(res.data))
|
||||
.catch((err) => console.error("게시글 목록 조회 실패:", err));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<div className="relative h-screen">
|
||||
<div className="absolute inset-0">
|
||||
<img
|
||||
src="https://public.readdy.ai/ai/img_res/41c06ed594c02f95a02361b098edd073.jpg"
|
||||
className="w-full h-full object-cover"
|
||||
alt="Hero Background"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/70 to-transparent">
|
||||
<div className="container mx-auto px-4 h-full flex items-center">
|
||||
<div className="max-w-2xl text-white">
|
||||
<h1 className="text-5xl font-bold mb-6">System Management Portal</h1>
|
||||
<p className="text-xl mb-8">데모 사이트 입니다.</p>
|
||||
<button className="bg-[#3B82F6] text-white px-8 py-3 rounded-lg">개발 중 입니다.</button>
|
||||
</div>
|
||||
<div className="min-h-screen bg-white">
|
||||
{/* Hero Section */}
|
||||
<section className="relative h-[80vh]">
|
||||
<img
|
||||
src="/bg.png"
|
||||
alt="Hero Background"
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-[#000000cc] to-[#00000000]" />
|
||||
<div className="relative z-10 flex items-center justify-start h-full container mx-auto px-4">
|
||||
<div className="text-white max-w-xl">
|
||||
<h1 className="text-5xl font-bold mb-6">
|
||||
IT Education <br />
|
||||
Basic & Advanced
|
||||
</h1>
|
||||
<p className="text-lg mb-6">
|
||||
Ubuntu Linux, Ansible, Docker, Kubernetes, etc.
|
||||
</p>
|
||||
<br/>
|
||||
<h1 className="text-5xl font-bold mb-6">
|
||||
System Management<br />
|
||||
|
||||
</h1>
|
||||
<p className="text-lg mb-6">
|
||||
IT System Management Portal
|
||||
</p>
|
||||
<Link
|
||||
to="/about"
|
||||
className="inline-block bg-white text-black px-6 py-3 rounded-lg font-semibold hover:opacity-90"
|
||||
>
|
||||
자세히 보기 >
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="container mx-auto px-4 py-20">
|
||||
{/* Posts Section */}
|
||||
<section className="container mx-auto px-4 py-20">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">최신 포스트</h2>
|
||||
<ul className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{posts.length > 0 ? (
|
||||
posts.map(post => (
|
||||
posts.map((post) => (
|
||||
<li
|
||||
key={post.id}
|
||||
className="p-4 border rounded shadow-sm bg-white hover:bg-gray-50 cursor-pointer list-none"
|
||||
className="bg-white p-6 rounded-lg shadow-md hover:shadow-lg transition cursor-pointer list-none border"
|
||||
onClick={() => navigate(`/posts/${post.id}`)}
|
||||
>
|
||||
<h2 className="text-xl font-semibold">{post.title}</h2>
|
||||
<p className="text-gray-600">작성자: {post.author_name}</p>
|
||||
<h3 className="text-xl font-semibold mb-2">{post.title}</h3>
|
||||
<p className="text-gray-700 mb-1">작성자: {post.author_name}</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
작성일: {new Date(post.created_at).toLocaleString()}
|
||||
{new Date(post.created_at).toLocaleString()}
|
||||
</p>
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<p className="text-center col-span-3 text-gray-500">게시글이 없습니다.</p>
|
||||
<p className="text-center col-span-3 text-gray-500">
|
||||
게시글이 없습니다.
|
||||
</p>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
63
src/pages/_unused_Home.js
Normal file
63
src/pages/_unused_Home.js
Normal file
@ -0,0 +1,63 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import blogApi from '../api/blogApi';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const Home = () => {
|
||||
const [posts, setPosts] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
blogApi.get('/api/blog/posts/')
|
||||
.then(res => setPosts(res.data))
|
||||
.catch(err => console.error('게시글 목록 조회 실패:', err));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
<div className="relative h-screen">
|
||||
<div className="absolute inset-0">
|
||||
<img
|
||||
src="/bg.png"
|
||||
className="w-full h-full object-cover"
|
||||
// className="w-full h-full object-cover"
|
||||
alt="Hero Background"
|
||||
/>
|
||||
</div>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-black/70 to-transparent">
|
||||
<div className="container mx-auto px-4 h-full flex items-center">
|
||||
<div className="max-w-2xl text-white">
|
||||
<h1 className="text-5xl font-bold mb-6">System Management Portal</h1>
|
||||
<p className="text-xl mb-8">데모 사이트 입니다.</p>
|
||||
<button className="bg-[#3B82F6] text-white px-8 py-3 rounded-lg">개발 중 입니다.</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-4 py-20">
|
||||
<h2 className="text-3xl font-bold mb-12 text-center">최신 포스트</h2>
|
||||
<ul className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{posts.length > 0 ? (
|
||||
posts.map(post => (
|
||||
<li
|
||||
key={post.id}
|
||||
className="p-4 border rounded shadow-sm bg-white hover:bg-gray-50 cursor-pointer list-none"
|
||||
onClick={() => navigate(`/posts/${post.id}`)}
|
||||
>
|
||||
<h2 className="text-xl font-semibold">{post.title}</h2>
|
||||
<p className="text-gray-600">작성자: {post.author_name}</p>
|
||||
<p className="text-gray-400 text-sm">
|
||||
작성일: {new Date(post.created_at).toLocaleString()}
|
||||
</p>
|
||||
</li>
|
||||
))
|
||||
) : (
|
||||
<p className="text-center col-span-3 text-gray-500">게시글이 없습니다.</p>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
Reference in New Issue
Block a user