inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

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

리트윗하기

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

해결된 질문

281

이가은

작성한 질문수 20

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);
  }
});

 

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

react redux node.js express next.js

답변 3

0

제로초(조현영)

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

0

이가은

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

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

++

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

imageimage

1

제로초(조현영)

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

0

이가은

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

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

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

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

0

제로초(조현영)

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

0

이가은

수정하였습니다!

0

인프런 AI 인턴

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

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

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

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

넥스트 버젼 질문

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