0018.1 Nest.js ๐ฑ
- 0018 Javascript โ๏ธ
- ๋ ธ๋ง๋ ์ฝ๋ nest.js
- NestJS๋ก ๋ฐฐ์ฐ๋ ๋ฐฑ์๋ ํ๋ก๊ทธ๋๋ฐ
- ๊ณต์๋ฌธ์
- nestjs and postgresql - CRUD tutorial
README#
NestJs ํ์ต์ 2023-11-10~2023-11-11 ์ดํ๊ฐ ์งํํ๊ธฐ๋ก ํ๋ค. ๊ป๋ฐ๊ธฐ๋ง ์กฐ๊ธ ํ๋๋ค๋ ๋๋์ผ๋ก ๊ฐ๊ณ ๋๋จธ์ง๋ ์ง์ ๊ตฌํํ๋ฉด์ ์ฑ์๋ฃ์ด๋ณด์.
Daily Dump#
2023-11-13#
Overview#
- NestJS ์ค์น๋ฒ:
npm i -g @nestjs/cli
nest new project-name
nest start
- ํต์ฌ ํ์ผ ๊ตฌ์กฐ
app.controller.ts
: ์ปจํธ๋กค๋ฌ๋ ๋ผ์ฐํฐ์app.controller.spec.ts
: spec์ด๋ผ๋ ์ด๋ฆ์ ์ ๋ํ ์คํธ๋ฅผ ์๋ฏธํจ.app.serivce.ts
: ์๋น์ค๋ ์ปจํธ๋กค๋ฌ์ ์ํด ํธ์ถ๋๋ ๋ค์ํ ์ฝ๋ฐฑ๋ค์ ๋ด์๋๋ ํ์ผapp.module.ts
: ํ๋ก์ ํธ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ๊ณณmain.ts
: ์ํธ๋ฆฌํ์ผ,app
์์ฑํจ
- ๋น ๋ฅด๊ฒ ์ปดํ์ผํ๊ณ ์คํ์ํค๋ ค๋ฉด...
npm run start -- -b swc
- hot reloading์ ์์ฒด์ ์ผ๋ก ์ง์ํ๋ค :
npm run start:dev
- CRUD ๊ฐ๋ฅํ ๋ชจ๋ (controller, module, service, dto, entity)์ ๋ง๋๋ ๋ช
๋ น์ด๋
nest g res <resource-name>
, ๋ค์ ์ธ ๋ช ๋ น์ด๋ฅผ ์ผ๊ด์ ์ผ๋ก ์ํํ๋๋ฏ.nest g controller
nest g service
nest g module
Controllers#
- request & response๋ฅผ ์ฒ๋ฆฌํ๋ ๋ ์ด์ด.
- ์ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ง๋๋ ๋ช
๋ น์ด๋
nest g controller <name>
์ด๋ค. - ํด๋์ค ์ด๋
ธํ
์ด์
์ผ๋ก
@Controller(<path>)
๊ฐ ๋ฌ๋ ค์๋ค. - ๋ฉ์๋๋ค์ด ๊ฐ๊ฐ์ ์๋ํฌ์ธํธ๋ก ์์ฉํ๊ธฐ ์ํด์
@GET([subpath])
์ ๊ฐ์ HTTP ๋ฉ์๋ ์ด๋ฆ์ ์ด๋ ธํ ์ด์ ์ ๋ฌ์์ผ ํ๋ค. - ์ฑ๊ณต์ ์ผ๋ก ์๋ต์ ๋ฐํํด์ผํ ๋ ๊ธฐ๋ณธ๊ฐ์ 200์ด๊ณ , POST์ผ ๊ฒฝ์ฐ 201์ด๋ค. ์ด๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ๊ธฐ ์ํด์
@HttpCode(<number>)
์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํด์ผ ํ๋ค.- ์๋ฌ status๋ฅผ ๋ฐํํ ๊ฒฝ์ฐ ์์ธ๋ฅผ throwํ๋ผ๋๋ฐ?
- ์๋ฌด status๋ ๋ฐํํ๊ณ ์ถ์๋ express์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ผ๊ณ ..
-
- Path ์ธ์๋
@Get(:id)
์ด๋ ธํ ์ด์ ์ผ๋ก ์ ์ํ๊ณ ํ์ฑ๋ ์ธ์๋ ๋ฉ์๋ ์ธ์๋ก ๋ค์ด์จ๋ค.
@Get(':id') fineOne(@Param() params: any): string { return param.id + 'cat'; }
- request ์ธ๋ถ์ฌํญ์ ์๊ธฐ ์ํด์ ๋ฉ์๋ ํ๋ผ๋ฉํฐ ์์
@Req()
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋ถ์ด๋ผ๊ณ !
import { Request } from 'express'; @Get() fineOne(@Req() request: Request): string { return request.body; }
- Path ์ธ์๋
-
์ด๋ ๊ฒ ๋ฉ์๋ ํ๋ผ๋ฉํฐ๋ก ๋ฃ์ ์ ์๋ ๋ฐ์ฝ๋ ์ดํฐ๋ค๋ก๋
@Res
,@Next
,@Session
,@Param
,@Body
,@Query
,@Headers
๋ฑ์ด ์๊ณ ๋ค์ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ์์ค.
@Param
#
๋์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋ Path์ธ์. /cat/1
์์์ 1
๊ณผ ๊ฐ์ ์์ฒญ์ ์ฒ๋ฆฌํ๋๋ฐ ์ฌ์ฉํ ์ ์๋ค. ๋งค๊ฐ ๋ณ์๊ฐ ์๋ ๊ฒฝ๋ก๋ ์ ์ ๊ฒฝ๋ก ๋ค์ ์ ์ธ๋์ด์ผ ํฉ๋๋ค. ์ด๊ฒ์ ๋งค๊ฐ ๋ณ์ํ๋ ๊ฒฝ๋ก๊ฐ ์ ์ ๊ฒฝ๋ก๋ก ํฅํ๋ ํธ๋ํฝ์ ๊ฐ๋ก์ฑ๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค.
@Query
#
?
์ดํ์ ๋์ค๊ณ &
๋ก ๋ถ๋ฆฌ๋ ์ธ์๋ค์ ์๋ฏธ. key=value
์์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
- ์์:
/cat?key1=value1&key2=value2
- deferred response๋ฅผ ๋ฆฌํดํด๋ ๋๋ค๊ณ . Promise ํ์ ๊ฐ์ฒด๋ฅผ ๋ฆฌํดํ๊ฒ ๋๋ฉด #RxJS์ observable stream์ผ๋ก ๋ฆฌํด์ด ๋๊ณ , ์คํธ๋ฆผ์ด ๋๋ ๊ฒฝ์ฐ ํด๋น ์์ค์ ์๋ฆผ์ด ๊ฐ๋ค๊ณ ๋ ํ๋๋ฐ ๋ญ ์๋ฆฐ์ง ์ ๋ชจ๋ฅด๊ฒ ์.
- Request Payloads
- DTO, Data Transfer Object๋ผ๊ณ ๋ถ๋ฆฌ์ฐ๋, ๋คํธ์ํฌ ์ ์ก์ ์ํด ์กด์ฌํ๋ ๊ฐ์ฒดํ์ ์ ํด๋์ค๋ก ์ ์๊ฐ ๋์ด์๋ค๊ณ . DTO ํด๋์ค ๋ฉค๋ฒ๋ค์ ์ง๋ ฌํ ๊ฐ๋ฅํ ํ์ ๋ง ๋ค์ด์ฌ ์ ์๋?
- express.js์์ POST์์ฒญ์ ๋ํ ์์ฒญ์ body๋ฅผ ๋ฏ์ด ์ํ๋ ํค๊ฐ๋ค์ ์ง์ ํ๋ํ๋ค๋ฉด, Nest.js๋ DTO ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ์์ ๊ณง๋ฐ๋ก ์ฌ์ฉํ ์ ์๋ค.
@Post() create(@Body() createCatDto: CreateCatDto) {...}
- app.module.ts ํ์ผ์ ๋ด๊ฐ ์ ์ํ ์ปจํธ๋กค๋ฌ๋ค์
@Module()
๋ฐ์ฝ๋ ์ดํฐ ์์ ๋ฃ์ ์ ์๋ค.
Providers#
์์กด์ฑ ์ฃผ์ ๋ ์ ์๋ ๋ค์ํ ํด๋์ค๋ค์ด๋ค. ๋ํ์ ์ผ๋ก Service, Repository, Factory, Helper, Middleware๊ฐ ์๋ค. ์ปจํธ๋กค๋ฌ๊ฐ ์ฒ๋ฆฌํ ์ ์๋ ๋ณต์กํ ์์ ์ Provider๋ค์๊ฒ ๋๊ฒจ์ฃผ๋ ์์๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ์๋ค.
Inversion of Control
https://en.wikipedia.org/wiki/Inversion_of_control
์๋๋ ์ ์ ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฝ๋๋ฅผ ํธ์ถํ์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฝ๋๊ฐ ์ธํฐํ์ด์ค๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ์ ์ฝ๋๋ฅผ ํธ์ถํ ์ ์๊ฒ ๋ง๋๋ ํจํด์ IoC๋ผ๊ณ ๋ถ๋ฅธ๋ค.
Dependency Injection์ด๋ผ๊ณ , ์์ฑ์ ํ์์ ์์กด์ฑ Provider๋ฅผ ์ฃผ์
ํ๋ ๊ณผ์ ์ ์๋ฏธ. ์๋์ ์ฝ๋์ Provider์ธ CatsService
๋ @Injectable
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋ฌ๊ณ ์๊ธฐ ๋๋ฌธ์ ์ปจํธ๋กค๋ฌ์ ์์ฑํ์์ ์ฃผ์
๋ ์ ์๋ค.
// interfaces/cats.interface.ts
export interface Cat {
name: string;
age: number;
breed: string;
}
// cats/cats.service.ts
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findall(): Cat[] {
return this.cats
}
}
// cats/cats.controller.ts
@Controller('cats')
export class CatsController {
/**
* ์๋์ผ๋ก ์ฃผ์
๋๋ CatsService
*/
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto)
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
์์ฑ์์ ์์กด์ฑ์ ๋ฃ๊ณ ์ถ์ง ์๋ค๋ฉด Property-based injection์ ์ฐธ๊ณ .
Modules#
๊ฐ๊ฐ์ ๋๋ฉ์ธ๋ค (users, cats, posts, comments, ...)์ ๊ฐ๊ฐ์ app์ผ๋ก ๊ด๋ฆฌ๋๋ค. ์ด๋ app์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๋ ์ฝ๋๋ค์ ์งํฉ์ ๋ชจ๋์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค. nest g module <module-name>
CLI ๋ช
๋ น์ด๋ก ์ ๋ชจ๋ ์์ฑ์ด ๊ฐ๋ฅํ๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฃจํธ๋ชจ๋์ import๋ฅผ ํด์ผํ๋ค.
exports
์์ฑ์ผ๋ก ๋ชจ๋ ๋ด provider๋ฅผ ๋ค๋ฅธ ๋ชจ๋์์๋ ์ฌ์ฉํ ์ ์๊ฒ ๋ง๋ ๋ค.
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService]
})
export class CatsModule {}
@Global
์ด๋
ธํ
์ด์
๋ฌ๋ฉด providers๋ฅผ ๋ค๋ฅธ ๋ชจ๋ ๋ชจ๋์์ ์ฌ์ฉํ ์ ์๋ค.
Middleware#
express.js์ ๋ฏธ๋ค์จ์ด์ ์คํด์ ๊ณ ์ค๋ํ ๋ฐ๋ผ๊ฐ๊ณ ์๋ค. ๋จ์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ next()
๋ฅผ ์ฌ์ฉ, ๋ค์ ๋ฏธ๋ค์จ์ด๋ก ์์ฒญ์ ์ ๋ฌํ ์๋ ์์ผ๋ฉฐ, NestMiddleware
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ปจํธ๋กค๋ฌ์ ์์กด์ฑ ์ฃผ์
์ ํ ์๋ ์๋ค.
// logger.middleware.ts
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('reqest...');
next();
}
}
// app.module.ts
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoogerMiddleware)
.forRoutes('cats');
}
}
consumer.forRoutes
์ ์ธ์๋ก ์ข ๋ ๊ตฌ์ฒด์ ์ธ path, method, ์ฌ์ง์ด๋ controller๋ฅผ ์ค์ ํ ์๋ ์๋ค.
Exception Filters (์ดํด๋ถ์กฑ)#
Nest์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์์ ์ฒ๋ฆฌ๋์ง ์์ ๋ชจ๋ ์์ธ๋ฅผ ์ฒ๋ฆฌํ๋ ์์ธ ๊ณ์ธต์ด ๋ด์ฅ๋์ด ์์ต๋๋ค. ์์ธ๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋์์ ์ฒ๋ฆฌ๋์ง ์์ผ๋ฉด ์ด ๊ณ์ธต์์ ์์ธ๋ฅผ ํฌ์ฐฉํ์ฌ ์ ์ ํ ์ฌ์ฉ์ ์นํ์ ์ธ ์๋ต์ ์๋์ผ๋ก ์ ์กํฉ๋๋ค.
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
getRequest<Request>
์getResponse<Response>
๋ฅผ ์ฌ์ฉ, ์์ธ ์์์๋ ์์ฒญ๊ณผ ์๋ต ๊ฐ์ฒด์ ์ฐธ์กฐ๋ฅผ ์ป์ด์ฌ ์ ์๋ค.exception
์ ํ์ฌ ์ฒ๋ฆฌ์ค์ธ ์์ธ๊ฐ์ฒด-
host
๋ArgumentHost
๊ฐ์ฒด๋ก, ์์ง ์ ๋ชจ๋ฅด๊ฒ๋ค- HTTP ์์ธ ๋ฟ๋ง ์๋๋ผ ๋ค์ํ ์์ธ (MSA, WebSocket)์ ๋์ํ๊ธฐ ์ํด
ArgumentHost
๋ฅผ ์ฌ์ฉํ๋ค๋ ์ ๋งํผ์ ์๊ณ ๋์ด๊ฐ์
- HTTP ์์ธ ๋ฟ๋ง ์๋๋ผ ๋ค์ํ ์์ธ (MSA, WebSocket)์ ๋์ํ๊ธฐ ์ํด
-
ํํฐ ์ฐ๊ฒฐ๋ฒ: ์ปจํธ๋กค๋ฌ (ํน์ ๋ฉ์๋)(ํน์
bootstrap
์์)์ ์ด๋ ธํ ์ด์ ์ผ๋ก@UseFilters()
๋ฅผ ์ถ๊ฐํ๋ค.
@Post()
@UseFilters(HttpExceptionFilter)
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
Pipes#
ํ์ดํ๋ ์ปจํธ๋กค๋ฌ๊ฐ ๋ผ์ฐํ ์ ํ๊ธฐ ์ ๋ ๊ฐ์ง ์ญํ ์ ์ํํ๋ค.
- ๋ฐ์ดํฐ ๋ณํ (์์. string โถ number)
- ๋ฐ์ดํฐ ๊ฒ์ฆ
๋ชจ๋ PipeTransform
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ @Injectable()
์ด๋
ธํ
์ด์
์ ๊ฐ๊ณ ์๋ ํด๋์ค๋ Pipe์ด๋ค.
path variable์ ์ํ๋ ํ์
์ผ๋ก ํ์ฑํด์ผํ ๊ฒฝ์ฐ, ์๋ฅผ ๋ค์ด ์ธ์์ ๋ค์ด์ฌ @Param
์ ParseXXXPipe
๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
path variable๋ง ๋๋? query parameter๋ ๋๋ค!
@Get()
async findOne(@Query('id'), ParseIntPipe) id: number) {
...
}
ํ์ฑ์ ์คํจํ ๊ฒฝ์ฐ (์๋ฅผ ๋ค์ด GET cats/hello
๊ฐ ๋ค์ด์จ ๊ฒฝ์ฐ) ์์ธ๋ฅผ ๋์ง๋ค. ๊ทธ ์์ธ๋ ์์์ ์ค๋ช
ํ Exception Filter์์ ์ฒ๋ฆฌ๋์ด ์๋ฌ ๋ฉ์์ง๋ฅผ ์๋ตํ๊ฒ ๋๋ค.
-
[?] ๊ทธ๋๋ ๋ฉ์๋ ์ ์ธ ์์๊ฐ ๋ฌธ์ ๊ฐ ๋ ๊ฒ ๊ฐ์๋ฐ?
@Get('cat/:id')
์@Get('cat/breed')
์ด ์์๋ก ๋ผ์ฐํ ์ ์ฒ๋ฆฌํด๋ฒ๋ฆฌ๋ฉด ์ ๋ถ ์ฒซ๋ฒ์งธ ์ปจํธ๋กค๋ฌ๋ก ๋ค์ด๊ฐ ๊ฒ์ด๊ณ ์์ธ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.// not ok @Get(':id') findOne(@Param('id', ParseIntPipe) id: number): string { return `meowingtone #${id}`; } @Get('breed') findBreed(): string { return 'you requested breed!'; }
// ok @Get('breed') findBreed(): string { return 'you requested breed!'; } @Get(':id') findOne(@Param('id', ParseIntPipe) id: number): string { return `meowingtone #${id}`; }
๋ง์ฝ์ ํ์ดํ๋ฅผ ํตํด์ ๋ณํํด์ผ ํ๋ ํ์ ์ด ๊ธฐ๋ณธํ์ ์ด ์๋๋ผ๋ฉด? => DTO Validation์ ์ฌ์ฉํ๋ฉด ๋๋ค. DTO Validation using class-validator {NestJS}
Guards#
guard๋ @Injectable
์ด๋
ธํ
์ด์
์ ๊ฐ์ง๊ณ CanActivate
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ํด๋์ค๋ฅผ ์๋ฏธํ๋ค.
guards๋ express.js ๋ฏธ๋ค์จ์ด์ ๋จ์ ์ธ next
๊ฐ ๋๊ตฌ์ธ์ง ๋ชจ๋ฅธ๋ค๋ ํน์ง์ ExecutionContext
์ธ์คํด์ค๋ฅผ ํตํด ๊ทน๋ณตํ ์ ์๋ค๊ณ .
๊ฐ์ฅ ๋ํ์ ์ธ ์ฌ์ฉ์ฌ๋ก๋ ์ญ์ ์ฌ์ฉ์ ์ธ๊ฐ์ด๋ค. nestjs.com/security/authentication ์ฌ์ฉ์ ์ธ๊ฐ์ ๋ํ ๋ก์ง์ ๋ง๋ค์ด ์ ์ญ์ ์ผ๋ก or ์ปจํธ๋กค๋ฌ or ๋ฉ์๋ ์ค์ฝํ์์ ๋์ํ๋๋ก ๋ง๋ค ์ ์๊ฒ ๋์๋ค.
ExecutionContext
๊ฐ์ฒด๋ ArgumentHost
๋ฅผ ์์ํ๊ธฐ ๋๋ฌธ์ request๊ฐ์ฒด์ response ๊ฐ์ฒด๋ฅผ ๋ฐ์์ฌ ์ ์๋ค.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
/**
* ExecutionContext์์ request ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
* ํค๋์ ๋ด๊ธด ํ ํฐ์ validateํ ์๋ ์๋ค. ๊ฒฐ๊ณผ๋ ๋๊ธฐ์ ์ผ๋ก
* (Promise), ํน์ ๋น๋๊ธฐ์ ์ผ๋ก (Observable), ํน์ ๊ทธ ์ฆ์
* (boolean) ๋ฆฌํดํ ์ ์๋ค.
*/
return validateRequest(request);
}
}
User Role์ ๋ฐ๋ผ์ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ค ์๋ ์๋ค. admin ์ ์ ๋ง์ด ์ ์ํ ์ ์๋ ๊ด๋ฆฌํ์ด์ง์ ๋ํด์ ์ปค์คํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์์ฑํ์ฌ ์ ์ ์ ์ญํ ์ ๊ฑฐ๋ฅผ ์๋ ์๋ค. Role-based authentication
Interceptors#
NestInterceptor
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ @Injectable
ํ ํด๋์ค๋ฅผ ์ธํฐ์
ํฐ๋ผ๊ณ ๋ถ๋ฆ. ExecutionContext
(์ค๊ธฐ์ฐจ๊ฒ ๋ดค๋๊ฑฐ) + CallHandler
(handle
์ ์ฌ์ฉํ์ฌ ๋ค์ ์ปจํธ๋กค๋ฌ ํธ๋ค๋ฌ๋ฅผ ๊ฐ์ ํธ์ถ(RxJS๋ฅผ ์ฌ์ฉํ๋๋ค))๋ฅผ ํตํ์ฌ intercept()
๋ฉ์๋๋ฅผ ๊ตฌํํ๋ฉด ๋๋ค.
CallHandler
๋ฅผ ์์ ๊ฐ์ผ ๊ฑธ ๋ณด๋ฉด request ~ response ๋ก์ง๋ณด๋ค ์ค์ฝํ๊ฐ ๋๋ค๋ ๊ฒ์ ์ ์ ์๋ค.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle() /**
* handle์ ํธ์ถํ๋ ๊ฒ์ผ๋ก request-response
* handler๊ฐ ์คํ๋๋ค. ํธ๋ค๋ฌ๊ฐ ๋ญ ์คํธ๋ฆผ์ ๋ฃ๋์ง๋
* ์ ๋ชจ๋ฅด๊ฒ ๋ค.
*/
.pipe(
/**
* `tap`์ observable stream์ด ์ ์์ ์ผ๋ก (์์ธ๋ ํฌํจ)
* ์ข
๋ฃ๋์๋ ๋ฐ์ํ๋ค.
*/
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
- Binding Interceptors with
@UseInterceptors(LoggingInterceptor)
for controller class & method- ์ ์ญ์ ์ผ๋ก ์ธํฐ์
ํฐ๋ฅผ ๋ฑ๋กํ๋ ค๋ฉด
useGlobalInterceptors()
๋ฅผ ๋ถํธ์คํธ๋ฉ ๋จ๊ณ์ ๋ฃ์ด์ฃผ์.
- ์ ์ญ์ ์ผ๋ก ์ธํฐ์
ํฐ๋ฅผ ๋ฑ๋กํ๋ ค๋ฉด
- Response Mapping
handle()
์ด #RxJS์Observable
๊ฐ์ฒด๋ผ๋ ์ฌ์ค์ ์์์ผ๋ ์ด ์คํธ๋ฆผ์ผ๋ก๋ถํฐ ๋ฌด์ธ๊ฐ๋ฅผmap
ํ ์๋ ์๋ค.์์ ํจ์ํ์ธ๋ฐ?- RxJS๋ฅผ ์ข ๋ฐฐ์์ผ ์ดํด๊ฐ ๋ ๋ฏ ํ๋ฐ, ์ด๋ฒ ํ๋ก์ ํธ์์๋ ์ธ์ง ๋ชจ๋ฅด๊ฒ ๋ค.
[TODO] JEST#
nest ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๊ณ ๋์ ๋ฑ ๊ธฐ๋ณธ ํ
ํ๋ฆฟ์ธ ์ํ์์ jest๋ฅผ ๋๋ ค๋ณด์. ๋ฐ๋ก ์คํจํ๋ค. getHello
์๋น์ค ๋ฉ์๋๊ฐ ์๋ค๊ณ ๋์ค๋๋ฐ, ๋ณ๋๋ก ์ธ์ ์
์ ํด์ผํ๋ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค.
TestRun "all-gifts:watch-tests-0:process-start:0 (0)" started
> all-gifts@0.0.1 test
> jest --testLocationInResults --json --useStderr --outputFile /tmp/jest_runner_all_gifts_1000.json --watch --no-coverage --reporters default --reporters /home/choiwheatley/.vscode-server/extensions/orta.vscode-jest-6.2.2/out/reporter.js --colors
FAIL src/app.controller.spec.ts
โ Test suite failed to run
src/app.controller.spec.ts:19:28 - error TS2339: Property 'getHello' does not exist on type 'AppController'.
19 expect(appController.getHello()).toBe('Hello World!');
~~~~~~~~
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 0.91 s
Custom decorators#
JS async, await, promise#
https://springfall.cc/article/2022-11/easy-promise-async-await
OS๋ ๋ฒจ์์ ์ค์ผ์ค๋ง์ ๋์์ ํ๋ก์ธ์ค์ด์ง๋ง JS ๋ ๋ฒจ์์ ์ค์ผ์ค๋ง์ ๋์์ ํจ์์ด๋ค.
๋ฐ๋ผ์, ์ฝ๋ฐฑํจ์๋, ๋ด๊ฐ ํจ์๋ฅผ ์ํ ๋ ํธ์ถํ๊ฒ ๋ง๋ค๊ธฐ ์ํ ์๋จ์ผ๋ก ๋์จ ๊ฒ์ด๋ค.
JS์์ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ ๋์์ ํ ์ ์์ง๋ง, ์ฒ๋ฆฌํ๋ ๊ฒ์ ๋์์ ํ์ง ๋ชปํ๋ค.
tasks, microtasks, queues and schedules {js}
[TODO] RxJS#
Database typeorm#
postgresql#
- EC2 Postgresql์ ์ฅ๊ณ ๊ธฐ๋ณธ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ํ์ฉํ๊ธฐ โถ ์๋ ์ฅ๊ณ ์ฌ์ฉํ๋ฉด์ ๊ฒช์๋ ๊ฐ๊ณ ์ ๋ชจ์๋์ ๋ฌธ์
- postgresql on docker ๋จ์ ๋ช ๋ น์ด ๋ชจ์
- rds postgresql ssl ์ธ์ฆ์ ๋ฐ๊ธํ์ฌ ์ ์ํ๊ธฐ
Model-View-Controller ๋ฅผ ์ฌ์ฉํ ํ์คํ ์๋ฒ ๋ง๋ค๊ธฐ#
HTML ํ ํ๋ฆฟ ์์ง ํจํค์ง๋ฅผ ์ฌ์ฉ, app์ ์ ์ ํ์ผ์ ์์น๋ฅผ ์ค์ ํ๊ณ view engine์ ์ธํ ํด์ค๋ค.