graphql 접속문제
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
https://practice.codebootcamp.co.kr/graphql 는 사용을 못하는데 그럼 과제 연습은 못하나요?
- javascript
- node.js
- express
- docker
- tdd
- rest-api
- nestjs
173만명의 커뮤니티!! 함께 토론해봐요.
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
https://practice.codebootcamp.co.kr/graphql 는 사용을 못하는데 그럼 과제 연습은 못하나요?
해결됨
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
강의 내용 처럼 postman에서 에러 응답을 받고 싶습니다. 하지만 제대로된 에러 응답을 받지 못하고 있습니다. 시도한 내용 첫번째로, postman에서 post || localhost:3095/api/users 로 body에 아무런 내용없이 send를 보냈습니다. 이후, 에러 응답으로 "msg":"이메일이 없네요." 라는 답을 받았습니다. (성공) body에 이메일을 작성 하여 send를 보냈지만, "msg":"이메일이 없네요." 라는 이전과 동일한 에러응답 을 받았습니다. (실패) 이메일을 send해도 "이메일이 없네요"라는 에러응답으로 답을 받고, 다른 것으로 send를 해도 "이메일이 없네요"라고 에러응답을 받았습니다. 이를 해결하려고 하는데, 참고할만한 코드가 있을까요? users.service.ts @Injectable() export class UsersService { constructor( @InjectRepository(Users) private usersRepository: Repository<Users>, ) {} getUser() {} async join(email: string, nickname: string, password: string) { if (!email) { throw new HttpException('이메일이 없네요', 400); } if (!nickname) { throw new HttpException('닉네임이 없네요', 400); } if (!password) { throw new HttpException('비밀번호가 없네요.', 400); } const user = await this.usersRepository.findOne({ where: { email }}); if (user) { throw new HttpException('등록된 사용자입니다.', 401); } const hashedPassword = await bcrypt.hash(password, 12); await this.usersRepository.save({ email, nickname, password: hashedPassword, }); } } [계속 아래 이미지와 같은 에러 응답만 출력됩니다] 또한, 깃헙에서 코드를 참고하려했으나 아래와 같이 진도가 달라서 어려웠습니다. ㅠㅠ 제로 초님께서 작성해주신 users.service.ts 는 아래 코드 입니다. 완성된 users.service.ts 내용인 것 같아서, 본 강의에 참고하기 어려워서 질문드렸습니다. @Injectable() export class UsersService { constructor( @InjectRepository(Users) private usersRepository: Repository<Users>, @InjectRepository(WorkspaceMembers) private workspaceMembersRepository: Repository<WorkspaceMembers>, @InjectRepository(ChannelMembers) private channelMembersRepository: Repository<ChannelMembers>, private dataSource: DataSource, ) {} async findByEmail(email: string) { return this.usersRepository.findOne({ where: { email }, select: ['id', 'email', 'password'], }); } async join(email: string, nickname: string, password: string) { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); const user = await queryRunner.manager .getRepository(Users) .findOne({ where: { email } }); if (user) { throw new ForbiddenException('이미 존재하는 사용자입니다'); } const hashedPassword = await bcrypt.hash(password, 12); try { const returned = await queryRunner.manager.getRepository(Users).save({ email, nickname, password: hashedPassword, }); const workspaceMember = queryRunner.manager .getRepository(WorkspaceMembers) .create(); workspaceMember.UserId = returned.id; workspaceMember.WorkspaceId = 1; await queryRunner.manager .getRepository(WorkspaceMembers) .save(workspaceMember); await queryRunner.manager.getRepository(ChannelMembers).save({ UserId: returned.id, ChannelId: 1, }); await queryRunner.commitTransaction(); return true; } catch (error) { console.error(error); await queryRunner.rollbackTransaction(); throw error; } finally { await queryRunner.release(); } } } 해결방법이 있을까요:?
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
docker로 mysql server를 띄우고 product_category table 에서 전자제품, uuid 를 등록하고 save를 누르면 한글을 인식할 수 없다는 에러가 납니다. 해결방법이 궁금합니다. ERROR [ExceptionsHandler] Incorrect string value: '\xEC\xA3\xBC\xEC\x86\x8C' for column 'address' at row 1
미해결
탄탄한 백엔드 NestJS, 기초부터 심화까지
안녕하세요 다음 연재 강의 주제는 소셜 인증이라고 강의 노트에 써있으나 저에게 프로젝트3는 이번 강의가 마지막인데 맞나요??
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
안녕하세요. 강사님. "Nest.js - GraphQL 연결" 강의중 제목과 같이 resolver에서 service의 메서드로 접근을 못하는듯 합니다. 그럼 인젝트가 안된거 아닌가요? 해결책 문의 드립니다. "yarn start:dev" 오류없이 실행은 됩니다. app.module.ts, boards.module.ts, boards.resolver.ts boards.service.ts 코드 입니다. // app.module.ts import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { BoardModule } from './apis/boards/boards.module'; @Module({ imports: [ BoardModule, GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: 'src/commons/graphql/schema.gql', }), ], // controllers: [AppController], // providers: [AppService], }) export class AppModule {} // boards.module.ts import { Module } from '@nestjs/common'; import { BoardResolver } from './boards.resolver'; import { BoardService } from './boards.service'; @Module({ // imports: [], // controllers: [], providers: [BoardResolver, BoardService], }) export class BoardModule {} // boards.resolver.ts import { Query, Resolver } from '@nestjs/graphql'; import { BoardService } from './boards.service'; @Resolver() export class BoardResolver { constructor(private readonly boardService: BoardService) {} @Query(() => String) getString(): string { return this.boardService.serviceString(); } @Query(() => Number) getNumber(): number { return this.boardService.serviceNumber(); } @Query(() => Boolean) getOnlyResolver(): boolean { return true; } } // boards.service.ts import { Injectable } from '@nestjs/common'; @Injectable() export class BoardService { serviceString() { return 'Hello World!'; } serviceNumber(): number { return 100; } } "getOnlyResolver" 쿼리는 정상적입니다. Service까지 가지 않도록 테스트 했습니다. "getString" 쿼리는 Service의 "serviceString()" 메서드로 접근 합니다. (오류 발생) "getNumber" 쿼리는 Service의 "serviceNumber()" 메서드로 접근 합니다. (오류 발생) "package.json" 정보 입니다. { "name": "aaa", "version": "0.0.1", "description": "", "author": "", "private": true, "license": "UNLICENSED", "scripts": { "build": "nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "nest start", "start:dev": "nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "jest", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { "@apollo/server": "^4.5.0", "@nestjs/apollo": "^11.0.4", "@nestjs/common": "^9.0.0", "@nestjs/core": "^9.0.0", "@nestjs/graphql": "^11.0.4", "@nestjs/platform-express": "^9.0.0", "graphql": "^16.6.0", "reflect-metadata": "^0.1.13", "rxjs": "^7.2.0" }, "devDependencies": { "@nestjs/cli": "^9.0.0", "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", "@types/express": "^4.17.13", "@types/jest": "29.2.4", "@types/node": "18.11.18", "@types/supertest": "^2.0.11", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^8.0.1", "eslint-config-prettier": "^8.3.0", "eslint-plugin-prettier": "^4.0.0", "jest": "29.3.1", "prettier": "^2.3.2", "source-map-support": "^0.5.20", "supertest": "^6.1.3", "ts-jest": "29.0.3", "ts-loader": "^9.2.3", "ts-node": "^10.0.0", "tsconfig-paths": "4.1.1", "typescript": "^4.7.4" } } 감사합니다.
미해결
탄탄한 백엔드 NestJS, 기초부터 심화까지
prettier 관련 질문도 드려도 되는지 모르겠지만 일단 강의에서 나와서 질문드립니다! :) 프리티어 세팅을 하고 작업을 하다가 const test = { a: "test", b: "test2", }; 위와 같은 코드를 써봤는데요. 이 때, 제가 원하는건 a속성과 b속성의 간격이 없는 즉, 의미없는 빈 줄이 없도록 하는 것입니다. 하지만, 이걸 prettier로 자동으로 하는 속성 값을 인터넷으로 찾아봐도(사실 검색할 단어를 잘 못찾은 것 같습니다) 없어서요.. 질문에 올립니다! 감사합니다!
해결됨
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
총 2개의 질문사항이 있습니다. GitHub에서 제공해주신 front폴더를 사용하려고 하는데, 아래와 같이 이름이 다른 front 폴더가 총 3개가 있습니다. front-js front-rq front 질문1. 이 중, 어떤 폴더에서 npm run dev의 명령어를 입력해야 하는 것인지 궁금합니다. 질문2. front이름 뒤에 붙어있는 js와 rq의 의미가 궁금합니다!
미해결
탄탄한 백엔드 NestJS, 기초부터 심화까지
안녕하세요. nestjs boilerplate강의 관련해서 마지막에 users.service.spec.ts의 테스트 코드들에 대하여 설명을 해주신다고 하였는데 제가 찾지 못한건지 아니면 추후에 강의가 올라오는지 궁금합니다 .
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
검색을 나름대로 열심히 해봤는데 잘 해결이 되지 않아서 질문 남깁니다. error: /app/node_modules/bcrypt/lib/binding/napi-v3/bcrypt_lib.node: invalid elf header 에러 메시지는 이렇습니다. bcrypt가 설치되는 OS에 따라 버전이 달라서 그렇다는거 같은데, Dockerfile에 bcrypt 삭제했다가 설치하는 명령어도 넣어봤는데 잘 안되네요ㅠ
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
강사님 안녕하세요? 토큰 재발급 API 실습 중에 해결되지 않는 부분이 있어 질문드려요. 관련 코드는 강의에서 진행하는 대로 모두 작성하였고, 마지막 실습 부분에서 막힙니다. login 과 fetchUser 까지는 진행이 잘 되는데 문제는, restoreAccessToken 부분에서 401 에러가 발생합니다. 관련 에러 명령 프롬프트 화면입니다. 관련 쿠키 값 입니다. (login 시도 시 쿠키 값) 코드는 실습대로 다 작성했구요. 실습도 그대로 따라하는 중 restoreAccessToken 부분만 에러가 나네요. auth.resolver.ts 의 @UseGuards 데코레이터를 빼보기도 하고, 제 나름대로 해결책을 찾아보려 했는데 잘 모르겠네요. 구글링 해봐도 안되고, 혹시 제가 빼먹은 부분이나 잘못한 부분이 있을까요? 도움 부탁드립니다 :) 아래는 관련 제 코드들 입니다. auth.module.ts import { Module } from '@nestjs/common'; import { AuthResolver } from './auth.resolver'; import { AuthService } from './auth.service'; import { JwtModule } from '@nestjs/jwt'; import { UserService } from '../users/user.service'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from '../users/entities/user.entity'; import { JwtRefreshStrategy } from 'src/commons/auth/jwt-refresh.strategy'; @Module({ imports: [ JwtModule.register({}), // TypeOrmModule.forFeature([User]), ], providers: [ JwtRefreshStrategy, // AuthResolver, AuthService, UserService, ], }) export class AuthModule {} auth.resolver.ts import { UnprocessableEntityException, UseGuards } from '@nestjs/common'; import { Args, Context, Mutation, Resolver } from '@nestjs/graphql'; import { UserService } from '../users/user.service'; import * as bcrypt from 'bcrypt'; import { AuthService } from './auth.service'; import { GqlAuthRefreshGuard } from 'src/commons/auth/gql-auth.guard'; import { CurrentUser } from 'src/commons/auth/gql-user.param'; @Resolver() export class AuthResolver { constructor( private readonly userService: UserService, // private readonly authService: AuthService, ) {} @Mutation(() => String) async login( @Args('email') email: string, // @Args('password') password: string, @Context() context: any, ) { // 1. 로그인(이메일이 일치하는 유저를 DB에서 찾기) const user = await this.userService.findOne({ email }); // 2. 일치하는 이메일이 없으면 -> 에러 던지기!! if (!user) throw new UnprocessableEntityException('이메일이 없습니다.'); // 3. 일치하는 이메일이 있지만, 비밀번호가 틀렸다면 -> 에러 던지기!! const isAuth = await bcrypt.compare(password, user.password); if (!isAuth) throw new UnprocessableEntityException('암호가 틀렸습니다.'); // 4. refreshToken(=JWT)을 만들어서 프론트엔드(쿠키)에 보내주기 this.authService.setRefreshToken({ user, res: context.res }); // 5. 이메일과 비밀번호 모두 일치한다면 -> accessToken(=JWT)을 만들어서 브라우저에 전달하기 return this.authService.getAccessToken({ user }); } @UseGuards(GqlAuthRefreshGuard) @Mutation(() => String) restoreAccessToken( @CurrentUser() currentUser: any, // ) { return this.authService.getAccessToken({ user: currentUser }); } } auth.service.ts import { Injectable } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; @Injectable() export class AuthService { constructor( private readonly jwtService: JwtService, // ) {} setRefreshToken({ user, res }) { const refreshToken = this.jwtService.sign( { email: user.email, sub: user.id }, { secret: 'myRefreshKey', expiresIn: '2w' }, ); // 개발 환경 res.setHeader('Set-Cookie', `refreshToken=${refreshToken}`); // 배포 환경 // res.setHeader('Access-Control-Allow-Origin', 'https://myfrontsite.com') // res.setHeader( // 'Set-Cookie', // `refreshToken=${refreshToken}; path=/; domain=.mybacksite.com; SameSite=None; Secure; httpOnly;` // ) } getAccessToken({ user }) { return this.jwtService.sign( { email: user.email, sub: user.id }, { secret: 'myAccessKey', expiresIn: '30s' }, ); } } jwt-refresh.strategy.ts import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-jwt'; export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'refresh') { constructor() { super({ jwtFromRequest: (req) => { const cookie = req.headers.cookie; const refreshToken = cookie.replace('refreshToken=', ''); return refreshToken; }, secretOrKey: 'myRefreshKey', }); } validate(payload) { console.log(payload); // { email: c@c.com, sub: qkwefuasdij-012093sd } return { email: payload.email, id: payload.sub, }; } } gql-auth-guard.ts import { ExecutionContext } from '@nestjs/common'; import { GqlExecutionContext } from '@nestjs/graphql'; import { AuthGuard } from '@nestjs/passport'; export class GqlAuthAccessGuard extends AuthGuard('access') { getRequest(context: ExecutionContext) { const ctx = GqlExecutionContext.create(context); return ctx.getContext().req; } } export class GqlAuthRefreshGuard extends AuthGuard('refresh') { getRequest(context: ExecutionContext) { const ctx = GqlExecutionContext.create(context); return ctx.getContext().req; } } app.module.ts import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthModule } from './apis/auth/auth.module'; import { BoardModule } from './apis/boards/boards.module'; import { ProductModule } from './apis/products/product.module'; import { ProductCategoryModule } from './apis/productCategory/productCategory.module'; import { UserModule } from './apis/users/user.module'; // import { AppController } from './app.controller'; // import { AppService } from './app.service'; @Module({ imports: [ AuthModule, BoardModule, ProductModule, ProductCategoryModule, UserModule, GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: 'src/commons/graphql/schema.gql', context: ({ req, res }) => ({ req, res }), }), TypeOrmModule.forRoot({ type: 'mysql', // 데이터 베이스 타입 host: 'localhost', // local 환경으로 진행 port: 3306, // mysql은 기본 port는 3306 username: 'root', // mysql은 기본 user는 root로 지정 password: 'bada332@', // 본인의 mysql password database: 'myproject03', // 연결할 데이터 베이스명 entities: [__dirname + '/apis/**/*.entity.*'], // 데이터 베이스와 연결할 entity synchronize: true, // entity 테이블을 데이터베이스와 동기화할 것인지 logging: true, // 콘솔 창에 log를 표시할 것인지 }), ], // controllers: [AppController], // providers: [AppService], }) export class AppModule {}
미해결
탄탄한 백엔드 NestJS, 기초부터 심화까지
nest.js로 개인 프로젝트 진행 중에 갑자기 문뜩 든 생각인데요. 빨간 색으로 동그라미 친 디렉토리는 뭐라고 불러야 하는 지 궁금합니다. 해당 디렉토리에는 API가 구현되어있는데, 도메인이라고 불러야 하는지 모듈이라고 불러야 하는지, 아니면 그냥 디렉토리인지 답변 해주실 수 있을까욥?
해결됨
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
DB연결 문제라고 해서, 처음부터 새로 생성해서 시도해보았으나, 에러 코드가 동일하게 출력됩니다ㅠㅠ ============================== 시도해본 것들 mysql 비밀번호 초기화, 재설정 mysql db 다시 세팅 dotenv 코드말고 직접 데이터베이스 정보 입력후 실행 터미널 메세지 base) C:\Users\user\Downloads\master\a-nest> npm run db:create > a-nest@0.0.1 db:create > ts-node ./node_modules/typeorm-extension/dist/cli/index.js db:create -d ./dataSource.ts C:\Users\user\Downloads\master\a-nest\node_modules\mysql2\lib\packets\packet.js:728 const err = new Error(message); ^ Error: Access denied for user ''@'localhost' (using password: YES) at Packet.asError (C:\Users\user\Downloads\master\a-nest\node_modules\mysql2\lib\packets\packet.js:728:17) at ClientHandshake.execute (C:\Users\user\Downloads\master\a-nest\node_modules\mysql2\lib\commands\command.js:29:26) at Connection.handlePacket (C:\Users\user\Downloads\master\a-nest\node_modules\mysql2\lib\connection.js:489:32) at PacketParser.onPacket (C:\Users\user\Downloads\master\a-nest\node_modules\mysql2\lib\connection.js:94:12) at PacketParser.executeStart (C:\Users\user\Downloads\master\a-nest\node_modules\mysql2\lib\packet_parser.js:75:16) at Socket.<anonymous> (C:\Users\user\Downloads\master\a-nest\node_modules\mysql2\lib\connection.js:101:25) at Socket.emit (node:events:513:28) at Socket.emit (node:domain:489:12) at addChunk (node:internal/streams/readable:324:12) at readableAddChunk (node:internal/streams/readable:297:9) { code: 'ER_ACCESS_DENIED_ERROR', errno: 1045, sqlState: '28000', sqlMessage: "Access denied for user ''@'localhost' (using password: YES)", sql: undefined. } app.module.ts @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3307, autoLoadEntities: true, entities: [ ChannelChats, ChannelMembers, Channels, DMs, Mentions, Users, WorkspaceMembers, Workspaces, ], keepConnectionAlive: true, migrations: [__dirname + '/migrations/*.ts'], charset: 'utf8mb4_general_ci', synchronize: false, logging: true, }), TypeOrmModule.forFeature([Users]), UsersModule, WorkspacesModule, ChannelsModule, ], controllers: [AppController], providers: [AppService], }) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer): void { consumer.apply(LoggerMiddleware).forRoutes('*'); // consumer.apply(FrontendMiddleware).forRoutes({ // path: '/**', // method: RequestMethod.ALL, // }); } }
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
postman에서 post요청 후 get으로 확인했을 때 잘 받아와집니다. MongoDB Compass에 localhost :27017로 연결해서 refresh해도 mydocker DB가 조회가 안됩니다. docker - mongodb가 연결이 잘 안된건지 어렵습니다.. 어떻게 확인할 수 있을까요? import express from 'express' import { checkValidationPhone, getToken, sendTokenToSMS } from './phone.js'; import swaggerUi from 'swagger-ui-express' import swaggerJSDoc from 'swagger-jsdoc' import { options } from './swagger/config.js' import cors from 'cors' import { checkValidationEmail, getWelcomeTemplate, sendWelcomeTemplateToEmail } from './email.js'; import mongoose from 'mongoose' import { Board } from './models/board.model.js' const app = express() app.use(cors()) app.use(express.json()); app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerJSDoc(options))); app.get('/boards', async (req, res) => { // const result = [ // { // number: 1, // writer: '철수', // title: '제목입니다~~~', // contents: '내용이에요@@@', // }, // { // number: 2, // writer: '영희', // title: '영희 제목입니다~~~', // contents: '영희 내용이에요@@@', // }, // { // number: 3, // writer: '훈이', // title: '훈이 제목입니다~~~', // contents: '훈이 내용이에요@@@' // }, // ]; const result = await Board.find() //DB접속해서 가져오는 내용 위랑동일 res.send(result) }) app.post('/boards', async (req, res) => { console.log(req.body); // 1. 데이터를 등록하는 로직 => DB에 접속해서 데이터 저장하기 const board = new Board({ writer: req.body.writer, title: req.body.title, contents: req.body.contents, }); await board.save(); //원래는 SQL문법을 써야하지만 mongoose가 자동으로 변환해줌.(ORM, ODM) // 2. 저장 결과 응답 주기 res.send("게시물 등록에 성공하였습니다."); }); app.post('/tokens/phone', (req, res) => { const myphone = req.body.myphone; const isValid = checkValidationPhone(myphone); if (isValid) { const mytoken = getToken(); sendTokenToSMS(myphone, mytoken); res.send('인증완료!!!'); } }); app.post("/users", (req, res) => { const user = req.body.myuser const isValid = checkValidationEmail(user.email) if(isValid){ const mytemplate = getWelcomeTemplate(user) sendWelcomeTemplateToEmail(user.email, mytemplate) res.send("가입완료!") } }) //몽고DB 접속 mongoose.connect("mongodb://my-database:27017/mydocker") // localhost로 접속하게되면 express 도커안에서의 localhost이기때문에 dockercompose로 묶인 my-database-1 컴퓨터로 들어가야함. // 단, dockercompose로 묶어뒀기 때문에 이름만 입력해서 진입가능(네임리졸루션). // Backend API 서버 오픈 app.listen(3000, () => console.log(`exemple app listening on port ${3000}`))
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
노션 자료 중 결제 - FrontEnd 적용 페이지 안에 있는 링크가 Page not found 로 나옵니다.
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
노션에 써있는걸로 sudo systemctl start mongod 실행하면 실행이 안되서 공식문서에서 찾아보니 sudo service mongod start 를 입력하면 starting database mongod 라고 뜬 후 fail이 뜹니다.... localhost:27017 로 접속을 하면 잘 뜨긴 하는데 해결 방법이 없을까요 ??
해결됨
탄탄한 백엔드 NestJS, 기초부터 심화까지
이거는 nodemon이랑 비슷한 개념인가요?..
해결됨
탄탄한 백엔드 NestJS, 기초부터 심화까지
imports: [MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])], 에서 왜 name, schema 를 가지는 객체가 필요한지 궁금해서 타고 들어가봤더니, ModelDefinition 타입이더라구요. 패키지에 도큐먼트 작성된 것이 없어서 npm mongoose , nestjs/mongoose 둘 다 찾아봤는데 mongoose 도큐먼트에는 따로 없었고, nestjs/mongoose 는 도큐먼트가 아예 안보이더라구요.. API 가 궁금할 때에는 어떤 방법으로 찾아 볼 수 있을까요..?
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
docker run 이미지ID 하고 새로운 터미널 열어서 docker ps 로 containerID 확인 후 docker exec -it 명령어 사용해서 도커 내부로 들어가려고 하면 OCI runtime exec failed: exec failed: unable to start container process: exec: "C:/Program Files/Git/usr/bin/bash": stat C:/Program Files/Git/usr/bin/bash: no such file or directory: unknown 이런식으로 오류가 뜹니다 왜 그런건가요?? 해결 방법 알려주세요!
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
products.service.ts에서 create 부근에 tag를 저장 하기 전 tag를 미리 조회하는 부분을 구현 중인데 findOne에서 {name: tagname} 을 구현하려고 할 때 다음과 같은 에러가 발생합니다. save에서는 에러가 발생하지 않는데 findOne 조회 부분만 에러가 발생하네요 관련된 코드 같이 보내드립니다. createProduct.input.ts import { InputType, Field, Int } from '@nestjs/graphql'; import { Min } from 'class-validator'; import { ProductSaleslocationInput } from 'src/apis/productsSaleslocation/entities/dto/productSaleslocation.input'; @InputType() export class CreateProductInput { @Field(() => String) name: string; @Field(() => String) description: string; @Min(0) @Field(() => Int) price: number; @Field(() => ProductSaleslocationInput) productSaleslocation: ProductSaleslocationInput; @Field(() => String) productCategotyId: string; @Field(() => [String]) productTags: string[]; } products.entity.ts import { Field, Int, ObjectType } from '@nestjs/graphql'; import { ProductCategory } from 'src/apis/productsCategory/entities/productsCategory.entity'; import { ProductTag } from 'src/apis/productsTags/productTags.entity'; import { User } from 'src/apis/users/users.entity'; import { Column, DeleteDateColumn, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, OneToOne, PrimaryGeneratedColumn, } from 'typeorm'; import { ProductSaleslocation } from '../../productsSaleslocation/entities/productsSaleslocation.entity'; @Entity() @ObjectType() export class Product { @PrimaryGeneratedColumn('uuid') @Field(() => String) id: string; @Field(() => String) @Column() name: string; @Field(() => String) @Column() description: string; @Field(() => Int) @Column() price: number; @Field(() => Boolean) @Column({ default: false }) isSoldout: boolean; @DeleteDateColumn() deletedAt: Date; @Field(() => ProductSaleslocation) @JoinColumn() @OneToOne(() => ProductSaleslocation) productSaleslocation: ProductSaleslocation; @Field(() => ProductCategory) @ManyToOne(() => ProductCategory) productCategory: ProductCategory; @Field(() => User) @ManyToOne(() => User) user: User; @JoinTable() @ManyToMany(() => ProductTag, (productTags) => productTags.products) @Field(() => [ProductTag]) productTags: ProductTag[]; } productTags.entity.ts import { Field, ObjectType } from '@nestjs/graphql'; import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm'; import { Product } from '../products/entities/products.entity'; @Entity() @ObjectType() export class ProductTag { @Field(() => String) @PrimaryGeneratedColumn('uuid') id: string; @Column() @Field(() => String) name: string; @Field(() => [Product]) @ManyToMany(() => Product, (products) => products.productTags) products: Product[]; } products.service.ts import { Product } from './entities/products.entity'; import { Injectable, UnprocessableEntityException } from '@nestjs/common'; import { Repository } from 'typeorm'; import { InjectRepository } from '@nestjs/typeorm'; import { ProductSaleslocation } from '../productsSaleslocation/entities/productsSaleslocation.entity'; import { ProductTag } from '../productsTags/productTags.entity'; @Injectable() export class ProductService { constructor( @InjectRepository(Product) private readonly productRepository: Repository<Product>, @InjectRepository(ProductSaleslocation) private readonly productSaleslocationRepository: Repository<ProductSaleslocation>, @InjectRepository(ProductTag) private readonly productTagRepository: Repository<ProductTag>, ) {} async findAll() { return await this.productRepository.find({ relations: ['productSaleslocation', 'productCategory', 'productTags'], }); } async findOne({ productId }) { return await this.productRepository.findOne({ where: { id: productId }, relations: ['productSaleslocation', 'productCategory', 'productTags'], }); } async create({ createProductInput }) { // 1. 상품만 등록하는 경우 // const result = await this.productRepository.save({ // ...createProductInput, // // 하나 하나 직접 나열하는 방식 // // name: createProductInput.name, // // description: createProductInput.description, // // price: createProductInput.price, // }); // 2. 상품과 상품거래 위치 같이 등록 const { productSaleslocation, productCategotyId, productTag, ...product } = createProductInput; const result = await this.productSaleslocationRepository.save({ ...productSaleslocation, }); // productTag // ["#electronics, #computer"] const result2 = []; // [{name: ..., id: ...}] for (let i = 0; i < productTags.length; i++) { const tagName = productTags[i].replace('#', ''); // check the tags that has already registered const checkTag = await this.productTagRepository.findOne({ name: tagName, }); // if the tags has been existed if (checkTag) { result2.push(checkTag); // if the tags hasn't been existed } else { const newTag = await this.productTagRepository.save({ name: tagName }); result2.push(newTag); } } const result3 = await this.productRepository.save({ ...product, productSaleslocation: result, // result 통째로 넣기 vs id만 넣기 productCategory: { id: productCategotyId }, productTags: result2, }); return result3; } async update({ productId, updateProductInput }) { const myProduct = await this.productRepository.findOne({ where: { id: productId }, }); const newProduct = { ...myProduct, id: productId, ...updateProductInput, }; return await this.productRepository.save(newProduct); } async checkSoldOut({ productId }) { const product = await this.productRepository.findOne({ where: { id: productId }, }); if (product.isSoldout) { throw new UnprocessableEntityException('Sold out'); } // if(product.isSoldout) { // throw new HttpException('이미 판매 완료 된 상품입니다.', HttpStatus.UNPROCESSABLE_ENTITY) // } } async delete({ productId }) { // 1. 실제 삭제 // const result = await this.productRepository.delete({ id: productId }); // return result.affected ? true : false // 2. 소프트 삭제(직접 구현) - isDeleted // this.productRepository.update({ id: productId }, { isDeleted: true }); // 3. 소프트 삭제(직접 구현) - deletedAt // this.productRepository.update({ id: productId }, { deletedAt: new Date() }); // 4. 소프트 삭제(TypeORM 제공) - softRemove - id로만 삭제 가능 // this.productRepository.softRemove({ id: productId }); // 4 . 소프트 삭제(TypeORM 제공) - softDelete const result = await this.productRepository.softDelete({ id: productId }); return result.affected ? true : false; } } 내용 확인 부탁드립니다.
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
@ObjectType이랑 @EntryType이랑 같이 사용을 할수는 없는건가요?? dto와 entry가 다른부분이 없어서 같이 쓰는게 낫겠다싶어서 시도하려니 안되네요 확실히 역할을 나눠야하는건가요??