반응형

M1 맥북의 기본쉘은 다음과 같이 확인할 수 있습니다.

echo $SHELL

결과 값으로는 다음과 같이 나옵니다.

/bin/sh

기본쉘을 zsh로 변경하려면 다음 명령을 사용하면 됩니다.

sudo chsh -s /bin/zsh

다시 기본쉘을 확인합니다.

echo $SHELL

결과는 다음과 같습니다.

반응형
반응형

 

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

https://youtu.be/Unkbarfk1-M

 

로그인시 jwt token을 client의 쿠키에 저장하고, 로그아웃시에 삭제하는 방법을 알아보겠습니다.

 

우선 기존 login 처리 controller에서 쿠키를 저장할 수 있도록 소스를 수정합니다.

- res.cookie를 이용해서 jwt 토큰 값을 전달합니다.

- res.cookie의 인자는 (key, value, option) 으로 구성됩니다.

- option에는 httpOnly는 브라우저에서 cookie를 이용할 수 없게 합니다. 따라서 XSS 등의 보안을 강화시킬 수 있습니다.

- maxAge는 쿠키의 유효기간을 설정할 수 있습니다.

 

src/auth/auth.controller.ts

@Post('/login')
async login(@Body() userDTO: UserDTO, @Res() res: Response): Promise<any> {
    const jwt = await this.authService.validateUser(userDTO);
    res.setHeader('Authorization', 'Bearer '+jwt.accessToken);
    res.cookie('jwt',jwt.accessToken,{
        httpOnly: true,
        maxAge: 24 * 60 * 60 * 1000 //1 day
    });
    return res.send({
        message: 'success'
    });
}

postman에서 login을 호출하면 다음과 같은 결과가 나옵니다.

- url = http://localhost:3000/auth/login

하단 결과 탭에 Cookie를 선택하면 다음과 같이 쿠키값이 들어있는 것을 확인할 수 있습니다.

이제 클라이언트에서 요청이 올때 이 쿠키 정보도 같이 보내게 됩니다.

이를 서버에서 처리하기 위해 cookie-parser 라는 패키지를 설치합니다.

npm i cookie-parser @types/cookie-parser

이제 다음과 같이 쿠키분석을 할 수 있도록 main.ts에 쿠키 파서를 추가합니다.

- app.use(cookieParser())를 추가함으로서 쿠키파서를 사용할 수 있습니다.

src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as cookieParser from 'cookie-parser';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(cookieParser());
  await app.listen(3000);
}
bootstrap();

쿠키값을 잘 읽어 오는지 확인하기 위해 컨트롤러를 하나 만듭니다.

- 여기서는 단순히 쿠키의 jwt 값을 읽어서 리턴해줍니다.

src/auth/auth.controller.ts

@Get('/cookies')
getCookies(@Req() req: Request, @Res() res: Response): any {
    const jwt =  req.cookies['jwt'];
    return res.send(jwt);
}

postman으로 호출하면 다음과 같은 결과를 확인할 수 있습니다.

- url = http://localhost:3000/auth/cookies

이제 로그아웃시에 쿠키를 삭제하는 컨트롤러를 추가합니다.

- 쿠키의 'jwt'값을 삭제합니다.

- 유효기간을 '0'으로 설정합니다.

src/auth/auth.controller.ts

@Post('/logout')
logout(@Req() req: Request, @Res() res: Response): any {
    res.cookie('jwt', '', {
        maxAge: 0
    })
    return res.send({
        message: 'success'
    })
}

postman에서 테스트하면 결과는 다음과 같습니다.

- url = http://localhost:3000/auth/logout

다음과 같이 Cookies 탭에 값이 없어진것을 확인할 수 있습니다.

이상으로 Nestjs에서 쿠키를 다루는 방법이었습니다.

반응형
반응형

AWS의 Route53에서 서브 도메인을 생성하는 방법입니다.

기존에 www.mycodegear.com이라는 도메인이 있을 경우, blog.mycodegear.com과 같은 서브 도메인을 추가할 수 있습니다.

 

- AWS 콘솔에 접속 후 Route53으로 이동합니다.

- 호스팅영역을 클릭합니다.

- 서브도메인을 추가할 도메인 이름을 클릭합니다.

- 레코드 생성을 클릭합니다.

- 레코드 이름에 서브도메인의 이름을 입력합니다. (blog.mycodegear.com의 경우 blog만 입력)

- 레코드 유형은 CNAME을 선택합니다.

- 값은 AWS 리소스의 Alias값을 입력합니다.

- TTL은 default 값인 300초로 합니다.

- 라우팅 정책은 default 값인 단순 라우팅으로 합니다.

 

이렇게 하고 레코드 생성을 클릭하면 작성한 레코드가 추가되고,

