묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
user/[id].js => Unhandled Runtime Error TypeError: Cannot read property 'id' of undefined 에러가 발생합니다.
페이지 로딩은 정상적으로 되는데 스크롤시 에러가 발생합니다. <Head> 태그 url 부분을 약간 다르게 수정해 주었고 다른 부분은 이상이 없어 보입니다. 로그를 찍어 봤는데.. 값이 나오는데 값을 찾을수 없다...???? user/[id].js const User = () => { const dispatch = useDispatch(); const router = useRouter(); const { id } = router.query; <==== 이부분에서 id 값은 잘 받아 오는것 같습니다. console.log(router.query); <==== 로그를 찍어보니 {id: "1"} 로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과 값도 동일하게 string 인데.. 정상적으로 스크롤 됩니다. const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post); const { userInfo } = useSelector((state) => state.user); FRONT user/[id]/.js // user/[id].js import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { Avatar, Card } from 'antd'; import { END } from 'redux-saga'; import Head from 'next/head'; import { useRouter } from 'next/router'; import axios from 'axios'; import { LOAD_USER_POSTS_REQUEST } from '../../reducers/post'; import { LOAD_MY_INFO_REQUEST, LOAD_USER_REQUEST } from '../../reducers/user'; import PostCard from '../../components/PostCard'; import wrapper from '../../store/configureStore'; import AppLayout from '../../components/AppLayout'; import { backUrl } from '../../config/config'; // 프론트서버와 브라우저 모두에서 실행 const User = () => { const dispatch = useDispatch(); const router = useRouter(); const { id } = router.query; <==== 이부분에서 id 값은 잘 받아 오는것 같습니다. console.log(router.query); <==== 로그를 찍어보니 {id: "1"} 로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과도 동일한데.. 정상적으로 스크롤 됩니다. const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post); const { userInfo } = useSelector((state) => state.user); useEffect(() => { function onScroll() { if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) { if (hasMorePosts && !loadPostsLoading) { dispatch({ type: LOAD_USER_POSTS_REQUEST, lastId: mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.lenth - 1].id, <====== 에러가 발생하는 부분 data: id, }); } } } window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); }; }, [ mainPosts.length, hasMorePosts, id]); return ( <AppLayout> <Head> <title> {userInfo.nickname} 님의 글 </title> <meta name="description" content={`${userInfo.nickname}님의 게시글`} /> <meta property="og:title" content={`${userInfo.nickname}님의 게시글`} /> <meta property="og:description" content={`${userInfo.nickname}님의 게시글`} /> <meta property="og:image" content={`${backUrl}/favicon.ico`} /> <meta property="og:url" content={`${backUrl}/user/${id}`} /> </Head> {userInfo ? ( <Card actions={[ <div key="twit"> 짹짹 <br /> {userInfo.Posts} </div>, <div key="following"> 팔로잉 <br /> {userInfo.Followings} </div>, <div key="follower"> 팔로워 <br /> {userInfo.Followers} </div>, ]} > <Card.Meta avatar={<Avatar>{userInfo.nickname[0]}</Avatar>} title={userInfo.nickname} /> </Card> ) : null} {mainPosts.map((c) => ( <PostCard key={c.id} post={c} /> ))} </AppLayout> ); }; // front server 에서 실행 export const getServerSideProps = wrapper.getServerSideProps(async (context) => { const cookie = context.req ? context.req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; if (context.req && cookie) { axios.defaults.headers.Cookie = cookie; } context.store.dispatch({ type: LOAD_USER_POSTS_REQUEST, data: context.params.id, }); context.store.dispatch({ type: LOAD_MY_INFO_REQUEST, }); context.store.dispatch({ type: LOAD_USER_REQUEST, data: context.params.id, }); context.store.dispatch(END); await context.store.sagaTask.toPromise(); // return { props: {} }; }); export default User; config/config.js const domainUrl = 'https://api.nodebird.com'; const localhostUrl = 'http://localhost:3065'; const backUrl = process.env.NODE_ENV === 'production' ? 'https://api.nodebird.com' : 'http://localhost:3065'; export { backUrl, domainUrl, localhostUrl }; BACK sagas/user.js // all fork call put delay debounce throttle takeLatest tabkeEvery takeLeding taekMaybe import { all, fork, put, takeLatest, throttle, call } from 'redux-saga/effects'; import axios from 'axios'; // import shortId from 'shortid'; import { LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE, LOAD_USER_POSTS_REQUEST, LOAD_USER_POSTS_SUCCESS, LOAD_USER_POSTS_FAILURE, LOAD_HASHTAG_POSTS_REQUEST, LOAD_HASHTAG_POSTS_SUCCESS, LOAD_HASHTAG_POSTS_FAILURE, LOAD_POST_REQUEST, LOAD_POST_SUCCESS, LOAD_POST_FAILURE, LIKE_POST_REQUEST, LIKE_POST_SUCCESS, LIKE_POST_FAILURE, UNLIKE_POST_REQUEST, UNLIKE_POST_SUCCESS, UNLIKE_POST_FAILURE, ADD_POST_REQUEST, ADD_POST_SUCCESS, ADD_POST_FAILURE, ADD_COMMENT_REQUEST, ADD_COMMENT_SUCCESS, ADD_COMMENT_FAILURE, REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS, REMOVE_POST_FAILURE, UPLOAD_IMAGES_REQUEST, UPLOAD_IMAGES_SUCCESS, UPLOAD_IMAGES_FAILURE, RETWEET_REQUEST, RETWEET_SUCCESS, RETWEET_FAILURE, } from '../reducers/post'; import { ADD_POST_TO_ME, REMOVE_POST_OF_ME, } from '../reducers/user'; function likePostAPI(data) { return axios.patch(`/post/${data}/like`); }; function* likePost(action) { try { const result = yield call(likePostAPI, action.data); yield put({ type: LIKE_POST_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LIKE_POST_FAILURE, error: err.response.data, }); } } function unlikePostAPI(data) { return axios.delete(`/post/${data}/like`); }; function* unlikePost(action) { try { const result = yield call(unlikePostAPI, action.data); yield put({ type: UNLIKE_POST_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: UNLIKE_POST_FAILURE, error: err.response.data, }); } } function loadPostsAPI(lastId) { return axios.get(`/posts?lastId=${lastId || 0}`); }; function* loadPosts(action) { try { const result = yield call(loadPostsAPI, action.lastId); yield put({ type: LOAD_POSTS_SUCCESS, data: result.data, // data: generateDummyPost(10), }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LOAD_POSTS_FAILURE, error: err.response.data, }); } } function loadPostAPI(data) { return axios.get(`/post/${data}`); }; function* loadPost(action) { try { const result = yield call(loadPostAPI, action.data); yield put({ type: LOAD_POST_SUCCESS, data: result.data, // data: generateDummyPost(10), }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LOAD_POST_FAILURE, error: err.response.data, }); } } function addCommentAPI(data) { return axios.post(`/post/${data.postId}/comment`, data); // POST /post/1/comment }; function* addComment(action) { try { const result = yield call(addCommentAPI, action.data); // yield delay(1000); yield put({ type: ADD_COMMENT_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: ADD_COMMENT_FAILURE, error: err.response.data, }); }; }; function addPostAPI(data) { return axios.post('/post', data); }; function* addPost(action) { try { const result = yield call(addPostAPI, action.data); yield put({ type: ADD_POST_SUCCESS, data: result.data }); yield put({ type: ADD_POST_TO_ME, data: result.data.id, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: ADD_POST_FAILURE, error: err.response.data, }); }; }; function removePostAPI(data) { return axios.delete(`/post/${data}`); }; function* removePost(action) { try { const result = yield call(removePostAPI, action.data); yield put({ type: REMOVE_POST_SUCCESS, data: result.data, }); console.log('removePost'); yield put({ type: REMOVE_POST_OF_ME, data: action.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: REMOVE_POST_FAILURE, error: err.response.data, }); }; }; function uploadImagesAPI(data) { return axios.post('/post/images', data); // POST /post/images }; function* uploadImages(action) { try { const result = yield call(uploadImagesAPI, action.data); yield put({ type: UPLOAD_IMAGES_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: UPLOAD_IMAGES_FAILURE, error: err.response.data, }); }; }; function retweetAPI(data) { return axios.post(`/post/${data}/retweet`); // POST /post/images }; function* retweet(action) { try { const result = yield call(retweetAPI, action.data); yield put({ type: RETWEET_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: RETWEET_FAILURE, error: err.response.data, }); }; }; function loadHashtagPostsAPI(data, lastId) { return axios.get(`/hashtag/${data}?lastId=${lastId || 0}`); }; function* loadHashtagPosts(action) { try { const result = yield call(loadHashtagPostsAPI, action.data, action.lastId); yield put({ type: LOAD_HASHTAG_POSTS_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: LOAD_HASHTAG_POSTS_FAILURE, error: err.response.data, }); } } function loadUserPostsAPI(data, lastId) { return axios.get(`/user/${data}/posts?lastId=${lastId || 0}`); }; function* loadUserPosts(action) { try { const result = yield call(loadUserPostsAPI, action.data, action.lastId); yield put({ type: LOAD_USER_POSTS_SUCCESS, data: result.data, // data: generateDummyPost(10), }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LOAD_USER_POSTS_FAILURE, error: err.response.data, }); } } function* watchLoadUserPosts() { yield throttle(5000, LOAD_USER_POSTS_REQUEST, loadUserPosts); }; function* watchLoadHashtagPosts() { // 5초에 한번 게사글이 로드 된다. yield throttle(5000, LOAD_HASHTAG_POSTS_REQUEST, loadHashtagPosts); }; function* watchLoadPosts() { // 5초에 한번 게사글이 로드 된다. yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts); }; function* watchLoadPost() { // 5초에 한번 게사글이 로드 된다. yield takeLatest(LOAD_POST_REQUEST, loadPost); }; function* watchRetweet() { // 5초에 한번 게사글이 로드 된다. yield takeLatest(RETWEET_REQUEST, retweet); }; function* watchAddPost() { yield takeLatest(ADD_POST_REQUEST, addPost); }; function* watchUploadImages() { yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages); }; function* watchAddComment() { yield takeLatest(ADD_COMMENT_REQUEST, addComment); }; function* watchRemovePost() { yield takeLatest(REMOVE_POST_REQUEST, removePost); }; function* watchLikePost() { yield takeLatest(LIKE_POST_REQUEST, likePost); }; function* watchUnLikePost() { yield takeLatest(UNLIKE_POST_REQUEST, unlikePost); }; export default function* postSaga() { yield all([ fork(watchRetweet), fork(watchUploadImages), fork(watchLikePost), fork(watchUnLikePost), fork(watchAddPost), fork(watchLoadPosts), fork(watchLoadHashtagPosts), fork(watchLoadUserPosts), fork(watchLoadPost), fork(watchRemovePost), fork(watchAddComment), ]); }; reducers/post.js reducers/user.js FRONT user/[id]/.js import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { Avatar, Card } from 'antd'; import { END } from 'redux-saga'; import Head from 'next/head'; import { useRouter } from 'next/router'; import axios from 'axios'; import { LOAD_USER_POSTS_REQUEST } from '../../reducers/post'; import { LOAD_MY_INFO_REQUEST, LOAD_USER_REQUEST } from '../../reducers/user'; import PostCard from '../../components/PostCard'; import wrapper from '../../store/configureStore'; import AppLayout from '../../components/AppLayout'; import { backUrl } from '../../config/config'; // 프론트서버와 브라우저 모두에서 실행 const User = () => { const dispatch = useDispatch(); const router = useRouter(); const { id } = router.query; <==== 이부분에서 id 값은 잘 받아 오는것 같습니다. console.log(router.query); <==== 로그를 찍어보니 {id: "1"} 로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과도 동일한데.. 정상적으로 스크롤 됩니다. const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post); const { userInfo } = useSelector((state) => state.user); useEffect(() => { function onScroll() { if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) { if (hasMorePosts && !loadPostsLoading) { dispatch({ type: LOAD_USER_POSTS_REQUEST, lastId: mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.lenth - 1].id, <====== 에러가 발생하는 부분 data: id, }); } } } window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); }; }, [ mainPosts.length, hasMorePosts, id]); return ( <AppLayout> <Head> <title> {userInfo.nickname} 님의 글 </title> <meta name="description" content={`${userInfo.nickname}님의 게시글`} /> <meta property="og:title" content={`${userInfo.nickname}님의 게시글`} /> <meta property="og:description" content={`${userInfo.nickname}님의 게시글`} /> <meta property="og:image" content={`${backUrl}/favicon.ico`} /> <meta property="og:url" content={`${backUrl}/user/${id}`} /> </Head> {userInfo ? ( <Card actions={[ <div key="twit"> 짹짹 <br /> {userInfo.Posts} </div>, <div key="following"> 팔로잉 <br /> {userInfo.Followings} </div>, <div key="follower"> 팔로워 <br /> {userInfo.Followers} </div>, ]} > <Card.Meta avatar={<Avatar>{userInfo.nickname[0]}</Avatar>} title={userInfo.nickname} /> </Card> ) : null} {mainPosts.map((c) => ( <PostCard key={c.id} post={c} /> ))} </AppLayout> ); }; // front server 에서 실행 export const getServerSideProps = wrapper.getServerSideProps(async (context) => { const cookie = context.req ? context.req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; if (context.req && cookie) { axios.defaults.headers.Cookie = cookie; } context.store.dispatch({ type: LOAD_USER_POSTS_REQUEST, data: context.params.id, }); context.store.dispatch({ type: LOAD_MY_INFO_REQUEST, }); context.store.dispatch({ type: LOAD_USER_REQUEST, data: context.params.id, }); context.store.dispatch(END); await context.store.sagaTask.toPromise(); // return { props: {} }; }); export default User; config/config.js const domainUrl = 'https://api.nodebird.com'; const localhostUrl = 'http://localhost:3065'; const backUrl = process.env.NODE_ENV === 'production' ? 'https://api.nodebird.com' : 'http://localhost:3065'; export { backUrl, domainUrl, localhostUrl }; BACK sagas/user.js // all fork call put delay debounce throttle takeLatest tabkeEvery takeLeding taekMaybe import { all, fork, put, takeLatest, throttle, call } from 'redux-saga/effects'; import axios from 'axios'; // import shortId from 'shortid'; import { LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE, LOAD_USER_POSTS_REQUEST, LOAD_USER_POSTS_SUCCESS, LOAD_USER_POSTS_FAILURE, LOAD_HASHTAG_POSTS_REQUEST, LOAD_HASHTAG_POSTS_SUCCESS, LOAD_HASHTAG_POSTS_FAILURE, LOAD_POST_REQUEST, LOAD_POST_SUCCESS, LOAD_POST_FAILURE, LIKE_POST_REQUEST, LIKE_POST_SUCCESS, LIKE_POST_FAILURE, UNLIKE_POST_REQUEST, UNLIKE_POST_SUCCESS, UNLIKE_POST_FAILURE, ADD_POST_REQUEST, ADD_POST_SUCCESS, ADD_POST_FAILURE, ADD_COMMENT_REQUEST, ADD_COMMENT_SUCCESS, ADD_COMMENT_FAILURE, REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS, REMOVE_POST_FAILURE, UPLOAD_IMAGES_REQUEST, UPLOAD_IMAGES_SUCCESS, UPLOAD_IMAGES_FAILURE, RETWEET_REQUEST, RETWEET_SUCCESS, RETWEET_FAILURE, } from '../reducers/post'; import { ADD_POST_TO_ME, REMOVE_POST_OF_ME, } from '../reducers/user'; function likePostAPI(data) { return axios.patch(`/post/${data}/like`); }; function* likePost(action) { try { const result = yield call(likePostAPI, action.data); yield put({ type: LIKE_POST_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LIKE_POST_FAILURE, error: err.response.data, }); } } function unlikePostAPI(data) { return axios.delete(`/post/${data}/like`); }; function* unlikePost(action) { try { const result = yield call(unlikePostAPI, action.data); yield put({ type: UNLIKE_POST_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: UNLIKE_POST_FAILURE, error: err.response.data, }); } } function loadPostsAPI(lastId) { return axios.get(`/posts?lastId=${lastId || 0}`); }; function* loadPosts(action) { try { const result = yield call(loadPostsAPI, action.lastId); yield put({ type: LOAD_POSTS_SUCCESS, data: result.data, // data: generateDummyPost(10), }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LOAD_POSTS_FAILURE, error: err.response.data, }); } } function loadPostAPI(data) { return axios.get(`/post/${data}`); }; function* loadPost(action) { try { const result = yield call(loadPostAPI, action.data); yield put({ type: LOAD_POST_SUCCESS, data: result.data, // data: generateDummyPost(10), }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LOAD_POST_FAILURE, error: err.response.data, }); } } function addCommentAPI(data) { return axios.post(`/post/${data.postId}/comment`, data); // POST /post/1/comment }; function* addComment(action) { try { const result = yield call(addCommentAPI, action.data); // yield delay(1000); yield put({ type: ADD_COMMENT_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: ADD_COMMENT_FAILURE, error: err.response.data, }); }; }; function addPostAPI(data) { return axios.post('/post', data); }; function* addPost(action) { try { const result = yield call(addPostAPI, action.data); yield put({ type: ADD_POST_SUCCESS, data: result.data }); yield put({ type: ADD_POST_TO_ME, data: result.data.id, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: ADD_POST_FAILURE, error: err.response.data, }); }; }; function removePostAPI(data) { return axios.delete(`/post/${data}`); }; function* removePost(action) { try { const result = yield call(removePostAPI, action.data); yield put({ type: REMOVE_POST_SUCCESS, data: result.data, }); console.log('removePost'); yield put({ type: REMOVE_POST_OF_ME, data: action.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: REMOVE_POST_FAILURE, error: err.response.data, }); }; }; function uploadImagesAPI(data) { return axios.post('/post/images', data); // POST /post/images }; function* uploadImages(action) { try { const result = yield call(uploadImagesAPI, action.data); yield put({ type: UPLOAD_IMAGES_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: UPLOAD_IMAGES_FAILURE, error: err.response.data, }); }; }; function retweetAPI(data) { return axios.post(`/post/${data}/retweet`); // POST /post/images }; function* retweet(action) { try { const result = yield call(retweetAPI, action.data); yield put({ type: RETWEET_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: RETWEET_FAILURE, error: err.response.data, }); }; }; function loadHashtagPostsAPI(data, lastId) { return axios.get(`/hashtag/${data}?lastId=${lastId || 0}`); }; function* loadHashtagPosts(action) { try { const result = yield call(loadHashtagPostsAPI, action.data, action.lastId); yield put({ type: LOAD_HASHTAG_POSTS_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: LOAD_HASHTAG_POSTS_FAILURE, error: err.response.data, }); } } function loadUserPostsAPI(data, lastId) { return axios.get(`/user/${data}/posts?lastId=${lastId || 0}`); }; function* loadUserPosts(action) { try { const result = yield call(loadUserPostsAPI, action.data, action.lastId); yield put({ type: LOAD_USER_POSTS_SUCCESS, data: result.data, // data: generateDummyPost(10), }); } catch (err) { console.error(err); yield put({ // put => dispatch 다. type: LOAD_USER_POSTS_FAILURE, error: err.response.data, }); } } function* watchLoadUserPosts() { yield throttle(5000, LOAD_USER_POSTS_REQUEST, loadUserPosts); }; function* watchLoadHashtagPosts() { // 5초에 한번 게사글이 로드 된다. yield throttle(5000, LOAD_HASHTAG_POSTS_REQUEST, loadHashtagPosts); }; function* watchLoadPosts() { // 5초에 한번 게사글이 로드 된다. yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts); }; function* watchLoadPost() { // 5초에 한번 게사글이 로드 된다. yield takeLatest(LOAD_POST_REQUEST, loadPost); }; function* watchRetweet() { // 5초에 한번 게사글이 로드 된다. yield takeLatest(RETWEET_REQUEST, retweet); }; function* watchAddPost() { yield takeLatest(ADD_POST_REQUEST, addPost); }; function* watchUploadImages() { yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages); }; function* watchAddComment() { yield takeLatest(ADD_COMMENT_REQUEST, addComment); }; function* watchRemovePost() { yield takeLatest(REMOVE_POST_REQUEST, removePost); }; function* watchLikePost() { yield takeLatest(LIKE_POST_REQUEST, likePost); }; function* watchUnLikePost() { yield takeLatest(UNLIKE_POST_REQUEST, unlikePost); }; export default function* postSaga() { yield all([ fork(watchRetweet), fork(watchUploadImages), fork(watchLikePost), fork(watchUnLikePost), fork(watchAddPost), fork(watchLoadPosts), fork(watchLoadHashtagPosts), fork(watchLoadUserPosts), fork(watchLoadPost), fork(watchRemovePost), fork(watchAddComment), ]); }; reducers/post.js import produce from 'immer'; import { REMOVE_POST_OF_ME } from './user'; export const initialState = { mainPosts: [], imagePaths: [], // 이미지를 업로드 할때 이미지 경로 singlePost: null, postAdded: false, hasMorePosts: true, likePostLoading: false, likePostDone: false, // 추가 likePostError: null, // 추가 retweetLoading: false, retweetDone: false, // 추가 retweetError: null, // 추가 uploadImagesLoading: false, uploadImagesDone: false, // 추가 uploadImagesError: null, // 추가 unlikePostLoading: false, unlikePostDone: false, // 추가 unlikePostError: null, // 추가 loadPostsLoading: false, loadPostsDone: false, // 추가 loadPostsError: null, // 추가 loadPostLoading: false, loadPostDone: false, // 추가 loadPostError: null, // 추가 // 게시글 추가가 완료되었을때 true 로 변한다. postAdded: false, => addPostLoading: false, addPostLoading: false, addPostDone: false, // 추가 addPostError: null, // 추가 addCommentLoading: false, // 댓글 추가가 완료되었을때 true 로 변한다. addCommentDone: false, // 추가 addCommentError: null, // 추가 removePostLoading: false, removePostDone: false, // 추가 removePostError: null, // 추가 }; export const UPLOAD_IMAGES_REQUEST = 'UPLOAD_IMAGES_REQUEST'; export const UPLOAD_IMAGES_SUCCESS = 'UPLOAD_IMAGES_SUCCESS'; export const UPLOAD_IMAGES_FAILURE = 'UPLOAD_IMAGES_FAILURE'; export const RETWEET_REQUEST = 'RETWEET_REQUEST'; export const RETWEET_SUCCESS = 'RETWEET_SUCCESS'; export const RETWEET_FAILURE = 'RETWEET_FAILURE'; 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 LOAD_POST_REQUEST = 'LOAD_POST_REQUEST'; export const LOAD_POST_SUCCESS = 'LOAD_POST_SUCCESS'; export const LOAD_POST_FAILURE = 'LOAD_POST_FAILURE'; export const LOAD_USER_POSTS_REQUEST = 'LOAD_USER_POSTS_REQUEST'; export const LOAD_USER_POSTS_SUCCESS = 'LOAD_USER_POSTS_SUCCESS'; export const LOAD_USER_POSTS_FAILURE = 'LOAD_USER_POSTS_FAILURE'; export const LOAD_HASHTAG_POSTS_REQUEST = 'LOAD_HASHTAG_POSTS_REQUEST'; export const LOAD_HASHTAG_POSTS_SUCCESS = 'LOAD_HASHTAG_POSTS_SUCCESS'; export const LOAD_HASHTAG_POSTS_FAILURE = 'LOAD_HASHTAG_POSTS_FAILURE'; export const LIKE_POST_REQUEST = 'LIKE_POST_REQUEST'; export const LIKE_POST_SUCCESS = 'LIKE_POST_SUCCESS'; export const LIKE_POST_FAILURE = 'LIKE_POST_FAILURE'; export const UNLIKE_POST_REQUEST = 'UNLIKE_POST_REQUEST'; export const UNLIKE_POST_SUCCESS = 'UNLIKE_POST_SUCCESS'; export const UNLIKE_POST_FAILURE = 'UNLIKE_POST_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 REMOVE_IMAGE = 'REMOVE_IMAGE'; export const addComment = (data) => ({ type: ADD_COMMENT_REQUEST, data, }); export const addPost = (data) => ({ type: ADD_POST_REQUEST, data, }); export const removePost = (data) => ({ type: REMOVE_POST_OF_ME, data, }); const reducer = (state = initialState, action) => produce(state, (draft) => { switch (action.type) { case REMOVE_IMAGE: draft.imagePaths = draft.imagePaths.filter((v, i) => i !== action.data); break; case RETWEET_REQUEST: draft.retweetLoading = true; draft.retweetDone = false; draft.retweetError = null; break; case RETWEET_SUCCESS: draft.retweetLoading = false; draft.retweetDone = true; draft.mainPosts.unshift(action.data); break; case RETWEET_FAILURE: draft.retweetLoading = false; draft.retweetError = action.error; break; case UPLOAD_IMAGES_REQUEST: draft.uploadImagesLoading = true; draft.uploadImagesDone = false; draft.uploadImagesError = null; break; case UPLOAD_IMAGES_SUCCESS: { draft.imagePaths = action.data; draft.uploadImagesLoading = false; draft.uploadImagesDone = true; break; } case UPLOAD_IMAGES_FAILURE: draft.uploadImagesLoading = false; draft.uploadImagesError = action.error; break; case LIKE_POST_REQUEST: draft.likePostLoading = true; draft.likePostDone = false; draft.likePostError = null; break; case LIKE_POST_SUCCESS: { const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Likers.push({ id: action.data.UserId }); draft.likePostLoading = false; draft.likePostDone = true; break; } case LIKE_POST_FAILURE: draft.likePostLoading = false; draft.likePostError = action.error; break; case UNLIKE_POST_REQUEST: draft.unlikePostLoading = true; draft.unlikePostDone = false; draft.unlikePostError = null; break; case UNLIKE_POST_SUCCESS: { console.log(action.data); const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Likers = post.Likers.filter((v) => v.id !== action.data.UserId); // 제거 draft.unlikePostLoading = false; draft.unlikePostDone = true; break; } case UNLIKE_POST_FAILURE: draft.unlikePostLoading = false; draft.unlikePostError = action.error; break; case LOAD_USER_POSTS_REQUEST: case LOAD_HASHTAG_POSTS_REQUEST: case LOAD_POSTS_REQUEST: draft.loadPostsLoading = true; draft.loadPostsDone = false; draft.loadPostsError = null; break; case LOAD_USER_POSTS_SUCCESS: case LOAD_HASHTAG_POSTS_SUCCESS: case LOAD_POSTS_SUCCESS: draft.loadPostsLoading = false; draft.loadPostsDone = true; draft.mainPosts = draft.mainPosts.concat(action.data); draft.hasMorePosts = action.data.length === 10; break; case LOAD_USER_POSTS_FAILURE: case LOAD_HASHTAG_POSTS_FAILURE: case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false; draft.loadPostsError = action.error; break; case LOAD_POST_REQUEST: draft.loadPostLoading = true; draft.loadPostDone = false; draft.loadPostError = null; break; case LOAD_POST_SUCCESS: draft.loadPostLoading = false; draft.loadPostDone = true; draft.singlePost = action.data; break; case LOAD_POST_FAILURE: draft.loadPostLoading = false; draft.loadPostError = action.error; break; 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 = []; break; case ADD_POST_FAILURE: draft.addPostLoading = true; draft.addPostError = action.error; break; case ADD_COMMENT_REQUEST: draft.addCommentLoading = true; draft.addCommentDone = false; draft.addCommentError = null; break; case ADD_COMMENT_SUCCESS: // action.data.content, action.data.postId, action.data.userId // post.Comments.unshift(dummyComment(action.data.content)); const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Comments.unshift(action.data); draft.addCommentLoading = false; draft.addCommentDone = true; break; case ADD_COMMENT_FAILURE: draft.addCommentLoading = false; draft.addCommentError = action.error; break; case REMOVE_POST_REQUEST: draft.removePostLoading = true; draft.removePostDone = false; draft.removePostError = null; break; // [ !== 일치하는 게시글만 지운다. ] 그리고 [ === 일치하는 게시글 이외의 모든 게시글이 지워진다. ] // filter를 사용해서 불변성을 유지한다. case REMOVE_POST_SUCCESS: draft.removePostLoading = false; draft.removePostDone = true; draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data.PostId); break; case REMOVE_POST_FAILURE: draft.removePostLoading = false; draft.removePostError = action.error; break; default: break; } }); export default reducer; reducers/user.js import produce from 'immer'; export const initialState = { removeFollowerLoading: false, // 내가 수락한 친구 차단하기 removeFollowerDone: false, // 추가 removeFollowerError: null, // 추가 loadUserLoading: false, // 사용자 정보 가져오기 시도중.. 로딩창을 띄운다. loadUserDone: false, loadUserError: null, loadMyInfoLoading: false, // 내 정보 가져오기 시도중.. 로딩창을 띄운다. loadMyInfoDone: false, loadMyInfoError: null, logInLoading: false, // 로그인 시도중.. 로딩창을 띄운다. logInDone: false, logInError: null, logOutLoading: false, // 로그아웃 시도중.. 로딩창을 띄운다. logOutDone: false, logOutError: null, followLoading: false, // 팔로우 시도중.. 로딩창을 띄운다. followDone: false, followError: null, unfollowLoading: false, // 언팔로우 시도중.. 로딩창을 띄운다. unfollowDone: false, unfollowError: null, loadFollowersLoading: false, // 팔로워 목록 가져오기 시도중.. 로딩창을 띄운다. loadFollowersDone: false, loadFollowersError: null, loadFollowingsLoading: false, // 팔로윙 목록 가져오기 시도중.. 로딩창을 띄운다. loadFollowingsDone: false, loadFollowingsError: null, signUpLoading: false, // 회원가입 시도중.. 로딩창을 띄운다. signUpDone: false, signUpError: null, changeNicknameLoading: false, // 닉네임 변경 시도중.. 로딩창을 띄운다. changeNicknameDone: false, changeNicknameError: null, me: null, userInfo: null, }; export const REMOVE_FOLLOWER_REQUEST = 'REMOVE_FOLLOWER_REQUEST'; export const REMOVE_FOLLOWER_SUCCESS = 'REMOVE_FOLLOWER_SUCCESS'; export const REMOVE_FOLLOWER_FAILURE = 'REMOVE_FOLLOWER_FAILURE'; export const LOAD_MY_INFO_REQUEST = 'LOAD_MY_INFO_REQUEST'; export const LOAD_MY_INFO_SUCCESS = 'LOAD_MY_INFO_SUCCESS'; export const LOAD_MY_INFO_FAILURE = 'LOAD_MY_INFO_FAILURE'; export const LOAD_USER_REQUEST = 'LOAD_USER_REQUEST'; export const LOAD_USER_SUCCESS = 'LOAD_USER_SUCCESS'; export const LOAD_USER_FAILURE = 'LOAD_USER_FAILURE'; 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 LOAD_FOLLOWERS_REQUEST = 'LOAD_FOLLOWERS_REQUEST'; export const LOAD_FOLLOWERS_SUCCESS = 'LOAD_FOLLOWERS_SUCCESS'; export const LOAD_FOLLOWERS_FAILURE = 'LOAD_FOLLOWERS_FAILURE'; export const LOAD_FOLLOWINGS_REQUEST = 'LOAD_FOLLOWINGS_REQUEST'; export const LOAD_FOLLOWINGS_SUCCESS = 'LOAD_FOLLOWINGS_SUCCESS'; export const LOAD_FOLLOWINGS_FAILURE = 'LOAD_FOLLOWINGS_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 reducer = (state = initialState, action) => produce (state, (draft) => { switch (action.type) { case REMOVE_FOLLOWER_REQUEST: draft.removeFollowerLoading = true; draft.removeFollowerDone = false; draft.removeFollowerError = null; break; case REMOVE_FOLLOWER_SUCCESS: { draft.removeFollowerLoading = false; draft.me.Followers = draft.me.Followers.filter((v) => v.id !== action.data.UserId); draft.removeFollowerDone = true; break; } case REMOVE_FOLLOWER_FAILURE: draft.removeFollowerLoading = false; draft.removeFollowerError = action.error; break; case LOAD_MY_INFO_REQUEST: draft.loadMyInfoLoading = true; draft.loadMyInfoError = null; draft.loadMyInfoDone = false; break; case LOAD_MY_INFO_SUCCESS: draft.loadMyInfoLoading = false; draft.me = action.data; draft.loadMyInfoDone = true; break; case LOAD_MY_INFO_FAILURE: draft.loadMyInfoLoading = false; // isLoggingOut: true, => logOutLoading: true, draft.loadMyInfoError = action.error; // logInError: action.error, => logOutDone: false, break; case LOAD_USER_REQUEST: draft.loadUserLoading = true; draft.loadUserError = null; draft.loadUserDone = false; break; case LOAD_USER_SUCCESS: draft.loadUserLoading = false; draft.userInfo = action.data; draft.loadUserDone = true; break; case LOAD_USER_FAILURE: draft.loadUserLoading = false; // isLoggingOut: true, => logOutLoading: true, draft.loadUserError = action.error; // logInError: action.error, => logOutDone: false, break; case LOAD_FOLLOWERS_REQUEST: draft.loadFollowersLoading = true; draft.loadFollowersError = null; draft.loadFollowersDone = false; break; case LOAD_FOLLOWERS_SUCCESS: draft.loadFollowersLoading = false; draft.me.Followers = action.data; draft.loadFollowersDone = true; break; case LOAD_FOLLOWERS_FAILURE: draft.loadFollowersLoading = false; draft.loadFollowersError = action.error; break; case LOAD_FOLLOWINGS_REQUEST: draft.loadFollowingsLoading = true; draft.loadFollowingsError = null; draft.loadFollowingsDone = false; break; case LOAD_FOLLOWINGS_SUCCESS: draft.loadFollowingsLoading = false; draft.me.Followings = action.data; draft.loadFollowingsDone = true; break; case LOAD_FOLLOWINGS_FAILURE: draft.loadFollowingsLoading = false; draft.loadFollowingsError = action.error; break; 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.UserId }); draft.followDone = true; break; case FOLLOW_FAILURE: draft.followLoading = false; // 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.UserId ); break; case UNFOLLOW_FAILURE: draft.unfollowLoading = false; // 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.me.nickname = action.data.nickname; 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 = action.data; break; case LOG_IN_FAILURE: draft.logInLoading = false; // 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; case REMOVE_POST_OF_ME: draft.me.Posts = draft.me.Posts.filter((v) => v.id !== action.data); break; default: break; } }); export default reducer;
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
로그인후 앱 크러쉬
https://github.com/lhj1004ss/LoginAndRegister_react-node ERROR: Cannot set headers after they are sent to the client 포스트맨으로 로인후에 유저토큰만 완성되고 바로 앱이 크러쉬됩니다. 구글링을 좀해보니, 이 에러가 보통 콜백이 서버에서의 아웃풋이 클라이언트로 생각했던것보다? 일찍 갔을 때 나오거나 콜백을 두번불렀을 때 나오는 에러라고 하는데, 코드를 검토해도찾을 수 없어서 여기에 올립니다!
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 쇼핑몰 사이트 만들기[전체 리뉴얼]
카트페이지에서 quantity만 못가져오는데요 왜그런걸까요 ?
스테잇을 확인하면 quantity 부분이 빠져있는데 어디를 확인해야 할지 모르겠네요. 도와주세요! 감사합니다 깃헙주소입니다: https://github.com/vladastam/shop_app
-
미해결파이썬 사용자를 위한 웹개발 입문 A to Z Django + Bootstrap
tdd 테스트코드 작성 B에서 아래와 같은 에러가 납니다.
test 누르면 아직 게시물이 없습니다. 에서 자꾸 에러가 나요. (venv) C:\Users\user\Desktop\HTML+django\my_django>python manage.py test Creating test database for alias 'default'... System check identified no issues (0 silenced). F ====================================================================== FAIL: test_post_list (blog.tests.TestView) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\user\Desktop\HTML+django\my_django\blog\tests.py", line 30, in test_post_list self.assertIn('아직 게시물이 없습니다.', soup.body.text) AssertionError: '아직 게시물이 없습니다.' not found in '\n\n\nBootswatch\n\n\n\n\n\n\nThemes \n\nDefault\n\nCerulean\nCosmo\nCyborg\nDarkly\nFlatly\nJournal\nLitera\nLumen\nLux\nMateria\nMinty\nPulse\nSandstone\nSimplex\nSketchy\nSlate\nSolar\nSpacelab\nSuperhero\nUnited\nYeti\n\n\n\nHelp\n\n\nBlog\n\n\nFlatly \n\nOpen in JSFiddle\n\nbootstrap.min.css\nbootstrap.css\n\n_variables.scss\n_bootswatch.scss\n\n\n\n\n\n\n\n\n\n\nBlog\nby matiii\n' ---------------------------------------------------------------------- Ran 1 test in 0.037s FAILED (failures=1) Destroying test database for alias 'default'... post_list.html <div class="col-md-8"> <h1 class="my-4">Blog</h1> <small>by matiii</small> </h1> <!-- Blog Post --> {% if objects_list.exist %} {% for p in object_list %} <div class="card mb-4"> {% if p.head_image %} <img class="card-img-top" src="{{ p.head_image.url }}" alt="Card image cap"> {% else %} <img class="card-img-top" src="https://picsum.photos/750/300/?random" alt="Card image cap"> {% endif %} <div class="card-body"> <h2 class="card-title">{{ p.title }}</h2> <p class="card-text">{{ p.content | truncatewords:50 }}</p> <a href="#" class="btn btn-primary">Read More →</a> </div> <div class="card-footer text-muted"> Posted on {{p.created}} <a href="#">{{p.author}}</a> </div> </div> <!-- Pagination --> <ul class="pagination justify-content-center mb-4"> <li class="page-item"> <a class="page-link" href="#">← Older</a> </li> <li class="page-item disabled"> <a class="page-link" href="#">Newer →</a> </li> </ul> {% endfor %} {% else %} <h3>아직 게시물이 없습니다.</h3> {% endif %}</div> tests.py from django.test import TestCase# Create your tests here.from django.test import TestCase, Clientfrom bs4 import BeautifulSoupfrom .models import Postfrom django.utils import timezonefrom django.contrib.auth.models import Userclass TestView(TestCase): def setUp(self): self.client = Client() self.author_OOO=User.objects.create(username='SAM', password='none') def test_post_list(self): response = self.client.get('/blog/') self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') title = soup.title self.assertEqual(title.text, 'Blog') navbar = soup.find('div', id='navbar') self.assertIn('Blog', navbar.text) self.assertIn('Help', navbar.text) self.assertEqual(Post.objects.count(), 0) self.assertIn('아직 게시물이 없습니다.', soup.body.text) post_OOO = Post.objects.create( title='The First Post', content='Hello World!', created=timezone.now(), author=self.author_OOO, ) self.assertGreater(Post.objects.count(), 0) response = self.client.get('/blog/') self.assertEqual(response.status_code, 200) soup = BeautifulSoup(response.content, 'html.parser') body = soup.body self.assertNotIn('아직 게시물이 없습니다.', body.text) self.assertIn(post_OOO.title, body.text)
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
H2실행
안녕하세요 강사님 !H2실행이 안돼서 여러가지 방법을 시도해봤는데 실행이 안되네요.. 브라우저 창이 열리지않고 아래 사진과 같이 보여지고 종료되는데 혹시 이유 아신다면 알려주시면 감사하겠습니다
-
미해결모의해킹 실무자가 알려주는, SQL Injection 공격 기법과 시큐어 코딩 : PART 1
대응방안 코드에 대해서 질문이 있습니다.
안녕하세요. 강의 "대응 방안" 파트의 "상세 대응 방안(2)" 강의를 보던 중 궁금한점이 생겨서 질문드립니다. 1) 첫째로, 문자 입력에 대한 대응 방안으로 강의에서 예시로 제시했던 코드가 아래와 같습니다. --------------------------------------------------------------------------- JAVA String keyword = request.getParameter("keyword"); // MSSQL, ORACLE 대응 방안 예시 keyword = keyword.replace("'", "''"); <<<<<<<<< (1) keyword = keyword.replace("\"", "\"\""); query = "select * from board where keyword like '%" + keyword + "%'"; ------------------------------------------------------------------------------- 이 부분에서 <<<<<<<< (1) 표시된 부분의 내용이 잘 이해가 되지 않아서요. 강의 하시면서 공격자가 '(작음따옴표) 하나를 입력하면 두개로 변하여 '(작은따옴표)가 검색이 된다고 말씀하셨는데요 만약 공격자가 '(작은따옴표) 하나를 입력한 경우라면 이 하나의 '(작은따옴표)가 연속된 두개의 ''(작은 따옴표)로 변하여 query 는 다음과 같이 생성될 것입니다. select * from board where keyword like '%''%' 이런식으로 되면 % 양쪽으로 그냥 '(작은따옴표)로 깜싸는 정도로 바뀔거라고 짐작하고 있는데요. 어떻게 '(작은따옴표)도 검색이 가능하게 되는지 이해가 되지 않습니다. 그리고 이 경우 mysql 경우 처럼 \' 패턴을 이용하면 우회가 가능한지도 궁금합니다. 그리고.... 2) 두번째로, 컬럼/테이블 입력에 대한 대응 방안으로 제시한 예제 코드의 일부는 다음과 같습니다. boolean flag = Pattern.matches("^[0-9a-zA-Z-]*$", tb_name); 강의에서 설명해주셨던 것 처럼 이 코드 내용은 숫자, 알파벳 대소문자 그리고 -(대시 또는 하이픈)문자를 필터링하는 것인데요. 제 생각에는 -(대시) 문자 이외의 _(언더바) 문자가 추가되어야 하지 않을까 해서요? 강의에서 사용하였던 "TB_BOARD" 처럼 _(언더바) 문자가 포함된 테이블이나 컬럼이 존재하는거로 봐서는요, 아니면 제시된 코드의 오타인걸로도 생각이 들고요. 감사합니다~
-
해결됨스프링 시큐리티
IP 제어 관련 문의 드립니다.
안녕하세요.. 현재 강의를 듣고 있는데요. Client IP 접근을 제어해서, 자원에 접근이 안되도록 하는 데모를 보았습니다. 이 기능을 사용할 경우, WAS 앞단에 L4 나 Proxy 와 같은 장치가 있어도, Client IP 를 정상적으로 인식 할 수 있나요? 혹시, L4 나 Proxy 가 있는 경우, 사용자의 Client IP 가 아니고, L4 IP 가 전달되지 않는 지 문의드립니다.
-
미해결자바 스프링 프레임워크(renew ver.) - 신입 프로그래머를 위한 강좌
강의자료는 없나요?????
3강 듣고있는데 이거 혹시 강의자료는 없나요???
-
해결됨Vue.js 완벽 가이드 - 실습과 리팩토링으로 배우는 실전 개념
아무리 찾아도답이안나와서 질문드립니다.
https://codepen.io/mikilll94/pen/RwbYrpz 간단한 예제인데요. 데이터가 많을때 v-model 바인딩을 하면 속도가 엄청느려지는데 이유가 뭔가요 v-model 동작방식과 관련이 있을까요??.. 그리고 해결방안이 있을까요
-
해결됨React로 NodeBird SNS 만들기
like, unlike
안녕하세요. 제로초님 다름이 아니라 axios like에 return axios.post(`/post/${postId}/like`, {}, { withCredentials: true }); 이렇게 하면 잘 날라가는데 return axios.delete(`/post/${postId}/like`, {}, { withCredentials: true }); 이렇게 unlike는 빈 데이터를 보내면 401 에러가 뜨는데 그 에러가 로그인 안했다는 isLoggedin 그 에러거든요? 근데 로그인 정보랑 다 들어있고 해서 의아했는데 저 빈 객체 를 없애니깐 잘 동작하더라고요. 이거 왜이러는걸까요?
-
미해결React로 NodeBird SNS 만들기
2020.08.09 antd
import React, { useState } from 'react'; import AppLayout from "../components/AppLayout"; import Head from "next/head"; import { Form, Input, Checkbox, Button } from 'antd'; const Signup = () => { const [id, setId] = useState(''); const [nick, setNick] = useState(''); const [password, setPassword] = useState(''); const [passwordCheck, setPasswordCheck] = useState(''); const [term, setTerm] = useState(false); const [passwordError, setPasswordError] = useState(false); const [termError, setTermError] = useState(false); const onFinish= () => { if (password !== passwordCheck) { return setPasswordError(true); } if (!term) { return setTermError(true); } console.log({ id, nick, password, passwordCheck, term, }); }; const onChangeId = (e) => { setId(e.target.value); }; const onChangeNick = (e) => { setNick(e.target.value); }; const onChangePassword = (e) => { setPassword(e.target.value); }; const onChangePasswordChk = (e) => { setPasswordError(e.target.value !== password); setPasswordCheck(e.target.value); }; const onChangeTerm = (e) => { setTermError(false); setTerm(e.target.checked); }; return ( <> <Head> <title>NodeBird</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.16.2/antd.css" /> </Head> <AppLayout> <Form onFinish={onFinish} style={{ padding: 10 }}> <Form.Item label={"아이디"} name={"user-id"} rules={[{ required: true, message: "Please input your user ID!"}]} > <Input name="user-id" value={id} required onChange={onChangeId} /> </Form.Item> <Form.Item label={"닉네임"} name={"user-nick"} rules={[{ required: true, message: "Please input your nickname!"}]} > <Input name="user-nick" value={nick} required onChange={onChangeNick} /> </Form.Item> <Form.Item label={"비밀번호"} name={"user-password"} rules={[{ required: true, message: "Please input your password!"}]} > <Input name="user-password" type={"password"} value={password} required onChange={onChangePassword} /> </Form.Item> <Form.Item label={"비밀번호 체크"} name={"user-password-check"} > <Input name="user-password-check" type={"password"} value={passwordCheck} required onChange={onChangePasswordChk} /> {passwordError && <div style={{ color: 'red' }}>비밀번호가 일치하지 않습니다.</div>} </Form.Item> <Form.Item name={"user-term"}> <Checkbox name={"user-term"} checked={term} onChange={onChangeTerm}>서비스 이용 동의</Checkbox> {termError && <div style={{ color: 'red' }}>약관에 동의하셔야 합니다.</div>} </Form.Item> <Form.Item> <Button type="primary" htmlType="submit">가입하기</Button> </Form.Item> </Form> </AppLayout> </> ); }; export default Signup;
-
미해결it 취업을 위한 알고리즘 문제풀이 입문 (with C/C++) : 코딩테스트 대비
cin/cout이 더 낫지 않을까요
c++로 통일하는게 낫다고 봅니다.
-
미해결실전 리액트 프로그래밍
자식 컴포넌트 props 문의 드립니다.
강사님 안녕하세요. 오늘도 영상 시청하던 중, 궁금한 점이 하나 생겨서 문의드립니다. 부모 컴포넌트에서 useSelector 로 얻은 상태값 일부를 자식 컴포넌트에 props 로 전달하여 자식컴포넌트에서 활용하는 식으로 코딩하는 과정을 확인하게 됐는데요. 자식컴포넌트에서 직접 useSelector 를 사용하여 얻는 것과 어떤 차이점이 있을까요?일단, props 로 내려보내는 방식의 경우, 자식컴포넌트가 특정 리덕스 state 에 의존하지 않게 되므로 다른 부모컴포넌트에서 재활용할 수 있을 것 같아보이는데요. 만약 자식 컴포넌트를 재활용할 일이 없는 경우에는 자식 쪽에서 직접 useSelector 를 사용하는 방법이 props 를 이용하는 것과 또 다른 차이가 있을까요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
heroku deploy
안녕하세요 강사님 제 로컬 서버에서는 페이지 작동이 잘되는데 헤로쿠에 올려보려 하니 이런 에러가 계속 떠서요! 2020-08-09T14:29:32.555557+00:00 app[web.1]: Error: ENOENT: no such file or directory, stat '/app/client/build/index.html' 2020-08-09T14:29:32.555899+00:00 heroku[router]: at=info method=GET path="/" host=seoul-tube.herokuapp.com request_id=99ff7d0c-8a4b-4399-a2bb-624cc8eca90b fwd="222.98.69.5" dyno=web.1 connect=0ms service=22ms status=404 bytes=412 protocol=https 2020-08-09T14:30:00.270232+00:00 app[web.1]: MongooseError [MongooseServerSelectionError]: connect ECONNREFUSED 127.0.0.1:27017 index.html파일을 못 찾는 것 같아 수정해보려고 하는데 폴더 구조를 client/public => client/build로 바꿨는데도 똑같아서 어찌 해야 할지 여쭤봅니다. 그리고 저희 구조 상에서는 build폴더가 안 보이는데 로컬에서는 잘 작동하는 게 신기한데 build폴더는 webpack실행 후 public폴더가 압축되서 만들어지고 눈에 안 보이더라도 실행되는 폴더인가요?
-
미해결홍정모의 따라하며 배우는 C언어
stdlib.h를 추가했는데 _itoa()함수가 불분명하다고 뜨네요 뭐가 잘못된걸까요..
아래와 같이 코드를 입력하여서 빌드하는 과정에서 warning : implicit declaration of function '_itoa' undefined reference to '_itoa'라는 워닝 문구가 뜹니다. 어떤것 때문에 이런 문제가 생기는 건지 궁금합니다. #define _CRT_SECURE_NO_WARNINGS #include <stdlib.h> #include <stdio.h> #include <limits.h> int main() { unsigned int u_max = UINT_MAX + 1; // i to binary representation char buffer[33]; _itoa(u_max, buffer, 2); // print decimal and binary printf("decimal: %u\n", u_max); printf("binary: %s\n", buffer); return 0; } ㅇ
-
미해결비전공자를 위한 개발자 취업 개론
튜터님 강의 정말 잘봤습니다!
안녕하세요 병욱님 : ) 저는 산업공학과를 졸업후 기회가되어 미국에서 직장생활을 해나가고있는 박수현입니다. 개발자로 커리어를 쌓아나가고싶은데 엔지니어링 학위가있긴하지만 마땅히 할줄아는것이 없어서 발만 동동구르고 이강의저강의 기웃거려도 손에 안잡힐때 병욱님 강의를 보게되었습니다. 처음에는 가이드 제시라기에는 조금 비싸다 ..싶었는데 강의시작하고 원큐로 끝내버렸네요 ㅎ 저처럼 마음만 급하고 갈피를 못잡는 사람들에게 너무나 유용한 강의같습니다. 일단 제가 정한 방향성은 '프론트엔트 웹 개발자'입니다. 로직과 딥러닝쪽에 관심이 많긴하지만 UI/UX처럼 사용자경험에 관련한 부분도 관심분야였기떄문에 첫 스타트로 해도 나쁘지않을것 같습니다. 제가 여쭙고싶은부분은 프론트엔드 웹개발자로 길을 정한다음 어떠한 강의부터 시작해나가면 될지 입니다. HTML, CSS는 완전기초강의 같은것은 한번 들어두긴했어서 이제 실전으로 HTML,CSS,JS 이용해서 실전 웹페이지를 만들어나가는 강의를 듣고싶은데 혹시 추천해주실만한 강의가있는지요? 종종 어떤강의는 설명이 되어야할부분이 너무 스킵이 되있다던지, 어떤부분이 구현이 안된다든지..하더라구요. 혹시 프론트엔드 개발을 빠르게 실전으로 습득할수있고 개념설명도 충분히 들어있는 강의 혹시 추천하실만한것이 있다면 부탁드리겠습니다 :) 다시한번 감사합니다!
-
미해결제주코딩베이스캠프 Code Festival: JavaScript 100제
LRU 페이징 중에서
안녕하세요, 좋은 강의 정말 잘 듣고 있습니다. 다름이 아니라 이번 강의를 수강하면서 있을 경우 temp.push(temp.shift())를 사용 하셨는데요. 해당 경우는 BAE가 temp에 있을 때 A가 hit 됬다면 temp를 AEB로 만들어서 LRU와 다른 방향으로 가는 것 같은데 확인 부탁드립니다.
-
미해결it 취업을 위한 알고리즘 문제풀이 입문 (with C/C++) : 코딩테스트 대비
질문있습니다.
int digit(int x){ int sum=0, tmp; while(x>0){ tmp=x%10; sum=sum+tmp; x=x/10; } return sum; } 질문있습니다 digit 함수 부분에서 int sum=0; 이라고 선언이 되어 있는데 "=0;" 부분을 빼고 int sum; 이렇게 선언하면 제대로된 값이 나오지 않더라구요. 왜 그러는지 알고 싶습니다.
-
미해결[리뉴얼] 파이썬입문과 크롤링기초 부트캠프 [파이썬, 웹, 데이터 이해 기본까지] (업데이트)
변수명
변수에 지정할떄 name 은 왜 ""을 쓰고 hobby 는 왜 ''를 쓰신건가요 무슨차이가 있는 건가요
-
미해결스프링 웹 MVC
질문드립니다.
안녕하세요. 아직은 부족하지만 기선님 강의듣고 열심히 공부진행하고 있습니다. 에러관련해서 질문 좀 드리려고 합니다. 소스코드를 똑같이 따라했지만, 테스트 진행시 Bean을 찾지못한다는 에러코드를 만나게 되었습니다. 강의찍으실때와의 버전이 달라서 에러가는 것인지 확인하기가 힘드네요. 그럼 잘 부탁드리겠습니다. 주소 : https://github.com/horange89/mvc_study_web 감사합니다~!