• 카테고리

    질문 & 답변
  • 세부 분야

    풀스택

  • 해결 여부

    해결됨

"더미데이터와 포스트폼 만들기" 강의순서 관련 코드 작성 뒤, 로그인에서 게시판으로 넘어가지 않는 문제로 질문 드립니다!

22.04.06 17:12 작성 조회수 391

0

안녕하세요. 제로초님의 강의에 큰 도움을 받고 있음에 감사의 말씀을 드립니다.

여러 검색결과도, 질문답변도, 해당강의 반복재생으로 찾아보았으나 해결치 못한 부분이 있어 이렇게 직접 질문을 하게 됐네요!

 

[ 마주한  문제점 & 상황 ] 은 다음과 같습니다.

1. (이전상황) 로그인폼(Id, Pwd)에 텍스트를 입력해 "로그인" 버튼을 누르면 정상적으로 로그인이 됐고, Redux 역시 정상작동해 개발자도구에서 확인이 가능했습니다.

2. (현재상황) 섹션3(Redux) "더미데이터와 포스트폼 만들기" 강의를 들으며 문제에 마주했습니다.

3. (더미데이터와 포스트폼 만들기 강의 후) 잘 넘어가던 로그인 화면에서 데이터는 넘어가 콘솔에 찍히지만, 화면 페이지는 게시판으로 넘어가지 않는 상황에 직면했습니다. 

 

[ 콘솔에 찍히는 Error ] 는 다음과 같습니다.

 

 

 

[정상적으로 넘어가는 로그인 데이터] -> 그러나 로그인이 되지 않고 화면이 넘어가질 않습니다!

 

 

[ 소스코드 ]

1. package.json 

{
  "name": "react-nodebird-front",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "next -p 3060",
    "build": "next build"
  },
  "author": "jj",
  "license": "ISC",
  "dependencies": {
    "@ant-design/icons": "^4.7.0",
    "antd": "^4.19.0",
    "next": "^9.5.5",
    "next-redux-wrapper": "^6.0.2",
    "prop-types": "^15.8.1",
    "react": "^16.14.0",
    "react-dom": "^16.14.0",
    "react-redux": "^7.2.8",
    "redux": "^4.1.2",
    "redux-devtools-extension": "^2.13.9",
    "styled-components": "^5.3.3"
  },
  "devDependencies": {
    "eslint": "^8.10.0",
    "eslint-plugin-hooks": "^0.4.2",
    "eslint-plugin-import": "^2.25.4",
    "eslint-plugin-react": "^7.29.3"
  }
}

2. LoginForm.js 파일

import React, { useState, useCallback, useMemo } from 'react';
import { Form, Input, Button } from 'antd';
import Link from 'next/link';
import styled from 'styled-components';
import { useDispatch, useSelector } from 'react-redux';
import useInput from "../hooks/useInput";
import { loginAction } from '../reducers/user';
 
const ButtonWrapper = styled.div
`
    margin-top: 10px;
`;
 
const FormWrapper = styled(Form)
`
    padding: 10px;
`;

const LoginForm = ({ setIsLoggedIn }) => {
    const dispatch = useDispatch();
    const [id, onChangeId] = useInput('');
    const [password, onChangePassword] = useInput('');
    const onSubmitForm = useCallback(() => {
        console.log(id, password);
        dispatch(loginAction({id, password}));
    }, [id, password]);
 
    return (
        <FormWrapper onFinish={onSubmitForm}>
            <div>
                <label htmlFor="user-id">아이디</label>
                <br />
                <Input name="user-id" value={id} onChange={onChangeId} required />
            </div>
            <div>
                <label htmlFor="user-password">비밀번호</label>
                <br />
                <Input
                    name="user-password"
                    type="password"
                    value={password}
                    onChange={onChangePassword}
                    required />
            </div>
            <ButtonWrapper>
                <Button type="primary" htmlType="submit" loading={false}>로그인</Button>
                <Link href="/signup"><a><Button>회원가입</Button></a></Link>
            </ButtonWrapper>
        </FormWrapper>
    );
}

export default LoginForm;

3. index.js

import React from 'react';
import { useSelector } from "react-redux";

import AppLayout from "../components/AppLayout";
import PostForm from '../components/PostForm';
import PostCard from '../components/PostCard';

const Home = () => {
    const { isLoggedIn } = useSelector((state) => state.user);
    const { mainPosts } = useSelector((state) => state.post);

    return (
        <AppLayout>
            {isLoggedIn && <PostForm /> }
            {mainPosts.map((post) => <PostCard key={post.id} post={post} />)}
        </AppLayout>
    );
}

export default Home;

4. configureStore.js

