묻고 답해요
156만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
dispatch로 데이터를 넘겨주는 과정 질문
제가 redux toolkit을 이용해서 제로초님 강의를 따라가고 있는데 dispatch로 데이터를 넘겨주는 과정중에 이해가 잘 안되는 부분이 있어서 질문드립니다로그인을 하는 과정인데LoginForm.js const onSubmitForm = useCallback(() => { console.log(email, password); dispatch( loginAction({ email, password, }) ); }, [email, password]);user.js export const loginAction = createAsyncThunk(LOG_IN, async (data) => { await wait(1000); return data; }); .... .addCase(loginAction.fulfilled, (state, action) => { state.logInLoading = false; state.logInDone = true; state.user = dummyUser; state.loginData = action.data; })이렇게 두부분의 코드로 아래의 action 과정이 일어나는데 LoginForm.js에서 dispatch로 email, password를 넘겨주었는데 딱히 user.js에서 이를 처리하는 코드는 보이지 않습니다 redux devtools에서는 payload에 email과 password를 받은것은 확인이 되는데 말입니다그리고 저는 initialState에 email과 password를 설정하지 않았습니다export const initialState = { logInLoading: false, // 로그인 시도중 logInError: null, // 로그인 에러 logInDone: false, // 로그인 상태 체크 logOutLoading: false, //로그아웃 시도중 logOutError: null, // 로그아웃 에러 signUpLoading: false, // 회원가입 시도중 signUpDone: false, // 회원가입 상태 체크 signUpError: null, // 회원가입 에러 user: null, signUpData: {}, loginData: {}, }; 이렇게 되면 payload에 들어있는 email과 password는 어디에 어떻게 저장이 되는걸로 생각을 해야하나요?
-
해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
fe `내 정보 조회`시 프론트에서 데이터 불러오지 못합니다.
조현영님 안녕하세요. 프론트, 백엔드 강좌를 잘 보고있습니다! 현영님의 강좌에서 useSwr를 사용하는법을 배우고, 백엔드도 구현해보고자 하여, 백엔드도 수강하게 되었습니다. 회원가입과 로그인을 하였을경우 정상적으로 작동하여, 로그인을 하였을경우, 프론트에게 쿠키값을 제대로 넘겨주고있지만, response 데이터를 받지 못하고있는데 원인을 알 수가 없습니다. front에서 로그인 하였을시 나오는 콘솔data user: {data: '', status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …} installHook.js:342 data user: {data: '', status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …} fetcher.ts:6 response: {data: '', status: 200, statusText: 'OK', headers: AxiosHeaders, config: {…}, …} index.tsx:91 back에서 로그인 request받았을때, 응답하는 콘솔[Nest] 69849 - 06/28/2023, 10:09:06 AM LOG [HTTP] GET /api/users 200 undefined - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ::1 query: SELECT DISTINCT `distinctAlias`.`Users_id` AS `ids_Users_id` FROM (SELECT `Users`.`id` AS `Users_id`, `Users`.`email` AS `Users_email`, `Users`.`nickname` AS `Users_nickname`, `Users__Users_Workspaces`.`id` AS `Users__Users_Workspaces_id`, `Users__Users_Workspaces`.`name` AS `Users__Users_Workspaces_name`, `Users__Users_Workspaces`.`url` AS `Users__Users_Workspaces_url`, `Users__Users_Workspaces`.`createdAt` AS `Users__Users_Workspaces_createdAt`, `Users__Users_Workspaces`.`updatedAt` AS `Users__Users_Workspaces_updatedAt`, `Users__Users_Workspaces`.`deletedAt` AS `Users__Users_Workspaces_deletedAt`, `Users__Users_Workspaces`.`OwnerId` AS `Users__Users_Workspaces_OwnerId` FROM `users` `Users` LEFT JOIN `workspacemembers` `Users_Users__Users_Workspaces` ON `Users_Users__Users_Workspaces`.`UserId`=`Users`.`id` LEFT JOIN `workspaces` `Users__Users_Workspaces` ON `Users__Users_Workspaces`.`id`=`Users_Users__Users_Workspaces`.`WorkspaceId` AND (`Users__Users_Workspaces`.`deletedAt` IS NULL) WHERE ( (`Users`.`id` = ?) ) AND ( `Users`.`deletedAt` IS NULL )) `distinctAlias` ORDER BY `Users_id` ASC LIMIT 1 -- PARAMETERS: [9] query: SELECT `Users`.`id` AS `Users_id`, `Users`.`email` AS `Users_email`, `Users`.`nickname` AS `Users_nickname`, `Users__Users_Workspaces`.`id` AS `Users__Users_Workspaces_id`, `Users__Users_Workspaces`.`name` AS `Users__Users_Workspaces_name`, `Users__Users_Workspaces`.`url` AS `Users__Users_Workspaces_url`, `Users__Users_Workspaces`.`createdAt` AS `Users__Users_Workspaces_createdAt`, `Users__Users_Workspaces`.`updatedAt` AS `Users__Users_Workspaces_updatedAt`, `Users__Users_Workspaces`.`deletedAt` AS `Users__Users_Workspaces_deletedAt`, `Users__Users_Workspaces`.`OwnerId` AS `Users__Users_Workspaces_OwnerId` FROM `users` `Users` LEFT JOIN `workspacemembers` `Users_Users__Users_Workspaces` ON `Users_Users__Users_Workspaces`.`UserId`=`Users`.`id` LEFT JOIN `workspaces` `Users__Users_Workspaces` ON `Users__Users_Workspaces`.`id`=`Users_Users__Users_Workspaces`.`WorkspaceId` AND (`Users__Users_Workspaces`.`deletedAt` IS NULL) WHERE ( (`Users`.`id` = ?) ) AND ( `Users`.`deletedAt` IS NULL ) AND ( `Users`.`id` IN (9) ) -- PARAMETERS: [9] getUsers user: Users { id: 9, email: 'sinde530@naver.com', nickname: '이카자', Workspaces: [] } [Nest] 69849 - 06/28/2023, 10:09:18 AM LOG [HTTP] GET /api/users 200 undefined - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 ::1 코드들// user.controller.ts import { Body, Controller, Get, Post, Req, Res, UseGuards, UseInterceptors, } from '@nestjs/common'; import { ApiCookieAuth, ApiOperation, ApiResponse, ApiTags, } from '@nestjs/swagger'; import { LocalAuthGuard } from 'src/auth/local-auth.guard'; import { LoggedInGuard } from 'src/auth/logged-in.guard'; import { NotLoggedInGuard } from 'src/auth/not-logged-in.guard'; import { User } from 'src/common/decoraters/users.decorater'; import { UserDto } from 'src/common/dto/user.dto'; import { UndefinedToNullInterceptor } from 'src/common/interceptors/undefinedToNull.interceptor'; import { Users } from 'src/entities/Users'; import { SignUpRequestDto } from './dto/signup.request.dto'; import { UsersService } from './users.service'; @UseInterceptors(UndefinedToNullInterceptor) @ApiTags('Users') @Controller('api/users') export class UsersController { constructor(private usersService: UsersService) {} @ApiResponse({ status: 200, description: '성공', type: UserDto, }) @ApiResponse({ status: 500, description: '서버 에러', }) @ApiCookieAuth('connect.sid') @ApiOperation({ summary: '내 정보 조회' }) @Get() async getUsers(@User() user: Users) { console.log('getUsers user:', user); return user || false; } @ApiOperation({ summary: '회원가입' }) @UseGuards(NotLoggedInGuard) @Post('signup') async signup(@Body() data: SignUpRequestDto) { await this.usersService.postUsers(data.email, data.nickname, data.password); } @ApiResponse({ status: 200, description: '로그인 성공', type: UserDto, }) @ApiResponse({ status: 500, description: '서버 에러', }) @ApiOperation({ summary: '로그인' }) @UseGuards(LocalAuthGuard) @Post('signin') async signin(@User() user: Users) { return user; } @ApiOperation({ summary: '로그아웃' }) @UseGuards(LoggedInGuard) @Post('logout') logOut(@Req() request, @Res() response) { request.logOut(); response.clearCookie('connect.sid', { httpOnly: true }); response.send('ok'); } } // SignIn.tsx import axios from 'axios'; import { useCallback, useState } from 'react'; import useInput from 'src/hooks/useInput'; import fetcher from 'src/utils/fetcher'; import useSWR from 'swr'; import { Button, Container, Form, InfoBox, Input, LoginInfoBox, LoginLink, LoginText, Logo, RegisterBox, RegisterErrorText, RegisterText, SubContainer, WelcomeBox, WelcomeText, WrapperImageLogo, } from './styled'; export default function SignIn() { const { data, error, mutate } = useSWR( 'http://localhost:3090/api/users', fetcher, ); const [email, onChangeEmail] = useInput(''); const [password, onChangePassword] = useInput(''); const emailRegex = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; const [errors, setErrors] = useState({ email: false, password: false, invalidEmail: false, }); const [presentEmail, setPresentEmailError] = useState(false); const [loginError, setLoginError] = useState(false); const Image = 'https://dummyimage.com/600x400/000/fff'; const handleSubmit = useCallback( async (e: any) => { e.preventDefault(); const newErrors = { email: !email, password: !password, invalidEmail: !!(email && !emailRegex.test(email)), }; setPresentEmailError(false); setErrors(newErrors); setLoginError(false); if (Object.values(newErrors).some((error) => error)) { return; } try { await axios .post( 'http://localhost:3090/api/users/signin', { email, password, }, { withCredentials: true }, ) .then(() => { mutate(); }) .catch((error) => { setPresentEmailError(error.response?.data); setLoginError(error); }); } catch (error) { console.error(error); } }, [email, password, mutate], ); console.log('data user:', data); if (error) console.log('error:', error); // if (!error && userData) { // console.log('로그인됨', userData); // return <Navigate to="/workspace/purrfect-chat/channel/general" />; // } return ( <Container> <SubContainer> <WrapperImageLogo> <Logo src={Image} alt="error" /> </WrapperImageLogo> <RegisterBox> <WelcomeBox> <WelcomeText>Welcome to Purrfect Chat!</WelcomeText> </WelcomeBox> <Form> <InfoBox> {errors.email && ( <RegisterErrorText> 이메일을 입력해 주세요. </RegisterErrorText> )} {errors.invalidEmail && ( <RegisterErrorText> 이메일 형식을 입력해 주세요. </RegisterErrorText> )} {loginError && ( <RegisterErrorText> 이메일 또는 비밀번호가 일치하지 않습니다. </RegisterErrorText> )} {!errors.email && !errors.invalidEmail && !presentEmail && ( <RegisterText>이메일</RegisterText> )} <Input name="email" type="text" value={email} onChange={onChangeEmail} /> </InfoBox> <InfoBox> {errors.password && ( <RegisterErrorText> 비밀번호를 입력해 주세요. </RegisterErrorText> )} {loginError && ( <RegisterErrorText> 이메일 또는 비밀번호가 일치하지 않습니다. </RegisterErrorText> )} {!errors.password && !loginError && ( <RegisterText>비밀번호</RegisterText> )} <Input name="password" type="password" autoComplete="true" value={password} onChange={onChangePassword} /> </InfoBox> <InfoBox> <Button type="button" onClick={handleSubmit}> 로그인 </Button> </InfoBox> <LoginInfoBox> <LoginText>아직 회원이 아니신가요?</LoginText> <LoginLink to="/signup"> 회원가입 하러가기 </LoginLink> </LoginInfoBox> </Form> </RegisterBox> </SubContainer> </Container> ); } // fetcher.ts import axios from 'axios'; const fetcher = async (url: string) => { try { const response = await axios.get(url, { withCredentials: true }); console.log('response:', response); return response; } catch (error: any) { throw new Error(error.response?.data); } }; export default fetcher; back/main.ts import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; import cookieParser from 'cookie-parser'; import session from 'express-session'; import passport from 'passport'; import { AppModule } from './app.module'; import { HttpExceptionFilter } from './httpException.filter'; declare const module: any; async function bootstrap() { const app = await NestFactory.create(AppModule); app.useGlobalFilters(new HttpExceptionFilter()); app.useGlobalPipes( new ValidationPipe({ transform: true, }), ); app.enableCors({ origin: true, credentials: true, }); const config = new DocumentBuilder() .setTitle('HTTP API ') .setDescription('개발 API 문서입니다.') .setVersion('1.0') .addCookieAuth('connect.sid') .build(); const documnet = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, documnet); app.use(cookieParser()); app.use( session({ resave: false, saveUninitialized: false, secret: process.env.COOKIE_SECRET, cookie: { httpOnly: true, }, }), ); app.use(passport.initialize()); app.use(passport.session()); const port = process.env.PORT || 3090; await app.listen(port); console.log(`listening on port ${port}`); if (module.hot) { module.hot.accept(); module.hot.dispose(() => app.close()); } } bootstrap();
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
Material Theme 테마 관련해서 질문 드립니다.
똑같이 설치 했는데 왜 저는 강사님 처럼데코레이터는 파란색함수명은 빨간색으로 하고 싶은데저는 이 2개가 같이 바뀌네요 ? ㅠ 스코프를 아래와 같이 설정하면, 데코랑 함수명이 같이 움직입니다. 따로 셋팅은 힘든 걸가요?"scope": ["entity.name.function"], "settings": { "foreground": "#FFFF00" }
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
WSL2로 강의 수강 시 문제점에 관하여 아래 내용여쭙고 싶어 글 남깁니다.
안녕하세요. 백엔드 과정을 수강 중인 학생입니다. WSL2로 강의 수강 시 문제점에 관하여 아래 내용여쭙고 싶어 글 남깁니다. 번거롭겠지만 답변해주시면 정말 감사하겠습니다. 현재 16GB RAM 사용 중이며 윈도우 기본 설치된 노트북을 사용 중입니다. 윈도우를 제거하고 우분투를 설치할 환경이 되지 않아서 WSL2를 설치하여 사용하려 하는데 강의를 들으면서 추후 RAM 관련 문제가 발생할 수 있을까요? ***아래 캡처는 현재 제 노트북 사양입니다***[캡처]
-
미해결Node.js 노드 빠르게 훑어보기: 서버부터 DB까지
datail 페이지 작성하는 부분은 강의에 없는건가요??
확인 부탁드립니다!
-
해결됨[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
10.6 재질문…!
exports.apiLimiter = async (req, res, next) => { let user; if (res.locals.decoded) { user = await User.findOne({ where: { id: res.locals.decoded.id } }); } rateLimit({ widowMs: 60 * 1000, max: user?.type === "premium" ? 10 : 1, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, message: "1분에 열 번만 요청 할 수 있습니다...", }); }, })(req, res, next); }; Api 프리미엄고객만 1분에 열번만요청할수있게 미들웨어 확장패턴으로만들었습니다 그런데 localhost:4000/myposts 접속해서 10 번이상새로고침해도 api가 제한이 안되고 제가작성한 게시글 목록만 뜹니다
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
다 잘되는데 배너 슬라이드가 안돼요
배너 슬라이드 기능만 잘 되지 않습니다.최신 버전 강의 따라 하였습니다....
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
도커 yarn install
#0 10.10 error @graphql-tools/merge@9.0.0: The engine "node" is incompatible with this module. Expected version ">=16.0.0". Got "14.21.3"#0 10.10 error Found incompatible module.#0 10.10 info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command. ------Dockerfile:10--------------------8 | COPY ./yarn.lock /myfolder/9 | WORKDIR /myfolder/10 | >>> RUN yarn install11 |12 | COPY . /myfolder/--------------------ERROR: failed to solve: process "/bin/sh -c yarn install" did not complete successfully: exit code: 1ERROR: Service 'my-backend' failed to build : Build faileddocker-compse bulid 했을때 에러가 발생합니다
-
미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
npm run db:create
import { DataSource } from 'typeorm'; import dotenv from 'dotenv'; import { ChannelChats } from './src/entities/ChannelChats'; import { ChannelMembers } from './src/entities/ChannelMembers'; import { Channels } from './src/entities/Channels'; import { DMs } from './src/entities/DMs'; import { Mentions } from './src/entities/Mentions'; import { Users } from './src/entities/Users'; import { WorkspaceMembers } from './src/entities/WorkspaceMembers'; import { Workspaces } from './src/entities/Workspaces'; dotenv.config(); const dataSource = new DataSource({ type: 'mysql', host: 'localhost', port: 3306, username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, entities: [ ChannelChats, ChannelMembers, Channels, DMs, Mentions, Users, WorkspaceMembers, Workspaces, ], migrations: [__dirname + '/src/migrations/*.ts'], charset: 'utf8mb4_general_ci', synchronize: false, logging: true, }); export default dataSource; 에러!Error: Can't add new command when connection is in closed stateat Connection._addCommandClosedState (/Users/minchankim/Desktop/study/nestToy/node_modules/mysql2/lib/connection.js:148:17)at Connection.end (/Users/minchankim/Desktop/study/nestToy/node_modules/mysql2/lib/connection.js:855:26)at Query.onResult (/Users/minchankim/Desktop/study/nestToy/node_modules/typeorm-extension/dist/database/driver/mysql.js:27:28)at Connection._notifyError (/Users/minchankim/Desktop/study/nestToy/node_modules/mysql2/lib/connection.js:228:17)at Connection._handleFatalError (/Users/minchankim/Desktop/study/nestToy/node_modules/mysql2/lib/connection.js:167:10)at Connection._handleNetworkError (/Users/minchankim/Desktop/study/nestToy/node_modules/mysql2/lib/connection.js:180:10)at Socket.emit (node:events:513:28)at Socket.emit (node:domain:489:12)at emitErrorNT (node:internal/streams/destroy:151:8)at emitErrorCloseNT (node:internal/streams/destroy:116:3) {fatal: true}계속 이렇게 나오는데 npm run db:create데이터베이스 정보확인 및 dotenv도 재설치 해봤는데 계속 안되서 질문드립니다!버전에 문제가 있나 싶어서 강사님 package.json에서 그대로 불러왔습니다!
-
해결됨[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
10.6 사용량 제한 질문
nodebird-api 미들웨어 index.js 타입이 any로 떠서 프리미엄이 체크가 안되고 계속 제가 작성한 게시글정보만 뜹니다 사용량제한 어떻게 하나요? ㅠㅠ const jwt = require("jsonwebtoken"); const rateLimit = require("express-rate-limit"); const User = require("../models/user"); exports.isLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { next(); } else { res.status(403).send("로그인 필요"); } }; exports.isNotLoggedIn = (req, res, next) => { if (!req.isAuthenticated()) { next(); } else { const message = encodeURIComponent("로그인한 상태입니다."); res.redirect(`/?error=${message}`); } }; exports.verifyToken = (req, res, next) => { try { res.locals.decoded = jwt.verify( req.headers.authorization, process.env.JWT_SECRET ); return next(); } catch (error) { if (error.name === "TokenExpiredError") { return res.status(419).json({ code: 419, message: "토큰이 만료되었습니다.", }); } return res.status(401).json({ code: 401, message: "유효하지 않은 토큰입니다.", }); } }; exports.apiLimiter = rateLimit({ windowMs: 60 * 1000, // 1분 max: 1, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, // 기본값 429 message: "1분에 한 번만 요청할 수 있습니다.", }); }, }); exports.apiLimiter = async (req, res, next) => { let user; if (res.locals.decoded) { user = await User.findOne({ where: { id: res.locals.decoded.id } }); } rateLimit({ widowMs: 60 * 1000, max: user?.type === "premium" ? 10 : 1, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, message: "1분에 열 번만 요청 할 수 있습니다...", }); }, })(req, res, next); }; exports.deprecated = (req, res) => { res.status(410).json({ code: 410, message: "새로운 버전이 나왔습니다. 새로운 버전을 사용하세요", }); };
-
미해결[웹 개발 풀스택 코스] Node.js 프로젝트 투입 일주일 전 - 기초에서 실무까지
현재 mysql 연동하기 강의를 듣고 있는데, ..
안녕하세요.현재 mysql 연동하기 강의를 듣고 있는데, sql 워크벤치에서 어떻게 칼럼을 넣어야 하는지 알수있을까요?
-
미해결따라하며 배우는 노드, 리액트 시리즈 - 영화 사이트 만들기
504 Gateway Timeout Error
저같은 경우는 오타 문제였습니다ㅠㅠ 비슷한 질문들 보이는데 서버 에러로그 잘 살펴보세요보면 ReferenceError에 falase라고 false 오타여서 오류났었어요
-
해결됨비전공자를 위한 진짜 입문 올인원 개발 부트캠프
강의 5분경 내용인 "메인 페이지" 글자가 뜨지 않습니다.
이전 메인페이지가 뜨지 않는다는 질문과 답변들을 통해 App.js파일과 index.js 파일 상단에 import React from 'react'; 를 넣기도 해보고 node와 npm 삭제 > 재설치 후 진행해 보아도 메인페이지에 글자가 뜨지 않아 질문드립니다ㅜㅜ또한 각 파일의 vscode내 터미널 로그를 보아도 딱히 에러가 보이지 않아 문의드립니다..!
-
미해결비전공자를 위한 진짜 입문 올인원 개발 부트캠프
따로 커밋을 하거나 재배포를 하지 않았는데, DB가 자동으로 초기화 됩니다.
fly.io에 server 프로젝트를 deploy 단계까지 마쳤다.web 프로젝트까지 배포가 완료 되어 모든 동작이 잘 이뤄지고 있다.하지만, 일정 시간이 지나면 자동으로 fly.io에 배포된 DB가 초기화가 된다.초기화가 된 것인지, local의 데이터가 자동 업로드 되면서 덮어씌여진 것인지는 잘 모르겠다.이전 질문의 답변을 보면, Commit을 하면서 fly.io가 자동으로 덮어씌여진다는 것을 보았다.-> 하지만, 실험 결과 Commit을 따로 하지 않아도 초기화가 된다.만약 git과 연관이 있다면6-1. 배포 이후 별다른 작업을 하지 않았지만, 일정시간이 지나면, vercel와 같이 자동으로 git의 repository 최신 내용 바탕으로 업데이트 되어, git에 이미 올라가 있던 DB가 fly.io에 반영이 되는것인가가 궁금합니다.6-2. 또한, 그렇다면 애초에 vercel은 레포지토리 주소를 참고하여 배포하였다면, fly.io는 그저 플랫폼 로그인 수단으로만 git을 연동하였는데, 최신 커밋을 참고하는 기능이 있는지도 궁금합니다.결론적으로 현재 반복적으로 DB가 초기화 되는 현상을 고치고 싶습니다. 추가적인 실험 결과, 로컬의 nodemon server.js가 돌고 있는 server 프로젝트를 종료를 하여도, 일정 시간이 지나면 fly.io의 DB가 초기화 되는 것으로 보아, server의 내용이 바뀌면서 자동으로 로컬 내용이 fly.io로 넘어가는 것도 아닌 것 같습니다.
-
해결됨[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
10.6 사용량 제한 구현하기파트 질문
nodebird-api 미들웨어 index.js코드 const jwt = require("jsonwebtoken"); const rateLimit = require("express-rate-limit"); const User = require("../models/user"); exports.isLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { next(); } else { res.status(403).send("로그인 필요"); } }; exports.isNotLoggedIn = (req, res, next) => { if (!req.isAuthenticated()) { next(); } else { const message = encodeURIComponent("로그인한 상태입니다."); res.redirect(`/?error=${message}`); } }; exports.verifyToken = (req, res, next) => { try { res.locals.decoded = jwt.verify( req.headers.authorization, process.env.JWT_SECRET ); return next(); } catch (error) { if (error.name === "TokenExpiredError") { return res.status(419).json({ code: 419, message: "토큰이 만료되었습니다.", }); } return res.status(401).json({ code: 401, message: "유효하지 않은 토큰입니다.", }); } }; exports.apiLimiter = async (req, res, next) => { let user; if (res.locals.decoded) { user = await User.findOne({ where: { id: res.locals.decoded.id } }); } rateLimit({ windowMs: 60 * 1000, max: user?.type === "premium" ? 1000 : 10, handler(req, res) { res.status(this.statusCode).json({ code: this.statusCode, message: "1분에 열 번만 요청할 수 있습니다.", }); }, }); }; exports.deprecated = (req, res) => { res.status(410).json({ code: 410, message: "새로운 버전이 나왔습니다. 새로운 버전을 사용하세요", }); }; nodebird-api routes v2.js 코드 const express = require("express"); const { verifyToken, apiLimiter } = require("../middlewares"); const { createToken, tokenTest, getMyPosts, getPostsByHashtag, } = require("../controllers/v2"); const router = express.Router(); router.post("/token", apiLimiter, createToken); router.get("/test", verifyToken, apiLimiter, tokenTest); router.get("/posts/my", verifyToken, apiLimiter, getMyPosts); router.get("/posts/hashtag/:title", verifyToken, apiLimiter, getPostsByHashtag); module.exports = router; 프리미엄으로 클라이언트 비밀키 발급하고 프리미엄만 사용량제한 구현했는데 터미널에 GET/search/%EA%B3%A0%EC%96%91%EC%9D%B4 - - ms - - 이렇게 나오고 사이트로딩만 뜹니다 ㅜㅜ
-
해결됨비전공자를 위한 진짜 입문 올인원 개발 부트캠프
왜 src폴더 위치에서 images가 불러와 지는 지 모르겠습니다
<img src="images/icons/logo.png" /> 만으로 사진이 불러와 지는데요 엄밀히 따지면 images 파일은 public 폴더 아래 있으니<img src="../public/images/icons/logo.png" /> 처럼 상위 폴더로 이동 후 public에서 images에 접근할 수 있는 것 아닌가요?
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
포트폴리오 프로젝트
안녕하세요,선생님 강의를 보며 열심히 백엔드 공부중입니다.혹시 포트폴리오 과제같은 경우는 프론트엔드 코스도 봐야 하나요? 아니면 백엔드 커리큘럼 한에서 만들기가 가능할까요?
-
해결됨[개정3판] Node.js 교과서 - 기본부터 프로젝트 실습까지
화살표 함수 자동완성이 궁금합니다
안녕하세요 선생님강의를 보다보니 그림판에서 텍스트로 코딩을 하시는데 화살표함수 작성시 뒤에 구문이 자동으로 입력이 되던데 그림판에 어떤 플러그인을 설치하셨길래 이런 기능이 되는건지 궁금합니다.
-
해결됨[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
Docker Compose Volumes - postman 조회 오류
"Docker Compose Volumes" 강의를 통해 local의 코드를 수정하면 docker 내 image가 nodemon으로인해 refresh되는 것을 cat 명령어로 파일을 열어 확인했습니다.그런데 코드를 변경 후 13:54에 나온것처럼 local의 콘솔창에 refresh에 대한 로그가 나타나지를 않으며,local에서 postman으로 확인할 때도 코드가 변경되지 않은 것처럼 작동합니다. 뭐가 문제일까요?
-
미해결MERN STACK 커뮤니티 : 시작부터 배포까지 알려주는 React
useEffect 두번 사용하는 것
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.두번사용한 이유가axios요청을하고 setPostInfo로 데이터를 저장했지만화면은 렌더링이 끝난 뒤니까 데이터는 가져왔지만화면에는 안띄워져서 useEffect를 두번 사용하신게 맞나요?? 첫번째 useEffect에서 서버 요청후에 만약 PostInfo 데이터가 변한다면 두번째 useEffect가 실행되어 렌더링되므로 데이터를 화면에 띄울 수 있게 되는 것이다.제가 이해한게 맞을까요?? 그렇다면 아래와 같이 처음 useEffect에 PostInfo를 중괄호에 넣으면 왜 안되는지 알 수 있을 까요??넣으니까 서버에 무한 요청만하고 데이터를 화면에 못띄우네요..ㅠㅠ 제가 이해를 잘한 건지 알려주시면 감사드리겠습니다!