inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

[리뉴얼] React로 NodeBird SNS 만들기

게시글 좋아요

게시글 좋아요 부분 강의에서 하트색이 바로 변하지 않습니다

554

장산

작성한 질문수 46

0

에러는 없고 데이터에서도 like하고 unlike가 잘되는건 확인했는데 화면에서는 새로고침 해야지만 바뀌네요

그리고 좋아요하고 다시 눌러서 취소 할려는데 취소가 안되고 계속 중복으로 좋아요가 됩니다

스크린샷 2023-04-10 오후 3.39.08.png

const express = require("express");

const { Post, Image, Comment, User } = require("../models");
const { isLoggedIn } = require("./middlewares");

const router = express.Router();
router.post("/", isLoggedIn, async (req, res, next) => {
  try {
    const post = await Post.create({
      content: req.body.content,
      userId: req.user.id,
    });
    const fullPost = await Post.findOne({
      where: { id: post.id },
      include: [
        {
          model: Image,
        },
        {
          model: Comment,
          include: [
            {
              model: User, // 댓글 작성자
              attributes: ["id", "nickname"],
            },
          ],
        },
        {
          model: User, // 게시글 작성자
          attributes: ["id", "nickname"],
        },
        {
          model: User, // 좋아요 누른 사람
          as: "Likers",
          attributes: ["id"],
        },
      ],
    });
    res.status(201).json(fullPost);
  } catch (error) {
    console.error(error);
    next(error);
  }
});
router.post("/:postId/comment", isLoggedIn, async (req, res, next) => {
  try {
    const post = await Post.findOne({
      where: { id: req.params.postId },
    });
    if (!post) {
      return res.status(403).send("존재하지 않는 게시글입니다"); //return을 붙여줘야지 send하고 밑에 json이 동시에 실행안됨
    }
    const comment = await Comment.create({
      content: req.body.content,
      PostId: req.params.postId,
      userId: req.user.id,
    });
    const fullComment = await Comment.findOne({
      where: { id: comment.id },
      include: [
        {
          model: User,
          attributes: ["id", "nickname"],
        },
      ],
    });
    res.status(201).json(fullComment);
  } catch (error) {
    console.error(error);
    next(error);
  }
});

router.patch("/:postId/like", isLoggedIn, async (req, res, next) => {
  try {
    const post = await Post.findOne({
      where: { id: req.params.postId },
    });
    if (!post) {
      return res.status(403).send("게시글이 존제하지 않습니다");
    }
    await post.addLikers(req.user.id);
    res.json({ PostId: post.id, userID: req.user.id });
  } catch (error) {
    console.error(error);
    next(error);
  }
});
router.delete("/:postId/like", isLoggedIn, async (req, res, next) => {
  try {
    const post = await Post.findOne({
      where: { id: req.params.postId },
    });
    if (!post) {
      return res.status(403).send("게시글이 존제하지 않습니다");
    }
    await post.removeLikers(req.user.id);
    res.json({ PostId: post.id, userID: req.user.id });
  } catch (error) {
    console.error(error);
    next(error);
  }
});

router.delete("/", (req, res) => {
  res.json({});
});

module.exports = router;

const express = require("express");

const { Post, Image, User, Comment } = require("../models");

const router = express.Router();

router.get("/", async (req, res, next) => {
  try {
    const posts = await Post.findAll({
      limit: 10,
      order: [
        ["createdAt", "DESC"],
        [Comment, "createdAt", "DESC"],
      ], //옵션  = ASC: 오름차순
      include: [
        {
          model: User,
          attributes: ["id", "nickname"], //아이디 ,닉네임만 가져오기
        },
        {
          model: Image,
        },
        {
          model: Comment,
          include: [
            {
              model: User,
              attributes: ["id", "nickname"],
            },
          ],
        },
        {
          model: User, // 좋아요 누른 사람
          as: "Likers",
          attributes: ["id"],
        },
      ],
    });
    res.status(200).json(posts);
  } catch (error) {
    console.error(error);
    next(error);
  }
});

module.exports = router;

