묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
이 프로젝트를 배포해서 실서비스로 사용할 수 있나요?
새로운 기능을 붙여서 배포해 서비스하려고 합니다.예외처리 같은 것을 추가해서 배포해도 문제가 없을까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
Server Cannot be Reached
vscode에서 계속 오류 떠서 playground에서 보내보았는데 server cannot be reached가 계속 뜨면서docs도 무한 로딩중이에요 ㅠ
-
해결됨Next.js 시작하기(feat. 지도 서비스 개발)
SSR vs SSG
안녕하세요 강의 정말 잘 듣고있습니다!SSG가 SSR보다 사용자 경험이 좋은건 이해했습니다! 근데 한가지 더 의문점인건 SSG가 아니고 SSR을 선택해야하는 케이스가 있을까요? 라는 의문이 듭니다! 예를 들면 검색결과 페이지라던가 매번 매순간 페이지의 내용이 달라지는 페이지라면 SSG가 아니고 SSR로 만드는게 더 적합한 그런 케이스가 실무에서 있을까요?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요, 리덕스를 통해서 좋아요 기능구현을 해보고 있는데 디스패치와 충돌이 나는것 같습니다.
안녕하세요, 좋은 강의 감사드립니다. 제가 좋아요 기능구현을 해보고 있는데, 클릭이 되면 state(clicked)를 통해 true로 만들어서 빨강색으로 만들고 1를 추가시키고 다시 클릭하면 false가 되면서 검게 되고 1을 감소하게 하는 기능을 구현하려고 하는데요. 각각 빨/검 1추가시키기는 되는데 둘이 합치려니까 1을 올리면서 리렌더링이 되고 clicked라는 스테이트가 바로 false로 초기화되버리는것 같습니다. 혹시 방법이있을까요? 뭐라고 검색해야 할지 감이 잘안잡혀서 질문남깁니다. const Heart = ({ pageId, id, currentIndex, postId, }) => { const dispatch = useDispatch(); const { coursePosts } = useSelector((state) => state.coursePost); const { clicked, setClicked } = useState(false); const heart = coursePosts[currentIndex].CourseReview[id].heart; const onClickHeart = () => { if (!clicked) { dispatch(coursePostActions.clickCourseReviewHeart({ pageId, postId })) } else { dispatch(coursePostActions.clickCourseReviewHeartAgain({ pageId, postId })) } setClicked(!clicked); } return ( <button type="button" onClick={onClickHeart} className={clicked === true ? styles.active : ""}> <FontAwesomeIcon icon={faHeart} /> <span>{heart}</span> </button> ); }; export default CourseReviewItems;const coursePostSlice = createSlice({ name: "coursePost", initialState, reducers: { clickCourseReviewHeart(state, action) { const postIndex = state.coursePosts.findIndex((v) => v.id === Number(action.payload.pageId)); const reviewIndex = state.coursePosts.findIndex((v) => v.id === action.payload.postId); state.coursePosts[postIndex].CourseReview[reviewIndex].heart += 1 }, clickCourseReviewHeartAgain(state, action) { const postIndex = state.coursePosts.findIndex((v) => v.id === Number(action.payload.pageId)); const reviewIndex = state.coursePosts.findIndex((v) => v.id === action.payload.postId); state.coursePosts[postIndex].CourseReview[reviewIndex].heart -= 1 } }
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
강좌 끝나고 댓글 수정 기능 만들어 보고 있습니다
{commentFormOpened && ( <div> {commentEditMode ? <CommentContent post={post} commentEditMode={commentEditMode} onClickUpdateComment={onClickUpdateComment} onCancelUpdateComment={onCancelUpdateComment} /> : ( <> <CommentForm post={post} /> <CommentContent post={post} commentEditMode={commentEditMode} onClickUpdateComment={onClickUpdateComment} onCancelUpdateComment={onCancelUpdateComment} /> </> ) } </div> )} //PostCard.js import React, { useCallback, useEffect, useState } from 'react'; import { Avatar, Comment, List, Input, Button, Popover } from 'antd'; import { useDispatch, useSelector } from 'react-redux'; import Link from 'next/link'; import PropTypes from 'prop-types'; import { EllipsisOutlined } from '@ant-design/icons'; import { UPDATE_COMMENT_REQUEST } from '../reducers/post'; const {TextArea} = Input; const CommentContent = ({ post, onCancelUpdateComment, commentEditMode, onClickUpdateComment }) => { const dispatch = useDispatch(); const id = useSelector((state) => state.user?.me.id); const { updateCommentLoading, updateCommentDone } = useSelector((state) => state.post); const [editText, setEditText] = useState(post.Comments.content); useEffect(() => { if (updateCommentDone) { onCancelUpdateComment(); } }, [updateCommentDone]); const onChangeCommentText = useCallback((e) => { setEditText(e.target.value); }, []); const onChangeComment = useCallback(() => { dispatch({ type: UPDATE_COMMENT_REQUEST, data: { PostId: post.id, CommentId: post.Comments.id, UserId: id, content: editText, }, }); }, [post, id, editText, post.Comments.id]); return ( <div> {commentEditMode ? ( <> <TextArea value={editText} onChange={onChangeCommentText} /> <Button.Group> <Button loading={updateCommentLoading} onClick={onChangeComment}>수정</Button> <Button type="danger" onClick={onCancelUpdateComment}>수정 취소</Button> </Button.Group> </> ) : <List header={`${post.Comments.length}개의 댓글`} itemLayout="horizontal" dataSource={post.Comments} renderItem={(item) => ( <li> <Comment actions={[<Popover key="more" content={ <Button.Group> {id && item.User.id === id ? ( <> <Button onClick={onClickUpdateComment}>수정</Button> <Button type="danger"> 삭제 </Button> </> ) : ( <Button>신고</Button> )} </Button.Group> } > <EllipsisOutlined /> </Popover>,]} author={item.User.nickname} avatar={ <Link href={`/user/${item.User.id}`}> <a><Avatar>{item.User.nickname[0]}</Avatar></a> </Link> } content={item.content} /> </li> )} /> } </div> ) } CommentContent.propTypes = { post: PropTypes.shape({ id: PropTypes.number.isRequired, Comments: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.number.isRequired, content: PropTypes.string.isRequired, })) }), onCancelUpdateComment: PropTypes.func.isRequired, onClickUpdateComment: PropTypes.func.isRequired, commentEditMode: PropTypes.bool }; CommentContent.defaultsProps = { commentEditMode: false, } export default CommentContent; //CommentContent.js case UPDATE_COMMENT_REQUEST: draft.updateCommentLoading = true; draft.updateCommentDone = false; draft.updateCommentError = null; break; case UPDATE_COMMENT_SUCCESS: draft.updateCommentLoading = false; draft.updateCommentDone = true; const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Comments = post.Comments.find((v) => v.id === action.data.CommentId); post.Comments = post.Comments.find((v) => v.id === action.data.UserId); post.Comments.content = action.data.content; break; case UPDATE_COMMENT_FAILURE: draft.updateCommentLoading = false; draft.updateCommentError = action.error; break; //reducers/post.js function updateCommentAPI(data) { return axios.patch(`/post/${data.PostId}/comment`, data); } function* updateComment(action) { try { const result = yield call(updateCommentAPI, action.data); yield put({ type: UPDATE_COMMENT_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: UPDATE_COMMENT_FAILURE, error: err.response.data, }); } } // sagas/post.js router.patch('/:postId/comment', isLoggedIn, async (req, res, next) => { // PATCH post/2/comment try { await Comment.update({ content: req.body.content, }, { where: { PostId: req.params.postId, UserId: req.user.id, }, }); res.status(200).json({ PostId: parseInt(req.params.postId, 10), UserId: req.user.id, content: req.body.content, }); } catch (error) { console.error(error); next(error); } }); //routes/post.js이렇게 PostId, UserId, content가 보내지고, 실패가 뜨면서 새로고침을 하면 해당 글에 달았던 댓글들이 모두 다 "zzz"로 변경되어 있습니다. 그래서 CommentId를 보내줘야 될 거 같은데 여기서 막혀서 감이 도무지 잡히질 않습니다.
-
해결됨Next.js 시작하기(feat. 지도 서비스 개발)
NoSSR 관련한 질문입니다:)
강의후반부에 window에 접근하는 상황인 NoSSR을 수강하면서 해당 상황이 어떤 케이스인지 확인차 질문 남깁니다.Next에서 window나 document같이 WebAPI에 기재된 객체들에 접근하는 코드를 쓸 때 백엔드 단에서 접근이 불가하니, dynamic을 사용한다 정도로 이해하면 되려나요?.? 이와는 다르게, useEffect의 경우 브라우저에서 렌더가 될 때 코드가 돌아가니까 WebAPI에 접근이 가능하다 정도로 이해하면 되려나요?.?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
data에 utility type으로 명시할 때 문제
섹션 8 강의 1:02:21 부분에서 문제가 발생했습니다 ㅠ utility type을 배우기 전 이렇게 해놨을 때는 문제가 없었습니다. 근데 강의처럼 바꾸어주니까 문제가 발생했고 IBoardWriteUIProps의 data 뒤에 물음표 때문인가해서 물음표를 주니까 container는 괜찮아 졌는데 presenter에서는 아직 defaultValue에 문제가 있다고 표시가됩니다.. data?.fetchBoard.writer -> data?.fetchBoard?.writer 이런식으로 바꿔줘도 defaultValue에 문제 발생... 분명 이렇게 타입명시를 잘 해줬는데 왜 강의처럼 안되는 건지 모르겠습니다..
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
"한줄씩 디버깅하는 법" 질문있습니다.
안녕하세요. 기존에는(강의 듣기전) visual studio 2019 버전 C++로 코드를 작성하고 F10, F5 등으로 한줄씩 디버깅을 진행했던 경험이 있습니다.그런데 프론트엔드쪽에 관심을 가지면서 해당 강의를 수강하고 있는데, visual studio code를 통해 html, css와 javascript 역시 F10과 F5를 활용하면 한줄씩 디버깅하면서 해당 변수에 어떠한 값이 할당되는지 알 수 있을까요?예를 들면 싸이월드 만들기 4탄에서 javascript를 통해 끝말잇기 게임을 만들었는데 해당 javascript의 동작 과정을 한줄씩 디버깅 해보고 싶습니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
yarn generate 실행시 YAML Error 발생하는 분들!
참고 - https://stackoverflow.com/questions/19975954/a-yaml-file-cannot-contain-tabs-as-indentationcodegen.yaml 파일에서 들여쓰기를 모두 지우고 띄어쓰기(space 키) 2번씩으로 바꿔주시면 해결됩니다!(plugins와 config는 띄어쓰기 4번, - typescript와 typesPrefix: I 는 띄어쓰기 6번, 이런식으로)
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
@graphql-codegen/cli 설치시 EPERM: operation not permitted 에러나시는 분들!
섹션8 - graphql-codegen 설치 및 실습과 기타 타입 영상시간 0:18:13 구간에서 yarn add --dev @graphql-codegen/cli 설치 시 에러가 날 경우 해당 프로젝트의 node_modules 를 삭제yarn install 명령어로 node_modules를 재설치다시 yarn add --dev @graphql-codegen/cli 로 설치이렇게 해주면 에러없이 잘 설치가 됩니다! 혼자 검색도 해보고 컴퓨터랑 vscode 껐다켜보기도 하고 여러 시도 끝에 겨우 해결했네요 ㅠ 다른 수강생분들은 저처럼 시간낭비하지 않으시길 바라겠습니다!
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
댓글 기능 컴포넌트 구조
강사님 안녕하세요~!자유게시판의 댓글 기능과 중고마켓의 댓글 기능을 리팩토링하면서 공통으로 뺄 수 있는 부분은 최대한 빼보려고 하고 있는데요. 댓글생성, 댓글수정 등등 각각의 뮤테이션도 모두 다르고 조건(회원/비회원, 자유게시판/중고마켓) 따라 보여지는 부분도 상당히 달라서 최대한 공통인 부분으로 잘게 쪼개봤는데도 코드 길이가 너무 길어집니다 ㅜㅜ 받아오는 데이터의 타입들도 각기 달라서 점점 더 미궁속으로 빠지고 있습니다 .... 자유게시판과 중고마켓 각 디렉토리에서 댓글 컴포넌트를 만들고 그 안에서 공통 UI 컴포넌트와 사용하는 뮤테이션들을 추가하는 방식으로 하고 있는데 계속 생각해봐도 비효율적이라는 생각이 들어서요 ..데이터 타입, 뮤테이션, 렌더링 조건 등등이 모두 다른 이런 상황에서 공통으로 기능을 어떻게 빼야할지 조언 부탁드려도 될까요?🥹
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
로그인한 회원이 자유게시판 글 작성할 때 오류가 납니다
안녕하세요 강사님 !로그인한 회원이 자유게시판에 글을 쓸 때 작성자가 존재하지 않는다는 오류가 나옵니다.피그마 화면에 작성자와 비밀번호 인풋이 없어서 똑같이 비회원일 때만 보여주도록 했고 뮤테이션을 날릴 때에도 필수값이 아니라 비회원 글작성 뮤테이션을 똑같이 썼습니다. 그런데 작성자가 존재하지 않는다는 오류가 나요. 플레이그라운드에서 토큰을 넣고 똑같이 시도해봤는데 CreateBoardInput에 작성자랑 비밀번호를 다 넣어줘야 정상적으로 등록이 되더라구요. 타입을 보면 필수가 아닌데.. 제가 놓친 부분이 어디인지 잘 모르겠습니다 ㅠㅠ 강의는 완강했습니다! +댓글등록도 해보는데 댓글은 작성자가 없다는 오류 없이 정상적으로 등록되지만 user 정보가 같이 저장되지 않습니다. 저는 토큰에서 user 정보를 알아서 가져가는 줄 알았는데 그게 아닌건지, 그게 맞는데 제 코드가 잘못된건지 모르겠습니다 😂
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
툴킷에서 SSR적용하는 부분 질문있습니다.
강의 들은 부분을 툴킷으로 다시 만들어보는중 SSR 적용하는 부분 질문있습니다. 초기 로딩후 로그인은 정상적으로 동작하는데요 새로고침하고 로그인 풀린 화면이 나오고 다시 로그인하면 로그인되지 않습니다. 코드는 다음과 같이 작성했습니다. // toolkit/index.ts import axios from 'axios'; import userSlice, { UserInitialState } from './user'; import postSlice, { PostState } from './post'; import { AnyAction, CombinedState } from '@reduxjs/toolkit'; import { combineReducers } from 'redux'; import { HYDRATE } from 'next-redux-wrapper'; axios.defaults.baseURL = 'http://localhost:3065'; axios.defaults.withCredentials = true; export interface IState { user: UserInitialState; post: PostState; } const rootReducer = ( state: IState, action: AnyAction ): CombinedState<IState> => { switch (action.type) { case HYDRATE: return action.payload; default: { const combinedReducer = combineReducers({ user: userSlice.reducer, post: postSlice.reducer, }); return combinedReducer(state, action); } } }; export default rootReducer;툴킷에 HYDRATE를 적용하고 // store/configureStore.ts import { AnyAction, Reducer, configureStore } from '@reduxjs/toolkit'; import { createWrapper } from 'next-redux-wrapper'; import rootReducer, { IState } from '../toolkit'; const makeStore = () => configureStore({ reducer: rootReducer as Reducer<IState, AnyAction>, devTools: true, middleware: (getDefaultMiddleware) => getDefaultMiddleware(), }); export default createWrapper<AppStore>(makeStore, { debug: process.env.NODE_ENV !== 'production', }); export type AppStore = ReturnType<typeof makeStore>; export type RootState = ReturnType<typeof rootReducer>; export type AppDispatch = AppStore['dispatch']; 리덕스 래퍼 부분은 저렇게 만들고 //pages/index.tsx export const getServerSideProps: GetServerSideProps = wrapper.getServerSideProps((store) => async ({ req }) => { const cookie = req ? req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; if (req && cookie) { axios.defaults.headers.Cookie = cookie; } await store.dispatch(loadPostAction()); await store.dispatch(loadUserAction()); }); export default Home; // toolkit/user.ts export const loadUserAction = createAsyncThunk( 'user/loadUser', async (data) => { const response = await axios.get(`/user/${data}`); return response.data; } ); .addCase(loadUserAction.fulfilled, (draft, action) => { draft.loadUserLoading = false; draft.loadUserDone = true; draft.me = action.payload || null; }) 툴킷쪽은 이런식으로 작성했습니다.
-
해결됨
(Next.js) 전역 상태 관리할 때...
현재 Next 13 APP라우팅 방식으로 프로젝트를 하고 있습니다.프로젝트에서 전역상태관리를 위해서 ContextAPI를 이용하고 Provider를 아래와 같은 방법으로 적용을 시켜줬습니다.(layout.js)return ( <html> <body> <Provider> {children} </Provider> </body> </html> )(Provider.js)'use client' //생략 export default function Provider({children}) { return ( <ContextProviber> {children} </ContextProviber> ) }이렇게 Provider로 감싸주었더니 'use client'를 사용하여 CSR환경에서 작동해야 한다고 하더라구요. 그래서 "use client"까지 적용했는데이렇게 'use client'로 작성된 component로 감싸면 이 아래오는 모든 코드가 SCR로 적용되는 건가요?만약 이게 맞다면 SSR의 장점을 전혀 살리지 못하는 것인데 어떻게 해결할 수 있나요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
CSR이더라도 메인 홈페이지는 검색이 되는게 맞죠?
제로쵸 선생님 너무 궁금해서 질문드립니다 ㅜ흔히 CSR은 SEO 최적화가 어렵다고 하는데 최적화가 어렵지 기본적인 메인 홈페이지는 검색이 되는게 맞죠?시작이 되는 index.html의 소스의 <head></head> 태그안에 meta 태그 속성 중 네임과 컨텐트,그리고 타이틀 태그로 인해서, 기본적인 메인 홈페이지는 검색이 되는것 맞나요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
동적라우팅
동적 라우팅 폴더를 생성해서 강의 따라서 입력했는데 경로에 들어가면 qqq 폴더 관련하여 에러가 뜹니다ㅠ 다르게 입력한건 없는데 왜 이런걸까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
라우터 푸시
섹션 27 라우팅 강의를 따라서 진행하며useRouter 를 사용해 router.push(/폴더경로) 를 입력하고 버튼 onClick 이벤트에 바인딩 해주었습니다.각 버튼 클릭시 url 은 정상적으로 이동되는걸로 나오는데, UI 화면은 바뀌지가 않습니다.콘솔로그를 찍어봐도 클릭 이벤트도 정상작동 되는걸로 나오는데.. 도대체 뭐가 문제일까요ㅠAPP.js 에서 컴포넌트를 바꿔봤을 때도 각 버튼 별 이동 페이지들이 문제없이 뜨는걸 확인했습니다.url 이동시 화면만 새로고침되지 않는 것 같아요..이렇게 경로 변경만 되고 화면은 리렌더링 되지 않는 경우엔 어떻게 해결해야하나요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
createBoard 질문있습니다
const 나의그래프큐엘셋팅 = gql` mutation createBoard($createBoardInput: CreateBoardInput!) { createBoard(createBoardInput: $createBoardInput) { _id } } `;처음에 단순히 결과값을 _id 만을 받도록 코딩했었는데 이 결과 등록하기 버튼을 누르면 create 된 값이 화면에 리렌더링 되기는 했지만 글의 갯수가 10개로 고정이 됬습니다. 예를들어 이미 1,2,....,9,10 이라는 글이 화면에 보이는 상황에서 a라는 글을 추가하면 a,1,2,...,9,10 으로 화면이 보이는 것이 아니라 a,1,2...,8,9 까지만 보입니다.const 나의그래프큐엘셋팅 = gql` mutation createBoard($createBoardInput: CreateBoardInput!) { createBoard(createBoardInput: $createBoardInput) { _id writer title contents } } `;그리고 다음과 같이 코드를 수정하게 되면 원하는 결과가 나오게 되었는데 무슨 차이가 있는 것일까요?
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
게시판 API 만들기 - cors 빨간 밑
안녕하세요 선생님!강의 게시판 API 만들기 실습을 보고 ApolloServer 안에 cors: true를 적었는데 빨간 밑줄이 생기면서 '{ typeDefs: string; resolvers: { Query: { fetchBoards: () => Promise<Board[]>; }; Mutation: { createBoard: (parent: any, args: any, context: any, info: any) => Promise<string>; }; }; cors: boolean; }' 형식의 인수는 'ApolloServerOptions<any>' 형식의 매개 변수에 할당될 수 없습니다. 개체 리터럴은 알려진 속성만 지정할 수 있으며 'ApolloServerOptionsWithTypeDefs<any>' 형식에 'cors'이(가) 없습니다. 라고 오류가 떴는데 아무리 찾아봐도 해결방법을 몰라 글을 올리게 되었습니다
-
미해결[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
회원가입 과제 피드백 부탁드립니다!
안녕하세요 강의를 들은 이후 나름 열심히 과제를 수행중이었는데, 도저히 해결되지 않는 문제도 있고, 피드백을 받고 싶어 질문을 남깁니다! 여성, 남성 선택 박스의 가운데 공백이 아무리 해도 생기질 않습니다.2. 이용 약관 아래에 <hr> 태그가 먹히지 않습니다. 입력하면 줄이 생겨야하는데 생기지 않고 있어요. (정확한 위치는 사진 속 주석 참고 부탁드립니다.) 3. 아래쪽 전체적 간격이 뭔가 어정쩡한 느낌입니다..4. 코드를 보시고 혹시 제가 고쳐야 할 점이나, 공부가 필요한 부분이 있을까요?