sns 프로젝트

북마크 기능 구현하기

우주속공간 2024. 1. 22. 16:53

 

 

📁 북마크 기능 미리보기

 

 

 

북마크 기능 구현 과정을 간단하게 정리해보자면 다음과 같다.

 

프론트

  • bookmark 버튼 및 bookmark 페이지 형성

 

백엔드

 

  • bookmark 테이블 생성(tweets 테이블과 관계형성)
  • getTweet API에 isBookmark 변수 생성후 트윗 북마크 유무 알려주기.

   =>isBookmark변수는 false가 기본값. isBookmark가 true인 트위터 게시물은 bookmark 페이지에서 bookmarkAPI 호출시 해당 데이터 넘겨주기

 

 

실제 구현한 코드


 

sequelize에 bookmark 테이블 생성하고 tweet_id을 기준으로 tweet데이터가 담겨져 있는 tweets 테이블과 관계형성하였다.

➡️ 관계를 형성한 이유는 나중에 북마크 페이지에서 데이터를 가지고 올때 tweet_id를 기준으로 Tweets테이블과 join하여 북마크 트윗게시물 데이터를 가지고 올 수 있기때문이다.

 

bookmark.ts

@Table({
  timestamps: false,
  tableName: "bookmark",
  charset: "utf8",
  collate: "utf8_general_ci",
})
export class Bookmark extends Model {
  @Column({
    type: DataType.INTEGER,
    autoIncrement: true,
    primaryKey: true,
  })
  id!: IntegerDataType;

  @Column({
    type: DataType.STRING,
    allowNull: true,
  })
  content!: string;

  @ForeignKey(() => Tweets)
  @Column({
    type: DataType.INTEGER,
    allowNull: false,
  })
  tweet_id!: IntegerDataType;

  @Column({
    type: DataType.INTEGER,
    allowNull: true,
  })
  user_id!: IntegerDataType;

  @BelongsTo(() => Tweets)
  tweets!: Tweets;
}

 

saveBookmark.ts

router.post(
  "/",
  verifyAccessToken,
  verifyRefreshToken,
  async (req: any, res: Response, next: NextFunction) => {
    await Bookmark.create({
      tweet_id: req.body.tweet_id,
      user_id: req.body.id,
    }).then((result) => {
      res.status(201).json(result);
    });
  }
);

router.post(
  "/delete",
  verifyAccessToken,
  verifyRefreshToken,
  async (req: any, res: Response, next: NextFunction) => {
    await Bookmark.destroy({
      where: {
        tweet_id: req.body.tweet_id,
        user_id: req.body.id,
      },
    }).then((result) => {
      res.status(201).json(result);
    });
  }
);

 

 

 

제일 처음 페이지를 로딩했을때 모든 트윗 데이터를 가져오기 위해 사용하는 getTweet API 코드이다.

isBookmark라는 변수를 하나 만들어서 해당 트윗 북마크 유무를 알려준다.

현재 로그인한 유저 데이터(currentUser)를 조회한 후

=> 유저가 북마크를 누른 tweet데이터가 있는지 찾아보고

=> 북마크된 트윗의 경우 is_bookmark를 true값으로 넘겨준다.(기본값은 false)

 

router.get(
  "/select",
  verifyRefreshToken,
  async (req: any, res: Response, next: NextFunction) => {
    const currentUser: Users[] = await Users.findOne({
      where: {
        email: req.email,
      },
    }).then((r: any) => {
      return r.user_id;
    });

    let pageNum = Number(res.req.query.pageCount);

    let offset = 0;
    if (pageNum > 1) {
      offset = 10 * (pageNum - 1);
    }

    const selectCurrentTweets = await Tweets.findAll({
      include: [Likes, Bookmark, Comments],
      offset: offset,
      limit: 10,
    }).then((d: any) => {
      return d.map((d: any) => {
        let isLike = false;
        //기본값은 false로 넘겨줌
        let isBookmark = false;

        if (
          d.like.some((i: any) => {
            return i.user_id === currentUser;
          })

        ) {
          isLike = true;
        }

		//트윗데이터중에서 현재 유저가 북마크를 한 데이터가 존재하는지 탐색 후 북마크된 데이터는 isBookmark를 true값으로 넘겨줌.

        if (
          d.bookmark.some((i: any) => {
            return i.user_id === currentUser;
          })
        ) {
          isBookmark = true;
        }

        return {
          tweet_id: d.tweet_id,
          content: d.content,
          email: d.email,
          like: d.like,
          tag: d.tag,
          user_id: d.user_id,
          write_date: d.write_date,
          upload_file: d.upload_file,
          reply_tweet_id: d.reply_tweet_id,
          is_like: isLike,
          is_bookmark: isBookmark,
          comment: [],
          is_opened: false,
          retweet_opened: false,
        };

    let count = await Tweets.count();
    let totalPageNumber = Math.round((await Tweets.count()) / 10);

    res.status(200).json({
      data: selectCurrentTweets,
      count,
      user_id: currentUser,
      totalPageNumber: totalPageNumber,
    });
  }
);

 

 

bookmark 버튼 및 관련 함수 

-> 북마크 유무에 따라 보여주는 아이콘과 클릭시 실행 함수 다르게 넣어주기. 

<img
                          className="w-6 h-4 pl-2"
                          alt="#"
                          src={
                            t.is_bookmark
                              ? "/assets/bookmark.png"
                              : "/assets/bookmark_before.png"
                          }
                          onClick={() => {
                            console.log("클릭!!!!!");
                            if (t.is_bookmark === true) {
                              deleteBookmark(t.tweet_id);
                            }

                            if (t.is_bookmark === false) {
                              addBookmark(t.tweet_id);
                            }
                          }}
                        />

 

  const deleteBookmark = (prop: number) => {
    customAxios
      .post("/saveBookmark/delete", {
        tweet_id: prop,
        id: id,
      })
      .then((res) => {
        customAxios
          .get("/getTweets/select", {
            params: { getCurrentPage },
          })
          .then((result: any) => {
           //데이터 업데이트 하기
            queryClient.invalidateQueries(["select"]);

          });
      });
  };

  const addBookmark = (tweet_id: number) => {
    customAxios
      .post("/saveBookmark", {
        tweet_id: tweet_id,
        id: id,
      })
      .then((res) => {
        customAxios
          .get("/getTweets/select", {
            params: { getCurrentPage },
          })
          .then((result: any) => {
            queryClient.invalidateQueries(["select"]);
          });
      });
  };