반응형

다음은 이 글의 동영상 강의 입니다.

https://youtu.be/4mjd5P7cZIA

https://youtu.be/NCqCfyQoNz8

 

Nest로 로그인 기능을 구현하기 위해 우선적으로 회원 가입을 만들어봅니다.

 

이 예제의 소스는 아래 사이트에서 받으실 수 있습니다.

https://github.com/CodeGearGit/nest-register-sample

 

GitHub - CodeGearGit/nest-register-sample

Contribute to CodeGearGit/nest-register-sample development by creating an account on GitHub.

github.com

 

USER 테이블의 구조는 다음과 같습니다.

auth 모듈을 만듭니다.

nest g module auth

auth/entity/user.entity.ts 를 만듭니다.

import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";

@Entity('user')
export class User{
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    username: string;
    
    @Column()
    password: string;
}

auth/user.repository.ts 를 만듭니다.

import { EntityRepository, Repository } from "typeorm";
import { User } from "./entity/user.entity";

@EntityRepository(User)
export class UserRepository extends Repository<User>{}

auth/dto/user.dto.ts 를 만듭니다.

export class UserDTO {
    username: string;
    password: string;
}

auth/user.servict.ts 를만듭니다.

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { FindOneOptions } from "typeorm";
import { UserDTO } from "./dto/user.dto";
import { UserRepository } from "./user.repository";

@Injectable()
export class UserService{
    constructor(@InjectRepository(UserRepository) private userRepository: UserRepository){}

    async findByFields(options: FindOneOptions<UserDTO>): Promise<UserDTO | undefined> {
        return await this.userRepository.findOne(options);
    }

    async save(userDTO: UserDTO): Promise<UserDTO | undefined> {
        return await this.userRepository.save(userDTO);
    }
}

auth/auth.service.ts 를 만듭니다.

(nest g service auth)

import { HttpException, HttpStatus, Injectable } from "@nestjs/common";
import { UserDTO } from "./dto/user.dto";
import { UserService } from "./user.service";

@Injectable()
export class AuthService {
    constructor(
        private userService: UserService
    ){}

    async registerNewUser(newUser: UserDTO): Promise<UserDTO> {
        let userFind: UserDTO = await this.userService.findByFields({ where: { username: newUser.username } });
        if(userFind){
            throw new HttpException('Username already used!', HttpStatus.BAD_REQUEST);
        }
        return this.userService.save(newUser);
    }
}

auth/auth.controller.ts를 만듭니다.

(nest g controller auth)

import { Body, Controller, Post, Req } from "@nestjs/common";
import { Response, Request } from 'express';
import { AuthService } from "./auth.service";
import { UserDTO } from "./dto/user.dto";

@Controller('api')
export class AuthController {
    constructor(private authService: AuthService){}

    @Post('/register')
    async registerAccount(@Req() req: Request, @Body() userDTO: UserDTO): Promise<any> {
        return await this.authService.registerNewUser(userDTO);
    }
}

auth/auth.module.ts를 아래와 같이 수정합니다.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserRepository } from './user.repository';
import { UserService } from './user.service';

@Module({
    imports: [TypeOrmModule.forFeature([UserRepository])],
    exports: [TypeOrmModule],
    controllers: [AuthController],
    providers: [AuthService, UserService]
  })
export class AuthModule {}

서버를 start 합니다.

npm run start:dev

postman에서 다음과 같이 테스트를 합니다.

POST http://localhost:3000/api/register
Body-raw-JSON
{
    "username": "codegear",
    "password": "1111"
}

실행 결과는 다음과 같습니다.

user 테이블에서도 다음과 같이 확인하실 수 있습니다.

 

반응형
반응형

이 글은 업데이트 되었습니다. 아래 링크를 참조하세요.

https://codegear.tistory.com/116

 

NestJS - 20. TypeORM (2023)

목차 TypeORM이란? NestJS에서 TypeORM 사용법 TypeORM이란? ORM은 Object-Relational Mapping의 약자입니다. 여기서 Object는 객체지향언어에서 말하는 객체를 의미합니다. Relational은 관계형 데이터베이스(Relational

codegear.tistory.com

----------------------------------------------------------

이 내용의 동영상 강의 2편입니다.

https://youtu.be/OTz22O8-PkE

https://youtu.be/t5XyFhe3sPA

 

Nest에서 db를 연결하여 개발을 진행해야할 경우, 좋은 선택지 중 하나가 TypeORM을 사용하는 것입니다.

 

TypeORM연결을 위해서는 DB가 설치 되어 있어야 합니다.

우리는 Docker를 이용해서 MySQL을 빠르게 설치하고 사용해보겠습니다.

MySQL 설치는 이전 글을 참고해 주세요.

https://codegear.tistory.com/66

 

MySQL 쉽게 설치하기 - with Docker

이 글의 동영상 강의입니다. https://youtu.be/5e0axgEP8EE 우선 아래 사이트에 가서 Docker를 설치합니다. https://www.docker.com/get-started Get Started with Docker | Docker Learn about the complete cont..

codegear.tistory.com

MySQL Workbench를 이용해서 test schema를 생성합니다.

CREATE SCHEMA test DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

 

이전까지 진행한 Nestjs 소스는 github에서 받으실 수 있습니다.

https://github.com/CodeGearGit/nest-cats

 

GitHub - CodeGearGit/nest-cats

Contribute to CodeGearGit/nest-cats development by creating an account on GitHub.

github.com

 

먼저 TypeORM을 설치합니다.

npm install --save @nestjs/typeorm typeorm mysql2

설치가 완료되면 app.module.ts에 TypeORM 모듈을 import 합니다.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [],
      synchronize: true,
    }),
    CatsModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