import { createWrapper } from 'next-redux-wrapper';
import { applyMiddleware, compose, createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';

import reducer from '../reducers';

const configureStore = () => {
    const middlewares = [];
    const enhancer = process.env.NODE_ENV === 'production'
        ? compose(applyMiddleware(...middlewares))
        : composeWithDevTools(applyMiddleware(...middlewares));
    const store = createStore(reducer, enhancer);
    return store;
};

const wrapper = createWrapper(configureStore, {
    debug: process.env.NODE_ENV === 'development',
});

export default wrapper;

5. post.js

// [ initialState ]
export const initialState = {
    mainPosts: [{
        id: 1,
        User: {
            id: 1,
            nickname: 'jj',
        },
        content: '첫 번째 게시글 #해시태그 #익스프레스',
        Images: [{
            src: 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAxNjExMjFfMjM2%2FMDAxNDc5NjY5MjEyOTEz.13R8uiaA0T8rJnLPJICAib4oVtrAzA424jbDMC9a3ckg.m0QoHt-5MdR0MH501npcL8aJof3Eu1h_9Zp0ceNm8e4g.PNG.guri4you%2F%25BB%25F6%25B1%25F2_%25BD%25C9%25B8%25AE%25C5%25D7%25BD%25BA%25C6%25AE1.png&type=sc960_832',
        }, {
            src: 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAxNjExMjFfMjM2%2FMDAxNDc5NjY5MjEyOTEz.13R8uiaA0T8rJnLPJICAib4oVtrAzA424jbDMC9a3ckg.m0QoHt-5MdR0MH501npcL8aJof3Eu1h_9Zp0ceNm8e4g.PNG.guri4you%2F%25BB%25F6%25B1%25F2_%25BD%25C9%25B8%25AE%25C5%25D7%25BD%25BA%25C6%25AE1.png&type=sc960_832',
        }, {
            src: 'https://search.pstatic.net/common/?src=http%3A%2F%2Fblogfiles.naver.net%2FMjAxNjExMjFfMjM2%2FMDAxNDc5NjY5MjEyOTEz.13R8uiaA0T8rJnLPJICAib4oVtrAzA424jbDMC9a3ckg.m0QoHt-5MdR0MH501npcL8aJof3Eu1h_9Zp0ceNm8e4g.PNG.guri4you%2F%25BB%25F6%25B1%25F2_%25BD%25C9%25B8%25AE%25C5%25D7%25BD%25BA%25C6%25AE1.png&type=sc960_832',
        }],
        Comments: [{
            User: {
                nickname: 'jj4',
            },
            content: 'redux를 학습 중입니다.',
        }, {
            User: {
                nickname: 'jj3',
            },
            content: '다음은 sage를 학습 예정임.',
        }]
    }],
    imagePaths: [],
    postAdded: false,
}
 
const ADD_POST = 'ADD_POST';
export const addPost = {
    type: ADD_POST,
}
const dummyPost = {
    id: 2,
    content: '더미데이터입니다.',
    User: {
        id: 1,
        nickname: 'jj1',
    },
    Images: [],
    Comments: [],
};

 
const reducer = (state = initialState, action) => {
    switch (action.type) {
        case ADD_POST:
            return {
                ...state,
                mainPosts: [dummyPost, ...state.mainPosts],
                postAdded: true,
            };
        default:
            return state;
    }
};

export default reducer;

6. PostForm.js

import React, { useCallback, useState, useRef } from 'react';
import { Form, Input, Button } from 'antd';
import { useSelector, useDispatch } from 'react-redux';
import { addPost } from '../reducers/post';

const PostForm = () => {
    const { imagePaths } = useSelector((state) => state.post);
    const dispatch = useDispatch();
    const imageInput = useRef();
    const [text, setText] = useState('');

    const onChangeText = useCallback((e) => {
        setText(e.target.value);
    }, []);

    const onSubmit = useCallback(() => {
        dispatch(addPost);
        setText('');
    }, []);

    const onClickImageUpload = useCallback(() => {
        imageInput.current.click();
    }, [imageInput.current]);

    return (
        <Form style={{ margin: '10px 0 20px' }} encType="multipart/form-data" onFinish={onSubmit}>
            <Input.TextArea
                value={text}
                onChange={onChangeText}
                maxLength={140}
                placeholder="어떤 신기한 일이 있었나요?"
            />
            <div>
                <input type="file" multiple hidden ref={imageInput}/>
                <Button onClick={onClickImageUpload}>이미지 업로드</Button>
                <Button type="primary" style={{ float: 'right' }} htmlFor="submit">짹짹</Button>
            </div>
            <div>
                {imagePaths.map((v) => (
                    <div key={v} style={{ display: 'inline-block'}}>
                        <img src={v} style={{ width: '200px' }} alt={v} />
                        <div>
                            <Button>제거</Button>
                        </div>
                    </div>
                ))}
            </div>
        </Form>
    )
};

export default PostForm;

7. PostCard.js

import React from 'react';

const PostCard = () => {
    return (
        <div>
            PostCard
        </div>
    );
};

export default PostCard;

8. user.js

import { HYDRATE } from 'next-redux-wrapper';

// initialState 부분
const initialState = {
    user: {
        isLoggedIn: false,
        user: null,
        signUpData: {},
        loginData: {},
    },
    post: {
        mainPosts: [],
    }
};

// [로그인] action creator
export const loginAction = (data) => {
    return {
        type: 'LOG_IN',
        data,
    }
};

// [로그아웃] action creator
export const logoutAction = (data) => {
    return {
        type: 'LOG_OUT',
    }
};

// reducer
const rootReducer = (state = initialState, action) => {
    switch (action.type) {
        case HYDRATE:
            console.log('HYDRATE', action);
            return { ...state, ...action.payload };
        case 'LOG_IN':
            return {
                ...state,
                user: {
                    ...state.user,
                    isLoggedIn: true,
                    user: action.data,
                },
            };
        case 'LOG_OUT':
            return {
                ...state,
                user: {
                    ...state.user,
                    isLoggedIn: false,
                    user: null,
                },
            };
        default:
            return state;
    }
};

export default rootReducer;

 

다소 많은 양의 글 내용이지만, 시간이 허락되실 때 답변 주시면, 참 감사할 것 같습니다!

오늘 하루도 고생많으셨고, 이번 한 주도 파이팅입니다!

늘 양질의 강의 감사합니다 ^^

 

답변 1

답변을 작성해보세요.

1

8. user.js가 이상합니다.

해당 HYDRATE 코드는 reducers/index.js에 적용하셔야 합니다. 또한 LogIn, LogOut 관련 return도 바꿔야하고요.

리덕스 데브툴즈 보시면 user안에 user안에 isLoggedIn이 있습니다.

제 깃헙 참고하세요.