

comment 일때와 post 일때를 구분하여 처리
이미 누른 경우 투표 무효 처리
const vote =async (value: number, comment?:Comment) => {
if(!authenticated) router.push("/login")
//if aleady clicked, reset the vote
if((!comment&&value===post?.userVote)||(comment && comment.userVote ===value)) value=0;
try {
await axios.post("/votes",{
identifier,
slug,
commentIdentifier: comment?.identifier,
value
})
mutate();
} catch (error) {
console.error(error);
}
}
post일때, comment 일때 나누어서 처리
vote 생성 혹은 수정시 post 정보를 다시 가져와 vote 다시 지정
const vote =async (req:Request,res:Response) => {
const {identifier,slug,commentIdentifier,value} = req.body;
//value check
if(![-1,0,1].includes(value)) return res.status(400).json({value:"Only -1, 0, 1 value is possible"})
try {
const user:User = res.locals.user;
let post: Post | undefined = await Post.findOneByOrFail({identifier,slug});
let vote: Vote | undefined;
let comment: Comment | undefined;
//find vote on DB
if(commentIdentifier){//comment vote
comment= await Comment.findOneByOrFail({identifier:commentIdentifier});
vote = await Vote.findOneBy({username:user.username,commentId: comment.id});
} else{ //post vote
vote = await Vote.findOneBy({username:user.username,postId: post.id});
}
//Vote not found
if(!vote && value===0) return res.status(404).json({error:"Vote not found"})
else if(!vote){ //create new vote
vote = new Vote();
vote.user=user;
vote.value=value;
if(comment) vote.comment = comment; //comment vote
else vote.post = post; //post vote
await vote.save()
}else if(value===0){ //reset vote
await vote.remove();
}else if(vote.value!==value){//update vote
vote.value=value;
await vote.save();
}
//find post
post = await Post.findOneOrFail({
where:{identifier, slug},
relations : ["comments","comments.votes","sub","votes"]
}
)
//set vote info to post
post.setUserVote(user);
post.comments.forEach((c)=>c.setUserVote(user))
return res.json(post);
} catch (error) {
console.error(error);
return res.status(500).json({error:"Something went wrong"})
}
}
router.post("/",userMiddleware,authMiddleware,vote)


커뮤니티에 소속된 post를 각각 랜더링하는 컴포넌트
post 페이지와 비슷한 UI로 구성
vote 함수는 따로 작성한다.
const vote = async (value:number) => {
if(!authenticated) router.push('/login')
if(value === userVote) value =0;
try {
await axios.post('/votes',{
identifier,
slug,
value
})
if(subMutate){
subMutate();
}
} catch (error) {
console.error(error);
}
}

페이지 매김 및 무한 로딩을 위한 SWR API
const getKey = (pageIndex, previousPageData) => {
if (previousPageData && !previousPageData.length) return null
return `/users?page=${pageIndex}&limit=10`
}
const { data, size, setSize } = useSWRInfinite(getKey, fetcher)
더이상 요청할 데이터가 없을 때까지 로딩. 로딩할 데이터가 없으면 null 반환
const getKey = (pageIndex: number, previousPageData:Post[]) =>{
if(previousPageData && !previousPageData.length) return null;
return `/posts?page=${pageIndex}`;
}
const {data, error, size:page, setSize:setPage, isValidating, mutate}=useSWRInfinite<Post[]>(getKey)
스크롤을 내릴 때 포스트를 더 가져오기 위하여 사용
마지막 element를 관찰하다가 스크롤을 내리면서 viewport에서 intersection이 발생하면 포스트를 추가로 로딩
const [observedPost, setobservedPost] = useState("")
useEffect(() => {
if(!posts || posts.length===0) return;
const id = posts[posts.length-1].identifier;
if(id!==observedPost){
setobservedPost(id);
observedElement(document.getElementById(id))
}
}, [posts])
const observedElement = (element: HTMLElement | null) =>{
if(!element) return;
const observer = new IntersectionObserver(
(entries) =>{
//reached bottom
if(entries[0].isIntersecting === true){
// console.log("reached bottom");
setPage(page+1);
observer.unobserve(element);
}
},
{threshold:1}
);
observer.observe(element);
}
정렬된 목록에서 이미 가져온 개수 만큼 지나친 뒤(skip) perPage 만큼 가져오기
const getPosts =async (req: Request, res: Response) => {
const currentPage: number = (req.query.page || 0 ) as number;
const perPage: number = (req.query.count || 8) as number;
try {
const posts = await Post.find({
order:{createdAt:"DESC"},
relations: ["sub","votes","comments"],
skip: currentPage*perPage,
take: perPage,
})
if(res.locals.user){
posts.forEach((p)=> p.setUserVote(res.locals.user));
}
return res.json(posts);
} catch (error) {
console.error(error);
return res.status(500).json({error:"Something went wrong"})
}
}