묻고 답해요
164만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결개발하는 정대리 스위프트 기초 문법
정대리님! 혹시 블로그에 정리한 내용 올려도 될까요?
정대리님 안녕하세요! iOS 개발자 준비중에 있는데 혹시 강의내용을 정리하여서 정대리님 출처로 달고 블로그에 올려도 될까요?
-
미해결[Pytorch] 파이토치를 활용한 딥러닝 모델 구축
linspace(100, 200, 10)
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.linspace(100, 200, 10) 의 경우 구간은 9개이고 포인트가 10개 인것 같습니다.
-
미해결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에선 잘돌아 갑니다.구글링을 해봐도 딱히 안보여 질문 남깁니다.