반응형

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/hJMxGJoNsjQ

https://youtu.be/QBTgOnKMo9s

https://youtu.be/e6HpJqXj1mk

https://youtu.be/d_-Q98-hKs8

 

사이트에서 특정 권한자만 접근이 가능하도록 제어해야할 필요가 있습니다.

예를 들면 회원관리 메뉴의 경우는 모든 사람이 접근하면 안되고, 관리자만 접근 가능해야 하는 경우입니다.

이를 구현하기 위해서는 사용자의 권한(Role)을 관리해야 합니다.

 

권한 관리를 위해서는 다음 사항들이 수행되어야 합니다.

- 권한 구분 : admin, user등으로 권한을 설정합니다.

- 사용자 별로 어떤 권한이 있는지를 db에 저장하고 있어야 합니다.

- 메뉴 접근시 사용자의 권한을 체크하여 사용 가능 여부에 따라 접근을 허가 또는 거부합니다.

 

db에 사용자의 권한을 관리하기 위한 테이블 user_autority의 구조는 다음과 같습니다.

다음을 SQL을 이용하여 user_authority 테이블을 만듭니다.

- user_id는 user 테이블의 id 값에 매핑되는 값입니다.

- authority_name은 다음 두가지 type을 갖도록 ENUM으로 만듭니다.

- authority_name : USER, ADMIN

- 초기 데이터를 아래와 같이 입력합니다.

