묻고 답해요
161만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결15일간의 빅데이터 파일럿 프로젝트
강의 수강 연장 부탁드립니다.
유사한 요청/답변글이 있어, 강의 수강 연장 부탁드립니다.프로젝트로 인해.. 강의 수강을 많이 못했습니다.저도 한달 연장해주신다면, 전체 수강완료하겠습니다. 꼭 완료하고 싶습니다!
-
미해결지금 당장 데브옵스 AWS
가비아 도메인 네임서버
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. 가비아 도메인 네임서버에서 AWS 네임서버 주소를 설정하라고 하셨는데, 그럼 가비아에서 도메인을 하나 사야하는 건가요?
-
미해결빅데이터분석기사 실기대비 (R 활용)
회귀모델구축 질문입니다.
mae나 rmse mape는 제출하는 마지막에서는 평가파일에 예측값이 없으니까 구할 수 없는 거죠?그래서 p3만 write해서 내면 되는 거죠?분류모델구축할때도 평가파일에 예측값이 없으니까 auc 못 구하고 p3[,2]이렇게 write 해서 제출하는것처럼이요~ 그런데 제출하는 게 아니라 그냥 rmse로 평가결과를 구하시오. 그러면 테스트 데이터로 rmse 결과 값을 구하면 되는 거죠?
-
미해결따라하며 배우는 리액트 A-Z[19버전 반영]
이미지 배너 만들기
이미지 배너 만들기 강의중에 배너 css 부분이 다운받은 자료에도 소개해주신 드라이브에 가서 다운받아도 그부분만 안보이는데 어디가서 볼수 있는 걸까요 ? 밑에 화면처럼 저부분만 짤려서 안보입니다
-
해결됨실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
orderstatus 문제로 질문 드립니다.
=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.제가 강의를 들으면서 그대로 따라한 프로젝트를 끝낸 뒤새로운 프로젝트를 만들어서 강의랑 같지만다대다관계도 바꾸고 조금씩 바꾸는 중입니다.그런데 마지막 주문조회 부분에 org.springframework.expression.spel.SpelEvaluationException: EL1005E: Type cannot be found 'jpabook.jpashop.domain.OrderStatus'라는 오류가 뜨는데 'jpabook.jpashop.domain.OrderStatus' 이건 전에 만들었던 프로젝트부분이고저는 jpabook이 아닌 japexample을 켜두고 있었습니다. db 주소도 새로 만들어주었는데 왜 이런지 모르겠습니다ㅠ.spring: datasource: url: jdbc:h2:tcp://localhost/~/test username: sa password: driver-class-name: org.h2.Driver thymeleaf: prefix: classpath:/templates/ suffix: .html jpa: hibernate: ddl-auto: none #spring.jpa.hibernate.ddl-auto: create 실행시점에서 테이블을 드랍하고 다시 실행 properties: hibernate: # show_sql: true format_sql: true logging.level: org.hibernate.SQL: debug # org.hibernate.type: trace
-
해결됨스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
hashmap 질문이요 !
java에서 hashmap 에서 key로 value를 찾을때 hashmap.get(key) 이렇게 찾잖아요 궁금한점은 java에서 value를 hashmap[key] 이렇게 찾을 수도 있나요? 빨간 밑줄은 타임리프에서 제공하는 특수한 문법인가요?
-
해결됨홍정모의 따라하며 배우는 C언어
11.4 문자열을 입력받는 다양한 방법들 15분 40초 Warning이 뜨는 이유?
warning C4047: '!=': 'int (__cdecl *)(void)'의 간접 참조 수준이 'int'과(와) 다릅니다.>> 위와 같은 경고가 뜨길래 원래의 코드에서 words[i] 배열의 원소를 (int)로 캐스팅하니까 경고가 뜨지 않더라구요. 혹시 이유를 알 수 있을까요? /* fgets() and fputs() */ char words[STRLEN] = ""; fgets(words, STRLEN, stdin); int i = 0; while ((int)words[i] != '\n' && (int)words[i] != '\0') { i++; } if ((int)words[i] == '\n') { fwords[i] = '\0'; } fputs(words, stdout); fputs("\nEND", stdout);
-
미해결[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문
필기 노트 깃헙에 올려도 될까요?
안녕하세요! 강의 잘 듣고 있습니다.다름이 아니라 필기 노트를 인프런 블로그에는 올려도 되는 것 같은데, 공부하면서 기록한 것들을 github이나 velog에 출처 표시하고 올려도 되는 지 궁금하여 질문드립니다.감사합니다.
-
미해결Do it! 자바 프로그래밍 입문 with 은종쌤
11분쯤 price, int price 질문
안녕하세요질문이 있어 문의 드립니다 11분쯤에 customerLee.calcPrice는 int price로 받았는데customerKim.calcPrice는 타입없이 price로 하셨는데 이유가 있을까요? 감사합니다.
-
미해결따라하며 배우는 리액트 테스트 [2023.11 업데이트]
컴포넌트 props내려줄때 코드는 어떻게 해야하나요
이런식으로 하위컴포넌트에 useState를 props로 다수 내려주고 있는경우에는 어떻게 해야할까요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 유튜브 사이트 만들기
typeError or 콘솔 500 뜨는분..
저도 잘 모르지만.. 몇시간을 찾고 구글링에.. 아무튼 그랬습니다.. 해결은 됐는데 코드를 보여드리자면routes/video.js (back단)router.post("/thumbnail", (req, res) => { let thumbsFilePath =""; let fileDuration =""; ffmpeg.ffprobe(req.body.filePath, function(err, metadata){ console.log(metadata); console.log(metadata.format.duration); fileDuration = metadata.format.duration; }) ffmpeg(req.body.filePath) .on('filenames', function (filenames) { console.log('Will generate ' + filenames.join(', ')) thumbsFilePath = "uploads/thumbnails/" + filenames[0]; }) .on('end', function () { console.log('Screenshots taken'); return res.json({ success: true, thumbsFilePath: thumbsFilePath, fileDuration: fileDuration}) }) .screenshots({ // Will take screens at 20%, 40%, 60% and 80% of the video count: 1, folder: 'uploads/thumbnails', size:'320x240', // %b input basename ( filename w/o extension ) filename:'thumbnail-%b.png' }); }); VideoUploadPage.js (front단)const onDrop = (files) => { let formData = new FormData(); const config = { header: { "content-type": "multipart/form-data" }, }; formData.append("file", files[0]); axios.post("/api/video/uploadfiles", formData, config).then((response) => { if (response.data.success) { console.log(response.data); let variable = { filePath: response.data.filePath, fileName: response.data.fileName } setFilePath(response.data.filePath) axios.post("/api/video/thumbnail", variable).then((response) => { if (response.data.success) { setDuration(response.data.fileDuration) setThumbnail(response.data.thumbsFilePath) } else { alert("썸네일 에러 발생"); } }); } else { alert("업로드 실패"); } }); };라고 됐습니다.. 혹시 필요하신분은 참조하시길 바라요~ 아 물론 윈도우라 ffmpeg 설치 해야합니다!
-
미해결파이썬/장고 웹서비스 개발 완벽 가이드 with 리액트
파이썬 가상환경 이 여러개가 뜹니다..
가상환경내에서 파이썬을 설치하였는데where python 하니 여러개가 뜨는데..제가 어떤부분을 잘못했을까요
-
미해결
비지니스 계정은 강의 결재할수없나요?
비지니스 계정은 강의 결재할수없나요?
-
미해결
게정을 인증하고 싶어요..
제가 계정을 인증할려고 해요.핸드폰이 없는 것은 아니에요.그런데 제가 핸드폰이 와이파이가 없어서(기능이) 카카오톡은 사용을 못해요.메세지로 인증할수 있다고 하는데, 메세지가 안오네요.
-
미해결Vue.js 완벽 가이드 - 실습과 리팩토링으로 배우는 실전 개념
import구문
import { fetchNewsList} from '../api/index.js'왜 import 구문에서 API 함수를 가져오는데 { } 객체처럼 가져오나요??
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
이미지 첨부하고 글 작성시 500에러나는 현상
에러메세지 /post/images/postSequelizeValidationError: notNull Violation: Image.content cannot be null at InstanceValidator._validate (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:50:13) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async InstanceValidator._validateAndRunHooks (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:60:7) at async InstanceValidator.validate (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:54:12) at async model.save (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\model.js:2368:7) at async Function.create (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\model.js:1344:12) at async Promise.all (index 0) at async C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\routes\post.js:65:24/post 에러메세지 (post.js를 고쳐봐도 해결이 되지 않습니다)(읽기 쉽게 주석을 지우고 올려서 post.js:65번 줄이 아니라 if(req.body.image)부분 봐주시면 될 것 같습니다.)const express = require("express"); // const multer = require("multer"); const path = require("path"); // const fs = require("fs"); const { Post, Image, Comment, User, Hashtag } = require("../models"); const { isLoggedIn } = require("./middlewares"); const router = express.Router(); try { fs.accessSync("uploads"); } catch (error) { console.log("uploads 폴더가 없으므로 생성합니다."); fs.mkdirSync("uploads"); } const upload = multer({ storage: multer.diskStorage({ destination(req, file, done) { done(null, "uploads"); }, filename(req, file, done) { const ext = path.extname(file.originalname); // 확장자 추출(.png) const basename = path.basename(file.originalname, ext); // 사용자 done(null, basename + "_" + new Date().getTime() + ext); // 사용자14512512.png }, }), limits: { fileSize: 20 * 1024 * 1024 }, // 20MB }); router.post("/", isLoggedIn, upload.none(), async (req, res, next) => { // POST / post try { const hashtags = req.body.content.match(/#[^\s#]+/g); const post = await Post.create({ content: req.body.content, UserId: req.user.id, }); if (hashtags) { const result = await Promise.all( hashtags.map((tag) => Hashtag.findOrCreate({ where: { name: tag.slice(1).toLowerCase() }, }) ) ); await post.addHashtags(result.map((v) => v[0])); } if (req.body.image) { if (Array.isArray(req.body.image)) { const images = await Promise.all(req.body.image.map((image) => Image.create({ src: image }))); console.log(images); await post.addImages(images); } else { const image = await Image.create({ src: req.body.image }); await post.addImages(image); } } const fullPost = await Post.findOne({ where: { id: post.id }, include: [ { model: Image, }, { model: Comment, include: [ { model: User, // attributes: ["id", "nickname"], }, ], }, { model: User, // 게시글 작성자 attributes: ["id", "nickname"], }, { model: User, // 좋아요 누른사람 // 위랑 구분하기 위해 as Likers가져옴 as: "Likers", attributes: ["id"], }, ], }); res.status(201).json(fullPost); } catch (error) { console.error(error); next(error); } }); router.post("/images", isLoggedIn, upload.array("image"), (req, res, next) => { // POST /post/images console.log(req.files); res.json(req.files.map((v) => v.filename)); }); router.post("/:postId/retweet", isLoggedIn, async (req, res, next) => { // POST /post/1/retweet try { const post = await Post.findOne({ where: { id: req.params.postId }, include: [ { model: Post, as: "Retweet", }, ], }); if (!post) { return res.status(403).send("존재하지 않는 게시글입니다."); } if (req.user.id === post.UserId || (post.Retweet && post.Retweet.UserId === req.user.id)) { return res.status(403).send("자신의 글은 리트윗할 수 없습니다."); } // 리트윗한 게시글인지 const retweetTargetId = post.RetweetId || post.id; const exPost = await Post.findOne({ where: { UserId: req.user.id, RetweetId: retweetTargetId, }, }); if (exPost) { return res.status(403).send("이미 리트윗한 게시글입니다."); } const retweet = await Post.create({ UserId: req.user.id, RetweetId: retweetTargetId, content: "retweet", }); const retweetWithPrevPost = await Post.findOne({ where: { id: retweet.id }, include: [ { model: Post, as: "Retweet", include: [ { model: User, attributes: ["id", "nickname"], }, { model: Image, }, ], }, { model: User, attributes: ["id", "nickname"], }, { model: Image, }, { model: Comment, include: [ { model: User, attributes: ["id", "nickname"], }, ], }, ], }); res.status(201).json(retweetWithPrevPost); } catch (error) { console.error(error); next(error); } }); router.post("/:postId/comment", isLoggedIn, async (req, res, next) => { // POST / post/comment try { const post = await Post.findOne({ where: { id: req.params.postId }, }); if (!post) { return res.status(403).send("존재하지 않는 게시글입니다."); } const comment = await Comment.create({ content: req.body.content, // :postId에 들어감 (문자열로) PostId: parseInt(req.params.postId, 10), UserId: req.user.id, }); const fullComment = await Comment.findOne({ where: { id: comment.id }, include: [ { model: User, attributes: ["id", "nickname"], }, ], }); res.status(201).json(fullComment); } catch (error) { console.error(error); next(error); } }); router.patch("/:postId/like", isLoggedIn, async (req, res, next) => { // PATCH /post/1/like try { const post = await Post.findOne({ where: { id: req.params.postId } }); if (!post) { return res.status(403).send("게시글이 존재하지 않습니다."); } await post.addLikers(req.user.id); res.json({ PostId: post.id, UserId: req.user.id }); } catch (error) { console.error(error); next(error); } }); router.delete("/:postId/like", isLoggedIn, async (req, res, next) => { // DELETE /post/1/like try { const post = await Post.findOne({ where: { id: req.params.postId } }); if (!post) { return res.status(403).send("게시글이 존재하지 않습니다."); } await post.removeLikers(req.user.id); res.json({ PostId: post.id, UserId: req.user.id }); } catch (error) { console.error(error); next(error); } }); router.delete("/:postId", isLoggedIn, async (req, res, next) => { // DELETE / post/10 try { await Post.destroy({ where: { id: req.params.postId, UserId: req.user.id }, }); res.status(200).json({ PostId: parseInt(req.params.postId, 10) }); } catch (error) { console.error(error); next(error); } }); module.exports = router;router/post.js 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 postRouter = require("./routes/post"); const postsRouter = require("./routes/posts"); const userRouter = require("./routes/user"); const db = require("./models"); const passportConfig = require("./passport"); dotenv.config(); const app = express(); db.sequelize .sync() .then(() => { console.log("db 연결 성공"); }) .catch(console.error); passportConfig(); app.use(morgan("dev")); // cors에러 해결 app.use( cors({ // https://localhost:3060에서 온 요청만 허용 origin: "http://localhost:3060", credentials: true, }) ); app.use("/", express.static(path.join(__dirname, "uploads"))); app.use(express.json()); 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.get("/", (req, res) => { res.send("Hello express"); }); app.use("/post", postRouter); app.use("/posts", postsRouter); app.use("/user", userRouter); app.listen(3065, () => { console.log("서버 실행중"); }); app.jsmodule.exports = (sequelize, DataTypes) => { const Post = sequelize.define( "Post", { content: { type: DataTypes.TEXT, allowNull: false, }, }, { charser: "utf8mb4", collate: "utf8mb4_general_ci", } ); Post.associate = (db) => { db.Post.belongsTo(db.User); db.Post.belongsToMany(db.Hashtag, { through: "PostHashtag" }); db.Post.hasMany(db.Comment); db.Post.hasMany(db.Image); db.Post.belongsToMany(db.User, { through: "Like", as: "Likers" }); db.Post.belongsTo(db.Post, { as: "Retweet" }); }; return Post; };models/post.js ValidationError [SequelizeValidationError]: notNull Violation: Image.content cannot be null at InstanceValidator._validate (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:50:13) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async InstanceValidator._validateAndRunHooks (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:60:7) at async InstanceValidator.validate (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:54:12) at async model.save (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\model.js:2368:7) at async Function.create (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\model.js:1344:12) at async C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\routes\post.js:70:23 { errors: [ ValidationErrorItem { message: 'Image.content cannot be null', type: 'notNull Violation', path: 'content', value: null, origin: 'CORE', instance: [Image], validatorKey: 'is_null', validatorName: null, validatorArgs: [] } ] } SequelizeValidationError: notNull Violation: Image.content cannot be null at InstanceValidator._validate (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:50:13) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async InstanceValidator._validateAndRunHooks (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:60:7) at async InstanceValidator.validate (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\instance-validator.js:54:12) at async model.save (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\model.js:2368:7) at async Function.create (C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\node_modules\sequelize\lib\model.js:1344:12) at async C:\Users\TaeIl\Desktop\frontStudy\React-Nodebird\backend\routes\post.js:70:23 POST /post 500 26.161 ms - 1182터미널 에러 import produce from 'immer'; export type mainPost = { mainPosts: any, imagePaths: object[], likePostLoading: boolean, likePostDone: boolean, likePostError: boolean, unlikePostLoading: boolean, unlikePostDone: boolean, unlikePostError: boolean, addPostLoading: boolean, addPostDone: boolean, addPostError: boolean, addCommentLoading: boolean, addCommentDone: boolean, addCommentError: boolean, hasMorePosts: boolean, loadPostsLoading: boolean, loadPostsDone: boolean, loadPostsError: boolean, removePostLoading: boolean, removePostDone: boolean, removePostError: boolean, uploadImagesLoading: boolean, uploadImagesDone: boolean, uploadImagesError: boolean, retweetLoading: boolean, retweetDone: boolean, retweetError: boolean, } export const initialState: mainPost = { mainPosts: [], imagePaths: [], hasMorePosts: true, // infinite scroll likePostLoading: false, likePostDone: false, likePostError: null, unlikePostLoading: false, unlikePostDone: false, unlikePostError: null, loadPostsLoading: false, loadPostsDone: false, loadPostsError: null, addPostLoading: false, addPostDone: false, addPostError: null, removePostLoading: false, removePostDone: false, removePostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, uploadImagesLoading: false, uploadImagesDone: false, uploadImagesError: null, retweetLoading: false, retweetDone: false, retweetError: null, } export const UPLOAD_IMAGES_REQUEST = 'UPLOAD_IMAGES_REQUEST' as const; export const UPLOAD_IMAGES_SUCCESS = 'UPLOAD_IMAGES_SUCCESS' as const; export const UPLOAD_IMAGES_FAILURE = 'UPLOAD_IMAGES_FAILURE' as const; export const LIKE_POST_REQUEST = 'LIKE_POST_REQUEST' as const; export const LIKE_POST_SUCCESS = 'LIKE_POST_SUCCESS' as const; export const LIKE_POST_FAILURE = 'LIKE_POST_FAILURE' as const; export const UNLIKE_POST_REQUEST = 'UNLIKE_POST_REQUEST' as const; export const UNLIKE_POST_SUCCESS = 'UNLIKE_POST_SUCCESS' as const; export const UNLIKE_POST_FAILURE = 'UNLIKE_POST_FAILURE' as const; export const LOAD_POSTS_REQUEST = 'LOAD_POSTS_REQUEST' as const; export const LOAD_POSTS_SUCCESS = 'LOAD_POSTS_SUCCESS' as const; export const LOAD_POSTS_FAILURE = 'LOAD_POSTS_FAILURE' as const; export const ADD_POST_REQUEST = 'ADD_POST_REQUEST' as const; export const ADD_POST_SUCCESS = 'ADD_POST_SUCCESS' as const; export const ADD_POST_FAILURE = 'ADD_POST_FAILURE' as const; export const REMOVE_POST_REQUEST = 'REMOVE_POST_REQUEST' as const; export const REMOVE_POST_SUCCESS = 'REMOVE_POST_SUCCESS' as const; export const REMOVE_POST_FAILURE = 'REMOVE_POST_FAILURE' as const; export const ADD_COMMENT_REQUEST = 'ADD_COMMENT_REQUEST' as const; export const ADD_COMMENT_SUCCESS = 'ADD_COMMENT_SUCCESS' as const; export const ADD_COMMENT_FAILURE = 'ADD_COMMENT_FAILURE' as const; export const RETWEET_REQUEST = 'RETWEET_REQUEST' as const; export const RETWEET_SUCCESS = 'RETWEET_SUCCESS' as const; export const RETWEET_FAILURE = 'RETWEET_FAILURE' as const; export const REMOVE_IMAGE = 'REMOVE_IMAGE' as const; export const addPost = (data) => ({ type: ADD_POST_REQUEST, data, }) export const addComment = (data) => ({ type: ADD_COMMENT_REQUEST, data, }) const reducer = (state: mainPost = initialState, action: any) => { return produce(state, (draft) => { switch (action.type) { case RETWEET_REQUEST: draft.retweetLoading = true; draft.retweetDone = false; draft.retweetError = null; break; case RETWEET_SUCCESS: draft.retweetLoading = false; draft.retweetDone = true; break; case RETWEET_FAILURE: draft.retweetLoading = false; draft.retweetError = action.error; break; default: break; // 서버에서 이미지를 지우고 싶으면 비동기로 만들어줘야 한다. // 서버에서 이미지를 안지우는 이유는 머신러닝 등을 위해 데이터 수집을 할 수도 있어서 case REMOVE_IMAGE: draft.imagePaths = draft.imagePaths.filter((v, i) => i !== action.data) break; case UPLOAD_IMAGES_REQUEST: draft.uploadImagesLoading = true; draft.uploadImagesDone = false; draft.uploadImagesError = null; break; case UPLOAD_IMAGES_SUCCESS: { // 데이터들 여기 저장 draft.imagePaths = action.data; draft.uploadImagesLoading = false; draft.uploadImagesDone = true; break; } case UPLOAD_IMAGES_FAILURE: draft.uploadImagesLoading = false; draft.uploadImagesError = action.error; break; case LIKE_POST_REQUEST: draft.likePostLoading = true; draft.likePostDone = false; draft.likePostError = null; break; case LIKE_POST_SUCCESS: { const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Likers.push({ id: action.data.UserId }); draft.likePostLoading = false; draft.likePostDone = true; break; } case LIKE_POST_FAILURE: draft.likePostLoading = false; draft.likePostError = action.error; break; case UNLIKE_POST_REQUEST: draft.unlikePostLoading = true; draft.unlikePostDone = false; draft.unlikePostError = null; break; case UNLIKE_POST_SUCCESS: { const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Likers = post.Likers.filter((v) => v.id !== action.data.UserId); draft.unlikePostLoading = false; draft.unlikePostDone = true; break; } case UNLIKE_POST_FAILURE: draft.unlikePostLoading = false; draft.unlikePostError = action.error; break; 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 = draft.mainPosts.concat(action.data); draft.hasMorePosts = action.data.length === 10; break; case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false; draft.loadPostsError = action.error; break; case ADD_POST_REQUEST: draft.addPostLoading = true; draft.addPostDone = false; draft.addPostError = null; break; case ADD_POST_SUCCESS: draft.addPostLoading = false; draft.addPostDone = true; draft.mainPosts.unshift(action.data); draft.imagePaths = []; break; case ADD_POST_FAILURE: draft.addPostLoading = false; draft.addPostError = action.error; break; case REMOVE_POST_REQUEST: draft.removePostLoading = true; draft.removePostDone = false; draft.removePostError = null; break; case REMOVE_POST_SUCCESS: draft.removePostLoading = false; draft.removePostDone = true; draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data.PostId); break; case REMOVE_POST_FAILURE: draft.removePostLoading = false; draft.removePostError = action.error; break; case ADD_COMMENT_REQUEST: draft.addCommentLoading = true; draft.addCommentDone = false; draft.addCommentError = null; break; case ADD_COMMENT_SUCCESS: { const post = draft.mainPosts.find((v) => v.id === action.data.PostId); post.Comments.unshift(action.data); draft.addCommentLoading = false; draft.addCommentDone = true; break; } case ADD_COMMENT_FAILURE: draft.addCommentLoading = false; draft.addCommentError = action.error; break; } }); } export default reducer;reducer/postimport { all, fork, delay, put, takeEvery, takeLatest, throttle, call } from 'redux-saga/effects'; import shortId from 'shortid'; import axios from 'axios'; import { ADD_COMMENT_FAILURE, ADD_COMMENT_REQUEST, ADD_COMMENT_SUCCESS, ADD_POST_FAILURE, ADD_POST_REQUEST, ADD_POST_SUCCESS, LIKE_POST_FAILURE, LIKE_POST_REQUEST, LIKE_POST_SUCCESS, LOAD_POSTS_FAILURE, LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, REMOVE_POST_FAILURE, REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS, RETWEET_FAILURE, RETWEET_REQUEST, RETWEET_SUCCESS, UNLIKE_POST_FAILURE, UNLIKE_POST_REQUEST, UNLIKE_POST_SUCCESS, UPLOAD_IMAGES_FAILURE, UPLOAD_IMAGES_REQUEST, UPLOAD_IMAGES_SUCCESS, } from '../reducers/post'; import { ADD_POST_TO_ME, REMOVE_POST_OF_ME } from '../reducers/user'; function retweetAPI(data) { return axios.post(`/post/${data}/retweet`); } function* retweet(action) { try { const result = yield call(retweetAPI, action.data); yield put({ type: RETWEET_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: RETWEET_FAILURE, error: err.response.data, }); } } function uploadImagesAPI(data) { return axios.post('/post/images', data); } function* uploadImages(action) { try { const result = yield call(uploadImagesAPI, action.data); yield put({ type: UPLOAD_IMAGES_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: UPLOAD_IMAGES_FAILURE, error: err.response.data, }); } } function likePostAPI(data) { return axios.patch(`/post/${data}/like`); } function* likePost(action) { try { const result = yield call(likePostAPI, action.data); yield put({ type: LIKE_POST_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: LIKE_POST_FAILURE, error: err.response.data, }); } } function unlikePostAPI(data) { return axios.delete(`/post/${data}/like`); } function* unlikePost(action) { try { const result = yield call(unlikePostAPI, action.data); yield put({ type: UNLIKE_POST_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: UNLIKE_POST_FAILURE, error: err.response.data, }); } } function loadPostsAPI(lastId) { return axios.get(`/posts?lastId=${lastId || 0}`) } function* loadPosts(action) { try { const result = yield call(loadPostsAPI, action.lastId) yield put({ type: LOAD_POSTS_SUCCESS, data: result.data, }); } catch (err) { console.error(err); yield put({ type: LOAD_POSTS_FAILURE, error: err.response.data, }) } } function addPostAPI(data) { return axios.post('/post', data) } function* addPost(action) { try { const result = yield call(addPostAPI, action.data) yield put({ type: ADD_POST_SUCCESS, data: result.data, }); yield put({ type: ADD_POST_TO_ME, data: result.data.id, }) } catch (err) { console.error(err); yield put({ type: ADD_POST_FAILURE, error: err.response.data, }) } } function removePostAPI(data) { return axios.delete(`/post/${data}`); } function* removePost(action) { try { const result = yield call(removePostAPI, action.data) yield put({ type: REMOVE_POST_SUCCESS, data: result.data, }); yield put({ type: REMOVE_POST_OF_ME, data: action.data, }) } catch (err) { console.error(err); yield put({ type: REMOVE_POST_FAILURE, error: err.response.data, }) } } function addCommentAPI(data) { return axios.post(`/post/${data.postId}/comment`, data) // POST /post/1/comment } function* addComment(action) { try { const result = yield call(addCommentAPI, action.data) yield put({ type: ADD_COMMENT_SUCCESS, data: result.data }) } catch (err) { console.error(err); yield put({ type: ADD_COMMENT_FAILURE, error: err.response.data, }) } } function* watchRetweet() { yield takeLatest(RETWEET_REQUEST, retweet); } function* watchUploadImages() { yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages); } function* watchLikePost() { yield takeLatest(LIKE_POST_REQUEST, likePost); } function* watchUnlikePost() { yield takeLatest(UNLIKE_POST_REQUEST, unlikePost); } function* watchLoadPosts() { yield throttle(2000, LOAD_POSTS_REQUEST, loadPosts); } function* watchAddPost() { yield takeLatest(ADD_POST_REQUEST, addPost); } function* watchRemovePost() { yield takeLatest(REMOVE_POST_REQUEST, removePost); } function* watchAddComment() { yield takeLatest(ADD_COMMENT_REQUEST, addComment); } export default function* postSaga() { yield all([ fork(watchRetweet), fork(watchUploadImages), fork(watchLikePost), fork(watchUnlikePost), fork(watchAddPost), fork(watchLoadPosts), fork(watchRemovePost), fork(watchAddComment), ]) } sagas/postimport { EllipsisOutlined, HeartOutlined, HeartTwoTone, MessageOutlined, RetweetOutlined } from '@ant-design/icons'; import { Avatar, Button, Card, List, Popover, Comment } from 'antd'; import { useDispatch, useSelector } from 'react-redux'; import PostImages from './PostImages'; import PropTypes from 'prop-types'; import { useCallback, useState } from 'react'; import CommentForm from './CommentForm'; import PostCardContent from './PostCardContent'; import { LIKE_POST_REQUEST, REMOVE_POST_REQUEST, UNLIKE_POST_REQUEST, RETWEET_REQUEST } from '../reducers/post'; import FollowButton from './FollowButton'; const PostCard = ({ post }) => { const dispatch = useDispatch(); const { removePostLoading } = useSelector((state: any) => state.post); const [commentFormOpened, setCommentFormOpened] = useState<boolean>(false); const id = useSelector((state: any) => state.user.me?.id) const onLike = useCallback(() => { if (!id) { return alert('로그인이 필요합니다.') } return dispatch({ type: LIKE_POST_REQUEST, data: post.id, }) }, [id]) const onUnLike = useCallback(() => { if (!id) { return alert('로그인이 필요합니다.') } return dispatch({ type: UNLIKE_POST_REQUEST, data: post.id, }) }, [id]) const onToggleComment = useCallback(() => { if (!id) { return alert('로그인이 필요합니다.') } setCommentFormOpened((prev) => !prev) }, [id]) const onRemovePost = useCallback(() => { if (!id) { return alert('로그인이 필요합니다.') } return dispatch({ type: REMOVE_POST_REQUEST, data: post.id, }) }, [id]) const onRetweet = useCallback(() => { if (!id) { return alert('로그인이 필요합니다.') } return dispatch({ type: RETWEET_REQUEST, data: post.id, }) }, [id]) const liked = post.Likers.find((v) => v.id === id); return ( <div style={{ marginBottom: 20 }}> <Card cover={post.Images[0] && <PostImages images={post.Images} />} actions={[ <RetweetOutlined key="retweet" onClick={onRetweet} />, liked ? <HeartTwoTone twoToneColor="#eb2f96" key="heart" onClick={onUnLike} /> : <HeartOutlined key="heart" onClick={onLike} />, <MessageOutlined key="message" onClick={onToggleComment} />, <Popover key="ellipsis" content={( <Button.Group> {id && post.UserId === id ? ( <> <Button>수정</Button> <Button type="danger" loading={removePostLoading} onClick={onRemovePost}>삭제</Button> </> ) : <Button>신고</Button>} </Button.Group> )} > <EllipsisOutlined /> </Popover>, ]} // 누가 리트윗 했는지 title={post.RetweetId ? `${post.User.nickname}님이 리트윗하셨습니다.` : null} extra={id && <FollowButton post={post} />} > {post.RetweetId && post.Retweet ? (<Card 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> {commentFormOpened && ( <div> <List header={`${post.Comments.length}개의 댓글`} itemLayout="horizontal" dataSource={post.Comments} renderItem={(item: any) => ( <li> <Comment author={item.User.nickname} avatar={<Avatar>{item.User.nickname[0]}</Avatar>} content={item.content} /> </li> )} /> </div> )} </div > ) } PostCard.propTypes = { post: PropTypes.shape({ id: PropTypes.number, User: PropTypes.object, UserId: PropTypes.number, content: PropTypes.string, createdAt: PropTypes.string, Comments: PropTypes.arrayOf(PropTypes.any), Images: PropTypes.arrayOf(PropTypes.any), Likers: PropTypes.arrayOf(PropTypes.object), RetweetId: PropTypes.number, Retweet: PropTypes.objectOf(PropTypes.any), }).isRequired, }; export default PostCard;PostCard import { Form, Input, Button } from 'antd'; import { useCallback, useRef, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import useInput from '../hooks/useInput'; import { ADD_POST_REQUEST, REMOVE_IMAGE, UPLOAD_IMAGES_REQUEST } from '../reducers/post'; const PostForm = () => { const { imagePaths, addPostDone } = useSelector((state: any) => state.post); const dispatch = useDispatch() const imageInput = useRef<any>(); const [text, onChangeText, setText] = useInput('') useEffect(() => { if (addPostDone) { setText('') } }, [addPostDone]) const onSubmit = useCallback(() => { if (!text || !text.trim()) { return alert('게시글을 작성하세요.'); } const formData = new FormData(); imagePaths.forEach((p: string) => { formData.append('image', p); }) formData.append('content', text); return dispatch({ type: ADD_POST_REQUEST, data: formData, }); }, [text, imagePaths]) const onClickImageUpload = useCallback(() => { imageInput.current.click(); }, [imageInput.current]) const onChangeImages = useCallback((e) => { console.log('images', e.target.files); const imageFormData = new FormData(); [].forEach.call(e.target.files, (f) => { imageFormData.append('image', f); }); dispatch({ type: UPLOAD_IMAGES_REQUEST, data: imageFormData, }) }, []) const onRemoveImage = useCallback((index: number) => () => { dispatch({ type: REMOVE_IMAGE, data: index, }) }, []) return ( <Form style={{ margin: '10px 0 20px' }} encType='multipart/form-data' onFinish={onSubmit}> <Input.TextArea value={text} onChange={onChangeText} maxLength={140} placeholder="어떤 신기한 일이 있었나요?" /> <div> <input type="file" name="image" multiple hidden ref={imageInput} onChange={onChangeImages} /> <Button onClick={onClickImageUpload}>이미지 업로드</Button> <Button type="primary" style={{ float: 'right' }} htmlType="submit">짹짹</Button> </div> <div> {imagePaths.map((v: string, i: number) => ( <div key={v} style={{ display: 'inline-block' }}> <img src={`http://localhost:3065/${v}`} style={{ width: '200px' }} alt={v} /> <div> <Button onClick={onRemoveImage(i)}>제거</Button> </div> </div> ))} </div> </Form> ) } export default PostForm;PostForm POST http://localhost:3065/post 500 (Internal Server Error) dispatchXhrRequest @ xhr.js:247 xhr @ xhr.js:49 dispatchRequest @ dispatchRequest.js:51 request @ Axios.js:142 httpMethod @ Axios.js:181 wrap @ bind.js:5 addPostAPI @ post.tsx:127 runCallEffect @ redux-saga-core.esm.js:524 runEffect @ redux-saga-core.esm.js:1204 digestEffect @ redux-saga-core.esm.js:1271 next @ redux-saga-core.esm.js:1161 proc @ redux-saga-core.esm.js:1108 (익명) @ redux-saga-core.esm.js:585 immediately @ redux-saga-core.esm.js:56 runForkEffect @ redux-saga-core.esm.js:584 runEffect @ redux-saga-core.esm.js:1204 digestEffect @ redux-saga-core.esm.js:1271 next @ redux-saga-core.esm.js:1161 currCb @ redux-saga-core.esm.js:1251 takeCb @ redux-saga-core.esm.js:503 put @ redux-saga-core.esm.js:339 (익명) @ redux-saga-core.esm.js:376 exec @ redux-saga-core.esm.js:31 flush @ redux-saga-core.esm.js:87 asap @ redux-saga-core.esm.js:46 chan.put @ redux-saga-core.esm.js:375 (익명) @ redux-saga-core.esm.js:1412 dispatch @ VM285:3665 (익명) @ PostForm.tsx:33 onFinish @ Form.js:68 (익명) @ useForm.js:761 Promise.then(비동기) FormStore.submit @ useForm.js:757 onSubmit @ Form.js:119 callCallback @ react-dom.development.js:188 invokeGuardedCallbackDev @ react-dom.development.js:237 invokeGuardedCallback @ react-dom.development.js:292 invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js:306 executeDispatch @ react-dom.development.js:389 executeDispatchesInOrder @ react-dom.development.js:414 executeDispatchesAndRelease @ react-dom.development.js:3278 executeDispatchesAndReleaseTopLevel @ react-dom.development.js:3287 forEachAccumulated @ react-dom.development.js:3259 runEventsInBatch @ react-dom.development.js:3304 runExtractedPluginEventsInBatch @ react-dom.development.js:3514 handleTopLevel @ react-dom.development.js:3558 batchedEventUpdates$1 @ react-dom.development.js:21871 batchedEventUpdates @ react-dom.development.js:795 dispatchEventForLegacyPluginEventSystem @ react-dom.development.js:3568 attemptToDispatchEvent @ react-dom.development.js:4267 dispatchEvent @ react-dom.development.js:4189 unstable_runWithPriority @ scheduler.development.js:653 runWithPriority$1 @ react-dom.development.js:11039 discreteUpdates$1 @ react-dom.development.js:21887 discreteUpdates @ react-dom.development.js:806 dispatchDiscreteEvent @ react-dom.development.js:4168 500 에러메세지 연거Image.content cannot be null이라는게 왜 나오는지 모르겠습니다..req.body.image에 도달하기 이전 어딘가에서 문제가 터지는거 같기는 한데 콘솔도 찍고 확인해봐도 어디 어디서 꼬인건지 못찾겠습니다..
-
미해결
Pdf 파일로도 받아볼수있나요?
전자책 기능이 불편해서요. 오늘 결제 하였습니다
-
미해결스프링 DB 2편 - 데이터 접근 활용 기술
jar 실행시 에러가 나서 질문 드립니다.
MyBatis 버전을 빌드하고java -jar Item.jar 로 실행 하면 에러가 나는데 혹시 해결 방안이 있을까요?IDE에선 잘돌아 갑니다.구글링을 해봐도 딱히 안보여 질문 남깁니다.
-
미해결냉동코더의 알기 쉬운 Modern Android Development 입문
Room과 Flow의 관계
책을 즐겨찾기를 추가하고 즐겨찾기 탭으로 갔을때 즐겨찾기 했던 책이 자동으로 Recyclerview에 추가되어있는 모습을 볼 수 있는데 이는 Flow, 즉 데이터 스트림을 사용했기 때문인거죠?? 즉 FavoriteFragment의 onViewCreated에 있는 함수가 collect 트리거를 작동시키면 자동으로 Room에 추가된 데이터가 RecyclerView에 추가되는 형식이라고 이해했습니다.근데 앱을 조금 변경하여 특정버튼을 눌러야 DB에서 데이터를 가져오는 로직으로 (즉 버튼을 눌러야 ViewModel의 getFavoriteBook을 호출하는 형식) 변경하였는데 버튼을 누르지 않아도 알아서 ViewModel에서 즐겨찾기 데이터가 갱신이 되더라고요...ㅇㅅㅇ 분명 collect 트리거를 작동시키지 않았는데요
-
미해결CSS에 날개를 달아주는 Sass (SCSS)
명령 프롬포트
여기서 도저히 다음으로 넘어가지질 않는데요 ㅠㅠ 저도 쌤이랑 동일하게 바탕화면에 sasstest 폴더를 만들었는데 바탕화면으로 나가지질 않습니다...