🐱 Nest.js 둜그인 μ„œλΉ„μŠ€ λ§Œλ“€μ–΄λ³΄κΈ° (1)

이쀀석·2021λ…„ 12μ›” 16일
0

nestjs

λͺ©λ‘ 보기
2/3
post-thumbnail

μ„œλ‘ 

μ €λ²ˆμ‹œκ°„μ— μ‹€ν–‰λ§Œ μ‹œμΌ°λŠ”λ° κ°‘μžκΈ° 둜그인 μ„œλΉ„μŠ€λ₯Ό λ§Œλ“ λ‹€κ³  ν•˜λ©΄ 비약이 λ„ˆλ¬΄ μ‹¬ν•˜λ‹€κ³  λŠλ‚„ 수 μžˆμ„ 것 κ°™λ‹€. ν•˜μ§€λ§Œ κ°œλ…μ μœΌλ‘œ μ„€λͺ…을 ν•˜λŠ”κ±°λ³΄λ‹€ λ°”λ‘œ κΈ°λŠ₯을 λ§Œλ“€λ©΄μ„œ 이야기 해보면 더 와닿고 이해가 μ‰¬μšΈ 것 κ°™λ‹€κ³  μƒκ°ν–ˆλ‹€.

λ”°λΌμ„œ JWT 토큰과 Passport 라이브러리λ₯Ό μ΄μš©ν•΄μ„œ 둜그인 μ„œλΉ„μŠ€λ₯Ό κ΅¬ν˜„ν•΄λ³΄λ©΄μ„œ Nest 에 λŒ€ν•΄μ„œ μ΅μˆ™ν•΄μ§ˆ 수 μžˆλŠ” μ‹œκ°„μ„ 가져보도둝 ν•˜μž

λ³Έλ‘ 

πŸ“ passport μ„€μΉ˜ν•˜κΈ°

μš°μ„  nest μ—μ„œ passport 라이브러리λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œ λͺ¨λ“ˆμ„ λ‹€μš΄λ‘œλ“œ λ°›μ•„μ•Ό λ˜λŠ”λ° λ‹€μŒκ³Ό 같이 ν”„λ‘œμ νŠΈ 루트 디렉토리 μœ„μΉ˜μ—μ„œ 터미널을 μ—΄κ³  λͺ…λ Ήμ–΄λ₯Ό μž…λ ₯ ν•  수 μžˆλ„λ‘ ν•œλ‹€.

$ npm install --save @nestjs/passport passport passport-local
$ npm install --save-dev @types/passport-local

κ°„λž΅ν•˜κ²Œ passport λΌμ΄λΈŒλŸ¬λ¦¬μ— λŒ€ν•΄μ„œ μ„€λͺ…ν•˜λ©΄ 인증 λ―Έλ“€μ›¨μ–΄λ‘œ node ν™˜κ²½μ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” λΌμ΄λΈŒλŸ¬λ¦¬μ΄λ‹€. λ”°λΌμ„œ μΈμ¦μ΄λΌλŠ” 관심사λ₯Ό λ”°λ‘œ λΆ„λ¦¬ν•˜κ²Œ 됨으둜써 (μΊ‘μŠν™”λœ passport 라이브러리λ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨) μ½”λ“œλ₯Ό κ°„κ²°ν•˜κ³  μœ μ§€λ³΄μˆ˜μ„±μ΄ 쒋은 μ½”λ“œλ₯Ό λ§Œλ“€ 수 μžˆλ„λ‘ λ„μ™€μ€λ‹ˆλ‹€.

μš°μ„  passport λ₯Ό μ„€μΉ˜ν–ˆμœΌλ‹ˆ 라이브러리λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•΄λ³΄λ„λ‘ ν•˜κ² λ‹€.

πŸ“ 둜그인 Controller, Service μƒμ„±ν•˜κΈ°