import { Card, Button, Avatar, Popover, List, Comment } from "antd";
import {
  RetweetOutlined,
  HeartOutlined,
  MessageOutlined,
  HeartTwoTone,
  EllipsisOutlined,
} from "@ant-design/icons";
import { useDispatch, useSelector } from "react-redux";
import PropTypes from "prop-types";
import PostImages from "./PostImages";
import { useCallback, useEffect, useState } from "react";
import CommentForm from "./CommentForm";
import PostCardContent from "./PostCardContent";
import FollowButton from "./FollowButton";
import {
  REMOVE_POST_REQUEST,
  LIKE_POST_REQUEST,
  UNLIKE_POST_REQUEST,
} from "../reducers/post";

const PostCard = ({ post }) => {
  const dispatch = useDispatch();
  const { removePostLoading } = useSelector((state) => state.post);
  const [commentFormOpened, setCommentFormOpened] = useState(false);
  const id = useSelector((state) => state.user.me?.id);
  const onLike = useCallback(() => {
    if (!id) {
      return alert("로그인이 필요합니다.");
    }
    dispatch({
      type: LIKE_POST_REQUEST,
      data: post.id,
    });
  }, [id]);
  const onUnLike = useCallback(() => {
    if (!id) {
      return alert("로그인이 필요합니다.");
    }
    dispatch({
      type: UNLIKE_POST_REQUEST,
      data: post.id,
    });
  }, [id]);

  const onToggleComment = useCallback(() => {
    setCommentFormOpened((prev) => !prev);
  }, []);
  const onRemovePost = useCallback(() => {
    dispatch({
      type: REMOVE_POST_REQUEST,
      data: post.id,
    });
  }, []);
  const liked = post.Likers.find((v) => v.id === id);
  console.log("@@@", liked);
  return (
    <div style={{ marginBottom: 10 }}>
      <Card
        cover={post.Images[0] && <PostImages images={post.Images} />}
        actions={[
          <RetweetOutlined key="retweet" />,
          liked ? (
            <HeartTwoTone
              twoToneColor="#eb2f96"
              key="heart"
              onClick={onUnLike}
            />
          ) : (
            <HeartOutlined key="heart" onClick={onLike} />
          ),
          <MessageOutlined key="message" onClick={onToggleComment} />,
          <Popover
            key="more"
            content={
              <Button.Group>
                {id && post.user?.id === id ? (
                  <>
                    <Button>수정</Button>
                    <Button
                      type="danger"
                      onClick={onRemovePost}
                      loading={removePostLoading}
                    >
                      삭제
                    </Button>
                  </>
                ) : (
                  <Button>신고</Button>
                )}
              </Button.Group>
            }
          >
            <EllipsisOutlined />
          </Popover>,
        ]}
        extra={id && <FollowButton post={post} />}
      >
        <Card.Meta
          avatar={<Avatar>{post.user?.nickname[0]}</Avatar>}
          title={post.user?.nickname}
          description={<PostCardContent postData={post.content} />}
        />
      </Card>
      {commentFormOpened && (
        <div>
          <CommentForm post={post} />
          <List
            header={`${post.Comments.length}개의 댓글`}
            itemLayout="horizontal"
            dataSource={post.Comments}
            renderItem={(item) => (
              <li>
                <Comment
                  author={item.user?.nickname}
                  avatar={<Avatar>{item.user?.nickname[0]}</Avatar>}
                  content={item.content}
                />
              </li>
            )}
          />
        </div>
      )}
    </div>
  );
};

PostCard.propTypes = {
  post: PropTypes.shape({
    id: PropTypes.number,
    user: PropTypes.object,
    content: PropTypes.string,
    createdAt: PropTypes.string,
    Comment: PropTypes.arrayOf(PropTypes.object),
    Images: PropTypes.arrayOf(PropTypes.object),
    Likers: PropTypes.arrayOf(PropTypes.object),
  }).isRequired,
};

export default PostCard;

react redux node.js express next.js

답변 2

1

제로초(조현영)

빨간색이 바로 안 들어오는 것은 리덕스 리듀서 문제입니다. 리듀서에서 해당하는 게시물을 찾아서 Likers에 내 아이디를 제대로 추가했는지 확인해보셔야 합니다.

0

장산

