Skip to content

Commit

Permalink
Feat: 5주차 과제 코드 #15
Browse files Browse the repository at this point in the history
  • Loading branch information
2mingyu committed Nov 7, 2024
1 parent cfdf0bd commit ccf6023
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 19 deletions.
74 changes: 74 additions & 0 deletions mingyu/week5/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions mingyu/week5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
"@types/node": "^16.18.119",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"axios": "^1.7.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.28.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
Expand Down
33 changes: 14 additions & 19 deletions mingyu/week5/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import RootPage from "pages/RootPage";
import LoginPage from "pages/LoginPage";
import HomePage from "pages/HomePage";
import PostPage from "pages/PostPage";

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
<BrowserRouter>
<Routes>
<Route path="/" element={<RootPage />}>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/post/:postId" element={<PostPage />} />
</Route>
</Routes>
</BrowserRouter>
);
}

Expand Down
65 changes: 65 additions & 0 deletions mingyu/week5/src/pages/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import axiosInstance from "services/axiosInstance";

interface Post {
id: number;
title: string;
category: string;
writer: string;
content: string;
like: number;
scrap: number;
replyCount: number;
}

export default function HomePage() {
const [page, setPage] = useState<number>(1);
const [posts, setPosts] = useState<Post[]>([]);
const navigate = useNavigate();

useEffect(() => {
const fetchPosts = async () => {
try {
const response = await axiosInstance.get(`/api/posts?page=${page}`);
setPosts(response.data.data.posts);
} catch (error) {
console.error(error);
}
};

fetchPosts();
}, [page]);

const handlePageChange = (newPage: number) => {
setPage(newPage);
};

return (
<div>
<h1>게시글 목록</h1>
<div>
<button
onClick={() => handlePageChange(page - 1)}
disabled={page === 1}
>
이전 페이지
</button>
<span>페이지 {page}</span>
<button onClick={() => handlePageChange(page + 1)}>다음 페이지</button>
</div>
<ul>
{posts.map((post) => (
<li key={post.id} onClick={() => navigate(`/post/${post.id}`)}>
<h2>{post.title}</h2>
<p>{post.content}</p>
<p>작성자: {post.writer}</p>
<p>
좋아요: {post.like} 스크랩: {post.scrap} 댓글: {post.replyCount}
</p>
</li>
))}
</ul>
</div>
);
}
42 changes: 42 additions & 0 deletions mingyu/week5/src/pages/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import axiosInstance from "services/axiosInstance";

export default function LoginPage() {
const [studentId, setStudentId] = useState("");
const [password, setPassword] = useState("");
const navigate = useNavigate();

const handleLogin = async () => {
try {
const response = await axiosInstance.post("/api/members/login", {
studentId: studentId,
password: password,
});
const accessToken = response.data.data.accessToken;
localStorage.setItem("accessToken", accessToken);
navigate("/");
} catch (error) {
console.error("로그인 실패:", error);
alert("로그인 실패");
}
};

return (
<div>
<input
type="text"
placeholder="학번"
value={studentId}
onChange={(e) => setStudentId(e.target.value)}
/>
<input
type="password"
placeholder="비밀번호"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>로그인</button>
</div>
);
}
97 changes: 97 additions & 0 deletions mingyu/week5/src/pages/PostPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axiosInstance from "services/axiosInstance";

interface DetailPost {
id: number;
title: string;
category: string;
writer: string;
content: string;
like: number;
isLiked: boolean;
scrap: number;
replyCount: number;
createDate: string;
replies: Reply[];
}

interface Reply {
id: number;
writer: string;
content: string;
createDate: string;
}

export default function PostPage() {
const { postId } = useParams<{ postId: string }>();
const [post, setPost] = useState<DetailPost | null>(null);

useEffect(() => {
const fetchPost = async () => {
try {
const response = await axiosInstance.get(`/api/posts/${postId}`);
setPost(response.data.data);
} catch (error) {
console.error("게시글 불러오기 실패", error);
}
};

fetchPost();
}, [postId]);

// 좋아요 버튼 클릭 핸들러
const handleLike = async () => {
if (!post) return;

try {
await axiosInstance.put(`/api/posts/${postId}/like`);
setPost((prevPost) =>
prevPost
? {
...prevPost,
isLiked: !prevPost.isLiked,
like: prevPost.isLiked ? prevPost.like - 1 : prevPost.like + 1,
}
: null
);
} catch (error: any) {
if (error.response && error.response.status === 400) {
alert("자신의 게시글에는 추천을 할 수 없습니다.");
} else {
console.error("좋아요 요청 실패:", error);
alert("좋아요 요청 실패");
}
}
};

if (!post) {
return <p>게시글을 불러오는 중입니다...</p>;
}

return (
<div>
<h1>{post.title}</h1>
<p>카테고리: {post.category}</p>
<p>작성자: {post.writer}</p>
<p>작성일: {new Date(post.createDate).toLocaleDateString()}</p>
<p>{post.content}</p>
<p>
좋아요: {post.like} | 스크랩: {post.scrap} | 댓글: {post.replyCount}
</p>

<button onClick={handleLike}>{post.isLiked ? "♥" : "♡"}</button>

<h2>댓글</h2>
<ul>
{post.replies.map((reply) => (
<li key={reply.id}>
<p>작성자: {reply.writer}</p>
<p>{reply.content}</p>
<p>작성일: {new Date(reply.createDate).toLocaleDateString()}</p>
</li>
))}
</ul>
</div>
);
}
16 changes: 16 additions & 0 deletions mingyu/week5/src/pages/RootPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Link, Outlet } from "react-router-dom";

export default function RootPage() {
return (
<>
<header>
<h1>5주차 과제: axios, router, localStorage</h1>
<Link to="/">메인 페이지</Link>
<Link to="/login">로그인 페이지</Link>
</header>
<main>
<Outlet />
</main>
</>
);
}
Loading

0 comments on commit ccf6023

Please sign in to comment.