MVC νŒ¨ν„΄μ„ μ΄μš©ν•΄μ„œ μ½”λ“œλ₯Ό μž‘μ„±ν•΄λ³΄λ„λ‘ ν•  μ˜ˆμ •μ΄λ‹€.

Nest cliλ₯Ό μ΄μš©ν•œ μ½”λ“œ 생성 방법

그리고 이제 본격적으둜 Controller 와 Service μ½”λ“œλ₯Ό 생성해야 λ˜λŠ”λ° nest μ—μ„œλŠ” cli 둜 μ½”λ“œλ₯Ό 생성할 수 μžˆλ„λ‘ μ§€μ›ν•΄μ£ΌλŠ”λ°, cli λ₯Ό μ΄μš©ν•΄μ„œ 생성해보도둝 ν•  μ˜ˆμ •μ΄κ³  터미널을 켜고 루트 디렉토리 μœ„μΉ˜μ—μ„œ λ‹€μŒκ³Ό 같이 λͺ…λ Ήμ–΄λ₯Ό 적어쀀닀.

$ nest g module login
$ nest g controller login
$ nest g service login

μœ„μ™€ 같이 λͺ…λ Ήμ–΄λ₯Ό μž…λ ₯ν•˜λ©΄ login 폴더가 루트 디렉토리에 생성이 λ˜λ©΄μ„œ μ•ˆμ—λŠ” Module, Controller, Service μ½”λ“œκ°€ μƒμ„±λ˜κ³  Controller, Service 에 λŒ€ν•œ spec 이름이 뢙은 ν…ŒμŠ€νŠΈ μ½”λ“œκΉŒμ§€ 생성이 λœλ‹€.

그리고 λͺ…λ Ήμ–΄λ‘œ μ½”λ“œλ₯Ό 생성할 경우 λͺ¨λ“ˆ μ½”λ“œ 내뢀에 μžλ™μœΌλ‘œ Controller, Service 에 λŒ€ν•œ 섀정도 ν•΄μ£Όκ²Œ λ˜λ©΄μ„œ 개발자 νŽΈμ˜μ„±μ΄ 높아진닀.

Module μ½”λ“œμ— λŒ€ν•œ κ°„λž΅ μ„€λͺ…

// login.module.ts

import { Module } from '@nestjs/common';
import { LoginController } from './login.controller';
import { LoginService } from './login.service';

@Module({
  controllers: [LoginController],
  providers: [LoginService],
})
export class LoginModule {}

Module μ½”λ“œμ— controller, provider 에 μžλ™μœΌλ‘œ μΆ”κ°€κ°€ λœλ‹€.
(λ§Œμ•½ 직접 μ½”λ“œλ₯Ό μƒμ„±ν•˜λ©΄ μœ„μ™€ 같은 μž‘μ—…μ„ μˆ˜λ™μœΌλ‘œ ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.)

μ—¬κΈ°μ„œ Controller 와 Service μ½”λ“œλŠ” Spring 을 μ ‘ν•΄λ³΄μ•˜κ±°λ‚˜ MVC νŒ¨ν„΄μ— λŒ€ν•΄μ„œ κ³΅λΆ€ν•œ μ‚¬λžŒλ“€μ΄λΌλ©΄ μ™„λ²½ν•˜κ²ŒλŠ” λͺ°λΌλ„ μ–΄λ–€ λ‰˜μ•™μŠ€μΈμ§€ νŒŒμ•…μ€ 될 것이닀.

