묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
verticalAlign이 제대로 안먹어서 "N개의 사진 더보기" 의 위치가 이상하게 나올경우.
<> <div> // img 태그의 스타일에도 버티컬얼라인 미들 추가해주면 정상적으로 나옵니다. <img role="presentation" style={{ verticalAlign: 'middle' }} width="50%" src = {images[0].src} alt={images[0].src} onClick={onZoom} /> <div role="presentation" style={{ display: 'inline-block', width: '50%', textAlign: 'center', verticalAlign: 'middle' }} onClick={onZoom} > <PlusOutlined /> <br /> {images.length - 1 }개의 사진 더보기 </div> </div> </>사용 환경 Antd 5
-
미해결
react, redux, node.js 추천서
안녕하세요 react, redux, node.js를 활용하여 웹 앱 개발을 하려고 하는데react의 경우 class형과 함수형 두 가지의 타입으로 개발이 되고 있더군요그래서 함수형 react의 참고서를 찾아봤는데 참고할만한 교재를 선택하기에 어려움이 있어서 질문 드립니다.혹시 추천해주실만한 참고서가 있을까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
npx sequelize db:create 오류
비밀번호를 바꾼후에 npx sequelize db:create를 실행했는데,Unexpected token '(' 이라는 에러가 뜹니다.보통 이런경우 타이포 error 일것 같아서 파일을 살펴 봤는데 별다른 오타는 없었습니다.다른곳을 살펴봐야할 부분이 있을까요?감사합니다.
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
PostDetailPage params 이 어디서 온건가요?
async function PostDetailPage({ params }: any) { const post = await getPost(params.id); return ( <div> <h1>Posts/{post.id}</h1> <div> <h3>{post.title}</h3> <p>{post.created}</p> </div> </div> ); }PostDetailPage 에서 params을 콘솔에 출력하면 id값이 나오는거는 주소에서 받아오는건가요?
-
미해결Redux vs MobX (둘 다 배우자!)
섹션 3 mobx autorun 관련 질문입니다.
const state = observable({ name: "zero", age: 28, married: false, }); autorun(() => { console.log("autorun : " + state.name); }); reaction( () => state.name, () => { console.log("reaction : name changed"); } ); runInAction(() => { state.name = "nureongi"; state.age = 26; }); const action1 = action(() => { state.married = true; }); const action2 = action(() => { state.married = false; }) action1(); action2(); /** autorun : zero autorun : nureongi reaction : name changed **/안녕하세요! 강의 잘 듣고 있습니다. 제가 궁금한 것은 강의에서 autorun은 observable에 담긴 모든 state가 변경될 때 마다 실행된다고 하셨는데실제 실험해보니 결과가 조금 다르게 나온 것 같았습니다.그래서 확인해보니 다음과 같이 동작한다는 사실을 알게 되었습니다. autorun이 정의될 때 한 번 실행autorun 함수 내부에서 참조하고 있는 observable state가 변경될 때 실행 아마 버전이 업데이트 되면서 바뀐 것 같은데 제가 알게 된 사실이 맞을까요?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
노드 버드 강의에서 배포 파트 관련 질문
혹시 해당 강의에서 진행하는 배포 파트의 경우 아래 내용이 포함되어 있을까요?현재 강의 진행전에 따로 공부 중인 내용인데 혼자 진행하기엔 좀 햇갈리는 부분이 있어 강의에 포함된다면 강의를 먼저 들어보고 진행하려합니다. 노드버드 강의에 없더라도 다른강의에 상세하게 다루고 있다면 해당 강의 명을 부탁드립니다. 실무에 적용될수있는 CI/CD가 내용에 포함되어있는지?2. 빌드와 관련해 Webpack, ESBuild, SWC, Babel에 관한 내용이 있는지?3. 개인도메인으로 배포하는법
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
CORS 설정을 했지만 CORS오류가 발생합니다.
안녕하세요 제로초님 다름이 아니라 로그인 기능을 모두 구현하고 혹시나 원래 잘되던 회원가입이 안되나해서 돌려보던 참에 갑자기 회원가입에서 CORS오류가 발생해서 멘붕이 왔습니다.처음에 CORS설정하고 회원가입을 했을땐 잘 작동했었습니다...설정도 그대로구요.. 어떤게 문제인지 모르겠어서 질문남깁니다.콘솔 오류 사진 2.back/app.js 코드const express = require("express"); const postRouter = require("./routes/post"); const userRouter = require("./routes/user"); const cors = require("cors"); const session = require("express-session"); const passport = require("passport"); const cookieParser = require("cookie-parser"); const dotenv = require("dotenv"); const app = express(); //익스프레스 서버 const db = require("./models"); const passportConfig = require("./passport"); db.sequelize .sync() .then(() => { console.log("db 연결 성공"); }) .catch(console.error); dotenv.config(); passportConfig(); app.use(express.json()); //익스프레스서버에 뭔가를 장착하겠다. //프론트에서 Json 형식으로 받은 것을 req.body에 넣어준다. app.use(express.urlencoded({ extended: true })); //form에서 제출한 것을 넘겨준다. //front에서 보낸 action.data를 req.body에 넣어주느 역할 app.use( session({ saveUninitialized: false, resave: false, secret: process.env.COOKIE_SECRET, }) ); app.use(passport.initialize()); app.use(passport.session()); app.use(cookieParser(process.env.COOKIE_SECRET)); app.get("/", (req, res) => { res.send("hello express"); }); app.get("/api", (req, res) => { res.send("hello api"); }); app.get("/api/posts", (req, res) => { res.json([ { id: 1, content: "hello" }, { id: 2, content: "hello2" }, { id: 3, content: "hello3" }, ]); }); app.use( cors({ origin: true, credentials: false, }) ); //cors설정 //res.setHeader("Access-Control-Allow-Origin", "http://localhost:3060"); //CORS해결법 *은 모든 주소에 대해서 라는 뜻 //localhost 3060에서 온 것은 허용해주게싸 app.use("/post", postRouter); //"/post"가 중복되므로 앞으로 뽑아줄 수 있다. app.use("/user", userRouter); //"/post"가 중복되므로 앞으로 뽑아줄 수 있다. app.listen(3065, () => { console.log("서버 실행 중"); });
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
회원가입 status 500 findOne 오류
안녕하십니까 제로초님 회원가입 구현 관련 질문드립니다.강의를 보며 코딩을 하였고 오타도 없는 것을 확인을 했습니다.그런데 response로 계속 500이 들어오며 findOne 관련 오류가 납니다.. 혹시 해결책을 알수있을까요?models/user.js//model은 mysql의 테이블과 같은 개념이다. module.exports = (sequelize, DataTypes) => { const User = sequelize.define( //여기서 User는 모델이름 => 자동으로 소문자화되고 복수형이 된다. ex)users "User", { //id는 mysql에서 자동으로 넣어주기 때문에 필요없다. email: { type: DataTypes.STRING(30), //STRIN, INTEGER, BOOLEAN, FLOAT, DATATIME 등이 자주 사용된다. //이메일은 문자열이고 30글자 이내여야한다. allowNull: false, //false면 필수 -> 무조건 입력해야함. unique: true, //이메일은 고유한 값이어야함. 중복값이 있으면 안된다. }, nickname: { type: DataTypes.STRING(30), allowNull: false, //false면 필수 -> 무조건 입력해야함. }, password: { type: DataTypes.STRING(100), //비밀번호는 암호화를 하게되면 길이가 늘어나기 때문에 여유있게 100글자 allowNull: false, //false면 필수 -> 무조건 입력해야함. }, }, { charset: "utf8", collate: "utf8_general_ci", //한글 저장 } ); User.associate = (db) => { db.User.hasMany(db.Post); //한 사람이 포스트를 여러개 가질 수 있음 db.User.hasMany(db.Comment); //한 사람이 댓글 여러개 가질 수 있음 db.User.belongsToMany(db.Post, { through: "Like", as: "Liked" }); //게시글 좋아요와 유저는 다대다 관계, 중간 테이블으 이름은 Like db.User.belongsToMany(db.User, { through: "Follow", as: "Followers", foreignKey: "FollowingId", }); db.User.belongsToMany(db.User, { through: "Follow", as: "Followings", foreignKey: "Followerid", }); //내가 팔로잉하는 사람을 찾으려면 나를 먼저 찾아야 한다. }; return User; };routes/user.jsconst express = require("express"); const { User } = require("../models"); const router = express.Router(); const bcrypt = require("bcrypt"); router.post("/", async (req, res, next) => { // POST /user/ try { const exUser = await User.findOne({ where: { email: req.body.email, }, }); if (exUser) { return res.status(403).send("이미 사용 중인 아이디입니다."); //여기서 리턴을 해주지 않으면 밑에 있는 res.json() 과 더불어서 응답이 두번이라 안된다. } const hashedPassword = await bcrypt.hash(req.body.password, 12); await User.create({ //순서를 맞춰주기 위한 Await //테이블안에 데이터 넣기 email: req.body.email, nickname: req.body.nickname, password: hashedPassword, //여기서 req.body가 프론트엔드 Signup에서 보낸 action.data와 같다. }); res.status(201).send("ok"); } catch (error) { console.error(error); next(error); // status 500 } }); module.exports = router;back/app.jsconst express = require("express"); const cors = require("cors"); const postRouter = require("./routes/post"); const userRouter = require("./routes/user"); const app = express(); const db = require("./models"); db.sequelize .sync() .then(() => { console.log("db 연결 성공"); }) .catch(console.error); app.use(express.json()); app.use(express.urlencoded({ extended: true })); //여기서 use는 express서버에서 뭔가 장착한다는 뜻 //또한 이 두 문장이 프론트에서 받아온 action.data를 req.body에 넣어준다는 뜻 app.get("/", (req, res) => { res.send("hello express"); }); app.get("/api", (req, res) => { res.send("hello api"); }); app.get("/api/posts", (req, res) => { res.json([ { id: 1, content: "hello" }, { id: 2, content: "hello2" }, { id: 3, content: "hello3" }, ]); }); app.use( cors({ origin: "*", //모두 허용 credentials: false, }) ); app.use("/post", postRouter); //"/post"가 중복되므로 앞으로 뽑아줄 수 있다. app.use("/user", userRouter); app.listen(3065, () => { console.log("서버 실행 중"); });
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
netlify를 배포를 했는데 문제 생겼습니다
netlify를 배포 했는데 경고와 빌드 문제 인 것 같습니다 어떻게 하면 될까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
다이나믹 라우팅 안에서 다른 다이나믹 라우팅으로 이동할때 발생하는 에러.
안녕하세요!user/[id].js 페이지 안에서 hashtag/[tag] 페이지로 이동하는 버튼을 만들에 사용 하면 알맞은 페이지로 이동하는 것이 아니라 user/hashtag/[tag]로 이동하게 됩니다.참고로, hashtag/[tag]로 이동하는 버튼을 Applayout에 추가 하고 user/[id]페이지에서 Applayout 컴퍼턴트로 감싸 줬습니다. 이 페이지에서 상단에 Search버튼을 누르면,이런 페이지로 이동합니다...import React,{useCallback} from 'react' import {HStack, Input, Button, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, useDisclosure} from '@chakra-ui/react' import {BiSearch} from 'react-icons/bi' import { css } from '@emotion/react' import Router from 'next/router' import PropTypes from 'prop-types' import useInput from '../hooks/useInput' import Spacer from './CustomizedUI/Spacer' const buttonCss = css` :hover { background-color: transparent; color: #1890ff; } `; export default function SearchButton({type, children}) { const { isOpen, onOpen, onClose } = useDisclosure(); const [value, onChangeValue, setValue] = useInput('') const onClickHandler = useCallback(()=>{ Router.push(`hashtag/${value}`); },[value]) const ConditionButton = type; return ( <> <ConditionButton variant="ghost" aria-label="Search Button" icon={<BiSearch />} leftIcon={type === Button && <BiSearch />} onClick={onOpen} fontSize="25px" css={buttonCss} > {children} </ConditionButton> <Modal isOpen={isOpen} onClose={onClose} size="md" isCentered> <ModalOverlay /> <ModalContent> <ModalHeader>Search</ModalHeader> <ModalCloseButton /> <ModalBody> <HStack> <Input type="text" value={value} onChange={onChangeValue} required /> <Button onClick={onClickHandler}>Search</Button> </HStack> <Spacer /> </ModalBody> </ModalContent> </Modal> </> ); } SearchButton.propTypes = { type: PropTypes.elementType.isRequired, children: PropTypes.node.isRequired, }이것은 제가 만든 컴퍼넌트인데, 혹시 어디서 문제가 있는걸까요? 혹시나 해서 Chakra UI 상의 문제 아닐까 해서 antd로도 해봤는데, 같은 오류가 납니다.신기한 오류라서 질문드립니다.감사합니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
sql에 User테이블만 존재하고 utf관련 오류와 sequelizeDatabaseError가 납니다.
안녕 하십니까 제로초님다름이 아니라 강의를 수강하는 중에 아래와 같은 오류가 발생했는데 해결법을 모르겠어서 질문을 남깁니다.제로초님이 말씀하신대로 npx sequelize db:create를 하고테이블을 봤는데 user밖에 없어서 터미널을 봤더니 아래와같은 오류가 났습니다. 찾아보려고 했지만 도저히 모르겠어서 질문을 남깁니다.<오류코드>Executing (default): SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = 'Users' AND TABLE_SCHEMA = 'react-nodebird'Executing (default): SHOW INDEX FROM Users FROM react-nodebirdExecuting (default): SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME = 'Posts' AND TABLE_SCHEMA = 'react-nodebird'Executing (default): CREATE TABLE IF NOT EXISTS Posts (`id` INTEGER NOT NULL auto_increment , content TEXT NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, UserId INTEGER, RetweetId INTEGER, PRIMARY KEY (`id`), FOREIGN KEY (`UserId`) REFERENCES Users (`id`) ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY (`RetweetId`) REFERENCES Posts (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8_general_ci;Error at Query.run (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/sequelize/lib/dialects/mysql/query.js:52:25) at /Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/sequelize/lib/sequelize.js:315:28 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async MySQLQueryInterface.createTable (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/sequelize/lib/dialects/abstract/query-interface.js:98:12) at async Post.sync (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/sequelize/lib/model.js:942:7) at async Sequelize.sync (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/sequelize/lib/sequelize.js:377:9) { name: 'SequelizeDatabaseError', parent: Error: COLLATION 'utf8mb3_general_ci' is not valid for CHARACTER SET 'utf8mb4' at Packet.asError (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/packets/packet.js:728:17) at Query.execute (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/commands/command.js:29:26) at Connection.handlePacket (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/connection.js:478:34) at PacketParser.onPacket (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/connection.js:97:12) at PacketParser.executeStart (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/packet_parser.js:75:16) at Socket.<anonymous> (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/connection.js:104:25) at Socket.emit (node:events:517:28) at addChunk (node:internal/streams/readable:335:12) at readableAddChunk (node:internal/streams/readable:308:9) at Readable.push (node:internal/streams/readable:245:10) { code: 'ER_COLLATION_CHARSET_MISMATCH', errno: 1253, sqlState: '42000', sqlMessage: "COLLATION 'utf8mb3_general_ci' is not valid for CHARACTER SET 'utf8mb4'", sql: 'CREATE TABLE IF NOT EXISTS Posts (`id` INTEGER NOT NULL auto_increment , content TEXT NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, UserId INTEGER, RetweetId INTEGER, PRIMARY KEY (`id`), FOREIGN KEY (`UserId`) REFERENCES Users (`id`) ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY (`RetweetId`) REFERENCES Posts (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8_general_ci;', parameters: undefined }, original: Error: COLLATION 'utf8mb3_general_ci' is not valid for CHARACTER SET 'utf8mb4' at Packet.asError (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/packets/packet.js:728:17) at Query.execute (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/commands/command.js:29:26) at Connection.handlePacket (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/connection.js:478:34) at PacketParser.onPacket (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/connection.js:97:12) at PacketParser.executeStart (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/packet_parser.js:75:16) at Socket.<anonymous> (/Users/ihyeoncheol/Desktop/nodebird_project/prepare/back/node_modules/mysql2/lib/connection.js:104:25) at Socket.emit (node:events:517:28) at addChunk (node:internal/streams/readable:335:12) at readableAddChunk (node:internal/streams/readable:308:9) at Readable.push (node:internal/streams/readable:245:10) { code: 'ER_COLLATION_CHARSET_MISMATCH', errno: 1253, sqlState: '42000', sqlMessage: "COLLATION 'utf8mb3_general_ci' is not valid for CHARACTER SET 'utf8mb4'", sql: 'CREATE TABLE IF NOT EXISTS Posts (`id` INTEGER NOT NULL auto_increment , content TEXT NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, UserId INTEGER, RetweetId INTEGER, PRIMARY KEY (`id`), FOREIGN KEY (`UserId`) REFERENCES Users (`id`) ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY (`RetweetId`) REFERENCES Posts (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8_general_ci;', parameters: undefined }, sql: 'CREATE TABLE IF NOT EXISTS Posts (`id` INTEGER NOT NULL auto_increment , content TEXT NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, UserId INTEGER, RetweetId INTEGER, PRIMARY KEY (`id`), FOREIGN KEY (`UserId`) REFERENCES Users (`id`) ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY (`RetweetId`) REFERENCES Posts (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8_general_ci;', parameters: {}}
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
Netlify 배포 가능할까요?
혹시 넷플릭스로 Netlify 배포 가능할까요?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
리액트 쿼리 SSR 부분 질문있습니다.
강의 프로젝트를 제로초님 깃헙 코드 참고해서 리액트 쿼리로 다시 만들어 보는중인데요 회원가입 페이지에 SSR 적용하는 과정에서 다음과 같은 에러가 납니다. 코드를 보면//pages/signup.tsx export const getServerSideProps = async (context: GetServerSidePropsContext) => { const cookie = context.req ? context.req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; if (context.req && cookie) { axios.defaults.headers.Cookie = cookie; } const response = await loadMyInfoAPI(); if (response.data) { return { redirect: { destination: '/', permanent: false, }, }; } return { props: {}, }; }; // api/user.ts export function loadMyInfoAPI() { return axios.get('/user').then((response) => response.data); } // routes/user.js router.get('/', async (req, res, next) => { try { if (req.user) { const fullUwerWithoutPassword = await User.findOne({ where: { id: req.user.id }, attributes: { exclude: ['password'] }, include: [ { model: Post, attributes: ['id'] }, { model: User, as: 'Followings', attributes: ['id'] }, { model: User, as: 'Followers', attributes: ['id'] }, ], }); res.status(200).json(fullUwerWithoutPassword); } else { res.status(200).json(null); } 로그인안한 상태에서 유저 정보를 불러오면 null이 반환되서 에러가 나는거 같은데요 const response = await loadMyInfoAPI(); if (response.data) { return { redirect: { destination: '/', permanent: false, }, }; } 이부분을 지우고 다음과 같이 바꿔주면export const getServerSideProps = async (context: GetServerSidePropsContext) => { const cookie = context.req ? context.req.headers.cookie : ''; axios.defaults.headers.Cookie = ''; if (context.req && cookie) { axios.defaults.headers.Cookie = cookie; } return { props: {}, }; }; 에러가 사라지는데 지워도 상관없는건가요??
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
process.env.REACT_APP_MOVIE_DB_API_KEY를 찾지 못하고 있어요
.env 생성하고 axios.js에서 process.env.REACT_APP_MOVIE_DB_API_KEY를 찾지 못하고 있어요process.env.REACT_APP_MOVIE_DB_API_KEY=00000000
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요. 게시글 필터링 관련해서 질문이있습니다.
안녕하세요. 현재 여러개의 select - option으로 게시된 게시글을 필터링을 해보려고 하고 있고 잘 작동하는데 코드가 너무 지저분합니다. 혹시 좀더 효율적으로 짤 수 있는 팁주실수 있으면 감사하겠습니다. ㅠㅠ const coursePosts = useSelector((state) => state.coursePost.coursePosts); const [group, setGroup] = useState("all"); const [subject, setSubject] = useState("all"); const [level, setLevel] = useState("all"); const fillteredCoursePosts = coursePosts.filter((post) => ( //문제의 부분 start group === 'all' && subject === 'all' && level === "all" ? post : subject === 'all' && level === "all" ? (post.group === group) : group === 'all' && level === "all" ? (post.subject === subject) : group === 'all' && subject === "all" ? (post.level === level) : group === "all" ? (post.subject === subject) && (post.level === level) : subject === "all" ? (post.group === group) && (post.level === level) : level === "all" ? (post.group === group) && (post.subject === subject) : (post.group === group) && (post.subject === subject) && (post.level === level) ) //문제의 부분 End ) const handleGroupChange = (e) => { setGroup(e.target.value); } const handleSubjectChange = (e) => { setSubject(e.target.value); } const handleLevelChange = (e) => { setLevel(e.target.value); } return ( <> <main className="width_content"> <div className={styles.course_box_wrap}> <div className={styles.filter_header}> <div className={styles.ft_select}> <select onChange={handleGroupChange}> <option value="all">ALL</option> <option value="Group1">Group 1</option> <option value="Group2">Group 2</option> <option value="Group3">Group 3</option> </select> <select onChange={handleSubjectChange}> <option value="all">ALL</option> <option value="English">English</option> <option value="Korean">Korean</option> </select> <select onChange={handleLevelChange}> <option value="all">ALL</option> <option value="Core">Core</option> <option value="test1">Embed</option> <option value="test2">Test</option> </select> </div> </div> </div> </main> </> ) }
-
미해결처음 만난 리덕스(Redux)
상태관리 불변성 질문드립니다
안녕하세요 소플님 리액트와 리덕스툴킷을 같이 사용 할때 궁금한 점이 있어 질문 드립니다상태관리에서 데이터를 계속 해서 추가 할 때 불변성을 유지하나요?상태관리에서 데이터를 수정, 삭제 할 때 불변성을 유지하나요?
-
미해결프로젝트로 배우는 React.js
AxiosError: Network Error가 뜹니다
13강에서 DB에 데이터를 저장할때 타이틀과 바디에 글을 쓴 뒤에 포스트 버튼을 누르면콘솔창과 화면에 이렇게 뜹니다..ㅠㅠ왜이럴까요..?
-
해결됨[리뉴얼] 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를 보내줘야 될 거 같은데 여기서 막혀서 감이 도무지 잡히질 않습니다.
-
해결됨[리뉴얼] 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; }) 툴킷쪽은 이런식으로 작성했습니다.