문의 드립니다. 팔로우 언팔로우 부분을 끝으로 강의해주신 부분을 테스트중에 회원 가입 부분을 실행해보니 아래와 같은 오류가 발생합니다. 런타임
해결된 질문
224
0
회원 가입페이지 자체가 열리지 않습니다.

AppLayout.js
import React from "react";
import PropTypes from 'prop-types';
import Link from 'next/link';
import { Menu, Input, Row, Col } from 'antd';
import styled from 'styled-components';
import { useSelector} from 'react-redux';
import UserProfile from '../components/UserProfile';
import LoginForm from '../components/LoginForm';
const SearchInput = styled(Input.Search)`
vertical-align: middle;
`;
const AppLayout = ({children}) => {
const me = useSelector((state) => state.user.me?.id);
// const inputStyle = useMemo(() => ({ verticalAlign: 'middle' }), []);
return (
<div>
<Menu mode="horizontal">
<Menu.Item>
<Link href="/"><a>노드버드</a></Link>
</Menu.Item>
<Menu.Item>
<Link href="/profile"><a>프로필</a></Link>
</Menu.Item>
<Menu.Item>
<SearchInput enterButton />
</Menu.Item>
<Menu.Item>
<Link href="/signup"><a>회원가입</a></Link>
</Menu.Item>
</Menu>
<Row gutter={8}>
<Col xs={24} md={6}>
{me
? <UserProfile />
: <LoginForm />}
</Col>
<Col xs={24} md={12}>
{children}
</Col>
<Col xs={24} md={6}>
</Col>
</Row>
</div>
);
};
AppLayout.propTypes = {
children: PropTypes.node.isRequired,
};
export default AppLayout;
signup.js
import React, { useCallback, useState, useEffect } from 'react';
import { Form, Input, Checkbox, Button } from 'antd';
import { useDispatch, useSelector } from 'react-redux';
import Router from 'next/router';
import Head from 'next/head';
import styled from 'styled-components';
import { SIGN_UP_REQUEST } from '../reducers/user';
import useInput from '../hooks/useInput';
import AppLayout from '../components/AppLayout';
const ErrorMessage = styled.div`
color: red;
`;
const Signup = () => {
const dispatch = useDispatch();
const [ signUpLoading, me, signUpDone ] = useSelector((state) => state.user);
const [email, onChangeEmail] = useInput('');
const [nickname, onChangeNickname] = useInput('');
const [password, onChangePassword] = useInput('');
const [passwordCheck, setPasswordCheck] = useState('');
const [passwordError, setPasswordError] = useState(false);
const [term, setTerm] = useState(false);
const [termError, setTermError] = useState(false);
useEffect(() => {
if (me) {
alert('로그인했으니 메인페이지로 이동합니다.');
Router.push('/');
}
}, [me && me.id]);
useEffect(() => {
if (signUpDone) {
Router.replace('/');
}
}, [signUpDone]);
const onSubmit = useCallback(() => {
if (password !== passwordCheck) {
return setPasswordError(true);
}
if (!term) {
return setTermError(true)
}
console.log(email, nickname, password )
return dispatch({
type: SIGN_UP_REQUEST,
data: {
email,
password,
nickname
},
});
}, [email, password, passwordCheck, term])
const onChangePasswordCheck = useCallback((e) => {
setPasswordCheck(e.target.value);
setPasswordError(e.target.value !== password);
}, [password]);
const onChangeTerm = useCallback((e) => {
setTerm(e.target.checked);
setTermError(false);
}, []);
return (
<AppLayout>
<Head>
<meta charSet="utf-8" />
<title>회원가입 | NodeBird</title>
</Head>
<Form onFinish={onSubmit} style={{ padding: 10 }}>
<div>
<label htmlFor="user-email">이메일</label>
<br />
<Input name="user-email" type="email" value={email} required onChange={onChangeEmail} />
</div>
<div>
<label htmlFor="user-nickname">닉네임</label>
<br />
<Input name="user-nickname" value={nickname} required onChange={onChangeNickname} />
</div>
<div>
<label htmlFor="user-password">비밀번호</label>
<br />
<Input name="user-password" type="password" value={password} required onChange={onChangePassword} />
</div>
<div>
<label htmlFor="user-password-check">비밀번호 체크</label>
<br />
<Input
name="user-password-check"
type="password"
value={passwordCheck}
required
onChange={onChangePasswordCheck}
/>
{passwordError &&
<ErrorMessage style={{ color: 'red'}}>
비밀번호가 일치하지 않습니다.
</ErrorMessage>
}
</div>
<div>
<Checkbox
name="user-term"
checked={term}
onChange={onChangeTerm}>
제로초 말을 잘 들을 것을 동의합니다.
</Checkbox>
{termError &&
<ErrorMessage style={{ color: 'red' }}>
약관에 동의하셔야 합니다.
</ErrorMessage>
}
</div>
<div style={{ marginTop: 10 }}>
<Button type="primary" htmlType="submit" loading={signUpLoading}>가입하기</Button>
</div>
</Form>
</AppLayout>
);
};
export default Signup;
reducers/user.js
import produce from 'immer';
export const initialState = {
logInLoading: false, // 팔로우 시도중.. 로딩창을 띄운다.
logInDone: false,
logInError: null,
logOutLoading: false, // 언팔로우 시도중.. 로딩창을 띄운다.
logOutDone: false,
logOutError: null,
followLoading: false, // 로그인 시도중.. 로딩창을 띄운다.
followDone: false,
followError: null,
unfollowLoading: false, // 로그인 시도중.. 로딩창을 띄운다.
unfollowDone: false,
unfollowError: null,
signUpLoading: false, // 회원가입 시도중.. 로딩창을 띄운다.
signUpDone: false,
signUpError: null,
changeNicknameLoading: false, // 닉네임 변경 시도중.. 로딩창을 띄운다.
changeNicknameDone: false,
changeNicknameError: null,
isLoggingIn: false,
isLoggedIn: false,
isLoggingOut: false,
me: null,
signUpData: {},
loginData: {},
};
export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';
export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';
export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
export const SIGN_UP_SUCCESS = 'SIGN_UP_SUCCESS';
export const SIGN_UP_FAILURE = 'SIGN_UP_FAILURE';
export const CHANGE_NICKNAME_REQUEST = 'CHANGE_NICKNAME_REQUEST';
export const CHANGE_NICKNAME_SUCCESS = 'CHANGE_NICKNAME_SUCCESS';
export const CHANGE_NICKNAME_FAILURE = 'CHANGE_NICKNAME_FAILURE';
export const FOLLOW_REQUEST = 'FOLLOW_REQUEST';
export const FOLLOW_SUCCESS = 'FOLLOW_SUCCESS';
export const FOLLOW_FAILURE = 'FOLLOW_FAILURE';
export const UNFOLLOW_REQUEST = 'UNFOLLOW_REQUEST';
export const UNFOLLOW_SUCCESS = 'UNFOLLOW_SUCCESS';
export const UNFOLLOW_FAILURE = 'UNFOLLOW_FAILURE';
export const ADD_POST_TO_ME = 'ADD_POST_TO_ME';
export const REMOVE_POST_OF_ME = 'REMOVE_POST_OF_ME';
export const ADD_COMMENT_TO_ME = 'ADD_COMMENT_TO_ME';
export const REMOVE_COMMENT_OF_ME = 'REMOVE_COMMENT_OF_ME';
const dummyUser = (data) => ({
...data,
nickname: '제로초',
id: 1,
// sequlize 에서 합쳐주는 데이터라서 앞글자가 대문자 이다.
Posts: [{ id: 1 }], // 내가 쓴 게시글
Followings: [{nickname: '부기초' }, { nickname: 'Chanho Lee' }, { nickname: 'neue zeal'}], // 내 팔로윙들...
Followers: [{nickname: '부기초' }, { nickname: 'Chanho Lee' }, { nickname: 'neue zeal'}], // 내 팔로워들...
});
export const loginAction = (data) => {
return {
type: LOG_IN_REQUEST,
data,
};
};
export const logoutAction = () => {
return {
type: LOG_OUT_REQUEST,
};
};
export const changeNickname = () => {
return {
type: CHANGE_NICKNAME_REQUEST,
};
};
// return 이 생략된다.
// const reducer = (state = initialState, action) => return produce (state, (draft) => {
const reducer = (state = initialState, action) => produce (state, (draft) => {
switch (action.type) {
case FOLLOW_REQUEST:
draft.followLoading = true;
draft.followError = null;
draft.followDone = false;
break;
case FOLLOW_SUCCESS:
draft.followLoading = false;
draft.me.Followings.push({ id: action.data });
draft.followDone = true;
// me: action.data, => me: { ...action.data, nicname: 'zerocho' }, => dummyUser{ action.data },
break;
case FOLLOW_FAILURE:
draft.followLoading = true; // isLoggingOut: true, => logOutLoading: true,
draft.followError = action.error; // logInError: action.error, => logOutDone: false,
break;
case UNFOLLOW_REQUEST:
draft.unfollowLoading = true;
draft.unfollowError = null;
draft.unfollowDone = false;
break;
case UNFOLLOW_SUCCESS:
draft.unfollowLoading = false;
draft.unfollowDone = true;
draft.me.Followings = draft.me.Followings.filter((v) => v.id !== action.data);
// me: action.data, => me: { ...action.data, nicname: 'zerocho' }, => dummyUser{ action.data },
break;
case UNFOLLOW_FAILURE:
draft.unfollowLoading = true; // isLoggingOut: true, => logOutLoading: true,
draft.unfollowError = action.error; // logInError: action.error, => logOutDone: false,
break;
case CHANGE_NICKNAME_REQUEST:
draft.changeNicknameLoading = true; // 닉네임 변경 시도중이니까 => 버튼 로딩 O
draft.changeNicknameDone = false; // 닉네임 변경중
draft.changeNicknameError = null;
break;
case CHANGE_NICKNAME_SUCCESS:
draft.changeNicknameLoading = false; // 닉네임 변경 요청이 성공했으니까 => 버튼 로딩 X
draft.changeNicknameDone = true; // 닉네임 변경 완료
break;
case CHANGE_NICKNAME_FAILURE:
draft.changeNicknameLoading = false; // 닉네임 변경 요청이 끝났으니까 => 버튼 로딩 X
draft.changeNicknameError = action.error;
break;
case SIGN_UP_REQUEST:
draft.signUpLoading = true; // 회원가입 시도중이니까 => true isLoggingOut: true, => logInLoading: true,
draft.signUpDone = false;
draft.signUpError = null;
break;
case SIGN_UP_SUCCESS:
draft.signUpLoading = false; // 회원가입 요청이 성공했으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.signUpDone = true; // isLoggedIn: false, => logOutDone: true,
break;
case SIGN_UP_FAILURE:
draft.signUpLoading = false; // 회원가입 요청이 끝났으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.signUpError = action.error; // 추가
break;
case LOG_IN_REQUEST:
draft.logInLoading = true;
draft.logInError = null;
draft.logInDone = false;
break;
case LOG_IN_SUCCESS:
draft.logInLoading = false;
draft.logInDone = true;
draft.me = dummyUser(action.data);
// me: action.data, => me: { ...action.data, nicname: 'zerocho' }, => dummyUser{ action.data },
break;
case LOG_IN_FAILURE:
draft.logInLoading = true; // isLoggingOut: true, => logOutLoading: true,
draft.logInError = action.error; // logInError: action.error, => logOutDone: false,
break;
case LOG_OUT_REQUEST:
draft.logOutLoading = true; // 로그아웃 시도중이니까 => true
draft.logOutDone = false;
draft.logOutError = null;
break;
case LOG_OUT_SUCCESS:
draft.logOutLoading = false; // 로그아웃 요청이 성공했으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.logOutDone = true; // isLoggedIn: false, => logOutDone: true,
draft.me = null;
break;
case LOG_OUT_FAILURE:
draft.isLoggingOut = false; // 요청이 끝났으니까 => false isLoggingOut: false, => logOutLoading: false,
draft.logOutError = action.error; // 추가
break;
case ADD_POST_TO_ME:
draft.me.Posts.unshift({ id: action.data });
break;
// me: {
// ...state.me,
// Posts: [{ id: action.data }, ...state.me.Posts],
// },
// };
case REMOVE_POST_OF_ME:
draft.me.Posts = draft.me.Posts.filter((v) => v.id !== action.data);
break;
// return {
// ...state,
// me: {
// ...state.me,
// Posts: state.me.Posts.filter((v) => v.id !== action.data),
// },
// };
// case ADD_COMMENT_TO_ME:
// break;
// me: {
// ...state.me,
// Posts: [{ id: action.data }, ...state.me.Posts],
// },
// };
// case REMOVE_COMMENT_OF_ME:
// break;
// me: {
// ...state.me,
// Posts: state.me.Posts.Comments.filter((v) => v.id !== action.data),
// },
// };
default:
break;
// default:
// return state;
// default: {
// return {
// ...state,
// };
// }
}
});
export default reducer;
sagas/user.js
// all fork call put delay debounce throttle takeLatest tabkeEvery takeLeding taekMaybe
import { all, put, fork, takeLatest, delay } from 'redux-saga/effects';
import axios from 'axios';
import {
LOG_IN_REQUEST,
LOG_IN_SUCCESS,
LOG_IN_FAILURE,
LOG_OUT_REQUEST,
LOG_OUT_SUCCESS,
LOG_OUT_FAILURE,
SIGN_UP_REQUEST,
SIGN_UP_SUCCESS,
SIGN_UP_FAILURE,
FOLLOW_REQUEST,
FOLLOW_SUCCESS,
FOLLOW_FAILURE,
UNFOLLOW_REQUEST,
UNFOLLOW_SUCCESS,
UNFOLLOW_FAILURE,
} from '../reducers/user';
function followAPI(data) {
return axios.post('/api/follow', data);
}
function* follow(action) {
try {
// yield put({
// type: 'FOLLOW_REQUEST',
// });
// const result = yield call(followAPI, action.data);
// ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
yield delay(1000);
// throw new Error('') // 아래 catch 로 넘어간다.
yield put({
type: FOLLOW_SUCCESS,
data: action.data,
});
} catch (err) {
yield put({ // put => dispatch 다.
type: FOLLOW_FAILURE,
data: err.response.data,
});
}
}
function unfollowAPI(data) {
return axios.post('/api/unfollow', data);
}
function* unfollow(action) {
try {
// yield put({
// type: 'UNFOLLOW_REQUEST',
// });
// const result = yield call(unfollowAPI, action.data);
// ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
yield delay(1000);
// throw new Error('') // 아래 catch 로 넘어간다.
yield put({
type: UNFOLLOW_SUCCESS,
data: action.data,
});
} catch (err) {
yield put({ // put => dispatch 다.
type: UNFOLLOW_FAILURE,
data: err.response.data,
});
}
}
function signUpAPI(data) {
return axios.post('/api/logout', data);
}
function* signUp(action) {
try {
// yield put({
// type: 'LOG_IN_REQUEST',
// });
// const result = yield call(signUpAPI, action.data);
// ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
yield delay(1000);
// throw new Error('') // 아래 catch 로 넘어간다.
yield put({
type: SIGN_UP_SUCCESS,
data: action.data,
});
} catch (err) {
yield put({ // put => dispatch 다.
type: SIGN_UP_FAILURE,
data: err.response.data,
});
}
}
function logOutAPI() {
return axios.post('/api/logout');
}
function* logOut() {
try {
// yield put({
// type: 'LOG_OUT_REQUEST',
// });
// const result = yield call(logOutAPI);
yield delay(1000);
yield put({
type: LOG_OUT_SUCCESS,
});
} catch (err) {
yield put({ // put => dispatch 다.
type: LOG_OUT_FAILURE,
error: err.response.data,
});
}
}
function* logIn(action) {
try {
console.log('saga logIn');
// yield put({
// type: 'LOG_IN_REQUEST',
// });
// const result = yield call(logInAPI, action.data);
// ex> const result = yield call(logInAPI, action.data, a, b, c); a, b, c 인자를 추가시
yield delay(1000);
yield put({
type: LOG_IN_SUCCESS,
data: action.data,
});
} catch (err) {
yield put({ // put => dispatch 다.
type: LOG_IN_FAILURE,
error: err.response.data,
});
}
}
{/*
takeLeading - 1번째 클릭만 실행
takeLatest - 마지막 클릭만 실행 ( [프론트 서버에서만] 먼저 실행한 부분은 놔두고 동시에 여러게가 실행된 경우만 마지막 이벤트만을 실행한다. )
takeEvery - 1번 사용하면 사라지는 take 대신... while 문(반복문)을 사용하지 않고도.. 계속 재사용할수 있도록 해준다.
*/}
function* watchFollow() {
yield takeLatest(FOLLOW_REQUEST, follow); // take =>logIn 액션이 실행될때까지 기다리겠다.
}
function* watchUnFollow() {
yield takeLatest(UNFOLLOW_REQUEST, unfollow); // take =>logIn 액션이 실행될때까지 기다리겠다.
}
function* watchLogIn() {
yield takeLatest(LOG_IN_REQUEST, logIn); // take =>logIn 액션이 실행될때까지 기다리겠다.
}
function* watchLogOut() {
yield takeLatest(LOG_OUT_REQUEST, logOut);
}
function* watchSignUp() {
yield takeLatest(SIGN_UP_REQUEST, signUp); // take =>logIn 액션이 실행될때까지 기다리겠다.
}
{/*
all 은 배열을 받아서 배열안에 있는 명령들을 한번에 실행 시켜준다.
fork 함수를 실행해준다. call도 있다.
fork[비동기] - 결과를 기다리지 않고 다음 명령 실행을 진행한다. [논블로킹]
call[동기] - 응답을 기다렸다가 응답을 받고 다음 명령을 실행 [블로킹]
*/}
export default function* userSaga() {
yield all([
fork(watchFollow),
fork(watchUnFollow),
fork(watchLogIn),
fork(watchLogOut),
fork(watchSignUp),
]);
}
답변 5
0
오타였네요.. ㅠ.ㅜ 이거 찾는데 반나절 걸린것 같습니다.
> 오타
const [ signUpLoading, me, signUpDone ] = useSelector((state) => state.user);
> 정상
const { signUpLoading, me, signUpDone } = useSelector((state) => state.user);
0
useInput은 문제가 없네요. 제가 말씀드린대로 콘솔에서 어디서 에러난건지 확인해보세요.
0
useInput.js 입니다..
import { useState, useCallback } from 'react';
export default (initValue = null) => {
const [value, setValue] = useState(initValue);
const handler = useCallback((e) => {
setValue(e.target.value);
}, []);
return [value, handler, setValue];
};
0
signup.js에서 19줄 46칸에 에러가 있다고 뜨는데요.
500번대 에러면 브라우저를 보시는 게 아니라 프론트서버쪽 로그도 같이 보셔야 합니다.
useInput은 어떻게 되어있나요(hooks/useInput)
콘솔창에서 uncaught 하고 에러 뜨는데 signup 34:43 이렇게 뜨는데 거기 눌러보시면 어떤 부분에서 에러난건지도 뜹니다.
넥스트 버젼 질문
로그인시 401 Unauthorized 오류가 뜹니다
무한 스크롤 중 스크롤 튐 현상
특정 페이지 접근을 막고 싶을 때
createGlobalStyle의 위치와 영향범위
인라인 스타일 리렌더링 관련
vsc 에서 npm init 설치시 오류
nextjs 15버전 사용 가능할까요?
화면 새로고침 문의
RTK에서 draft, state 차이가 있나요?
Next 14 사용해도 될까요?
next, node 버전 / 폴더 구조 질문 드립니다.
url 오류 질문있습니다
ssh xxxxx로 우분투에 들어가려니까 port 22: Connection timed out
sudo certbot --nginx 에러
Minified React error 콘솔에러 (hydrate)
카카오 공유했을 때 이전에 작성했던 글이 나오는 버그
프론트서버 배포 후 EADDRINUSE에러 발생
npm run build 에러
front 서버 npm run build 중에 발생한 에러들
서버 실행하고 브라우저로 들어갔을때 404에러
css 서버사이드 랜더링이 적용되지 않아서 문의 드립니다.
팔로워 3명씩 불러오고 데이터 합쳐주는걸로 바꾸고 서버요청을 무한으로하고있습니다.
해시태그 검색에서 throttle에 관해 질문있습니다.