diff --git a/public/bg.png b/public/bg.png new file mode 100644 index 0000000..2ab8edf Binary files /dev/null and b/public/bg.png differ diff --git a/src/App.js b/src/App.js index e9be491..4f7fb51 100644 --- a/src/App.js +++ b/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 ( + +
+ + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + +
+
+ ); +} function App() { return ( - -
- -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
-
-
-
+ {/* ✅ useAuth는 이 내부에서만 사용 가능 */}
); } diff --git a/src/api/attachInterceptors.js b/src/api/attachInterceptors.js index abc01cb..207804b 100644 --- a/src/api/attachInterceptors.js +++ b/src/api/attachInterceptors.js @@ -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'; } } diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js new file mode 100644 index 0000000..3a556f0 --- /dev/null +++ b/src/components/Layout/Layout.js @@ -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 ( +
+ {children} +
+ ); +}; + +export default Layout; diff --git a/src/components/Navbar.js b/src/components/Navbar.js index 876d2a8..f0c18b5 100644 --- a/src/components/Navbar.js +++ b/src/components/Navbar.js @@ -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 ( - diff --git a/src/components/_unused_Navbar.js b/src/components/_unused_Navbar.js index 3229d42..876d2a8 100644 --- a/src/components/_unused_Navbar.js +++ b/src/components/_unused_Navbar.js @@ -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 ( diff --git a/src/index.js b/src/index.js index 1675893..517f389 100644 --- a/src/index.js +++ b/src/index.js @@ -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( - + {/* ✅ 전역 로그인 상태 관리 */} + + -); \ No newline at end of file +); diff --git a/src/pages/About.js b/src/pages/About.js index 6319c49..4dfcb63 100644 --- a/src/pages/About.js +++ b/src/pages/About.js @@ -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 = () => {

우리의 비전

-

- 엔지니어에게 필요한 관리 포탈을 만듭니다. -

+

핵심 가치

-

전문 역량

+

교육 역량

diff --git a/src/pages/Board.js b/src/pages/Board.js index f4c4108..a3f3ff7 100644 --- a/src/pages/Board.js +++ b/src/pages/Board.js @@ -13,7 +13,7 @@ const Board = () => { ); return ( -
+
diff --git a/src/pages/Home.js b/src/pages/Home.js index d0ae249..cf2dee1 100644 --- a/src/pages/Home.js +++ b/src/pages/Home.js @@ -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 ( -
-
-
- Hero Background -
-
-
-
-

System Management Portal

-

데모 사이트 입니다.

- -
+
+ {/* Hero Section */} +
+ Hero Background +
+
+
+

+ IT Education
+ Basic & Advanced +

+

+ Ubuntu Linux, Ansible, Docker, Kubernetes, etc. +

+
+

+ System Management
+ +

+

+ IT System Management Portal +

+ + 자세히 보기 > +
-
+
-
+ {/* Posts Section */} +

최신 포스트

    {posts.length > 0 ? ( - posts.map(post => ( + posts.map((post) => (
  • navigate(`/posts/${post.id}`)} > -

    {post.title}

    -

    작성자: {post.author_name}

    +

    {post.title}

    +

    작성자: {post.author_name}

    - 작성일: {new Date(post.created_at).toLocaleString()} + {new Date(post.created_at).toLocaleString()}

  • )) ) : ( -

    게시글이 없습니다.

    +

    + 게시글이 없습니다. +

    )}
-
+
); }; diff --git a/src/pages/_unused_Home.js b/src/pages/_unused_Home.js new file mode 100644 index 0000000..1960818 --- /dev/null +++ b/src/pages/_unused_Home.js @@ -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 ( +
+
+
+ Hero Background +
+
+
+
+

System Management Portal

+

데모 사이트 입니다.

+ +
+
+
+
+ +
+

최신 포스트

+
    + {posts.length > 0 ? ( + posts.map(post => ( +
  • navigate(`/posts/${post.id}`)} + > +

    {post.title}

    +

    작성자: {post.author_name}

    +

    + 작성일: {new Date(post.created_at).toLocaleString()} +

    +
  • + )) + ) : ( +

    게시글이 없습니다.

    + )} +
+
+
+ ); +}; + +export default Home; diff --git a/version b/version index 9beca35..e484aaf 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.0.15 \ No newline at end of file +0.0.16 \ No newline at end of file