ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • NestJS_온라인 공연 예매 서비스 프로젝트 2편_프로젝트 필수 기능 구현
    TIL (Today I Learned) 2023. 12. 24. 22:21

    # Nest JS 프로젝트 본격적으로 시작에 앞서

    지난 포스팅에 이어 두 번째 이야기를 시작하겠습니다. 이전 글에서는 프로젝트의 기초 세팅에 대해 다뤘습니다. 만약 그 내용에 대해 궁금하시거나 기초 세팅 방법에 대해 더 자세히 알고 싶으신 분들은 아래 링크를 확인해주세요.

     

    NestJS_온라인 공연 예매 서비스 프로젝트 1편_기본 세팅

    # Nest JS 프로젝트를 시작하며 TypeScript와 NestJS를 활용한 온라인 공연 예매 서비스 프로젝트를 자세하게 기록하는 포스팅을 시작하고자 합니다. 지금까지 다양한 과제를 수행해왔지만, 아마도 이

    k0zdevel.tistory.com


    # Nest JS_DTO를 엄격하게 다루기 위한 `ValidationPipe`의 활용하기

    ***DTO란?***

    DTO는 Data Transfer Object의 약자로, 데이터 전송을 위해 작성된 객체를 뜻합니다. 주로 클라이언트와 서버 간의 데이터 교환을 위한 객체로 사용됩니다. 클라이언트가 서버로 데이터를 전달할 때는 DTO를 사용하여 데이터를 패키징하고, 서버가 클라이언트로 응답할 때도 DTO를 활용하여 데이터를 전송합니다. 이를 통해 데이터의 구조가 명확해지고, 불필요한 정보를 제거하여 효율적인 통신이 가능해집니다. NestJS에서 모든 데이터는 DTO를 통해 운반됩니다. 

    *** ValidationPipe란?***

    ValidationPipe는 NestJS에서 제공하는 기능 중 하나로 DTO 사용을 위한 필수 옵션입니다. DTO에 정의된 유효성 검사 규칙을 자동으로 적용해주는 역할을 합니다. 클라이언트가 전달한 데이터를 DTO 객체로 변환하기 전에 유효성을 검사하며, DTO 클래스에 정의된 데코레이터들을 사용하여 각 필드의 데이터 타입이나 형식을 검증합니다. 예를 들어, @IsString(), @IsInt(), @IsEmail() 등의 데코레이터를 활용하여 데이터의 일관성과 유효성을 유지할 수 있습니다.

     

    이를 통해 애플리케이션은 클라이언트로부터의 입력 데이터를 강력하게 검증할 수 있게 되어, 안전하고 신뢰성 있는 데이터 처리가 가능해집니다. 만약 유효성 검사에 실패하면 NestJS는 자동으로 예외를 발생시키거나 사용자가 정의한 예외 핸들러를 호출하여 오류를 처리합니다. 이를 통해 잘못된 데이터가 애플리케이션으로 들어오는 것을 방지하고, 더 견고하고 안전한 코드를 작성할 수 있게 됩니다.

     

     

    Documentation | NestJS - A progressive Node.js framework

    Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

    docs.nestjs.com


    ***DTO 사용을 위한 패키지 설치 명령어 -   class-validator, class-transformer ***

    npm i  class-validator class-transformer

    ***  main.ts 기존 코드 ***

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      await app.listen(3000);
    }
    bootstrap();

     

     

    *** main.ts 변경 코드 ***

    // NestJS에서 제공하는 ValidationPipe를 사용하기 위해 import합니다.
    import { ValidationPipe } from '@nestjs/common';
    
    // NestJS의 핵심 모듈에서 NestFactory, 애플리케이션의 메인 모듈인 AppModule을 import합니다.
    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    
    // 애플리케이션을 생성하고 실행하는 비동기 함수 bootstrap을 선언합니다.
    async function bootstrap() {
      // NestFactory.create 메서드를 사용하여 AppModule을 기반으로 한 NestJS 애플리케이션을 생성합니다.
      const app = await NestFactory.create(AppModule);
    
      // useGlobalPipes 메서드를 통해 전역적으로 사용할 ValidationPipe를 등록합니다.
      // transform: true 옵션은 클라이언트가 보낸 JSON 데이터를 컨트롤러에서 사용하는 DTO 객체로 자동 변환해줍니다.
      app.useGlobalPipes(
        new ValidationPipe({
          transform: true,
        }),
      );
    
      // 생성된 애플리케이션을 3000번 포트에서 실행합니다.
      await app.listen(3000);
    }
    
    // bootstrap 함수를 호출하여 애플리케이션을 생성하고 실행합니다.
    bootstrap();

    # Nest JS_Database연결을 위한 TypeORM 적용하기

    ***TypeORM이란? ***

    TypeORM은 NestJS에서 사용되는 강력하고 확장 가능한 객체 관계 매핑(ORM) 라이브러리입니다. ORM은 데이터베이스와 애플리케이션의 객체 모델 간의 상호 작용을 추상화하며, 데이터베이스 조작을 SQL이 아닌 객체로 다룰 수 있게 해줍니다. TypeORM은 Typescript계의 Sequelize, Prisma입니다.

     

     

    README_ko - typeorm

    이 예시에서는 MySQL을 사용하고 있지만 지원되는 다른 데이터베이스를 사용할 수도 있다. 다른 데이터 베이스를 사용하려면 옵션의 type을 사용 중인 데이터베이스 타입으로 변경하기만 하면 된

    orkhan.gitbook.io

    1. 엔터티 (Entity)

    엔터티는 데이터베이스의 테이블과 매핑되는 TypeScript 또는 JavaScript 클래스입니다. 각 클래스 속성은 테이블 컬럼과 일치하며, 엔터티의 인스턴스는 데이터베이스 레코드에 해당합니다.

    2. 저장소(Repository)

    Repository는 엔터티의 데이터베이스 조작을 담당하는 객체입니다. getRepository 함수를 사용하여 Repository를 얻고, 해당 Repository를 통해 엔터티의 생성, 조회, 갱신, 삭제 등을 수행할 수 있습니다.

    3. 마이그레이션 (Migration) 

    TypeORM은 데이터베이스 스키마의 변경을 관리하는 마이그레이션을 지원합니다. 새로운 엔터티를 추가하거나 스키마를 업데이트할 때 사용됩니다.

    4. 조인과 관계 (Join and Relation)

    엔터티 간의 관계를 설정하고, @ManyToOne, @OneToMany, @ManyToMany 등의 데코레이터를 사용하여 쉽게 관계를 매핑할 수 있습니다.

    5. 쿼리 빌더( Query Builder)

    TypeORM은 SQL 쿼리를 직접 작성할 수 있는 Query Builder를 제공합니다. 또한, TypeORM의 쿼리 API를 사용하여 간단한 쿼리도 작성할 수 있습니다.

    6. CLI(Command Line Interface)

    TypeORM은 커맨드 라인 인터페이스를 제공하여 마이그레이션 생성, 실행, 엔터티 생성 등의 작업을 쉽게 수행할 수 있습니다.

    7. 트랜잭션 (Transaction)

    트랜잭션을 사용하여 여러 데이터베이스 작업을 원자적으로 처리할 수 있습니다.

    8. 클라이언트-서버 구조(Client-Server Architecture)

    TypeORM은 클라이언트-서버 구조를 지원하여 여러 서비스 간의 데이터 공유와 통합을 편리하게 처리할 수 있습니다.


    ***TypeORM 설치 명령어***

    npm i @nestjs/typeorm typeorm mysql2

     


    ***app.module.ts 기존 작성 방법의 문제점***

    import { Module } from '@nestjs/common';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
    import { AppModuleController } from './app.controller';
    import { AppModuleService } from './app.service';
    
    @Module({
      imports: [ //  해당 모듈을 사용할 것이라는 것을 명시
        // TypeORM 모듈을 애플리케이션에 추가합니다.
        TypeOrmModule.forRoot({ //  forRoot 함수로 설정된 내용들은 모든 모듈에 적용이 되기 때문에 forRoot 함수로 설정
          type: 'mysql', // 사용할 데이터베이스의 타입 (여기서는 MySQL)
          host: 'localhost', // 데이터베이스 호스트 주소
          port: 3306, // 데이터베이스 연결 포트
          username: 'your_database_username', // 데이터베이스 사용자 이름
          password: 'your_database_password', // 데이터베이스 비밀번호
          database: 'your_database_name', // 사용할 데이터베이스 이름
          entities: [], // TypeORM이 관리할 엔터티 클래스를 여기에 추가해야 합니다.
          synchronize: true, // 애플리케이션을 시작할 때 자동으로 데이터베이스를 동기화할지 여부
          namingStrategy: new SnakeNamingStrategy(), // 스네이크 케이스 네이밍 전략을 설정합니다.
          logging: true, // TypeORM 로깅을 활성화합니다.
        }),
      ],
      controllers: [AppModuleController], // 애플리케이션의 컨트롤러 클래스를 여기에 추가합니다.
      providers: [AppModuleService], // 애플리케이션의 서비스 클래스를 여기에 추가합니다.
    })
    export class AppModule {}

     

    Nest.js에서는 데이터베이스 접속 정보와 같은 민감한 정보가 코드에 노출되는 것을 방지하기 위해 @nestjs/config 패키지를 사용합니다. 이 패키지는 .env 파일과 유사한 역할을 하며, 민감한 정보를 안전하게 관리할 수 있습니다. .env 파일을 효과적으로 다루기 위해 joi 패키지를 사용할 수 있습니다. joi를 활용하면 .env 파일의 형식을 강제할 수 있어, 필요한 환경 변수가 누락되거나 형식에 맞지 않는 경우 서버가 시작되지 않도록 설정할 수 있습니다.

     

    요약하면, Nest.js에서는 @nestjs/config와 joi를 사용하여 .env 파일을 통한 환경 변수 관리를 강화하고, 코드에서 민감한 정보 노출을 최소화합니다.


    ***@nestjs/config와 joi 설치 명령어***

    npm i @nestjs/config joi

     


    ***  app.module.ts 기존 코드 ***

    프로젝트에 진입점 역할을 하는 module => app.module.ts 

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    
    @Module({
      imports: [],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}

     

    ***  app.module.ts 변경 코드 ***

     

    // NestJS에서 사용하는 Joi와 TypeORM 네이밍 전략을 임포트합니다.
    import Joi from 'joi';
    import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
    
    // NestJS에서 사용하는 모듈, 설정, TypeORM 모듈을 임포트합니다.
    import { Module } from '@nestjs/common';
    import { ConfigModule, ConfigService } from '@nestjs/config';
    import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
    
    // TypeORM 모듈 설정 옵션을 동적으로 생성하는 객체입니다.
    const typeOrmModuleOptions = {
      // 비동기적으로 TypeOrmModuleOptions를 생성하는 useFactory 메서드입니다.
      useFactory: async (
        configService: ConfigService,
      ): Promise<TypeOrmModuleOptions> => ({
        // SnakeNamingStrategy를 사용하여 스네이크 케이스 네이밍 전략을 설정합니다.
        namingStrategy: new SnakeNamingStrategy(),
        // 데이터베이스 연결 정보를 환경 변수에서 동적으로 가져옵니다.
        type: 'mysql',
        username: configService.get('DB_USERNAME'),
        password: configService.get('DB_PASSWORD'),
        host: configService.get('DB_HOST'),
        port: configService.get('DB_PORT'),
        database: configService.get('DB_NAME'),
        // 애플리케이션을 시작할 때 자동으로 데이터베이스를 동기화할지 여부를 설정합니다.
        synchronize: configService.get('DB_SYNC'),
        // TypeORM 로깅을 활성화합니다.
        logging: true,
      }),
      // useFactory에서 사용할 의존성으로 ConfigService를 주입합니다.
      inject: [ConfigService],
    };
    
    // NestJS 모듈을 정의합니다.
    @Module({
      // 사용할 외부 모듈들을 imports 배열에 추가합니다.
      imports: [
        // 환경 변수 설정 및 검증을 위한 ConfigModule을 추가합니다.
        ConfigModule.forRoot({
          isGlobal: true,
          // 환경 변수의 유효성을 검사하기 위한 Joi 스키마를 정의합니다.
          validationSchema: Joi.object({
            JWT_SECRET_KEY: Joi.string().required(),
            DB_USERNAME: Joi.string().required(),
            DB_PASSWORD: Joi.string().required(),
            DB_HOST: Joi.string().required(),
            DB_PORT: Joi.number().required(),
            DB_NAME: Joi.string().required(),
            DB_SYNC: Joi.boolean().required(),
          }),
        }),
        // TypeORM을 비동기적으로 설정하기 위해 TypeOrmModule.forRootAsync를 사용합니다.
        TypeOrmModule.forRootAsync(typeOrmModuleOptions),
      ],
      // 현재 모듈에서 사용할 컨트롤러와 프로바이더를 정의합니다.
      controllers: [],
      providers: [],
    })
    export class AppModule {}

     

    NestJS 애플리케이션의 AppModule 코드를 보면, 중요한 설정들이 동적으로 환경 변수에서 가져와지고, 환경 변수의 유효성을 Joi 스키마를 통해 검사하며, TypeORM 설정은 동적으로 주입되고 있습니다.

     

    1. ConfigModule 설정

    ConfigModule.forRoot를 사용하여 환경 변수 설정을 정의합니다. isGlobal: true는 이 설정이 전역적으로 적용됨을 나타냅니다. validationSchema에 정의된 Joi 스키마대로 .env가 정의되어 있지 않으면 서버가 시작되지 않습니다.

    ConfigModule.forRoot({
      isGlobal: true,
      validationSchema: Joi.object({
        JWT_SECRET_KEY: Joi.string().required(),
        DB_USERNAME: Joi.string().required(),
        DB_PASSWORD: Joi.string().required(),
        DB_HOST: Joi.string().required(),
        DB_PORT: Joi.number().required(),
        DB_NAME: Joi.string().required(),
        DB_SYNC: Joi.boolean().required(),
      }),
    }),

     

    2. TypeOrmModule 설정

    TypeOrmModule.forRootAsync를 사용하여 TypeORM 설정을 동적으로 주입합니다. useFactory 메서드에서는 ConfigService를 사용하여 환경 변수에서 동적으로 값을 가져와 TypeORM 설정 객체를 생성합니다. SnakeNamingStrategy를 사용하여 스네이크 케이스 네이밍 전략을 적용합니다.

    TypeOrmModule.forRootAsync({
      useFactory: async (configService: ConfigService): Promise<TypeOrmModuleOptions> => ({
        namingStrategy: new SnakeNamingStrategy(),
        type: 'mysql',
        username: configService.get('DB_USERNAME'),
        password: configService.get('DB_PASSWORD'),
        host: configService.get('DB_HOST'),
        port: configService.get('DB_PORT'),
        database: configService.get('DB_NAME'),
        synchronize: configService.get('DB_SYNC'),
        logging: true,
      }),
      inject: [ConfigService],
    }),

     

    3.의존성 주입(DI) 및 ConfigService 활용

    TypeOrmModule.forRootAsync의 useFactory 메서드에서 inject: [ConfigService] 옵션을 사용하여 ConfigService를 주입합니다. 이렇게 함으로써, ConfigService를 활용하여 환경 변수 값을 동적으로 가져와 TypeORM 설정 객체를 생성할 수 있습니다.

    TypeOrmModule.forRootAsync({
      useFactory: async (configService: ConfigService): Promise<TypeOrmModuleOptions> => ({
        namingStrategy: new SnakeNamingStrategy(),
        type: 'mysql',
        username: configService.get('DB_USERNAME'),
        password: configService.get('DB_PASSWORD'),
        host: configService.get('DB_HOST'),
        port: configService.get('DB_PORT'),
        database: configService.get('DB_NAME'),
        synchronize: configService.get('DB_SYNC'),
        logging: true,
      }),
      inject: [ConfigService],
    }),

     

    여기서 inject: [ConfigService]는 의존성 주입(Dependency Injection)을 설정하는 옵션입니다. useFactory 메서드에서 사용할 외부의 서비스나 클래스를 지정하는 역할을 합니다. 즉, ConfigServiceuseFactory 메서드에 주입함으로써, 해당 메서드 내에서 ConfigService를 사용할 수 있게 됩니다.

     

    이렇게 함으로써, ConfigService를 활용하여 환경 변수에 동적으로 접근하고, 이 값을 활용하여 TypeORM 설정 객체를 생성할 수 있습니다. 이는 환경에 따라 변경되는 설정을 쉽게 다룰 수 있도록 도와주는 중요한 패턴 중 하나입니다.


    # Nest JS_인증 및 인가 기능을 책임질 Auth 모듈 생성하기

    ***Auth 모듈의 역할***

    Auth 모듈은 NestJS 애플리케이션에서 인증(Authentication) 및 인가(Authorization) 기능을 책임지는 모듈로서 중요한 역할을 수행합니다. Auth 모듈은 보안과 관련된 중요한 부분을 담당하므로, 신중한 설계와 구현이 필요합니다.

    1. 사용자 인증 (Authentication)

    **로그인 및 로그아웃 관리

    사용자의 인증 정보를 기반으로 로그인 및 로그아웃을 효과적으로 관리합니다.

     

    **토큰 생성 및 검증

    사용자의 인증이 성공하면 토큰을 생성하고, 이 토큰을 활용하여 향후 요청에서 사용자를 인증합니다.

     

    **다양한 인증 전략

    JWT(Json Web Token), 세션과 같은 다양한 인증 방식을 지원하여 유연한 사용자 인증을 구현합니다.

    2. 사용자 인가 (Authorization)

    **역할 및 권한 관리

    사용자의 역할과 권한을 체계적으로 관리하고, 특정 기능 또는 리소스에 대한 접근 권한을 제어합니다.

     

    **미들웨어 활용

    NestJS의 미들웨어를 활용하여 특정 엔드포인트나 라우트에 대한 인가 체크를 간편하게 수행합니다.

     

    **사용자 권한 검증

    사용자의 역할과 권한을 기반으로 특정 작업이나 리소스에 대한 권한을 검증합니다.

    3. 보안 강화

    **보안 정책 구현

    강력한 보안 정책을 적용하여 인가되지 않은 사용자의 접근을 방지하고 시스템의 보안성을 강화합니다.

     

    **세션 및 토큰 관리

    세션과 토큰의 유효성을 철저히 관리하여 보안에 취약한 상태를 방지합니다.

    4. 인증 및 인가 기능의 모듈화

    **재사용 가능한 컴포넌트

    Auth 모듈은 간편하게 재사용 가능한 컴포넌트로 설계되어 다른 모듈에서도 효과적으로 활용합니다.

     

    **설정 가능한 옵션

    프로젝트의 다양한 요구사항에 대응하기 위해 설정 가능한 옵션을 제공하여 모듈을 유연하게 활용합니다.

    5. 로그인/로그아웃 이벤트 처리

    **이벤트 핸들링

    로그인 및 로그아웃과 같은 이벤트가 발생할 때 신속하게 처리하고 필요한 작업을 수행합니다.

     

    **사용자 상태 관리

    로그인 상태를 효과적으로 유지하고 사용자의 상태를 체계적으로 관리합니다.

    6. 사용자 정보 제공

    **인증된 사용자 정보 제공

    인증된 사용자 정보를 제공하여 애플리케이션에서 사용자 경험을 최적화합니다.



     

    ***SRC 디렉토리 이동 후 Auth 모듈 명령어를 통해 생성하기***

    nest g mo auth // auth하는 이름으로 module을 생성

     

    **생성 전 디렉토리 구조

     

     

    **생성 후 디렉토리 구조_auth 디렉토리에 authmodule.ts 파일이 생성되었습니다.

     

    NestJS CLI의 강력한 기능을 통해 nest g mo auth 명령어로 authmodule.ts 파일을 간편하게 생성할 수 있다는 점은 정말 개발자를 위한 프레임워크의 편리함을 보여주는 부분입니다. 이제 생성된 모듈을 기반으로 실제 인증을 구현해보겠습니다.


    # Nest JS_Auth 모듈에서 사용할 인증 기능 구현하기

    JWT 발급 및 검증을 통한 구현에는 AuthMiddleware와 Passport 패키지를 활용한 두 가지 방법이 있습니다. 이번 프로젝트에서는 Passport를 사용할 예정이며,나중에 시간이 남는다면 AuthMiddleware 방법도 다루어볼 예정입니다.

    ***AuthMiddleware 방법***

    AuthMiddleware는 NestJS의 미들웨어를 활용하여 특정 라우트 또는 컨트롤러에 대한 인증 및 인가 체크를 수행하는 방법입니다. 이 방법은 직접 로직을 작성하여 인증 여부를 확인하고 처리하기 때문에 프로젝트에 딱 맞는 간결한 로직을 작성할 수 있습니다. 또한, Passport보다는 간단한 로직으로 시작할 수 있는 장점이 있습니다.

     

    하지만 이 방법의 단점으로는 초기 설정이 조금 더 복잡할 수 있습니다. 또한, Passport와는 달리 미들웨어를 다른 프로젝트에서 재사용하기 어려운 한계가 있습니다.

    ***Passport 패키지 방법***

    Passport는 Node.js용 인증 미들웨어로, NestJS에서는 Passport 모듈을 활용하여 간편하게 다양한 인증 전략을 구현할 수 있습니다. Passport는 Local, OAuth, JWT 등 다양한 인증 전략을 지원하며, 필요에 따라 선택적으로 구현할 수 있어 소셜 미디어 로그인, 토큰 기반의 인증 등을 효과적으로 다룰 수 있습니다. 또한, 새로운 전략을 추가하거나 커스텀 전략을 만들어 기존 로직을 확장하기 용이합니다.

     

    다만, Passport를 사용하면 추가적인 의존성이 발생하며 이를 관리하기 위해서는 몇 가지 설정이 필요합니다.


    ***src/auth 디렉토리에 jwt.strategy.ts 파일 생성하기***

    **src/auth 디렉토리로 이동해서  jwt.strategy.ts 파일 생성(jwt의 전략)

     

    ** jwt.strategy.ts 코드 작성

    // passport-jwt 모듈에서 필요한 ExtractJwt 및 Strategy를 가져옵니다.
    import { ExtractJwt, Strategy } from 'passport-jwt';
    
    // NestJS에서 제공하는 Injectable 데코레이터를 가져옵니다.
    import { Injectable } from '@nestjs/common';
    
    // NestJS의 환경 설정(Config)을 다루는 ConfigService를 가져옵니다.
    import { ConfigService } from '@nestjs/config';
    
    // NestJS에서 제공하는 PassportStrategy를 가져옵니다.
    import { PassportStrategy } from '@nestjs/passport';
    
    // Injectable 데코레이터를 사용하여 클래스를 NestJS에서 관리 가능한 Injectable로 만듭니다.
    @Injectable()
    // JwtStrategy 클래스를 정의하며, PassportStrategy를 상속하고 있습니다.
    export class JwtStrategy extends PassportStrategy(Strategy) {
      // JwtStrategy 클래스의 생성자를 정의하고, ConfigService를 주입받고 있습니다.
      constructor(private readonly configService: ConfigService) {
        // 부모 클래스인 PassportStrategy(Strategy)의 생성자를 호출하면서 JWT 전략에 필요한 옵션들을 전달합니다.
        super({
          // Bearer 토큰에서 JWT를 추출하는 방법을 지정합니다.
          jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
          // 만료 기간을 무시할지 여부를 나타냅니다.
          ignoreExpiration: false,
          // JWT를 검증할 때 사용할 비밀 키를 ConfigService를 통해 가져옵니다.
          secretOrKey: configService.get('JWT_SECRET_KEY'),
        });
      }
    
      // validate 메서드는 전략이 성공적으로 수행된 후 호출되며, 여기서는 주어진 JWT의 payload를 받아 실제 유저 정보를 조회하는 로직이 구현되어야 합니다.
      async validate(payload: any) {
        // TODO. payload로 전달된 데이터를 통해 실제 유저 정보를 조회해야 합니다!
      }
    }

     

    Passport는 마치 애플리케이션의 문지기처럼 동작하는 도구로, 여러 가지 로그인 방법을 유연하게 처리해줍니다. 현재 프로젝트는 간단한 이메일과 비밀번호를 이용한 로그인에 중점을 두고 있습니다만, 나중에는 구글이나 네이버, 카카오와 같은 소셜 로그인도 추가할 예정입니다.

     

    Passport를 사용하면 로그인과 권한 부여를 편리하게 구현할 수 있습니다. 특히 이번 프로젝트에서는 JWT(토큰)를 이용한 방법을 선택했습니다. 이렇게 함으로써 개발자는 로그인 관련 기능을 빠르게 구현할 수 있고, 나중에 소셜 로그인을 추가하는 것도 간단해질 것입니다. 이런 식으로 개발자는 복잡한 부분을 신경 쓰지 않고 중요한 기능에 집중할 수 있습니다. 

     

    다음으로는 Auth 모듈에서 방금 작성한 JwtStrategy를 적용할 수 있도록 코드를 수정해야합니다. 


    ***JwtStrategy 적용을 위해 Auth모듈 코드 수정하기***

    // NestJS에서 사용되는 모듈을 불러옵니다.
    import { Module } from '@nestjs/common';
    import { ConfigService } from '@nestjs/config';
    import { JwtModule } from '@nestjs/jwt';
    import { PassportModule } from '@nestjs/passport';
    
    // Jwt 전략을 담당하는 JwtStrategy를 불러옵니다.
    import { JwtStrategy } from './jwt.strategy';
    
    // AuthModule을 정의합니다.
    @Module({
      // AuthModule이 사용할 다른 모듈들을 imports 배열에 포함합니다.
      imports: [
        // Passport 모듈을 등록합니다. 
        // 여기서는 JWT 전략을 기본 전략으로 사용하며, 세션은 사용하지 않도록 설정합니다.
        PassportModule.register({ defaultStrategy: 'jwt', session: false }),
    
        // JwtModule을 비동기적으로 등록합니다.
        JwtModule.registerAsync({
          // useFactory 함수를 통해 JwtModule 설정을 동적으로 생성합니다.
          useFactory: (config: ConfigService) => ({
            // JWT 비밀 키를 ConfigService를 통해 가져와 설정합니다.
            secret: config.get<string>('JWT_SECRET_KEY'),
          }),
          // useFactory 함수에 주입할 의존성을 명시합니다.
          inject: [ConfigService],
        }),
      ],
      // AuthModule이 사용할 프로바이더(서비스, 전략 등)를 providers 배열에 포함합니다.
      providers: [JwtStrategy],
    })
    export class AuthModule {}  // AuthModule 클래스를 외부에 공개합니다.

     

    PassportModule.register({ defaultStrategy: 'jwt', session: false }) 코드에서 중요한 부분은 Passport 모듈의 설정입니다. 여기서 기본 전략을 JWT로 설정하고, 세션을 사용하지 않도록 설정하고 있습니다.

     

    JWT는 상태를 저장하지 않는 stateless한 인증 방식입니다. 따라서 각 요청이 서로 독립적으로 처리되며, 사용자의 로그인 상태를 서버에 저장하지 않습니다. 이것이 session: false로 설정되는 이유입니다.

     

    이 설정을 통해 JWT를 이용한 인증이 효과적으로 이뤄지면서, 애플리케이션이 사용자의 로그인 상태를 서버에 저장하지 않아도 된다는 장점이 있습니다. 이렇게 함으로써 서버는 각각의 요청을 독립적으로 처리하며, 더 효율적인 stateless한 구조를 가질 수 있습니다.


     

     

Designed by Tistory.