빨리 인클라인 벤치프레스 하러가야하는 지금 이 시점에서 급하게 후다닥 작성하는 무지성 마감드리븐 마지막 졸업과제 후기!!
일단 맥스님께서 마지막 졸업과제에 대한것도 좋고 스터디 전체에 대한 회고도 좋다고 말씀하셨는데, 스터디 회고록은 별도로 다른 포스팅을 통해서 작성했기때문에 졸업과제 관련해서 삽질했던 내용들만 빠르게 작성 해보겠습니다.
가장 크게 느낀점 한가지만 먼저 말씀드리자면,
입니다.
일단 요구사항은 크게 아래와 같습니다.
그리고 이것외에 추가로 구현해보려고 한것들과 구현한것들은 아래와 같습니다.
일단 결과적으로 9번은 시도도 해보지 못한채 시간이 부족해서 끝내버렸다.
그래서 추후에 메신저 관련된 기능을 만들어볼 생각을 하고있다.
그외에 1번부터 8번까지는 모두 구현을했고, 배포도 완료했다.
배포하고나서 아이쓰님의 해킹과 나의 안일함 덕분에 몇가지 문제를 직면했다.
Delete / Edit를 Author에게만 Button이 보여지게 했지만, URL을 직접 조작해서 수정 및 삭제가 가능했다.
가장큰 문제였는데, 게시물을 보여줄때마다 불필요하게 많은 POST REQUEST가 발생되면서 성능에 저하가 생겼다.
사실 크게 어렵지않게 해결할수 있었다, Delete / Edit function이 Call될때 현재 로그인한 유저와, 글을 작성한 작성자가 동일한 사람인지 확인하는 로직을 아래와같이 추가해줬다.
const session = await getSession();
const post = await PRISMA_DB.post.findUnique({
where: {
id: id,
},
select: {
userId: true,
},
});
if (post?.userId !== session.id) {
redirect("/tweet");
}
여기서 post가 없는경우에도 방어코드를 작성해주는것이 맞지만, 마감드리븐 무지성 코드작성을 했기에 그 부분은 과감히 생략했다.
물론 현업에서 실제로 일을할때는 저렇게 무책임하게 코드를 작성하지는 않!는!다!
사실 local에서 코드를 작성하고 확인할때에도 불필요하게 많은 POST REQUEST가 발생되는걸 인지하고 있었다.
그럼에도 불구하고 왜 구조를 고치지 않았냐라고 물어볼 수 있는데, Local에서는 속도가 Resonable한 수준이라고 판단했기 때문이다.
일단 과제를 하면서 디자인과 전체적인 컨셉을 Meta의 Threads를 모방하려고 했다, 그러면서 작성하는 게시물은 이미지를 제외하고 오직 텍스트만 가능하게 했다.
그래야만 전체 Post를 보여줄때 데이터를 로드하는 부담이 덜하면서 더 빠르게 보여줄 수 있을거라고 판단했다.
그리고 솔직히 귀찮아서 pagination도 일부로 구현 안했기 때문에, 여튼 전체 Post를 보여줄때 최대한 빠르게 보여주고 싶었다.
(React Master 졸업과제에서 망해버린 기억도 있었고...)
여튼 흐리눈하면서 배포하고 확인해보니 화가날만큼 너무느렸다.
또, 수행시간이 게시물갯수에 비례해서 늘어날거니까 최악이라고 생각했다.
그래서 어디서 이런 불필요한 요청이 생기는지 확인해보니,
위 2가지를 확인하는 부분때문에 게시물을 보여줄때마다, like <-> dislike 할때마다 미쳐버릴정도로 느린 속도를 보여줬다.
구조를 바꾸는건 어렵지 않았다 여기저기 건들여야해서 귀찮았을뿐...
일단 현재 문제가 되는 부분을 잘 살펴보니, 개별적으로 게시물을 렌더링하는 컴포넌트 내부에서 위 2개의 로직을 수행하고 있었다.
그래서 어차피 "서버컴포넌트 내부에서 클라이언트 컴포넌트로 게시물을 랜더링하고 있으니, 게시물을 렌더링하는 컴포넌트의 props로 정보를 넘겨줘버리자." 라고 생각했다.
(근데 이게 옳바른 방향인지는 잘 모르겠다.)
그래서 구조를 아래와 같이 바꿔봤다.
// /tweet/page.tsx
async function getAllPosts() {
const posts = await PRISMA_DB.post.findMany({
orderBy: {
created_at: "desc",
},
select: {
id: true,
like: true,
userId: true,
payload: true,
comment: true,
user: {
select: {
id: true,
username: true,
profile_image: true,
likePost: true,
},
},
},
});
return posts;
}
return (
{posts.map((post) => (
<PostSummary
{...post}
key={post.id}
sessionId={session.id!}
currentUser={currentUser!}
/>
))}
);
// components/post/post-summary.tsx
reutrn (
<ActionButton
likeCount={like}
id={id}
userId={sessionId}
alreadyLike={currentUser!.likePost.includes(id)}
/>
);
결과적으로 위 사진처럼, 기존에는 1개의 게시물을 그리는데 2.3초가량이 걸렸다면, 현재는 모든 게시물을 그리는데 4.5초가 걸렸다.
+@
추가로 주간회고 시간에 백앤드 엔지니어이신 대훈님께서 설명해주셨는데, 이걸 N+1문제라고 하셨다.
전체 트윗을 불러오는 1개의 요청과, 개별 트윗에서 좋아요 상태를 불러오는 N개의 요청. 그래서 N+1 이라고 하셨다.
그리고 해결방법도 내가 통밥으로 이럼되겠지? 하고 시도한 방법이 맞아서 기분이 좋았다ㅋㅋ
나중에 또 찾아볼거같은 대훈님의 노션 (혹시 지워야 한다면 말씀해주세요 대훈님ㅠ)
마지막은 내가만든 결과물중 일부분에 대한 영상으로 마무으리~