• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    해결됨

리트윗 라우터에서 설정한 리트윗 검사가 되지 않습니다!(중복 리트윗 체크 및 본인 게시글 리트윗이 막아지지 않는 문제)

24.01.02 20:29 작성 24.01.03 00:45 수정 조회수 144

1

안녕하세요! React로 NodeBird SNS 만들기
섹션4 리트윗하기 강의 시청 중 발생한 에러에 대해 질문 드립니다!
항상 강의 잘 보고 있습니다! 제로초님 감사합니다!

 

[리트윗 라우터에서 설정한 리트윗 검사]
1. 자기 게시글을 리트윗한 경우
2. 자기 게시글을 리트윗한 다른 게시글을 다시 자기가 리트윗한 경우
3. 이미 리트윗한 게시글을 또 리트윗 하는 경우(중복 리트윗)

 

여러 개의 게시글을 가져오는 라우터에서 리트윗한 게시글, 작성자, 이미지 모델을 넣었으며,
게시글 Reducer에서 리트윗 실패 시 실패 확인을 'draft.retweetError = action.error;'로 확인하였습니다.
게시글 Saga에서도 리트윗 요청 실패 시 실패 결과를 'error: err.response.data'로 설정했습니다.

★ 프론트, 백엔드 쪽 터미널과 콘솔, 리덕스, 네티워크 쪽에 오류가 없음을 확인하였습니다.


코드 오타가 원인이라고 파악해 제로초님의 깃허브와 제 코드를 비교하며 확인했으나
원인을 찾을 수 없어 질문 올립니다. 아래는 가장 의심되는 코드 입니다.
중요하지 않은 코드는 '. . .' 으로 생략하였습니다.

front/pages/index.js


