본문 바로가기
내일배움 강의/강의-NestJS

5주차

by GREEN나무 2025. 1. 27.
728x90

인증(Authentication)
구글/네이버/카카오/로컬 로그인 인증 주로 사용됨

JWT를 통한 인증
어떤값을 나에게 주면 너를 OOO으로 인정할게

jwt 발급 구현

___환경설정__________

__nestjs_user폴더의 상위폴더에서 설치__________________________

nest new nestjs_user
cd nestjs_user

@nestjs/mapped-types, class-validator 패키지를 설치
npm i @nestjs/mapped-types class-validator

lodash 패키지를 설치
npm i lodash @types/lodash

________________________________________
Visual Studio Code로 게시판 프로젝트 열기
code .

__nestjs_user폴더에서 설치__________________________

npm i @nestjs/config @nestjs/jwt @nestjs/passport passport passport-jwt @types/passport-jwt typeorm @nestjs/typeorm mysql2 multer bcrypt @types/bcrypt class-validator class-transformer multer @types/multer papaparse @types/papaparse joi typeorm-naming-str @nestjs/cache-manager cache-manager
npm install mysql2 # 이게 설치가 안됬었음.. 왜죠? 위에 있는데?


user 뼈대 만들기
nest g resource user #REST API   Y


tsconfig.json에 추가
  "esModuleInterop": true // 추가. ES6 모듈 사양을 준수하여 CommonJS 모듈을 가져오기


prettier 설치
npm install --save-dev prettier

.prettierrc파일 추가
{
  "singleQuote": true,
  "trailingComma": "all",
  "semi": true,               
  "tabWidth": 2,               
  "useTabs": false,         
  "printWidth": 80,            
  "bracketSpacing": true,
  "jsxBracketSameLine": false, 
  "arrowParens": "always"      
}

package.json에 추가(자동으로 추가되지만 확인해보자)
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
___________________________________________________________________________

___nestjs_board/src 폴더 안에서__________
nestjs_board프로젝트의 ./src 폴더로 이동
cd src 

게시판 뼈대 한 번에 완성하기  nest g resource 폴더이름
nest g resource post # REST API, Y

결과
  PS V:\Sparta\memoNote\github\D1\dailyCoding\NESTJS\3W> cd src
  PS V:\Sparta\memoNote\github\D1\dailyCoding\NESTJS\3W\src> nest g resource post
  ? What transport layer do you use? REST API
  ? Would you like to generate CRUD entry points? Yes
  CREATE post/post.controller.ts (917 bytes)
  CREATE post/post.controller.spec.ts (576 bytes)
  CREATE post/post.module.ts (250 bytes)
  CREATE post/post.service.ts (633 bytes)
  CREATE post/post.service.spec.ts (464 bytes)
  CREATE post/dto/create-post.dto.ts (31 bytes)
  CREATE post/dto/update-post.dto.ts (173 bytes)
  CREATE post/entities/post.entity.ts (22 bytes)

빌드
npm run build

실행
npm run start

이렇게 뜨면 정상작동
PS V:\Sparta\memoNote\github\D1\dailyCoding\NESTJS\3W\nestjs_board\src> npm run start

> nestjs_board@0.0.1 start
> nest start

[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [NestFactory] Starting Nest application...
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [InstanceLoader] AppModule dependencies initialized +6ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [InstanceLoader] PostModule dependencies initialized +0ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RoutesResolver] AppController {/}: +3ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RoutesResolver] PostController {/post}: +0ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RouterExplorer] Mapped {/post, POST} route +1ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RouterExplorer] Mapped {/post, GET} route +0ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RouterExplorer] Mapped {/post/:id, GET} route +1ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RouterExplorer] Mapped {/post/:id, PATCH} route +0ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [RouterExplorer] Mapped {/post/:id, DELETE} route +1ms
[Nest] 23560  - 2025. 01. 21. 오후 3:31:03     LOG [NestApplication] Nest application successfully started +1ms

______________________________________




__ JWT 검증 구현______________________________________
클라이언트가 헤더에 Authorization 필드로 Bearer {JWT} 를 보내면 
AuthMiddleware는 JWT를 파싱하여 특정 유저임을 파악할 수 있다.

AuthMiddleware는 미들웨어로서, 직접적으로 NestJS의 DI 시스템에 포함되지 않는다.
 JwtService와 같은 서비스를 사용하려면, 해당 서비스가 포함된 모듈을 AppModule에 import해야 한한다.
 
 이 경우, JwtModule을 AppModule에 import하여 JwtService를 AuthMiddleware에서 사용할 수 있도록 했다
    AppModule에 import를 하면 UserService에서도 JwtModule을 사용할 수 있다.

--test----------------
GET을 제외한 나머지 API에는 Headers에 Content-Type: application/json을 꼭 넣기



