묻고 답해요
131만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
서버 실패시, 패스포트 로그인 실패시 통합테스트 방법
exports.login = (req, res, next) => { passport.authenticate('local', (authError, user, info) => { // 1. 서버 실패 부분 if (authError) { console.error(authError); return next(authError); } if (!user) { return res.redirect(`/?error=${info.message}`); } return req.login(user, (loginError) => { //2. 패스포트 로그인 실패 if (loginError) { console.error(loginError); return next(loginError); } return res.redirect('/'); }); })(req, res, next); }; --------------------- 1번과 2번은 통합테스트 코드로 어떻게 테스트 코드를 작성해야 하는지 여쭤봐도 될까요? 1번은 try - catch 로 어떻게 해보려해도 생각이 안나용 2번은 아애 생각이 안납니다 ㅜㅜ controllers를 자체를 단위테스트를 하는 것이 아니라 분기마다 test를 작성하라고 하신 말씀에 이 부분은 통합테스트로 코드를 짤 수 있나 의문이 들어서요! 감사합니다
-
미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
유저 컨트롤러 코드 질문
깃허브 코드를 보면 @ApiOperation({ summary: '회원가입' }) @UseGuards(NotLoggedInGuard) @Post() async join(@Body() data: JoinRequestDto) { const user = this.usersService.findByEmail(data.email); if (!user) { throw new NotFoundException(); } const result = await this.usersService.join( data.email, data.nickname, data.password, ); if (result) { return 'ok'; } else { throw new ForbiddenException(); } } 이런데 여기서 user가 없는데 왜 NotFoundException()을 날리는 건가요? user가 없으면 그대로 join하여 사용자 등록을 해야 하는 것이 아닌가요?
-
해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
package.json / "scripts" / schema:drop, schma:sync에 `-d ./dataSource.ts` 옵션을 넣지 않으신 이유가 있으신가요?
테이블 삭제도 시도해보고 싶어서 터미널에$ npm run schema:drop 을 입력하였더니 아래와 같은 내용이 나왔습니다.Drops all tables in the database on your default dataSource. To drop table of a concrete connection's database use -c option. 옵션: -h, --help 도움말 표시 [불리언] -d, --dataSource Path to the file where your DataSource instance is defined. [필수] -v, --version 버전 표시 [불리언] 필수 인수가 주어지지 않았습니다: dataSource dataSource가 주어지지 않았다고 해서 살펴보다package.json/scripts/schema에 다른 명령어들과는 다르게 경로가 지정되어 있지 않는것 같아아래와 같이 수정했고, { "scripts": { "seed": "ts-node ./node_modules/typeorm-extension/bin/cli.cjs seed:run -d ./dataSource.ts", "schema:drop": "ts-node ./node_modules/typeorm/cli.js schema:drop -d ./dataSource.ts", "schema:sync": "ts-node ./node_modules/typeorm/cli.js schema:sync", } } 테이블 삭제에 성공했습니다.$ npm run schema:drop > a-nest@0.0.1 schema:drop > ts-node ./node_modules/typeorm/cli.js schema:drop -d ./dataSource.ts query: SELECT VERSION() AS `version` query: SELECT * FROM `INFORMATION_SCHEMA`.`SCHEMATA` WHERE `SCHEMA_NAME` = '00_nestjs_typeorm' query: START TRANSACTION query: SELECT concat('DROP VIEW IF EXISTS `', table_schema, '`.`', table_name, '`') AS `query` FROM `INFORMATION_SCHEMA`.`VIEWS` WHERE `TABLE_SCHEMA` = '00_nestjs_typeorm' query: SELECT concat('DROP TABLE IF EXISTS `', table_schema, '`.`', table_name, '`') AS `query` FROM ` `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = '00_nestjs_typeorm' INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = '00_nestjs_typeorm' query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`channelchats` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`channelmembers` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`channels` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`dms` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`mentions` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`migrations` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`users` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`workspacemembers` query: DROP TABLE IF EXISTS `00_nestjs_typeorm`.`workspaces` query: SET FOREIGN_KEY_CHECKS = 1; query: COMMIT Database schema has been successfully dropped. 혹시 schema:drop, schema:sync 명령어에 경로를 지정하지 않으신 이유가 있으신지,경로를 빼놓은 상태로 유지하다가, 정말 필요할 때에만 경로 지정하고 명령어를 사용하는게 좋은지가 궁금합니다. 실수로 입력해서 데이터 손실이 발생하는 것을 막기 위해서일까요?
-
미해결[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
7.5 시퀄라이즈 사용하기 에러
7.5 시퀄라이즈 사용하기 강좌 부분입니다.피피티와 동일하게 작성했는데 sync에러가 자꾸 뜹니다. 어떻게 고쳐야할지 모르겠습니다. app.jsconst express = require('express'); const path = require('path'); const morgan = require('morgan'); const nunjucks = require('nunjucks'); const { sequelize } = require('./models'); const app = express(); app.set('port', process.env.PORT || 3001); app.set('view engine', 'html'); nunjucks.configure('views', { express: app, watch: true, }); sequelize.sync({ force: false }) .then(() => { console.log('데이터베이스 연결 성공'); }) .catch((err) => { console.error(err); }); app.use(morgan('dev')); app.use(express.static(path.join(__dirname, 'public'))); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use((req, res, next) => { const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`); error.status = 404; next(error); }); app.use((err, req, res, next) => { res.locals.message = err.message; res.locals.error = process.env.NODE_ENV !== 'production' ? err : {}; res.status(err.status || 500); res.render('error'); }); app.listen(app.get('port'), () => { console.log(app.get('port'), '번 포트에서 대기 중'); }); index.jsconst Sequelize = require('sequelize'); const env = process.env.NODE_ENV || 'development'; const config = require('../config/config')[env]; const db = {}; const sequelize = new Sequelize(config.database, config.username, config.password, config); db.seqelize = sequelize; module.exports = db;error ->[nodemon] restarting due to changes...[nodemon] starting node app.jsC:\Users\jyoun\udr_node\lecture\app.js:15sequelize.sync({ force: false }) ^TypeError: Cannot read properties of undefined (reading 'sync') at Object.<anonymous> (C:\Users\jyoun\udr_node\lecture\app.js:15:11) at Module._compile (node:internal/modules/cjs/loader:1241:14) at Module._extensions..js (node:internal/modules/cjs/loader:1295:10) at Module.load (node:internal/modules/cjs/loader:1091:32) at Module._load (node:internal/modules/cjs/loader:938:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:83:12) at node:internal/main/run_main_module:23:47Node.js v20.7.0[nodemon] app crashed - waiting for file changes before starting... [제로초 강좌 질문 필독 사항입니다]질문에는 여러분에게 도움이 되는 질문과 도움이 되지 않는 질문이 있습니다.도움이 되는 질문을 하는 방법을 알려드립니다.https://www.youtube.com/watch?v=PUKOWrOuC0c0. 숫자 0부터 시작한 이유는 1보다 더 중요한 것이기 때문입니다. 에러가 났을 때 해결을 하는 게 중요한 게 아닙니다. 왜 여러분은 해결을 못 하고 저는 해결을 하는지, 어디서 힌트를 얻은 것이고 어떻게 해결한 건지 그걸 알아가셔야 합니다. 그렇지 못한 질문은 무의미한 질문입니다.1. 에러 메시지를 올리기 전에 반드시 스스로 번역을 해야 합니다. 번역기 요즘 잘 되어 있습니다. 에러 메시지가 에러 해결 단서의 90%를 차지합니다. 한글로 번역만 해도 대부분 풀립니다. 그냥 에러메시지를 올리고(심지어 안 올리는 분도 있습니다. 저는 독심술사가 아닙니다) 해결해달라고 하시면 아무런 도움이 안 됩니다.2. 에러 메시지를 잘라서 올리지 않아야 합니다. 입문자일수록 에러메시지에서 어떤 부분이 가장 중요한 부분인지 모르실 겁니다. 그러니 통째로 올리셔야 합니다.3. 코드도 같이 올려주세요. 다만 코드 전체를 다 올리거나, 깃헙 주소만 띡 던지지는 마세요. 여러분이 "가장" 의심스럽다고 생각하는 코드를 올려주세요.4. 이 강좌를 바탕으로 여러분이 응용을 해보다가 막히는 부분, 여러 개의 선택지 중에서 조언이 필요한 부분, 제 경험이 궁금한 부분에 대한 질문은 대환영입니다. 다만 여러분의 회사 일은 질문하지 마세요.5. 강좌 하나 끝날 때마다 남의 질문들을 읽어보세요. 여러분이 곧 만나게 될 에러들입니다.6. 위에 적은 내용을 명심하지 않으시면 백날 강좌를 봐도(제 강좌가 아니더라도) 실력이 늘지 않고 그냥 코딩쇼 관람 및 한컴타자연습을 한 셈이 될 겁니다.
-
미해결[웹 개발 풀스택 코스] Node.js 프로젝트 투입 일주일 전 - 기초에서 실무까지
그럼 그걸 다 들어야 하나요?
다른 강의에 테이블이 있다고 해서 가보니..파일로 있는 게 아니고 화면에 있던데..그러면 저 처럼 그런 강의가 필요 없는 사람은..그걸 다 보고 만들라는 건가요? 그러기엔 시간이 아까운데요?하루죙일..개발 하다가 왔는데..그걸 보고 일일이 쳐서 만들라는 건 좀 아니지 않나요?
-
미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
uploads파일의 이미지 이름 한글 깨짐 질문
프론트에서 formData넣기 직전에 파일 객체를 봤을때 한글이 제대로 잘 들어가 있었는데, 이걸 보내고 multer 설정에서 받자마자 바로 destination 메서드와 filename메서드에서 file.originalname을 콘솔 로그로 확인을 해보니 이미 한글이 깨져있더라고요. 그래서 프로젝트 로컬에서 터미널로 chcp 65001 명령어로 인코딩을 utf-8로 바꿔줬는데 그래도 여전히 이미지 파일명의 한글이 깨지던데 시도해볼 다른 방법이 더 있나요?
-
미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
카카오 로그인 serializer 구현
안녕하세요 제로초님.passport-kakao를 이용해서 kakao 로그인을 구현해보고 있습니다.로그인과 회원가입 까지는 잘 되는데, serializer을 통해서 cookie가 저장되지 않습니다. kakako.strategy.tsimport { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-kakao'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import * as _ from 'lodash'; import { AuthService } from './auth.service'; import { Platform } from 'src/entities/common/Platforms'; @Injectable() export class KakaoStrategy extends PassportStrategy(Strategy) { constructor( private readonly configService: ConfigService, private authService: AuthService, ) { super({ clientID: configService.get<string>('KAKAO_REST_API_KEY'), clientSecret: configService.get<string>('KAKAO_CLIENT_SECRET'), callbackURL: configService.get<string>('KAKAO_REDIRECT_URI'), }); } async validate(accessToken, refreshToken, profile, done) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { _json: { id, properties: { nickname, profile_image: profileImage }, kakao_account: { email }, }, } = profile; const user = await this.authService.findOrCreateUser( email, nickname, Platform.KAKAO, ); return done(null, user); } } 아래에 있는 kakao-auth.guard.ts와 kakao.serializer.ts는 로컬 로그인과 다른 점이 없을 것 같아서 그대로 썼습니다. kakao-auth.guard.tsimport { ExecutionContext, Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class KakaoAuthGuard extends AuthGuard('kakao') { async canActivate(context: ExecutionContext): Promise<boolean> { const can = await super.canActivate(context); if (can) { const request = context.switchToHttp().getRequest(); await super.logIn(request); } return true; } } kakao.serializer.tsimport { Injectable } from '@nestjs/common'; import { PassportSerializer } from '@nestjs/passport'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { AuthService } from './auth.service'; import { Users } from 'src/entities/Users'; @Injectable() export class KakaoSerializer extends PassportSerializer { constructor( private readonly authService: AuthService, @InjectRepository(Users) private usersRepository: Repository<Users>, ) { super(); } serializeUser(user: Users, done: CallableFunction) { done(null, user.id); } async deserializeUser(userId: string, done: CallableFunction) { return await this.usersRepository .findOneOrFail({ where: { id: +userId }, select: ['id', 'email', 'nickname'], }) .then((user) => { done(null, user); }) .catch((error) => done(error)); } }이처럼 작성하면, 로그인 시 세션에 쿠키가 저장되어야 하는게 아닌가요?사실 강의에서 설명해주신 내용중 이해가 잘 가지 않는 부분이 있습니다.localStrategy에서 done(null, user) -> local-auth.guard.ts에서 super.logIn(request) -> local serializer 에서 serializeUser() 을 호출한다고 말씀하셨는데, 세 가지 파일에서 LocalStrategy, LocalAuthGuard, LocalSerializer을 서로 명시적으로 연결해준적이 없음에도 불구하고 어떻게 서로 잘 알아서 호출되는지 궁금합니다.그냥 앞에 모두 Local이 붙어서, 잘 찾아서 호출되는 건가요?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
faker 사용 후 postData.split is not a function 에러 질문
이전에는 잘 되었는데 faker로 더미데이터 사용 후에 다음과 같은 에러가 뜹니다!faker 버전 이슈를 보고 삭제 후 npm i -D faker@5 로 재설치했는데 오류가 해결되지 않았습니다ㅠ console.log 찍어본 postData의 타입이랑 에러메시지 입니다!4. WrappedApp created new store with withRedux(NodeBird) { initialState: undefined, initialStateFromGSPorGSSR: undefined }type : stringtype : functionTypeError: postData.split is not a functionat PostCardContent (C:\Users\pc\react-nodebird\prepare\front\.next\server\pages\index.js:1131:13)at processChild (C:\Users\pc\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3043:14)at resolve (C:\Users\pc\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:2960:5)at ReactDOMServerRenderer.render (C:\Users\pc\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3435:22)at ReactDOMServerRenderer.read (C:\Users\pc\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3373:29)at renderToString (C:\Users\pc\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3988:27)at Object.renderPage (C:\Users\pc\react-nodebird\prepare\front\node_modules\next\dist\next-server\server\render.js:50:851)at Document.getInitialProps (C:\Users\pc\react-nodebird\prepare\front\.next\server\pages\_document.js:264:19)at loadGetInitialProps (C:\Users\pc\react-nodebird\prepare\front\node_modules\next\dist\next-server\lib\utils.js:5:101)at renderToHTML (C:\Users\pc\react-nodebird\prepare\front\node_modules\next\dist\next-server\server\render.js:50:1142) PostCardContent.js 코드 중 의심스러운 부분입니다const PostCardContent = ({ postData }) => ( <div> {postData.split(/(#[^\s#]+)/g).map((v, i) => { if (v.match(/(#[^\s#]+)/)) { return <Link href={`/hashtag/${v.slice(1)}`} key={i}><a>{v}</a></Link>; } return v; })} </div> ); PostCardContent.propTypes = { postData: PropTypes.string.isRequired, };
-
미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
중간 테이블 질문
엔티티 작성할 때 @ManyToMany와 @JoinTable을 사용해서 중간 테이블을 설정해주고, 따로 또 ~Members엔티티를 작성해서 중간 테이블을 설정해줬는데 이게 겹치는 문제는 없나요? 워크 벤치에 생성된 중간 테이블은 하나밖에 없던데 그건 @JoinTable에 의해 생성된 중간 테이블인가요? 직접 작성한 중간 테이블인 ~Members엔티티인가요?
-
미해결[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
NODE로 프로그램을 WINDOW 설치 프로그램으로 만들고싶습니다.
제가 만든 NODEJS 프로그램을 한글처럼 WINDOW 환경에서 통합설치 프로그램 하나만 설치하면 DB, NODE 프로그램이 설치되도록 하고싶은데 어떤 방법이 있고 구글에 어떻게 검색해야 하는지 궁금합니다.
-
해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
서비스 중 API 수정이 어렵다는 부분에 궁금한 점이 있습니다
7:47부터 말씀하시는 내용에 대한 질문입니다.API 설계가 잘못되었더라도 서비스 도중이면 수정하기가 어렵다고 하셨는데요.예시로 보여주신 것처럼 API 내에서 쓰이는 함수는 섣불리 건들면 안된다는건 이해가 되었습니다. 그런데 (':url/members/:id')에서 (':url/users/:id')로 고치는 것과 같이 URL 수정에는 어떤 이유로 어려움이 있는지가 궁금합니다.제가 생각하기에는 개발자가 아닌 일반적인 사용자들은 프론트엔드에서 버튼과 같은 UI를 클릭해서 이용하지, 위 URL을 전부 입력해서 사용하는 경우는 잘 없지 않을까? 라고 생각이 들거든요.혹시 같이 작업 중인 동료 개발자들에게 혼란을 줄 수 있는 이유에서일까요?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
slick에 이미지가 안 뜨는 오류
상세이미지에서 전체화면까지는 되었는데, 이미지가 안 떠서 질문드립니다 밑에 이미지 Indicator를 보면 스크롤하면 다음으로 넘어가는 기능은 잘 작동하는 것 같은데이미지 불러오기가 안 되는 것 같습니다만 이유를 모르겠습니다.. 컴파일도 잘 되었구요ㅠ ImagesZoom/index.js 코드 첨부합니다const ImagesZoom = ({ images, onClose }) => { const [currentSlide, setCurrentSlide] = useState(0); return ( <Overlay> <Global /> <Header> <h1>상세 이미지</h1> <CloseBtn onClick={onClose}>X</CloseBtn> </Header> <SlickWrapper> <div> <Slick initialSlide={0} beforeChange={(slide) => setCurrentSlide(slide)} infinite arrows={false} slidesToShow={1} slidesToScroll={1} > {images.map((v) => ( <ImgWrapper key={v.src}> <img src={v.src} alt={v.src} /> </ImgWrapper> ))} </Slick> <Indicator> <div> {currentSlide + 1} {' '} / {images.length} </div> </Indicator> </div> </SlickWrapper> </Overlay> ); }; PostImages.js 코드입니다const PostImages = ({ images }) => { const [showImagesZoom, setShowImagesZoom] = useState(false); const onZoom = useCallback(() => { setShowImagesZoom(true); }, []); const onClose = useCallback(() => { setShowImagesZoom(false); }, []); if (images.length === 1) { return ( <> <img role="presentation" src={images[0].src} alt={images[0].src} onClick={onZoom} /> {showImagesZoom && <ImagesZoom images={images} onClose={onClose} />} </> ); } if (images.length === 2) { return ( <> <div> <img style={{ width: "50%", display: 'inline-block' }} role="presentation" src={images[0].src} alt={images[0].src} onClick={onZoom} /> <img style={{ width: "50%", display: 'inline-block' }} role="presentation" src={images[1].src} alt={images[1].src} onClick={onZoom} /> </div> {showImagesZoom && <ImagesZoom images={images} onClose={onClose} />} </> ); } return ( <> <div> <img style={{ width: "50%" }} role="presentation" 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> {showImagesZoom && <ImagesZoom images={images} onClose={onClose} />} </> ); };
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요 제로초님 이미지 업로드 관련 질문이 있습니다.
이미지 업로드를 위한 multer 강의까지 수강을 하였습니다.이미지 업로드를 할때 uploads 폴더에도 이미지가 잘 들어가고UPLOAD_IMAGES_SUCCESS도 잘 나오는 상황입니다.하지만 제로초님의 화면은 제거 버튼과 비록 깨지지만 이미지가 올라간 화면이 보이는데 저는 그 부분이 나오지 않아서 이 점이 궁금하여 질문 드립니다.import React, { useState } from "react"; import { Button, Card, Popover, Avatar, Image, List, Comment } from "antd"; import { RetweetOutlined, HeartOutlined, MessageOutlined, EllipsisOutlined, HeartTwoTone, } from "@ant-design/icons"; import { useCallback } from "react"; import { useSelector, useDispatch } from "react-redux"; import PropTypes from "prop-types"; import PostImages from "./PostImages"; import CommentForm from "./CommentForm"; import PostCardContent from "./PostCardContent"; import { REMOVE_POST_REQUEST, LIKE_POST_REQUEST, UNLIKE_POST_REQUEST, } from "../reducers/post"; import FollowButton from "./FollowButton"; const PostCard = ({ post }) => { //pages/index.js에서 mainPosts에서 하나씩 뜯어서 보내줌 const dispatch = useDispatch(); const [commentFormOpened, setCommentFormOpened] = useState(false); //댓글창 열지 말지 const onLike = useCallback(() => { dispatch({ type: LIKE_POST_REQUEST, data: post.id, }); }, []); //좋아요 const onUnlike = useCallback(() => { dispatch({ type: UNLIKE_POST_REQUEST, data: post.id, }); }, []); //좋아요 취소 const onToggleComment = useCallback(() => { setCommentFormOpened((prev) => !prev); }, []); //폼 버튼 한번 더 누르면 폼 닫기 const onRemovePost = useCallback(() => { return dispatch({ type: REMOVE_POST_REQUEST, data: post.id, }); }, [id]); const id = useSelector((state) => state.user.me?.id); const { removePostloading } = useSelector((state) => state.post); const liked = post.Likers.find((v) => v.id === id); //게시글 좋아요 누른 사람 중에 내가 있는지. return ( <div style={{ marginBottom: 20 }}> <Card cover={post.Images?.[0] && <PostImages images={post.Images} />} //이미지가 존재한다면 PostImages를 출력 actions={[ //카드 아래에 존재하는 것들 <RetweetOutlined key="retweet" />, liked ? ( <HeartTwoTone twoToneColor="red" onClick={onUnlike} /> ) : ( <HeartOutlined key="heart" onClick={onLike} /> ), <MessageOutlined onClick={onToggleComment} key="comment" />, <Popover //더보기 같은 역할 key="more" content={ <Button.Group> {id && post.User.id === id ? ( <> {/* 내가 쓴 글이면 수정, 삭제 */} <Button>수정</Button> <Button type="danger" onClick={onRemovePost} loading={removePostloading} > 삭제 </Button> </> ) : ( // 내가 쓴 글이 아니라면 <Button>신고</Button> )} </Button.Group> } > <EllipsisOutlined /> </Popover>, ]} extra={id && <FollowButton post={post} />} > <Card.Meta //프로필과 내용 등 avatar={<Avatar>{post.User.nickname[0]}</Avatar>} title={post.User.nickname} description={<PostCardContent postData={post.content} />} /> </Card> {commentFormOpened && ( //commentFormOpened가 true이면 열어라 <div> {/* 어떤 게시글에 댓글을 남기는지.. */} <CommentForm post={post} /> <List header={`${post.Comments.length}개의 댓글`} itemLayout="horizontal" dataSource={post.Comments} //데이터는 여기서 가져와서 renderItem={( item //이런식으로 출력한다 ) => ( <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, content: PropTypes.string, createdAt: PropTypes.string, Comment: PropTypes.arrayOf(PropTypes.object), Images: PropTypes.arrayOf(PropTypes.object), Likers: PropTypes.arrayOf(PropTypes.object), }).isRequired, }; export default PostCard;저의 PostCard 코드입니다 이 코드에서 cover={post.Images?.[0] && <PostImages images={post.Images} />}이 부분이 이미지들을 출력해주는 부분이 아닌가요?? 저의 화면에는 아래처럼 나오지 않습니다.
-
해결됨[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
sequelize initiate() 쓰는 방식 최신인지, 공식문서 방법인지 궁금합니다
영상에서는 sequelize 공식문서에서 바뀌어서 따라갔다고 하는데 공식문서에서 initiate() 쓰는 것 검색하였을 때 안보이며 static이랑 같이 쓴 것도 아직은 못찾았습니다 공식문서 따라가보면 주로 define(), init()을 쓰는데 강의영상의 방식과는 차이점이 있습니다.define()const { Sequelize, DataTypes } = require('sequelize'); const sequelize = new Sequelize('sqlite::memory:'); const User = sequelize.define('User', { // Model attributes are defined here firstName: { type: DataTypes.STRING, allowNull: false }, lastName: { type: DataTypes.STRING // allowNull defaults to true } }, { // Other model options go here }); // `sequelize.define` also returns the model console.log(User === sequelize.models.User); // trueinit()// Invalid class User extends Model { id; // this field will shadow sequelize's getter & setter. It should be removed. otherPublicField; // this field does not shadow anything. It is fine. } User.init({ id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true } }, { sequelize }); const user = new User({ id: 1 }); user.id; // undefined 강의에서 알려주는 방식으로 DB연결도 되고 조작도 잘되지만이렇게 질문을 남긴 이유는강의에서는 공식문서를 따라갔다고 하는데 공식문서에서는 찾기 어렵다는 점과강의에 나오는 방식이 시기가 지난 방법이 아닐까 하는 생각에 질문 남기게 되었습니다 제가 찾아본게 잘못된걸수도 있기에 그런점 있다면 알려주세요글읽어주셔서 고맙습니다:)
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
게시글 작성 오류
안녕하세요 제로초님!게시글을 작성하게되면 아래와 같이 성공했다고 응답도 잘 도착하지만 id가 undefined이라고 오류가 납니다.제가 어느 부분을 놓치고 있는건가요??
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
로그인 오류
안녕하세요 제로초님 middleware.js를 사용하여로그인했을때와 하지않았을 때의 경우를 나눠놓으셨자나요!그 강좌를 듣고 코드를 그대로 작성하고 로그인을 진행해보는데올바른 이메일과 비밀번호를 입력해도 이 알림이 뜹니다.. 로그인도 실패로 응답하구요.. 어떤 부분이 문제일 가능성이 높을까요??
-
미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
app.use(passport.initialize()) 질문
궁금한거 있으면 일단 ChatGPT에 검색해서 보고 있는데"NestJS의 Passport 통합에서는 PassportModule의 설정을 사용하여 Passport 초기화 및 세션 관리를 추상화합니다. 즉, PassportModule을 사용하면 내부적으로 passport.initialize()는 처리됩니다.그러나 passport.session()과 관련하여서는 다르게 동작합니다. passport.session() 미들웨어는 세션을 관리하기 위해 필요한 작업을 수행합니다. 이 미들웨어는 사용자의 요청마다 Passport를 통해 세션에서 사용자 정보를 복원하는 역할을 합니다. NestJS에서 세션 기반 인증을 사용할 때는 passport.session() 미들웨어를 수동으로 추가해주어야 합니다.따라서, 당신의 질문에 답하면, NestJS에서 passport.initialize()는 PassportModule을 사용함으로써 내부적으로 처리되지만, passport.session()은 명시적으로 추가해주어야 합니다.즉, app.use(passport.session())는 필요하며, app.use(passport.initialize())는 PassportModule 사용시에는 필요하지 않습니다."라고 하더군요. app.use(passport.initialize())는 필요가 없나요?
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
특정 페이지의 비로그인 차단
강좌에서처럼 me(내 정보 불러오는 상태변수) 변수를 통해 로그인 유지를 하고특정 페이지에서 비로그인 사용자를 차단할경우useEffect(() => { if(!me){ redirects(); } },[])이런식으로 로직을 짤수가 있는데문제가 브라우저 url로 접속할 경우 초기 me 의 상태가 null 이기 때문에 로그인을 한 상태더라도리다이렉트가 되는 문제가 있었습니다. 물론 ssr을 이용하면 이 문제가 해결은 되지만csr만 이용하는 선에서 리다이렉트가 정상적으로 작동하도록 하고 싶은데 어떻게 하면 좋을까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요 제로초님 로그인 관련 redux 질문있습니다.
다름이 아니라 제로초님의 로그인 문제 해결하기 챕터를 보다가Posts.length관련 오류 해결하는 강의를 보는데제로초님의 redux화면이랑 저의 화면이랑 구성이 좀 다른 것 같아서 문의드립니다.제로초님의 화면은 위 사진처럼 me라는 객체로 나와서 확인하기 쉬운데제 화면은 아래처럼 그냥 데이터 라고 나오고 있습니다..어느 부분을 수정해야하는지 잘 모르겠어서 질문드립니다.
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
글 작성이 안 되는 오류
컴파일은 오류 없이 잘 되는데, 폼에서 짹짹(게시)버튼을 눌러도 글 작성이 되지 않습니다개발자도구에서 나타나는 오류입니Incorrect use of <label for=FORM_ELEMENT>The label's for attribute doesn't match any element id. This might prevent the browser from correctly autofilling the form and accessibility tools from working correctly.To fix this issue, make sure the label's for attribute references the correct id of a form field. PostCard가 작성되지 않는데 코드에서 오류를 못 찾겠어서 질문합니다! 의심스러운 부분들만 올려봅니다..post.js 코드입니다const ADD_POST = 'ADD_POST'; export const addPost = { type: ADD_POST } const reducer = (state = initialState, action) => { switch (action.type) { case ADD_POST: { return { ...state, mainPosts: [dummyPost, ...state.mainPosts], postAdded: true, }; }; default: { return { ...state, }; }; } }; PostForm.js 코드입니다const PostForm = () => { const { imagePaths } = useSelector((state) => state.post); const dispatch = useDispatch(); const imageInput = useRef(); const [text, setText] = useState(''); const onChangeText = useCallback((e) => { setText(e.target.value); }, []); const onSubmit = useCallback(() => { dispatch(addPost); setText('') }, []); 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" multiple hidden ref={imageInput} /> <Button onClick={onClickImageUpload}>이미지 업로드</Button> <Button type="primary" style={{ float: 'right' }} htmlType="submit">게시</Button> </div> <div> {imagePaths?.map((v) => ( <div key={v} style={{ display: 'inline-block' }}> <img scr={v} style={{ width: '200px' }} alt={v} /> <div> <Button>제거</Button> </div> </div> ))} </div> </Form> ) }; pages/index.js 코드입니다const Home = () => { const { isLoggedIn } = useSelector((state) => state.user); const { mainPosts } = useSelector((state) => state.post); return ( <AppLayout> {isLoggedIn && <PostForm />} {mainPosts.map((post) => <PostCard key={post.id} post={post} />)} </AppLayout> );