// React 라이브러리 훅 불러오기
import React, { useEffect } from 'react';
. . .
// 홈 컴포넌트(사용자 정의 태그)
const Home = () => {
  const dispatch = useDispatch();
  const { me } = useSelector((state) => state.user);
  const { mainPosts, hasMorePosts, loadPostsLoading, retweetError } = useSelector((state) => state.post);
. . .
  // 리트윗 실패 시 리트윗 에러 alert 창 띄우기
  useEffect(() => {
    if (retweetError) {
      alert(retweetError);
    }
  }, [retweetError]);


front/components/PostCard.js
따로 게시글을 리트윗 실패(RETWEET_FAILURE) 액션을 디스패치 해야 하는지 의심이 들었습니다!

// 게시글 카드 컴포넌트(사용자 정의 태그)
const PostCard = ({ post }) => {
. . .
  // 리트윗 버튼 콜백 함수
  const onRetweet = useCallback(() => {
    // 로그인을 안했을 때 '로그인이 필요합니다.' alert 창 띄우기
    if (!id) {
      return alert('로그인이 필요합니다.');
    }
    /* 리트윗 요청 액션 객체 디스패치 */
    return dispatch({
      type: RETWEET_REQUEST, // 리트윗 요청 액션
      data: post.id,         // 게시글 아이디
    });
  }, [id]);
. . .
  return (
    <div style={{ marginBottom: '20px' }}>
      <Card
        /* ---------- 이미지 : 이미지는 1개 이상 ---------- */
        cover={post.Images[0] && <PostImages images={post.Images} />}
        /* ---------- 액션 버튼 ---------- */
        actions={[
          /* ---------- 리트윗 버튼 ---------- */
          <RetweetOutlined key="retweet" onClick={onRetweet} />,
          . . .
        ]}
        /* 카드 제목 */
        title={post.RetweetId
          // 리트윗 게시글이면 게시글 사용자 닉네임님이 리트윗 하셨습니다. 제목 써주기
          ? `${post.User.nickname}님이 리트윗 하셨습니다.`
          // 일반 게시글이면 제목 안 써주기
          : null
        }
         . . .
      > {/* Card 닫기 */}

        {/* ---------- 리트윗 게시글 ---------- */}
        {post.RetweetId && post.Retweet
          ? (
            <Card
              /* ---------- 이미지 : 이미지는 1개 이상 ---------- */
              cover={post.Retweet.Images[0]
              && <PostImages images={post.Retweet.Images} />}
            >
              <Card.Meta
                // 메인 게시글 리트윗한 사용자 닉네임의 첫 번째 글자를
                // 아바타 아이콘으로 표시
                avatar={<Avatar>{post.Retweet.User.nickname[0]}</Avatar>}
                // 메인 게시글 리트윗한 게시글 작성자 이름 
                title={post.Retweet.User.nickname}
                // 메인 게시글 게시글 콘텐츠
                description={<PostCardContent postData={post.Retweet.content} />}
              />
            </Card>
          )
          : (
            /* ---------- (리트윗을 하지않은) 일반 게시글 ---------- */
            <Card.Meta
              // 메인 게시글 사용자 닉네임의 첫 번째 글자를 아바타 아이콘으로 표시
              avatar={<Avatar>{post.User.nickname[0]}</Avatar>}
              // 메인 게시글 작성자 이름 
              title={post.User.nickname}
              // 메인 게시글 콘텐츠
              description={<PostCardContent postData={post.content} />}
            />
          )}
      </Card>

 

back/routes/post.js

// 리트윗 라우터
router.post('/:postId/retweet', isLoggedIn, async (req, res, next) => { // POST /post/동적 히든/retweet
  try {
    /* 존재하지 않는 게시글이 있는지 검사하는 함수 */
    const post = await Post.findOne({
      where: { id: req.params.postId },
      // 모델 가져오기
      include: [{
        model: Post,
        as: 'Retweet', // as: 'Retweet'으로 include를 해주면 post.retweet이 생긴다.
      }],
    });
    
    /* ---------- 만약 존재하지 않는 게시글이 있다면 400번대 에러 출력 ---------- */
    if (!post) {
      return res.status(403).send('존재하지 않는 게시글입니다.');
    }
    
    /* 자기 게시글을 리트윗하기
    or 자기 게시글을 리트윗한 다른 게시글을 다시 자기가 리트윗하기 막기 */
    if (req.user.id === post.UserId
    || (post.Retweet && post.Retweet.UserId === req.user.id)) {
      return res.status(403).send('자신의 글은 리트윗할 수 없습니다.');
    }
    
    // 리트윗할 Id : 리트윗한 게시글이면 리트윗 아이디 사용 or 아니면 게시글 아이디 사용
    const retweetTargetId = post.RetweetId || post.id;
    /* 이미 리트윗한 게시글을 또 리트윗하는지 검사하는 함수(두 번 리트윗 막기) */
    const exPost = await Post.findOne({
      where: {
        UserId: req.user.id,
        RetweetId: retweetTargetId,
      },
    });
    /* ----- 만약 이미 리트윗한 게시글을 또 리트윗한다면 400번대 에러 출력 ----- */
    if (exPost) {
      return res.status(403).send('이미 리트윗했습니다.');
    }

    /* await : 실제로 데이터가 들어감, create : 테이블 안에 데이터를 넣음 */
    const retweet = await Post.create({
      UserId: req.user.id,
      RetweetId: retweetTargetId,
      content: 'retweet',
    // 게시글 모델에서 allowNull을 false로 설정했기 때문에 게시글 콘텐츠가 필수다.
    });

    /* ---------- 내가 어떤 게시글을 리트윗했는지 찾는 함수 ---------- */
    const retweetWithPrevPost = await Post.findOne({
      where: { id: retweet.id },
      // 모델 가져오기
      include: [{
        /* ---------- 리트윗한 게시글 ---------- */
        model: Post,
        as: 'Retweet', // 리트윗한 게시글이 post.Retweet으로 담긴다.
        // 모델 가져오기
        include: [{
          /* ---------- 리트윗한 게시글의 작성자 ---------- */
          model: User,
          attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기
        }, {
          /* ---------- 리트윗한 게시글의 이미지 ---------- */
          model: Image,
        }]
      }, {
        /* ---------- 게시글 작성자 ---------- */
        model: User,
        attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기
      }, {
        /* ---------- 게시글 좋아요 누른 사람들 ---------- */
        model: User,
        as: 'Likers',
        attributes: ['id'], // id 데이터만 가져오기
      }, {
        /* ---------- 게시글 이미지 ---------- */
        model: Image,
      }, {
        /* ---------- 게시글 답글 ---------- */
        model: Comment,
        // 모델 가져오기
        include: [{
          /* ---------- 게시글 답글의 작성자 ---------- */
          model: User,
          attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기
        }],
      }],
    });
    /* 게시글 작성 성공 시 어떤 게시글을 리트윗 했는지에 대한 정보를 프론트로 돌려주기 */
    res.status(201).json(retweetWithPrevPost);

  /* ---------- 에러 캐치 ---------- */
  } catch (error) {
    console.error(error);
    next(error);
  }
});

 

+++ 줄바꿈이 되지 않은 문제를 수정하였습니다.

답변 3

·

답변을 작성해보세요.

0

req.user.id, post.Retweet, post.UserId, exPost 같은 것들 전부를 console.log 해서 체크해보시면 되겠죠?

이가은님의 프로필

이가은

질문자

2024.01.02

콘솔에 찍히지 않습니다! post 라우터 쪽에서 해당하는 위치에 콘솔로그를 했습니다.

      console.log(req.user.id);
      console.log(post.Retweet);
      console.log(post.UserId);
      console.log(exPost);

++

MySQL을 확인해보니 userId는 있지만 RetweetId가 null로 찍혀있었습니다.
리덕스에서 리트윗 버튼을 누르면 RETWEET_REQUEST 요청 액션만 간 것입니다!

imageimage

처음에 리트윗 할 때부터 에러가 있는 겁니다. catch쪽에서 에러 있을텐데요? 네트워크 탭도 봐보세요

이가은님의 프로필

이가은

질문자

2024.01.03

image네트워크 탭에는 문제가 없습니다!
saga catch쪽에는 문제가 없었으나 리트윗 액션 쪽에서 yield를 쓰지 않아 발생한 문제였습니다!

// 리트윗 액션 수정 전
function* watchRetweet() {
  takeLatest(RETWEET_REQUEST, retweet);
}

// 리트윗 액션 수정 후
function* watchRetweet() {
  yield takeLatest(RETWEET_REQUEST, retweet);
}

이제 리트윗 검사가 정상적으로 실행되고, MySQL에서 RetweetId가 정상적으로 숫자로 들어갑니다!
늦은 시간에 질문을 올려서 죄송하고 끝까지 알려주셔서 정말로 감사합니다 제로초님!!

0

줄바꿈 신경좀 써주세요...

이가은님의 프로필

이가은

질문자

2024.01.02

수정하였습니다!

0

인프런 AI 인턴님의 프로필

인프런 AI 인턴

2024.01.02

안녕하세요, 인프런 AI 인턴입니다.

이가은님께서 질문하신 ‘리트윗 라우터에서 설정한 리트윗 검사가 되지 않습니다!‘에 대해 이전에 비슷한 주제로 질문하신 다른 수강생분들의 질문과 강사님의 답변이 도움이 될 것 같습니다.

다음은 리트윗 관련 문제와 해결 방법에 대한 질문과 강사님의 답변이 담긴 게시물 링크입니다. 참고하시면 문제를 해결하는 데 도움이 될 것입니다.

이러한 경우들을 참고하셔서 코드를 다시 검토해 보시는 것도 좋은 접근 방법일 것 같습니다.