__테스트______________

_________
회원가입
req
  body json
  {
    "userId": "test_ID",
    "password": "test_PW"
  }

res
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNzM3NjI4NDU2fQ.LfheKReAVfgMk8CpXzPXMG129WVoa-uS8NVUUTIxRpM

________________
로그인
req
  body json
  {
    "userId": "test_ID",
    "password": "test_PW"
  }

res
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNzM3NjI4NDkxfQ.bt43YFkgzMXcPMaEqYOxPto2sIsRNStKHlg5lHh2EFM

______________
회원체크
GET http://localhost:3000/user/check
req
헤더에 로그인에서 받은 토큰 넣기
Authentication : Bearer 발급받은토큰

res
{유저 정보: {"id":1,"iat":1737628491}}




_______________________________________________
_________인가__________________________________
_______________________________________________

데코레이터
클래스나 함수와 같은 곳에 메타데이터를 추가하는 방법을 제공하는 선언 방법
코드에 추가적인 정보를 제공하여 실행 시점에 코드의 동작 방법이 제공되는 것
 타데이터들로 Nest.js 프레임워크 자체가 실행 시점에 이 함수를 어떻게 호출해야 하는지 알 수 있다

컨트롤러에서 @Get데코레이터가 붙은 함수 : 함수가 HTTP GET 요청을 처리해야 함을 나타냄


커스텀 데코레이터
 - 우리가 원하는 대로 데코레이터의 동작을 정의할 수 있다
 - 코드에 추가적인 로직을 삽입하거나 특정 정보를 추출하거나 변경할 수 있다

 인가구현시 커스텀 데코레이터
 - e.g. 유저를 식별하는 데코레이터
    - 요청에서 사용자 정보를 추출하고 이를 라우트 핸들러에 전달할 수 있다
- e.g2. 유저의 접근을 제어하는 데코레이터
    - 사용자의 역할을 기반으로 특정 엔드포인트에 대한 접근을 제어할 수 있다

예시 - createParamDecorator
import { createParamDecorator, ExecutionContext } from "@nestjs/common";

export const UserInfo = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    if (request.user) {
      return request.user;
    }
    return null;
  }
);

createParamDecorator 함수를 이용하여 새로운 커스텀 데코레이터를 만들 수 있다.
createParamDecorator함수는 2개의 인자를 가진다.
  data: 데코레이터의 인자로 전달될 수 있는 데이터
  ctx: 현재 요청의 ExecutionContext(실행 컨텍스트) = Nest.js의 실행 컨텍스트
        HTTP 요청과 관련된 모든 정보를 포함함

const request = ctx.switchToHttp().getRequest();
  - AuthMiddleware : HTTP 요청이 라우트 핸들러에 도달하기 전에 실행된다
                     그 과정에서 req.user에 사용자 정보가 저장되어있다.
  - 커스텀 데코레이터 UserInfo에서 ExecutionContext 타입인 ctx 변수를 이용해 현재의 HTTP 요청 객체(req)에 접근할 수 있으며 이를 통해 req.user에도 접근 할 수 있게 된다
  - 즉, ctx.switchToHttp().getRequest()라는 메소드를 통해 req에 접근을 하는 것
  - ctx.switchToHttp().getResponse() : 응답 객체에 접근하는 메소드 → 즉, HTTP 응답을 조작할 수 있다


커스텀 데코레이터 적용 예시
import { Controller, Get } from '@nestjs/common';
import { CurrentUser } from './current-user.decorator';

@Controller('user')
export class UserController {
  @Get()
  getProfile(@UserInfo() user: any) {
    return user;
  }
}

___________________________
__Guard____________________
Guard는 Nest.js에서 특정 라우트에 대한 접근을 제어하는 역할
  ex : 인증된 사용자만 특정 라우트에 접근할 수 있도록 하거나, 
       관리자만 특정 기능을 사용할 수 있도록 할 때
       
Guard 작동 방식
- Guard는 `canActivate` 메서드를 구현하는 클래스로 구성
- `canActivate` 메서드는 라우트 접근을 허용할지 또는 거부할지 결정함.

Guard 생성 방법
nest g d 데코레이터 이름
nest g guard 데코레이터이름


Guard 코드분석
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';

@Injectable()
// LevelGuard라는 이름의 Guard를 생성
export class LevelGuard implements CanActivate {
  // ExecutionContext : 실행컨텍스트에서 html의 request객체를 가져올 수 있다
  //  canActivate 메서드에서 HTTP 요청 객체를 통해 사용자 정보를 가져오기
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();// 요청 객체 가져오기
    const user = request.user;  // user 객체는 { level: number } 형태라고 가정합니다.
    
