묻고 답해요
137만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
이미지 s3 업로드 관련 질문드립니다!
안녕하세요 코팩님!다름이 아니라 현재 제가 프로젝트에서 presigned url 방식으로 프론트단에서 이미지를 s3로 업로드하고 람다가 트리거 돼서 이미지 리사이징을 해주고 있습니다. 현재 업로드 방식은 s3 잉여 데이터들이 쌓이는게 걱정돼서 매번 s3로 바로 올려놓는게 아닌 최종 업로드 버튼시에 모든 이미지가 한번에 업로드 되는 구조입니다. 그런데 용량이 좀 큰 이미지는 람다 리사이징이 좀 걸려서 딜레이 때문에 프론트단에서 로딩을 걸어놓긴 하는데 사용자 경험이 좋지 못합니다. 그리고 게시물 작성 중 임시저장 기능을 새롭게 추가하려고 하니깐 이미지 처리를 어떻게 가져가야하나 싶더라고요.혹시 강의에 나오는 temp 폴더에서 post 폴더로 옮겨가는 로직을 이용해서 s3에 적용을 한다면 좋을까요? 아님 더 좋은 해결방안이 있을까요?그리고 리사이징 부분이 람다로 작동되다 보니 사용자가 게시물을 업로드 하지도 않고 계속 이미지를 수정하는 상황이 발생하면 람다 비용 문제도 고민되기도 합니다... 고민이 많다보니 횡설수설 질문드렸는데 현업에서는 기획에 따라 다르겠지만 이런 상황에선 어떻게 하는지가 좀 궁금합니다! 항상 감사드립니다.
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
post에서 타입
authorId의 타입은 number인데,포스트맨에서 return받는 newPost의 id의값은 스트링으로 들어오는 이유는 궁금합니다.
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
강의 질문입니다.
안녕하세요.typeOrm 에 관한 질문입니다.댓글과 포스트에 관한 ManyToOne의 관계에서 commentRepository에 save할때 포스트와 연결하고 싶을때 포스트 모델 객체를 넣어도 되고 post : {id:postid} 값만 넣어줘도 되는지 궁금합니다. comment post 요청 작성시 author은 모델로 save 하고 post는 id로 save 해서 궁금해서 여쭤 봅니다.미리 감사합니다
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
다음 강의는 언제쯤...
안녕하세요 코드 팩토리님..강의 너무 잘듣고 있습니다.혹시 nestjs part2 강의는 언제 계획되어 있으신지 궁금합니다.빨리 보고 싶어서요^^;;
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
환경변수 설정 시 Error: SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string 라는 에러가 납니다.
안녕하세요. 환경변수 설정 간 TypeOrmModule.forRoot()를 설정하는 구간에서강의대로 process.env로 .env에 있는 키들을 받아오려 하니 제목과 같은 에러가 납니다.구글링을 해보았는데, forRootAsync를 활용하라는 말 등은 있지만 저 에러가 동일하게 구현된 사례는 없었습니다. 혹시 제가 어떤 잘못을 했는지 궁금하여 질문을 남깁니다. 강의 정말 잘 보고 있습니다. 감사합니다. @Module({ imports: [ TypeOrmModule.forRoot({ type: 'postgres', host: process.env[ENV_DB_HOST], port: parseInt(process.env[ENV_DB_PORT]), username: process.env[ENV_DB_USERNAME], password: process.env[ENV_DB_PASSWORD], database: process.env[ENV_DB_DATABASE], synchronize: true, }), CommonModule, ConfigModule.forRoot({ envFilePath: process.env.NODE_ENV === 'production' ? '.env.production.local' : '.env.development.local', isGlobal: true, }), AuthModule, UsersModule, ], controllers: [AppController], providers: [AppService], }) export class AppModule {} // .env.development.local DB_TYPE=postgres DB_HOST=localhost DB_PORT=5430 DB_USERNAME=123123 DB_PASSWORD=123123 DB_DATABASE=123123 // constants.ts export const ENV_DB_HOST = 'DB_HOST'; export const ENV_DB_PORT = 'DB_PORT'; export const ENV_DB_USERNAME = 'DB_USERNAME'; export const ENV_DB_PASSWORD = 'DB_PASSWORD'; export const ENV_DB_DATABASE = 'DB_DATABASE'; // 에러 메세지 Error: SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
db 컬럼 camel or snake
안녕하세요! 강의 너무 잘 듣고 있습니다. 별건 아니고 혹시 db 컬럼을 평소에도 camel로 쓰시는건가요??보통 db컬럼은 스네이크로 많이들 쓰는걸로 알고 있어서 여쭤봅니다!
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
docker 와 local DB 연결 시 각 폴더는 어떻게 연결되는 건가요?
안녕하세요! nestjs 수강생 입니다!docker 부분에서 질문이 있어서 문의 드리게 되었습니다. 현재 local 과 docker postgres image 가 이렇게 연결되어 있습니다. 알고 있는 점(1) docker-compose up 을 실행하면 두 폴더가 연결되며 docker postgres 의 데이터 폴더를 로컬 폴더와 매핑한다.(2) docker postgres 에서 데이터 손실 시 로컬에서 관리하는 postgres-data 폴더 덕분에 문제 없음(3) 로컬 폴더를 날리고 docker-compose up을 재 실행하면 모든 데이터 날라감. 궁금한 점(1) 개발자는 자신의 로컬 폴더에 있는 데이터만 CRUD 하고 그 결과가 docker postgres 에 반영되는 건가요? 그래서 로컬폴더를 삭제하고 docker-compose up 을 재 실행하면 데이터가 모두 날라가는 건가요?(2) docker-compost up 을 실행하면 처음에는 로컬에서 docker postgres 에 데이터를 가져오는데 그럼 ./postgres-data 폴더가 있을때와 없을때가 다르게 동작하는 건가요? 예를들어 있을때는 로컬에서 docker postgres 로 전송, 없을때는 docker postgres에서 로컬로 전송. 아래는 제가 이해하고 있는 점인데 어떤 부분이 맞고 틀린 지를 알 수 없어서 쭉 적어봤습니다!!처음 docker-compose up 을 실행하면 ./postgres-data 폴더가 없어서 docker postgres의 폴더를 복사해서 가져와 매핑합니다. 매핑 되어있는 폴더 덕분에 우리는 로컬 폴더와 TypeORM 을 사용해서 데이터에 변화를 주고 이러한 CRUD 는 docker postgres 에 반영됩니다.이러한 매핑 덕분에 docker 가 문제가 되어 docker postgres 의 데이터가 삭제되어도 로컬에서 저장된 데이터를 매핑할 수 있어 데이터를 보존할 수 있습니다.로컬에서 ./postgres-data 를 삭제하고 docker-compose up 을 실행하면 docker postgres 는 로컬의 CRUD 중 Delete 상황을 docker postgres 에 반영하여 데이터를 모두 삭제하고 폴더가 없어졌으니 다시 받아오는 작업을 한다. 혹시 잘못된 개념 부분이 있을까요?
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
class validation & class transformer
안녕하세요.수강생입니다. 강의 잘보고 있습니다.한가지 질문이 있습니다. class validation 과 class transformer 도 일종의 pipe 인가요? context를 알고 validation 과 transformer 를 수행하는거 같아서요..
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
debug 문의입니다.
안녕하세요 디버그가 잘 안되어서 문의 드립니다. { // IntelliSense를 사용하여 가능한 특성에 대해 알아보세요. // 기존 특성에 대한 설명을 보려면 가리킵니다. // 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요. "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug NestJS", "runtimeExecutable": "yarn", "runtimeArgs": [ "start:debug" ], "console": "integratedTerminal", "restart": true, "port": 9229, "autoAttachChildProcesses": true } ] }위와 같이 설정을 하고, 실행을 하였는데, 아래 메세지가 뜨면서 실패합니다. source /Users/ik/workspace/full-gpt/venv/bin/activate➜ workspace source /Users/ik/workspace/full-gpt/venv/bin/activate(venv) ➜ workspace /usr/bin/env 'NODE_OPTIONS= --require "/Applications/Visual Studio Code.app/Contents/Resources/app/extensions/ms-vscode.js-debug/src/bootloader.js" --inspect-publish-uid=http' 'VSCODE_INSPECTOR_OPTIONS=:::{"inspectorIpc":"/var/folders/tz/c_vc4l0n1y5fhzwqv7rphrxm0000gn/T/node-cdp.28589-f5f66828-7.sock","deferredMode":false,"waitForDebugger":"","execPath":"/Users/ik/.nvm/versions/node/v20.10.0/bin/node","onlyEntrypoint":false,"autoAttachMode":"always","fileCallback":"/var/folders/tz/c_vc4l0n1y5fhzwqv7rphrxm0000gn/T/node-debug-callback-8c26c617c10dd13b"}' /Users/ik/.nvm/versions/node/v20.10.0/bin/yarn start:debug Debugger attached.yarn run v1.22.21error Couldn't find a package.json file in "/Users/ik/workspace"info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.Waiting for the debugger to disconnect...(venv) ➜ workspace 왜 그런지 알 수 있을까요?
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
docker compose / postgres 부분 질문드려요
accept connections 까지 확인 후에도 data 폴더 안에 아무것도 생성되지 않아서요! 질문드립니다!!
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
token 재발급문제
강의 잘듣고 있습니다. 유투브에서 flutter 영상만 보다가 평소 관심있었던 nest 까지 이어왔습니다(자바스크립트, 타입스크립트 그리고 nestjs까지)토큰 관련 마지막강의해서 refresh token 이 만료되면 다시 ....refresh end point 로 요청을 보내서 다시 refresh 토큰을 받는다 말씀하셨는데요... 기간 만료된 refresh 토큰을 보내도 되는건가요? 아님 그 기능을 따로 구현해야 하는건가요?
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
followeeCount, followerCount 증가, 감소를 위한 메소드 일반화하기 코드 공유
increment(conditions: FindOptionsWhere<Entity>, propertyPath: string, value: number | string)먼저 increment메소드의 propertyPath 부분이 특정 Model의 프로퍼티를 추론 하지 않고 string으로 박혀 있는게 마음에 들지 않아서 대상이 되는 프로퍼티 필드 명을 추론 하게 작성 했습니다.// users.service.ts async incrementFollowerCount( userId: number, fieldName: keyof Pick<UsersModel, 'followerCount' | 'followeeCount'>, incrementCount: number, qr?: QueryRunner, ) { const usersRepository = this.getUsersRepository(qr); await usersRepository.increment( { id: userId, }, fieldName, incrementCount, ); }1. fieldName: 어떤 프로퍼티를 증가, 감소를 할것인지 특정하는 파라미터2. incrementCount : 몇 증가 시킬것인지 증가 value// users.service.ts async decrementFollowerCount( userId: number, fieldName: keyof Pick<UsersModel, 'followerCount' | 'followeeCount'>, decrementCount: number, qr?: QueryRunner, ) { const usersRepository = this.getUsersRepository(qr); await usersRepository.decrement( { id: userId, }, fieldName, decrementCount, ); }contoller에서 사용하기// users.controller.ts @Patch('follow/:id/confirm') @UseInterceptors(TransactionInterceptoer) async patchFollowConfirm( @User() user: UsersModel, @Param('id', ParseIntPipe) followerId: number, @QueryRunnerDecorator() qr: QueryRunner, ) { await this.usersService.confirmFollow(followerId, user.id); await this.usersService.incrementFollowerCount( user.id, 'followerCount', 1, qr, ); await this.usersService.incrementFollowerCount( followerId, 'followeeCount', 1, qr, ); return true; } @Delete('follow/:id') @UseInterceptors(TransactionInterceptoer) async deleteFollow( @User() user: UsersModel, @Param('id', ParseIntPipe) followeeId: number, @QueryRunnerDecorator() qr: QueryRunner, ) { await this.usersService.deleteFollow(user.id, followeeId); await this.usersService.decrementFollowerCount( user.id, 'followerCount', 1, qr, ); await this.usersService.decrementFollowerCount( followeeId, 'followeeCount', 1, qr, ); return true; }filedName 파라미터를 특정 프로퍼티만 올 수 있게 자동완성 잘됩니다.팔로워 confirm 되면 나의 follower 증가 + 상대방 followee 증가팔로워 삭제 되면 나의 follower 감소 + 상대방 followee 감소await this.usersService.incrementFollowerCount( followerId, 'followeeCount', 1, qr, );userId가 오는 파라미터 자리에 user.id가 아닌 followerId가 들어간 이유는 followeeCount를 증가 해야 되기 때문이다. 즉, 팔로워를 수락 했으면 나의 followerCount를 증가 시키고 상대방 followeeCount를 증가 시켜야 되기 때문에, 삭제 했을때도 같은 원리[결과]2번 사용자가 1번 사용자를 팔로워하고, 1번 사용자가 팔로워를 수락 했을때
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
@IsPublic 어노테이션으로 RefreshTokenGuard에서 RefreshToken 검증을 하기 위한 코드 공유
// is-public.const.ts export enum IsPublicEnum { ISPUBLIC = 'ISPUBLIC', ISREFRESHTOKEN = 'ISREFRESHTOKEN', }// is-public.decorator.ts import { SetMetadata } from '@nestjs/common'; import { IsPublicEnum } from '../const/is-public.const'; export const ISPUBLIC_KEY = 'is_public'; export const IsPublic = (status: IsPublicEnum) => SetMetadata(ISPUBLIC_KEY, status);// bearer-token.guard.ts @Injectable() export class BearerTokenGuard implements CanActivate { constructor( private readonly authService: AuthService, private readonly usersService: UsersService, public readonly reflector: Reflector, ) {} async canActivate(context: ExecutionContext): Promise<boolean> { const req = context.switchToHttp().getRequest(); const requiredPublic = this.reflector.getAllAndOverride(ISPUBLIC_KEY, [ context.getHandler(), context.getClass, ]); if (requiredPublic) { req.requiredPublic = requiredPublic; } if (requiredPublic === IsPublicEnum.ISPUBLIC) { return true; } const rawToken = req.headers['authorization']; if (!rawToken) throw new UnauthorizedException('토큰이 없습니다!'); const token = this.authService.extractTokenFromHeader(rawToken, true); const result = await this.authService.verifyToken(token); const user = await this.usersService.getUserByEmail(result.email); req.user = user; req.token = token; req.tokenType = result.type; return true; } }추가된 코드if (requiredPublic) { req.requiredPublic = requiredPublic; }requiredPublic 있을때만 req에 담아줌.추가된 코드if (requiredPublic === IsPublicEnum.ISPUBLIC) { return true; }IsPublic일때만 return true 즉, ISREFRESHTOKEN 일때는 아래 로직들이 정상적으로 실행됨.// bearer-token.guard.ts @Injectable() export class AccessTokenGuard extends BearerTokenGuard { async canActivate(context: ExecutionContext): Promise<boolean> { await super.canActivate(context); const req = context.switchToHttp().getRequest(); if ( req.requiredPublic === IsPublicEnum.ISPUBLIC || IsPublicEnum.ISREFRESHTOKEN ) { return true; } if (req.tokenType !== 'access') { throw new UnauthorizedException('Access Token이 아닙니다.'); } return true; } }추가된 코드if (req.requiredPublic === IsPublicEnum.ISPUBLIC || IsPublicEnum.ISREFRESHTOKEN) { return true; }req.requiredPublic이 ISPUBLIC이거나 ISREFRESHTOKEN이면 return true로 global accessTokenGuard return true로 빠져나옴.// bearer-token-guard.ts @Injectable() export class RefreshTokenGuard extends BearerTokenGuard { async canActivate(context: ExecutionContext): Promise<boolean> { await super.canActivate(context); const req = context.switchToHttp().getRequest(); if (req.tokenType !== 'refresh') { throw new UnauthorizedException('Refresh Token이 아닙니다.'); } return true; } }변경 사항 없음. 왜냐하면 위에서 정상적으로 refreshToken을 verify하고 req에 값이 담겼기 때문에 RefreshTokenGuard가 정상적으로 실행 되어야함. auth.controller에 적용하기@Controller('auth') export class AuthController { constructor(private readonly authService: AuthService) {} @IsPublic(IsPublicEnum.ISREFRESHTOKEN) @Post('token/access') @UseGuards(RefreshTokenGuard) postTokenAccess(@Headers('authorization') rawToken: string) { ... } @IsPublic(IsPublicEnum.ISREFRESHTOKEN) @Post('token/refresh') @UseGuards(RefreshTokenGuard) postTokenRefresh(@Headers('authorization') rawToken: string) { ... } @IsPublic(IsPublicEnum.ISPUBLIC) @Post('login/email') @UseGuards(BasicTokenGuard) postLoginEmail(@Headers('authorization') rawToken: string) { ... } @IsPublic(IsPublicEnum.ISPUBLIC) @Post('register/email') postRegisterEmail(@Body() body: RegisterUserDto) { ... } }
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
별거 아니긴 한데 nest-cli 로 만들때
nest cli로 resource 만들때 경로를 / 넣어주면 아래에 바로 생성 됩니다.nest g resource posts/comments이렇게 하면 posts폴더 아래에 comments가 생깁니다!
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
가드와 미들웨어 질문드립니다.
안녕하세요.,강의 잘 보고있습니다. 가드랑 미들웨어 용도를 이렇게 이해하면 좋을까요? 1.가드:-특정 컨트롤러로 들어온 파라매터, 혹은 context데이터를 가공하거나 검증하고 싶을때 2.미들웨어:-특정 규칙을 가진 패쓰 혹은 컨트롤러 전체에 데이터를 검증하고 싶을때 아주 맨 처음에 저는 token 을 검증하는 BearerTokenGuard이 가드보다는 middleware 로 가는것이 맞지 않나 싶었는데요.context.user 데이터를 controller 에 내려주기 위해IsCommentMine Guard 처럼 다른 가드로 유저데이터를 넘겨줘야하는 경우가 있어서가드로 사용하는것으로 이해했는데 맞을까요?(그리고 middleware 로는 불가능한걸까요?)
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
update comment 관련
안녕하세요.update comment 관련 두가지 질문이 있습니다.update(patch) 에서는 query runner를사용하지 않아도 되나요?update 시에repository.update 가 아닌 save 를 사용한 이유가 있을까요?항상 강의 잘 보고있습니다.감사합니다!
-
해결됨[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
docker-compose up 후에 터미널엔 연결 됐다고 떴는데 postgres-data 폴더에 아무것도 들어와 있지 않아요
다 맞게 잘 한 거 같은데 뭐가 문제인 지 모르겠습니다ㅜㅜ
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
섹션5의 Post 요청 만들기에서 질문있습니다.
안녕하세요.섹션5의 Post 요청 만들기 영상을 보다가 궁금증이 생겨 질문드립니다. Q1)새로운 post 생성시 id를 부여할 때배열의 마지막 인덱스 아이템을 꺼내해당 아이템의 id에 1을 더하셨습니다.그런데 배열 안의 아이템 순서가 꼭 id-오름차순으로 정렬되어있을거란 보장이 없기 때문에배열에서 가장 큰 id를 찾은 후, +1을 하여 새로운 id를 부여해줘야하지 않을까 싶은데 제가 잘못 생각하고 있는걸까요? Q2)새로운 post를 기존 배열에 push하지 않고,spread 방식으로 추가해주셨는데특별한 이유가 있는건지도 궁금합니다!
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
Transform이 적용이 안됩니다.
Transform을 적용해도 path값에 public/posts값이 붙지 않은 채로 계속 나옵니다.main.ts파일에 위와 같이 적용을 해도 안되는데 혹시 원인을 알 수 있나요?
-
미해결[코드팩토리] [초급] NestJS REST API 백엔드 완전 정복 마스터 클래스 - NestJS Core
웹에서 포스트 맨 사용하면 파일업로드가 불가능합니다.
아무리 찾아봐도 웹에서 해결하는 것이 불가능해서 postman desktop 까는 것을 추천드립니다 ㅎㅎ