묻고 답해요
167만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
postcss-preset-env대신 postcss autoprefixer 설치 해도 될까요?
tailwind.css 공식 홈페이지에는 postcss와 autoprefixer 설치가 나오던데 저걸 대신 설치 해도 될까요?https://tailwindcss.com/docs/guides/nextjs2번째 질문으로 위에 tailwind css 공식 홈페이지대로 하면 잘 되는데 저거 대로 진행해도 될까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
svg 파일 관련 질문드립니다.
svg 파일을 사용하기 위해서 @svgr/webpack 설정하고 import 해서 컴포넌트처럼 사용하고있는데요.이미지 파일을 public에 두고 사용중인데 svg 파일은 제가 찾아본바로는 이미지 파일이라기보다 html/css에 가깝고 컴포넌트처럼 import 해서 사용하면 이미지가 아니기때문에 public 폴더가 아닌 다른 폴더로 빼서 파일을 관리해야될것같은데 svg 파일은 어디에 두는게 좋은가요? public 폴더에 놔두고 사용하는게 좋은지 아니면 따로 assests 폴더등을 만들어서 따로 관리하는게 좋은지 궁금합니다. !
-
미해결만들면서 배우는 프론트엔드 DO IT 코딩 (Next.js, Typescript)
배포 관련 질문
안녕하세요, 강사님.강의를 듣고 있는 학생입니다.제가 Next.js를 가지고 개발을 하고 있는데 배포를 할 때, Next.js로만 dockerfile을 작성해서 배포하면 되는 걸까요? 아니면 Vue.js와 같은 SPA 처럼 Nginx를 포함시켜서 dockerfile을 작성하고 각각 컨테이너를 만들고 연결시켜주면 되는 건가요?제가 이쪽 지식이 부족해서 수업 이외에 다른 것도 여쭤보게 되었는데,, 만약 답변이 가능하시다면 염치불구하고 물어보고 싶습니다 ㅠㅠ배포 환경은 EC2와 같은 가상컴퓨터환경(클라우드)에서 쿠버네티스를 만들고 그 위에 올릴거 같은데,, 어떤 구조를 참고하면 좋을지 고민이에요.
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
압축파일 말고, git이나 코드 복사할 수 있는 링크는 없나요?
생각보다 파일의 용량이 커서 압축이 안 풀립니다..ㅎㅎ복사해도 되는 부분들은 복사해서 쓰고 싶은데 있으면 공유 부탁드릴게요
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
loadPosts 에러
안녕하세요 선생님.무한스크롤링 강의까지 수강 했습니다. faker로 dummydata 만들어서 무한 스크롤링 구현했는데 중간에 뭘 잘못 건드렸는지 에러가 뜨네요..제 생각에는 아래의 에러 부분 문제 인거 같아서 post saga 부분과 reducer를 오랜 시간 봤는데 어디가 잘못 된건지 모르겠습니다... react_devtools_backend.js:2655 The above error occurred in task loadPosts created by throttle(LOAD_POSTS_REQUEST, loadPosts) created by watchLoadPosts created by postSaga created by rootSagaTasks cancelled due to error:throttle(LOAD_POSTS_REQUEST, loadPosts)watchAddPostwatchRemovePostwatchAddCommentuserSaga
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
ADD_COMMENT_FAILURE 에러
error - ./sagas/post.jsAttempted import error: 'ADD_COMMENT_FAILURE' is not exported from '../reducers/post'.와 같이 오류 코드가 뜹니다. 간단한 문제인거 같은데 해결이 안되네요.아래는 두 코드입니다. ./reducer/post.jsimport shortId from 'shortid'; import faker from 'faker'; import produce from '../util/produce'; export const initialState = { mainPosts: [], imagePaths: [], hasMorePosts: true, loadPostsLoading: false, loadPostsDone: false, loadPostsError: null, addPostLoading: false, addPostDone: false, addPostError: null, removePostLoading: false, removePostDone: false, removePostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, }; // -> index.js export const generateDummyPost = (number) => Array(number).fill().map(() => ({ id: shortId.generate(), User: { id: shortId.generate(), nickname: faker.name.findName(), }, content: faker.lorem.paragraph(), //아무런 글자 Images: [{ src: faker.image.image(), //공간만 차지할꺼면 placeholder }], Comments: [{ User: { id: shortId.generate(), nickname: faker.name.findName(), }, content: faker.lorem.sentence(), }], })); 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 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) => ({ type: ADD_POST_REQUEST, data, }); export const addComment = (data) => ({ type: ADD_COMMENT_REQUEST, data, }); const dummyPost = (data) => ({ id: data.id, content: data.content, User: { id: 1, nickname: '제로초', }, Images: [], Comments: [], }); const dummyComment = (data) => ({ id: shortId.generate(), content: data, User: { id: 1, nickname: '제로초', }, }); // 이전 상태를 액션을 통해 다음 상태로 만들어내는 함수(불변성은 지키면서) const reducer = (state = initialState, action) => produce(state, (draft) => { switch (action.type) { //state를 draft로 case LOAD_POSTS_REQUEST: draft.loadPostsLoading = true; //리퀘스트가 갔을때는 로딩 draft.loadPostsDone = false; draft.loadPostsError = null; break; case LOAD_POSTS_SUCCESS: draft.loadPostsLoading = false; draft.loadPostsDone = true; draft.mainPosts = action.data.concat(draft.mainPosts); //데이터합치기 draft.hasMorePosts = draft.mainPosts.length < 50; //50개로 제한 게시글 50개만 보겠다. break; case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false; draft.loadPostsError = 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(dummyPost(action.data)); 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; break; case REMOVE_POST_SUCCESS: draft.removePostLoading = false; draft.removePostDone = true; draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data); 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]; // const mainPosts = [...state.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;./sagas/post.jsimport axios from 'axios'; import shortId from 'shortid'; import { all, delay, fork, put, takeLatest, throttle } from 'redux-saga/effects'; import { ADD_COMMENT_FAILURE, ADD_COMMENT_REQUEST, ADD_COMMENT_SUCCESS, ADD_POST_FAILURE, ADD_POST_REQUEST, ADD_POST_SUCCESS, generateDummyPost, LOAD_POSTS_FAILURE, LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, REMOVE_POST_FAILURE, REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS, } from '../reducers/post'; import { ADD_POST_TO_ME, REMOVE_POST_OF_ME } from '../reducers/user'; function loadPostsAPI(data) { return axios.get('/api/posts', data); } function* loadPosts(action) { try { // const result = yield call(loadPostsAPI, action.data); yield delay(1000); yield put({ type: LOAD_POSTS_SUCCESS, data: generateDummyPost(10), }); } catch (err) { console.error(err); yield put({ type: LOAD_POSTS_FAILURE, data: err.response.data, }); } } 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) { console.error(err); yield put({ type: ADD_POST_FAILURE, data: err.response.data, }); } } function removePostAPI(data) { return axios.delete('/api/post', data); } function* removePost(action) { try { // const result = yield call(removePostAPI, action.data); yield delay(1000); yield put({ type: REMOVE_POST_SUCCESS, data: action.data, }); yield put({ type: REMOVE_POST_OF_ME, data: action.data, }); } catch (err) { console.error(err); yield put({ type: REMOVE_POST_FAILURE, data: 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, data: err.response.data, }); } } //takeevery function* watchLoadPosts() { yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts); //5초동안은 post request 한 번만 실행 } function* watchAddPost() { //클릭 실수 2번했을때(동시에 로딩 중일때) 마지막 클릭만 실행되게 every는 두 번 다 실행 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(watchLoadPosts), fork(watchRemovePost), fork(watchAddComment), ]); }제로초님 강의와 깃허브 보면서 제 코드에 문제가 있는지 찾아보고 수정도 해봤습니다.import export둘다 잘 들어 갔는데 다른 문제 일까요? 아니면 install이 안된게 있는 걸까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
중복된 닉네임
중복된 닉네임할때 const exUser = await User.findOne({ where: { email: req.body.email, nickname: req.body.nickname, }, });이렇게 추가 해주면 되나요? 아니면 따로 만들어줘야하나요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
컨테이너 파일과 타입스 파일 매칭 질문
안녕하세요~ 포트폴리오 주소 가져오기 공부하면서 질문드립니다BoardWrite 컨테이너 파일과 타입스 파일에서 타입 명시해주는 부분이 이해되지 않습니다동그라미 친 부분만 그대로 타입 명시해주면 되는 줄 알았는데 타입스 파일에는 4부분으로 나눠져서 타입이 명시되 있어서요~ 저는 타입스 파일에서 4번째 동그라미만 있으면 될 것 같았어요~
-
해결됨따라하며 배우는 리액트 A-Z[19버전 반영]
Create Post 가 안되네요 ㅜ
강의 따라 하던 중에 앞 부분은 무리없이 잘 되었는데,강사님 코드 그대로 따라쳐도Create Post 가 아무 반응을 안합니다.물론 pocketBase에 아무 것도 안올라갑니다.혹시나 하여 Postman 으로pocketBase에 직접 POST request 해봤는데바로 잘 올라갑니다!
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
프론트쪽에서 env 나누나요?
현재 강의 front쪽에서 env 로컬 배포 일떄도 나누나요?
-
해결됨따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
노드 아예 모르는 상태로 들어도 괜찮을까요?
리액트는 어느정도 프로젝트도 해보고 웬만한건 구현이 가능한 정도인데 노드에 관해서 전혀 모르는데 수강해도 괜찮을까요?혹시 노드 지식이 필요하다면 어떤 강의를 듣고 와서 수강을 해야할까요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
antd rating 질문
안녕하세요 ant design 사이트에서 별점을 가져와서 사용하는 부분 질문있습니다콘테이너 파일에서는 리턴부분에 바로 setStar={setStar}로 넘겨주면 프레젠터 파일에서 props.setStar로 받고 있습니다 다른 매개변수처럼 onChange 함수를 사용하지 않고 바로 setStar로 넘겨주는 이유는 무엇인가요? 작동이 되는 이유가 궁금합니다그리고 types 파일에서 setStar: Dispatch<SetStateAction<number>>;타입을 이렇게 받고 있는데 이 설명은 강의에서 못본것 같아서요~ ant design 사이트에서 알 수 있는 것인가요?? BoardCommentWrite.container.tsx 파일BoardCommentWrite.types.tsx 파일
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
fetchBoardComments 데이터를 map으로 뿌린 el 타입 지정
안녕하세요 멘토님!시간을 짬짬히 내서 공부하는지라 진도는 느리지만 그래도 여차저차 타입스크립트 포폴 전환까지 완료헀습니다.빨간 줄 한 곳도 없이 무사히 전환 완료하였으나 강의 내용에도 나오지 않은듯한? 부분에서 궁금한게 생겨서 질문 드립니다.댓글 리스트 컴포넌트 부분을 map으로 렌더링 하는 방식으로 작업하였는데요, 여기 el의 타입을 지정하는 부분에서 Pick<IQuery, "fetchBoardComments"> 타입이 먹히지 않아서 삽질을 좀 했습니다. 그래도 여차저차 types.ts 내부를 참고하면서 수정해본 결과, el: IBoardComment 를 그대로 el의 타입으로 지정해주니까 컴파일 에러가 사라지고 해결되었습니다.이건 알고 해결했다기 보다는 모르고 바꿨는데 운 좋게 에러가 사라진 것이라 왜 해결됐는지 알고 넘어가고 싶어서 질문드립니다. (이게 편법이거나 정답이 아닐 수도 있다는 생각도 들었습니다.)그리고 Pick<IQuery, "fetchBoardComments">와 IBoardComment는 어떠한 차이가 있는지 궁금합니다. 참고로 el을 props drilling 하여 보낸 presenter 부분은 이렇게 사용하고 있습니다. 감사합니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
함께 아는 친구 기능 구현
다른 유저와 공통적으로 팔로잉을 하고 있는 유저 숫자를 구하고 싶은데 혹시 api를 어떤 방식으로 코딩하면 될까요? 항상 좋은강의 감사합니다
-
해결됨Next.js 시작하기(feat. 지도 서비스 개발)
killall -9 node 가 무엇인지요?
강의에서 포트가 남아있을경우 killall -9 node명령어를 사용하라고 하셨는데요..포트가 남아있다는게 무슨 뜻인지 모르겠습니다..저는 vscode로 진행중이며,npm run bulid 후에 npm run dev로 서버를 다시 시작하고 코드가 변경될때마다 Ctrl + c로 껏다가 다시 재시작 하여 반복 하는데요.강사님께서는 killall -9 node를 사용하시는데 이걸 왜 쓰는지 모르겠어요..그리고 killall node 사이의 -9 는 무슨뜻인가요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
다대다 관계 중간 테이블에서 find하기
포스트정보를 가져올 때 이렇게 좋아요를 가져오는데 좋아요에 대한 건 중간 테이블에서 가져오는게 아닌가요?as Likers는 단순히 user테이블의 이름만 바꾼다고 생각을 했습니다..좋아요 테이블은 따로 find로 접근을 할 수 없나요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
models post.js nickname column 생성
안녕하세요 제로초쌤 유저의 게시글을 [id]로 받아오는건 잘 되는데유저의 게시글을 동적라우팅을 할때 유저의 닉네임을 넣어서 받아올려고 합니다!하지만 백엔드의 post.js의 column 형태가 아래와 같이 되어있어서nickname을 받아오게 하기 위해서 Posts DB안에 있던 게시글을 전부 지운뒤 models 의 post.js 안에 nickname관련 설정을 따로 넣었습니다.근데 저렇게 작성하면 백엔드에서는 nickname관련한 필드리스트를 찾을 수 없다고 해서 에러가 나더라구요혹시 post.js의 설정 중에서 어떤걸 건드려야 게시글 작성자의 nickname을 들어가게 할 수 있나요?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
getStaticProps 포스트리스트 나열 영상 중Module not found: Can't resolve 'fs' 에러에 대해
이런 이유로 렌더링이 안된는것 같은데 검색을 해보면웹팩이나 버전에 대한 설명이 있는것 같은데 잘 모르겠네요 . 아시는 분 답변 부탁드려용.~~ㅜ.ㅜ
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
리트윗 따로 관리 시 테이블 관계성 질문
안녕하세요 선생님.제 프로젝트에서는 리트윗 기능이 일반 게시글과 분리되어 관리되거든요.(리트윗만 모아서 보여주고 게시글과 한꺼번에 렌더링해서 보여주지 않습니다)그래서 처음에는 그냥 for문으로 리트윗 내용일 땐 렌더링 하지 않는 식으로 처리했다가제 프로젝트에서 mainPosts로 map함수를 쓰는 컴포넌트가 몇 개 돼서그냥 리트윗 테이블을 따로 만들어서 관리할까 하는데 이럴 때 post와의 관계성은db.Post.hasMany(db.Retweet);이런 식으로 잡아주면 되나요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 레딧 사이트 만들기(NextJS)(Pages Router)
typeorm @Index()에 궁금한거 질문 드립니다!
export default class User extends BaseEntity{ ... @Index() @Column({ unique: true}) email:string; ... }여기에서 다루는 이 코드와@Unique(['email']) export default class User extends BaseEntity{ ... @Column() email:string; ... }두가지가 어떤 차이가 있는걸까요...? 너무 궁금 합니다!