    // 사용자의 레벨을 확인하여 level이 1인 경우(운영자)에만 접근을 허용하고, 
    //  그렇지 않은 경우 ForbiddenException을 발생시켜 접근을 거부
    // level === 1  :  이넘을 쓰지는 않지만 운영자라고 취급
    if (user && user.level === 1) {
      return true;
    }
    // 접근금지
    throw new ForbiddenException('접근이 금지되었습니다!');
  }
}
- Guard는 Nest.js의 요소 중 하나이기 때문에 커맨드로 직접 생성할 수 있다.


Guard를 컨트롤러에 적용

import { Controller, Get, UseGuards } from '@nestjs/common';
import { LevelGuard } from './level.guard';

// admin(운영자)만 AdminController의 라우트에 접근할 수 있다
@Controller('admin')
// UseGuards를 컨트롤러 위에 세우기 = 전역적으로 적용하겠다
@UseGuards(LevelGuard)
// LevelGuard는 AdminController에 적용됨. 모든 라우트는 이제 LevelGuard에 의해 보호된다.
export class AdminController {
  @Get('dashboard') // 전역적으로 사용하지 않고 함수마다 사용할 수 있음
  getDashboard() {
    return { message: 'Admin Dashboard' };
  }
}

커스텀 데코레이터와 Guard를 활용하면 인가 기능을 구현할 수 있다(6주차 더 참고하기)


NestJS에서 IoC, DI, AOP는 애플리케이션 구조와 설계의 핵심적인 개념으로, 유지보수성과 확장성을 높이는 데 중요한 역할을 함
IoC (Inversion of Control): 객체 생성과 관리의 제어권을 프레임워크에 위임.
DI (Dependency Injection): 필요한 의존성을 외부에서 주입받아 사용. @Injectable() 데코레이터를 사용
AOP (Aspect-Oriented Programming): 핵심 로직과 부가적인 관심사를 분리하여 깔끔한 코드 구조 유지.

AOP
 여러 부분에 걸쳐서 반복되는 공통 관심사를 분리하고 중앙에서 관리할 수 있게 하는 프로그래밍 기법
 코드의 모듈성을 향상시키고 중복을 줄인다.

 JavaScript에서는 프록시 객체나 헬퍼 객체들로 이러한 개념을 구현할 수 있다.
  프록시 객체는 JavaScript 내장 객체
    다른 객체의 기본 동작을 사용자 정의 동작으로 수정할 수 있게 함
      로깅이나 인증 및 에러 처리 등
  헬퍼 객체는 특정 작업을 수행하는 메서드만 입맞에 맞게 정의를 하고 이를 통해 코드의 재사용성을 높일 수 있다
    공통 기능을 모듈화하고 이를 여러 부분에서 호출

AOP 개념을 구현하기
인터셉터(가로체다) : AOP 개념을 구현하는 핵심 요소. 특정 작업을 수행하기 전이나 후에 추가 로직을 실행할 수 있는 코드 블록
  - HTTP 요청과 응답을 처리할 때 특히 유용.
  - 주로 로깅, 에러 처리, 데이터 변환 및 인증과 같은 공통 관심사를 처리하는 데 사용

인터셉터 구현
  어떠한 요소를 만들기 전에는 보통 
    1) Nest.js에서 제공하는 인터페이스를 구현하거나 
    2) 명령어를 통해 요소를 만드는 절차가 꼭 있다
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';

@Injectable()// 의존성 주입
// 인터셉터를 만들기 위해서는 NestInterceptor 인터페이스를 구현하는 것이 필수
export class LoggingInterceptor implements NestInterceptor {
  // intercept라는 메소드를 통해 요청을 가로챈 후 next.handle()을 호출
  //  = 요청을 처리하고 응답을 반환하는 컨트롤러 메소드로 이동을 한다는 뜻 
  // 즉, 요청이 처리된 이후임을 뜻
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = Date.now();

    return next
      .handle()  // next.handle()
      // pipe()라는 함수로 요청 처리 파이프라인의 다음단계로 이동을 한 후 
      // tap()이라는 연산자 내에서 응답이 처리된 시간을 로그로 찍는다.
      .pipe(tap(() => console.log(`완료에 걸린 시간: ${Date.now() - now}ms`)));
  }
}


_____________________________________________
_____캐싱_____________________________________

자주 변하지 않는 데이터에 동일한 요청이 지속적으로 들어오는 경우 저장해드고 필여할 때때

Nest.js에서는 `cache-manager`와 연계를 하여 캐싱 기능을 사용할 수 있다
npm i @nestjs/cache-manager cache-manager

적용하기
 app.module.ts
 post.service.ts
 
______________________________________________

 

 

 

 

새 폴더.zip
0.21MB

'내일배움 강의 > 강의-NestJS' 카테고리의 다른 글

4주차  (0) 2025.01.22
3주차 실습  (0) 2025.01.21
1주차  (1) 2025.01.19