CREATE TABLE IF NOT EXISTS `test`.`user_authority` (
  `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  `user_id` BIGINT NOT NULL,
  `authority_name` ENUM('ROLE_USER', 'ROLE_ADMIN') NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB

insert into `test`.`user_authority` (user_id, authority_name) values (1,'ROLE_USER');
insert into `test`.`user_authority` (user_id, authority_name) values (1,'ROLE_ADMIN');
insert into `test`.`user_authority` (user_id, authority_name) values (2,'ROLE_USER');

- user 테이블에 아래와 같이 id에 값이 있어야 합니다.

 

user-authority entity를 만듭니다.

- 한 사용자가 여러개의 authority를 갖을 수 있으므로 user_authority 테이블에서는 ManyToOne으로 Join을 합니다.

- auth/entity/user-authority.entity.ts

import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm";
import { User } from "./user.entity";

@Entity('user_authority')
export class UserAuthority {
    @PrimaryColumn()
    id: number;

    @Column('int',{name: 'user_id'})
    userId: number;

    @Column('varchar',{name: 'authority_name'})
    authorityName: string;

    @ManyToOne(type=>User, user=>user.authorities)
    @JoinColumn({name: 'user_id'})
    user: User;
}

user-entity를 다음과 같이 수정합니다.

- 한 사용자가 여러개의 authority를 갖을 수 있으므로 user 테이블에서는 OneToMany Join을 합니다.

- auth/entity/user.entity.ts

import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { UserAuthority } from "./user-authority.entity";

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

    @Column()
    username: string;

    @Column()
    password: string;

    @OneToMany(()=>UserAuthority, userAuthority => userAuthority.user, {eager: true})
    authorities?: any[];
}

authority repository를 만듭니다.

- auth/repository/user-autority.repository.ts

import { EntityRepository, Repository } from "typeorm";
import { UserAuthority } from "../entity/user-authority.entity";

@EntityRepository(UserAuthority)
export class UserAuthorityRepository extends Repository<UserAuthority> {}

App Module의 TypeOrmModule.forRoot에 UserAuthority를 추가합니다.

- app.module.ts

TypeOrmModule.forRoot({
  type: 'mysql',
  host: 'localhost',
  port: 13306,
  username: 'root',
  password: 'root',
  database: 'test',
  entities: [Cat, User, Authority],
  synchronize: true,
}),

Auth Module에 UserAuthorityRepository를 추가합니다.

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './security/passport.jwt.strategy';
import { UserRepository } from './repository/user.repository';
import { UserService } from './user.service';
import { UserAuthorityRepository } from './repository/user-authority.repository';

@Module({
  imports: [
    TypeOrmModule.forFeature([UserRepository, UserAuthorityRepository]),
    JwtModule.register({
      secret: 'SECRET_KEY',
      signOptions: {expiresIn: '300s'},
    }),
    PassportModule
  ],
  exports: [TypeOrmModule],
  controllers: [AuthController],
  providers: [AuthService, UserService, JwtStrategy]
})
export class AuthModule {}

payload에도 UserAutyrity를 추가합니다.

- auth/security/payload.interface.ts

export interface Payload {
    id: number;
    username: string;
    authorities?: any[];
}

 

Role Type을 다음과 같이 만듭니다.

- auth/role-type.ts

export enum RoleType {
    USER = 'ROLE_USER',
    ADMIN = 'ROLE_ADMIN',
    ANONYMOUS = 'ROLE_ANONYMOUS',
}

Role Decorator를 다음과 같이 만듭니다.

- auth/decorator/role.decorator.ts

import { SetMetadata } from '@nestjs/common';
import { RoleType } from '../role-type';

export const Roles = (...roles: RoleType[]): any => SetMetadata('roles', roles);

 

Role Guard를 다음과 같이 만듭니다.

- auth/security/roles.guard.ts

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { User } from '../entity/user.entity';

@Injectable()
export class RolesGuard implements CanActivate {
    constructor(private readonly reflector: Reflector) {}

    canActivate(context: ExecutionContext): boolean {
        const roles = this.reflector.get<string[]>('roles', context.getHandler());

        if (!roles) {
            return true;
        }

        const request = context.switchToHttp().getRequest();
        const user = request.user as User;

        return user && user.authorities && user.authorities.some(role => roles.includes(role));
    }
}

RolesGuard에서 사용된 Reflector는 Application Runtime시에 정의된 Metadata를 이용해 type을 알아내는데 사용합니다.

 

AuthService의 payload에 authority를 추가합니다.

- authorities는 배열 형태로 RoleType이 들어갑니다.

- auth/auth.service.ts

async validateUser(userDTO: UserDTO): Promise<{accessToken: string} | undefined> {
    let userFind: User = await this.userService.findByFields({
        where: { username: userDTO.username }
    });
    if(!userFind) {
        throw new UnauthorizedException();
    }
    const validatePassword = await bcrypt.compare(userDTO.password, userFind.password);
    if(!validatePassword) {
        throw new UnauthorizedException();
    }

    this.convertInAuthorities(userFind);

    const payload: Payload = { id: userFind.id, username: userFind.username, authorities: userFind.authorities };
    return {
        accessToken: this.jwtService.sign(payload)
    };
}

async tokenValidateUser(payload: Payload): Promise<User| undefined> {
    const userFind = await this.userService.findByFields({
        where: { id: payload.id }
    });
    this.flatAuthorities(userFind);
    return userFind;
}

private flatAuthorities(user: any): User {
    if (user && user.authorities) {
        const authorities: string[] = [];
        user.authorities.forEach(authority => authorities.push(authority.authorityName));
        user.authorities = authorities;
    }
    return user;
}

private convertInAuthorities(user: any): User {
    if (user && user.authorities) {
        const authorities: any[] = [];
        user.authorities.forEach(authority => authorities.push({ name: authority.authorityName }));
        user.authorities = authorities;
    }
    return user;
}

테스트를 위해 Controller에 핸들러를 하나 추가합니다.

- auth/auth.controller.ts

@Get('/admin-role')
@UseGuards(AuthGuard, RolesGuard)
@Roles(RoleType.ADMIN)
adminRole(@Req() req: Request): any {
    const user: any = req.user;
    return user;
}

Application을 실행합니다.

npm run start:dev

 

ADMIN 권한이 있는 아이디로 테스트 

- 관리자 계정으로 로그인 후 토큰값을 받아 옵니다.

accessToken을 이용해서 관리자 접근 권한을 체크합니다.

ADMIN 권한이 없는 아이디로 테스트

권한이 없을 경우 아래와 가티 에러가 발생됩니다.

이상으로 Nest에서 권한을 체크하는 방법에 대해서 알아보았습니다.

반응형
반응형

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

https://youtu.be/1upOqWW2zyU

 

Nestjs에서 JWT 토큰을 이용한 인증시에는 Guard를 사용합니다.

Guard는 라우팅 전에 작동하는 일종의 미들웨어입니다.

 

이전까지의 코드가 들어있는 샘플소스는 다음 github 저장소에서 받으실 수 있습니다.

https://github.com/CodeGearGit/nestjs-05-jwt

 

GitHub - CodeGearGit/nestjs-05-jwt

Contribute to CodeGearGit/nestjs-05-jwt development by creating an account on GitHub.

github.com

 

패키지 설치

프로젝트에 다음과 같이 패키지를 설치합니다.

npm i --save @nestjs/passport @types/passport-jwt

 

코드 작성

JWT 검증을 위해 JwtStrategy를 만듭니다.

auth/security/passport.jwt.strategy.ts

import { Injectable, UnauthorizedException } from "@nestjs/common";
import { ExtractJwt, Strategy, VerifiedCallback } from 'passport-jwt';
import { PassportStrategy } from "@nestjs/passport";
import { AuthService } from "../auth.service";
import { Payload } from "./payload.interface";

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy){
    constructor(private authService: AuthService){
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: true,
            secretOrKey: 'SECRET_KEY',
        })
    }

    async validate(payload: Payload, done: VerifiedCallback): Promise<any> {
        const user = await this.authService.tokenValidateUser(payload);
        if (!user) {
            return done(new UnauthorizedException({ message: 'user does not exist' }), false);
        }

        return done(null, user);
    }
}

auth/auth.service.ts 에 tokenValidateUser를 추가합니다.

async tokenValidateUser(payload: Payload): Promise<UserDTO | undefined> {
    return await this.userService.findByFields({
        where: { id: payload.id }
    });
}

auth/auth.module.ts

- imports에 PassportModule 을 추가하고,

- providers에 JwtStrategy 를 추가합니다.

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './security/passport.jwt.strategy';
import { UserRepository } from './user.repository';
import { UserService } from './user.service';

@Module({
  imports: [
    TypeOrmModule.forFeature([UserRepository]),
    JwtModule.register({
      secret: 'SECRET_KEY',
      signOptions: {expiresIn: '300s'},
    }),
    PassportModule
  ],
  exports: [TypeOrmModule],
  controllers: [AuthController],
  providers: [AuthService, UserService, JwtStrategy]
})
export class AuthModule {}

auth/security/auth.guard.ts를 만듭니다.

import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard as NestAuthGuard } from '@nestjs/passport';

@Injectable()
export class AuthGuard extends NestAuthGuard('jwt') {
    canActivate(context: ExecutionContext): any {
        return super.canActivate(context);
    }
}

auth/auth.controller.ts에 아래와 같이 라우터를 하나 추가합니다.

@Get('/authenticate')
@UseGuards(AuthGuard)
isAuthenticated(@Req() req: Request): any { 
    const user: any = req.user;
    return user;
}

 

테스트

jwt 인증 모듈의 개발이 완료되었으므로 Postman을 이용해서 테스트를 해봅니다.

토큰이 없을 경우 다음과 같이 Unauthorized 값이 리턴됩니다.

로그인 호출을 통해 토큰 값을 받아옵니다.

토큰을 이용해 authenticate를 다시 호출합니다.

- Headers 에 다음 값을 입력합니다.

   KEY: Authorization

   VALUE : 로그인에서 받아온 토큰값

다음과 같이 결과가 나오는걸 보실 수 있습니다.

이상으로 JWT 인증하는 법에 대해 알아보았습니다.

 

최종 완료된 소스는 아래 URL에서 확인하시면 됩니다.

https://github.com/CodeGearGit/nestjs-06-jwt-auth

 

GitHub - CodeGearGit/nestjs-06-jwt-auth

Contribute to CodeGearGit/nestjs-06-jwt-auth development by creating an account on GitHub.

github.com

 

반응형
반응형

 

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

https://youtu.be/lwJLrv6zj04

 

 

* Nestjs 개발을 위해서는 Nodejs가 설치되어 있어야 합니다.

* Editor는 Visual Studio Code(VSCode)를 사용했습니다.

 

NestJS 프로젝트를 만들기 위해서는 Nestjs CLI(Command Line Interface)를 설치해야 합니다.

  • 아래와 같이 Nestjs cli를 설치합니다.
npm i -g @nestjs/cli
  • 설치가 완료되면 아래와 같이 CLI를 이용해 프로젝트를 만들 수 있습니다.
nest new project-name

 

  • 생성된 프로젝트의 구조는 다음과 같습니다.
    • dist : typescript 코드를 컴파일해서 빌드한 .js 파일이 저장되는 폴더
    • node_moduels : package.json에 정의된 패키지 모듈이 설치되는 폴더
    • src : typescript 코드가 저장되는 사용자 소스 폴더
    • test : test 소스가 저장되는 폴더

nest project 구조

  • 자동생성된 프로젝트 소스 폴더의 구조는 다음과 같습니다.
    • app.controller.spec.ts : test용 소스 - controller를 테스트합니다.
    • app.controller.ts : controller 소스 - client의 request을 처리하고, response를 보냅니다. 
    • app.module.ts : module 소스 - 모듈을 정의합니다.(controller와 service 정의)
    • app.service.ts : service 소스 - controller가 요청한 비즈니스 로직을 처리합니다.
    • main.ts : 프로젝트 시작점(Entry Point)

src 폴더

  • main.ts
    • 애플리케이션 Entry Point 즉, 시작 소스입니다.
    • 서버를 스타트하고 포트 설정, CORS 등을 정의합니다.

  • package.json
    • 프로젝트의 설정 파일입니다.
    • 사용하는 모듈, 서버 실행 및 빌드등의 스크립트를 정의합니다.

 

프로젝트 실행

  • VSCode 상단메뉴에서 터미널-새터미널을 클릭합니다.
  • package.json에 정의된 실행 스크립트를 실행합니다.
npm start
  • 브라우저에서 다음 URL을 입력합니다.
http://localhost:3000
  • 브라우저에 다음과 같이 내용이 출력됩니다.

 

반응형
반응형

nestjs homepage

* 본 문서는 nestjs 홈페이지(https://docs.nestjs.com/)를 참조하였습니다.

* nestjs 한글 홈페이지 주소 : https://docs.nestjs.kr/

 

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

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

docs.nestjs.kr

 

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

https://youtu.be/QniEcuwvLSU

 

Nest(NestJS)의 특징

  • Nest는 nodejs 서버 애플리케이션을 구축하기 위한 프레임워크입니다.
  • Nest는 서버 애플리케이션 개발시 필요한 많은 부분들을 기본적으로 제공하고 있습니다.
  • Nest는 Express 기반으로 만들어졌습니다.
  • Nest는Typescript를 사용합니다. javascript도 코딩이 가능합니다.
  • Nest는 다음 요소를 포함합니다.
    • OOP - 객체지향 프로그래밍
    • FP - Functional 프로그래밍
    • FRP - Functional React 프로그래밍
  • Nest는 외부 모듈을 자유롭게 이용할 수 있습니다.
  • Nest는 unit 테스트와 e2e 테스트를 할 수 있는 툴을 제공합니다.

Nest의 탄생배경

  • 최근 Nodejs로 인해 javascript를 이용한 풀스택(클라이언트+서버) 개발이 활발해졌습니다.
  • javascript라는 한가지 언어로 개발을 하기 때문에 생산성을 향상시키고 빠른 개발이 가능해졌습니다.
  • 그러나 Node.js의 높은 자유도로 인해 Architecture 구성이 어렵고 효과적이지 못했습니다.
  • 이를 해결하기 위해 Angular의 아키텍처 사상을 기반으로 Nest가 만들어졌습니다.

 

Nest를 사용하면 좋은 점

  • Nest는 Java의 Spring과 같이 규칙을 제공합니다. 
    이로 인해 개발자들이 아키텍처의 구성에 대해 고민해야 할 부분이 많이 줄어듭니다.
  • 기본적으로 제공하는 라우팅, 보안등의 기능이 많이 탑재되어 있어 편리합니다.
  • 외부모듈을 통한 확장이 얼마든지 가능합니다.
  • Java+Spring 사용자라면 아키텍처 구조가 비슷해서 쉽게 배울 수 있습니다.
  • Angular 사용자라면 기본적인 사용법이 동일하므로 쉽게 배울 수 있습니다.

반응형
반응형

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

https://youtu.be/wb4qGjfocwI

 

Express에서 라우팅을 별도의 파일로 분리할 수 있습니다.

 

한개의 파일로 분리하는 경우

  • 다음과 같이 router.js 파일을 만듭니다.
const express = require('express');
var router = express.Router();

router.get('/', function (req, res) {
  res.send('Express Server!');
});

module.exports = router;
  • index.js 파일에서 router.js를 사용하도록 코드를 추가합니다.
const express = require('express');
const app = express();
 
app.listen(3000);

app.use(require('./router'));

 

여러개의 파일로 분리하는 경우

  • user-router.js 파일을 만들고 다음 코드를 추가합니다.
const express = require('express');
var router = express.Router();

router.get('/', function (req, res) {
  res.send('This is user-router!');
});

module.exports = router;
  • board-router.js 파일을 만들고 다음 코드를 추가합니다.
const express = require('express');
var router = express.Router();

router.get('/', function (req, res) {
  res.send('This is board-router!');
});

module.exports = router;
  • index.js를 다음과 같이 수정합니다.
const express = require('express');
const app = express();
 
app.listen(3000);

app.use('/', require('./router'));
app.use('/user', require('./user-router'));
app.use('/board', require('./board-router'));

 

반응형
반응형

NodeJS의 내부모듈인 http module을 사용해서 서버를 만들때 복잡하고 불편한 점들이 있습니다.

이를 개선한 NodeJS의 확장모듈인 express를 사용하는 방법을 알아봅니다.

 

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

https://youtu.be/Ej-N6c9JCbY

 

Express의 특징

  • Express는 모바일 및 웹용 애플리케이션 제작을 위한 일련의 기능을 제공하는 프레임워크입니다.
  • Express는 수많은 HTTP 유틸리티 메소드와 미들웨어를 통해 쉽고 빠르게 강력한 API를 제작할 수 있습니다.
  • Express는 빠른 성능을 제공합니다.
  • 많은 NodeJS 프레임워크들이 Express를 기반으로 하고 있습니다.

참조 : https://expressjs.com/ko

 

Express - Node.js 웹 애플리케이션 프레임워크

Node.js를 위한 빠르고 개방적인 간결한 웹 프레임워크 $ npm install express --save

expressjs.com

 

 

프로젝트 만들기

  • 프로젝트 폴더를 생성한 후 visual studio code를 실행합니다.
  • vscode의 메뉴에서 파일-폴더열기를 선택한 후 프로젝트폴더를 선택합니다.
  • 메뉴 터미널-새터미널을 선택한 후 다음을 입력하여 Nodejs프로젝트를 생성합니다.
npm init -y

 

express 설치

  • 우선 express 설치를 위해 npmjs.com에서 express를 검색합니다.
    리스트 중 제일 상단의 express (작성자:dougwilson)를 선택합니다.

npmjs.com

  • express의 설치 방법 사용예제 등을 확인합니다.

 

  • visual studio code의 터미널에서 다음 코드를 이용하여 expres를 설치합니다.
npm i express

 

프로그램 개발

  • vscode에서 index.js파일을 만들고 express 예제 코드를 아래와 같이 입력합니다.
const express = require('express')
const app = express()
 
app.get('/', function (req, res) {
  res.send('Hello World')
})
 
app.listen(3000)

 

서버 실행 및 확인

  • vscode의 터미널에서 다음 코드를 입력합니다.
node index
  • 브라우저를 실행하고 주소창에 다음 url을 입력합니다.
http://localhost:3000
  • 브라우저 결과는 다음과 같습니다.

이상으로 express의 설치와 실행방법에 대해 알아보았습니다.

반응형
반응형

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

https://youtu.be/560LIgIiU8g

다음은 동영상 강의 2편입니다.

https://youtu.be/LoZhQXs85r8

 

이 글은 NodeJS 공식 홈페이지의 다음 글을 참고하여 작성하였습니다.

https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/

 

HTTP 트랜잭션 해부 | Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

 

NodeJS로 서버를 개발하려고 하려면 HTTP의 처리 과정을 이해하여야 합니다.

NodeJS에서 제공하는 http 모듈의 사용법과 같이 배워보도록 하겠습니다.

 

웹서버 만들기

  • 우선 웹서버 객체를 만듭니다.
const http = require('http');

const server = http.createServer((request, response) => {
});
  • 이때 사용된 createServer는 server 객체를 생성하고 발생한 이벤트를 전달하는 역할을 하므로 다음과 같이 변경할 수 있습니다.
const server = http.createServer();
server.listen(8080);
server.on('listening', () => {
   console.log(`listen on 8080 !!!`);
});

 

Request 처리하기

  • 생성한 서버에서 request를 받아 처리할 수 있습니다.
server.on('request', (request, response) => {
   console.log(request.headers);
   response.write('<h1>Node Server</h1>');
   response.end();
});
  • request에는 url, method, header 등의 정보가 포함됩니다.
server.on('request', (request, response) => {
  const { headers, method, url } = request;
  const userAgent = headers['user-agent'];
  console.log(`method : ${method}`);
  console.log(`url : ${url }`);
  console.log(`userAgent : ${userAgent}`);

  response.write('<h1>Node Server</h1>');
  response.end();
});
  • post나 put의 request에서 stream으로 전달되는 data와 end를 이용하면 body를 읽어올 수 있습니다.
  let body = [];
  request.on('error', (err) => {
    console.error(err);
  }).on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString();
    console.log(body);

    response.write('<h1>Node Server</h1>');
    response.end();
  });
  • 테스트를 위해서 postman에서 다음과 같이 변경 합니다.
    • request method를 GET에서 POST로 바꿉니다.
    • headers에 다음 값을 추가합니다.
      • Content-Type: application/json

  • Body탭에서 다음 항목을 변경합니다.
    • body type을 none에서 raw로 변경합니다.
    • data type을 JSON으로 변경합니다.

  • Send를 클릭하면 다음과 같은 결과를 확인할 수 있습니다.

  • request에서 error 이벤트를 받아 오류를 처리할 수 있습니다.
request.on('error', (err) => {
  // 여기서 `stderr`에 오류 메시지와 스택 트레이스를 출력합니다.
  console.error(err.stack);
});

 

Response 사용하기

  • statusCode를 이용해 상태 코드를 전달할 수 있습니다.
    지정하지 않을 경우의 상태코드는 항상 200입니다.
response.statusCode = 404; // 클라이언트에게 리소스를 찾을 수 없다고 알려줍니다.
  • setHeader를 이용해 header를 설정할 수 있습니다.
response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'bacon');
  • 아래와 같이 명시적으로 header를 정의 할 수 있습니다.
response.writeHead(200, {
  'Content-Type': 'application/json',
  'X-Powered-By': 'bacon'
});
  • write와 end를 이용해 response body를 만들 수 있습니다.
response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();
// 또는
response.end('<html><body><h1>Hello, World!</h1></body></html>');
  • respone에서 error event는 다음과 같이 처리할 수 있습니다.
response.on('error', (err) => {
  console.error(err);
});
  • 지금 까지의 내용을 정리하면 다음과 같이 코드를 작성할 수 있습니다.
const http = require('http');

http.createServer((request, response) => {
  const { headers, method, url } = request;
  let body = [];
  request.on('error', (err) => {
    console.error(err);
  }).on('data', (chunk) => {
    body.push(chunk);
  }).on('end', () => {
    body = Buffer.concat(body).toString();

    response.on('error', (err) => {
      console.error(err);
    });

    response.writeHead(200, {'Content-Type': 'application/json'})
    const responseBody = { headers, method, url, body };
    response.end(JSON.stringify(responseBody))
  });
}).listen(8080);

에코 서버 만들기

  • 다음과 같이 /echo로 요청하는 post 메소드에 대해서 응답하는 서버를 만들 수 있습니다.
const http = require('http');

http.createServer((request, response) => {
  request.on('error', (err) => {
    console.error(err);
    response.statusCode = 400;
    response.end();
  });
  response.on('error', (err) => {
    console.error(err);
  });
  if (request.method === 'POST' && request.url === '/echo') {
    request.pipe(response);
  } else {
    response.statusCode = 404;
    response.end();
  }
}).listen(8080);

이상으로 nodejs의 http 요청을 처리하는 기본적인 방법에 대해 알아보았습니다.

 

반응형
반응형

이 글의 동영상 강의

https://youtu.be/eTsgL0tPhy4

 

다음 시간부터는 NodeJS 서버개발에 대해 배울 예정입니다.

서버개발시 테스트를 위해서는 frontend 개발이 필요합니다.

이때 frontend 개발 없이 서버를 테스트 할 수 있는 tool들을 사용하게 되는데,

그 중에서 가장 많이 사용하는 Postman을 설치하고 테스트 해보겠습니다.

 

postman

Postman 설치

  • 구글에서 postman을 조회합니다.

postman

  • 첫번째 조회된 postman.com 사이트를 클릭합니다.
    첫 페이지의 Download the desktop app에서 자신의 OS에 맞춰 다운로드하고 설치를 진행합니다.

postman download

  • 설치가 완료되면 아래와 같이 postman이 실행됩니다.

postman desktop app

테스트용 Nodejs 서버 만들기

  • nodejs 프로젝트를 만들고 index.js에 다음 코드를 작성합니다.
const http = require('http');

http.createServer((request, response) => {
  response.write("<h1>NodeJS Test Server!</h1>");
  response.end();
}).listen(8080);
  • 다음 명령으로 서버를 start합니다.
node index

Postman으로 요청하기

  • postman 첫화면에서 "+" 탭을 클릭해서 새로운 요청을 만듭니다.

new request

  • "http://localhost:8080"으로 요청을 작성하고 Send 버튼을 클릭합니다.
    body에 아래와 같이 결과값이 나오면 정상적으로 요청이 처리된 것입니다.

  • 아래 Response Body 에서 "Preview" 탭을 클릭하면 브라우저의 결과를 볼 수 있습니다.

이상으로 postman을 이용해서 nodejs의 서버를 테스트하는 방법을 확인해 보았습니다.

 

다음 시간에는 nodejs의 http에 대해 자세히 알아보고,

테스트를 할때 postman의 다양한 사용법에 대해서도 알아보도록 하겠습니다.

 

반응형
반응형

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

https://youtu.be/sD7-xrF7XR4

 
NodeJS 프로젝트를 진행하다 보면 다음과 같은 상황들이 발생합니다.
A프로젝트는 노드 버전 12.13.0을 사용한다.
B프로젝트는 노드 버전 16.13.0을 사용한다.

 

즉, NodeJS 버전을 바꿔가면서 작업을 해야 하는 상황이 생기는 것입니다.

프로젝트를 변경할 때마다 NodeJS를 매번 새롭게 설치하기는 어렵습니다.

이때 NodeJS 버전을 쉽게 변경할 수 있는 툴이 NVM입니다.

 

NVM 설치하기

검색 사이트에서 NVM 또는 NVM windows를 검색하여 설치합니다.

https://github.com/coreybutler/nvm-windows

 

GitHub - coreybutler/nvm-windows: A node.js version management utility for Windows. Ironically written in Go.

A node.js version management utility for Windows. Ironically written in Go. - GitHub - coreybutler/nvm-windows: A node.js version management utility for Windows. Ironically written in Go.

github.com

 

 

설치가 완료되면 cmd 또는 powershell에서 다음 명령을 실행할 수 있습니다.

  • 설치된 NodeJS 목록 확인하기
nvm ls

  • nvm install을 사용해서 새로운 버전의 NodeJS 추가할 수 있습니다.
    nvm install {버전번호} (nodejs의 특정 버전 설치)
    nvm install latest (최신버전의 nodejs 설치)
    nvm install lts (nodejs LTS 버전 설치)
nvm install 12.13.0
nvm install 16.13.0
nvm install latest
nvm install lts
  • NodeJS 버전 변경하기
nvm use 12.13.0

 

윈도우용 NVM 오류 해결 방법

윈도우용 NVM을 사용하면 "Exit 5..." 라는 오류가 종종 나옵니다.

이때 cmd나 파워쉘을 관리자 권한으로 실행해서 nvm use를 사용하시면 됩니다.

 

그래도 안될때는 다음 방법을 사용하면 오류를 해결하실 수 있습니다.

  • NVM 위치 변경하기
nvm root D:\nodejs-root\nvm
  • NVM 기본 설치 위치에서 다음 파일들을 복사해서 새로운 NVM 폴더에 넣어줍니다.
    기본 설치 위치 = C:\사용자\{사용자ID}\AppData\Roaming\nvm
elevate.cmd
elevate.vbs
  • CMD 또는 PowerShell을 관리자 권한으로 실행하고, nvm을 사용합니다.
nvm use 12.13.0

 

NVM을 사용하면 NodeJS의 버전을 손쉽게 변경하여 사용할 수 있습니다.

 

반응형

+ Recent posts