nest

nest

PGI 2024. 7. 20. 00:18
반응형

01 스웨거

api명세서 라고 생각하면 되고 node는 이게 만들기 힘든데 nest는 편하다.

 npm install --save @nestjs/swagger swagger-ui-express

// main.ts 기본설정

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';

declare const module: any;
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  // 스웨거
  const config = new DocumentBuilder()
    .setTitle('트렌드셀러 api')
    .setDescription('트렌드셀러 개발을 위한 api문서')
    .setVersion('1.0')
    // 로그인이 필요한 문서일경우
    .addCookieAuth('connect.sid')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);
  const port = process.env.PORT || 3000;
  await app.listen(port);
  if (module.hot) {
    module.hot.accept();
    module.hot.dispose(() => app.close());
  }
}
bootstrap();

// controller

@ApiResponse({
    status: 200,
    description: '성공',
    type: UserDto,
})
@ApiResponse({
    status: 500,
    description: '서버에러',
})
이런식으로 쓰면 됨

 

 

02 데코레이터

nest에서는 @Req @Res 같은 데코레이터를 사용하지 않는것이 좋다.

안좋은 이유로는 
1. 테스트의 어려움 : http 요청객체를 직접 주입받기 때문에 컨트롤러를 단위 테스트 하기 힘들다. 

2. 타입 안전성 저하 : req.user 같은 것은 일반적으로 any취급을 받기 때문에 컴파일시 타입 오류를 잡아내기 힘들다.

 

그레서 커스텀으로 데코레이터를 만들어 보려고 한다. 

타입추론할수있게 예시로는 아래 와 같음

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

//저장된 사용자 정보에 접근 가능
export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const req = ctx.switchToHttp().getRequest();
    return req.user;
  },
);

// 토큰검증
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const Token = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const res = ctx.switchToHttp().getResponse();
    return res.locals.jwt;
  },
);

 

추가적으로 ctx라는것은 실행컨텍스트라고 한다. (js에서의 실행컨텍스트가 아니랑)
http 웹소켓등을 동시에 지원하기 위해 만들어진 nest만의 개념이라고 한다.

03 인터셉터

응답을 보냇긴 햇는데 한번더 조작을 했으면 좋겠다 라고 생각을 했을때 사용하는게 인터셉터라고 보면된다.

응답을 보내기전 마지막 찬스 같은 느낌.

 

지금 만든거는 데이터가 알수 없을때 null값 아니면 data를 보내는 인터셉터이다.

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
} from '@nestjs/common';
import { map, Observable } from 'rxjs';
// 응답을 보냇는데 한번더 조작햇으면 좋겟다 라고 생각할때
// 인터셉터를 쓴다고 함. 마지막 가공 찬스
//implements 타입을 정확하게 지킬수 잇음.
@Injectable()
export class UndefinedToNullInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    next: CallHandler<any>,
  ): Observable<any> | Promise<Observable<any>> {
    // 응답을 보내기 전
    return next
      .handle()
      .pipe(map((data) => (data === undefined ? null : data)));
  }
}

04. 엔티티 설정 살짝

엔티티 설정 하는방법을 살짝해볼껀데 나는 유저 기준으로 써놔보겠다.

다 써놓긴 했는데 여기서 중요한거
@OneToOne 이게 1;1 상황일때 
@OneToMany 이게 1: 다 상황일때 그레서 배열로...

@ManyToOne 이면 다:1이니까 1;1때처럼 써주면 된다. 

비록 erd를 쓰는법을 몰라... 작업이 늦어지고는 있지만... 화이팅해보자... 킹모장이시여 ㅠㅠ

import {
  Column,
  CreateDateColumn,
  DeleteDateColumn,
  Entity,
  OneToMany,
  OneToOne,
  PrimaryGeneratedColumn,
  UpdateDateColumn,
} from 'typeorm';
import { Likes } from './Likes';
import { Mentos } from './Mentos';
import { Comments } from './Comments';
import { Payments } from './Payments';

// 회원등급
export enum UserRole {
  USER = 'user',
  MENTOR = 'mentor',
  ADMIN = 'admin',
}
// 소셜로그인
export enum SocialLoginProvider {
  GOOGLE = 'google',
  KAKAO = 'kakao',
  NAVER = 'naver',
  LOCAL = 'local',
}
@Entity({ schema: 'konnect', name: 'users' })
export class Users {
  // 키값
  @PrimaryGeneratedColumn({ type: 'int', name: 'id' })
  id: number;
  // 이메일
  @Column('varchar', {
    name: 'email',
    length: 30,
    unique: true,
    nullable: true,
  })
  email: string;
  // 비밀번호
  @Column('varchar', { name: 'password', length: 100, nullable: true })
  password: string | null;
  //닉네임
  @Column('varchar', { name: 'nickname', length: 30, unique: true })
  nickname: string;
  // 이름
  @Column('varchar', { name: 'name', length: 30 })
  name: string;
  // 전화번호
  @Column('varchar', { name: 'phone', length: 11, unique: true })
  phone: string;
  // 프로필이미지
  @Column('varchar', { name: 'image', length: 200, nullable: true })
  image: string;
  // 소셜로그인
  // 일반회원일수 잇기때문에 null로 설정
  @Column('enum', {
    enum: SocialLoginProvider,
    nullable: true,
    default: SocialLoginProvider.LOCAL,
  })
  socialLoginProvider: SocialLoginProvider | null;
  // snsid
  @Column('varchar', { name: 'snsid', nullable: true })
  // 유저등급
  @Column('enum', { enum: UserRole, default: UserRole.USER })
  role: UserRole;
  @CreateDateColumn()
  createdAt: Date;
  @UpdateDateColumn()
  updatedAt: Date;
  @DeleteDateColumn()
  deleteAt: Date;
  // 멘토 신청 관계설정
  @OneToOne(() => Mentos, (mento) => mento.users)
  mentos: Mentos;
  // 게시물 댓글 관계설정
  @OneToMany(() => Comments, (Comment) => Comment.users)
  comments: Comments[];
  // 게시물 좋아요
  @OneToMany(() => Likes, (like) => like.users)
  likes: Likes[];
  // 결제 관계설정
  @OneToMany(() => Payments, (payment) => payment.users)
  payments: Payments[];
}

 

반응형