case LIKE_POST_REQUEST:
        draft.likePostLoading = true;
        draft.likePostDone = false;
        draft.likePostError = null;
        break;
      case LIKE_POST_SUCCESS: {
        const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
        post.Likers.push({ id: action.data.userId });
        draft.likePostLoading = false;
        draft.likePostDone = true;
        break;
      }
      case LIKE_POST_FAILURE:
        draft.likePostLoading = false;
        draft.likePostError = action.error;
        break;
      case UNLIKE_POST_REQUEST:
        draft.unlikePostLoading = true;
        draft.unlikePostDone = false;
        draft.unlikePostError = null;
        break;
      case UNLIKE_POST_SUCCESS: {
        const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
        post.Likers = post.Likers.filter((v) => v.id !== action.data.userId);
        draft.unlikePostLoading = false;
        draft.unlikePostDone = true;
        break;
      }
      case UNLIKE_POST_FAILURE:
        draft.unlikePostLoading = false;
        draft.unlikePostError = action.error;
        break;

리듀서에서는 제대로 들어가는거같은데

스크린샷 2023-04-10 오후 8.15.36.png바로 반영이 안되는거같네요...

현재 좋아요랑 좋아요취소랑 바로 반영이 안되고 새로고침해야지 반영이됩니다 ㅠㅠ 그리고 좋아요 버튼 두번누르면 좋아요 되었다가 바로 취소가 되어야하는데 중복으로 좋아요가 됩니다 ㅠㅠ

어디가 문제인지 잘모르겠습니다 ㅠㅠ

1

제로초(조현영)

리듀서가 문제라서 반영이 안되는 겁니다. action 탭도 클릭해서 값들 제대로 있는지 확인하세요.

1

제로초(조현영)

const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Likers = post.Likers.filter((v) => v.id !== action.data.userId); draft.unlikePostLoading = false; draft.unlikePostDone = true;

이게 문제인거니까 각 요소들 다 확인을 해야죠

0

장산

후 겨우 찾았습니다.

routes - post.js에 있는 res.json보니까 userID로 되어있고 reducer에서는 userId로 되어있어서 안되는거였습니다. 둘다 일치시켜주니까 작동되네요

제로초님 힌트아니였으면 눈치채지도 못했겠네요 ㅠㅠ감사합니다.요즘 대소문자때문에 많이 힘드네요 ㅠㅠ

1

제로초(조현영)

나중에 타입스크립트로 꼭 넘어가세요. 그러면 오타를 낼 일이 많이 줄어듭니다.

0

장산

삭제된 글입니다

1

제로초(조현영)

입력이 된것 같은데요??

const liked = post.Likers.find((v) => v.id === id);

여기서 id랑 post.Likers를 개별적으로 콘솔로그해보세요.

넥스트 버젼 질문

0

78

2

로그인시 401 Unauthorized 오류가 뜹니다

0

89

1

무한 스크롤 중 스크롤 튐 현상

0

175

1

특정 페이지 접근을 막고 싶을 때

0

103

2

createGlobalStyle의 위치와 영향범위

0

96

2

인라인 스타일 리렌더링 관련

0

91

2

vsc 에서 npm init 설치시 오류

0

146

2

nextjs 15버전 사용 가능할까요?

0

158

1

화면 새로고침 문의

0

121

1

RTK에서 draft, state 차이가 있나요?

0

153

2

Next 14 사용해도 될까요?

0

452

1

next, node 버전 / 폴더 구조 질문 드립니다.

0

349

1

url 오류 질문있습니다

0

211

1

ssh xxxxx로 우분투에 들어가려니까 port 22: Connection timed out

0

373

1

sudo certbot --nginx 에러

0

1275

2

Minified React error 콘솔에러 (hydrate)

0

470

1

카카오 공유했을 때 이전에 작성했던 글이 나오는 버그

0

247

1

프론트서버 배포 후 EADDRINUSE에러 발생

0

327

1

npm run build 에러

0

518

1

front 서버 npm run build 중에 발생한 에러들

0

382

1

서버 실행하고 브라우저로 들어갔을때 404에러

0

338

2

css 서버사이드 랜더링이 적용되지 않아서 문의 드립니다.

0

288

1

팔로워 3명씩 불러오고 데이터 합쳐주는걸로 바꾸고 서버요청을 무한으로하고있습니다.

0

239

2

해시태그 검색에서 throttle에 관해 질문있습니다.

0

201

1