월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
ssh xxxxx로 우분투에 들어가려니까 port 22: Connection timed out
nginx 부분을 따라하다가실수로 서버에서 나갔다가 다시 들어가려니까ssh: connect to host zzimzzim-front port 22: Connection timed out위와같은 에러가나면서 갑자기 들어가지지 않습니다.검색했는데도 해결되지 않아서 답답해다가이럴땐 어떤걸 참고하면 좋을지 조언 주시면 감사하겠습니다. ㅠ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
sudo certbot --nginx 에러
ubuntu@ip-172-31-37-191:~/react-nodebird/prepare/front$ sudo certbot --nginx Saving debug log to /var/log/letsencrypt/letsencrypt.log Which names would you like to activate HTTPS for? We recommend selecting either all domains, or all domains in a VirtualHost/server block. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: zzimzzim.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate numbers separated by commas and/or spaces, or leave input blank to select all options shown (Enter 'c' to cancel): Requesting a certificate for zzimzzim.com Certbot failed to authenticate some domains (authenticator: nginx). The Certificate Authority reported these problems: Domain: zzimzzim.com Type: connection Detail: 3.37.101.58: Fetching http://zzimzzim.com/.well-known/acme-challenge/SNUl0WTK7OKWJ-oYyHTrIFuh67ww_P11CUJXw2zWRZk: Timeout during connect (likely firewall problem) Hint: The Certificate Authority failed to verify the temporary nginx configuration changes made by Certbot. Ensure the listed domains point to this nginx server and that it is accessible from the internet. Some challenges have failed. Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.위와같은 에러가 발생합니다.다른 질문들을 보고 제로초님https://www.zerocho.com/category/NodeJS/post/5ef450a5701d8a001f84baeb 따라해보았는데 해결되지 않아서 여쭤봅니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Minified React error 콘솔에러 (hydrate)
안녕하세요 선생님카카오톡 공유하기 까지 듣고 화면을 테스트해보고 있는데Minified React error 이 에러가 떠서 질문드립니다. 메인화면, 상세보기에서 게시글이 있을때 나옵니다.Uncaught Error: Minified React error #418; visit https://reactjs.org/docs/error-decoder.html?invariant=418 for the full message or use the non-minified dev environment for full errors and additional helpful warnings. at lg (framework-ecc4130bc7a58a64.js:9:46457) at i (framework-ecc4130bc7a58a64.js:9:121052) at oO (framework-ecc4130bc7a58a64.js:9:99019) at framework-ecc4130bc7a58a64.js:9:98886 at oF (framework-ecc4130bc7a58a64.js:9:98893) at oS (framework-ecc4130bc7a58a64.js:9:93932) at x (framework-ecc4130bc7a58a64.js:33:1364) at MessagePort.T (framework-ecc4130bc7a58a64.js:33:1894)검색해봤더니 hydrate 쪽 이슈더라구요.혼자서 풀어보다가 잘 풀리지 않아서 여쭤봅니다.혹시 제로초님은 저런 에러가 나지 않으시는지난다면 어느 로직을 확인해야할지 조언 부탁드립니다./pages/post/[id].jsimport axios from 'axios'; import Head from 'next/head'; import { useRouter } from 'next/router'; import { useSelector } from 'react-redux'; import AppLayout from '../../components/AppLayout'; import PostCard from '../../components/PostCard'; import { loadPost } from '../../reducers/post'; import { loadMyInfo } from '../../reducers/user'; import wrapper from '../../store/configurStore'; const Post = () => { const router = useRouter(); const { id } = router.query; const { singlePost } = useSelector((state) => state.post); return ( <AppLayout> {singlePost ? ( <> <Head> <title> {singlePost?.User.nickname} 님의 글 </title> <meta name="description" content={singlePost.content} /> <meta property="og:title" content={`${singlePost.User.nickname}님의 게시글`} /> <meta property="og:description" content={singlePost.content} /> <meta property="og:image" content={ singlePost.Images[0] ? singlePost.Images[0].src : 'https://nodebird.com/favicon.ico' } /> <meta property="og:url" content={`https://nodebird.com/post/${id}`} /> </Head> <PostCard post={singlePost}>{id}번 게시글</PostCard> </> ) : ( <div>존재하지 않는 게시물입니다.</div> )} </AppLayout> ); }; export const getServerSideProps = wrapper.getServerSideProps( (store) => async ({ req, params }) => { const cookie = req ? req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; // 쿠키가 브라우저에 있는경우만 넣어서 실행 // (주의, 아래 조건이 없다면 다른 사람으로 로그인 될 수도 있음) if (req && cookie) { axios.defaults.headers.Cookie = cookie; } await store.dispatch(loadMyInfo()); await store.dispatch(loadPost(params.id)); }, ); export default Post; /components/PostCard.jsimport { RetweetOutlined, HeartOutlined, HeartTwoTone, MessageOutlined, EllipsisOutlined, } from '@ant-design/icons'; import { Card, Popover, Button, Avatar, List } from 'antd'; import dayjs from 'dayjs'; import Link from 'next/link'; import PropTypes from 'prop-types'; import { useState, useCallback, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import CommentForm from './CommentForm'; import Followbutton from './FollowButton'; import PostCardContent from './PostCardContent'; import PostImages from './PostImages'; import { removePostRequestAction, likePostRequestAction, unLikePostRequestAction, retweetRequestAction, } from '../reducers/post'; dayjs.locale('ko'); const PostCard = ({ post }) => { // const { me: {id} } = useSelector((state) => state.user); // const id = me && me.id; // const id = me?.id; // 옵셔널 체이닝 연산자 const { removePostLoading } = useSelector((state) => state.post); const dispatch = useDispatch(); const [commentFormOpend, setCommentFormOpend] = useState(false); const id = useSelector((state) => state.user.me?.id); const liked = post.Likers.find((d) => d.id === id); const onLike = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(likePostRequestAction(post.id)); }, [id]); const onUnLike = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(unLikePostRequestAction(post.id)); }, [id]); const onToggleComment = useCallback(() => { setCommentFormOpend((prev) => !prev); }, []); const onRemovePost = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(removePostRequestAction({ id: post.id })); }, [id]); const onRetweet = useCallback(() => { if (!id) { alert('로그인이 필요합니다.'); } dispatch(retweetRequestAction(post.id)); }, [id]); return ( <div style={{ marginTop: 10 }}> <Card cover={post.Images[0] && <PostImages images={post.Images} />} actions={[ // 배열안에 들어가는 것들은 다 key를 넣어줘야 한다. <RetweetOutlined key="retweet" onClick={onRetweet} />, liked ? ( <HeartTwoTone key="heart" twoToneColor="#eb2f96" onClick={onUnLike} /> ) : ( <HeartOutlined key="heart" onClick={onLike} /> ), <MessageOutlined key="comment" onClick={onToggleComment} />, <Popover key="more" content={ <Button.Group> {id && post.User?.id === id ? ( <> <Button type="primary" key="modify"> 수정 </Button> <Button type="danger" key="delete" onClick={onRemovePost} loading={removePostLoading} > 삭제 </Button> </> ) : ( <Button type="dashed" key="report"> 신고 </Button> )} </Button.Group> } > <EllipsisOutlined /> </Popover>, ]} extra={id && <Followbutton post={post} />} title={ post.RetweetId ? `${post.User.nickname}님이 리트윗하셨습니다.` : null } > {post.RetweetId && post.Retweet ? ( <Card cover={ post.Retweet.Images[0] && ( <PostImages images={post.Retweet.Images} /> ) } > <div style={{ float: 'right' }}> {dayjs(post.createdAt).format('YYYY.MM.DD')} </div> <Card.Meta avatar={ <Link href={`/user/${post.Retweet.User.id}`}> <Avatar>{post.Retweet.User?.nickname[0]}</Avatar> </Link> } title={post.Retweet.User?.nickname} description={<PostCardContent postData={post.Retweet.content} />} /> </Card> ) : ( <> <div style={{ float: 'right' }}> {dayjs(post.createdAt).format('YYYY.MM.DD')} </div> <Card.Meta avatar={ <Link href={`/user/${post.User.id}`}> <Avatar>{post.User?.nickname[0]}</Avatar> </Link> } title={post.User?.nickname} description={<PostCardContent postData={post.content} />} /> </> )} </Card> {commentFormOpend && ( <div> {/* 게시글의 아이디 위해서 post 넘겨줌 */} <CommentForm post={post} /> <List header={`${post.Comments.length}개의 댓글`} itemLayout="horizontal" dataSource={post.Comments} renderItem={(item) => ( <List.Item key={item.id}> <List.Item.Meta title={item.User.nickname} avatar={ <Link href={`/user/${item.User.id}`}> <Avatar>{item.User.nickname[0]}</Avatar> </Link> } description={item.content} /> </List.Item> )} /> </div> )} </div> ); }; PostCard.propTypes = { post: PropTypes.shape({ id: PropTypes.number, User: PropTypes.object, content: PropTypes.string, createdAt: PropTypes.string, Comments: PropTypes.arrayOf(PropTypes.object), Images: PropTypes.arrayOf(PropTypes.object), Likers: PropTypes.arrayOf(PropTypes.object), RetweetId: PropTypes.number, Retweet: PropTypes.objectOf(PropTypes.any), }).isRequired, }; export default PostCard; 여유되실때 한번 봐주시면 감사하겠습니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
카카오 공유했을 때 이전에 작성했던 글이 나오는 버그
안녕하세요 선생님보너스 강의 전까지 보고 카카오 공유하기 까지 테스트하고 있는데버그를 발견한것 같아서 어떻게 해결할지 여쭤보려 올립니다.위처럼 post/5의 게시글을 작성하고카카오톡에 공유를 하면이전에 썼던 (로컬에서 썼었던) 내용이 보여집니다.혹시 기존 데이터베이스를 리셋하거나 추가적인 작업이 필요한건지 궁금합니다.
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
프론트서버 배포 후 EADDRINUSE에러 발생
안녕하세요 제로초님덕분에 front배포 잘 하게 되었습니다.그후 접속해서 테스트했는데, 회원가입할 때 connect_refused가 발생해 로그를 찾아보았습니다.EADDRINUSE를 발견하고 원인을 찾으려고 뒤져봤는데 해결되지 않아 여쭤봅니다. ㅠfront = 13.125.119.94 back = 13.125.252.5 아래는 설정한 내용/front/package.json"scripts": { "dev": "next dev", "build": "cross-env ANALYZE=true NODE_ENV=production next build", "start": "cross-env NODE_ENV=production next start -p 80", }, /back/packge.json"scripts": { "dev": "nodemon app", "start": "cross-env NODE_ENV=production pm2 start app.js" }, /back/app.js// node에서는 import / export 안쓰고 require / module.exports 사용 const express = require('express'); const cors = require('cors'); const session = require('express-session'); const cookieParser = require('cookie-parser'); const passport = require('passport'); const dotenv = require('dotenv'); const morgan = require('morgan'); const path = require('path'); const hpp = require('hpp'); const helmet = require('helmet'); const postRouter = require('./routes/post'); const postsRouter = require('./routes/posts'); const userRouter = require('./routes/user'); const hashtahRouter = require('./routes/hashtag'); const db = require('./models'); // sequelize에서 model 모두 등록 // express에서 그 sequelize를 등록해야 한다. const passportConfig = require('./passport'); dotenv.config(); const app = express(); db.sequelize .sync() .then(() => { console.log('db연결 성공!'); }) .catch(console.error); passportConfig(); // 운영용 빌드 if (process.env.NOD_ENV === 'production') { app.use(morgan('combined')); // 로그볼 수 있게 해주는 것 // 보안에 도움되는 패키지들 app.use(hpp()); app.use(helmet()); } else { app.use(morgan('dev')); // 로그볼 수 있게 해주는 것 } app.use( cors({ origin: ['http://localhost:3000', 'nodebird.com', 'http://13.125.119.94'], credentials: true, }) ); app.use('/', express.static(path.join(__dirname, 'uploads'))); app.use(express.urlencoded({ extended: true })); app.use(cookieParser(process.env.COOKIE_SECRET)); app.use( session({ saveUninitialized: false, resave: false, secret: process.env.COOKIE_SECRET, }) ); app.use(passport.initialize()); app.use(passport.session()); app.use('/post', postRouter); app.use('/posts', postsRouter); app.use('/user', userRouter); app.use('/hashtag', hashtahRouter); app.listen(80, () => { console.log('서버 실행 중'); }); 프론트 실행화면백엔드 실행 화면 아래는 에러 내용입니다./front 에러AxiosError: connect ECONNREFUSED 13.125.252.5:443 0|npm | at AxiosError.from (file:///home/ubuntu/react-nodebird/prepare/front/node_modules/axios/lib/core/AxiosError.js:89:14) 0|npm | at RedirectableRequest.handleRequestError (file:///home/ubuntu/react-nodebird/prepare/front/node_modules/axios/lib/adapters/http.js:610:25) 0|npm | at RedirectableRequest.emit (node:events:517:28) 0|npm | at eventHandlers.<computed> (/home/ubuntu/react-nodebird/prepare/front/node_modules/follow-redirects/index.js:38:24) 0|npm | at ClientRequest.emit (node:events:517:28) 0|npm | at TLSSocket.socketErrorListener (node:_http_client:501:9) 0|npm | at TLSSocket.emit (node:events:517:28) 0|npm | at emitErrorNT (node:internal/streams/destroy:151:8) 0|npm | at emitErrorCloseNT (node:internal/streams/destroy:116:3) 0|npm | at process.processTicksAndRejections (node:internal/process/task_queues:82:21) 0|npm | at Axios.request (file:///home/ubuntu/react-nodebird/prepare/front/node_modules/axios/lib/core/Axios.js:45:41) 0|npm | at process.processTicksAndRejections (node:internal/process/task_queues:95:5) 0|npm | at async y (/home/ubuntu/react-nodebird/prepare/front/.next/server/chunks/414.js:1:1838) { 0|npm | port: 443, 0|npm | address: '13.125.252.5', 0|npm | syscall: 'connect', 0|npm | code: 'ECONNREFUSED', 0|npm | errno: -111, 0|npm | config: { 0|npm | transitional: { 0|npm | silentJSONParsing: true, 0|npm | forcedJSONParsing: true, 0|npm | clarifyTimeoutError: false 0|npm | }, cause: Error: connect ECONNREFUSED 13.125.252.5:443 0|npm | at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1555:16) { 0|npm | errno: -111, 0|npm | code: 'ECONNREFUSED', 0|npm | syscall: 'connect', 0|npm | address: '13.125.252.5', 0|npm | port: 443 0|npm | } back 에러 | Warning: connect.session() MemoryStore is not 0|app | designed for a production environment, as it will leak 0|app | memory, and will not scale past a single process. 0|app | Error: listen EADDRINUSE: address already in use / 0|app | at Server.setupListenHandle [as _listen2] (node:net:1800:21) 0|app | at listenInCluster (node:net:1865:12) 0|app | at Server.listen (node:net:1964:5) 0|app | at Function.listen (/home/ubuntu/react-nodebird/prepare/back/node_modules/express/lib/application.js:635:24) 0|app | at Object.<anonymous> (/home/ubuntu/react-nodebird/prepare/back/app.js:75:5) 0|app | at Module._compile (node:internal/modules/cjs/loader:1364:14) 0|app | at Module._extensions..js (node:internal/modules/cjs/loader:1422:10) 0|app | at Module.load (node:internal/modules/cjs/loader:1203:32) 0|app | at Module._load (node:internal/modules/cjs/loader:1019:12) 0|app | at Object.<anonymous> (/home/ubuntu/react-nodebird/prepare/back/node_modules/pm2/lib/ProcessContainerFork.js:33:23) { 0|app | code: 'EADDRINUSE', 0|app | errno: -98, 0|app | syscall: 'listen', 0|app | address: '/', 0|app | port: -1 0|app | } front monit back monit 비슷한 질문들이 올라와 있어서 (sudo) npx pm2 kill 해보거나 다른 검색들을 해봤는데 원인을 모르겠더라구요어떤 부분을 수정해야할지 어떤부분을 확인해봐야할지 봐주시면 정말 감사하겠습니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
npm run build 에러
안녕하세요 제로초님프론트 서버를 npm run build 하니까아래와 같은 에러가 나오는데검색해서 찾아봐도 잘 모르겠더라구요.혹시 어느 부분을 확인해보고 어떤 부분을 수정해야할지 조언해주실 수 있을까요?빌드하는것만 몇일째라 ㅠ답답합니다. Collecting page data ./home/ubuntu/react-nodebird/prepare/front/node_modules/rc-util/es/omit.js:1 import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; ^^^^^^ SyntaxError: Cannot use import statement outside a module at internalCompileFunction (node:internal/vm:76:18) at wrapSafe (node:internal/modules/cjs/loader:1283:20) at Module._compile (node:internal/modules/cjs/loader:1328:27) at Module._extensions..js (node:internal/modules/cjs/loader:1422:10) at Module.load (node:internal/modules/cjs/loader:1203:32) at Module._load (node:internal/modules/cjs/loader:1019:12) at Module.require (node:internal/modules/cjs/loader:1231:19) at mod.require (/home/ubuntu/react-nodebird/prepare/front/node_modules/next/dist/server/require-hook.js:65:28) at require (node:internal/modules/helpers:177:18) at 5514 (/home/ubuntu/react-nodebird/prepare/front/.next/server/pages/signup.js:7:10601) /home/ubuntu/react-nodebird/prepare/front/node_modules/rc-util/es/omit.js:1 import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2"; ^^^^^^ SyntaxError: Cannot use import statement outside a module at internalCompileFunction (node:internal/vm:76:18) at wrapSafe (node:internal/modules/cjs/loader:1283:20) at Module._compile (node:internal/modules/cjs/loader:1328:27) at Module._extensions..js (node:internal/modules/cjs/loader:1422:10) at Module.load (node:internal/modules/cjs/loader:1203:32) at Module._load (node:internal/modules/cjs/loader:1019:12) at Module.require (node:internal/modules/cjs/loader:1231:19) at mod.require (/home/ubuntu/react-nodebird/prepare/front/node_modules/next/dist/server/require-hook.js:65:28) at require (node:internal/modules/helpers:177:18) at 5514 (/home/ubuntu/react-nodebird/prepare/front/.next/server/pages/signup.js:7:10601) > Build error occurred Error: Failed to collect page data for /signup at /home/ubuntu/react-nodebird/prepare/front/node_modules/next/dist/build/utils.js:1268:15 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) { type: 'Error' } /pages.index.jsimport axios from 'axios'; import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import AppLayout from '../components/AppLayout'; import PostCard from '../components/PostCard'; import PostForm from '../components/PostForm'; import { loadPosts, loadPostsError } from '../reducers/post'; import { loadMyInfo } from '../reducers/user'; import wrapper from '../store/configurStore'; // 프론트, 브라우저 같이 실행 const Home = () => { const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePosts, loadPostsLoading, retweetError } = useSelector((state) => state.post); const dispatch = useDispatch(); useEffect(() => { if (retweetError) { alert(retweetError); } }, [retweetError]); useEffect(() => { const onScroll = () => { if ( window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300 ) { if (hasMorePosts && !loadPostsLoading) { const lastId = mainPosts[mainPosts.length - 1]?.id; dispatch(loadPosts({ lastId, limit: 10 })); } } }; window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); }; }, [hasMorePosts, loadPostsLoading, mainPosts.length]); return ( <AppLayout> {me && <PostForm />} {mainPosts && mainPosts[0] ? mainPosts.map((post) => <PostCard key={post.id} post={post} />) : null} </AppLayout> ); }; export const getServerSideProps = wrapper.getServerSideProps( (store) => async ({ req }) => { console.log('getServerSideProps start--------------------------'); console.log(req.headers); const cookie = req ? req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; // 쿠키가 브라우저에 있는경우만 넣어서 실행 // (주의, 아래 조건이 없다면 다른 사람으로 로그인 될 수도 있음) if (req && cookie) { axios.defaults.headers.Cookie = cookie; } await store.dispatch(loadPosts()); await store.dispatch(loadMyInfo()); }, ); export default Home;
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
front 서버 npm run build 중에 발생한 에러들
안녕하세요 제로초님front 서버를 빌드하던중에 빌드가 된줄알고 pm2 monit으로 확인해보니 Could not find a production build in the '.next' directory 이런 에러가 떠있었습니다. 확인해봤더니 아래와 같은 에러 들이 엄청 나오더라구요.warning도 아니고 다 error들이라 검색해보고 .eslintrc를 고쳐봤는데도 잘 안되서 여쭤봅니다 ㅠ 다른 분들은 이런 에러 없이 잘 되는거같은데 전 왜이런지 도와주시면 감사하겠습니다 ㅠ ㅠ npm run build 했을때 나타나는 에러중 일부/components/LoginForm.js 11:1 Error: Unexpected tab character. no-tabs 14:1 Error: Unexpected tab character. no-tabs 17:19 Error: Function component is not a function declaration react/function-component-definition 18:1 Error: Unexpected tab character. no-tabs 18:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 19:1 Error: Unexpected tab character. no-tabs 19:1 Error: Expected indentation of 4 spaces but found 2 tabs. indent 20:1 Error: Unexpected tab character. no-tabs 20:1 Error: Expected indentation of 4 spaces but found 2 tabs. indent 21:1 Error: Unexpected tab character. no-tabs 21:1 Error: Expected indentation of 4 spaces but found 2 tabs. indent 22:1 Error: Unexpected tab character. no-tabs 22:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 23:1 Error: Unexpected tab character. no-tabs 23:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 25:1 Error: Unexpected tab character. no-tabs 25:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 26:1 Error: Unexpected tab character. no-tabs 26:1 Error: Expected indentation of 4 spaces but found 2 tabs. indent 27:1 Error: Unexpected tab character. no-tabs 27:1 Error: Expected indentation of 6 spaces but found 3 tabs. indent 27:4 Warning: Unexpected alert. no-alert 28:1 Error: Unexpected tab character. no-tabs 28:1 Error: Expected indentation of 4 spaces but found 2 tabs. indent 29:1 Error: Unexpected tab character. no-tabs 29:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 31:1 Error: Unexpected tab character. no-tabs 31:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 32:1 Error: Unexpected tab character. no-tabs 32:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 33:1 Error: Unexpected tab character. no-tabs 33:1 Error: Expected indentation of 4 spaces but found 2 tabs. indent 34:1 Error: Unexpected tab character. no-tabs 34:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 36:1 Error: Unexpected tab character. no-tabs 36:1 Error: Expected indentation of 2 spaces but found 1 tab. indent 37:1 Error: Unexpected tab character. no-tabs 37:3 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 38:1 Error: Unexpected tab character. no-tabs 38:4 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 39:1 Error: Unexpected tab character. no-tabs 39:5 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 39:20 Error: Unexpected usage of singlequote. jsx-quotes 40:1 Error: Unexpected tab character. no-tabs 40:5 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 41:1 Error: Unexpected tab character. no-tabs 41:5 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 42:1 Error: Unexpected tab character. no-tabs 42:6 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent-props 42:11 Error: Unexpected usage of singlequote. jsx-quotes 43:1 Error: Unexpected tab character. no-tabs 43:6 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent-props 44:1 Error: Unexpected tab character. no-tabs 44:6 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent-props 45:1 Error: Unexpected tab character. no-tabs 45:1 Error: Expected indentation of 7 spaces but found 6 tabs. indent 46:1 Error: Unexpected tab character. no-tabs 46:1 Error: Expected indentation of 7 spaces but found 6 tabs. indent 47:1 Error: Unexpected tab character. no-tabs 48:1 Error: Unexpected tab character. no-tabs 48:6 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent-props 49:1 Error: Unexpected tab character. no-tabs 49:7 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 50:1 Error: Unexpected tab character. no-tabs 50:8 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 50:29 Error: Unexpected usage of singlequote. jsx-quotes 50:42 Error: Unexpected usage of singlequote. jsx-quotes 50:62 Error: Unexpected usage of singlequote. jsx-quotes 51:1 Error: Unexpected tab character. no-tabs 51:8 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 52:1 Error: Unexpected tab character. no-tabs 53:1 Error: Unexpected tab character. no-tabs 54:1 Error: Unexpected tab character. no-tabs 55:1 Error: Unexpected tab character. no-tabs 56:1 Error: Unexpected tab character. no-tabs 56:4 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 57:1 Error: Unexpected tab character. no-tabs 57:5 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 57:20 Error: Unexpected usage of singlequote. jsx-quotes 58:1 Error: Unexpected tab character. no-tabs 58:5 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 59:1 Error: Unexpected tab character. no-tabs 59:5 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent 60:1 Error: Unexpected tab character. no-tabs 60:6 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent-props 60:11 Error: Unexpected usage of singlequote. jsx-quotes 61:1 Error: Unexpected tab character. no-tabs 61:6 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent-props 62:1 Error: Unexpected tab character. no-tabs 62:6 Error: Expected indentation of 2 space characters but found 0. react/jsx-indent-props 63:1 Error: Unexpected tab character. no-tabs 63:1 Error: Expected indentation of 7 spaces but found 6 tabs. indent 64:1 Error: Unexpected tab character. no-tabs 64:1 Error: Expected indentation of 7 spaces but found 6 tabs. indent 65:1 Error: Unexpected tab character. no-tab .eslintrc, next.config.js 는 제로초님의 깃헙과 동일하게 했습니다..eslintrc{ "parser": "@babel/eslint-parser", "parserOptions": { "ecmaVersion": 2020, "sourceType": "module", "ecmaFeatures": { "jsx": true }, "babelOptions": { "presets": ["next/babel"] }, "requireConfigFile": false }, "env": { "browser": true, "node": true, "es6": true }, "extends": ["airbnb"], "plugins": ["import", "react-hooks", "jsx-a11y"], "rules": { "jsx-a11y/label-has-associated-control": "off", "jsx-a11y/anchor-is-valid": "off", "no-console": "off", "no-underscore-dangle": "off", "react/forbid-prop-types": "off", "react/jsx-filename-extension": "off", "react/jsx-one-expression-per-line": "off", "react/jsx-props-no-spreading": "off", "object-curly-newline": "off", "linebreak-style": "off", "no-param-reassign": "off", "max-len": "off", "react/react-in-jsx-scope": "off" } } next.config.jsconst withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: process.env.ANALYZE === "true", }); module.exports = withBundleAnalyzer({ images: { domains: ["react-nodebird.s3.ap-northeast-2.amazonaws.com", "react-nodebird-s3.s3.amazonaws.com"], }, compress: true, compiler: { styledComponents: { ssr: true, displayName: true, }, }, webpack(config, { webpack }) { const prod = process.env.NODE_ENV === "production"; return { ...config, mode: prod ? "production" : "development", devtool: prod ? "hidden-source-map" : "inline-source-map", plugins: [...config.plugins], }; }, }); /package.json{ "name": "react-nodebird", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "next dev", "build": "cross-env ANALYZE=true NODE_ENV=production next build", "start": "cross-env NODE_ENV=production next start -p 80" }, "author": "", "license": "MIT", "dependencies": { "@ant-design/icons": "^5.3.6", "@next/bundle-analyzer": "^14.2.3", "@reduxjs/toolkit": "^2.2.3", "antd": "^5.8.3", "axios": "^1.6.8", "babel-plugin-styled-components": "^2.1.4", "cross-env": "^7.0.3", "dayjs": "^1.11.11", "lodash": "^4.17.21", "next": "^14.2.3", "next-redux-wrapper": "^8.1.0", "pm2": "^5.3.1", "prop-types": "^15.8.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.51.3", "react-redux": "^9.1.1", "react-slick": "^0.30.2", "redux": "^5.0.1", "redux-saga": "^1.3.0", "shortid": "^2.2.16", "styled-components": "^6.1.8", "swr": "^2.2.5" }, "devDependencies": { "@babel/eslint-parser": "^7.24.5", "@faker-js/faker": "^8.4.1", "babel-eslint": "^10.1.0", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.2" } } 깃헙 : https://github.com/dydcodydco/react-nodebird
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
서버 실행하고 브라우저로 들어갔을때 404에러
안녕하세요 제로초님서버 실행하고 브라우저로 들어갔을때 404에러가 나오는건정상적인 실행결과가 아닌건가요?제대로 작동하고 있는것 같은데... 저랑 같은 상태로 보이시는 분의 질문이 있는걸 보고 혹시나해서 여쭤봅니다.https://www.inflearn.com/questions/685249/%EC%84%9C%EB%B2%84-%EC%8B%A4%ED%96%89%EC%8B%9C-404%EC%97%90%EB%9F%AC-%EA%B4%80%EB%A0%A8%ED%95%B4%EC%84%9C-%EC%A7%88%EB%AC%B8%EB%93%9C%EB%A6%AC%EA%B2%A0%EC%8A%B5%EB%8B%88%EB%8B%A4
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
css 서버사이드 랜더링이 적용되지 않아서 문의 드립니다.
https://www.inflearn.com/course/lecture?courseSlug=%EB%85%B8%EB%93%9C%EB%B2%84%EB%93%9C-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EB%89%B4%EC%96%BC&unitId=49018&category=questionDetail&tab=community&q=1075492안녕하세요 제로초님!위의 질문 답변을 보고 css 서버사이드 랜더링을 하기위해서 이것저것 해보았는데 적용이 안되서 문의 드립니다..babelrc 작성next.config.js에서도 옵션으로 가능하다고해서 수정_document.js 수정빌드 하고 npm start하고 테스트위 링크의 답변 참고해서 @ant-design/cssinjs 적용해봄 그럼에도 적용안되더라구요 ㅠpages/_docuemnts.js (이 전에는 제로초님깃헙의 것을 썼었습니다. 안돼서 @ant-design/cssinjs 적용한 버전입니다.import React from "react"; import Document, { Html, Head, Main, NextScript } from "next/document"; import { createCache, extractStyle, StyleProvider } from "@ant-design/cssinjs"; import { ServerStyleSheet } from "styled-components"; export default class MyDocument extends Document { static async getInitialProps(ctx) { const cache = createCache(); const sheet = new ServerStyleSheet(); const originalRenderPage = ctx.renderPage; try { ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => ( <StyleProvider cache={cache}> <App {...props} /> </StyleProvider> ), }); const initialProps = await Document.getInitialProps(ctx); const style = extractStyle(cache, true); return { ...initialProps, styles: ( <> {initialProps.styles} <style dangerouslySetInnerHTML={{ __html: style }} /> </> ), }; } catch (error) { console.error(error); } finally { sheet.seal(); } } render() { return ( <Html> <Head /> <body> <Main /> <NextScript /> </body> </Html> ); } } next.config.jsconst withBundleAnalyzer = require("@next/bundle-analyzer")({ enabled: process.env.ANALYZE === "true", }); module.exports = withBundleAnalyzer({ images: { domains: ["react-nodebird.s3.ap-northeast-2.amazonaws.com", "react-nodebird-s3.s3.amazonaws.com"], }, compress: true, compiler: { styledComponents: { ssr: true, displayName: true, }, }, webpack(config, { webpack }) { const prod = process.env.NODE_ENV === "production"; return { ...config, mode: prod ? "production" : "development", devtool: prod ? "hidden-source-map" : "inline-source-map", plugins: [...config.plugins], }; }, }); .eslintrc{ "parser": "@babel/eslint-parser", "parserOptions": { "ecmaVersion": "latest", "sourceType": "module", "ecmaFeatures": { "jsx": true }, "requireConfigFile": false, "babelOptions": { "presets": ["next/babel"] } }, "env": { "browser": true, "node": true, "es6": true }, "extends": [ "airbnb", "next/babel" ], "plugins": ["import", "react-hooks", "jsx-a11y"], "rules": { "react/react-in-jsx-scope": "off", "jsx-a11y/label-has-associated-control": "off", "jsx-a11y/anchor-is-valid": "off", "no-console": "off", "no-underscore-dangle": "off", "react/forbid-prop-types": "off", "react/jsx-filename-extension": "off", "react/jsx-one-expression-per-line": "off", "react/jsx-props-no-spreading": "off", "object-curly-newline": "off", "linebreak-style": "off", "no-param-reassign": "off", "max-len": "off" } } /.babelrc{ "presets": ["next/babel"], "plugins": [ [ "styled-components", { "ssr": true, "displayName": true } ] ] } https://github.com/dydcodydco/react-nodebird혹시나해서 깃헙 주소도 남깁니다.마지막으로 하나 더 궁금한게 있습니다.이 강의를 내껄로 만들고, 다음강의 슬랙까지 강의보고 하면중고신입으로 개발자 이직할 수 있을지도 궁금합니다.좋은 강의 정말 감사합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
팔로워 3명씩 불러오고 데이터 합쳐주는걸로 바꾸고 서버요청을 무한으로하고있습니다.
안녕하세요 제로초님.강의중에 말씀해주셨건걸 참고해서팔로워, 팔로잉을 3명 호출하고, 이후에 다음3명씩 호출하고 불러온 데이터를 합쳐서 리스트를 만드는식으로 바꿔봤습니다.그런데 이슈가 한번 불러오기, 더보기 다 작동하는데 서버 요청을 무한으로 하고 있습니다.어떤 부분을 수정해야할지 봐주실 수 있을까요?limit은 3으로 고정, page를 조절해서 다음 3명씩/pages/profile.jsimport Head from "next/head"; import { useDispatch, useSelector } from "react-redux"; import { useCallback, useEffect, useState } from "react"; import { useRouter } from "next/router"; import axios from "axios"; import useSWR from "swr"; import { loadFollowersRequestAction, loadFollowingsRequestAction, loadMyInfo } from "../reducers/user"; import AppLayout from "../components/AppLayout"; import NicknameEditForm from "../components/NicknameEditForm"; import FollowList from "../components/FollowList"; import wrapper from "../store/configurStore"; const fetcher = (url) => axios.get(url, { widthCredentials: true }).then((result) => { console.log("fetcher----------------------"); return result.data; }); const Profile = () => { const router = useRouter(); const dispatch = useDispatch(); const { me } = useSelector((state) => state.user); const [followersLimit, setFollowersLimit] = useState(1); const [followingsimit, setFollowingsLimit] = useState(1); const [followers, setFollowers] = useState([]); const [followings, setFollowings] = useState([]); const { data: followersData, error: followerError, isLoading: followerLoading, } = useSWR(`http://localhost:3065/user/followers?page=${followersLimit}`, fetcher, { onSuccess: (data) => { setFollowers((prev) => [...prev, ...data]); }, }); const { data: followingsData, error: followingError, isLoading: followingLoading, } = useSWR(`http://localhost:3065/user/followings?page=${followingsimit}`, fetcher, { onSuccess: (data) => { setFollowings((prev) => [...prev, ...data]); }, }); useEffect(() => { if (!(me && me.id)) { router.push("/"); } }, [me && me.id]); const loadMoreFollowings = useCallback(() => { setFollowingsLimit((prev) => prev + 1); }, []); const loadMoreFolloweers = useCallback(() => { setFollowersLimit((prev) => prev + 1); }, []); if (!me) { return <div>내정보 로딩중...</div>; } if (followerError || followingError) { console.error(followerError || followingError); return <div>팔로잉/팔로워 로딩 중 에러 발생...</div>; } return ( <> <Head> <title>내 프로필 | NodeBird</title> </Head> <AppLayout> <NicknameEditForm /> <FollowList header='팔로워' data={followers} onClickMore={loadMoreFolloweers} loading={followerLoading} /> <FollowList header='팔로잉' data={followings} onClickMore={loadMoreFollowings} loading={followingLoading} /> </AppLayout> </> ); }; export const getServerSideProps = wrapper.getServerSideProps((store) => async ({ req }) => { console.log(req.headers); const cookie = req ? req.headers.cookie : ""; axios.defaults.headers.Cookie = ""; if (req && cookie) { axios.defaults.headers.Cookie = cookie; } await store.dispatch(loadMyInfo()); }); export default Profile; /routes/userl.js// GET /user/followers 팔로워즈 불러오기 router.get("/followers", isLoggedIn, async (req, res, next) => { try { // 나를 먼저 찾고 const user = await User.findOne({ where: { id: req.user.id }, }); // 내 팔로워즈 get 하기 const limit = parseInt(req.query.limit, 10) || 3; // 기본값 3 const page = parseInt(req.query.page, 10) || 1; // 기본값 1 const offset = (page - 1) * limit; const followers = await user.getFollowers({ limit, offset }); res.status(200).json(followers); } catch (error) { console.error(error); next(error); } }); // GET /user/followings 팔로잉즈 불러오기 // 미들웨어... (req, res, next) 이 콜백함수도 미들웨어 router.get("/followings", isLoggedIn, async (req, res, next) => { try { const user = await User.findOne({ where: { id: req.user.id }, }); const limit = parseInt(req.query.limit, 10) || 3; // 기본값 3 const page = parseInt(req.query.page, 10) || 1; // 기본값 1 const offset = (page - 1) * limit; const followings = await user.getFollowings({ limit, offset }); res.status(200).json(followings); } catch (error) { console.error(error); next(error); } });
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
해시태그 검색에서 throttle에 관해 질문있습니다.
해시태그 검색에서 throttle에 관해 질문있습니다.해시태그 검색을 하게 되면 throttle때문에 5초 이후에 검색이 되고 있습니다.스크롤할 때에는 유용하지만 하나를 클릭하고, 5초 이내에 클릭하게되면 url은 바뀌지만 게시글은 바뀌지 않고 ux에 좋지않아보입니다. const loadHashtagPostsThrottle = async (payload) => { const queryStr = setQuerystring(payload); const url = `/hashtag/${encodeURIComponent(payload.tag)}${queryStr ? "?" + queryStr : ""}`; const response = await axios.get(url); return response; }; export const loadHashtagPosts = createAsyncThunk("post/loadHashtagPosts", _.throttle(loadHashtagPostsThrottle, 5000)); 특저 유저의 게시글을 검색할때도 비슷한 현상이 나올거같은데 이럴땐 어떻게 하면 ux를 좋게 할 수 있을지 궁금합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
full reload 될때는 서버 데이터를 호출하는데 그 이후로 새로고침할대는 호출하지 않아서 답답합니다
안녕하세요 제로초님full reload 될때는 서버 데이터를 호출하는데 그 이후로 새로고침할대는 호출하지 않아서 답답합니다.구글링하고 제로초님 로직을 따라 만들어보다가posts게시물이 내려오도록 수정을 했는데,이상하게 처음엔 서버에 요청하고 데이터를 내려받아서 게시물이 보이는데, 그이후 새로고침하면 서버 호출까지도 안하는것 같습니다.이젠 좀 나오나 싶었는데, full reload될때만 되서 여유되실 때 로직한번 봐주시면 감사하겠습니다 ㅠ / pages/index.jsimport { useDispatch, useSelector } from "react-redux"; import AppLayout from "../components/AppLayout"; import PostForm from "../components/PostForm"; import PostCard from "../components/PostCard"; import { useEffect } from "react"; import { loadPostsRequestAction } from "../reducers/post"; import { loadMyInfoRequestAction } from "../reducers/user"; import wrapper from "../store/configurStore"; import axios from "axios"; import { END } from "redux-saga"; // 프론트, 브라우저 같이 실행 const Home = () => { const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePosts, loadPostsLoading, retweetError } = useSelector((state) => state.post); const dispatch = useDispatch(); useEffect(() => { if (retweetError) { alert(retweetError); } }, [retweetError]); useEffect(() => { const onScroll = () => { if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) { if (hasMorePosts && !loadPostsLoading) { const lastId = mainPosts[mainPosts.length - 1]?.id; dispatch(loadPostsRequestAction({ lastId, limit: 10 })); } } }; window.addEventListener("scroll", onScroll); return () => { window.removeEventListener("scroll", onScroll); }; }, [hasMorePosts, loadPostsLoading, mainPosts]); return ( <AppLayout> {me && <PostForm />} {/* 순서가 바뀌거나 삭제될 수 있는 리스트들에 key값으로 index를 쓰면 안됀다. */} {/* 반복문이 있고 바뀌지 않는 리스트일 경우에만 사용해도 된다. */} {mainPosts.map((post) => ( <PostCard key={post.id} post={post} /> ))} </AppLayout> ); }; export const getServerSideProps = wrapper.getServerSideProps((store) => async ({ req }) => { store.dispatch(loadPostsRequestAction()); store.dispatch(loadMyInfoRequestAction()); store.dispatch(END); await store.sagaTask.toPromise(); // 사가 작업 완료 대기 console.log("state", store.getState()); }); export function reportWebVitals(metric) { console.log(metric); } export default Home; /pages/_app.jsimport PropTypes from "prop-types"; import Head from "next/head"; import { Provider } from "react-redux"; import wrapper from "../store/configurStore"; const NodeBird = ({ Component, ...rest }) => { const { store, props } = wrapper.useWrappedStore(rest); const { pageProps } = props; return ( <Provider store={store}> <Head> <meta charSet='utf-8' /> <meta name='viewport' content='width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' /> <title>NodeBird</title> </Head> <Component {...pageProps} /> </Provider> ); }; NodeBird.propTypes = { Component: PropTypes.elementType.isRequired, pageProps: PropTypes.any.isRequired, }; export function reportWebVitals(metric) { // console.log("-----------------------------------"); // console.log(metric); } export default NodeBird; store/configureStore.jsimport { createWrapper } from "next-redux-wrapper"; import { configureStore } from "@reduxjs/toolkit"; import reducer from "../reducers"; import user from "../reducers/user"; import post from "../reducers/post"; import createSagaMiddleware from "redux-saga"; import rootSaga from "../sagas"; // redux-thunk를 참조해서 만든 미들웨어 const loggerMiddleware = ({ dispatch, getState }) => (next) => (action) => { return next(action); }; const sagaMiddleware = createSagaMiddleware(); function getServerState() { return typeof document !== "undefined" ? JSON.parse(document.querySelector("#__NEXT_DATA__").textContent)?.props.pageProps.initialState : undefined; } const serverState = getServerState(); const makeStore = () => { // configureStore: store 를 생성 const store = configureStore({ reducer: { user, post, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat([sagaMiddleware, loggerMiddleware]), preloadedState: serverState, // SSR }); store.sagaTask = sagaMiddleware.run(rootSaga); return store; }; const wrapper = createWrapper(makeStore, { debug: process.env.NODE_ENV === "development", }); export default wrapper; /reducers.js/post.js import { HYDRATE } from "next-redux-wrapper"; import { createSlice } from "@reduxjs/toolkit"; import shortId from "shortid"; import produce from "immer"; 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, likePostLoading: false, // 좋아요 시도중 likePostDone: false, likePostError: null, unLikePostLoading: false, // 좋아요 취소중 unLikePostDone: false, unLikePostError: null, uploadImagesLoading: false, // 이미지 업로드 시도중 uploadImagesDone: false, uploadImagesError: null, retweetLoading: false, // 리트윗 시도중 retweetDone: false, retweetError: null, }; const postSlice = createSlice({ name: "post", initialState, reducers: {loadPostsRequestAction: (state, action) => { console.log("-------------------요청-------------------"); state.loadPostsLoading = true; state.loadPostsDone = false; state.loadPostsError = null; }, loadPostsSuccessAction: (state, action) => { console.log("-------------------성공-------------------"); state.loadPostsLoading = false; state.loadPostsDone = true; state.mainPosts = [... state.hasMorePosts = action.payload.length === 10; }, loadPostsFailureAction: (state, action) => { console.log("-------------------실패-------------------"); state.loadPostsLoading = false; state.loadPostsError = action.payload; }, }, extraReducers: (builder) => builder .addCase(HYDRATE, (state, action) => { console.log("HYDRATE", action); return { ...state, ...action.payload.post, }; }) .addDefaultCase((state) => state), }); export const { loadPostsRequestAction, loadPostsSuccessAction, loadPostsFailureAction, } = postSlice.actions; export default postSlice.reducer;
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
getServerSideProps에서 데이터가 제대로 내려오지 않는듯합니다.
안녕하세요 제로초님https://github.com/ZeroCho/react-nodebird/blob/master/toolkit/front/pages/index.js위 내용을 참고해서코드를 작성해보았습니다.import { useDispatch, useSelector } from "react-redux"; import AppLayout from "../components/AppLayout"; import PostForm from "../components/PostForm"; import PostCard from "../components/PostCard"; import { useEffect } from "react"; import { loadPostsRequestAction } from "../reducers/post"; import { loadMyInfoRequestAction } from "../reducers/user"; import wrapper from "../store/configurStore"; import axios from "axios"; // 프론트, 브라우저 같이 실행 const Home = () => { const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePosts, loadPostsLoading, retweetError } = useSelector((state) => state.post); const dispatch = useDispatch(); useEffect(() => { if (retweetError) { alert(retweetError); } }, [retweetError]); useEffect(() => { const onScroll = () => { if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) { if (hasMorePosts && !loadPostsLoading) { const lastId = mainPosts[mainPosts.length - 1]?.id; dispatch(loadPostsRequestAction({ lastId, limit: 10 })); } } }; window.addEventListener("scroll", onScroll); return () => { window.removeEventListener("scroll", onScroll); }; }, [hasMorePosts, loadPostsLoading, mainPosts]); return ( <AppLayout> {me && <PostForm />} {/* 순서가 바뀌거나 삭제될 수 있는 리스트들에 key값으로 index를 쓰면 안됀다. */} {/* 반복문이 있고 바뀌지 않는 리스트일 경우에만 사용해도 된다. */} {mainPosts.map((post) => ( <PostCard key={post.id} post={post} /> ))} </AppLayout> ); }; export const 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(loadPostsRequestAction()); await store.dispatch(loadMyInfoRequestAction()); console.log("state", store.getState()); }); export default Home; 그런데 await에서 await' has no effect on the type of this expression.ts(80007이런 경고문 때문인지이전 영상에서 말씀해주셨던것처럼 데이터를 success까지 기다리지 않는건지,빈값들만 오고 있습니다.혹시 어디를 살펴보면 좋을지 알 수 있을까요?{ "name": "react-nodebird", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "next dev", "build": "next build", "start": "next start" }, "author": "", "license": "MIT", "dependencies": { "@ant-design/icons": "^5.3.6", "@reduxjs/toolkit": "^2.2.3", "antd": "^5.8.3", "axios": "^1.6.8", "next": "^14.2.3", "next-redux-wrapper": "^8.1.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.51.3", "react-redux": "^9.1.1", "react-slick": "^0.30.2", "redux": "^5.0.1", "shortid": "^2.2.16", "styled-components": "^6.1.8" }, "devDependencies": { "@faker-js/faker": "^8.4.1", "babel-eslint": "^10.1.0", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsx-a11y": "^6.8.0", "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.2" } } 환경은 위와같습니다.감사합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
다른 페이지갔다가 오면 게시글 불러와지는 이슈
게시글을 끝까지 스크롤하고,다른 페이지에 다녀오면다른 페이지갔다가 오면 게시글 불러와지는 이슈를 발견했습니다.index.js에서 게시글 불러오는 부분에mainposts의 조건을 붙여서 실행하면될거같은데차후 강의에서 해결해주시는 이슈인지 궁금합니다.아니면 저만 그런건지 여쭤봅니다!감사합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
도메인과 S3연결 후 AWS 요금 과금문제 문의드립니다.
강의를 보고 따라하다가 오늘 보니 저번 달 사용량에 관련된 요금이 결제되었다고 메일을 받았습니다.. Amazon Virtual Private Cloud 에서 좀 많이 나왔고,,Amazon Route 53에도 요금이 나왔네요..총 만원정도 과금이 되었는데 어떤건지 몰라서 어디부분을 삭제해야 비용이 발생하지 않는걸까요?강의를 잘 보고 따라했는데.. 당황스럽습니다...
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
회원가입하고나서 로그인 풀리는 현상
안녕하세요회원가입 후에 로그인이 된 상태여야 하는데로그인이 풀리는 현상을 확인했습니다. 왜 인지 찾아보니프론트에선 회원가입 후 더미데이터를 가지고 로그인 하고 있었음백단에서는 회원가입 유저의 정보를 내려주지 않고 있었음.이런 경우때문인 것 같았습니다.그래서 회원가입 후 메인 페이지로 라우팅 되면메인페이지에서 로그인 유무를 판단하는 로직이 실행되고,더미데이터로 있던 데이터를 로그인 풀어버려서 그런게 아닐까 합니다. 그러면/back/routes/user.jsconst user = await User.create({ email, nickname, password: hashedPassword, }); 여기서 user정보를 내려줘야할거 같은데여기서 더 필요한 post, image같은 이미지는 어떻게 추가해야하는지 궁금합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
센셕4 게시물 불러오기 postcard.js에서 post.User.nickname[0]에러
안녕하세요센셕4 게시물 불러오기 postcard.js에서 post.User.nickname[0]에러가 발생해서 처음엔 간단하게/components/postcard.js<Card.Meta avatar={<Avatar>{post.User?.nickname[0]}</Avatar>} title={post.User?.nickname} description={<PostCardContent postData={post.content} />} />이런식으로 해결했었습니다.아 같은 파일 위치에서{id && post.User?.id === id ? ( <> <Button type='primary' key='modify'> 수정 </Button> <Button type='danger' key={"delete"} onClick={onRemovePost} loading={removePostLoading}> 삭제 </Button> </> ) : ( <Button type='dashed' key={"report"}> 신고 </Button> )}post.User?.id이것도 같은 식으로 처리했었습니다.그런데 제로초님 코딩을 몇번 다시봤더니비슷한 에러가 코멘트에서 났었는데/routes/posts.jsconst express = require("express"); const router = express.Router(); const { Post, User, Image, Comment } = require("../models"); // GET /posts 여러 게시글 가져오기 router.get("/", async (req, res, next) => { try { const posts = await Post.findAll({ limit: 10, include: [ { model: User, attributes: ["id", "nickname"], }, { model: Image, }, { model: Comment, include: [ { model: User, attributes: ["id", "nickname"], }, ], }, ], }); res.status(200).json(posts); } catch (error) { console.error(error); next(error); } }); module.exports = router; 이런식으로 데이 필요한 id, nickname을 넣어주셔서 ?를 붙이지 않고 해결하셨더라구요.혹시 저 nickname부분도 위와같이 백단에서 코드를 수정해서 고칠수 있을까요?따라서 해봤는데 잘 안되서 여쭤봅니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
로그인 기능을 next-auth와 수업에서 처럼 직접 구현하는 것 어떤걸 더 추천하시나요?
안녕하세요 제로초님강의를 따라가다 보니 로그인 부분이 엄청 복잡하더라구요,개인적으로 next-auth을 사용하면 되게 간편했던걸로 기억하는데 제로초님은로그인 기능을 만들게 된다면 next-auth와 수업에서 처럼 직접 구현하는 것 어떤걸 더 추천하실지 궁금합니다.직접구현하는게 로그인이 어케 이뤄지는지 이해할수있어서 좋은거같은데 여쭤보고 싶었습니다!
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
redux toolkit, saga으로 하는중에 devtool에서 액션 명칭이 undefined로 나옵니다
안녕하세요 제로초님여쭤보고 싶은게 있습니다.강의를 따라가다가 데브툴에서 액션, 데이터들을 확인하시는 모습을 봤는데,저도 따라해보니 액션명칭도, state tree의 데이터들도 제대로 안나오는것 같더라구요.그래서 좀 테스트해보니까아래의 로직처럼put에 액션 함수를 실행하면 데브툴에서 액션 명칭니 나오고예시: put(액션명(데이터)),기존처럼put({type: 액션명, payload: 데이터}) 호출하게되면undefined로 나오는것 같습니다.function* addPost(action) { try { yield delay(1000); const id = shortid.generate(); yield put(addPostSuccessAction({ id, content: action.payload.text, }) ); yield put({ type: addPostToMe, payload: id }); } catch (err) { yield put({ type: addPostFailureAction, error: err.response.data }); } }아무래도 toolkit과 saga를 사용할때 저렇게 사용하는게 아닌가 싶은데undefined로 나오는 부분은 개선했다고 하더라도action 부분이 제로초님과 다르게 나오더라구요.액션명만 나오는게 아니라 reducer의 위치도 함께나온느데이렇게 나와도 괜찮은건지 여쭙고 싶습니다.다른 부분은 잘 작동하는것 같습니다. import { HYDRATE } from "next-redux-wrapper"; import { createSlice } from "@reduxjs/toolkit"; import shortId from "shortid"; export const initialState = { mainPosts: [ { id: "1", User: { id: "1", nickname: "zzimzzim", }, content: "첫 번째 게시글 #해시태그, #익스프레스", Images: [ { id: shortId.generate(), src: "https://loremflickr.com/cache/resized/65535_53669042936_630c778818_320_240_nofilter.jpg", }, { id: shortId.generate(), src: "https://loremflickr.com/cache/resized/65535_52982053835_12fc661207_320_240_nofilter.jpg", }, { id: shortId.generate(), src: "https://loremflickr.com/cache/resized/65535_52905479084_303bf25ec0_320_240_nofilter.jpg", }, ], Comments: [ { id: shortId.generate(), User: { nickname: "찜찜", }, content: "얼른 사고싶어요~", }, { id: shortId.generate(), User: { nickname: "hero", }, content: "리액트 넥스트 고수가 될테다~", }, ], createdAt: {}, }, ], imagePaths: [], addPostLoading: false, addPostDone: false, addPostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, }; const dummyPost = ({ id, content }) => { return { id, content, User: { id: "1", nickname: "WlaWla", }, Images: [], Comments: [], }; }; const dummyComment = (content) => ({ id: shortId.generate(), content: content, User: { id: "1", nickname: "WlaWla", }, }); const postSlice = createSlice({ name: "post", initialState, reducers: { addPostRequestAction: (state, action) => { state.addPostLoading = true; state.addPostDone = false; state.addPostError = null; }, addPostSuccessAction: (state, action) => { state.addPostLoading = false; state.addPostDone = true; state.mainPosts.unshift(dummyPost(action.payload)); }, addPostFailureAction: (state, action) => { state.addPostLoading = false; state.addPostError = action.error; }, addCommentRequestAction: (state, action) => { state.addCommentLoading = true; state.addCommentDone = false; state.addCommentError = null; }, addCommentSuccessAction: (state, action) => { const { content, userId, postId } = action.payload; const post = state.mainPosts.find((d) => d.id === postId); post.Comments.unshift(dummyComment(content)); state.addCommentLoading = false; state.addCommentDone = true; }, addCommentFailureAction: (state, action) => { state.addCommentLoading = false; state.addCommentError = action.error; }, }, extraReducers: (builder) => builder .addCase(HYDRATE, (state, action) => ({ ...state, ...action.payload.post, })) .addDefaultCase((state) => state), }); export const { addPostRequestAction, addPostSuccessAction, addPostFailureAction, addCommentRequestAction, addCommentSuccessAction, addCommentFailureAction } = postSlice.actions; export default postSlice.reducer; 사가 코드입니다.import { all, fork, delay, put, takeLatest } from "redux-saga/effects"; import axios from "axios"; import { addPostRequestAction, addPostSuccessAction, addPostFailureAction, addCommentRequestAction, addCommentSuccessAction, addCommentFailureAction } from "../reducers/post"; import { addPostToMe, removePostOfMe } 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); console.log("addPost saga", action); yield delay(1000); const id = shortid.generate(); yield put( addPostSuccessAction({ id, content: action.payload.text, }) ); yield put({ type: addPostToMe, payload: id }); } catch (err) { yield put({ type: addPostFailureAction, error: err.response.data }); } } function addCommentApi(data) { return axios.post(`/api/post/${data.postId}/comment`); } function* addComment(action) { try { // const result = yield call(addPostAPI, action.data); console.log("addComment saga", action); yield delay(1000); yield put({ type: addCommentSuccessAction, payload: action.payload, }); } catch (err) { console.log(err); yield put({ type: addCommentFailureAction, error: err.response.data, }); } } function* watchAddPost() { yield takeLatest(addPostRequestAction, addPost); } function* watchAddComment() { yield takeLatest(addCommentRequestAction, addComment); } export default function* postSaga() { yield all([fork(watchAddPost), fork(watchAddComment)]); }
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
제로쵸 선생님 ㅜ 리액트 문제는 아닌데 질문드려도 될까요?ㅜ
제로쵸 님의 강의를 여러개 듣고 있는 코딩 입문자입니다 ㅜ간단한 사진첩 클릭 사이트를 만들었는데요.스샷처럼 한쪽 사진만 계속 선택 했을 때는 함수가 한번만 실행되며 제가 원하는 아웃풋이 나오는데,다른 사진을 선택하면 함수가 여러번 실행이 되네요... 왜이러는걸까요? ㅜㅜ소스는 html+js+부트스트랩 로 짰습니다. 원본소스의 링크는 아래 올려놨습니다.https://drive.google.com/file/d/1Ov0Qik9ofDHli-TsblLw5OkUz7uCXTDo/view?usp=drive_link