생성된 domain을 브라우저에 입력하면 연결을 확인할 수 있습니다.

반응형
반응형

Nestjs에서 기본적으로 제공하는 ValidationPipe를 이용하면 요청 Parameter의 유효성을 쉽게 체크하고 처리할 수 있습니다.

예를 들면 필수 입력 값의 경우 @IsNotEmpty를 사용하는 것만으로 유효성 체크가 끝이 납니다.

 

사용자 등록시 아이디와 패스워드를 필수 값으로 처리하는 방법에 대해 알아보겠습니다.

이전 예제에서 사용자 등록시 Input Parameter는 아래의 UserDTO를 사용하였습니다.

src/auth/dto/user.dto.ts

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

이 경우에 username을 빈값으로 보내게 되어도 다음과 같이 회원 가입이 됩니다.

db에는 다음과 같이 id 3번 값이 공백으로 들어가게 됩니다.

공백이 입력되는 것을 막는 ValidationPipe를 사용해 보겠습니다.

 

우선 class-validator 패키지를 설치합니다.

npm i --save class-validator

UserDTO에서 "@IsNotEmpty()" 데코레이터를 추가합니다.

src/auth/dto/user.dto.ts

import { IsNotEmpty } from "class-validator";

export class UserDTO {
    @IsNotEmpty()
    username: string;
    @IsNotEmpty()
    password: string;
}

 

회원가입 라우팅 메소드에 "@UsePipes(ValidationPipe)"를 추가합니다.

src/auth/auth.controller.ts

import { Body, Controller, Get, Post, Req, Res, UseGuards, UsePipes, ValidationPipe } from "@nestjs/common";
...
@Post('/register')
@UsePipes(ValidationPipe)
async registerAccount(@Req() req: Request, @Body() userDTO: UserDTO): Promise<any> {
    return await this.authService.registerUser(userDTO);
}
...

 

이제 다시 위와 동일하게 username을 공백으로 API를 호출합니다.

* 이때 Validation 체크가 되지 않을 경우 "npm run build"를 한번 실행 후에 하시면 됩니다.

결과는 400 에러가 나오고 , username은 반드시 입력해야 한다는 메시지가 나옵니다.

 

class-validator의 데코레이터는 다음 사이트에가면 확인하실 수 있습니다.

https://github.com/typestack/class-validator#manual-validation

 

GitHub - typestack/class-validator: Decorator-based property validation for classes.

Decorator-based property validation for classes. Contribute to typestack/class-validator development by creating an account on GitHub.

github.com

 

반응형
반응형

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

https://youtu.be/hgilx-qB76o

 

AppModule에서 설정했던 TypeORM 설정을 외부 파일로 분리하면 관리가 좀 더 편해집니다.

그리고 Entity 파일도 동적으로 읽어오게 하면 Entity가 생길때마다 추가해야하는 일을 줄일 수 있습니다.

위 2가지 방법으로 기존 소스를 변경해 보겠습니다.

 

기존 AppModule에 TypeOrm은 아래와 같이 설정하였습니다.

src/app.module.ts

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';
import { AuthModule } from './auth/auth.module';

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

이를 다음과 같이 외부 설정 파일로 분리할 수 있습니다.

src/orm.config.ts

import { TypeOrmModuleOptions } from '@nestjs/typeorm';

function ormConfig(): TypeOrmModuleOptions {
	const commonConf = {
        SYNCRONIZE: false,
        ENTITIES: [__dirname + '/domain/*.entity{.ts,.js}'],
        MIGRATIONS: [__dirname + '/migrations/**/*{.ts,.js}'],
        CLI: {
            migrationsDir: 'src/migrations',
        },
        MIGRATIONS_RUN: false,
    };

    const ormconfig: TypeOrmModuleOptions = {
        name: 'default',
        type: 'mysql',
        database: 'test',
        host: 'localhost',
        port: 13306,
        username: 'root',
        password: 'root',
        logging: true,
        synchronize: commonConf.SYNCRONIZE,
        entities: commonConf.ENTITIES,
        migrations: commonConf.MIGRATIONS,
        cli: commonConf.CLI,
        migrationsRun: commonConf.MIGRATIONS_RUN,
    };
    
    return ormconfig;
}

export { ormConfig };

Entity 파일들은 동적으로 읽어오기 위해 src/domain 폴더로 옮겨줍니다.

 

이렇게 설정 파일이 만들어지면 AppModule에서는 다음과 같이 변경하면 됩니다.

src/app.module.ts

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

@Module({
  imports: [
    TypeOrmModule.forRootAsync({ useFactory: ormConfig }),
    CatsModule,
    AuthModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

 

이렇게 해서 좀 더 깔끔한 소스가 되었습니다.^^

반응형

+ Recent posts