유저 페이지의 context path
는 /u/[username]
의 형태를 갖도록 한다.
이를 위해 reddit-clone-app\client\src\pages\u\[username].tsx
파일을 생성하고 아래 내용을 입력한다.
import UserPage from 'react'
const UserPage = () => {
const router = useRouter();
// 사용자 이름을 URL에서 가져옴
const username = router.query.username;
// swr 요청을 사용해 사용자 정보 가져옴
const {data, error} = useSWR(username ? `/users/${username}` : null);
return (
<div>UserPage</div>
)
}
export default UserPage
사용자 관련 API 핸들러를 정의하기 위해 reddit-clone-app\server\src\routes\users.ts
파일을 생성하고
아래 내용을 추가한다.
import { Router, Request, Response } from "express";
import { User } from "../entities/User";
import user from "../middlewares/user";
import userMiddleware from "../middlewares/user";
import Post from "../entities/Post";
import Comment from "../entities/Comment"
const getUserData = async (req: Request, res: Response) => {
console.log('테스트')
try {
// 유저 정보 가져오기
const user = await User.findOneOrFail({
where: { username: req.params.username},
select: ["username", "createdAt"]
})
// 유저가 쓴 포스트 정보 가져오기
const posts = await Post.find({
where: {username: user.username},
// 연관테이블을 가져오는 이유는 userVote, voteScore 데이터를 가져오기 위함
relations: ["comments", "votes", "sub"]
})
// 유저가 쓴 댓글정보 가져오기
const comments = await Comment.find({
where: {username: user.username},
relations: ["post"]
})
// 각 포스트와 코멘트에 현재 사용자의 투표 정보 입력
if(res.locals.user) {
const {user} = res.locals;
posts.forEach(p => p.setUserVote(user));
comments.forEach(c => c.setUserVote(user));
}
let userData: any[] = [];
// 엘리먼트 타입의 데이터를 json 형태 데이터로 변경 필요
// @Expose() 데코레이터를 사용한 Getter 데이터를 포함하기 위함
posts.forEach(p => userData.push({ type: "Post", ...p.toJSON() }));
comments.forEach(c => userData.push({ type: "Comment", ...c.toJSON() }))
// 최신 정보가 먼저 오게 순서 정렬
userData.sort((a, b) => {
if(b.createdAt > a.createdAt) return 1;
if(b.createdAt < a.createdAt) return -1;
return 0;
})
return res.json({ user, userData });
} catch (error) {
console.log(error);
return res.status(500).json({ error: "문제가 발생했습니다." });
}
}
const router = Router();
// "/api/users/[username]" 형태의 요청을 받아 핸들러 매핑
router.get("/:username", userMiddleware, getUserData)
export default router;
reddit-clone-app\client\src\pages\u\[username].tsx
파일을 아래 내용으로 수정한다.
import Link from 'next/link';
import { useRouter } from 'next/router'
import React from 'react'
import useSWR from 'swr'
import PostCard from '../../components/PostCard';
import { Post, Comment } from '../../types';
import Image from 'next/image'
import dayjs from 'dayjs';
const UserPage = () => {
const router = useRouter();
const username = router.query.username;
const {data, error} = useSWR(username ? `/users/${username}` : null);
if(!data) return null;
return (
<div className='flex max-w-5xl px-4 pt-5 mx-auto'>
{/* 유저 포스트 댓글 리스트 */}
<div className='w-full md:mr-3 md:w-8/12'>
{data.userData.map((data: any) => {
if(data.type === "Post") {
const post:Post = data;
return <PostCard key={post.identifier} post={post} />
} else {
const comment: Comment = data;
return (
<div
key={comment.identifier}
className="flex my-4 bg-white rounded"
>
<div className='flex-shrink-0 w-10 py-10 text-center bg-gray-200 rounded-l'>
<i className='text-gray-500 fas fa-comment-alt fa-xs'></i>
</div>
<div className='w-full p-2'>
<p className='mb-2 text-xs text-gray-500'>
<Link href={`/u/${comment.username}`}>
<a className='cursor-pointer hover:underline'>
{comment.username}
</a>
</Link>
<span>commented on</span>
<Link href={`${comment.post?.url}`}>
<a className='cursor-pointer font-semibold hover:underline'>
{comment.post?.title}
</a>
</Link>
<span>*</span>
<Link href={`/u/${comment.post?.subName}`}>
<a className='text-black cursor-pointer hover:underline'>
/r/{comment.post?.subName}
</a>
</Link>
</p>
<hr />
<p className='p-1'>{comment.body}</p>
</div>
</div>
)
}
})}
</div>
{/* 유저 정보 */}
<div className='hidden w-4/12 ml-3 md:block'>
<div className='flex items-center p-3 bg-gray-400 rounded-t'>
<Image
src="http://www.gravatar.com/avatar?d=mp&f=y"
alt="user profile"
className="mx-auto border border-white rounded-full"
width={40}
height={40}
/>
<p className='pl-2 text-md'>{data.user.username}</p>
</div>
<div>
<p>
{dayjs(data.user.create).format("YYYY.MM.DD")} 가입
</p>
</div>
</div>
</div>
)
}
export default UserPage