• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    미해결

post 추가 시 오류

23.10.30 22:43 작성 조회수 149

0

안녕하세요 제로초님

다름이 아니라 로그인 후 포스트를 추가할 때 에러가 발생하는데

혼자 해결하려고 노력해봤지만 해결을 아직 하지 못하여 질문드립니다.

reducers/post.js

import shortId from "shortid";
import { produce } from "immer";
import faker from "faker";
export const initialValue = {
    mainPosts: [],
    //이미지업로드 할떄 이미지경로들이 여기 들어간다.
    imagePaths: [],

    hasMorePost: true, //true면 가져올 시도를 해라. (스크롤 했을 때)

    loadPostsLoading: false,
    loadPostsDone: false,
    loadPostsError: null,
    //게시글 추가가 완료되었을때 TRue
    addPostLoading: false,
    addPostDone: false,
    addPostError: null,

    removePostLoading: false,
    removePostDone: false,
    removePostError: null,

    addCommentLoading: false,
    addCommentDone: false,
    addCommentError: null,
};
//가짜 데이터

export const generateDummyPost = (number) =>
    Array(number)
        .fill()
        .map(() => ({
            id: shortId.generate(),
            User: {
                id: shortId.generate(),
                nickname: faker.name.findName(),
            },
            content: faker.lorem.paragraph(), //아무 문장,
            Images: [
                {
                    src: "https://image.utoimage.com/preview/cp872722/2022/12/202212008462_500.jpg",
                },
            ],
            Comments: [
                {
                    User: {
                        id: shortId.generate(),
                        nickname: faker.name.findName(),
                    },
                    content: faker.lorem.sentence(),
                },
            ],
        }));

export const LOAD_POSTS_REQUEST = "LOAD_POSTS_REQUEST";
export const LOAD_POSTS_SUCCESS = "LOAD_POSTS_SUCCESS";
export const LOAD_POSTS_FAILURE = "LOAD_POSTS_FAILURE";

export const ADD_POST_REQUEST = "ADD_POST_REQUEST";
export const ADD_POST_SUCCESS = "ADD_POST_SUCCESS";
export const ADD_POST_FAILURE = "ADD_POST_FAILURE";

export const REMOVE_POST_REQUEST = "REMOVE_POST_REQUEST";
export const REMOVE_POST_SUCCESS = "REMOVE_POST_SUCCESS";
export const REMOVE_POST_FAILURE = "REMOVE_POST_FAILURE";

export const ADD_COMMENT_REQUEST = "ADD_COMMENT_REQUEST";
export const ADD_COMMENT_SUCCESS = "ADD_COMMENT_SUCCESS";
export const ADD_COMMENT_FAILURE = "ADD_COMMENT_FAILURE";

export const addPost = (data) => ({
    type: ADD_POST_REQUEST,
    data,
});
export const addComment = (data) => ({
    type: ADD_COMMENT_REQUEST,
    data,
});

//리듀서란 이전 상태를 액션을 통해 다음 상태로 만들어내는 함수(단, 불변성은 지키면서)
//draft는 불변성 상관없이 바꾸면 immer가 알아서 불변성있게 만들어준다.
const reducer = (state = initialValue, action) =>
    produce(state, (draft) => {
        switch (action.type) {
            //게시글 추가
            case ADD_POST_REQUEST:
                draft.addPostLoading = true;
                draft.addPostDone = false;
                draft.addPostError = null;
                break;
            case ADD_POST_SUCCESS:
                draft.addPostLoading = false;
                draft.addPostDone = true;
                draft.mainPosts.unshift(action.data);
                draft.imagePaths = [];
                //unshift란 배열의 맨 앞에다가 추가하는 함수
                break;
            case ADD_POST_FAILURE:
                draft.addPostLoading = false;
                draft.addPostError = action.error;
                break;

            //게시글 불러오기
            case LOAD_POSTS_REQUEST:
                draft.loadPostsLoading = true;
                draft.loadPostsDone = false;
                draft.loadPostsError = null;
                break;
            case LOAD_POSTS_SUCCESS:
                draft.loadPostsLoading = false;
                draft.loadPostsDone = true;
                draft.mainPosts = action.data.concat(draft.mainPosts);
                //concat은 두개 이상의 배열을 합칠 때 사용
                //action.data에는 더미데이터들이 들어있고 draft.mainPosts는 원래 데이터
                draft.hasMorePost = draft.mainPosts.length < 50;
                //50개보다 적으면 불러와야함
                break;
            case LOAD_POSTS_FAILURE:
                draft.loadPostsLoading = false;
                draft.loadPostsError = action.error;
                break;

            //댓글 추가
            case ADD_COMMENT_REQUEST:
                draft.addCommentLoading = true;
                draft.addCommentDone = false;
                draft.addCommentError = null;
                break;

            case ADD_COMMENT_SUCCESS:
                //immer버전 (너무 간단함)
                const post = draft.mainPosts.find(
                    (v) => v.id === action.data.postId
                ); //해당 게시글 찾기
                post.Comments.unshift(dummyComment(action.data.content));
                draft.addCommentLoading = false;
                draft.addCommentDone = true;
                //댓글 넣어주기
                break;

            //immer를 안 쓴 부분
            // const postIndex = state.mainPosts.findIndex(
            //     (v) => v.id === action.data.postId
            // );
            // const post = { ...state.mainPosts[postIndex] };

            // post.Comments = [
            //     dummyComment(action.data.content),
            //     ...post.Comments,
            // ]; //얕은 복사

            // const mainPosts = [...state.mainPosts];
            // mainPosts[postIndex] = post; //댓글 추가하는 부분 너무어려움..
            // //불변성을 지키다 보니 가독성이 너무 안좋음

            // return {
            //     ...state,
            //     mainPosts,
            //     addCommentLoading: false,
            //     addCommentDone: true,
            // };
            case ADD_COMMENT_FAILURE:
                draft.addCommentLoading = false;
                draft.addCommentError = action.error;
                break;

            //게시글 삭제
            case REMOVE_POST_REQUEST:
                draft.removePostDone = false;
                draft.removePostLoading = true;
                draft.removePostError = null;
                break;

            case REMOVE_POST_SUCCESS:
                draft.removePostLoading = false;
                draft.removePostDone = true;
                draft.mainPosts = draft.mainPosts.filter(
                    (v) => v.id !== action.data
                );
                break;
            case REMOVE_POST_FAILURE:
                draft.removePostLoading = false;
                draft.removePostError = action.error;
                break;
            default:
                break;
        }
    });

