묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
const{}=getDiaryAnalysis() 부분이 잘 이해가 안가요
const{goodCount,badCount,goodRatio}=getDiaryAnalysis() 7:40초에 이 코드가getDiaryAnlysis()를 통해 받아온 것을각각 다시 할당 하는건가요?
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
useCallback이 적용 되지 않습니다.. (onRemove, onEdit)
안녕하세요 강사님! 강의 잘 보고 있습니다 :)섹션 5 - 최적화 4 강의에서 5분정도 쯤에 나오는 내용에 관한 것 입니다.저와 같은 질문을 하신 분의 답변을 보면, DiaryList에서 DiaryItem 컴포넌트의 key 값을 인덱스를 사용했기 때문에 자꾸 렌더링이 되고 강의에서는 dataId 를 사용해서 렌더링이 안된다고 답변 주셨는데, 저는 dataId를 key로 사용하고 있는데도 렌더링이 일어납니다..! 왜 그럴까요..ㅠㅠhttps://github.com/LEESOLL/EmotionDiary/commit/5acc047e645124b86601161ab7f3d3b40a4c62cc깃 링크 첨부하여 드립니다..!
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
next js 무한 렌더링 문제
import React, { useState } from "react"; const ToggleButtons = () => { const [buttonAActive, setButtonAActive] = useState(false); const [buttonBActive, setButtonBActive] = useState(false); const handleButtonClick = (button) => { if (button === "A") { setButtonAActive((prev) => !prev); setButtonBActive(false); } else if (button === "B") { setButtonBActive((prev) => !prev); setButtonAActive(false); } }; return ( <div> <button onClick={() => handleButtonClick("A")} style={{ backgroundColor: buttonAActive ? "green" : "white" }} > Button A </button> <button onClick={() => handleButtonClick("B")} style={{ backgroundColor: buttonBActive ? "red" : "white" }} > Button B </button> </div> ); };next js로 버튼 A와 B가 있는데 A가 켜져있는 상태에서 B를 누르면 A가 꺼지고 B가 켜져있는 상태에서 A를 누르면 꺼지고 A가 켜져있는 상태에서 A를 또 누르면 꺼지고 B를 누른 상태에서 B를 또 누르면 꺼지는 버튼 2개를 만들고 있는데 이렇게 코드를 작성하니까 무한 렌더링이 걸리는데 해결 방법이 있을까요?
-
해결됨비전공자를 위한 진짜 입문 올인원 개발 부트캠프
강의에 사용되는 노션 링크가 어디있을까요?
다른 수강생분들에게도 문제 해결에 도움을 줄 수 있도록 좋은 질문을 남겨봅시다 :) 1. 질문은 문제 상황을 최대한 표현해주세요.2. 구체적이고 최대한 맥락을 알려줄 수 있도록 질문을 남겨 주실수록 좋습니다. 그렇지 않으면 답변을 얻는데 시간이 오래걸릴 수 있습니다 ㅠㅠex) A라는 상황에서 B라는 문제가 있었고 이에 C라는 시도를 해봤는데 되지 않았다!3. 먼저 유사한 질문이 있었는지 꼭 검색해주세요! 강의에 사용하시는 노션 링크가 영상 하단에 있다고 커뮤니티에서 찾아보았는데, 아무리 찾아도 없어서, 혹시 어디서 찾아볼수있을까요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
post login model.findone() no longer accepts a callback
callback문법이 적용이 안되서 바꿔 성공했는데 좋은 코드인지는 모르겠습니다 참고 하실분은 참고해주세요app.post("/api/users/login", async (req, res) => { try { // 같은 이메일의 유저가 있는지 확인 const user = await User.findOne({ email: req.body.email }); if (!user) { return res.json({ loginSuccess: false, message: "제공된 이메일에 해당하는 유저가 없습니다.", }); } // 비밀번호 확인 const isMatch = await user.comparePassword(req.body.password); if (!isMatch) { return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다.", }); } // 토큰 쿠키에 저장 const userdata = await user.generateToken(); // 토큰을 저장한다. 어디에? 쿠키, 로컬스토리지 res .cookie("x_auth", userdata.token) .status(200) .json({ loginSuccess: true, userId: userdata._id }); } catch (err) { return res.status(400).send(err); } }); userSchema.methods.comparePassword = function (plainPassword) { // plainpassword와 db에 암호화된 비밀번호가 같은지 확인 const result = bcrypt.compare(plainPassword, this.password); return result; }; userSchema.methods.generateToken = async function (cb) { var user = this; // jsonwebtoken을 이용해서 token을 생성하기 var token = jwt.sign(user._id.toHexString(), "secretToken"); // user._id(db의 _id) + secreToken = token // token으로 user를 판별할 수 있다. // user.token = token; user.token = token; try { const savedUser = await user.save(); return user; } catch (err) { return err; } };
-
미해결Do it! Node.js 프로그래밍 입문
updateContact 함수관련 문의
updateContact 함수에서 findByIdAndUpdate 을 사용하지 않고 아이디를 가져와서 save 함수를 사용한 이유가 있나요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
playground에서 코딩할 떄 팁 없나요?
fetchProduct,updateProduct,deleteProduct등 플레이 그라운드에서 코딩할 때mutation{ updateProduct(productId:"7a40df24-4feb-43d0-b07e-c5855437a441" updateProductInput:{ name:"가나" }) query{ fetchProducts{ id name description }} 중괄호,소괄호 쓰는데어떤 위치에서 중괄호,소괄호를 사용해야하는지 팁 없나요?
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
그림 링크 오류
Failed to load resource: the server responded with a status of 403 () 이런 에러가 뜨는데 어떻게 해결하나요ㅜ
-
미해결JavaScript 비동기 프로그래밍 완벽 가이드 - Promise, await, async
Promise 객체 강의 7분 27초 부분 질문입니다.
const p = add(1,2);console.log(p); //Promise { <pending> }const t = p.then(....).catch(....).finally(...)이 때 총 4개의 각각 다른 프라미스가 생성된 상태라고 이해했습니다.1) p - 프라미스 P1 (add 에서 리턴한 프라미스)2) p.then - 프라미스 P2 (P1.then 에서 리턴한 프라미스)3) p.then().catch() - 프라미스 P3 (P2.catch 에서 리턴한 프라미스)4) p.then().catch.().finally() - 프라미스 P4 (P3.finally 에서 리턴한 프라미스)결과는 아래처럼 되는거라고 생각했구요.P1가 리졸브(3) 이므로 then 실행했고 3 출력, 리턴이 없으므로 리졸브-언디파인드P2는 fulfilled(리졸브-언디파인드) 이므로 catch 실행했고 에러가 아니므로 리졸브-언디파인드P3는 fulfilled(리졸브-언디파인드) / rejected 이므로 finally 실행했고 'finally' 출력, 리턴이 없으므로 리졸브-언디파인드P4는 함수반환과 관계없이 P3와 동일한 상태임. fulfilled(리졸브-언디파인드) 인데 등록된 콜백이 없으므로 이대로 끝.그런데 강의에서 설명하실 때는 P1 프라미스에then, catch, finally 함수로 콜백을 등록한 것처럼 느껴져서요.제가 이해한 내용이 맞는지 궁금합니다.
-
해결됨[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
setCookie 질문입니다.
안녕하십니까 제로초님 질문이 있어 글을 올립니다.현재 next.js를 이용하여 localhost:3000 포트를 이용해 front를 진행하고 있고, back은 제로초님의 강의를 이용하여 localhost:8001 포트로 서버를 만들어 진행하고 있습니다. 그러다, express passport 로그인 후 setCookie로 쿠키에 값을 저장하는 과정에서, front 서버 localhost:3000 포트에서는네트워크 탭에는 위와 같이 Set-Cookie로 명시 되어 있으나, 애플리케이션 탭의 쿠키에는 저장이 되지 않았습니다. 그래서 postman으로 실험을 해본 결과 postman에서는 정상적으로 쿠키가 저장된 모습을 확인할 수 있었습니다. 때문에 이와 같은 경우를 계속 찾아보고, 검색을 해보았는데, 브라우저의 쿠키 정책에 따라, 서로 다른 도메인 간에는 쿠키를 공유할 수 없습니다. 이를 해결하기 위해서는 서버에서 쿠키를 설정할 때 samesite 옵션을 none으로 설정 후 https 를 이용해 통신해야 한다고 나왔었습니다. 위와 같은 설명 때문에 localhost:3000 에서는 쿠키값이 저장이 안 된 것이 맞는 지 궁금합니다.
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 기본 강의
model.findone() no longer accepts a callback
더이상 저 문법을 지원하지 않는다고 해서 이것저것 코드 수정해 봤는데...비밀번호가 일치해도 일치한다는 메세지가 포스트맨에 나오지 않고에러도 뜨고 있습니다[에러 메세지] cb(null, isMatch); ^TypeError: cb is not a function at C:\Users\wwww\Documents\boiler-plate\models\User.js:69:5 index.js와 user.js를 첨부합니다 혹시 해결방법을 알 수 있을까요?// http://localhost:5000/ const express = require("express"); const app = express(); const port = 5000; const bodyParser = require("body-parser"); const cookieParser = require("cookie-parser"); const config = require("./config/key"); const { User } = require("./models/User"); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(cookieParser()); const mongoose = require("mongoose"); mongoose .connect(config.mongoURI) .then(() => console.log("MongoDB Connected...")) .catch((err) => console.log(err)); app.get("/", (req, res) => { res.send("Hello World!~~안녕 새해복"); }); app.post("/register", async (req, res) => { try { const user = new User(req.body); await user.save(); return res.status(200).json({ success: true, }); } catch (err) { return res.json({ success: false, err: err.message, }); } }); app.post("/api/users/login", async (req, res) => { try { // 요청된 이메일을 데이터베이스에서 찾기 const user = await User.findOne({ email: req.body.email }); if (!user) { return res.json({ loginSuccess: false, message: "제공된 이메일에 해당하는 유저가 없습니다.", }); } const isMatch = await user.comparePassword(req.body.password); if (!isMatch) { return res.json({ loginSuccess: false, message: "비밀번호가 틀렸습니다.", }); } // 비밀번호가 일치하면 토큰 생성 const userWithToken = await user.generateToken(); // 토큰 저장 res .cookie("x_auth", userWithToken.token) .status(200) .json({ loginSuccess: true, userId: userWithToken._id }); } catch (err) { return res.status(400).send(err); } }); app.listen(port, () => { console.log(`Example app listening on port ${port}`); }); const mongoose = require("mongoose"); // 몽구스 모듈을 가져와 몽고DB와 연결 const bcrypt = require("bcrypt"); const saltRounds = 10; const jwt = require("jsonwebtoken"); // 몽구스를 이용해 스키마 생성(필드들 작성) const userSchema = mongoose.Schema({ name: { type: String, maxlength: 50, }, email: { type: String, trim: true, unique: true, }, password: { type: String, minlength: 5, maxlength: 100, }, lastname: { type: String, maxlength: 50, }, role: { type: Number, default: 0, }, image: String, token: { type: String, }, tokenExp: { type: Number, }, }); userSchema.pre("save", function (next) { var user = this; if (user.isModified("password")) { bcrypt.genSalt(saltRounds, function (err, salt) { if (err) return next(err); bcrypt.hash(user.password, salt, function (err, hash) { if (err) return next(err); user.password = hash; next(); }); }); } else { next(); } }); // comparePassword userSchema.methods.comparePassword = function (PlainPassword, cb) { bcrypt.compare(PlainPassword, this.password, function (err, isMatch) { if (err) return cb(err); cb(null, isMatch); }); }; userSchema.methods.generateToken = function () { var user = this; return new Promise((resolve, reject) => { var token = jwt.sign(user._id.toHexString(), "secretToken"); user.token = token; user .save() .then((user) => resolve(user)) .catch((err) => reject(err)); }); }; const User = mongoose.model("User", userSchema); module.exports = { User };
-
미해결JavaScript 비동기 프로그래밍 완벽 가이드 - Promise, await, async
안녕하세요. await async 강의 7분31초 부분에서 질문입니다. 7분 31초 부분에서 질문있습니다.
안녕하세요. const p = add(1,2);console.log(p); 여기서 Promise { <pending> } 이 출력되는데요.add 함수 내부에서 await setTimeout 호출되면 add 함수는 더 진행되지 않는다고 하셨습니다. 그럼 return 도 못한건데 왜 p 에 값이 Promise 가 들어왔는지 궁금합니다..
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 프론트엔드 코스
학습자료는 어디에있나요
제가 잘못찾는것인지...학습자료, 노션링크 위치를 찾을수가 없어요...ㅠㅠ
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
10-08 product.service.ts에 final(), findOne에
productTags는 왜 relations에 넣지 않는건가요? 자동으로 되는건가요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
playground 관련 질문드립니다.
안녕하세요.GraphQL로 서버를 개발하면, 프론트 개발자는 Playground를 보고 API 사양을 확인하게 될 것 같은데요..!받아올 수 없는 값을 Playground에서 미리 알려줄 수 있는 방법이 없는지 궁금합니다.예를 들어, 아래와 같이 createProduct를 할 때, productTags나 productCategory는 name을 받으려 하면 에러가 발생하잖아요.근데, Playground의 DOCS에는 name 타입도 받아올 수 있는 것처럼 확인이 되는데, 오류를 받아보기 전에 해당 값은 받아올 수 없는 값이라는 걸 알려줄 수 있는 방법이 있을까요..?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
리트윗 라우터에서 설정한 리트윗 검사가 되지 않습니다!(중복 리트윗 체크 및 본인 게시글 리트윗이 막아지지 않는 문제)
안녕하세요! React로 NodeBird SNS 만들기섹션4 리트윗하기 강의 시청 중 발생한 에러에 대해 질문 드립니다!항상 강의 잘 보고 있습니다! 제로초님 감사합니다! [리트윗 라우터에서 설정한 리트윗 검사]1. 자기 게시글을 리트윗한 경우2. 자기 게시글을 리트윗한 다른 게시글을 다시 자기가 리트윗한 경우3. 이미 리트윗한 게시글을 또 리트윗 하는 경우(중복 리트윗) 여러 개의 게시글을 가져오는 라우터에서 리트윗한 게시글, 작성자, 이미지 모델을 넣었으며,게시글 Reducer에서 리트윗 실패 시 실패 확인을 'draft.retweetError = action.error;'로 확인하였습니다.게시글 Saga에서도 리트윗 요청 실패 시 실패 결과를 'error: err.response.data'로 설정했습니다.★ 프론트, 백엔드 쪽 터미널과 콘솔, 리덕스, 네티워크 쪽에 오류가 없음을 확인하였습니다.코드 오타가 원인이라고 파악해 제로초님의 깃허브와 제 코드를 비교하며 확인했으나원인을 찾을 수 없어 질문 올립니다. 아래는 가장 의심되는 코드 입니다.중요하지 않은 코드는 '. . .' 으로 생략하였습니다.front/pages/index.js // React 라이브러리 훅 불러오기 import React, { useEffect } from 'react'; . . . // 홈 컴포넌트(사용자 정의 태그) const Home = () => { const dispatch = useDispatch(); const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePosts, loadPostsLoading, retweetError } = useSelector((state) => state.post); . . . // 리트윗 실패 시 리트윗 에러 alert 창 띄우기 useEffect(() => { if (retweetError) { alert(retweetError); } }, [retweetError]);front/components/PostCard.js따로 게시글을 리트윗 실패(RETWEET_FAILURE) 액션을 디스패치 해야 하는지 의심이 들었습니다!// 게시글 카드 컴포넌트(사용자 정의 태그) const PostCard = ({ post }) => { . . . // 리트윗 버튼 콜백 함수 const onRetweet = useCallback(() => { // 로그인을 안했을 때 '로그인이 필요합니다.' alert 창 띄우기 if (!id) { return alert('로그인이 필요합니다.'); } /* 리트윗 요청 액션 객체 디스패치 */ return dispatch({ type: RETWEET_REQUEST, // 리트윗 요청 액션 data: post.id, // 게시글 아이디 }); }, [id]); . . . return ( <div style={{ marginBottom: '20px' }}> <Card /* ---------- 이미지 : 이미지는 1개 이상 ---------- */ cover={post.Images[0] && <PostImages images={post.Images} />} /* ---------- 액션 버튼 ---------- */ actions={[ /* ---------- 리트윗 버튼 ---------- */ <RetweetOutlined key="retweet" onClick={onRetweet} />, . . . ]} /* 카드 제목 */ title={post.RetweetId // 리트윗 게시글이면 게시글 사용자 닉네임님이 리트윗 하셨습니다. 제목 써주기 ? `${post.User.nickname}님이 리트윗 하셨습니다.` // 일반 게시글이면 제목 안 써주기 : null } . . . > {/* Card 닫기 */} {/* ---------- 리트윗 게시글 ---------- */} {post.RetweetId && post.Retweet ? ( <Card /* ---------- 이미지 : 이미지는 1개 이상 ---------- */ cover={post.Retweet.Images[0] && <PostImages images={post.Retweet.Images} />} > <Card.Meta // 메인 게시글 리트윗한 사용자 닉네임의 첫 번째 글자를 // 아바타 아이콘으로 표시 avatar={<Avatar>{post.Retweet.User.nickname[0]}</Avatar>} // 메인 게시글 리트윗한 게시글 작성자 이름 title={post.Retweet.User.nickname} // 메인 게시글 게시글 콘텐츠 description={<PostCardContent postData={post.Retweet.content} />} /> </Card> ) : ( /* ---------- (리트윗을 하지않은) 일반 게시글 ---------- */ <Card.Meta // 메인 게시글 사용자 닉네임의 첫 번째 글자를 아바타 아이콘으로 표시 avatar={<Avatar>{post.User.nickname[0]}</Avatar>} // 메인 게시글 작성자 이름 title={post.User.nickname} // 메인 게시글 콘텐츠 description={<PostCardContent postData={post.content} />} /> )} </Card> back/routes/post.js// 리트윗 라우터 router.post('/:postId/retweet', isLoggedIn, async (req, res, next) => { // POST /post/동적 히든/retweet try { /* 존재하지 않는 게시글이 있는지 검사하는 함수 */ const post = await Post.findOne({ where: { id: req.params.postId }, // 모델 가져오기 include: [{ model: Post, as: 'Retweet', // as: 'Retweet'으로 include를 해주면 post.retweet이 생긴다. }], }); /* ---------- 만약 존재하지 않는 게시글이 있다면 400번대 에러 출력 ---------- */ if (!post) { return res.status(403).send('존재하지 않는 게시글입니다.'); } /* 자기 게시글을 리트윗하기 or 자기 게시글을 리트윗한 다른 게시글을 다시 자기가 리트윗하기 막기 */ if (req.user.id === post.UserId || (post.Retweet && post.Retweet.UserId === req.user.id)) { return res.status(403).send('자신의 글은 리트윗할 수 없습니다.'); } // 리트윗할 Id : 리트윗한 게시글이면 리트윗 아이디 사용 or 아니면 게시글 아이디 사용 const retweetTargetId = post.RetweetId || post.id; /* 이미 리트윗한 게시글을 또 리트윗하는지 검사하는 함수(두 번 리트윗 막기) */ const exPost = await Post.findOne({ where: { UserId: req.user.id, RetweetId: retweetTargetId, }, }); /* ----- 만약 이미 리트윗한 게시글을 또 리트윗한다면 400번대 에러 출력 ----- */ if (exPost) { return res.status(403).send('이미 리트윗했습니다.'); } /* await : 실제로 데이터가 들어감, create : 테이블 안에 데이터를 넣음 */ const retweet = await Post.create({ UserId: req.user.id, RetweetId: retweetTargetId, content: 'retweet', // 게시글 모델에서 allowNull을 false로 설정했기 때문에 게시글 콘텐츠가 필수다. }); /* ---------- 내가 어떤 게시글을 리트윗했는지 찾는 함수 ---------- */ const retweetWithPrevPost = await Post.findOne({ where: { id: retweet.id }, // 모델 가져오기 include: [{ /* ---------- 리트윗한 게시글 ---------- */ model: Post, as: 'Retweet', // 리트윗한 게시글이 post.Retweet으로 담긴다. // 모델 가져오기 include: [{ /* ---------- 리트윗한 게시글의 작성자 ---------- */ model: User, attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기 }, { /* ---------- 리트윗한 게시글의 이미지 ---------- */ model: Image, }] }, { /* ---------- 게시글 작성자 ---------- */ model: User, attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기 }, { /* ---------- 게시글 좋아요 누른 사람들 ---------- */ model: User, as: 'Likers', attributes: ['id'], // id 데이터만 가져오기 }, { /* ---------- 게시글 이미지 ---------- */ model: Image, }, { /* ---------- 게시글 답글 ---------- */ model: Comment, // 모델 가져오기 include: [{ /* ---------- 게시글 답글의 작성자 ---------- */ model: User, attributes: ['id', 'nickname'], // id, nickname 데이터만 가져오기 }], }], }); /* 게시글 작성 성공 시 어떤 게시글을 리트윗 했는지에 대한 정보를 프론트로 돌려주기 */ res.status(201).json(retweetWithPrevPost); /* ---------- 에러 캐치 ---------- */ } catch (error) { console.error(error); next(error); } }); +++ 줄바꿈이 되지 않은 문제를 수정하였습니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
인피니트 스크롤링 적용시 LOAD_POST_REQUEST 두번 찍히는 문제
안녕하세요 선생님 상황)인피니트 스크롤링 적용시 LOAD_POST_REQUEST 두번 찍히는 상황인데 이거의 원인과 해결방법을 어떻게 찾을 수 있을까요? loadPostsLoading과 throttle을 적용했는데도 2번씩 실행되는 상황입니다.redux) 작성한 코드) 10,000 자이하만 적을 수 있어서 LOAD_POSTS_REQUEST 관련 코드만 올립니다..!!pages/index.jsimport React, { 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 {LOAD_POSTS_REQUEST} from '../reducers/post'; const Home = () => { const dispatch = useDispatch(); const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post); useEffect(() => { dispatch({ type: LOAD_POSTS_REQUEST, }); }, []); useEffect(() => { function onScroll() { if(window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight-300) { if(hasMorePosts && !loadPostsLoading) { dispatch({ type: LOAD_POSTS_REQUEST, }); } } } window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); }; }, [hasMorePosts, loadPostsLoading]); return ( <AppLayout> {me && <PostForm />} {mainPosts.map((post) => <PostCard key={post.id} post={post} />)} </AppLayout> ); }; export default Home;reducers/post.jsimport shortId from 'shortid'; import {produce} from 'immer'; import faker from 'faker'; export const initialState = { mainPosts:[], imagePaths: [], //게시물 저장 경로 hasMorePosts: true, loadPostsLoading: false, //게시글 로드 완료시 true loadPostsDone: false, loadPostsError: null, } 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: 'https://cdn.pixabay.com/photo/2017/07/25/01/22/cat-2536662_1280.jpg' //faker.image.imageUrl(640, 480, true), lorempixel.com 고장나서 임시로 }], 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'; 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){ 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; break; case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false; draft.loadPostsError = action.error; break; default: break; } }); export default reducer; sagas/post.jsimport { all, fork, takeLatest, put, delay, throttle } from 'redux-saga/effects'; import axios from 'axios'; import shortId from 'shortid'; import { LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE, generateDummyPost, } from '../reducers/post'; function loadPostsAPI(data){ return axios.get('/api/post', 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) { yield put({ type: LOAD_POSTS_FAILURE, data: err.response.data }); } } function* watchLoadPosts(){ yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts); } export default function* postSaga() { yield all([ fork(watchLoadPosts) ]); }사용중인 OS) macOS (Apple M1 Pro)
-
해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
하위 테이블이 되는 엔티티에서 외래키 컬럼을 별도로 작성하는 이유가 궁금합니다
강의에서 OneToMany, ManyToOne 관계인 엔티티들을 보면/entity/Users.ts// ... @Entity({ schema: 'sleact', name: 'users' }) export class Users { @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) id: number; // ... @OneToMany( () => WorkspaceMembers, (workspacemembers) => workspacemembers.User, ) WorkspaceMembers: WorkspaceMembers[]; // ... }/entity/WorkspaceMembers.ts@Entity('workspacemembers', { schema: 'sleact' }) export class WorkspaceMembers { // ... @Column('int', { primary: true, name: 'UserId' }) UserId: number; // ... @ManyToOne(() => Users, (users) => users.WorkspaceMembers, { onDelete: 'CASCADE', onUpdate: 'CASCADE', }) @JoinColumn([{ name: 'UserId', referencedColumnName: 'id' }]) User: Users; // ... }WorkspaceMembers 엔티티에서 UserId 컬럼을 별도로 작성하신 뒤@JoinColumn 데코레이터에서 Users 엔티티에서 가져온 'id'컬럼을 본 엔티티에서 'UserId' 컬럼으로 사용하겠다고 지정하셨는데요 이외에도 다른 일대다, 다대일 관계인 엔티티 중 다대일 파일들에 모두 '(상위테이블)Id' 이런 식으로 컬럼을 직접 설정하셨더라구요.이 이유가 무엇인지에 대해서 질문드리고 싶어서 글 작성합니다. 개인적으로 연습하면서 TypeORM 공식문서나 깃북을 확인했을때는 관계에 대해서는 지정을 하되,하위 테이블에선 별도로 외래키에 대한 컬럼까지는 작성하지 않았는데요.제가 작업중인 환경에서 테스트할 때도user.entity.ts// ... @Entity({ name: 'Users' }) export class Users { @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) id: number; // .. @OneToMany(() => Posts, (post: Posts) => post.user, { cascade: true, }) post: Posts[]; }posts.entity.ts// ... @Entity({ name: 'Posts' }) export class Posts { @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) id: number; // @Column({ type: 'int' }) // userId: number; // ... @ManyToOne(() => Users, (user: Users) => user.post, { onDelete: 'CASCADE', }) // 외래키 정보 // @JoinColumn([ // { // referencedColumnName: 'id', // 상대방 컬럼 // name: 'userId', // 여기서 쓸 컬럼 // }, // ]) user: Users[]; }이 상태에서 DB와 테이블을 생성했을때 외래키가 되는 'userId'가 생성되는 것 확인하였습니다.단 이 상태에서는 서비스 단에서 리포지터리로 데이터를 입력할 수가 없었습니다. 이 부분은 JoinColumn 데코레이터가 있어도 결과는 같았습니다. 혹시 자동적으로 만들어지는 외래키 컬럼에는 서비스에서 typeorm 사용해도직접적으로 데이터를 집어 넣지 못하기 때문에,외래키 컬럼을 직접 만들어주고 JoinColumn 데코레이터 사용해서 명시적으로 지정을 해줘야만 한다 라고 생각하면 될까요? 제가 제대로 이해한 것이 맞을까요?
-
해결됨한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지
State 문제
useState(0) 에서 결과값을 볼려고 했는데 0이 안떠서 값을 못보겠습니다. 코드에 문제가 있는걸까요?
-
해결됨React + GPT API로 AI회고록 서비스 개발 (원데이 클래스)
프롬프트 명령어 - 입력값과 동일한 언어로 받으려면 어떻게 작성할까요?
좋은 내용 감사합니다.프롬프트 내용중에 "Translate Into Korean~" 이라는 내용으로 답변을 한글로 받게 됩니다.혹시 [events] 밑에 오는 사용자 입력값과 동일한 언어로 결과를 받고 싶다면 어떻게 작성하면 될까요?강의 내용을 기준으로 다국어 서비스를 만들려고 하는데, 영어가 짧아서 질문 드려요