ν•˜μ§€λ§Œ nest μ—λŠ” Module μ½”λ“œκ°€ 또 μ‘΄μž¬ν•˜λŠ”λ°, μ‰½κ²Œ μ–˜κΈ°ν•˜λ©΄ @Module μ• λ…Έν…Œμ΄μ…˜μ— λͺ…μ‹œλ₯Ό ν•΄μ£Όμ–΄μ•Όλ§Œ λͺ…μ‹œλœ μ½”λ“œλ“€ 사이에 자유둭게 클래슀λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€. λ‹€μ‹œ λ§ν•˜λ©΄ μ˜μ‘΄μ„± μ£Όμž…μ„ ν•˜κΈ° μœ„ν•΄μ„œλŠ” Module μ½”λ“œμ˜ @Module μ• λ…Έν…Œμ΄μ…˜μ— 섀정에 λ§žμΆ°μ„œ 클래슀λ₯Ό λ„£μ–΄μ£Όμ–΄μ•Ό ν•©λ‹ˆλ‹€.

μ˜μ‘΄μ„± μ£Όμž…(Dependency Injection) 은 Spring μ—μ„œλ§Œ μ‚¬μš©λ˜λŠ” κ°œλ…μ΄ μ•„λ‹Œ λͺ¨λ“  ν”„λ‘œκ·Έλž˜λ°μ—μ„œ μ‚¬μš©λ  수 μžˆλŠ” λ””μžμΈ νŒ¨ν„΄μœΌλ‘œ nest μ—μ„œλ„ ν•΄λ‹Ή νŒ¨ν„΄μ„ μ΄μš©ν•΄μ„œ 객체λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.

λ”°λΌμ„œ @Module μ• λ…Έν…Œμ΄μ…˜ μ„€μ • 값듀은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • imports : λ‹€λ₯Έ λͺ¨λ“ˆμ—μ„œ exportν•œ λͺ¨λ“ˆμ„ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œ ν•΄λ‹Ή 뢀뢄에 μž‘μ„±ν•©λ‹ˆλ‹€.
  • controllers : ν•΄λ‹Ή λͺ¨λ“ˆμ—μ„œ μ‚¬μš©ν•  controller 클래슀λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.
  • providers: ν•΄λ‹Ή λͺ¨λ“ˆμ—μ„œ μ‚¬μš©ν•  service 클래슀λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.
  • exports : λ‹€λ₯Έ λͺ¨λ“ˆμ—μ„œ μ‚¬μš©ν•˜κΈ° μœ„ν•œ 클래슀λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

μ΄ν•΄ν•˜κΈ° μ‰½κ²Œ μž‘μ„±ν•œ λ‚΄μš©μœΌλ‘œ 보닀 μžμ„Έν•œ λ‚΄μš©μ€ ν•΄λ‹Ή 링크λ₯Ό μ°Έκ³ ν•΄μ£Όμ„Έμš”!

Controller ν΄λž˜μŠ€μ— λΌμš°ν„° μƒμ„±ν•˜κΈ°

이제 ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄ Controller 에 λΌμš°ν„° ν•˜λ‚˜λ₯Ό 생성해보도둝 ν•˜μž.

import { Controller, Get } from '@nestjs/common';

@Controller('login')
export class LoginController {
  @Get()
  login() {
    return 'login controller test!!';
  }
}