* 주의 synchronize: true는 운영에서는 사용하지 마세요. 

 

다음과 같이 cats/entity/cats.entity.ts 파일을 만듭니다.

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Cat {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  age: number;

  @Column()
  breed: string;

  @Column({ default: true })
  isActive: boolean;
}

app.module.ts에 cats.entity를 추가합니다.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsModule } from './cats/cats.module';
import { Cat } from './cats/entity/cats.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      entities: [Cat],
      synchronize: true,
    }),
    CatsModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

cats.module.ts에서 cats.entity를 사용하기 위해 다음과 같이 소스를 추가합니다.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { Cat } from './entity/cats.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Cat])],
  exports: [TypeOrmModule],
  controllers: [CatsController],
  providers: [CatsService]
})
export class CatsModule {}

cats.service.ts에 catsRepository를 생성하여 data를 핸들링할 수 있게 합니다.

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cat } from './entity/cats.entity';
import { getConnection } from "typeorm";

@Injectable()
export class CatsService {
    constructor(
        @InjectRepository(Cat)
        private catsRepository: Repository<Cat>,
      ) {}
    
      findAll(): Promise<Cat[]> {
        return this.catsRepository.find();
      }
    
      findOne(id: string): Promise<Cat> {
        return this.catsRepository.findOne(id);
      }

      async create(cat: Cat): Promise<void> {
        await this.catsRepository.save(cat);
      }
    
      async remove(id: number): Promise<void> {
        await this.catsRepository.delete(id);
      }

      async update(id: number, cat: Cat): Promise<void> {
        const existCat = await this.catsRepository.findOne(id);
        if(existCat){
            await getConnection()
                .createQueryBuilder()
                .update(Cat)
                .set({ 
                    name: cat.name, 
                    age: cat.age,
                    breed: cat.breed
                })
                .where("id = :id", { id })
                .execute();
        }
      }
}

cats.controller.ts를 다음과 같이 작성합니다.

import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
import { CatsService } from './cats.service';
import { Cat } from './entity/cats.entity';

@Controller('cats')
export class CatsController {
    constructor(private catsService: CatsService){};

    @Get()
    findAll(): Promise<Cat[]> {
        return this.catsService.findAll();
    }

    @Get(':id')
    findOne(@Param('id')id: string): string {
        return `This action returns a #${id} cat`;
    }

    @Post()
    create(@Body() cat: Cat){
        return this.catsService.create(cat);
    }

    @Put(':id')
    update(@Param('id')id: number, @Body() cat: Cat){
        this.catsService.update(id, cat);
        return `This action updates a #${id} cat`;
    }

    @Delete(':id')
    remove(@Param('id')id: number){
        this.catsService.remove(id);
        return `This action removes a #${id} cat`;
    }
}

이제 Postman을 이용해서 테스트를 합니다.

POST를 이용해 데이터를 생성합니다.

GET으로 조회를 하면 다음과 같습니다.

다시 POST 로 추가 데이터를 입력합니다.

다시 GET으로 전체 데이터를 조회합니다.

수정, 삭제등도 테스트 하시면 됩니다.

 

반응형
반응형

이 글의 동영상 강의입니다.

https://youtu.be/5e0axgEP8EE

 

우선 아래 사이트에 가서 Docker를 설치합니다.

https://www.docker.com/get-started

 

Get Started with Docker | Docker

Learn about the complete container solution provided by Docker. Find information for developers, IT operations, and business executives.

www.docker.com

 

다음은 프로젝트 루트 폴더에 다음과 같이 docker-compose.yml을 생성합니다.

version: '3'
services:
  local-db:
    image: library/mysql:5.7
    container_name: local-db
    restart: always
    ports:
      - 13306:3306
    environment:
      MYSQL_ROOT_PASSWORD: root
      TZ: Asia/Seoul
    volumes:
      - ./db/mysql/data:/var/lib/mysql
      - ./db/mysql/init:/docker-entrypoint-initdb.d
    platform: linux/x86_64

 아래 스크립트를 실행합니다.

docker-compose up -d

설치가 완료되면 Docker Dashboard에 아래와 같이 mysql 이 추가됩니다.

우측의 start 버튼을 클릭하여 서버를 실행합니다.

MySQL Workbench를 이용해서 db에 접속합니다.

접속 정보는 다음과 같습니다.

 

이후로는 MySQL Schema와 사용자를 생성하시면 됩니다.

 

반응형
반응형

맥북에서 무료 FTP 프로그램을 찾아보던 중 CyberDuck이란 프로그램이 있어 설치를 해 보았습니다.

