묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨애플 웹사이트 인터랙션 클론!
오류가 나는데 뭐가 문제인지 모르겠어요ㅠㅠ
(() => { let yOffset = 0; // window.pageYOffset 대신 쓸 변수 let prevScrollHeight = 0; // 현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합 let currentScene = 0; // 현재 활성화된(눈 앞에 보고있는) 씬(scroll-section) const sceneInfo = [ { // 0 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-0') } }, { // 1 type: 'normal', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-1') } }, { // 2 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-2') } }, { // 3 type: 'sticky', heightNum: 5, //브라우저 높이의 5배로 scrollHeight 세팅 scrollHeight: 0, objs: { container: document.querySelector('#scroll-section-3') } } ]; function setLayout() { // 각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++){ sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } //console.log(sceneInfo); } function scrollLoop() { for (let i = 0; i < currentScene; i++) { prevScrollHeight += sceneInfo[i].scrollHeight; } //console.log(prevScrollHeight); if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { currentScene++; } if (yOffset < prevScrollHeight) { currentScene--; } console.log(currentScene); } window.addEventListener('resize', setLayout); window.addEventListener('scroll', () => { yOffset = window.pageYOffset; scrollLoop(); }); setLayout(); })();
-
미해결[개정판] 파이썬 머신러닝 완벽 가이드
full_matrices인자의 의미
안녕하세요! 질문하나만 드리겠습니다! scipy의 svd 함수에서 full_matrices 인자가 True/ False에 따라 SVD분할할 때 분할한 행렬들의 차원수가 달라지는 것 같은데.. T/F값에 따라 궁극적으로 미치는 영향이 무엇인가요..? 이것도 일종의 Compact SVD인건가요..? 저 U, Sigma, Vt의 각 shape를 반환해보면 모두 (6,6) , (6,), (6,6)으로 반환되는거 보면 차원 축소는 아닌 것 같은데...밑의 주석으로 쓴 것은 documentation을 보고 해석해놓은 건데.. 정확히 저게 어떤 영향을 미치는지 궁금합니다..!
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
질문있습니다.
우선적으로 좋은 강의 주셔서 감사하다는 말씀 먼저 드리고싶구요 ㅎ 이전에는 next 를 사용하지 않고 react 를 사용하는 강의라던지 블로그라던지 등등을 봤었는데요 . 지금은 제로초 님 강의에서 next 를 들으면서 드는생각이 코드적인 부분에서 크게 차이가 없는데 , 물론 앞서 설명하신 부분에 대해서 이해는 했지만 코드적인 부분에서 무엇이 다른지 잘 이해가 안갑니다. import React from "react"; 를 적지 않는거,..? 라고 우선 기억을 하고있는데요 .무슨 차이가 있을까요
-
미해결C 프로그래밍 - 입문부터 게임 개발까지
256이 궁금합니다.
/*char str[256]; scanf_s("%s", str, sizeof(str)); printf("%s\n", str);*/ 이부분에서 왜 하필 크기를 정해줄 때 256이라는 숫자를 사용하는거에요?? 그냥 아무 숫자나 큰 걸로 고르신건가요??
-
해결됨MQTT 사물인터넷 통신 프로젝트 (Arduino, MQTT, Node.js, mongoDB, Android)
mqtt전송
안녕하세요! 좋은 강의를 만나 프로젝트를 무난하게 진행하고 있던 와중 궁금한 점이 생겨 이렇게 질문드립니다. 혹시 publisher가 서버에 json데이터만이 아닌 이미지파일을 전송하려고 하는것도 가능할까요??!
-
미해결리버스쿨 Level3 - 안드로이드 모바일 리버싱
녹스를 제공받은 vmware 리눅스 os에서 실행을 시키는건가요 아니면 기존에 사용중인 윈도우에서 실행을 시키는건가요>??
윈도우에서 녹스를 실행시켰을 때 ip가 172로 잡히고 리눅스 ip는 192여서 adb connect 를 했을 때 연결이 안되는것 같아요.. 녹스를 리눅스로 파일을 옮기고 해봤는데도 exe라서 실행이 안되는것 같고 해결방안을 좀 알 수 있을가요.. 4일동안 구글링을 해봤는데도 안나와서 질문드려요 ㅠㅠ
-
미해결유니티(Unity)로 시작하는 게임개발: Part 2. C# 프로그래밍 입문
유니티 프로그램 다운로드 후 프로젝트 실행할 때
프로젝트에서 새로 생성을 클릭 후 생선버튼을 눌렀을 때 "에디터 실행 중 오류가 발생했습니다. 라이선스가 유효하지 않습니다" 라고 합니다. 어떻게 해야 되나요?
-
미해결쉽게 배우는 Webpack
리뉴얼 강의 쿠폰 발급 부탁드립니다.
늦게 봐서 확인을 못헀습니다 ㅜㅜ 부탁드려도 될까요?
-
JSP 웹 쇼핑몰 프로그래밍 기본 과정(JSP WEB Programming)
mvc2 도 그렇고 이 강의도 유튜브에 업로드 되어 있는 것 같은데요..
삭제된 글입니다
-
미해결[개정판] 딥러닝 컴퓨터 비전 완벽 가이드
GPU와 TPU차이
최근에 코랩에 TPU도 추가가 된거 같은데 GPU를 썻을때와 TPU를 썻을때에 성능차이가 있을까요?
-
미해결애플 웹사이트 인터랙션 클론!
강의등록만해놓고진도가안나가네요(@^^@)
열심히 해야하는데... 업무핑계로 계속 미루고있네요 ㅠㅠ 강의는 초반만 조금 봤는데... 디테일하게 알려주셔서 너무 좋더라구요... 다음강의도 좀더 좋은 컨텐츠로 부탁드립니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요. useEffect 관련 질문 드립니다!
안녕하세요 useEffect hooks 관련해서 질문드립니다.useEffect를 하지 않았을경우 당연히 렌더는 1번되지만 useEffect로 디스패치를 하면 렌더링이 3번됩니다. 이게 정상인지 아닌지 잘 모르겠어서 질문드립니다..
-
미해결[리뉴얼] 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강 듣고있는데 이거 혹시 강의자료는 없나요???