묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨Next.js 시작하기(feat. 지도 서비스 개발)
map.morph가 작동하지 않습니다.
안녕하세요 강사님, 좋은 강의 잘 듣고 있습니다!다름 아니라, 아래 캡쳐와 같이(캡쳐하기 위해 일부러 sleep을 걸고 진행했습니다.),resetMapOptions를 실행하면서, morph를 못찾겠다면서 그냥 화면을 새로고침만 해버립니다.제가 사용하는 환경은 아래와 같고,"next": "^13.4.9", "react": "^18.2.0", "react-dom": "^18.2.0", "@types/navermaps": "^3.6.5",next.config.js도 말씀주신대로 잘 따라했고/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: false, } module.exports = nextConfig모든 컴포넌트 제일 상단에 'use client'도 명시했는데 왜 안되는지 모르겠네요 ㅜㅜ 도움주시면 감사하겠습니다..! 어쩌면 버전의 문제일 수도 있을 것 같은데.. 주변에 물어볼 사람이 없어서 강사님께 한 번 고민 부탁드려봅니다!감사합니다.
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
게시글 삭제 시, 얕은 복사를 사용하지 않는 이유가 궁금합니다.
[ 게시글 삭제 saga 작성하기 ] 수강 중 질문 드립니다.이전 댓글을 추가할때는 reudce에서 case ADD_COMMENT_SUCCESS: { const postIndex = state.mainPosts.findIndex(v => v.id === action.data.postId); const post = { ...state.mainPosts[postIndex] }; // 얕은 복사 const Comments = [action.data, ...post.Comments]; const mainPosts = [...state.mainPosts]; mainPosts[postIndex] = { ...post, Comments, }; return { ...state, mainPosts, addCommentLoading: false, addCommentDone: true, addCommentError: false, }; }이런식으로 얕은 복사를 하셨는데요. case REMOVE_POST_SUCCESS: { const mainPosts = state.mainPosts.filter(v => v.id !== action.data.id); return { ...state, mainPosts, removePostLoading: false, removePostDone: true, removePostError: false, }; }게시글을 삭제할때는 왜 'state.mainPosts'를 얕은 복사해서 조작하지 않는 건지 궁금합니다.혹시 filter가 기존 state.mainPosts 을 수정하지 않으며, 새로운 배열을 return하기에 얕은 복사가 필요없는 건가요? 답변 기다리겠습니다.감사합니다 :)
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
CSR에서 로그인 유지시 깜빡임을 없애는 방법이 있나요?
강좌에서는 SSR 적용전 CSR 만으로 로그인 유지를 할 때 초기상태가 로그인 유지가 안되있는 상태기 때문에 로그인정보 부분에 깜빡임이 발생하는데요. 혹시 CSR에서도 깜빡임 없이 로그인 유지를 할 수 있는 방법이 있나요? 혹시 리액트 라이프 사이클쪽을 건드려야 하는건가요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
redux toolkit으로 action.error에 원하는 메시지 넣는 방
if (exUser) { return res.status(403).send("이미 사용중인 아이디입니다." ); }이렇게 send를 보내고redux toolkit에서는 이 메시지를 error.message에 넣는 방법을 잘 모르겠는데 어떻게 하면 되나요?위에 사진은 signup/rejected의 Action안에 있는 정보입니다
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
Ant design + React hook form 사용법
게시물 포토폴리오 리펙토링 하는 과정에서 ant design icon 사용해 만든 별점 기능에 다른 form ( wrtier, password, contents)과 동일하게 react hook form 을 사용하려고 하는데 어떻게 사용해야 할지 모르겠습니다. 도와주세여 ㅠㅠ
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
context를 두개로 나눠쓰는 이유에 대해 궁금합니다.
StateContext안에 value로 state와 dispatch 두개를 전달하면 하나의 ContextProvider만 쓰면되는데, context를 두개로 나눈 이유가 무엇인지 궁금합니다!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
포트폴리오 만드는중에 GraphQL 질문
mutation으로 게시글 삭제 구현했는데비밀번호 확인할수있는 방법이 없어서 확인절차없이 전부 삭제가 됩니다API가 바뀌어야 할것같은데 확인부탁드려요
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
post에 게시글 추가시 오류
로그인 후 post를 추가하면 아래와 같이 오류가 납니다 ㅠㅠ!화면에러내용Unhandled Runtime Error TypeError: Cannot read properties of undefined (reading '0') Source components\PostCard.js (47:27) @ PostCard 45 | <Card 46 | // post안에 Images가 있는 경우 > 47 | cover={post.Images[0] && <PostImages images={post.Images} />} | ^ 48 | actions={[ 49 | <RetweetOutlined key="retweet" />, 50 | liked ? (콘솔 추가 에러내용react-dom.development.js:19527 The above error occurred in the <PostCard> component: in PostCard (at pages/index.js:24) in div (created by Context.Consumer) in Col (at AppLayout.js:57) in div (created by Context.Consumer) in Row (at AppLayout.js:53) in div (at AppLayout.js:35) in AppLayout (at pages/index.js:21) in Home (at _app.js:13) in NodeBird (created by withRedux(NodeBird)) in Provider (created by withRedux(NodeBird)) in withRedux(NodeBird) in ErrorBoundary (created by ReactDevOverlay) in ReactDevOverlay (created by Container) in Container (created by AppContainer) in AppContainer in Root page/index.jsimport { all, fork } from "redux-saga/effects"; import postSaga from "./post"; import userSaga from "./user"; export default function* rootSaga() { yield all([fork(postSaga), fork(userSaga)]); } reducer/post.jsimport shortId from "shortid"; import produce from "immer"; import immer from "immer"; export const initailState = { mainPosts: [ { id: 1, User: { id: 1, nickname: "행복", }, content: "첫 번째 게시글 #해시태그 #해시태그2", Images: [ { src: "https://bookthumb-phinf.pstatic.net/cover/137/995/13799585.jpg?udate=20180726", }, { src: "https://gimg.gilbut.co.kr/book/BN001958/rn_view_BN001958.jpg", }, { src: "https://gimg.gilbut.co.kr/book/BN001998/rn_view_BN001998.jpg", }, ], Comments: [ { User: { nickname: "nero", }, content: "우와 개정판이 나왔군요~", }, { User: { nickname: "hero", }, content: "얼른 사고싶어요~", }, ], }, ], imagePaths: [], addPostLoading: false, addPostDone: false, addPostError: null, removePostLoading: false, removePostDone: false, removePostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, }; export const ADD_POST_REQUEST = "ADD_POST_REQUEST"; export const ADD_POST_SUCCESS = "ADD_POST_SUCCESS"; export const ADD_POST_FAILURE = "ADD_POST_FAILURE"; export const REMOVE_POST_REQUEST = "REMOVE_POST_REQUEST"; export const REMOVE_POST_SUCCESS = "REMOVE_POST_SUCCESS"; export const REMOVE_POST_FAILURE = "REMOVE_POST_FAILURE"; export const ADD_COMMENT_REQUEST = "ADD_COMMENT_REQUEST"; export const ADD_COMMENT_SUCCESS = "ADD_COMMENT_SUCCESS"; export const ADD_COMMENT_FAILURE = "ADD_COMMENT_FAILURE"; export const addPost = (data) => { return { type: ADD_POST_REQUEST, data, }; }; export const addComment = (data) => { return { type: ADD_COMMENT_REQUEST, data, }; }; const dummyPost = (data) => ({ id: data.id, content: data.content, User: { id: 1, nickname: "행복", }, }); const dummyComment = (data) => ({ id: shortId.generate(), content: data, User: { id: 1, nickname: "행복", }, Images: [], Comments: [], }); const reducer = (state = initailState, action) => { return produce(state, (draft) => { switch (action.type) { case ADD_POST_REQUEST: draft.addPostLoading = true; draft.addPostDone = false; draft.addPostError = null; break; case ADD_POST_SUCCESS: draft.mainPosts.unshift(dummyPost(action.data)); draft.addPostLoading = false; draft.addPostDone = true; break; case ADD_POST_FAILURE: draft.addPostLoading = false; draft.addPostError = action.error; break; case REMOVE_POST_REQUEST: draft.removePostLoading = true; draft.removePostDone = false; draft.removePostError = null; draft.mainPosts = state.main; break; case REMOVE_POST_SUCCESS: // draft.mainPosts = state.mainPosts.filter((v) => v.id !== action.data); draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data); draft.removePostLoading = false; draft.removePostDone = true; break; case REMOVE_POST_FAILURE: draft.removePostLoading = false; draft.removePostError = action.error; break; case ADD_COMMENT_REQUEST: draft.addCommentLoading = true; draft.addCommentDone = false; draft.addCommentError = null; break; case ADD_COMMENT_SUCCESS: { // 게시글을 찾음 const post = draft.mainPosts.find((v) => v.id === action.data.postId); // 그 게시글 댓글에다가 새로운 댓글 추가 post.Comments.unshift(dummyComment(action.data.content)); draft.addCommentLoading = false; draft.addCommentDone = true; break; // const postIndex = state.mainPosts.findIndex( // (v) => v.id === action.data.postId // ); // const post = { ...state.mainPosts[postIndex] }; // post.Comments = [dummyComment(action.data.content), ...post.Comments]; // Comments 얕은 복사 후 dummyComment 추가 // const mainPosts = [...state.mainPosts]; // mainPosts 새로운객체 생성 // mainPosts[postIndex] = post; // return { // ...state, // mainPosts, // addCommentLoading: false, // addCommentDone: true, // }; } case ADD_COMMENT_FAILURE: draft.addCommentLoading = false; draft.addCommentError = action.error; break; default: break; } }); }; export default reducer; //기본 구조 // const initailState = {}; // const reducer = (state = initailState, action) => { // switch (action.type) { // default: // return state; // } // }; // export default reducer; // 데이터를 구성하고 그에따라 액션구성후 reducer작성 그 후 화면 구성 sagas/post.jsimport { all, fork, delay, put, takeLatest } from "redux-saga/effects"; // import axios from "axios"; import { 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, } from "../reducers/post"; import { REMOVE_POST_OF_ME } from "../reducers/user"; import { ADD_POST_TO_ME } from "../reducers/user"; import shortId from "shortid"; function addPostAPI(data) { return axios.post("/api/post", data); // 서버에 요청 } function* addPost(action) { try { // const result = yield call(addPostAPI, action.data); yield delay(1000); const id = shortId.generate(); yield put({ type: ADD_POST_SUCCESS, data: { id, content: action.data, }, }); yield put({ type: ADD_POST_TO_ME, data: id, }); } catch (err) { yield put({ type: ADD_POST_FAILURE, error: err.response.data, }); } } function removePostAPI(data) { return axios.delete("/api/post", data); // 서버에 요청 } function* removePost(action) { try { yield delay(1000); yield put({ type: REMOVE_POST_SUCCESS, data: action.data, }); yield put({ type: REMOVE_POST_OF_ME, data: action.data, }); } catch (err) { yield put({ type: REMOVE_POST_FAILURE, error: err.response.data, }); } } function addcommentAPI(data) { return axios.post(`/api/post/${data.postId}/comment`, data); // 서버에 요청 } function* addComment(action) { try { // const result = yield call(addCommentAPI, action.data); yield delay(1000); yield put({ type: ADD_COMMENT_SUCCESS, data: action.data, }); } catch (err) { yield put({ type: ADD_COMMENT_FAILURE, error: err.response.data, }); } } function* watchAddPost() { yield takeLatest(ADD_POST_REQUEST, addPost); } function* watchRemovePost() { yield takeLatest(REMOVE_POST_REQUEST, removePost); } function* watchAddComment() { yield takeLatest(ADD_COMMENT_REQUEST, addComment); } export default function* postSaga() { yield all([fork(watchAddPost), fork(watchRemovePost), fork(watchAddComment)]); } components/PosdCard.jsimport React, { useState, useCallback } from "react"; import { Card, Popover, Button, Avatar, Comment, List } from "antd"; import PropTypes from "prop-types"; import CommentForm from "./CommentForm"; import { RetweetOutlined, HeartOutlined, MessageOutlined, EllipsisOutlined, HeartTwoTone, } from "@ant-design/icons"; import { useDispatch, useSelector } from "react-redux"; import PostImages from "./PostImages"; import PostCardContent from "./PostCardContent"; import { REMOVE_POST_REQUEST } from "../reducers/post"; const PostCard = ({ post }) => { const dispatch = useDispatch(); const { removePostLoading } = useSelector((state) => state.post); const [liked, setLiked] = useState(false); const [commentFormOpened, setCommentFormOpened] = useState(false); const onToggleLike = useCallback(() => { setLiked((prev) => !prev); }, []); const onToggleComment = useCallback(() => { setCommentFormOpened((prev) => !prev); }, []); const id = useSelector((state) => state.user.me?.id); // = const id = useSelector((state) => state.user.me && state.user.me.id); //const { me } = useSelector((state) => state.user); //const id = me?.id; // 로그인을 한 상태이고 아이디가 있으면 으로 해석해서 설계해보자 // const id = me && me.id; const onRemovePost = useCallback(() => { dispatch({ type: REMOVE_POST_REQUEST, data: post.id, }); }, []); return ( <div style={{ marginBottom: 20 }}> <Card // post안에 Images가 있는 경우 cover={post.Images[0] && <PostImages images={post.Images} />} actions={[ <RetweetOutlined key="retweet" />, liked ? ( <HeartTwoTone twoToneColor="#eb2f96" key="heart" onClick={onToggleLike} /> ) : ( <HeartOutlined key="heart" onClick={onToggleLike} /> ), <MessageOutlined key="comment" onClick={onToggleComment} />, <Popover key="more" content={ <Button.Group> {/* 내아이디랑 게시글작성아디가 나랑 같으면 */} {id && post.User.id === id ? ( <> <Button>수정</Button> <Button type="danger" onClick={onRemovePost} loading={removePostLoading} > 삭제 </Button> </> ) : ( <Button>신고</Button> )} </Button.Group> } > <EllipsisOutlined /> </Popover>, ]} > <Card.Meta avatar={<Avatar>{post.User.nickname[0]}</Avatar>} title={post.User.nickname} description={<PostCardContent postData={post.content} />} /> </Card> {commentFormOpened && ( <div> {/* post를 넘겨주는 이유는? 어떤 게시글에 댓글을 달건지 게시글의 id가 필요하기 때문에 */} <CommentForm post={post} /> <List header={`${post.Comments.length}개의 댓글`} itemLayout="horizontal" dataSource={post.Comments} //post.Comments가 각각 item으로 들어감 renderItem={(item) => ( <li> <Comment author={item.User.nickname} avatar={<Avatar>{item.User.nickname[0]}</Avatar>} content={item.content} /> </li> )} /> </div> )} </div> ); }; PostCard.propTypes = { post: PropTypes.shape({ id: PropTypes.number, User: PropTypes.object, content: PropTypes.string, createdAt: PropTypes.object, Comments: PropTypes.arrayOf(PropTypes.object), Images: PropTypes.arrayOf(PropTypes.object), }).isRequired, }; export default PostCard; componets/PostForm.jsimport React, { useCallback, useRef, useEffect } from "react"; import { Form, Input, Button } from "antd"; import { useDispatch, useSelector } from "react-redux"; import { addPost } from "../reducers/post"; import useInput from "../hooks/useInput"; const PostForm = () => { const { imagePaths, addPostDone } = useSelector((state) => state.post); const dispath = useDispatch(); const [text, onChangeText, setText] = useInput(""); useEffect(() => { if (addPostDone) { setText(""); } }, [addPostDone]); const onSubmit = useCallback(() => { dispath(addPost(text)); }, [text]); const imageInput = useRef(); const onClickImageUpload = useCallback(() => { imageInput.current.click(); }, [imageInput.current]); return ( <Form onFinish={onSubmit} style={{ margin: "10px 0 20px" }} encType="multipart/form-data" > <Input.TextArea value={text} onChange={onChangeText} maxLength={140} placeholder="어떤 신기한 일이?" /> <div> <input type="file" multiple hidden ref={imageInput} /> <Button onClick={onClickImageUpload}>이미지 업로드</Button> <Button type="primary" style={{ float: "right" }} htmlType="submit"> 등록 </Button> </div> <div> {imagePaths.map((v) => ( <div key={v} style={{ display: "inline-block" }}> <img src={v} style={{ width: "200px" }} alt={v} /> <div> <Button>제거</Button> </div> </div> ))} </div> </Form> ); }; export default PostForm;
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
훈훈한 자바스크립트 섹션8 질문
자꾸 저런 오류가 뜨는데 레퍼런스 코드를 보고 참고해도 똑같네요 무엇이 문제인가요>? ㅜscript.js: const todoInput = document.querySelector("#todo-input"); const todoList = document.querySelector("#todo-list"); const savedTodoList = JSON.parse(localStorage.getItem("saved-items")); const createTodo = function (storageData) { let todoContents = todoInput.value; if (storageData) { todoContents = storageData.contents; } const newLi = document.createElement("li"); const newSpan = document.createElement("span"); const newBtn = document.createElement("button"); newBtn.addEventListener("click", () => { newLi.classList.toggle("complete"); saveItemsFn(); }); newLi.addEventListener("dblclick", () => { newLi.remove(); saveItemsFn(); }); if (storageData?.complete) { newLi.classList.add("complete"); } newSpan.textContent = todoContents; newLi.appendChild(newBtn); newLi.appendChild(newSpan); todoList.appendChild(newLi); todoInput.value = ""; saveItemsFn(); }; const keyCodeCheck = function () { if (window.event.keyCode === 13 && todoInput.value !== "") { createTodo(); } }; const deleteAll = function () { const liList = document.querySelectorAll("li"); for (let i = 0; i < liList.length; i++) { liList[i].remove(); } saveItemsFn(); }; const saveItemsFn = function () { const saveItems = []; for (i = 0; i < todoList.children.length; i++) { const todoObj = { contents: todoList.children[i].querySelector("span").textContent, complete: todoList.children[i].classList.contains("complete"), }; saveItems.push(todoObj); } saveItems.length === 0 ? localStorage.removeItem("saved-items") : localStorage.setItem("saved-items", JSON.stringify(saveItems)); // if (saveItems.length === 0) { // localStorage.removeItem("saved-items"); // } else { // localStorage.setItem("saved-items", JSON.stringify(saveItems)); // } }; if (savedTodoList) { for (let i = 0; i < savedTodoList.length; i++) { createTodo(savedTodoList[i]); } } const weatherSearch = function (position) { console.log(position.latitude); console.log(position.longitude); fetch( `https://api.openweathermap.org/data/2.5/weather?lat=${position.latitude}&lon=${position.longitude}&appid=8980c5d6dfb72e97b5871537ee59d9c1` ) .then((res) => { return res.json(); }) .then((json) => { console.log(json.name, json.weather[0].description); }) .catch((err) => { console.error(err); }); }; const accessToGeo = function (position) { const positionObj = { latitude: position.coords.latitude, longitude: position.coords.longitude, }; weatherSearch(positionObj); }; const askForLocation = function () { navigator.geolocation.getCurrentPosition(accessToGeo, (err) => { console.log(err); }); }; askForLocation();
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
yup schema에 따로 타입을 지정해줘야 하나요?
vscode가 아닌 웹스톰을 사용하고 있습니다.에디터 문제인지는 모르겠지만schema에 계속 타입을 요구하길래schema: yup.ObjectSchema<any>로 넘어갔는데 괜찮겠죠?그런데 any를 안에 넣고 싶지 않아서다른 방법을 강구해봤는데 잘 안되네요 ㅠ라이브러리가 업데이트 되어서 그런건가요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
강의명에 오타가 있는 것 같습니다.
강의 너무 잘듣고 있습니다.23-05-login-chech-hoc / login-check-hoc-success앞쪽에 chech라고 오타가 있는 것 같아요
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
refetchQueries 질문
안녕하세요.강의를 복습하던 도 중 궁금한게 생겨서 질문 드립니다.onClick={qqq}으로 버튼을 클릭시 5개 정도의 데이터가 refetchqueries 되어야 하는데 모두 다 쓰면 비효율적 인 듯 합니다.많은 데이터가 refetch되어야 할 때 페이지가 새로고침 되는것이 낫다고 생각 하는데.. 1.새로고침을 한다면 어떤식으로 코드를 써야하나요?2. 둘 중 어느것이 더 효율적일까요.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
api 질문입니다.
포토폴리오 마이페이지 - 내포인트 부분을 작업하는 도중 api 질문입니다.fetchPointTransactionsOfBuying 이 내포인트 -> 구매내역 api로 알고있습니다.피그마를 보면 거기서 판매자 데이터를 가져오고있는데 오류가 뜨네요.ㅠ판매자 데이터를 가져오고 싶은데 여기서 seller {name} 이부분을 넣으면 데이터가 안가져오네요..판매자데이터가 없어서 그런건지 왜 그런지와 어떻게 해야하는지 두가지 모두 알고싶습니다.
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
redux thunk 원리 질문
1.thunk를 사용하면 함수형 action을 dispatch 했을때action을 단순히 실행하는 것으로 보이는데요. 그렇다면 아래 두 코드처럼함수형 action을 dispatch하는 방식과 함수로 감싸지 않고 하는 방식과 결과는 같다고 생각되는데 맞나요?const loginAction = () => { return (dispatch) => { dispatch(loginRequestAction()); axios.post('/api/login') .then((res) => { dispatch(loginSuccessAction(res.data)); }) .catch((err) => { dispatch(loginFailureAction(err)); }) } } const onClickLogin = () => { dispatch(loginAction()); }const onClickLogin = () => { dispatch(loginRequestAction()); axios.post('/api/login') .then((res) => { dispatch(loginSuccessAction(res.data)); }) .catch((err) => { dispatch(loginFailureAction(err)); }) }2.1이 맞다면 함수 action을 dispatch하는 방식은 편의성 때문이라고 봐도 될까요?3.강의 10:40 쯤에 thunk는 한번에 dispatch를 여러번 할 수 있게 해준다고 하셨는데thunk없이 아래처럼 여러번 쓰는 것은 문제가 될 수 있나요?// action은 임의로 지었습니다 const onClickButton = () => { dispatch({type: 'CHANGE_ID'}); dispatch({type: 'CHANGE_PASSWORD'}); }
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
sequlize같은 orm에 대한 질문
javascript는 sequilize java는 JPA같은 데이터베이스를 다루는 ORM이 있는 것 같은데 ORM에 대해 좀더 찾아보니 sql 쿼리문을 직접 작성하는 것보다 코드의 양도 줄고 유지 보수도 더 쉬운 것 같습니다.나중에 sql 쿼리문을 직접 입력하는 방식을 배워야하나 생각이 들었지만 이렇게 장점이 많은 ORM을 안 쓸 이유가 없는 것 같고 나중에 다른 백앤드 프레임 워크 예를 들어 java Spring같은 것을 배워도 JPA같은 ORM을 배우지 sql 쿼리문을 직접 넣는 방식으로는 하지 않을꺼 같습니다.그럼에도 불구하고 sql 쿼리문을 직접 넣는 방식을 배워둘 필요가 있을까요? 아니면 ORM방식에 단점이 있을까요?
-
해결됨Next.js 시작하기(feat. 지도 서비스 개발)
CSR에서 window나 document를 접근할수 없다는 부분이 이해가 잘 안갑니다.
CSR 부분 강의를 듣던도중 useEffect 바깥에서 window나 document를 접근하는 경우 문제가 되는것을 알게 되었습니다.강의에서는 초기에 값이 들어있지않은 html을 프리렌더링해서 제공한다고 설명하고 있는데 이부분에서 의문점이 있습니다. 1. 제가 기존에 알고있던 CSR은 프리렌더링을 하지 않는걸로 알고 있었는데그러면 결국 프리렌더링 이라는것은 SSR SSG ISR CSR 까지 모두 적용되고있는 방식인가요? 2. 프리렌더링을 하는것이 맞다면, 저같은 경우 일전에 했던 리액트 프로젝트를정적 파일을 저장하는 aws S3에 배포해본적이 있는데 이것이 가능했던 이유가서버에서 렌더링을 진행하기 않기때문에 가능한것이라고 알고 있었습니다.그렇다면 S3에서 CSR만 적용된 리액트 프로젝트를 배포할수있는 이유는 무엇인가요?
-
해결됨Next.js 시작하기(feat. 지도 서비스 개발)
보안 관점에서의 SSR과 CSR 선택지
개인정보 페이지같은 보안이 중요한경우 CSR보다 SSR을 적용하는게 좋다고 하셨는데 오히려 검색엔진의 정보수집때문에(SEO) 개인정보를 노출할수도 있지 않나요? 검색을 해보니 이런 주장을 하는 분들도 계셔서요요청을 최소화 하냐 vs 정보수집에 노출되지 않도록 하냐 이 두개의 충돌같은데 어떻게 생각하시는지 궁금합니다.
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
state is not defined 에러가 뜹니다
이 에러는 왜 발생한 건가요?구글에 검색도 해봤는데 안나오네요도와주세요 ㅠreducers/user.js 코드입니다export const initialState = { } export const loginAction = (data) => { return { type: 'LOG_IN', data, } } export const logoutAction = () => { return { type: 'LOG_OUT', } } const reducer = (State = initialState, action) => { switch (action.type) { case 'LOG_IN': { return { ...state, isLoggedIn: true, user: action.data, }; } case 'LOG_OUT': { return { ...state, isLoggedIn: false, user: null, }; } default: return state; } }; export default reducer; 터미널창 오류 메세지 입니다error - reducers/user.js (37:12) @ reducererror - ReferenceError: state is not defined at reducer (webpack-internal:///./reducers/user.js:39:13) at /Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/node_modules/redux/lib/redux.js:476:24 at Array.forEach (<anonymous>) at assertReducerShape (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/node_modules/redux/lib/redux.js:474:25) at combineReducers (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/node_modules/redux/lib/redux.js:539:5) at eval (webpack-internal:///./reducers/index.js:15:75) at Object../reducers/index.js (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/.next/server/pages/_app.js:33:1) at __webpack_require__ (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/.next/server/webpack-runtime.js:33:42) at eval (webpack-internal:///./store/configureStore.js:9:67) at Object../store/configureStore.js (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/.next/server/pages/_app.js:66:1) { page: '/'} 35 | } 36 | default:> 37 | return state; | ^ 38 | 39 | } 40 | };event - compiled client and server successfully in 100 ms (1518 modules)4. WrappedApp created new store with withRedux(NodeBird) { initialState: undefined, initialStateFromGSPorGSSR: undefined }ReferenceError: state is not defined at reducer (webpack-internal:///./reducers/user.js:39:13) at /Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/node_modules/redux/lib/redux.js:476:24 at Array.forEach (<anonymous>) at assertReducerShape (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/node_modules/redux/lib/redux.js:474:25) at combineReducers (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/node_modules/redux/lib/redux.js:539:5) at eval (webpack-internal:///./reducers/index.js:15:75) at Object../reducers/index.js (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/.next/server/pages/_app.js:33:1) at __webpack_require__ (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/.next/server/webpack-runtime.js:33:42) at eval (webpack-internal:///./store/configureStore.js:9:67) at Object../store/configureStore.js (/Users/hyeonyeongjeong/Documents/2023project/nodebird/prepare/front/.next/server/pages/_app.js:66:1) http://localhost:3060 에 뜬 오류 입니다 Server ErrorReferenceError: state is not definedThis error happened while generating the page. Any console logs will be displayed in the terminal window.Sourcereducers/user.js (35:12) @ reducer 33 | } 34 | default: > 35 | return state; | ^ 36 | 37 | } 38 | };
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
ApolloError: null value in column "writer" of relation "board" violates not-null constraint
하드코딩으로 값을 넣었을때는 잘진행이 되었는데요ㅠㅠ 안에 값을 지정하니 에러가 계속뜨네요 아마 이 부분 에서 고쳐야 할 점이 있는 것 같은데 똑같이 작성을 한 것 같음에도 동작이 안되서 질문드려봅니다
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
윈도우 WSL 에서 yarn dev 후에 수정한 index.js 반영
브라우저에서 새로고침해도 수정된 index.js가 반영이 안되던데 왜그러는걸까요 ㅠ