๐ก 02. ๋ชจ๋๊ณผ ์ปจํธ๋กค๋ฌ โ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ผ๋์ ์ถ์ ๋ฌธ
๐ ๊ฐ์
NestJS ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ผ๋์ธ ๋ชจ๋๊ณผ, ํด๋ผ์ด์ธํธ์ ์์ฒญ์ ๋ฐ์ ์๋ต์ ๋ฐํํ๋ ์ปจํธ๋กค๋ฌ์ ๋ชจ๋ ๊ฒ์ ๋ฐฐ์๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ ๋ชจ๋ (Module) โ ๊ฑด๋ฌผ์ ์ธต๊ณผ ๋ถ์ ๐ก
- ๐งฉ ์ปจํธ๋กค๋ฌ (Controller) โ ์๋ด๋ฐ์คํฌ ์ง์ ๐ข
- ๐งช ๋ฐ๋ผํด๋ณด๊ธฐ: ์ค์ ์ฃผ์ ์ปจํธ๋กค๋ฌ ๋ง๋ค๊ธฐ
- ๐ผ ๋ฒ ์คํธ ํ๋ํฐ์ค์ ์ค๋ฌด ํ
- ๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐๏ธ ์นํธ์ํธ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 20๋ถ (์ ์ฒด) / ํต์ฌ ํํธ๋ง: 10๋ถ
๐งณ ์ ์ ์ง์ ์ฒดํฌ๋ฆฌ์คํธ
์๋ ํญ๋ชฉ์ ๋ชจ๋ ์๋ฉด ๋ฐ๋ก [๋ชจ๋ ์น์
]๋ถํฐ ์ฝ์ด๋ ๋ผ:
- ๋ฐ์ฝ๋ ์ดํฐ(
@) ๋ฌธ๋ฒ์ ๋ณด๋ฉด ํจ์๋ ํด๋์ค์ ์์ฑ์ ๋ถ์ฌํ๋ ๊ฒ์์ ์๋ค. - HTTP ๋ฉ์๋(GET, POST, PUT, DELETE)์ ์ฐจ์ด๋ฅผ ์๋ค.
- 01์ฅ 'NestJS ์๊ฐ'์ ์จ์ดํฐ/์ ฐํ ๋น์ ๋ฅผ ๊ธฐ์ตํ๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
๋ชจ๋๋ก ์์ญ ๋๋๊ธฐ โ ์ปจํธ๋กค๋ฌ๋ก ์์ฒญ ๋ฐ๊ธฐ โ ๋ฆฌํ์คํธ ๋ฐ์ดํฐ(@Body, @Query) ๋ฝ์๋ด๊ธฐ โ ๋ผ์ฐํ
๊ท์น โ ์๋ฌ ํด๊ฒฐ ๋ฐ ํด์ฆ
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- ์ฉ๋๋ณ๋ก ๋ชจ๋์ ์ชผ๊ฐ์(
UsersModule,StocksModule) ์ ๋ฆฌํ ์ ์๋ค. - URL ์ฃผ์๋ ๋ณธ๋ฌธ์ ๋ด๊ธด ๋ฐ์ดํฐ๋ฅผ ์ปจํธ๋กค๋ฌ์์ ์ ํํ ๋นผ๋ผ ์ ์๋ค.
- ๋ผ์ฐํ ์์ ์ค๋ฅ๋ก ์ธํ 404 ์๋ฌ ์์ธ์ ์ค์ค๋ก ์ฐพ์ ์ ์๋ค.
๐ค ์ ์์์ผ ํ๋๊ฐ
๊ฑด๋ฌผ์ ์ง์ ๋ ์ค๊ณ๋ ์์ด ๋ฌด์์ ๋ฒฝ๋๋ง ์์ผ๋ฉด, ๋์ค์ ํ์ฅ์ค ๋ฐฐ๊ด์ ๊ณ ์น๋ ค๊ณ ๊ฑด๋ฌผ ์ ์ฒด๋ฅผ ๋ถ์ด์ผ ํด.
NestJS์์ ๋ชจ๋ ์ ๊ฑด๋ฌผ์ '์ธต(Floor)'๊ณผ '๊ตฌ์ญ(Zone)'์ผ๋ก ๋๋๋ ์ค๊ณ๋ ์ญํ ์ ํ๊ณ , ์ปจํธ๋กค๋ฌ ๋ ๊ฐ ๊ตฌ์ญ์ ๋ค์ด๊ฐ๋ ์ถ์
๋ฌธ ์ญํ ์ ํด.
์ด ๋ ๊ฐ์ง๋ฅผ ์ ๋๋ก ์๋ฉด, ์ฝ๋๊ฐ ์์ฒ ์ค๋ก ๋์ด๋๋ ์ด๋๋ฅผ ๊ณ ์ณ์ผ ํ ์ง ๋จ 3์ด ๋ง์ ์ฐพ์ ์ ์๊ฒ ๋ผ.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
์ปค๋ค๋ ์ข
ํฉ๋ณ์์ ์์ํด๋ด.
๋ชจ๋(Module) ์ '์์๊ณผ', '๋ด๊ณผ', '์ ํ์ธ๊ณผ' ๊ฐ์ ์ง๋ฃ๊ณผ(๋ถ์)์ผ. ์๋ก ํ๋ ์ผ์ด ์๋ฒฝํ๊ฒ ๋๋์ด ์์ง.
์ปจํธ๋กค๋ฌ(Controller) ๋ ๊ฐ ์ง๋ฃ๊ณผ ์์ ์๋ '์๋ด๋ฐ์คํฌ'์ผ. "๋ฐฐ๊ฐ ์ํ์ ์์ด์(์์ฒญ)" ํ๋ฉด "๋ด๊ณผ 3๋ฒ ๋ฐฉ์ผ๋ก ๊ฐ์ธ์" ํ๊ณ ๊ธธ์ ์๋ดํด ์ฃผ์ง. (์๋ด๋ฐ์คํฌ ์ง์์ด ์ง์ ๋ฐฐ๋ฅผ ์์ ํ์ง ์์!)
๐งฉ ๋ชจ๋ (Module) โ ๊ฑด๋ฌผ์ ์ธต๊ณผ ๋ถ์ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ๋ชจ๋์ 4๊ฐ์ง ํต์ฌ ๋ฐฐ์ด(
imports,controllers,providers,exports)์ ๊ตฌ๋ณํ ์ ์๋ค. - ์ ํ๋์
AppModule์ ๋ค ๋๋ ค๋ฐ์ผ๋ฉด ์ ๋๋์ง ์ค๋ช ํ ์ ์๋ค.
๐ค ์ ๊น, ๋จผ์ ์๊ฐํด๋ด
์ ์ ๊ด๋ฆฌ ๊ธฐ๋ฅ, ์ฃผ์ ๊ฒ์ ๊ธฐ๋ฅ, ๊ฒฐ์ ๊ธฐ๋ฅ์ด ๋ชจ๋ ํ ํ์ผ์ ์์ฌ ์๋ค๊ณ ์์ํด๋ด. ๊ฒฐ์ ๊ธฐ๋ฅ์ ์์ ํ๋ค๊ฐ ์ ์ ๊ธฐ๋ฅ์ด ๊ณ ์ฅ๋๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์ด๊ฒ ๋ญ๊ฐ?
์ฐ๊ด๋ ์ฝ๋(์จ์ดํฐ์ ์ ฐํ)๋ฅผ ํ๋์ ์ธํ๋ฆฌ๋ก ๋ฌถ์ด์ฃผ๋ ๋จ์์ผ.
import { Module } from '@nestjs/common';
import { StocksController } from './stocks.controller';
import { StocksService } from './stocks.service';
import { DatabaseModule } from '../database/database.module';
@Module({
imports: [DatabaseModule], // 1. ๋จ์ ๋ถ์(๋ชจ๋)์์ ๋น๋ ค์ฌ ๊ฒ
controllers: [StocksController], // 2. ์ฐ๋ฆฌ ๋ถ์์ ์จ์ดํฐ(์ ์์ฒ)
providers: [StocksService], // 3. ์ฐ๋ฆฌ ๋ถ์์ ์
ฐํ(์ค์ ๋ก์ง)
exports: [StocksService], // 4. ๋จ์ ๋ถ์์ ๋น๋ ค์ค ๊ฒ
})
export class StocksModule {}์ ์ด๊ฒ ๋ฐ๋ก ์กด์ฌํ๋๊ฐ?
โ ์ด๊ฒ ์์๋ค๋ฉด?
๋ชจ๋ ์ฝ๋๊ฐ AppModule ํ๋์ ๋ค ๋ชฐ๋ ค์์ ๊ฑฐ์ผ. 100๋ช
์ ์จ์ดํฐ์ 100๋ช
์ ์
ฐํ๊ฐ ๋ฒฝ๋ ์๋ ๊ฑฐ๋ํ ๊ฐ๋น ํ๋์์ ์ผํ๋ค๊ณ ์๊ฐํด๋ด. ๋๊ฐ ๋๊ตฌ๋ ์ผํ๋์ง ์๋ฌด๋ ๋ชจ๋ฅผ ๊ฑฐ์ผ (๊ฐํ ๊ฒฐํฉ๋).
โ
์ด๊ฒ ์๊ธด ๋ค์๋?
"์ฃผ์ ๋ถ์(StocksModule)", "์ ์ ๋ถ์(UsersModule)"๊ฐ ๋
๋ฆฝ๋ ์ฌ๋ฌด์ค์ ๊ฐ์ ธ. ์ฃผ์ ๋ถ์๋ฅผ ๋ค๋ฅธ ํ๋ก์ ํธ์ ๊ทธ๋๋ก ๋ณต์ฌํด์ ์จ๋ ์๋ฒฝํ๊ฒ ๋์ํด (์ฌ์ฌ์ฉ์ฑ/์บก์ํ).
๋ชจ๋ ์ค์ 4๋์ฅ ๋ฏ์ด๋ณด๊ธฐ
| ํ๋ผ๋ฏธํฐ | ๋น์ | ์ค๋ช |
|---|---|---|
imports | ์ธ๋ถ ์ฉ์ญ ๊ณ์ฝ | ์ฐ๋ฆฌ ๋ถ์๊ฐ ๊ธฐ๋ฅ์ ์ํํ๊ธฐ ์ํด ํ์ํ ๋ค๋ก ๋ชจ๋ (์: DB ๋ชจ๋) |
controllers | ์ฐ๋ฆฌ ๋ถ์ ์ ์์ฒ | ํด๋ผ์ด์ธํธ์ HTTP ์์ฒญ์ ๋ฐ์ ํด๋์ค ๋ชฉ๋ก |
providers | ์ฐ๋ฆฌ ๋ถ์ ์ง์๋ค | ์ค์ ์ผ์ ํ๋ ์๋น์ค๋ ์ ์ฅ์. NestJS๊ฐ ์ธ์คํด์ค๋ฅผ ๊ด๋ฆฌํด์ค |
exports | ์ธ๋ถ ์๋น์ค ๊ฐ๋ฐฉ | ์ฐ๋ฆฌ ๋ถ์ ์ง์ ์ค, ๋ค๋ฅธ ๋ถ์๊ฐ ๋ถ๋ฅผ ์ ์๊ฒ ํ๋ฝํด์ค ์ง์ ๋ชฉ๋ก |
๐ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ
๊ณต์ ๋ชจ๋์ด๋ ๊ธ๋ก๋ฒ ๋ชจ๋(@Global()) ์ค์ ์ ๋์ค์ ์ํคํ ์ฒ๋ฅผ ๊น๊ฒ ์งค ๋ ๋ค์ ๋์. ์ผ๋จ์ "๊ด๋ จ๋ ๊ฒ๋ผ๋ฆฌ ๋ฌถ๋๋ค"๋ง ๊ธฐ์ตํด!
๐งฉ ์ปจํธ๋กค๋ฌ (Controller) โ ์๋ด๋ฐ์คํฌ ์ง์ ๐ข
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- HTTP ์์ฒญ์์ ํ๋ผ๋ฏธํฐ(
@Query,@Param,@Body)๋ฅผ ๋ฝ์๋ด๋ ๋ฐฉ๋ฒ์ ์ ์ ์๋ค. @Controller๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ด์ฉํด ๊ฒฝ๋ก๋ฅผ ๊น๋ํ๊ฒ ์ค๊ณํ ์ ์๋ค.
1. ๋ผ์ฐํ ๊ณผ ๋ฉ์๋ ๋งคํ
์ปจํธ๋กค๋ฌ๋ URL ์ฃผ์์ HTTP ๋ฉ์๋๋ฅผ ํ์ธํ๊ณ , ์ ์ ํ ์ฒ๋ฆฌ ํจ์๋ก ์ฐ๊ฒฐํด์ค.
import { Controller, Get, Post, Param, Body, HttpCode, HttpStatus } from '@nestjs/common';
@Controller('stocks') // ๐ ์ด ์ปจํธ๋กค๋ฌ๋ '/stocks'๋ก ์์ํ๋ ์์ฒญ๋ง ๋ด๋นํด!
export class StocksController {
// GET /stocks
@Get()
findAll() {
return '๋ชจ๋ ์ฃผ์ ๋ชฉ๋ก์ ๋ฐํํฉ๋๋ค.';
}
// GET /stocks/AAPL
@Get(':symbol')
findOne(@Param('symbol') symbol: string) {
// URL์ ์๋ 'AAPL' ๊ธ์๋ฅผ symbol ๋ณ์๋ก ์ ๋นผ์ด!
return `${symbol} ์ฃผ์์ ์ ๋ณด์
๋๋ค.`;
}
// POST /stocks
@Post()
@HttpCode(HttpStatus.CREATED) // ์ฑ๊ณตํ๋ฉด 201 ์ฝ๋๋ฅผ ์ด์ค
create(@Body() dto: CreateStockDto) {
// ์์ฒญ ๋ณธ๋ฌธ(JSON) ์ ์ฒด๋ฅผ dto๋ผ๋ ๊ฐ์ฒด๋ก ๋ฐ์์ด!
return '์ฃผ์์ด ์์ฑ๋์์ต๋๋ค.';
}
}2. ์์ฒญ ๋ฐ์ดํฐ ์ถ์ถ ๋ฐ์ฝ๋ ์ดํฐ ์ด์ ๋ฆฌ
๊ณ ๊ฐ(ํด๋ผ์ด์ธํธ)์ด ์๋ด๋ฐ์คํฌ์ ๋๊ฒจ์ฃผ๋ ์ ๋ณด์ ํํ๋ ์ฌ๋ฌ ๊ฐ์ง์ผ. ์ฃผ์์ฐฝ์ ์ ์ด์ ์ค ์๋ ์๊ณ , ๋ดํฌ์ ๋ด์์ ์ค ์๋ ์์ง.
| ๋ฐ์ฝ๋ ์ดํฐ | ์ด๋์ ๋ฝ์์ค๋? | ์์ URL / ์ํฉ |
|---|---|---|
@Param('id') | URL ๊ฒฝ๋ก์ ๋ณ์ ์๋ฆฌ | GET /users/123 โ id = 123 |
@Query('page') | URL ๋ฌผ์ํ(?) ๋ค | GET /users?page=2 โ page = 2 |
@Body() | ์์ฒญ ๋ณธ๋ฌธ (JSON ๋ฉ์ด๋ฆฌ) | ๊ฒ์๊ธ ์์ฑ(POST), ์์ (PUT) ์ ๋ฐ์ดํฐ |
@Headers('auth') | ์จ๊ฒจ์ง ๋ฉํ ์ ๋ณด | ์ธ์ฆ ํ ํฐ์ ํ์ธํ ๋ |
3. ์๋ต(Response) ์ฒ๋ฆฌ ๋ฐฉ์
Express์์๋ res.status(200).json(...) ์ฒ๋ผ ์ผ์ผ์ด ์๋ต์ ๋ณด๋์ง๋ง, NestJS๋ ํจ์ฌ ๋๋ํด.
์ปจํธ๋กค๋ฌ ํจ์์์ ๊ทธ๋ฅ return ๊ฐ์ฒด๋ง ๋์ง๋ฉด:
- ์์์ JSON ๋ฌธ์์ด๋ก ๋ฐ๊ฟ์ค!
- ์์์ ์ํ ์ฝ๋ 200(์ฑ๊ณต)์ ๋ฌ์์ค! (POST๋ 201)
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
์ปจํธ๋กค๋ฌ๋ ๊ธธ๋ผ์ก์ด์ผ.
์ด๋ค URL๋ก ๋ค์ด์จ์ด๋ค ๋ฐ์ดํฐ์ธ์ง๋ง ๋ฝ์์ ์๋น์ค(์ ฐํ)์๊ฒ ๋์ ธ์ฃผ๊ณ ๋ท์ผ์ ์ ๊ฒฝ ์ฐ์ง ๋ง.
๐งช ๋ฐ๋ผํด๋ณด๊ธฐ: ์ค์ ์ฃผ์ ์ปจํธ๋กค๋ฌ ๋ง๋ค๊ธฐ
๋จ์ํ ์์๋ฅผ ๋์ด, ์ค์ ์ค๋ฌด์์ ์ธ ๋ฒํ ์ฃผ์ ๊ฒ์์ฉ ๋ณตํฉ ํ๋ผ๋ฏธํฐ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ง๋ค์ด๋ณด์.
๐ ์๋ ์ฝ๋๋ฅผ ๋์ผ๋ก ์ญ ๋ฐ๋ผ๊ฐ๋ฉฐ ์ฝ์ด๋ด.
@Get('search')
// ์ฃผ์: GET /stocks/search?exchange=NASDAQ§or=Tech&page=1
search(
@Query('exchange') exchange?: string,
@Query('sector') sector?: string,
@Query('page', ParseIntPipe) page: number = 1,
@Query('limit', ParseIntPipe) limit: number = 20,
) {
// 1. ํ์ํ ์์๋ฅผ ๋ค ๋ฝ์์ผ๋ ์
ฐํ(์๋น์ค)์๊ฒ ์๋ฆฌ ์๋ขฐ!
return this.stocksService.search(exchange, sector, page, limit);
}๐ ์ฝ๋ ํ ์ค์ฉ ๋ฏ์ด๋ณด๊ธฐ:
| ๋ถ๋ถ | ์๋ฏธ |
|---|---|
@Get('search') | ์ด ํจ์์ ๋ผ์ฐํธ๋ ๋ถ๋ชจ์ /stocks ์ ํฉ์ณ์ ธ์ /stocks/search๊ฐ ๋ผ. |
exchange?: string | ? ๊ธฐํธ๋ "์ด ๊ฐ์ด ์ ๋ค์ด์ฌ ์๋ ์๋ค(์ต์
)"๋ ๋ป์ด์ผ. |
ParseIntPipe | ์ฟผ๋ฆฌ์คํธ๋ง์ ์๋ ๋ฌด์กฐ๊ฑด ๋ฌธ์์ด("1")์ธ๋ฐ, ์ด๊ฑธ ์ง์ง ์ซ์(1)๋ก ์์์ ๋ฐ๊ฟ์ฃผ๊ณ ์ซ์๊ฐ ์๋๋ฉด 400 ์๋ฌ๋ฅผ ๋ฑ์ด๋ด๋ ๋๋ํ ๋ฌธ์ง๊ธฐ์ผ. |
= 1 | ํด๋ผ์ด์ธํธ๊ฐ page ๊ฐ์ ์ ๋๊ฒจ์ฃผ๋ฉด ๊ธฐ๋ณธ๊ฐ์ผ๋ก 1์ ์ฐ๊ฒ ๋ค๋ ๋ป์ด์ผ. |
โ ์ค์ต ํ ์ฒดํฌ๋ฆฌ์คํธ
-
@Param๊ณผ@Query๊ฐ ๊ฐ๊ฐ URL์ ์ด๋ ๋ถ๋ถ์์ ๊ฐ์ ๋ฝ์์ค๋์ง ๊ตฌ๋ณํ ์ ์๋ค. -
ParseIntPipe๊ฐ ์ ํ์ํ์ง ์ค๋ช ํ ์ ์๋ค.
์์ง ์๋ฆฌ์กํ๋ค๋ฉด [์์ฒญ ๋ฐ์ดํฐ ์ถ์ถ ์ด์ ๋ฆฌ ํ]๋ฅผ ๋ค์ ํ๋ฒ ์ค์ฝ ํ๊ณ ์ค์!
๐ผ ๋ฒ ์คํธ ํ๋ํฐ์ค์ ์ค๋ฌด ํ ๐ก
1. ๋ผ์ฐํธ ์์ ์ฃผ์ (์น๋ช ์ ์ค์ ๋ฐฉ์ง)
REST API๋ฅผ ์งค ๋ ์ ์ผ ๋ง์ด ๋นํ๋ ํจ์ ์ค ํ๋์ผ. ๊ตฌ์ฒด์ ์ธ ๊ฒฝ๋ก๋ฅผ ๋์ ๋งค๊ฐ๋ณ์(:id)๋ณด๋ค ๋ฌด์กฐ๊ฑด ๋จผ์ ์จ์ผ ํด.
// โ
์ข์ ์: ๊ตฌ์ฒด์ ์ธ 'trending'์ด ๋จผ์ ๋์ด
@Get('trending')
getTrending() { return '์ธ๊ธฐ ์ฃผ์'; }
@Get(':symbol')
getBySymbol(@Param('symbol') symbol: string) { return symbol; }
// โ ๋์ ์ญ์ ์:
@Get(':symbol') // ๋ชจ๋ ๊ฒ์ ๋ค ๋นจ์๋ค์
getBySymbol() { ... }
@Get('trending') // ์ด ์ฝ๋๋ ํ์ ์คํ๋์ง ๋ชปํจ (์์ ๋งํ์)
getTrending() { ... }2. ์ปจํธ๋กค๋ฌ๋ "์ข ์์ฅ์ฒ๋ผ ์๊ฒ" ์ ์งํด๋ผ
์จ์ดํฐ๊ฐ ์ฃผ๋ฐฉ์ ๋ค์ด๊ฐ์ ๊ณ ๊ธฐ ๊ตฌ์ฐ๋ฉด ์ ๋ผ. ์ปจํธ๋กค๋ฌ์ const user = await db.query... ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ก์ง์ด 1์ค์ด๋ผ๋ ๋ณด์ธ๋ค๋ฉด ์ค๊ณ๊ฐ ๋ง๊ฐ์ง ๊ฑฐ์ผ. ์ค์ง ์ ํจ์ฑ ์ฒดํฌ, ์๋ขฐ, ์๋ต๋ง ๋ด๋นํด!
3. API ๊ธ๋ก๋ฒ ์ ๋์ฌ (Global Prefix) ์ถ๊ฐ
์ค๋ฌด์์๋ API ์ฃผ์ ์์ /api/v1 ๊ฐ์ ๊ฑธ ๋ถ์ฌ. ํด๋ผ์ด์ธํธ์ ๋ฆฌ์กํธ ์ฑ ๊ฒฝ๋ก๊ฐ ๊ฒน์น์ง ์๊ฒ ํ๊ธฐ ์ํด์์ผ.
main.ts ์์ ํ ์ค์ด๋ฉด ์ฑ ์ ์ฒด์ ์ ์ฉ๋ผ:
// main.ts
app.setGlobalPrefix('api/v1');
// ์ด์ localhost:3000/api/v1/stocks ๋ก ์์ฒญํด์ผ ํด!๐ฅ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
์๋ฌ ๋ฉ์์ง๊ฐ ๋จ๋ฉด Ctrl+F๋ก ๊ฒ์ํด๋ด.
โ 404 Not Found (๋ผ์ฐํ
๋ฌธ์ )
์ธ์ ๋์ค๋๊ฐ?
{ "statusCode": 404, "message": "Cannot GET /stocks/trending", "error": "Not Found" }์์ธ:
- ๋ชจ๋์
AppModule์imports์ ๋ฑ๋กํ์ง ์์์ NestJS๊ฐ ์ปจํธ๋กค๋ฌ ์์ฒด๋ฅผ ๋ชจ๋ฆ. - ์์์ ์ค๋ช
ํ ๋ผ์ฐํธ ์์(์์) ๋ฌธ์ ๋๋ฌธ์,
/:symbol์ด/trending์ ์ผ์ผ๋ฒ๋ ค์ ์๋ฌ ์ฒ๋ฆฌ๊ธฐ๋ก ๋น ์ง.
ํด๊ฒฐ์ฑ :
app.module.ts์imports: [StocksModule]์ด ๋ค์ด์๋์ง ํ์ธ.- ์ปจํธ๋กค๋ฌ ๋ด ํจ์ ๋ฐฐ์น ์์์์ ๊ณ ์ ๋ ์๋จ์ด ๊ฒฝ๋ก๊ฐ ์ฝ๋ก (
:)์ด ๋ถ์ ๋ณ์ ๊ฒฝ๋ก๋ณด๋ค ์์ ์๋์ง ํ์ธ.
โ Validation failed (numeric string is expected)
์ธ์ ๋์ค๋๊ฐ?
{ "statusCode": 400, "message": "Validation failed (numeric string is expected)", "error": "Bad Request" }์์ธ: @Query('id', ParseIntPipe)๋ฅผ ์ผ๋๋ฐ ํด๋ผ์ด์ธํธ๊ฐ ?id=abc ์ฒ๋ผ ๋ฌธ์๋ฅผ ๋ณด๋๋ค.
ํด๊ฒฐ์ฑ
:
์ ์์ ์ธ ๋์์ด๋ค. ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์๊ฒ "์ซ์๋ฅผ ๋ณด๋ด์ธ์!" ๋ผ๊ณ ์๋ ค์ฃผ๋ฉด ๋๋ค. ํ์ดํ๊ฐ ์๋ฒ ๋ค์ด์ ๋ง์์ค ๊ณ ๋ง์ด ์ํฉ.
๐๏ธ ์นํธ์ํธ โ ์ค๋ฌด ์์ฝ ์นด๋
์ด๊ฒ๋ง ๋ณต๋ถํด์ ์จ!
๐ ์์ฒญ ๋ฐ๊ธฐ 3์ด์ฌ
| ๋ด๊ฐ ๋นผ์ค๊ณ ์ถ์ ์ ๋ณด | ์ด๋ ๊ฒ ์จ! |
|---|---|
/users/123 ๐ 123 | @Param('id') id: string |
/search?kw=์๋
๐ ์๋
| @Query('kw') kw: string |
POST { "name": "Kim" } | @Body() dto: UserDto |
โ ๏ธ ์ ๋ ํ์ง ๋ง ๊ฒ
| ์ํฉ | โ ๋์ ์ (๊ธ์ง) | โ ์ข์ ์ |
|---|---|---|
| ์ปจํธ๋กค๋ฌ์ ์ญํ | ์ปจํธ๋กค๋ฌ ํ์ผ์ DB SQL ์ฟผ๋ฆฌ ์์ฑ | ์๋น์ค ํจ์์ ํ๋ผ๋ฏธํฐ๋ง ๋๊ธฐ๊ธฐ |
| Express ๊ฐ์ฒด ์ฌ์ฉ | @Res() res: Response ๋ฌด๋ถ๋ณํ๊ฒ ์ฐ๊ธฐ | ํ๋ ์์ํฌ๊ฐ ์๋ตํ๊ฒ ์์ ๋ฐํ |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ๋น์ ์ ๊ณ ๊ฐ์ ํ๋กํ์ ์ ๋ฐ์ดํธํ๋ ๋ผ์ฐํฐ๋ฅผ ๋ง๋ค๊ณ ์ถ๋ค. URL ๊ฒฝ๋ก ๊ท์น๊ณผ HTTP ๋ฉ์๋ ๋ฐ์ฝ๋ ์ดํฐ ์กฐํฉ์ผ๋ก ์ฌ๋ฐ๋ฅธ ๊ฒ์?
- A)
@Post('/user/update') - B)
@Put(':id') - C)
@Get('/update/:id') - D)
@Controller(':id')
โ ์ ๋ต: B
๐ก **์ค๋ช
:**REST API ๊ท์ฝ์ ๋ฐ๋ผ ๋ฐ์ดํฐ์ ์
๋ฐ์ดํธ(์์ )๋ PUT ๋๋ PATCH๋ฅผ ์ฐ๋ฉฐ, ์์ ํ ๋์์ ์๋ณ์๋ @Param์ผ๋ก ๋๊ธฐ๋ ๊ฒ์ด ์ ์์ด์ผ.
Q2. ์๋ ๋น์นธ์ ์ฑ์๋ด.
์ปจํธ๋กค๋ฌ์์ ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ธ JSON ๋ฐ์ดํฐ(์: ํ์๊ฐ์ ์์) ์ ์ฒด๋ฅผ ํ๋์ ๊ฐ์ฒด๋ก ์ด์๊ฒ ๊ฐ์ ธ์ค๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ ๋ฐ์ฝ๋ ์ดํฐ๋?
@Post()
signup( [ ] body: SignupDto ) {
return this.authService.register(body);
}โ
์ ๋ต: @Body()
๐ ํต์ฌ ๊ธฐ์ต๋ฒ: ํ๋ฐฐ ์์(HTTP ํต์ ) ์์ ๋ค์ด์๋ ๋ด์ฉ๋ฌผ(๋ณธ๋ฌธ = Body)!
Q3. ๋๋ฒ๊น ํด์ฆ: ์๋ ์ปจํธ๋กค๋ฌ์์ ๋ฐ์ํ ๋ฌธ์ ๋ฅผ ์ฐพ์๋ด.
@Controller('files')
export class FilesController {
@Get(':filename')
downloadFile(@Param('filename') name: string) {
return `ํ์ผ ๋ค์ด๋ก๋: ${name}`;
}
@Get('download-logs')
downloadAllLogs() {
return '๋ชจ๋ ๋ก๊ทธ ์์ถ ๋ค์ด๋ก๋';
}
}์ด ์ฝ๋๋ฅผ ์คํํ๊ณ /files/download-logs ์ฃผ์๋ก ๋ค์ด๊ฐ๋ฉด ์ด๋ค ์ผ์ด ์๊ธธ๊น?
- A) ์ํ๋ ๋๋ก '๋ชจ๋ ๋ก๊ทธ ์์ถ ๋ค์ด๋ก๋'๊ฐ ๋์จ๋ค.
- B) 'ํ์ผ ๋ค์ด๋ก๋: download-logs' ๋ผ๋ ๋ฌธ๊ตฌ๊ฐ ๋ฌ๋ค.
- C) 500 ์๋ฌ ์๋ฒ ํฌ๋์๊ฐ ๋ฐ์ํ๋ค.
โ ์ ๋ต: B
๐ก ์ค๋ช
: ๊ฐ์ฅ ์ง๊ทธ๋ฝ๊ฒ ๊ฒช์ ๋ผ์ฐํ
์์ ์ค๋ฅ์ผ! ๊ตฌ์ฒด์ ์ธ ๊ฒฝ๋ก์ธ @Get('download-logs')๋ฅผ @Get(':filename') ๋ณด๋ค ์๋ก ์ฌ๋ ค์ผ ํด. ์ ๊ทธ๋ฌ๋ฉด NestJS๋ "์, ๋๊ฐ ์์ฒญํ ํ์ผ ์ด๋ฆ์ด download-logs ๊ตฌ๋!" ํ๊ณ ์ฐฉ๊ฐํด๋ฒ๋ ค.