[공감병동 프로젝트] 응원 및 스크랩 기능

somin·2021년 12월 13일
0

공감병동 프로젝트

목록 보기
10/12
post-thumbnail

공감병동 프로젝트에서 내가 맡은 페이지들은 아래와 같다.

  • 메인(Home) 페이지
  • 글 작성 및 수정 페이지, 상세 게시글 페이지
  • 진료과별 이야기 페이지
  • 상급종합병원 목록 페이지
  • 나의 기록 페이지

이번 포스트는 기록해놓고 싶은 기능에 대한 포스팅이다.
바로 상세 게시글 페이지의 응원 및 스크랩 기능인데,
유난히 기능 구현에 있어서 시행착오가 많았기 때문이다.

[Step.01] Post Data Fetching

공감병동 프로젝트는 Next.js를 사용하였는데, 해당 학습은 github에 남겨져 있다.

먼제 Post Data를 데이터베이스에서 가져와야했다.
해당 페이지는 응원하기와 스크랩, 댓글 등 사용자 인터렉션이 발생하기 때문에
GetServerSideProps를 이용하였다.


export const getServerSideProps: GetServerSideProps = async (context: any) => {
  const { slug } = context.query;

  const postRes = await axios.get(
    `${process.env.NEXT_PUBLIC_API_URL}/post/detail/${encodeURI(slug)}`
  );

  const postData = postRes.data;

  return { props: { postData } };
};

slug를 query에서 가져와 서버에 데이터를 요청하였고,
해당 데이터를 props로 페이지에 주입시켜준다.
postData에는 응원의 개수와 스크랩의 개수도 포함되어있다.

[Step.02] useState

데이터베이스에서 불러온 응원 및 스크랩의 개수를 페이지 렌더링에 사용하게 되면
응원 및 스크랩을 누를때마다 모든 데이터를 다시 렌더링해주어야 하는 불편함이 있었다.
그래서 useState를 사용해 Count와 해당 토글을 눌렀는지의 여부를 상태로 관리하였다.

const [likes, setLikes] = useState({
    isLike: false,
    likesCount: postData.likes,
  });

  const [scraps, setScraps] = useState({
    isScrap: false,
    scrapsCount: postData.scraps,
  });

이렇게 만들어둔 상태는 해당 토글을 누를때 마다 불린값과 카운트가 변화되도록 해주었다.
또한, 데이터베이스도 업데이트 되도록 함수를 만들었는데, submitPostsubmitDelete는 아래에서 따로 기록하겠다. (scraps도 동일한 로직이다.)

const handleLikes = async () => {
    if (likes.isLike) {
      setLikes({
        isLike: false,
        likesCount: likes.likesCount - 1,
      });
      submitDelete("deleteLike");
    } else {
      setLikes({
        isLike: true,
        likesCount: likes.likesCount + 1,
      });
      submitPost("postLike");
    }
  };

[Step.03] useEffect

사용자가 이미 응원하기나 스크랩하기를 누른 상태인지를 확인하기 위해 useEffect를 사용하였다.
코드는 아래와 같이 작성되었는데, 사실 이 부분을 구현하기 위해 atom과 서버를 수정해 주었다.

useEffect(() => {
    if (user.isLogin) {
      if (user.likes) {
        const findLikes = user.likes.filter(
          (like: { posts_id: number }) => like.posts_id === postData.id
        );
        if (findLikes.length === 1) {
          setLikes({
            isLike: true,
            likesCount: likes.likesCount,
          });
        }
      }

      if (user.scraps) {
        const findScraps = user.scraps.filter(
          (scrap: { posts_id: number }) => scrap.posts_id === postData.id
        );
        if (findScraps.length === 1) {
          setScraps({
            isScrap: true,
            scrapsCount: scraps.scrapsCount,
          });
        }
      }
    }
  }, []);

atom 수정

: user state에 likes와 scraps 배열 추가

export const userState = atom({
  ...,
  default: {
    accessToken: "",
    isLogin: false,
    description: "",
    id: 0,
    img: "",
    loginType: "",
    nickname: "",
    social_id: "",
    likes: [{ posts_id: 0 }],
    scraps: [{ posts_id: 0 }],
    ...
  },
  ...,
});

서버 수정

: 로그인시 보내주는 user data에 likes와 scraps 배열 추가

await users.findOrCreate({
      where: { social_id: userInfo.data.id },
      defaults: userInfoValue,
      include: [
        {
          model: likes,
          required: false,
          attributes: ["posts_id"],
        },
        {
          model: scraps,
          required: false,
          attributes: ["posts_id"],
        },
      ],
});

[Step.04] 기능 구현

이제 데이터를 불러와 상태로 저장하는 것을 완료했으니, 응원 및 스크랩 기능 구현만 남았다.
사실 응원 및 스크랩은 저장되는 데이터베이스 테이블만 다를 뿐 로직이 같기 때문에 변수로
likesscraps를 입력받아 하나의 함수로 구현해 주었다.

const submitPost = async (context: string) => {
    const result = await axios.post(
      `${process.env.NEXT_PUBLIC_API_URL}/post/${context}`,
      {
        users_id: user.id,
        posts_id: postData.id,
      },
      {
        headers: {
          Authorization: `Bearer ${user.accessToken}`,
          "Content-Type": "application/json",
        },
        withCredentials: true,
      }
    );

    if (result.status === 201) {
      if (context === "postLike") {
        setUser({
          ...user,
          likes: [...user.likes, { posts_id: postData.id }],
        });
      } else {
       ... //위와 동일
      }
    }
  };

  const submitDelete = async (context: string) => {
    const result = await axios.delete(
      `${process.env.NEXT_PUBLIC_API_URL}/post/${context}?users_id=${user.id}&posts_id=${postData.id}`,
      {
        headers: {
          Authorization: `Bearer ${user.accessToken}`,
          "Content-Type": "application/json",
        },
        withCredentials: true,
      }
    );

    if (result.status === 200) {
      if (context === "deleteLike") {
        setUser({
          ...user,
          likes: user.likes.filter(
            (like: { posts_id: number }) => like.posts_id !== postData.id
          ),
        });
      } else {
        ... //위와 동일
      }
    }
  };

사실 axios 요청은 앞선 다른 프로젝트들을 진행하며
충분히 익숙해졌기 때문에 문제없이 코드를 작성할 수 있었지만,
atom을 업데이트하는 것에 어려움을 겪었다.

많은 고민을 했는데, 막상 완성된 코드는 생각보다 단순하다..!
그냥 spread로 기존 user 데이터를 펼쳐주고 likes와 scraps를 필터링한 것 뿐..

그래도 오래 고민하고 다양한 시도를 해본 것이 뿌듯하게, 나름 만족스러운 결과가 나왔다!

profile
✏️

0개의 댓글

관련 채용 정보