AppStore에는 유료 버전이 올라와 있기 때문에 아래 URL을 통해 설치하였습니다.

https://cyberduck.softonic.kr/mac/download

 

Cyberduck

우아하고 효율적인 무료 FTP 클라이언트

cyberduck.softonic.kr

 

설치 후 실행을 하면 다음과 같은 화면이 나옵니다.

여기서 상단의 새연결을 클릭하면 연결 설정을 할 수 있습니다.

제일 상단의 Selectbox를 클릭하면 접속 형태를 변경할 수 있습니다.

SFTP를 선택할 경우 아래와 같이  SSH 개인키를 설정하는 부분이 추가적으로 보입니다.

AWS의 경우 .pem 형식의 개인키를 지정하시면 됩니다.

완료된 후에 연결 버튼을 클릭하면 접속이 완료됩니다.

반응형
반응형

다음은 이 글의 동영상 강의입니다.

https://youtu.be/MEaTJTyHVnc

다음은 실습 동영상입니다.

https://youtu.be/J8HWEoZSC-Y

 

* 이 글은 nestjs 공식 홈페이지를 참조하여 작성하였습니다.

https://docs.nestjs.kr/middleware

 

네스트JS 한국어 매뉴얼 사이트

네스트JS 한국, 네스트JS Korea 한국어 매뉴얼

docs.nestjs.kr

Nest의 미들웨어란?

미들웨어는 라우터 핸들러 이전에 호출되는 함수입니다.

Middlewar

다시말해 위 그림에서와 같이 클라이언트의 요청을 라우터 핸들러가 받기 전에 가로채 다른 작업을 처리할 수 있습니다.

이를 응용하면 여러가지 공통적으로 처리해야 하는 부분들의 처리를 중복 없이 개발할 수 있습니다.

예를 들면 다음과 같은 것들을 할 수 있습니다.

  • 모든 코드가 공통으로 실행해야 하는 인증, 로깅등을 처리할 수 있습니다.
  • 요청과 응답 객체를 변경할 수 있습니다.
  • 요청의 validation을 체크하여 오류 처리를 할 수 있습니다.

 

Nest의 미들웨어 사용법

Nest에서 미들웨는 다음과 같이 사용할 수 있습니다.

  • @Injectable 데코레이터를 사용합니다.
  • NestMiddleware 인터페이스를 implements 해서 사용합니다.
  • Module의 class 내부에 configure를 사용하여 선언합니다. 이때 NestModule 인터페이스를 implements 합니다.

다음은 Log를 위한 Middleware 예제입니다.

지금까지의 실습 소스는 아래 git 주소에서 받으시면 됩니다.

https://github.com/CodeGearGit/nest-cats.git

 

GitHub - CodeGearGit/nest-cats

Contribute to CodeGearGit/nest-cats development by creating an account on GitHub.

github.com

logger.middleware.ts

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log('Request...');
    next();
  }
}

app.module.ts

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats');
  }
}

다음과 같이 라우트를 특정하여 사용할 수도 있습니다.

.forRoutes({ path: 'cats', method: RequestMethod.GET });

다음과 같이 패턴 기반의 라우팅도 지원됩니다.

.forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });

 

Middleware 관리를 위한 내장 모듈 - MiddlewareConsumer

MiddlewareConsumer라는 헬퍼클래스를 사용하면 여러 스타일로 미들웨어를 설정할 수 있습니다.

forRoutes() 메소드는 단일 문자열, 여러 문자열, RouteInfo 객체, 컨트롤러 클래스 및 여러 컨트롤러 클래스를 사용할 수 있습니다.

다음은 Controller의 사용 예입니다.

app.module.ts

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller.ts';

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(CatsController);
  }
}

apply 메서드는 여러 미들웨어를 지정할 수 있습니다.

 

라우트 예외처리

다음과 같이 exclude() 메소드로 라우트를 제외할 수 있습니다.

consumer
  .apply(LoggerMiddleware)
  .exclude(
    { path: 'cats', method: RequestMethod.GET },
    { path: 'cats', method: RequestMethod.POST },
    'cats/(.*)',
  )
  .forRoutes(CatsController);

Functional middleware

지금까지 만든 Class 미들웨어는 아래와 같이 간단한 Funtional middleware로 변경할 수 있습니다.

logger.middleware.ts

import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request...`);
  next();
};

위의 logger functrion은 Module에서 다음과 같이 사용할 수 있습니다.

app.module.ts

consumer
  .apply(logger)
  .forRoutes(CatsController);

 

여러개 미들웨어 사용

다음과 같이 apply() 메서드에 여러개의 미들웨어를 사용할 수 있습니다,

consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);

Global 미들웨어

모든 경로에서 사용하는 미들웨어는 INestApplication 인스턴스에서 제공하는 user() 메서드를 사용할 수 있습니다.

const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
  • 단, Global 미들웨어에서 DI 컨테이너에 액세스할 수 없습니다.
  • app.use()에서 미들웨어를 사용할 때는 대신 functional middleware를 사용하고 있습니다.

반응형

+ Recent posts