@Controller() : ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ Controller μž„μ„ λͺ…μ‹œ.
(κ΄„ν˜Έμ•ˆμ— 적인 ν…μŠ€νŠΈλŠ” URL 경둜λ₯Ό 의미, http://localhost:포트/login 으둜 ν˜ΈμΆœν•˜λ©΄ ν•΄λ‹Ή 클래슀둜 μš”μ²­μ΄ μ˜¨λ‹€.)

@Get() : login 으둜 μš”μ²­μ΄ 왔을 λ•Œ GET λ©”μ„œλ“œμΌ 경우 ν•΄λ‹Ή λ©”μ„œλ“œλ₯Ό μ‹€ν–‰μ‹œν‚¨λ‹€.
(GET http://localhost:포트/login μš”μ²­μΌ 경우 μ‹€ν–‰, @Get('test') 둜 μž‘μ„±ν•  경우 GET http://localhost:포트/login/test 둜 μš”μ²­μ΄ 와야지 login() λ©”μ„œλ“œκ°€ μ‹€ν–‰λœλ‹€.

그리고 포슀트맨이 있으면 포슀트맨으둜 싀행해도 λ˜μ§€λ§Œ GET μš”μ²­μ€ λΈŒλΌμš°μ €λ₯Ό μ΄μš©ν•΄μ„œ 해도 상관 μ—†λ‹€. λ”°λΌμ„œ μ½”λ“œλ₯Ό npm run start:dev λͺ…λ Ήμ–΄λ₯Ό μ΄μš©ν•΄μ„œ μ‹€ν–‰μ‹œν‚€κ³  자기의 ν¬νŠΈμ— 맞게 λΈŒλΌμš°μ €μ— μž…λ ₯ν•΄μ€€λ‹€. (ν•„μžλŠ” 8080 포트)

μœ„μ™€ 같이 login controller test!! ν…μŠ€νŠΈκ°€ 좜λ ₯ λ˜μ—ˆμœΌλ©΄ μ •μƒμ μœΌλ‘œ 잘 싀행이 된 것이닀.

μ—¬κΈ°μ„œ ν•œκ°€μ§€ κ°„κ³Όν•œ 뢀뢄이 μžˆλŠ”λ°, app.module.ts μ½”λ“œλ₯Ό 열어보면 둜그인과 κ΄€λ ¨λœ μ½”λ“œλ“€μ΄ μ•Œμ•„μ„œ 섀정에 μΆ”κ°€λœ 뢀뢄을 λ³Ό 수 μžˆλ‹€.

/*
 * controller, provider에 Login κ΄€λ ¨ 클래슀λ₯Ό 적지 μ•Šμ•„λ„
 * LoginModule이 import λ˜μ–΄ μžˆμ–΄μ„œ 싀행이 λœλ‹€...
 * exportλ₯Ό 해주지 μ•Šμ•„λ„ λ˜λŠ”κ±΄ 아직 잘 λͺ¨λ₯΄κ² λ‹€...
 */
@Module({
  imports: [ConfigModule.forRoot(), LoginModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

μ—¬κΈ°μ„œ Loginκ³Ό κ΄€λ ¨λœ λͺ¨λ“  클래슀λ₯Ό μ œκ±°ν•˜κ³  λ‹€μ‹œ http://localhost:포트/login 으둜 μ ‘κ·Όν•˜λ©΄ 404 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

κ·Έ μ΄μœ λŠ” κ°„λž΅ν•˜κ²Œ μ„€λͺ…ν•˜μžλ©΄ main.ts μ½”λ“œκ°€ μ‹€ν–‰λ˜λ©΄μ„œ NestApplication 이 μƒμ„±μ΄λ˜κ³  μ„œλ²„κ°€ 싀행이 λ˜λŠ”λ°, κ·Έ λ•Œ AppModule 이 λ“±λ‘λ˜λ©΄μ„œ μ‹œμž‘ν•œλ‹€. λ”°λΌμ„œ μš°λ¦¬κ°€ ν”„λ‘œμ νŠΈμ—μ„œ λͺ¨λ“ˆμ΄ 좔가될 λ•Œ λ§ˆλ‹€ AppModule 에 등둝을 ν•΄μ£Όμ–΄μ•Όμ§€λ§Œ μ •μƒμ μœΌλ‘œ μ½”λ“œλ₯Ό μ‚¬μš©ν•  수 있게 λœλ‹€.

npm run start:dev -> main.ts μ‹€ν–‰ -> AppModule 등둝 (AppModule에 λ“±λ‘λœ λͺ¨λ“œ μ½”λ“œκ°€ λ“±λ‘λœλ‹€.)

Spring μ—μ„œλŠ” @ComponentScanμ΄λΌλŠ” μ• λ…Έν…Œμ΄μ…˜μ— ν”„λ‘œμ νŠΈ μ‹œμž‘μ‹œ μŠ€μΊ”ν•˜κ³ μž ν•˜λŠ” νŒ¨ν‚€μ§€λͺ…을 적어주면 @Controller, @Service, @Repository, @Component λ“±μ˜ μ• λ…Έν…Œμ΄μ…˜μ΄ 뢙은 ν΄λž˜μŠ€λ“€μ„ μžλ™μœΌλ‘œ 빈 객체둜 λ“±λ‘ν•΄μ„œ μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•΄μ£ΌλŠ”λ°, 아직 λ‚΄κ°€ μ•„λŠ” λ°”λ‘œλŠ” nest μ—μ„œλŠ” 이 뢀뢄을 μˆ˜λ™μœΌλ‘œ AppModule 에 등둝해주어야 ν•˜λŠ” 것 κ°™λ‹€.

service μ½”λ“œ 생성 ν›„ controllerμ—μ„œ μ‚¬μš©ν•˜κΈ°

μ΄λ²ˆμ—λŠ” μ„œλΉ„μŠ€ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κ³  컨트둀러 μ½”λ“œμ—μ„œ μ‚¬μš© ν•  수 μžˆλŠ” 방법에 λŒ€ν•΄μ„œ μ•Œμ•„λ³΄μž.

μ»¨νŠΈλ‘€λŸ¬μ—μ„œλŠ” λΌμš°ν„° λΆ„κΈ° 처리만 μ§„ν–‰ν•˜κ³  본격적인 λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ€ μ„œλΉ„μŠ€ μ½”λ“œμ— μž‘μ„±ν•˜λŠ” 것이 MVC νŒ¨ν„΄μ˜ ν•œ 뢀뢄이닀. μš°μ„ μ€ ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•΄μ„œ ν…μŠ€νŠΈλ₯Ό λ°˜ν™˜ν•˜λŠ” μ„œλΉ„μŠ€ μ½”λ“œλ₯Ό 생성할 수 μžˆλ„λ‘ ν•œλ‹€.

import { Injectable } from '@nestjs/common';

@Injectable()
export class LoginService {
  login() {
    return 'test';
  }
}

@Injectable() : μ˜μ‘΄μ„± μ£Όμž…μ„ ν•  수 μžˆλŠ” 클래슀라고 λͺ…μ‹œν•˜λŠ” μ• λ…Έν…Œμ΄μ…˜μ΄λ‹€.

그리고 μ„œλΉ„μŠ€ μ½”λ“œλ₯Ό μ»¨νŠΈλ‘€λŸ¬μ—μ„œ λ‹€μŒκ³Ό 같이 ν˜ΈμΆœν•΄λ³΄λ„λ‘ ν•˜μž.

import { Controller, Get } from '@nestjs/common';
import { LoginService } from './login.service';

@Controller('login')
export class LoginController {
  constructor(private readonly loginService: LoginService) {}

  @Get()
  login() {
    return this.loginService.login();
  }
}

μžλ°” μ½”λ“œμ—μ„œ μ˜μ‘΄μ„± μ£Όμž…μ„ ν•  λ•Œ μƒμ„±μžμ—μ„œ μ£Όμž…ν•΄μ£ΌλŠ” 것과 λ§ˆμ°¬κ°€μ§€λ‘œ nest μ—μ„œλ„ μƒμ„±μžμ—μ„œ μ˜μ‘΄μ„± μ£Όμž…μ„ ν•΄μ£ΌλŠ”λ° constructor(private readonly loginService: LoginService) {} 처럼 λͺ…μ‹œν•΄μ£Όλ©΄ @Injectable() μ• λ…Έν…Œμ΄μ…˜μœΌλ‘œ 인해 μ˜μ‘΄μ„± μ£Όμž…μ΄ λ˜μ–΄μ„œ μ„œλΉ„μŠ€ μ½”λ“œλ₯Ό μ‚¬μš©ν•  수 있게 λœλ‹€.

μ½”λ“œλ₯Ό μ €μž₯ν•˜κ³  λ‹€μ‹œ μ‹€ν–‰μ‹œν‚€λ©΄ λ‹€μŒκ³Ό 같이 μ •μƒμ μœΌλ‘œ μ‹€ν–‰λœλ‹€. (npm start 둜 μ‹€ν–‰ν•˜μ‹  κ²½μš°μ—λŠ” μž¬μ‹œμž‘ ν•΄μ£Όμ…”μ•Ό ν•©λ‹ˆλ‹€.)

μ—¬κΈ°μ„œ login.module.ts μ½”λ“œμ—μ„œ provider 에 λͺ…μ‹œλœ loginService λ₯Ό μ‚­μ œν•˜κ³  μ‹€ν–‰ν•˜λ©΄ μ–΄λ–»κ²Œ 될까?

import { Module } from '@nestjs/common';
import { LoginController } from './login.controller';

@Module({
  controllers: [LoginController],
  providers: [], // loginService μ‚­μ œ
})
export class LoginModule {}

그러면 λ‹€μŒκ³Ό 같은 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

Error: Nest can't resolve dependencies of the LoginController (?). Please make sure that the argument LoginService at index [0] is available in the LoginModule context.

Potential solutions:
- If LoginService is a provider, is it part of the current LoginModule?
- If LoginService is exported from a separate @Module, is that module imported within LoginModule?
  @Module({

ν•΄μ„ν•˜μžλ©΄ LoginController μ½”λ“œμ—μ„œ μ˜μ‘΄μ„± μ£Όμž…μ„ ν•΄μ£Όκ³  싢은데 LoginModule 의 Provider 에 μ •μ˜λœ μ„œλΉ„μŠ€κ°€ μ—†λ‹€λŠ” μ˜λ―Έμ΄λ‹€.

μ•„κΉŒ μœ„μ—μ„œλ„ λ§ν–ˆλ“―μ΄ λͺ¨λ“ˆμ— μ •μ˜λœ ν΄λž˜μŠ€λ“€λΌλ¦¬ 자유둭게 클래슀λ₯Ό μ˜μ‘΄μ„± μ£Όμž…ν•΄μ„œ μ‚¬μš©ν•˜λ €λ©΄ λͺ¨λ“ˆ μ½”λ“œμ— 잘 μ •μ˜ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€.

κ²°λ‘ 

κ°„λž΅ν•˜κ²Œ μ„€λͺ…을 ν–ˆμŒμ—λ„ λΆˆκ΅¬ν•˜κ³  λ‚΄μš©μ΄ κΈΈμ–΄μ Έμ„œ 이번 ν¬μŠ€νŒ…μ€ μ—¬κΈ°μ„œ 마무리 ν•˜κ³ , λ‹€μŒ ν¬μŠ€νŒ…μ— μ΄μ–΄μ„œ 본격적으둜 Passport, jwt 라이브러리λ₯Ό μ‚¬μš©ν•˜κ³  둜그인 κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λŠ” λ‚΄μš©μ„ μž‘μ„±ν•΄λ³΄λ„λ‘ ν•˜λ„λ‘ ν•˜κ² λ‹€.

아직 곡뢀쀑이라 μ™„λ²½ν•˜κ²Œ μ•„λŠ” λ‚΄μš©μ΄ μ•„λ‹ˆκ³ , 잘λͺ» κ³΅λΆ€ν•΄μ„œ 잘λͺ» μ•Œκ³  μžˆλŠ” 뢀뢄듀도 μžˆμ„ 수 μžˆλŠ”λ° μ§€μ ν•΄μ£Όμ‹œλ©΄ μˆ˜μ • ν•  수 μžˆλ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

이만 총총.. πŸƒπŸ»β€β™€οΈ

profile
호주 μ›Œν™€μ€‘

0개의 λŒ“κΈ€