๐ NestJS ํ๋ ์์ํฌ ๋ง์คํฐํ๊ธฐ: ์ด๋ณด์๋ถํฐ ์ ๋ฌธ๊ฐ๊น์ง! ๐

์๋ ํ์ธ์, ์ด์ ๋์น๋ ๊ฐ๋ฐ์ ์ฌ๋ฌ๋ถ! ์ค๋์ ํ๋ ์น ๊ฐ๋ฐ์ ํซํ ํธ๋ ๋, NestJS ํ๋ ์์ํฌ์ ๋ํด ๊น์ด ์๊ฒ ํ๊ตฌํด๋ณด๋ ค๊ณ ํฉ๋๋ค. ๐ NestJS๋ ํจ์จ์ ์ด๊ณ ํ์ฅ ๊ฐ๋ฅํ ์๋ฒ ์ฌ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์ํ ๋๊ตฌ์ธ๋ฐ์, ์ด ๊ธ์ ํตํด ์ฌ๋ฌ๋ถ๋ NestJS์ ๋ง๋ฒ ๊ฐ์ ์ธ๊ณ๋ก ๋น ์ ธ๋ค๊ฒ ๋ ๊ฑฐ์์!
์ฐ๋ฆฌ์ ์ฌ์ ์ ๊ธฐ์ด๋ถํฐ ์์ํด ๊ณ ๊ธ ๊ธฐ์ ๊น์ง ๋ค๋ฃฐ ์์ ์ด๋, ํธ์ํ ์๋ฆฌ์ ์์ ์ฆ๊ฒ๊ฒ ๋ฐ๋ผ์ ์ฃผ์ธ์. ๐ชโจ ์ด ๊ธ์ ๋ค ์ฝ๊ณ ๋๋ฉด, ์ฌ๋ฌ๋ถ๋ NestJS ๋ง์คํฐ๊ฐ ๋์ด ์์ ๊ฒ๋๋ค!
๐ก Pro Tip: ์ด ๊ธ์์ ๋ฐฐ์ด ๋ด์ฉ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด์ธ์. ์ค์ ๊ฒฝํ๋งํผ ์ข์ ํ์ต ๋ฐฉ๋ฒ์ ์๋ต๋๋ค. ์ฌ๋ฌ๋ถ์ ์ฐฝ์์ ์ธ ์์ด๋์ด๋ฅผ NestJS๋ก ๊ตฌํํด๋ณด๋ ๊ฑด ์ด๋จ๊น์? ์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ์ฌ๋ฅ ๊ณต์ ํ๋ซํผ์ ๋ฐฑ์๋๋ฅผ NestJS๋ก ๊ตฌ์ถํด๋ณด๋ ๊ฒ๋ ์ข์ ์ฐ์ต์ด ๋ ๊ฑฐ์์!
์, ๊ทธ๋ผ NestJS์ ์ ๋น๋ก์ด ์ธ๊ณ๋ก ํจ๊ป ๋ ๋๋ณผ๊น์? ๐
1. NestJS ์๊ฐ: ์น ๊ฐ๋ฐ์ ์๋ก์ด ์งํ ๐
NestJS๋ ํจ์จ์ ์ด๊ณ ํ์ฅ ๊ฐ๋ฅํ Node.js ์๋ฒ ์ฌ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๊ธฐ ์ํ ํ๋ ์์ํฌ์ ๋๋ค. Angular์์ ์๊ฐ์ ๋ฐ์ ์ค๊ณ๋์์ผ๋ฉฐ, TypeScript๋ฅผ ์๋ฒฝํ๊ฒ ์ง์ํฉ๋๋ค. ๐ฆธโโ๏ธ
1.1 NestJS์ ํน์ง
- ๐ ๊ตฌ์กฐํ๋ ์ํคํ ์ฒ: ๋ชจ๋, ์ปจํธ๋กค๋ฌ, ์๋น์ค ๋ฑ์ ๋ช ํํ ๊ตฌ์กฐ
- ๐ง ์์กด์ฑ ์ฃผ์ : ๋์จํ ๊ฒฐํฉ๊ณผ ํ ์คํธ ์ฉ์ด์ฑ ์ ๊ณต
- ๐ ๋ฐ์ฝ๋ ์ดํฐ ๊ธฐ๋ฐ: ๊ฐ๊ฒฐํ๊ณ ์ง๊ด์ ์ธ ์ฝ๋ ์์ฑ ๊ฐ๋ฅ
- ๐ ๋ค์ํ ์ด๋ํฐ: Express, Fastify ๋ฑ ๋ค์ํ HTTP ํ๋ซํผ ์ง์
- ๐ ํ๋ถํ ์ํ๊ณ: ๋ค์ํ ๋ด์ฅ ๋ฐ ์๋ํํฐ ๋ชจ๋ ์ ๊ณต
๐ ํ์ต ํฌ์ธํธ: NestJS์ ๊ตฌ์กฐํ๋ ์ํคํ ์ฒ๋ ๋๊ท๋ชจ ํ๋ก์ ํธ์์ ํนํ ๋น์ ๋ฐํฉ๋๋ค. ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ํฌ๊ฒ ํฅ์๋์ฃ . ์ด๋ ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ํ๋ซํผ์ ๊ฐ๋ฐํ ๋ ํฐ ์ฅ์ ์ด ๋ ์ ์์ต๋๋ค.
1.2 NestJS vs Express
Express๋ ์ค๋ซ๋์ Node.js ์ํ๊ณ์ ๋ํ์ฃผ์์์ต๋๋ค. ํ์ง๋ง NestJS๋ Express์ ์ฅ์ ์ ํก์ํ๋ฉด์๋ ๋ ๋์ ๊ตฌ์กฐ์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
ํน์ง | Express | NestJS |
---|---|---|
์ํคํ ์ฒ | ์์ ๋ก์ด ๊ตฌ์กฐ | ๋ชจ๋ํ๋ ๊ตฌ์กฐ |
TypeScript ์ง์ | ๋ถ๋ถ์ ์ง์ | ์๋ฒฝํ ์ง์ |
์์กด์ฑ ์ฃผ์ | ์์ | ๋ด์ฅ |
ํ์ต ๊ณก์ | ๋ฎ์ | ์ค๊ฐ |
NestJS๋ Express์ ๊ฐ๊ฒฐํจ๊ณผ ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋, ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ํ์ํ ๊ตฌ์กฐ์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ ํ๋ก์ ํธ์ ํ์ฅ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฌ๊ฒ ํฅ์์ํต๋๋ค.
1.3 NestJS์ ์ฒ ํ
NestJS๋ "๊ฐ๋ฐ์ ๊ฒฝํ"์ ์ค์ํ๊ฒ ์ฌ๊น๋๋ค. ์ด๋ ๋ค์๊ณผ ๊ฐ์ ์ฒ ํ์ผ๋ก ๋ํ๋ฉ๋๋ค:
- ๐ ๊ตฌ์กฐํ๋ ์ฝ๋๋ฒ ์ด์ค: ์ผ๊ด๋ ์ฝ๋ ๊ตฌ์กฐ๋ก ํ ํ์ ์ฉ์ด
- ๐ ๋ช ํํ ์ถ์ํ: ๋ณต์กํ ๋ก์ง์ ๊ฐ๊ฒฐํ๊ฒ ํํ
- ๐ ๋ชจ๋์ฑ: ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ก ๊ฐ๋ฐ ํจ์จ์ฑ ์ฆ๋
- ๐ ํ์ฅ์ฑ: ์ฌ์ด ๊ธฐ๋ฅ ์ถ๊ฐ์ ์์
๐ก ์ค์ ํ: NestJS์ ์ฒ ํ์ ์ดํดํ๊ณ ์ ์ฉํ๋ฉด, ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง๋ ๊น๋ํ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท์ ์ฌ์ฉ์ ๊ด๋ฆฌ, ๊ฒฐ์ ์์คํ , ๊ฒ์ ๊ธฐ๋ฅ ๋ฑ์ ๋ชจ๋๋ณ๋ก ๊ตฌํํ๋ฉด ์ฝ๋ ๊ด๋ฆฌ๊ฐ ํจ์ฌ ์ฌ์์ง๋๋ค.
1.4 NestJS ์์ํ๊ธฐ
NestJS๋ฅผ ์์ํ๋ ๊ฒ์ ๋งค์ฐ ๊ฐ๋จํฉ๋๋ค. ๋ค์ ๋ช ๋ น์ด๋ก NestJS CLI๋ฅผ ์ค์นํ๊ณ ์ ํ๋ก์ ํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค:
npm i -g @nestjs/cli
nest new project-name
์ด๋ ๊ฒ ํ๋ฉด ๊ธฐ๋ณธ ๊ตฌ์กฐ์ NestJS ํ๋ก์ ํธ๊ฐ ์์ฑ๋ฉ๋๋ค. ๐
์ด ๊ตฌ์กฐ๋ฅผ ์ดํดํ๋ ๊ฒ์ด NestJS ๋ง์คํฐ์ ์ฒซ ๊ฑธ์์ ๋๋ค. ๊ฐ ํ์ผ์ ์ญํ ์ ์์๋ณผ๊น์?
- ๐ src/: ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ค ์ฝ๋๊ฐ ์์น
- ๐ test/: ํ ์คํธ ํ์ผ๋ค์ด ์์น
- ๐ node_modules/: ํ๋ก์ ํธ ์์กด์ฑ ๋ชจ๋๋ค์ด ์ค์น๋๋ ๊ณณ
- ๐ main.ts: ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํธ๋ฆฌ ํฌ์ธํธ
- ๐ app.module.ts: ๋ฃจํธ ๋ชจ๋ ์ ์
- ๐ app.controller.ts: ๊ธฐ๋ณธ ์ปจํธ๋กค๋ฌ
- ๐ app.service.ts: ๊ธฐ๋ณธ ์๋น์ค
๐ ์ฑ์ฅ ํฌ์ธํธ: NestJS์ ๊ตฌ์กฐ๋ฅผ ์ดํดํ๊ณ ๋๋ฉด, ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ๋ ์ฒด๊ณ์ ์ผ๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค. ์ด๋ ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋๊ท๋ชจ ํ๋ซํผ์ ๊ฐ๋ฐํ ๋ ํนํ ์ ์ฉํฉ๋๋ค. ๊ฐ ๊ธฐ๋ฅ์ ๋ชจ๋๋ก ๋๋๊ณ , ๊ด๋ จ ์ปจํธ๋กค๋ฌ์ ์๋น์ค๋ฅผ ๊ตฌ์ฑํ๋ฉด ์ฝ๋์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ํฌ๊ฒ ํฅ์๋ฉ๋๋ค.
์ด์ NestJS์ ๊ธฐ๋ณธ์ ์์์ผ๋, ๋ ๊น์ด ๋ค์ด๊ฐ ๋ณผ๊น์? ๋ค์ ์น์ ์์๋ NestJS์ ํต์ฌ ๊ฐ๋ ๋ค์ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ค๋น๋์ จ๋์? Let's dive in! ๐โโ๏ธ
2. NestJS์ ํต์ฌ ๊ฐ๋ : ๋ชจ๋, ์ปจํธ๋กค๋ฌ, ์๋น์ค ๐งฉ
NestJS์ ์๋ฆ๋ค์์ ๊ทธ ๊ตฌ์กฐ์ ์ฐ์ํจ์ ์์ต๋๋ค. ์ด ์น์ ์์๋ NestJS์ ํต์ฌ ๊ตฌ์ฑ ์์์ธ ๋ชจ๋, ์ปจํธ๋กค๋ฌ, ์๋น์ค์ ๋ํด ์์ธํ ์์๋ณด๊ฒ ์ต๋๋ค. ์ด ๊ฐ๋ ๋ค์ ์ ๋๋ก ์ดํดํ๋ฉด, ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ๋ ์ฒด๊ณ์ ์ผ๋ก ๊ตฌ์ถํ ์ ์๋ต๋๋ค! ๐๏ธ
2.1 ๋ชจ๋ (Modules)
๋ชจ๋์ NestJS ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ธฐ๋ณธ ๋น๋ฉ ๋ธ๋ก์ ๋๋ค. ๊ด๋ จ๋ ๊ธฐ๋ฅ๋ค์ ๊ทธ๋ฃนํํ๊ณ ๊ตฌ์กฐํํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
๐ ํต์ฌ ๊ฐ๋ : ๋ชจ๋์ @Module() ๋ฐ์ฝ๋ ์ดํฐ๋ก ์ฃผ์์ด ๋ฌ๋ฆฐ ํด๋์ค์ ๋๋ค. ์ด ๋ฐ์ฝ๋ ์ดํฐ๋ Nest๊ฐ ์ ํ๋ฆฌ์ผ์ด์ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ฑํ๋ ๋ฐ ์ฌ์ฉํ๋ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ชจ๋์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณผ๊น์?
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
imports: [],
controllers: [UserController],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
์ฌ๊ธฐ์ ๊ฐ ์์ฑ์ ์๋ฏธ๋ฅผ ์์๋ด ์๋ค:
- ๐ฆ imports: ์ด ๋ชจ๋์์ ์ฌ์ฉํ ๋ค๋ฅธ ๋ชจ๋๋ค์ ๊ฐ์ ธ์ต๋๋ค.
- ๐ฎ controllers: ์ด ๋ชจ๋์ ์ํ ์ปจํธ๋กค๋ฌ๋ค์ ์ ์ํฉ๋๋ค.
- ๐ providers: ์ด ๋ชจ๋์์ ์ฌ์ฉํ ์๋น์ค๋ ๊ธฐํ ํ๋ก๋ฐ์ด๋๋ค์ ์ ์ํฉ๋๋ค.
- ๐ค exports: ์ด ๋ชจ๋์์ ๋ค๋ฅธ ๋ชจ๋๋ก ๋ด๋ณด๋ผ ํ๋ก๋ฐ์ด๋๋ค์ ์ ์ํฉ๋๋ค.
๐ก ์ค์ ํ: ๋ชจ๋์ ์ ๊ตฌ์ฑํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์กฐ๋ฅผ ๋ช ํํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท์์ ์ฌ์ฉ์ ๊ด๋ฆฌ, ๊ฒฐ์ , ๊ฒ์ ๋ฑ์ ๊ธฐ๋ฅ์ ๊ฐ๊ฐ ๋ณ๋์ ๋ชจ๋๋ก ๊ตฌ์ฑํ๋ฉด ์ฝ๋์ ๊ด๋ฆฌ์ ํ์ฅ์ด ํจ์ฌ ์ฌ์์ง๋๋ค.
2.2 ์ปจํธ๋กค๋ฌ (Controllers)
์ปจํธ๋กค๋ฌ๋ ๋ค์ด์ค๋ ์์ฒญ์ ์ฒ๋ฆฌํ๊ณ ํด๋ผ์ด์ธํธ์ ์๋ต์ ๋ฐํํ๋ ์ญํ ์ ํฉ๋๋ค. ๋ผ์ฐํ ๋ฉ์ปค๋์ฆ์ ํตํด ๊ฐ ์ปจํธ๋กค๋ฌ๊ฐ ํน์ ์์ฒญ์ ์์ ํ๋๋ก ์ค์ ํฉ๋๋ค.
์ปจํธ๋กค๋ฌ์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณผ๊น์?
import { Controller, Get, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
}
์ฌ๊ธฐ์ ์ฃผ๋ชฉํ ์ ๋ค:
- ๐ญ @Controller() ๋ฐ์ฝ๋ ์ดํฐ: ์ด ํด๋์ค๊ฐ ์ปจํธ๋กค๋ฌ์์ ๋ํ๋ ๋๋ค. 'users'๋ ์ด ์ปจํธ๋กค๋ฌ์ ๋ผ์ฐํธ ์ ๋์ฌ์ ๋๋ค.
- ๐ ์์ฑ์ ์ฃผ์ : UserService๋ฅผ ์ฃผ์ ๋ฐ์ ์ฌ์ฉํฉ๋๋ค.
- ๐ฆ ๋ผ์ฐํธ ํธ๋ค๋ฌ: @Get(), @Post() ๋ฑ์ ๋ฐ์ฝ๋ ์ดํฐ๋ก HTTP ๋ฉ์๋์ ์๋ํฌ์ธํธ๋ฅผ ์ ์ํฉ๋๋ค.
๐ ์ฑ์ฅ ํฌ์ธํธ: ์ปจํธ๋กค๋ฌ๋ฅผ ์ค๊ณํ ๋๋ ๋จ์ผ ์ฑ ์ ์์น(Single Responsibility Principle)์ ์ผ๋์ ๋์ธ์. ๊ฐ ์ปจํธ๋กค๋ฌ๋ ํน์ ๋๋ฉ์ธ์ด๋ ๋ฆฌ์์ค์ ๋ํ ์์ ๋ง์ ์ฒ๋ฆฌํ๋๋ก ํ๋ฉด ์ฝ๋์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ํฅ์๋ฉ๋๋ค.
2.3 ์๋น์ค (Services)
์๋น์ค๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ด๋นํฉ๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์กฐ์, ์ธ๋ถ API ํธ์ถ ๋ฑ์ ์์ ์ ์ํํ๋ฉฐ, ์ปจํธ๋กค๋ฌ์ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
@Injectable()
export class UserService {
private users = [];
findAll() {
return this.users;
}
create(createUserDto: CreateUserDto) {
this.users.push(createUserDto);
return createUserDto;
}
}
์๋น์ค์ ์ฃผ์ ํน์ง:
- ๐ @Injectable() ๋ฐ์ฝ๋ ์ดํฐ: ์ด ํด๋์ค๊ฐ Nest IoC ์ปจํ ์ด๋์ ์ํด ๊ด๋ฆฌ๋๋ ์๋น์ค์์ ๋ํ๋ ๋๋ค.
- ๐ง ๋น์ฆ๋์ค ๋ก์ง: ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๊ธฐ๋ฅ์ ๊ตฌํํฉ๋๋ค.
- ๐ ์ฌ์ฌ์ฉ์ฑ: ์ฌ๋ฌ ์ปจํธ๋กค๋ฌ์์ ๋์ผํ ์๋น์ค๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๐ก ์ค์ ํ: ์๋น์ค๋ฅผ ์ค๊ณํ ๋๋ ๋จ์ผ ์ฑ ์ ์์น๊ณผ ํจ๊ป ์์กด์ฑ ์ฃผ์ (Dependency Injection)์ ์ ๊ทน ํ์ฉํ์ธ์. ์ด๋ ๊ฒ ํ๋ฉด ํ ์คํธํ๊ธฐ ์ฝ๊ณ ์ ์ง๋ณด์๊ฐ ์ฉ์ดํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท์ ๊ฒฐ์ ์๋น์ค๋ฅผ ๊ตฌํํ ๋ ์ธ๋ถ ๊ฒฐ์ API์์ ํต์ ์ ๋ด๋นํ๋ ์๋น์ค๋ฅผ ๋ณ๋๋ก ๋ง๋ค์ด ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ๋ฉด, ๋์ค์ ๊ฒฐ์ ์์คํ ์ ๋ณ๊ฒฝํ๋๋ผ๋ ์ฝ๋์ ์์ ์ ์ต์ํํ ์ ์์ต๋๋ค.
2.4 ๋ชจ๋, ์ปจํธ๋กค๋ฌ, ์๋น์ค์ ์ํธ์์ฉ
์ด ์ธ ๊ฐ์ง ์์๋ NestJS ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ ๊ตฌ์กฐ๋ฅผ ํ์ฑํฉ๋๋ค. ๊ทธ๋ค์ ์ํธ์์ฉ์ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค:
- ๋ชจ๋์ ๊ด๋ จ๋ ์ปจํธ๋กค๋ฌ์ ์๋น์ค๋ฅผ ๊ทธ๋ฃนํํฉ๋๋ค.
- ์ปจํธ๋กค๋ฌ๋ ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋ฐ์ ์ ์ ํ ์๋น์ค ๋ฉ์๋๋ฅผ ํธ์ถํฉ๋๋ค.
- ์๋น์ค๋ ์ค์ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ปจํธ๋กค๋ฌ์ ๋ฐํํฉ๋๋ค.
- ์ปจํธ๋กค๋ฌ๋ ์๋น์ค๋ก๋ถํฐ ๋ฐ์ ๊ฒฐ๊ณผ๋ฅผ ํด๋ผ์ด์ธํธ์๊ฒ ์๋ต์ผ๋ก ์ ์กํฉ๋๋ค.
์ด๋ฌํ ๊ตฌ์กฐ๋ ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ(Separation of Concerns)๋ฅผ ์คํํ๋ฉฐ, ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ํ ์คํธ ์ฉ์ด์ฑ์ ํฌ๊ฒ ํฅ์์ํต๋๋ค.
๐ ํ์ต ํฌ์ธํธ: NestJS์ ์ด๋ฌํ ๊ตฌ์กฐ์ ํน์ฑ์ ์ ํ์ฉํ๋ฉด, ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ํ๋ซํผ๋ ์ฒด๊ณ์ ์ผ๋ก ๊ตฌ์ถํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด:
- ์ฌ์ฉ์ ๊ด๋ฆฌ, ์ฌ๋ฅ ๊ฑฐ๋, ๊ฒฐ์ ์์คํ ๋ฑ์ ๊ฐ๊ฐ์ ๋ชจ๋๋ก ๊ตฌ์ฑ
- ๊ฐ ๋ชจ๋ ๋ด์์ ๊ด๋ จ ์ปจํธ๋กค๋ฌ์ ์๋น์ค๋ฅผ ์ ์
- ๊ณตํต ๊ธฐ๋ฅ(์: ์ธ์ฆ, ๋ก๊น )์ ๋ณ๋์ ๋ชจ๋๋ก ๋ง๋ค์ด ์ฌ์ฌ์ฉ
์ด๋ ๊ฒ ๊ตฌ์ฑํ๋ฉด ๊ธฐ๋ฅ ์ถ๊ฐ๋ ์์ ์ด ํ์ํ ๋ ํด๋น ๋ชจ๋๋ง ์ง์ค์ ์ผ๋ก ์์ ํ ์ ์์ด ์ ์ง๋ณด์๊ฐ ์ฉ์ดํด์ง๋๋ค.
2.5 ์ค์ ์์ : ์ฌ๋ฅ๋ท์ ์ฌ์ฉ์ ๊ด๋ฆฌ ๋ชจ๋
์ด์ ์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๊ฐ๋ ์ ๋ฐํ์ผ๋ก ์ฌ๋ฅ๋ท์ ์ฌ์ฉ์ ๊ด๋ฆฌ ๋ชจ๋์ ๊ฐ๋จํ ๊ตฌํํด ๋ณผ๊น์?
// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
// user.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findOne(id);
}
}
// user.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
@Injectable()
export class UserService {
private users = [];
create(createUserDto: CreateUserDto) {
const newUser = { id: Date.now().toString(), ...createUserDto };
this.users.push(newUser);
return newUser;
}
findOne(id: string) {
return this.users.find(user => user.id === id);
}
}
// create-user.dto.ts
export class CreateUserDto {
readonly name: string;
readonly email: string;
readonly skills: string[];
}
์ด ์์ ์์:
- ๐งฉ UserModule์ UserController์ UserService๋ฅผ ํ๋๋ก ๋ฌถ์ต๋๋ค.
- ๐ฎ UserController๋ ์ฌ์ฉ์ ์์ฑ๊ณผ ์กฐํ๋ฅผ ์ํ ์๋ํฌ์ธํธ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ๐ UserService๋ ์ค์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋ ๋ก์ง์ ๊ตฌํํฉ๋๋ค.
- ๐ CreateUserDto๋ ์ฌ์ฉ์ ์์ฑ ์ ํ์ํ ๋ฐ์ดํฐ์ ๊ตฌ์กฐ๋ฅผ ์ ์ํฉ๋๋ค.
๐ก ์ค์ ํ: ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋, ์ธ์ฆ ๋ฐ ์ธ๊ฐ, ์๋ฌ ์ฒ๋ฆฌ ๋ฑ ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ด ํ์ํฉ๋๋ค. NestJS๋ ์ด๋ฌํ ๊ธฐ๋ฅ๋ค์ ์ฝ๊ฒ ํตํฉํ ์ ์๋ ๋ค์ํ ๋ชจ๋๊ณผ ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์๋ฅผ ๋ค์ด, TypeORM์ ์ฌ์ฉํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋, Passport๋ฅผ ์ด์ฉํ ์ธ์ฆ, ๊ธ๋ก๋ฒ ์์ธ ํํฐ๋ฅผ ํตํ ์๋ฌ ์ฒ๋ฆฌ ๋ฑ์ ๊ตฌํํ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ NestJS์ ํต์ฌ ๊ฐ๋ ์ธ ๋ชจ๋, ์ปจํธ๋กค๋ฌ, ์๋น์ค์ ๋ํด ์์ธํ ์์๋ณด์์ต๋๋ค. ์ด ๊ตฌ์กฐ๋ฅผ ์ ํ์ฉํ๋ฉด ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ ์ง๋ณด์๊ฐ ์ฌ์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์์ต๋๋ค. ๋ค์ ์น์ ์์๋ NestJS์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ์ดํด๋ณด๋ฉฐ, ๋ ๋ณต์กํ ์๋๋ฆฌ์ค๋ฅผ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ ์๋์ง ์์๋ณด๊ฒ ์ต๋๋ค. Ready for more? Let's go! ๐
3. NestJS์ ๊ณ ๊ธ ๊ธฐ๋ฅ: ๋ฏธ๋ค์จ์ด, ํ์ดํ, ๊ฐ๋, ์ธํฐ์ ํฐ ๐ก๏ธ
NestJS์ ์ง์ ํ ํ์ ๊ทธ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ์์ต๋๋ค. ์ด ์น์ ์์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ ์ธ๋ฐํ๊ฒ ์ ์ดํ๊ณ ํ์ฅํ ์ ์๊ฒ ํด์ฃผ๋ ๋ฏธ๋ค์จ์ด, ํ์ดํ, ๊ฐ๋, ์ธํฐ์ ํฐ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์ด ๊ธฐ๋ฅ๋ค์ ๋ง์คํฐํ๋ฉด, ์ฌ๋ฌ๋ถ์ ์ง์ ํ NestJS ์ ๋ฌธ๊ฐ๊ฐ ๋ ์ ์์ต๋๋ค! ๐
3.1 ๋ฏธ๋ค์จ์ด (Middleware)
๋ฏธ๋ค์จ์ด๋ ๋ผ์ฐํธ ํธ๋ค๋ฌ ์ ์ ํธ์ถ๋๋ ํจ์์ ๋๋ค. ์์ฒญ ๋ฐ ์๋ต ๊ฐ์ฒด์ ์ ๊ทผํ๊ณ , ์์ฒญ-์๋ต ์ฃผ๊ธฐ๋ฅผ ์กฐ์ํ ์ ์์ต๋๋ค.
๐ ํต์ฌ ๊ฐ๋ : ๋ฏธ๋ค์จ์ด๋ ๋ก๊น , ์์ฒญ ํ์ฑ, ์ธ์ฆ ๋ฑ ๋ค์ํ ์ฉ๋๋ก ์ฌ์ฉ๋ ์ ์์ต๋๋ค. Express์ ๋ฏธ๋ค์จ์ด์ ์ ์ฌํ์ง๋ง, NestJS์ ์์กด์ฑ ์ฃผ์ ์์คํ ๊ณผ ํตํฉ๋์ด ์์ต๋๋ค.
๊ฐ๋จํ ๋ก๊น ๋ฏธ๋ค์จ์ด๋ฅผ ๋ง๋ค์ด ๋ณผ๊น์?
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`Request... ${req.method} ${req.url}`);
next();
}
}
์ด ๋ฏธ๋ค์จ์ด๋ฅผ ๋ชจ๋์ ์ ์ฉํ๋ ค๋ฉด:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('users');
}
}
๐ก ์ค์ ํ: ์ฌ๋ฅ๋ท์์ ๋ฏธ๋ค์จ์ด๋ฅผ ํ์ฉํ๋ฉด ๋ชจ๋ API ์์ฒญ์ ๋ํ ๋ก๊น , ์ฌ์ฉ์ ์ธ์ฆ ์ํ ํ์ธ, ์์ฒญ ๋ณธ๋ฌธ ํ์ฑ ๋ฑ์ ํจ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ๋ชจ๋ ์์ฒญ์ ๋ํด ์ฌ์ฉ์์ API ์ฌ์ฉ๋์ ์ถ์ ํ๋ ๋ฏธ๋ค์จ์ด๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.
3.2 ํ์ดํ (Pipes)
ํ์ดํ๋ ์ ๋ ฅ ๋ฐ์ดํฐ๋ฅผ ์ํ๋ ํ์์ผ๋ก ๋ณํํ๊ฑฐ๋ ์ ํจ์ฑ์ ๊ฒ์ฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
NestJS๋ ๋ช ๊ฐ์ง ๋ด์ฅ ํ์ดํ๋ฅผ ์ ๊ณตํ์ง๋ง, ์ปค์คํ ํ์ดํ๋ฅผ ๋ง๋ค ์๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ์ซ์ ํ์์ ๊ฒ์ฆํ๋ ํ์ดํ๋ฅผ ๋ง๋ค์ด ๋ณผ๊น์?
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform<string number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
</string>
์ด ํ์ดํ๋ฅผ ์ปจํธ๋กค๋ฌ์์ ์ฌ์ฉํ๋ ค๋ฉด:
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.userService.findOne(id);
}
๐ ์ฑ์ฅ ํฌ์ธํธ: ํ์ดํ๋ฅผ ์ ํ์ฉํ๋ฉด ์ ๋ ฅ ๋ฐ์ดํฐ์ ์์ ์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. ์ฌ๋ฅ๋ท์์ ์ฌ์ฉ์๊ฐ ์ ๊ณตํ๋ ์ ๋ณด(์: ๊ฐ๊ฒฉ, ๋ ์ง)๋ฅผ ์๋์ผ๋ก ์ ์ ํ ํ์์ผ๋ก ๋ณํํ๊ณ ์ ํจ์ฑ์ ๊ฒ์ฌํ๋ ๋ฐ ํ์ดํ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
3.3 ๊ฐ๋ (Guards)
๊ฐ๋๋ ์ฃผ๋ก ์ธ์ฆ๊ณผ ์ธ๊ฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค. ํน์ ๋ผ์ฐํธ์ ๋ํ ์ ๊ทผ์ ์ ์ดํ ์ ์์ต๋๋ค.
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();
return validateRequest(request);
}
}
function validateRequest(request: any): boolean {
// ์ฌ๊ธฐ์ ์ค์ ์ธ์ฆ ๋ก์ง์ ๊ตฌํํฉ๋๋ค.
return true; // ๋๋ false
}
</boolean></boolean>
๊ฐ๋๋ฅผ ์ปจํธ๋กค๋ฌ๋ ๋ฉ์๋์ ์ ์ฉํ๋ ค๋ฉด:
@UseGuards(AuthGuard)
@Controller('users')
export class UserController {
// ...
}
๐ก ์ค์ ํ: ์ฌ๋ฅ๋ท์์ ๊ฐ๋๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ API์ ๋ํ ์ ๊ทผ์ ์ ํํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ํ๋ฆฌ๋ฏธ์ ์ฌ์ฉ์๋ง ์ ๊ทผํ ์ ์๋ ํน๋ณ ๊ธฐ๋ฅ์ด๋ ๊ด๋ฆฌ์ ์ ์ฉ API๋ฅผ ๊ตฌํํ ๋ ๊ฐ๋๋ฅผ ํ์ฉํ ์ ์์ต๋๋ค.
3.4 ์ธํฐ์ ํฐ (Interceptors)
์ธํฐ์ ํฐ๋ ์์ฒญ๊ณผ ์๋ต์ ๊ฐ๋ก์ฑ๊ณ ๋ณํํ ์ ์๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ ๋๋ค. ๋ก๊น , ์บ์ฑ, ์๋ต ๋ณํ ๋ฑ์ ์ฌ์ฉ๋ ์ ์์ต๋๋ค.
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()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
</any>
์ธํฐ์ ํฐ๋ฅผ ์ ์ฉํ๋ ค๋ฉด:
@UseInterceptors(LoggingInterceptor)
@Controller('users')
export class UserController {
// ...
}
๐ ํ์ต ํฌ์ธํธ: ์ธํฐ์ ํฐ๋ ํก๋จ ๊ด์ฌ์ฌ(cross-cutting concerns)๋ฅผ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ค๋๋ค. ์ฌ๋ฅ๋ท์์ ์ธํฐ์ ํฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ ์๋ต์ ๊ณตํต ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋, API ํธ์ถ ์๊ฐ์ ์ธก์ ํ์ฌ ์ฑ๋ฅ์ ๋ชจ๋ํฐ๋งํ๋ ๋ฑ์ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
3.5 ์ค์ ์์ : ์ฌ๋ฅ๋ท์ ๊ณ ๊ธ ๊ธฐ๋ฅ ๊ตฌํ
์ด์ ์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ํ์ฉํ์ฌ ์ฌ๋ฅ๋ท์ ์ผ๋ถ ๊ธฐ๋ฅ์ ๊ตฌํํด ๋ณผ๊น์?
// auth.guard.ts
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
return request.headers.authorization === 'Bearer valid-token';
}
}
// logging.interceptor.ts
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const method = request.method;
const url = request.url;
console.log(`[${method}] ${url} - ${new Date().toISOString()}`);
return next.handle().pipe(
tap(() => console.log(`[${method}] ${url} - Completed`))
);
}
}
// validation.pipe.ts
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
if (!value.name || typeof value.name !== 'string') {
throw new BadRequestException('Invalid name');
}
if (!value.price || typeof value.price !== 'number') {
throw new BadRequestException('Invalid price');
}
return value;
}
}
// talent.controller.ts
@Controller('talents')
@UseGuards(AuthGuard)
@UseInterceptors(LoggingInterceptor)
export class TalentController {
@Post()
createTalent(@Body(ValidationPipe) createTalentDto: CreateTalentDto) {
// ์ฌ๋ฅ ์์ฑ ๋ก์ง
}
@Get()
findAllTalents() {
// ๋ชจ๋ ์ฌ๋ฅ ์กฐํ ๋ก์ง
}
}
</any>
์ด ์์ ์์:
- ๐ก๏ธ AuthGuard๋ ๋ชจ๋ ์ฌ๋ฅ ๊ด๋ จ API์ ๋ํ ์ธ์ฆ์ ์ฒ๋ฆฌํฉ๋๋ค.
- ๐ LoggingInterceptor๋ ๋ชจ๋ ์์ฒญ๊ณผ ์๋ต์ ๋ก๊น ํฉ๋๋ค.
- ๐ ValidationPipe๋ ์ฌ๋ฅ ์์ฑ ์ ์ ๋ ฅ ๋ฐ์ดํฐ์ ์ ํจ์ฑ์ ๊ฒ์ฌํฉ๋๋ค.
๐ ์ฑ์ฅ ํฌ์ธํธ: ์ด๋ฌํ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ์กฐํฉํ์ฌ ์ฌ์ฉํ๋ฉด, ์์ ํ๊ณ ์ ์ง๋ณด์๊ฐ ์ฉ์ดํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์์ต๋๋ค. ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ํ๋ซํผ์์๋ ์ด๋ฌํ ๊ธฐ๋ฅ๋ค์ด ์ฝ๋์ ํ์ง๊ณผ ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ ์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ NestJS์ ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค์ ๋ํด ์์๋ณด์์ต๋๋ค. ์ด ๊ธฐ๋ฅ๋ค์ ๋ง์คํฐํ๋ฉด, ์ฌ๋ฌ๋ถ์ ๋ ๊ฐ๋ ฅํ๊ณ ์ ์ฐํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ ์ ์์ ๊ฒ์ ๋๋ค. ๋ค์ ์น์ ์์๋ NestJS์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํตํฉ๊ณผ ORM ์ฌ์ฉ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. Ready to dive deeper? Let's go! ๐โโ๏ธ
4. NestJS์ ๋ฐ์ดํฐ๋ฒ ์ด์ค: TypeORM์ ํ์ฉํ ๋ฐ์ดํฐ ๊ด๋ฆฌ ๐๏ธ
์น ์ ํ๋ฆฌ์ผ์ด์ ์ ํต์ฌ์ ๋ฐ์ดํฐ ๊ด๋ฆฌ์ ๋๋ค. NestJS๋ ๋ค์ํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ORM(Object-Relational Mapping)์ ์ง์ํ์ง๋ง, ์ด ์น์ ์์๋ ํนํ TypeORM๊ณผ์ ํตํฉ์ ์ด์ ์ ๋ง์ถ๊ฒ ์ต๋๋ค. TypeORM์ TypeScript์ ์ ์ด์ธ๋ฆฌ๋ฉฐ, NestJS์ ํจ๊ป ์ฌ์ฉํ๊ธฐ์ ๋งค์ฐ ์ ํฉํฉ๋๋ค. ๐ค
4.1 TypeORM ์๊ฐ
TypeORM์ Node.js์์ ์ฌ์ฉํ ์ ์๋ ORM ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, TypeScript๋ก ์์ฑ๋์ด ์์ด NestJS์ ๊ถํฉ์ด ์ข์ต๋๋ค.
๐ ํต์ฌ ๊ฐ๋ : TypeORM์ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ TypeScript ํด๋์ค๋ก ํํํ ์ ์์ผ๋ฉฐ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์ ๊ฐ์ฒด ์งํฅ์ ์ผ๋ก ์ํํ ์ ์์ต๋๋ค.
4.2 NestJS์ TypeORM ํตํฉํ๊ธฐ
๋จผ์ , ํ์ํ ํจํค์ง๋ฅผ ์ค์นํฉ์๋ค:
npm install @nestjs/typeorm typeorm mysql2
๊ทธ๋ฆฌ๊ณ AppModule์ TypeORM์ ์ค์ ํฉ๋๋ค:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'talentnet',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
],
})
export class AppModule {}
โ ๏ธ ์ฃผ์: production ํ๊ฒฝ์์๋ synchronize: true ์ต์ ์ ์ฌ์ฉํ์ง ๋ง์ธ์. ์ด ์ต์ ์ ๊ฐ๋ฐ ์ค์๋ ํธ๋ฆฌํ์ง๋ง, ์ค์ ์ด์ ํ๊ฒฝ์์๋ ๋ฐ์ดํฐ ์์ค์ ์ํ์ด ์์ต๋๋ค.
4.3 ์ํฐํฐ ์ ์ํ๊ธฐ
์ฌ๋ฅ๋ท์ '์ฌ๋ฅ' ์ํฐํฐ๋ฅผ ์ ์ํด ๋ณผ๊น์?
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Talent {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
price: number;
@Column()
userId: number;
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
}
4.4 ๋ฆฌํฌ์งํ ๋ฆฌ ์ฌ์ฉํ๊ธฐ
TypeORM์ ๋ฆฌํฌ์งํ ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์ ์ํํ ์ ์์ต๋๋ค:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Talent } from './talent.entity';
@Injectable()
export class TalentService {
constructor(
@InjectRepository(Talent)
private talentRepository: Repository<talent>,
) {}
async create(talent: Partial<talent>): Promise<talent> {
const newTalent = this.talentRepository.create(talent);
return this.talentRepository.save(newTalent);
}
async findAll(): Promise<talent> {
return this.talentRepository.find();
}
async findOne(id: number): Promise<talent> {
return this.talentRepository.findOne({ where: { id } });
}
async update(id: number, talent: Partial<talent>): Promise<talent> {
await this.talentRepository.update(id, talent);
return this.talentRepository.findOne({ where: { id } });
}
async remove(id: number): Promise<void> {
await this.talentRepository.delete(id);
}
}
</void></talent></talent></talent></talent></talent></talent></talent>
๐ก ์ค์ ํ: ๋ณต์กํ ์ฟผ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ, TypeORM์ QueryBuilder๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, ํน์ ๊ฐ๊ฒฉ ๋ฒ์ ๋ด์ ์ฌ๋ฅ์ ๊ฒ์ํ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ ๋ ์ ์ฉํฉ๋๋ค.
4.5 ๊ด๊ณ ์ค์ ํ๊ธฐ
์ฌ๋ฅ๊ณผ ์ฌ์ฉ์ ์ฌ์ด์ ๊ด๊ณ๋ฅผ ์ค์ ํด ๋ด ์๋ค:
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
import { Talent } from './talent.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@OneToMany(() => Talent, talent => talent.user)
talents: Talent[];
}
// talent.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.entity';
@Entity()
export class Talent {
// ... ๊ธฐ์กด ํ๋๋ค
@ManyToOne(() => User, user => user.talents)
user: User;
}
4.6 ํธ๋์ญ์ ์ฌ์ฉํ๊ธฐ4.6 ํธ๋์ญ์ ์ฌ์ฉํ๊ธฐ
๋ฐ์ดํฐ์ ์ผ๊ด์ฑ์ ์ ์งํ๊ธฐ ์ํด ํธ๋์ญ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. TypeORM์์๋ ํธ๋์ญ์ ์ ์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Connection } from 'typeorm';
import { Talent } from './talent.entity';
import { User } from './user.entity';
@Injectable()
export class TalentService {
constructor(
@InjectRepository(Talent)
private talentRepository: Repository<talent>,
@InjectRepository(User)
private userRepository: Repository<user>,
private connection: Connection,
) {}
async createTalentWithUser(talentData: Partial<talent>, userId: number): Promise<talent> {
const queryRunner = this.connection.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
const user = await this.userRepository.findOne({ where: { id: userId } });
if (!user) {
throw new Error('User not found');
}
const talent = this.talentRepository.create(talentData);
talent.user = user;
await queryRunner.manager.save(talent);
await queryRunner.commitTransaction();
return talent;
} catch (err) {
await queryRunner.rollbackTransaction();
throw err;
} finally {
await queryRunner.release();
}
}
}
</talent></talent></user></talent>
โ ๏ธ ์ฃผ์: ํธ๋์ญ์ ์ ์ฌ์ฉํ ๋๋ ํญ์ ์๋ฌ ์ฒ๋ฆฌ์ ๋กค๋ฐฑ์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค. ์ ์์ ์์๋ try-catch ๋ธ๋ก์ ์ฌ์ฉํ์ฌ ์๋ฌ ๋ฐ์ ์ ํธ๋์ญ์ ์ ๋กค๋ฐฑํ๊ณ ์์ต๋๋ค.
4.7 ๋ง์ด๊ทธ๋ ์ด์ ๊ด๋ฆฌ
ํ๋ก๋์ ํ๊ฒฝ์์๋ ์คํค๋ง ๋ณ๊ฒฝ์ ์์ ํ๊ฒ ๊ด๋ฆฌํ๊ธฐ ์ํด ๋ง์ด๊ทธ๋ ์ด์ ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค. TypeORM์ ๋ง์ด๊ทธ๋ ์ด์ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
// ๋ง์ด๊ทธ๋ ์ด์
์์ฑ
npx typeorm migration:create -n CreateTalentTable
// ๋ง์ด๊ทธ๋ ์ด์
์คํ
npx typeorm migration:run
// ๋ง์ด๊ทธ๋ ์ด์
๋๋๋ฆฌ๊ธฐ
npx typeorm migration:revert
๐ก ์ค์ ํ: ๋ง์ด๊ทธ๋ ์ด์ ์คํฌ๋ฆฝํธ๋ฅผ ๋ฒ์ ๊ด๋ฆฌ ์์คํ ์ ํฌํจ์ํค์ธ์. ์ด๋ ๊ฒ ํ๋ฉด ํ์๋ค๊ณผ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์คํค๋ง ๋ณ๊ฒฝ ๋ด์ญ์ ๊ณต์ ํ๊ณ ์ถ์ ํ ์ ์์ต๋๋ค.
4.8 ์ฑ๋ฅ ์ต์ ํ
๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์ฑ๋ฅ์ด ์ค์ํฉ๋๋ค. TypeORM์์ ์ฑ๋ฅ์ ์ต์ ํํ๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
- ์ธ๋ฑ์ค ์ฌ์ฉ: ์์ฃผ ๊ฒ์๋๋ ํ๋์ ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- Eager/Lazy ๋ก๋ฉ ๊ด๋ฆฌ: ํ์ํ ๊ฒฝ์ฐ์๋ง ๊ด๊ณ๋ฅผ ๋ก๋ํฉ๋๋ค.
- ํ์ด์ง๋ค์ด์ ๊ตฌํ: ๋๋์ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ๋ก๋ํ์ง ์๋๋ก ํฉ๋๋ค.
@Entity()
export class Talent {
// ...
@Index()
@Column()
title: string;
// ...
}
@Entity()
export class User {
// ...
@OneToMany(() => Talent, talent => talent.user, { lazy: true })
talents: Promise<talent>;
}
</talent>
async findTalents(page: number, limit: number): Promise<talent> {
return this.talentRepository.find({
skip: (page - 1) * limit,
take: limit,
});
}
</talent>
๐ ์ฑ์ฅ ํฌ์ธํธ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฑ๋ฅ ์ต์ ํ๋ ์ง์์ ์ธ ๊ณผ์ ์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฉ ํจํด์ ๋ชจ๋ํฐ๋งํ๊ณ , ํ์์ ๋ฐ๋ผ ์ฟผ๋ฆฌ์ ์คํค๋ง๋ฅผ ์ต์ ํํ์ธ์. ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ํ๋ซํผ์์๋ ์ฌ์ฉ์ ์๊ฐ ์ฆ๊ฐํจ์ ๋ฐ๋ผ ์ฑ๋ฅ ์ต์ ํ๊ฐ ๋์ฑ ์ค์ํด์ง๋๋ค.
4.9 ์ค์ ์์ : ์ฌ๋ฅ๋ท์ ๊ฒ์ ๊ธฐ๋ฅ ๊ตฌํ
์ด์ ์ฐ๋ฆฌ๊ฐ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ฌ๋ฅ๋ท์ ๊ฒ์ ๊ธฐ๋ฅ์ ๊ตฌํํด ๋ณผ๊น์?
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Talent } from './talent.entity';
@Injectable()
export class TalentService {
constructor(
@InjectRepository(Talent)
private talentRepository: Repository<talent>,
) {}
async searchTalents(query: string, page: number = 1, limit: number = 10): Promise<{ talents: Talent[], total: number }> {
const [talents, total] = await this.talentRepository.createQueryBuilder('talent')
.where('talent.title LIKE :query OR talent.description LIKE :query', { query: `%${query}%` })
.leftJoinAndSelect('talent.user', 'user')
.orderBy('talent.createdAt', 'DESC')
.skip((page - 1) * limit)
.take(limit)
.getManyAndCount();
return { talents, total };
}
}
</talent>
์ด ์์ ์์๋:
- ์ ๋ชฉ์ด๋ ์ค๋ช ์ ๊ฒ์์ด๊ฐ ํฌํจ๋ ์ฌ๋ฅ์ ์ฐพ์ต๋๋ค.
- ์ฌ๋ฅ๊ณผ ์ฐ๊ด๋ ์ฌ์ฉ์ ์ ๋ณด๋ ํจ๊ป ๊ฐ์ ธ์ต๋๋ค.
- ๊ฒฐ๊ณผ๋ฅผ ์ต์ ์์ผ๋ก ์ ๋ ฌํฉ๋๋ค.
- ํ์ด์ง๋ค์ด์ ์ ๊ตฌํํ์ฌ ์ฑ๋ฅ์ ๊ฐ์ ํฉ๋๋ค.
๐ก ์ค์ ํ: ์ค์ ์๋น์ค์์๋ ์ ๋ฌธ ๊ฒ์ ์์ง(์: Elasticsearch)์ ๋์ ํ๋ ๊ฒ์ ๊ณ ๋ คํด๋ณด์ธ์. ๋๊ท๋ชจ ๋ฐ์ดํฐ์์์ ์ ๋ฌธ ๊ฒ์, ํํฐ๋ง, ์ ๋ ฌ ๋ฑ์ ๋ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ NestJS์ TypeORM์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์์ต๋๋ค. ์ด๋ฌํ ๊ธฐ์ ์ ๋ง์คํฐํ๋ฉด, ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ ์ง๋ณด์๊ฐ ์ฉ์ดํ ๋ฐฑ์๋ ์์คํ ์ ๊ตฌ์ถํ ์ ์์ต๋๋ค. ๋ค์ ์น์ ์์๋ NestJS์์์ ํ ์คํ ๊ณผ ๋ฐฐํฌ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. Ready to level up your NestJS skills even further? Let's go! ๐
5. NestJS ํ ์คํ ๊ณผ ๋ฐฐํฌ: ํ์ง๊ณผ ์์ ์ฑ ํ๋ณดํ๊ธฐ ๐งช๐
์ํํธ์จ์ด ๊ฐ๋ฐ์์ ํ ์คํ ๊ณผ ๋ฐฐํฌ๋ ๋งค์ฐ ์ค์ํ ๊ณผ์ ์ ๋๋ค. ์ด ์น์ ์์๋ NestJS ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธ ์์ฑ ๋ฐฉ๋ฒ๊ณผ ํจ๊ณผ์ ์ธ ๋ฐฐํฌ ์ ๋ต์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์ด๋ฅผ ํตํด ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ง๊ณผ ์์ ์ฑ์ ๋์ผ ์ ์์ต๋๋ค. ๐ ๏ธ
5.1 NestJS ํ ์คํ ๊ธฐ์ด
NestJS๋ Jest๋ฅผ ๊ธฐ๋ณธ ํ ์คํ ํ๋ ์์ํฌ๋ก ์ฌ์ฉํฉ๋๋ค. ์ ๋ ํ ์คํธ, ํตํฉ ํ ์คํธ, e2e ํ ์คํธ ๋ฑ ๋ค์ํ ์ ํ์ ํ ์คํธ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
๐ ํต์ฌ ๊ฐ๋ : ํ ์คํธ ์ฃผ๋ ๊ฐ๋ฐ(TDD)์ ์ค์ฒํ๋ฉด ์ฝ๋์ ํ์ง์ ๋์ด๊ณ ๋ฒ๊ทธ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค. NestJS์ ๋ชจ๋ํ๋ ๊ตฌ์กฐ๋ ๋จ์ ํ ์คํธ ์์ฑ์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
5.2 ์ ๋ ํ ์คํธ ์์ฑํ๊ธฐ
์๋น์ค ๋ก์ง์ ๋ํ ์ ๋ ํ ์คํธ๋ฅผ ์์ฑํด ๋ด ์๋ค:
// talent.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { TalentService } from './talent.service';
import { Talent } from './talent.entity';
describe('TalentService', () => {
let service: TalentService;
let mockRepository;
beforeEach(async () => {
mockRepository = {
find: jest.fn(),
create: jest.fn(),
save: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
TalentService,
{
provide: getRepositoryToken(Talent),
useValue: mockRepository,
},
],
}).compile();
service = module.get<talentservice>(TalentService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('findAll', () => {
it('should return an array of talents', async () => {
const result = [{ id: 1, title: 'Test Talent' }];
mockRepository.find.mockResolvedValue(result);
expect(await service.findAll()).toBe(result);
});
});
});
</talentservice>
๐ก ์ค์ ํ: ๋ชจ๋ ์ฃ์ง ์ผ์ด์ค๋ฅผ ๊ณ ๋ คํ์ฌ ํ ์คํธ๋ฅผ ์์ฑํ์ธ์. ์๋ฅผ ๋ค์ด, ๋ฐ์ดํฐ๊ฐ ์๋ ๊ฒฝ์ฐ, ์๋ชป๋ ์ ๋ ฅ์ด ์ฃผ์ด์ง ๊ฒฝ์ฐ ๋ฑ์ ๋ํ ํ ์คํธ๋ ํฌํจ์ํค๋ฉด ์ข์ต๋๋ค.
5.3 e2e ํ ์คํธ ์์ฑํ๊ธฐ
e2e(end-to-end) ํ ์คํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฒด ํ๋ฆ์ ํ ์คํธํฉ๋๋ค:
// test/talent.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('TalentController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/talents (GET)', () => {
return request(app.getHttpServer())
.get('/talents')
.expect(200)
.expect('Content-Type', /json/)
.expect(res => {
expect(Array.isArray(res.body)).toBeTruthy();
});
});
afterAll(async () => {
await app.close();
});
});
5.4 ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง ํ์ธ
Jest์ ์ปค๋ฒ๋ฆฌ์ง ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค:
npm run test:cov
๐ ์ฑ์ฅ ํฌ์ธํธ: ๋์ ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ ์งํ๋ฉด ์ฝ๋ ๋ณ๊ฒฝ ์ ๋ฐ์ํ ์ ์๋ ํ๊ท ์ค๋ฅ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค. ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ์์คํ ์์๋ ํนํ ์ค์ํฉ๋๋ค.
5.5 CI/CD ํ์ดํ๋ผ์ธ ๊ตฌ์ถ
์ง์์ ํตํฉ(CI)๊ณผ ์ง์์ ๋ฐฐํฌ(CD)๋ฅผ ๊ตฌ์ถํ์ฌ ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ์๋ํํ ์ ์์ต๋๋ค. GitHub Actions๋ฅผ ์ฌ์ฉํ ์์๋ฅผ ์ดํด๋ด ์๋ค:
# .github/workflows/main.yml
name: CI/CD
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '14.x'
- run: npm ci
- run: npm run build --if-present
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v2
- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.12.12
with:
heroku_api_key: ${{secrets.HEROKU_API_KEY}}
heroku_app_name: "your-app-name"
heroku_email: "your-email@example.com"
โ ๏ธ ์ฃผ์: CI/CD ํ์ดํ๋ผ์ธ์์ ์ฌ์ฉ๋๋ ๋น๋ฐ ํค๋ ํ๊ฒฝ ๋ณ์๋ ์์ ํ๊ฒ ๊ด๋ฆฌํด์ผ ํฉ๋๋ค. GitHub Secrets ๋ฑ์ ํ์ฉํ์ฌ ์ค์ ์ ๋ณด๋ฅผ ๋ณดํธํ์ธ์.
5.6 ๋ฐฐํฌ ์ ๋ต
NestJS ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐฐํฌํ ๋ ๊ณ ๋ คํด์ผ ํ ๋ช ๊ฐ์ง ์ ๋ต์ด ์์ต๋๋ค:
- ํ๊ฒฝ ๋ณ์ ์ฌ์ฉ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ ๋ณด, API ํค ๋ฑ์ ํ๊ฒฝ ๋ณ์๋ก ๊ด๋ฆฌํฉ๋๋ค.
- PM2 ์ฌ์ฉ: ํ๋ก๋์ ํ๊ฒฝ์์ Node.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ด๋ฆฌํ๋ ๋ฐ ์ ์ฉํฉ๋๋ค.
- ๋ก๋ ๋ฐธ๋ฐ์ฑ: ํธ๋ํฝ์ด ๋ง์ ๊ฒฝ์ฐ, ์ฌ๋ฌ ์ธ์คํด์ค๋ฅผ ์คํํ๊ณ ๋ก๋ ๋ฐธ๋ฐ์๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ๋ชจ๋ํฐ๋ง: New Relic, Datadog ๋ฑ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ์ ๋ชจ๋ํฐ๋งํฉ๋๋ค.
5.7 ์ฑ๋ฅ ์ต์ ํ
๋ฐฐํฌ ํ์๋ ์ง์์ ์ธ ์ฑ๋ฅ ์ต์ ํ๊ฐ ํ์ํฉ๋๋ค:
- ์บ์ฑ ์ ๋ต ๊ตฌํ (Redis ๋ฑ ์ฌ์ฉ)
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์ต์ ํ
- ๋น๋๊ธฐ ์์ ์ฒ๋ฆฌ (Bull.js ๋ฑ ์ฌ์ฉ)
- CDN ํ์ฉ
๐ก ์ค์ ํ: ์ฑ๋ฅ ๋ณ๋ชฉ ํ์์ ์๋ณํ๊ธฐ ์ํด ์ฃผ๊ธฐ์ ์ผ๋ก ํ๋กํ์ผ๋ง์ ์ํํ์ธ์. ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋๊ท๋ชจ ํ๋ซํผ์์๋ ์์ ์ต์ ํ๋ ํฐ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
5.8 ๋ณด์ ๊ณ ๋ ค์ฌํญ
์ ํ๋ฆฌ์ผ์ด์ ๋ณด์์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ๋ค์ ์ฌํญ๋ค์ ๊ณ ๋ คํ์ธ์:
- HTTPS ์ฌ์ฉ
- ์ ๋ ฅ ๋ฐ์ดํฐ ๊ฒ์ฆ ๋ฐ ์ด๊ท
- CORS ์ค์
- Rate limiting ๊ตฌํ
- ์ ๊ธฐ์ ์ธ ๋ณด์ ๊ฐ์ฌ ์ํ
โ ๏ธ ์ฃผ์: ๋ณด์์ ์ง์์ ์ธ ๊ณผ์ ์ ๋๋ค. ์๋ก์ด ์ทจ์ฝ์ ์ด ๋ฐ๊ฒฌ๋ ์ ์์ผ๋ฏ๋ก, ํญ์ ์ต์ ๋ณด์ ์ ๋ฐ์ดํธ๋ฅผ ์ ์ฉํ๊ณ ๋ณด์ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด์ธ์.
5.9 ์ค์ ์์ : ์ฌ๋ฅ๋ท ๋ฐฐํฌ ์ฒดํฌ๋ฆฌ์คํธ
์ฌ๋ฅ๋ท์ ๋ฐฐํฌํ ๋ ์ฌ์ฉํ ์ ์๋ ์ฒดํฌ๋ฆฌ์คํธ๋ฅผ ๋ง๋ค์ด ๋ด ์๋ค:
[ ] ๋ชจ๋ ํ
์คํธ ํต๊ณผ ํ์ธ
[ ] ํ๊ฒฝ ๋ณ์ ์ค์ (๋ฐ์ดํฐ๋ฒ ์ด์ค URL, API ํค ๋ฑ)
[ ] ํ๋ก๋์
๋น๋ ์์ฑ
[ ] ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์
์คํ
[ ] PM2 ๋๋ ์ ์ฌํ ํ๋ก์ธ์ค ๊ด๋ฆฌ์ ์ค์
[ ] HTTPS ์ค์
[ ] ๋ก๋ ๋ฐธ๋ฐ์ ๊ตฌ์ฑ (ํ์ํ ๊ฒฝ์ฐ)
[ ] ๋ชจ๋ํฐ๋ง ๋๊ตฌ ์ค์ (New Relic, Datadog ๋ฑ)
[ ] ๋ก๊ทธ ๊ด๋ฆฌ ์์คํ
์ฐ๋
[ ] ๋ฐฑ์
์์คํ
ํ์ธ
[ ] ์ฑ๋ฅ ํ
์คํธ ์ํ
[ ] ๋ณด์ ์ค์บ ์คํ
[ ] ๋กค๋ฐฑ ๊ณํ ์๋ฆฝ
[ ] ์ฌ์ฉ์ ๋์ ๊ณต์ง ์ค๋น (์๋ก์ด ๊ธฐ๋ฅ์ด๋ ๋ณ๊ฒฝ์ฌํญ์ด ์๋ ๊ฒฝ์ฐ)
๐ ์ฑ์ฅ ํฌ์ธํธ: ๋ฐฐํฌ ํ๋ก์ธ์ค๋ฅผ ์๋ํํ๊ณ ํ์คํํ๋ฉด ์ธ์ ์ค๋ฅ๋ฅผ ์ค์ด๊ณ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์์ต๋๋ค. ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ์์คํ ์์๋ ์ด๋ฌํ ์ฒด๊ณ์ ์ธ ์ ๊ทผ์ด ํนํ ์ค์ํฉ๋๋ค.
์ด๋ ๊ฒ NestJS ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํ ๊ณผ ๋ฐฐํฌ์ ๋ํด ์์๋ณด์์ต๋๋ค. ์ด๋ฌํ ์ค์ฒ์ ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋๊ท๋ชจ ํ๋ซํผ์ ์์ ์ฑ๊ณผ ์ ๋ขฐ์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค. ํ ์คํธ ์์ฑ๋ถํฐ ๋ฐฐํฌ, ๊ทธ๋ฆฌ๊ณ ์ง์์ ์ธ ๋ชจ๋ํฐ๋ง๊ณผ ์ต์ ํ๊น์ง, ์ ์ฒด ๊ฐ๋ฐ ๋ผ์ดํ์ฌ์ดํด์ ๊ณ ๋ คํ ์ ๊ทผ์ด ์ค์ํฉ๋๋ค. ์ด์ ์ฌ๋ฌ๋ถ์ NestJS๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฌ๊ณ ํ๊ณ ํ์ฅ ๊ฐ๋ฅํ ๋ฐฑ์๋ ์์คํ ์ ๊ตฌ์ถํ ์ค๋น๊ฐ ๋์์ต๋๋ค. Happy coding! ๐๐จโ๐ป๐ฉโ๐ป
6. ๊ฒฐ๋ก : NestJS ๋ง์คํฐ์ ๊ธธ ๐
์ถํํฉ๋๋ค! ์ฌ๋ฌ๋ถ์ ์ด์ NestJS์ ํต์ฌ ๊ฐ๋ ๋ถํฐ ๊ณ ๊ธ ๊ธฐ๋ฅ, ๊ทธ๋ฆฌ๊ณ ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ๊ณผ ๋ฐฐํฌ๊น์ง ์ ๋ฐ์ ์ธ ๋ด์ฉ์ ํ์ตํ์ต๋๋ค. ์ด ์ง์์ ๋ฐํ์ผ๋ก ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ํ๋ซํผ์ ๊ตฌ์ถํ ์ ์๋ ๊ธฐ๋ฐ์ ๋ง๋ จํ์ต๋๋ค. ๐
6.1 ํต์ฌ ์์ฝ
- NestJS์ ๋ชจ๋ํ๋ ๊ตฌ์กฐ์ ์์กด์ฑ ์ฃผ์ ์์คํ
- ์ปจํธ๋กค๋ฌ, ์๋น์ค, ๋ฏธ๋ค์จ์ด, ํ์ดํ, ๊ฐ๋, ์ธํฐ์ ํฐ์ ์ญํ ๊ณผ ์ฌ์ฉ๋ฒ
- TypeORM์ ํ์ฉํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ด๋ฆฌ
- ๋จ์ ํ ์คํธ์ e2e ํ ์คํธ ์์ฑ ๋ฐฉ๋ฒ
- CI/CD ํ์ดํ๋ผ์ธ ๊ตฌ์ถ๊ณผ ํจ๊ณผ์ ์ธ ๋ฐฐํฌ ์ ๋ต
๐ก ํต์ฌ ํต์ฐฐ: NestJS์ ์ง์ ํ ๊ฐ์ ์ ๊ทธ ๊ตฌ์กฐํ๋ ์ํคํ ์ฒ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋๊ท๋ชจ ์ ํ๋ฆฌ์ผ์ด์ ๋ ์ฒด๊ณ์ ์ผ๋ก ๊ด๋ฆฌํ๊ณ ํ์ฅํ ์ ์์ต๋๋ค.
6.2 ๋ค์ ๋จ๊ณ
NestJS ๋ง์คํฐ๊ฐ ๋๊ธฐ ์ํ ์ฌ์ ์ ์ฌ๊ธฐ์ ๋๋์ง ์์ต๋๋ค. ๋ค์์ ์ฌ๋ฌ๋ถ์ด ๋ ๊น์ด ํ๊ตฌํ ์ ์๋ ์ฃผ์ ๋ค์ ๋๋ค:
- GraphQL๊ณผ NestJS ํตํฉ
- ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ ๊ตฌํ
- WebSocket์ ํ์ฉํ ์ค์๊ฐ ๊ธฐ๋ฅ ๊ฐ๋ฐ
- ์๋ฒ๋ฆฌ์ค ์ํคํ ์ฒ์ NestJS
- ๊ณ ๊ธ ๋ณด์ ๊ธฐ๋ฒ (OAuth, JWT ๋ฑ)
6.3 ์ค์ ํ๋ก์ ํธ ์์ด๋์ด
ํ์ตํ ๋ด์ฉ์ ์ค์ ๋ก ์ ์ฉํด๋ณด๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ ํ์ต ๋ฐฉ๋ฒ์ ๋๋ค. ๋ค์๊ณผ ๊ฐ์ ํ๋ก์ ํธ๋ฅผ ์๋ํด ๋ณด์ธ์:
- ๋ธ๋ก๊ทธ ํ๋ซํผ ๊ตฌ์ถ (์ฌ์ฉ์ ์ธ์ฆ, ๊ฒ์๊ธ CRUD, ๋๊ธ ๊ธฐ๋ฅ)
- ์ค์๊ฐ ์ฑํ ์ ํ๋ฆฌ์ผ์ด์ (WebSocket ํ์ฉ)
- ์จ๋ผ์ธ ์ผํ๋ชฐ ๋ฐฑ์๋ (์ํ ๊ด๋ฆฌ, ์ฅ๋ฐ๊ตฌ๋, ๊ฒฐ์ ํตํฉ)
- API ๊ฒ์ดํธ์จ์ด ๊ตฌํ (๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ)
๐ ์ฑ์ฅ ์กฐ์ธ: ์คํ ์์ค ํ๋ก์ ํธ์ ๊ธฐ์ฌํ๋ ๊ฒ๋ ์ข์ ํ์ต ๋ฐฉ๋ฒ์ ๋๋ค. NestJS ์ํ๊ณ์ ๋ค์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ๋ฌ๊ทธ์ธ์ ๊ธฐ์ฌํ๋ฉด์ ์ค์ ๊ฒฝํ์ ์์๋ณด์ธ์.
6.4 ์ปค๋ฎค๋ํฐ ์ฐธ์ฌ
NestJS ์ปค๋ฎค๋ํฐ์ ์ฐธ์ฌํ์ฌ ์ง์์ ์ผ๋ก ํ์ตํ๊ณ ์ฑ์ฅํ์ธ์:
- NestJS ๊ณต์ Discord ์ฑ๋ ์ฐธ์ฌ
- Stack Overflow์์ ์ง๋ฌธํ๊ณ ๋ต๋ณํ๊ธฐ
- NestJS ๊ด๋ จ ์ปจํผ๋ฐ์ค๋ ๋ฐ์ ์ฐธ์
- ๊ธฐ์ ๋ธ๋ก๊ทธ ์ด์ํ๊ธฐ
6.5 ๋ง์ง๋ง ์กฐ์ธ
NestJS ๋ง์คํฐ๊ฐ ๋๋ ๊ธธ์ ๋์์๋ ํ์ต๊ณผ ์ค์ฒ์ ๊ณผ์ ์ ๋๋ค. ๊ธฐ์ ์ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ผ๋ฏ๋ก, ํญ์ ์๋ก์ด ๊ฒ์ ๋ฐฐ์ฐ๊ณ ์ ์ฉํ๋ ค๋ ์์ธ๊ฐ ์ค์ํฉ๋๋ค.
๐ก ์ต์ข ํ: ์ฝ๋๋ฅผ ์์ฑํ ๋๋ง๋ค "์ด ์ฝ๋๋ฅผ ์ด๋ป๊ฒ ๋ ๊ฐ์ ํ ์ ์์๊น?"๋ผ๊ณ ์๋ฌธํด ๋ณด์ธ์. ์ด๋ฌํ ๋นํ์ ์ฌ๊ณ ๊ฐ ์ฌ๋ฌ๋ถ์ ๋ ๋์ ๊ฐ๋ฐ์๋ก ๋ง๋ค์ด ์ค ๊ฒ์ ๋๋ค.
์ฌ๋ฌ๋ถ์ NestJS ์ฌ์ ์ด ์ฌ๊ธฐ์ ๋๋์ง ์๊ธฐ๋ฅผ ๋ฐ๋๋ ๋ค. ์ด์ ์ฌ๋ฌ๋ถ์ ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ๋ณต์กํ ํ๋ซํผ์ ๊ตฌ์ถํ ์ ์๋ ์ง์๊ณผ ๋๊ตฌ๋ฅผ ๊ฐ์ถ๊ฒ ๋์์ต๋๋ค. ์ด๋ฅผ ๋ฐํ์ผ๋ก ๋ ํฐ ๋์ ์ ๋์๊ณ , ํ์ ์ ์ธ ์๋ฃจ์ ์ ๋ง๋ค์ด๋ด๊ธธ ๋ฐ๋๋๋ค. NestJS์ ์ธ๊ณ๋ ๊ดํํ๊ณ ๊ฐ๋ฅ์ฑ์ผ๋ก ๊ฐ๋ ์ฐจ ์์ต๋๋ค. ์ฌ๋ฌ๋ถ์ ์ฐฝ์์ฑ๊ณผ ์ด์ ์ผ๋ก ์ด ๊ฐ๋ฅ์ฑ์ ํ์ค๋ก ๋ง๋ค์ด ๋๊ฐ์ธ์!
๋ง์ง๋ง์ผ๋ก, ๊ฐ๋ฐ์ ๋จ์ํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ ์ด์์ ์๋ฏธ๋ฅผ ๊ฐ์ง๋๋ค. ์ฌ์ฉ์์ ๋์ฆ๋ฅผ ์ดํดํ๊ณ , ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ฉฐ, ๊ฐ์น๋ฅผ ์ฐฝ์ถํ๋ ๊ณผ์ ์ ๋๋ค. NestJS๋ ์ด๋ฌํ ๋ชฉํ๋ฅผ ๋ฌ์ฑํ๊ธฐ ์ํ ๊ฐ๋ ฅํ ๋๊ตฌ์ผ ๋ฟ์ ๋๋ค. ํญ์ ํฐ ๊ทธ๋ฆผ์ ๋ณด๋ฉด์ ๊ฐ๋ฐ์ ์ํ์ธ์.
์ฌ๋ฌ๋ถ์ NestJS ๋ง์คํฐ ์ฌ์ ์ ํ์ด์ด ํจ๊ปํ๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. ๋์์์ด ํ์ตํ๊ณ , ๋์ ํ๊ณ , ์ฑ์ฅํ์ธ์. ๋ฏธ๋์ ์๋ จ๋ NestJS ๊ฐ๋ฐ์์ธ ์ฌ๋ฌ๋ถ์ ์์ํฉ๋๋ค! ๐๐
7. ๋ถ๋ก: ์ ์ฉํ ๋ฆฌ์์ค ๋ฐ ๋๊ตฌ ๐๐ ๏ธ
NestJS ํ์ต๊ณผ ๊ฐ๋ฐ์ ๋์ฑ ํจ๊ณผ์ ์ผ๋ก ํ ์ ์๋๋ก ๋ช ๊ฐ์ง ์ ์ฉํ ๋ฆฌ์์ค์ ๋๊ตฌ๋ฅผ ์๊ฐํฉ๋๋ค. ์ด๋ค์ ํ์ฉํ์ฌ ์ฌ๋ฌ๋ถ์ NestJS ์คํฌ์ ํ ๋จ๊ณ ๋ ๋ฐ์ ์์ผ ๋ณด์ธ์!
7.1 ๊ณต์ ๋ฌธ์ ๋ฐ ํ์ต ์๋ฃ
- NestJS ๊ณต์ ๋ฌธ์ - ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ด๊ณ ์ค์ํ ํ์ต ๋ฆฌ์์ค
- NestJS ๊ณต์ ๊ฐ์ข - ์ฒด๊ณ์ ์ธ ํ์ต์ ์ํ๋ค๋ฉด ์ถ์ฒ
- Awesome NestJS - NestJS ๊ด๋ จ ๋ฆฌ์์ค ๋ชจ์
7.2 ๊ฐ๋ฐ ๋๊ตฌ
- WebStorm - NestJS ๊ฐ๋ฐ์ ์ต์ ํ๋ IDE
- NestJS Snippets for VS Code - ์ฝ๋ ์์ฑ์ ๋๋ VS Code ํ์ฅ ํ๋ก๊ทธ๋จ
- Testing NestJS - NestJS ํ ์คํ ์ ์ํ ์ ์ฉํ ๋๊ตฌ ๋ชจ์
7.3 ์ปค๋ฎค๋ํฐ ๋ฐ ํฌ๋ผ
- NestJS Discord - ์ค์๊ฐ์ผ๋ก ๋์์ ๋ฐ์ ์ ์๋ ๊ณต์ ์ปค๋ฎค๋ํฐ
- Stack Overflow NestJS ํ๊ทธ - ๋ฌธ์ ํด๊ฒฐ์ ๋์์ด ๋๋ Q&A
- GitHub Discussions - NestJS ๊ด๋ จ ํ ๋ก ๋ฐ ์์ด๋์ด ๊ณต์
7.4 ๋ธ๋ก๊ทธ ๋ฐ ๋ด์ค๋ ํฐ
- Trilon Blog - NestJS ์ฝ์ด ํ์ ์ธ์ฌ์ดํธ๋ฅผ ์ป์ ์ ์๋ ๋ธ๋ก๊ทธ
- Dev.to NestJS ํ๊ทธ - ์ปค๋ฎค๋ํฐ ๋ฉค๋ฒ๋ค์ ๋ค์ํ NestJS ๊ด๋ จ ๊ธ
- NestJS ๋ด์ค๋ ํฐ - ์ต์ ์ ๋ฐ์ดํธ์ ํ์ ๋ฐ์๋ณผ ์ ์๋ ๊ณต์ ๋ด์ค๋ ํฐ
7.5 ์ ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ ๋ชจ๋
- @nestjsx/crud - CRUD ์์ ์ ๊ฐ์ํํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- @nestjs/terminus - ํฌ์ค์ฒดํฌ ๋ชจ๋
- @nestjs/config - ํ๊ฒฝ ์ค์ ๊ด๋ฆฌ๋ฅผ ์ํ ๋ชจ๋
- @ogma/nestjs-module - ๋ก๊น ์ ์ํ ๊ฐ๋ ฅํ ๋ชจ๋
๐ก ํ์ต ํ: ์ด๋ฌํ ๋ฆฌ์์ค๋ค์ ์ ๊ธฐ์ ์ผ๋ก ํ์ธํ๊ณ ํ์ฉํ์ธ์. ๊ธฐ์ ํธ๋ ๋์ ๋ฒ ์คํธ ํ๋ํฐ์ค๋ ๊ณ์ ๋ณํํ๋ฏ๋ก, ์ง์์ ์ธ ํ์ต์ด ์ค์ํฉ๋๋ค.
7.6 ์ฑ ์ถ์ฒ
- "NestJS in Action" by Kamil Mysliwiec (NestJS ์ฐฝ์์)
- "Building Node.js APIs with NestJS" by Zachary Harris
- "Hands-On RESTful Web Services with TypeScript 3" by Biharck Muniz Araรบjo
7.7 ์จ๋ผ์ธ ์ฝ์ค
- NestJS Zero to Hero - Modern TypeScript Back-end Development (Udemy)
- Node.js, Express, MongoDB & More: The Complete Bootcamp 2023 (Udemy - NestJS ์น์ ํฌํจ)
- Building Applications with NestJS (Pluralsight)
์ด๋ฌํ ๋ฆฌ์์ค๋ค์ ํ์ฉํ์ฌ ์ฌ๋ฌ๋ถ์ NestJS ์คํฌ์ ์ง์์ ์ผ๋ก ํฅ์์ํค์ธ์. ๊ธฐ์ ์ ์ญ๋๋ฟ๋ง ์๋๋ผ ๋ฌธ์ ํด๊ฒฐ ๋ฅ๋ ฅ, ์ํคํ ์ฒ ์ค๊ณ ๋ฅ๋ ฅ๋ ํจ๊ป ๋ฐ์ ์์ผ ๋๊ฐ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. NestJS ๋ง์คํฐ๋ก ๊ฐ๋ ์ฌ์ ์์ ์ด ๋ฆฌ์์ค๋ค์ด ๋ ๋ ํ ๊ธธ์ก์ด๊ฐ ๋์ด์ค ๊ฒ์ ๋๋ค.
๐ ์ต์ข ์กฐ์ธ: ๋จ์ํ ์๋นํ๋ ๊ฒ์ ๊ทธ์น์ง ๋ง๊ณ , ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ง์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์ธ์. ์ค์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ณผ์ ์์ ๊ฐ์ฅ ํฐ ์ฑ์ฅ์ด ์ด๋ฃจ์ด์ง๋๋ค. ์ฌ๋ฅ๋ท๊ณผ ๊ฐ์ ํ๋ก์ ํธ๋ฅผ ๋จ๊ณ์ ์ผ๋ก ๊ตฌํํด๋ณด๋ฉด์ ์ค๋ ฅ์ ํค์๋๊ฐ์ธ์!
NestJS ๋ง์คํฐ๊ฐ ๋๋ ์ฌ์ ์์ ์ด ๊ฐ์ด๋์ ๋ฆฌ์์ค๋ค์ด ์ฌ๋ฌ๋ถ์๊ฒ ๋์์ด ๋๊ธฐ๋ฅผ ๋ฐ๋๋๋ค. ๋์์๋ ํธ๊ธฐ์ฌ๊ณผ ํ์ต ์ด์ ์ผ๋ก ์ฌ๋ฌ๋ถ์ ๊ฐ๋ฐ ์คํฌ์ ํ ๋จ๊ณ ๋ ๋ฐ์ ์์ผ ๋๊ฐ์ธ์. ํ๋ฅญํ NestJS ๊ฐ๋ฐ์๋ก ์ฑ์ฅํ ์ฌ๋ฌ๋ถ์ ๋ฏธ๋๋ฅผ ์์ํฉ๋๋ค! ํ์ดํ ! ๐๐
- ์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์ฌ๋ฅ๋ท์ ์ต์ AI ๊ธฐ์ ๊ณผ ๋ฒ๋ฅ ์ ๊ธฐ๋ฐํ์ฌ ์์ฌ์ ์ง์ ์ฌ์ฐ๊ถ์ ์ ๊ทน์ ์ผ๋ก ๋ณดํธํ๋ฉฐ,
๋ฌด๋จ ์ฌ์ฉ ๋ฐ ์นจํด ํ์์ ๋ํด ๋ฒ์ ๋์์ ํ ๊ถ๋ฆฌ๋ฅผ ๋ณด์ ํฉ๋๋ค.
ยฉ 2025 ์ฌ๋ฅ๋ท | All rights reserved.
๋๊ธ 0๊ฐ