diff --git a/frontend/src/AppContent.tsx b/frontend/src/AppContent.tsx
index 92d8d458..7967b8b1 100644
--- a/frontend/src/AppContent.tsx
+++ b/frontend/src/AppContent.tsx
@@ -40,6 +40,7 @@ import SeminarDetail from './pages/Seminar/SeminarDetail';
import SeminarCreate from './pages/Seminar/SeminarCreate';
import SeminarEdit from './pages/Seminar/SeminarEdit';
import News from './pages/News/News/News';
+import NewsDetail from './pages/News/News/NewsDetail';
interface PageTransitionProps {
children: React.ReactNode;
@@ -214,7 +215,6 @@ function AppContent() {
} />
{/* news */}
- } />
} />
} />
} />
} />
} />
+ {/*학부 뉴스*/}
+ } />
+ } />
{/* 어드민 권한 보호 Routes */}
{
setIsLoading(false);
}
};
-
const handleNewsClick = (newsId: number) => {
navigate(`/news/${newsId}`);
};
diff --git a/frontend/src/pages/News/News/NewsDetail.tsx b/frontend/src/pages/News/News/NewsDetail.tsx
new file mode 100644
index 00000000..1d32009b
--- /dev/null
+++ b/frontend/src/pages/News/News/NewsDetail.tsx
@@ -0,0 +1,139 @@
+// NewsDetail.tsx
+import React, { useState, useEffect } from 'react';
+import { useParams, useNavigate } from 'react-router-dom';
+import { Eye, ArrowLeft } from 'lucide-react';
+import moment from 'moment';
+import { apiEndpoints } from '../../../config/apiConfig';
+import {
+ Container,
+ BackButton,
+ NewsWrapper,
+ NewsHeader,
+ NewsTitle,
+ NewsMetadata,
+ NewsDate,
+ NewsViews,
+ NewsDivider,
+ NewsImage,
+ NewsContent,
+ NewsLink,
+ LoadingSpinner,
+ ErrorMessage,
+} from './NewsDetailStyle';
+
+interface NewsDetailData {
+ id: number;
+ title: string;
+ content: string;
+ view: number;
+ createDate: string;
+ link: string;
+ image: string;
+}
+
+const NewsDetail = () => {
+ const { newsId } = useParams();
+ const navigate = useNavigate();
+ const [newsData, setNewsData] = useState(null);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState('');
+
+ useEffect(() => {
+ const fetchNewsDetail = async () => {
+ try {
+ setIsLoading(true);
+ if (!newsId) return;
+
+ const response = await fetch(apiEndpoints.news.get(parseInt(newsId)));
+
+ if (!response.ok) {
+ if (response.status === 404) {
+ throw new Error('뉴스를 찾을 수 없습니다.');
+ }
+ throw new Error('뉴스를 불러오는데 실패했습니다.');
+ }
+
+ const data = await response.json();
+ setNewsData(data);
+ } catch (error) {
+ setError(
+ error instanceof Error
+ ? error.message
+ : '알 수 없는 오류가 발생했습니다.',
+ );
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ if (newsId) {
+ fetchNewsDetail();
+ }
+ }, [newsId]);
+
+ const handleBack = () => {
+ navigate(-1);
+ };
+
+ if (isLoading) {
+ return (
+
+ 로딩 중...
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ {error}
+
+ );
+ }
+
+ if (!newsData) {
+ return null;
+ }
+
+ return (
+
+
+
+ 뒤로 가기
+
+
+
+ {newsData.title}
+
+
+ {moment(newsData.createDate).format('YYYY.MM.DD')}
+
+
+
+ {newsData.view}
+
+
+
+
+ {newsData.image && (
+
+ )}
+ {newsData.content}
+ {newsData.link && (
+
+ 원문 보기
+
+ )}
+
+
+ );
+};
+
+export default NewsDetail;
diff --git a/frontend/src/pages/News/News/NewsDetailStyle.ts b/frontend/src/pages/News/News/NewsDetailStyle.ts
new file mode 100644
index 00000000..6b081e73
--- /dev/null
+++ b/frontend/src/pages/News/News/NewsDetailStyle.ts
@@ -0,0 +1,140 @@
+// NewsDetailStyle.ts
+import styled from 'styled-components';
+import { media } from '../../../styles/media';
+import { SEJONG_COLORS } from '../../../constants/colors';
+
+export const Container = styled.div`
+ max-width: 1000px;
+ width: 95%;
+ margin: 0 auto;
+ padding: 40px 20px;
+
+ ${media.mobile} {
+ padding: 20px 10px;
+ }
+`;
+
+export const BackButton = styled.button`
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 16px;
+ border: 1px solid ${SEJONG_COLORS.COOL_GRAY};
+ border-radius: 4px;
+ background: white;
+ color: ${SEJONG_COLORS.GRAY};
+ font-size: 14px;
+ cursor: pointer;
+ transition: all 0.2s;
+
+ &:hover {
+ background: ${SEJONG_COLORS.IVORY};
+ color: ${SEJONG_COLORS.CRIMSON_RED};
+ }
+`;
+
+export const NewsWrapper = styled.article`
+ margin-top: 24px;
+ background: white;
+ border: 1px solid ${SEJONG_COLORS.COOL_GRAY};
+ border-radius: 8px;
+ overflow: hidden;
+`;
+
+export const NewsHeader = styled.header`
+ padding: 24px;
+`;
+
+export const NewsTitle = styled.h1`
+ margin: 0;
+ font-size: 24px;
+ font-weight: 600;
+ color: ${SEJONG_COLORS.GRAY};
+ line-height: 1.4;
+
+ ${media.mobile} {
+ font-size: 20px;
+ }
+`;
+
+export const NewsMetadata = styled.div`
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ margin-top: 16px;
+`;
+
+export const NewsDate = styled.span`
+ font-size: 14px;
+ color: ${SEJONG_COLORS.LIGHT_GRAY};
+`;
+
+export const NewsViews = styled.span`
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 14px;
+ color: ${SEJONG_COLORS.LIGHT_GRAY};
+
+ svg {
+ color: ${SEJONG_COLORS.WARM_GRAY1};
+ }
+`;
+
+export const NewsDivider = styled.hr`
+ margin: 0;
+ border: none;
+ border-top: 1px solid ${SEJONG_COLORS.COOL_GRAY};
+`;
+
+export const NewsImage = styled.img`
+ display: block;
+ width: 100%;
+ max-height: 500px;
+ object-fit: cover;
+ margin: 0 auto;
+`;
+
+export const NewsContent = styled.div`
+ padding: 24px;
+ font-size: 16px;
+ line-height: 1.8;
+ color: ${SEJONG_COLORS.GRAY};
+ white-space: pre-wrap;
+
+ ${media.mobile} {
+ font-size: 15px;
+ }
+`;
+
+export const NewsLink = styled.a`
+ display: inline-block;
+ margin: 0 24px 24px;
+ padding: 8px 16px;
+ border: 1px solid ${SEJONG_COLORS.CRIMSON_RED};
+ border-radius: 4px;
+ color: ${SEJONG_COLORS.CRIMSON_RED};
+ text-decoration: none;
+ font-size: 14px;
+ transition: all 0.2s;
+
+ &:hover {
+ background: ${SEJONG_COLORS.CRIMSON_RED};
+ color: white;
+ }
+`;
+
+export const LoadingSpinner = styled.div`
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 200px;
+ color: ${SEJONG_COLORS.CRIMSON_RED};
+`;
+
+export const ErrorMessage = styled.div`
+ text-align: center;
+ padding: 40px;
+ color: ${SEJONG_COLORS.CRIMSON_RED};
+ font-size: 16px;
+`;