묻고 답해요
130만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
jwt passport 질문입니다
2종류의 user테이블a_user, b_user 테이블을 가지고 있는데 각 유저테이블에 대해 jwt 검증을 나눠서 하고싶은데..아무리해도 안되는데 팁이 있을까요
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
cat repository에서의 오류
async existsByEmail(email: string): Promise<boolean> { try { const result = await this.catModel.exists({ email }); return result; } catch (error) { throw new HttpException('db error', 400); } } 에서 return result부분에서 오류가 발생합니다. src/cats/cats.repository.ts:20:7 - error TS2322: Type 'Pick<Document<Cat, any, any>, "_id">' is not assignable to type 'boolean'. 20 return result; ~~~~~~~~~~~~~~exists() 따라가 보면 리턴타입이 boolean이 아닌거 같은데 어떤 부분을 확인해 보면 좋을까요? console.log로 result를 찍어보면 { _id: new ObjectId~~~} 가 나옵니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
getServerSideProps 쿠키 이슈 관련해서 질문드립니다! 도와주세요ㅠ
안녕하세요, 제로초님 강의 잘 들었습니다. 강의를 듣고 혼자서 프로젝트를 진행하는데 너무 막히는점이 있어서 질문드립니다. 마이페이지에서 유저 정보를 불러오는 작업을 하고 있습니다. 토큰을 받아서 쿠키에 저장하여 사용하고 있는데.. 배포시에 저 말고 다른 사람도 저의 토큰으로 요청을 보내는 모습이 발견되었는데 해결을 못하고 있어요ㅠ 로초님 강의 후반부에서 보긴봤었는데 상황이 약간 다른거 같아서 질문드려요ㅠ saveCookies는 쿠키를 받아서 디스패치하여 스토어에 저장하는 함수이고 createCustomHeader는 Authorization : {`Bearer ${token}`}을 리턴하는 함수입니다. export const getServerSideProps = wrapper.getServerSideProps( (store) => async (context) => { saveCookies(store, context); const rootState: RootState = store.getState(); const user_id = rootState.loginState.user_id; axiosInstance.interceptors.request.use( async function (config) { try { config.headers = null; if (context.req && context.req.headers.cookie) { const allCookies = cookies(context); const accessToken = allCookies.accessToken; config.headers = createCustomHeader(accessToken); } return config; } catch (error) { console.log(error); } }, function (error) { return Promise.reject(error); }, ); store.dispatch(getUserInfoInMypageRequest(user_id)); store.dispatch(END); await store.sagaTask.toPromise(); return { props: {}, }; }, );
-
해결됨탄탄한 백엔드 NestJS, 기초부터 심화까지
PositiveIntPipe 생성할때 @Injectable() 사용한 이유
안녕하세요! 강의 잘듣고있습니다.!! PositiveIntPipe pipe를 만들때도 의존성 주입을 해야하기 때문에 @Injectable()를 사용하신건가요?? 만약 맞다면 사용한 이유가 궁금합니다.!!( @Injectable 를빼도 작동이 잘되고 파이프도 의존성을 주입해야하나? 라는 궁금증이 있어서 남깁니다!) 감사합니다!
-
미해결Nuxt.js 시작하기
모바일, 데스크탑을 SSR에서 구분하는 방법?
안녕하세요. 문제 어떤 UI가 모바일용과 데스크탑용의 구성이 너무 달라서 모바일용 컴포넌트 / 데스크탑용 컴포넌트로 개발되어있습니다. 이걸 미디어쿼리처럼 사용자의 device width에 따라 모바일/데스크탑용 UI를 각각 뿌려주고 싶지만, SSR에서는 javascript의 window 객체는 만들어져 있지 않기 때문에 접근은 불가능하므로 javascript로는 사용자 device의 width를 가져오는건 불가능에 가까운것 같고 대신에 request의user-agent를 이용해 사용자 device의 정보를 가져오면 판별은 할 수 있을꺼 같은데 미디어 쿼리같이 실시간으로 사용자 device의 width에 따라 UI를 나누는건 어려운 상황 질문 보통 모바일용과 데스크탑용 컴포넌트를 가지고 있을때 SSR에서는 어떻게 모바일과 데스크탑을 구분하시나요?
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
mongodb 연결 불가 문제
안녕하세요. .env에 MONGODB_URI를 못 읽어서 계속 mongodb connect 실패가 발생하고 있습니다. 혹시나 하여 app.modules.ts 에서 @Modules 앞 뒤로 MONGODB_URI를 콘솔로 찍어봤는데 @Modules 전에는 값이 안나오고 이후에는 잘 출력되는데요. 혹시 추가로 확인해야할 부분이 있을까요? 감사합니다.
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
ssr 개념 관한 질문
강의 진행하다가 앞부분 개념이 헷갈려 다시 듣고 왔는데 궁금한 점이 있어 여쭤 봅니다. ssr시 첫 번째 로딩만 브라우저 -> 프론트 서버 -> 벡엔드 -> 디비 이런식으로 전통적인 방법으로 데이터를 받아오고 이후에는 그냥 spa 방식으로 동작한다 라고 이해했는데 질문 1. 그럼 저희 프로젝트에서 saga에서 api 요청을 보내고 받아오는건 브라우저에서 벡엔드로 요청을 보내고 받아오는 것이라고 이해하는 게 맞는지 궁금합니다. 2. 저희 프로젝트에서 npm run dev 로 프론트 서버 실행후localhost:3060 이런 주소로 프론트 서버에 요청을 보내면 벡엔드에서 데이터 요청 후 데이터가 들어있는 정적 파일을받아 오는게 CSR 와의 차이라고 이해했는데 이 데이터를 받아온다는게 pages 폴더 안에 있는 index.js말하는 건가요? 아님 front 단에 작성된 모든 coponent들과 page들을 전부 들고와서 메모리에 캐싱 한다는 말인가요? 나름 구글링하고 찾아 봤는데 명쾌하게 이해되지가 않네요 혹시 강의 뒷부분에 다루어지는 내용이라면 죄송합니다.. 매번 감사드려요
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
파일을 클릭하면 제일 1번라인 import에 빨간줄 문의드립니다
안녕하세요 강사님 궁금하게 생겨 문의 드립니다. 다음 사진처럼 항상 파일을 클릭하면 상단 import줄이 저렇게 변하는데요 파일을 다시 닫으면 빨간줄이 사라집니다. 깃허브에 있는 tsconfig.json과 .eslintrc.js는 그대로 복붙했습니다. 제가놓친게있을까요?
-
해결됨탄탄한 백엔드 NestJS, 기초부터 심화까지
cats.module.ts에서 MongooseModule.forFeature 질문 드립니다
안녕하세요 강사님 cats.module.ts 파일에서 import: [MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }]),이부분에서 빨갛게 해논부분에 왜 Cat.name , name이 붙어있는 이유가 뭔지 알고싶습니다. 저기서 name에 할당한 값으로 Service에서 생성자 주입 받을때? Cat.name 으로 @InjectModel(Cat.name) 하는걸로 보이는데 왜 ".name" 이 붙은건가요?..
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
nodebird 강의 redux 대신 mobx 써서해보고있는데
이런 경고가뜨는데 왜 뜨는걸까요? RootStoreProvider 쪽에서 문제가 되는것같아서 찾아봤는데 RootStoreProvider에서 경고가 뜨는데 저게 찾아보니 스택오버플로에 hooks 쓸때 16버전 부터는 useEffect() 로 감싸라는것같은데 서버사이드렌더링은 클라이언트에서 화면그리기전에 먼저 store 세팅하는거라 어떻게 해야될까요?
-
미해결[리뉴얼] React로 NodeBird SNS 만들기
getStaticProps
11:37 에 getStaticProps 가 이벤트페이지 같은거에 적합하다 하셨는데 getStaticProps 라는게 npm build 를 하거나 npm run dev 를해서 서버를 키면 getStaticProps가 html 을 정적으로 만드는것까지 이해가 가는데 만약 이벤트 페이지를 변경하거나 삭제하고싶을때 이벤트 정보가 담긴 DB 는 삭제 또는 수정해도 이미 html 로 만들어놔서 그대로 보여주고 서버를 껏다 켜야지만 삭제 또는 수정한 내용이 적용되는건가요?
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
소셜 로그인 질문 (passport-apple)
저는 passport-jwt를 사용하여 로그인을 구현하였는데, 소셜 로그인을 위해 passport-apple 를 사용하여 유저 정보를 받아오려고하는데 막혔습니다. https://github.com/ananay/passport-apple/issues/30 nest.js에서 passport-apple사용시 Strategy 클래스의 validate함수에 인자가 제대로 들어오지않는 이슈가 있는것같습니다. 제가 유저정보를 받아온후 그것을 db에 저장후 jwt를 발행하여 다음요청부터는 애플을 거치지않고 제 서버로 jwt를 보내면 제가 유저정보 주는방식으로 구현하려고하는데, 유저 정보를 어떻게 받아와야하는지 모르겠습니다. 구글링해보니 idToken을 decode하면 유저정보가 들어있다고하는데, import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { PassportStrategy } from '@nestjs/passport'; import { DecodedIdToken, Strategy, VerifyCallback } from 'passport-apple'; @Injectable() export class AppleStrategy extends PassportStrategy(Strategy, 'apple') { constructor(private jwtService: JwtService) { super({ clientID: process.env.APPLE_CLIENT_ID, teamID: process.env.APPLE_TEAM_ID, callbackURL: process.env.APPLE_CALLBACK_URL, keyID: process.env.APPLE_KEY_ID, privateKeyString: process.env.APPLE_KEY.replace(/\\n/g, '\n'), passReqToCallback: false, }); } async validate( accessToken: string, idToken: string, profile: any, done: VerifyCallback, ) { const decodedIdToken: DecodedIdToken = this.jwtService.verify(idToken); console.log(decodedIdToken); const user = { provider: 'apple', snsId: decodedIdToken.sub, password: decodedIdToken.sub, }; console.log(user); done(null, user); } } 이렇게 코드를 짜니 [Nest] 31 - 02/06/2022, 8:41:18 AM ERROR [ExceptionsHandler] jwt malformed JsonWebTokenError: jwt malformed at Object.module.exports [as verify] (/usr/src/app/node_modules/jsonwebtoken/verify.js:63:17) at JwtService.verify (/usr/src/app/node_modules/@nestjs/jwt/dist/jwt.service.js:37:20) at AppleStrategy.validate (/usr/src/app/src/auth/strategies/apple.strategy.ts:25:60) at AppleStrategy.<anonymous> (/usr/src/app/node_modules/@nestjs/passport/dist/passport/passport.strategy.js:20:55) at Generator.next (<anonymous>) at /usr/src/app/node_modules/@nestjs/passport/dist/passport/passport.strategy.js:8:71 at new Promise (<anonymous>) at __awaiter (/usr/src/app/node_modules/@nestjs/passport/dist/passport/passport.strategy.js:4:12) at AppleStrategy.callback [as _verify] (/usr/src/app/node_modules/@nestjs/passport/dist/passport/passport.strategy.js:17:45) at /usr/src/app/node_modules/passport-oauth2/lib/strategy.js:205:24 이러한 에러가 뜹니다. idToken을 어떻게 해독해야하는지 알수있을까요..? 혹시 실무에서 애플로그인 서비스를 구현해보셨다면 이방법말고 다른방법이라도 있다면 알려주시면 감사하겠습니다!
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
안녕하세요 강사님 실행할때 오류가 발생해서 질문드립니다.
Error: Cannot find module 'app.model' Require stack: - D:\Study\Inflearn\NestJS\Express\letsStart\dist\app.js at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15) at Function.Module._load (node:internal/modules/cjs/loader:778:27) at Module.require (node:internal/modules/cjs/loader:1005:19) at require (node:internal/modules/cjs/helpers:102:18) at Object.<anonymous> (D:\Study\Inflearn\NestJS\Express\letsStart\dist\app.js:4:19) at Module._compile (node:internal/modules/cjs/loader:1101:14) at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10) at Module.load (node:internal/modules/cjs/loader:981:32) at Function.Module._load (node:internal/modules/cjs/loader:822:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) { code: 'MODULE_NOT_FOUND', requireStack: [ 'D:\\Study\\Inflearn\\NestJS\\Express\\letsStart\\dist\\app.js' ] } npm run start:dev 했을때 이와같은 오류가 발생하고 postman도 마찬가지로 localhost:8000으로 get 검색시 찾지 못하는데 어느 부분에서 잘못됬는지를 모르겠습니다. 코드는 오타 없이 강사님이 작성한것과 동일합니다.
-
미해결스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술
SSR관해서
HTML을 WAS에서 생성할때와 서버에서 생성할때의 차이가 무엇입니까?
-
미해결Nuxt.js 시작하기
서버사이드 렌더링에서 서버란 무엇인가요?!
안녕하세요!! 강의 정말 유용하고 잘봤습니다! 근데 SSR과 CSR의 개념적인 부분에서 CSR은 쉽게 얘기해서 제가사이트를 들어갔을때 최초로 서버에서 빈 HTML과 모든 JS, CSS파일등을 받아서 제 브라우저(?)에서 페이지를 그리는걸 의미한다라고 이해했고 SSR같은 경우는 최초 사이트 진입시 해당 페이지의 완성된 페이지를 서버에서 받아오고 다른페이지 시동시마다도 완성된페이지를 서버에서 받아온다라고 이해했습니다 근데 궁금한점이 SSR에서 서버란게 어떤서버를 의미하는건지 잘모르겠습니다. 웹서버나 WAS같은 서버를 의미하는걸까요?
-
해결됨탄탄한 백엔드 NestJS, 기초부터 심화까지
TypeORM 트랜잭션 질문드립니다.
안녕하세요 윤상석 강사님. TypeORM의 트랜잭션 관련으로 질문이 있습니다.TypeORM에서 트랜잭션을 다루는 방법은 이전 질문글에 답변해주셨듯이 크게 3가지인 것으로 알고 있습니다.1. QueryRunner2. getConnection, getManager3. @Transactional개인적으로는 @Transactional() 데코레이터를 사용하는것이 편리하여 사용하고 있었는데요.NestJS 공식문서에서 데코레이터를 이용하는 것을 권장하지 않고 있고,구글링을 해보아도 유닛 테스트에 어려움이 있어 권장하지 않는 분들이 많은 것을 알게되었습니다.여기서 첫 번째 질문이 있습니다.Q1. "유닛 테스트에 어려움이 있다"는 이야기가 구체적으로 무슨 의미인지 궁금합니다. 모킹이 어렵다. 내 마음대로 제어가 불가능하다 등의 이야기가 이해는 가는데 와닿지는 않아서 질문드립니다.----QueryRunner 방식을 이용하게 되면 try-catch-finally 코드가 강제가 되는 것 같아보입니다.여기서 두 번째 질문이 있습니다.Q2. 모든 에러를 감지하는 Filter를 적용해두었다면 QueryRunner를 사용하는 try-catch-finally 구문에서 에러 처리를 해버려 필터가 작동하지 않을 것 같은데 맞을까요?맞다면, 만약 Filter에서 요청이 실패하였을 때의 로깅기능이 있다고 가정한다면 로깅기능이 작동하지 않을 것 같습니다.그렇다면 catch 문에서 따로 Filter에서 구현한 로깅을 다시 구현해주어야 할까요?----트랜잭션 처리를 도와주는 typeorm-transactional-cls-hooked 패키지를 발견하였습니다.여기서 마지막 세 번째 질문이 있습니다.Q3. 해당 패키지는 유닛 테스를 할 때 트랜잭션 기능이 작동하지 않도록 모킹기능도 제공해주는 것 같습니다. QuerryRunner 보다 안 좋은점이 있다면 어떤 점이 있을까요?----질문이 너무 많은 것 같아 죄송합니다.좋은 강의와 답변해주셔서 항상 감사합니다.
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
useFactory 사용시 resource 파일 분리 후 di 방법
먼저 너무 좋은 ADMIN 대시보드 라이브러리를 알려주셔서 감사합니다. 해당 라이브러리로 몇가지 테스트해보는 중인데 adminJsOptions가 너무 길어져 resources를 별도의 파일로 분리하려 하고 있습니다. 하다보니 이미 factory로 blogModel을 inject하고 있는데 resources를 별도의 파일로 분리하려고 보니 blogModel을 분리한 파일에서 어떻게 가져와야할지 막막하더라구요. adminjs뿐아니라 다른 모듈을 사용할때도 useFactory를 통해 di를 해주는경우가 많은데 이럴때는 분리된 파일에는 어떻게 의존성을 주입해줘야 할까요??? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 import * as AdminJSMongoose from '@adminjs/mongoose'; import { AdminModule as AdminBroModule } from '@adminjs/nestjs'; import { Module } from '@nestjs/common'; import { getModelToken } from '@nestjs/mongoose'; import AdminJS from 'adminjs'; import { Model } from 'mongoose'; import { UsersModule } from 'src/users/users.module'; import { User } from 'src/users/users.schema'; import { after, before } from './hooks/users.hooks'; AdminJS.registerAdapter(AdminJSMongoose); @Module({ imports: [ AdminBroModule.createAdminAsync({ imports: [UsersModule], inject: [getModelToken(User.name)], useFactory: (usersModel: Model<User>) => ({ adminJsOptions: { rootPath: '/admin', resources: [ { resource: usersModel, options: { navigation: { name: null, icon: '', }, properties: { email: { isTitle: false, position: 1, }, name: { isTitle: true, }, _id: { isVisible: { list: false }, }, password: { isVisible: false, }, }, actions: { edit: { isAccessible: isAdmin }, delete: { isAccessible: isAdmin }, new: { isAccessible: isAdmin }, show: { isAccessible: isAdmin, }, list: { isAccessible: isAdmin }, custom_delete: { actionType: 'record', icon: 'TrashCan', guard: 'doYouReallyWantToDoThis', variant: 'danger', before: before, after: after, handler: async (request, response, context) => { const user = context.record; await usersModel.findOne({_id: request.params.id}); console.log('custom handler!!!'); return { record: user.toJSON(context.currentAdmin), }; } }, }, }, }, ], }, auth: { authenticate: async (email, password) => { const user = await usersModel.findOne({ email: email }); if (user) { const matched = user.password === password; if (matched && user.role === 'ADMIN') { return user.readOnlyData; } } return null; }, cookieName: 'cookie-test', cookiePassword: 'test', }, }), }), ], }) export class AdminModule {} const isAdmin = ({ currentAdmin }) => currentAdmin && currentAdmin.role === 'ADMIN'; resource 코드 분리 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import * as AdminJSMongoose from '@adminjs/mongoose'; import { AdminModule as AdminBroModule } from '@adminjs/nestjs'; import { Module } from '@nestjs/common'; import { getModelToken } from '@nestjs/mongoose'; import AdminJS from 'adminjs'; import { Model } from 'mongoose'; import { UsersModule } from 'src/users/users.module'; import { User } from 'src/users/users.schema'; import { after, before } from './hooks/users.hooks'; import { userResource } from './resources/users.resource'; AdminJS.registerAdapter(AdminJSMongoose); @Module({ imports: [ AdminBroModule.createAdminAsync({ imports: [UsersModule], inject: [getModelToken(User.name)], useFactory: (usersModel: Model<User>) => ({ adminJsOptions: { rootPath: '/admin', resources: [ userResource ], }, auth: { authenticate: async (email, password) => { const user = await usersModel.findOne({ email: email }); if (user) { const matched = user.password === password; if (matched && user.role === 'ADMIN') { return user.readOnlyData; } } return null; }, cookieName: 'cookie-test', cookiePassword: 'test', }, }), }), ], }) export class AdminModule {} const isAdmin = ({ currentAdmin }) => currentAdmin && currentAdmin.role === 'ADMIN'; 함수로 만들어 파라미터로 넘겨주는 방법밖에 없을까요???
-
해결됨[리뉴얼] React로 NodeBird SNS 만들기
질문드립니다 getServerSideProps or getInitialProps
만약 모든 페이지에서 사용자 정보가 SSR되어야한다면 getServerSideProps보다 getInitalProps로 _app에다가 설정하는게 나을꺼 같은데 혹시 이 방법 말고 더 좋은 방법이 있을까 여쭤봅니다. getInitalProps는 레거시 코드니까 사용하지 않는 게 좋다는데 이거 말고 대안을 못 찾겠네요.ㅠㅠ 1. _app -> getInitalProps MyApp.getInitialProps = wrapper.getInitialAppProps( (store) => async ({ Component, ctx }) => { store.dispatch( setCredentials({ user: await axios .get("http://localhost:4000/users/me", { withCredentials: true, headers: { cookie: ctx.req?.headers.cookie || "", }, }) .then((response) => response.data) .catch(() => null), }) ); return { pageProps: { ...(Component.getInitialProps ? await Component.getInitialProps({ ...ctx, store }) : {}), pathname: ctx.pathname, }, }; } ); 2. pages -> getServerSideProps wrapper.getServerSideProps((store) => async (context) => { store.dispatch( setCredentials({ user: await axios .get("http://localhost:4000/users/me", { withCredentials: true, headers: { cookie: context.req.headers.cookie || "", }, }) .then((response) => response.data) .catch(() => null), }) ); return {props: {}}; });
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
next()가 없는 (불필요한) 경우에도, middleware 함수라고 볼 수 있나요??
안녕하세요~ 좋은 강의 넘 잘 듣고 있습니다~ 감사합니다 :) 이 에러 핸들링을 하는 함수를 middleware라고 언급해주셨는데, 저는 조금 헷갈려서 질문 드립니다! app.use((req: express.Request, res: express.Response, next: express.NextFunction) => { console.log('this is error middleware'); res.send({ error: '404 not found error' }); }); 미들웨어는 하려는 일의 중간에서, 개발자가 원하는 무언가를 할 수 있도록 하는 것을 말하는거니까 express 에서는 next() 가 있는 경우를 middleware 로 본다 는 것으로 이해했습니다. 그런데 위 코드는 다음으로 넘기는 것 없이 바로 404를 반환해서, next가 꼭 필요하지 않은 것 같은데... 이 경우에도 이 함수를 middleware 함수라고 볼 수 있나요~?
-
미해결탄탄한 백엔드 NestJS, 기초부터 심화까지
http-exception.filter.ts 파일 내용 중 질문이 있습니다.
const error = exception.getResponse() as | string | { error: string; statusCode: number; message: string | string[] }; 강의 중에 http-exception.filter.ts 파일에서 error 변수를 위와 같이 할당을 하는데 as | string 뒤에 있는 타입을 { error: string, statusCode: number; message: string | string[] };로 받는데 객체의 속성들을 특별하게 넣어준 이유가 있나요? 단순히 { error: string }로만 처리해도 다른 속성 값들이 자동으로 들어오기 때문에 출력할 때는 문제가 없는 것 같은데 정확한 타입을 제공하기 위해서 라던가 등 특별한 이유가 있는지 궁금합니다.