Skip to content

Commit

Permalink
#244 feat: 뉴스 상세 조회 기능 추가 (초기)
Browse files Browse the repository at this point in the history
  • Loading branch information
pillow12360 committed Jan 5, 2025
1 parent cbf85cd commit 8095cd4
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 2 deletions.
5 changes: 4 additions & 1 deletion frontend/src/AppContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -214,7 +215,6 @@ function AppContent() {
<Route path="/about/organization" element={<Organization />} />

{/* news */}
<Route path="/news" element={<News />} />
<Route path="/news/noticeboard" element={<NoticeBoard />} />
<Route path="/news/noticeboard/:id" element={<NoticeDetail />} />
<Route
Expand All @@ -225,6 +225,9 @@ function AppContent() {
<Route path="/news/seminar/:id" element={<SeminarDetail />} />
<Route path="/news/thesis" element={<ThesisList />} />
<Route path="/news/thesis/:id" element={<ThesisDetail />} />
{/*학부 뉴스*/}
<Route path="/news" element={<News />} />
<Route path="/news/:newsId" element={<NewsDetail />} />

{/* 어드민 권한 보호 Routes */}
<Route
Expand Down
1 change: 0 additions & 1 deletion frontend/src/pages/News/News/News.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ const News = () => {
setIsLoading(false);
}
};

const handleNewsClick = (newsId: number) => {
navigate(`/news/${newsId}`);
};
Expand Down
139 changes: 139 additions & 0 deletions frontend/src/pages/News/News/NewsDetail.tsx
Original file line number Diff line number Diff line change
@@ -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<NewsDetailData | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string>('');

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 (
<Container>
<LoadingSpinner>로딩 중...</LoadingSpinner>
</Container>
);
}

if (error) {
return (
<Container>
<ErrorMessage>{error}</ErrorMessage>
</Container>
);
}

if (!newsData) {
return null;
}

return (
<Container>
<BackButton onClick={handleBack}>
<ArrowLeft size={20} />
뒤로 가기
</BackButton>
<NewsWrapper>
<NewsHeader>
<NewsTitle>{newsData.title}</NewsTitle>
<NewsMetadata>
<NewsDate>
{moment(newsData.createDate).format('YYYY.MM.DD')}
</NewsDate>
<NewsViews>
<Eye size={16} />
{newsData.view}
</NewsViews>
</NewsMetadata>
</NewsHeader>
<NewsDivider />
{newsData.image && (
<NewsImage
src={`https://dibb-bucket.s3.ap-northeast-2.amazonaws.com/news/${newsData.image}`}
alt={newsData.title}
/>
)}
<NewsContent>{newsData.content}</NewsContent>
{newsData.link && (
<NewsLink
href={newsData.link}
target="_blank"
rel="noopener noreferrer"
>
원문 보기
</NewsLink>
)}
</NewsWrapper>
</Container>
);
};

export default NewsDetail;
140 changes: 140 additions & 0 deletions frontend/src/pages/News/News/NewsDetailStyle.ts
Original file line number Diff line number Diff line change
@@ -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;
`;

0 comments on commit 8095cd4

Please sign in to comment.