Font Awesome에서는 다양한 아이콘을 무료로 제공한다. React 환경에서는 SVG-CORE를 사용하여 아이콘을 이용할 수 있다.
npm i --save @fortawesome/fontawesome-svg-core @fortawesome/free-regular-svg-icons @fortawesome/free-solid-svg-icons @fortawesome/react-fontawesome
_app.tsx에서 다음과 같이 설정해주면 typescript 환경에서 FontAwesomeIcon 태그를 활용하여 아이콘을 사용할 수 있다.
import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
import '@fortawesome/fontawesome-svg-core/styles.css'
library.add(fas)
각 아이콘은 패키지 종류(fas/far)와 아이콘 이름을 통하여 지정할 수 있다. style을 활용하여 아이콘의 크기 등을 조절할 수 있고 color를 통하여 색 또한 변경할 수 있다.
<FontAwesomeIcon icon={['fas','users']} style={{marginRight:5, maxWidth:15}}/>
로그인한 사용자만 페이지에 접근할 수 있게 설정. 로그인하지 않은 경우 로그인 페이지로 이동
export const getServerSideProps : GetServerSideProps =async ({req,res}) => {
try {
const cookie = req.headers.cookie;
//cookie missing
if(!cookie) throw new Error("Missing auth token");
//user authentication on backend
await axios.get("/auth/me",{headers:{cookie}})
return {props: {}}
} catch (error) {
//move to login page
res.writeHead(307,{Location:"/login"}).end()
return {props: {}}
}
}
사용자가 작성한 title과 body를 axios로 서버에 전송
포스트 성공시 생성된 포스트 페이지로 이동
const submitPost = async (e: FormEvent) => {
e.preventDefault()
if(title.trim()===""||!subName) return;
try {
const {data:post} = await axios.post<Post>("/posts",{
title: title.trim(),
body,
sub: subName
})
router.push(`/r/${subName}/${post.identifier}/${post.slug}`)
} catch (error) {
console.error(error);
}
}
포스트가 속한 sub을 찾은 뒤 새로운 Post 생성하여 DB에 저장
const createPost = async (req: Request, res: Response) => {
const {title, body, sub} = req.body;
//if title is empty
if(title.trim()===""){
return res.status(400).json({title: "Title cannot be empty"})
}
const user = res.locals.user;
try {
const subRecord = await Sub.findOneByOrFail({name: sub});
const post = new Post();
post.title=title;
post.body=body;
post.user=user;
post.sub=subRecord;
await post.save();
return res.json(post);
} catch (error) {
console.error(error);
return res.status(500).json({error: "Something went wrong"})
}
}
router.post("/",userMiddleware,authMiddleware,createPost)
sub 정보를 가져올 때 속한 포스트 정보도 같이 전달
SWR에서 사용할 fetcher를 모든 페이지에 적용하기 위하여 _app.tsx를 수정
//swr fetcher
const fetcher =async (url:string) => {
try {
const res = await axios.get(url);
return res.data
} catch (error:any) {
throw error.response.data
}
}
return <SWRConfig
value={{fetcher}}
>
<AuthProvider>
<Head>
<title>Reddit_Clone</title>
<link rel="icon" href="/favicon.ico" />
</Head>
{!authRoute && <NavBar/>}
<div className={authRoute ? "" : "pt-16"}>
<Component {...pageProps} />
</div>
</AuthProvider>
</SWRConfig>
사용:
const {data: post, error} = useSWR<Post>(identifier&&slug? `/posts/${identifier}/${slug}`:null)
comment 내용을 입력받아 axios로 backend에 전달
const submitComment = async (e:FormEvent) => {
e.preventDefault();
if(newComment.trim()==="") return;
try {
await axios.post(`/posts/${post?.identifier}/${post?.slug}/comments`,{
body: newComment
})
setnewComment("")
mutate()
} catch (error) {
console.error(error);
}
}
mutate(): SWR의 캐시된 데이터를 갱신하는 함수
댓글을 새로 등록하면 바로 밑에 출력되도록 설정하기 위해서는 useSWR에서 mutate 매서드를 받아온 후, 댓글 등록이 완료된 후 mutate를 실행해 주면 된다.
새로운 comment 생성
const createPostComment = async (req: Request, res: Response) => {
const {identifier,slug} = req.params;
const body = req.body.body;
try {
const post = await Post.findOneByOrFail({identifier,slug})
const comment = new Comment()
comment.body=body;
comment.user = res.locals.user;
comment.post=post;
if(res.locals.user){
post.setUserVote(res.locals.user);
}
await comment.save();
return res.json(comment)
} catch (error) {
console.error(error);
return res.status(404).json({error:"Post not found"})
}
}
포스트에 속한 모든 댓글 찾아서 제공
const getPostComment =async (req: Request, res: Response) => {
const {identifier,slug} = req.params;
try {
const post =await Post.findOneByOrFail({identifier,slug})
const comments = await Comment.find({
where: {postId: post.id},
order: {createdAt:"DESC"},
relations: ["votes"]
})
if(res.locals.user){
comments.forEach((c)=>c.setUserVote(res.locals.user))
}
return res.json(comments);
} catch (error) {
console.error(error);
return res.status(500).json({error: "Something went wrong"})
}
}