export default reducer;

여기서 Images안에 아무것도 들어있지 않아서 나타나는 오류같은데 잘 모르겠습니다.

답변 1

답변을 작성해보세요.

0

윤채현님의 프로필

윤채현

질문자

2023.10.30

더미포스트에는 faker가 이미지가 깨져서 제가 임의로 아무 이미지링크나 넣었습니다.

post.Images가 잠깐 동안 undefined인 상황이 있는 것 같습니다. post.Images?.[0] 수정해보세요.

윤채현님의 프로필

윤채현

질문자

2023.10.30

  cover={post.Images?.[0] && <PostImages images={post.Images} />}

이렇게 수정하였는데 이번엔.. id쪽에서 오류가 발생하네요...

윤채현님의 프로필

윤채현

질문자

2023.10.30

제가 만든 dummypost에 id라는 속성이 있는데도 납니다

윤채현님의 프로필

윤채현

질문자

2023.10.30

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

const PostCard = ({ post }) => {
    //pages/index.js에서 mainPosts에서 하나씩 뜯어서 보내줌
    const dispatch = useDispatch();

    const [liked, setLiked] = useState(false); //좋아요
    const [commentFormOpened, setCommentFormOpened] = useState(false);
    //댓글창 열지 말지

    const onToggleLike = useCallback(() => {
        setLiked((prev) => !prev);
    }, []); //좋아요를 한번 더 누르면 좋아요 취소

    const onToggleComment = useCallback(() => {
        setCommentFormOpened((prev) => !prev);
    }, []); //폼 버튼 한번 더 누르면 폼 닫기

    const onRemovePost = useCallback(() => {
        dispatch({
            type: REMOVE_POST_REQUEST,
            data: post.id, //지울 포스트의 id
        });
    }, []);

    const id = useSelector((state) => state.user.me?.id);
    const { removePostloading } = useSelector((state) => state.post);

    return (
        <div style={{ marginBottom: 20 }}>
            <Card
                cover={post.Images?.[0] && <PostImages images={post.Images} />}
                //이미지가 존재한다면 PostImages를 출력
                actions={[
                    //카드 아래에 존재하는 것들
                    <RetweetOutlined key="retweet" />,
                    liked ? (
                        <HeartTwoTone
                            twoToneColor="red"
                            onClick={onToggleLike}
                        />
                    ) : (
                        <HeartOutlined key="heart" onClick={onToggleLike} />
                    ),
                    <MessageOutlined onClick={onToggleComment} key="comment" />,
                    <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 && (
                //commentFormOpened가 true이면 열어라
                <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.object,
        Comment: PropTypes.arrayOf(PropTypes.object),
        Images: PropTypes.arrayOf(PropTypes.object),
    }).isRequired,
};

export default PostCard;

이런식으로 postcard를 작성했습니다.

윤채현님의 프로필

윤채현

질문자

2023.10.30

리덕스를 보니 이런식으로 User와 Image등 다른 속성들이 들어가지 않았네요

새 데이터 추가할 때 에러나는 거라면 addPost 시에 빈 Images랑 User같이 넣으세요.