๐ก 03. ์๋น์ค์ ํ๋ก๋ฐ์ด๋ โ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ์ ์ด์ ์ญ์ (IoC)
๐ ๊ฐ์
๋น์ฆ๋์ค ๋ก์ง์ ํต์ฌ์ธ ์๋น์ค์, ์ด๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌํด ์ฃผ๋ ์์ฐ์ค๋ฌ์ด ์์คํ ์ธ 'ํ๋ก๋ฐ์ด๋(Provider)์ ์์กด์ฑ ์ฃผ์ (DI)'์ ๋ง์คํฐํฉ๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ ์๋น์ค (Service) โ ์๋ฆฌ๋ฅผ ์ ๋ดํ๋ ์ ฐํ ๐ก
- ๐งฉ ์์กด์ฑ ์ฃผ์ (DI) โ ๋๋ํ ์ธ์ฌ๊ด๋ฆฌ ์์คํ ๐ข
- ๐งช ๋ฐ๋ผํด๋ณด๊ธฐ: ์ค์ CRUD ์๋น์ค ๊น์๋ณด๊ธฐ
- ๐ผ ๋ฒ ์คํธ ํ๋ํฐ์ค์ ์ค๋ฌด ํ
- โ ๏ธ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
- ๐๏ธ ์นํธ์ํธ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 20๋ถ (์ ์ฒด) / ํต์ฌ ํํธ๋ง: 10๋ถ
๐งณ ์ ์ ์ง์ ์ฒดํฌ๋ฆฌ์คํธ
- 02์ฅ ์ปจํธ๋กค๋ฌ์ ์ญํ (์์ฒญ ์์ ๋ฐ ์๋ต ๋ฐํ)์ ์์ ํ ์ดํดํ๋ค.
- TypeScript์
constructor(private readonly ...)๋ฌธ๋ฒ์ด ๋ฌด์์ ์๋ฏธํ๋์ง ์๋ค. - (์ ํ) ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ์ '์ธํฐํ์ด์ค(Interface)' ๊ฐ๋ ์ ๋ค์ด๋ณธ ์ ์ด ์๋ค.
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
์๋น์ค์ ํ์์ฑ โ DI(์์กด์ฑ ์ฃผ์
)์ ๊ฐ๋ ฅํจ โ ์ค์ DB ์ฐ๊ฒฐ ์ฝ๋ ์์ฑ โ ์ปค์คํ
ํ๋ก๋ฐ์ด๋ โ ์ํ ์ฐธ์กฐ ์๋ฌ ํด๊ฒฐ๋ฒ
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- ์ปจํธ๋กค๋ฌ์์ ๋น์ฆ๋์ค ๋ก์ง์ ์๋ฒฝํ๊ฒ ๋ถ๋ฆฌํ์ฌ ์๋น์ค๋ก ์ฎ๊ธธ ์ ์๋ค.
- "์์กด์ฑ ์ฃผ์ ์ ์ ์ฐ๋์?" ๋ผ๋ ์ง๋ฌธ์ ๋น๋นํ๊ฒ ๋๋ตํ ์ ์๋ค.
- ์ธํฐํ์ด์ค๋ฅผ ํ์ฉํด ํ ์คํธํ๊ธฐ ์ฌ์ด(Mocking ๊ฐ๋ฅํ) ์ฝ๋๋ฅผ ์งค ์ ์๋ค.
๐ค ์ ์์์ผ ํ๋๊ฐ
์ด๋ณด ๊ฐ๋ฐ์๋ค์ ํํ "๋ผ์ฐํฐ ํ๋๊ณ ๊ทธ ์์์ DB๋ ๋ถ๋ฅด๊ณ , ๊ณ์ฐ๋ ํ๊ณ , ์๋ฌ๋ ๋ฑ์ผ๋ฉด ๋๋ ๊ฑฐ ์๋์ผ?" ๋ผ๊ณ ์ฐฉ๊ฐํด.
ํ์ง๋ง ๋ก์ง์ด ๋ณต์กํด์ง์๋ก ์ปจํธ๋กค๋ฌ๋ ๋ฑ๋ฑํด์ง๊ณ , ๋์ค์ "๊ฒฐ์ ๋ก์ง"๋ง ๋ ๋ผ์ด์ "์ค์ผ์ค๋ฌ"์์ ์ฌ์ฌ์ฉํ๊ณ ์ถ์ด๋ ๋ผ์ด๋ผ ์๊ฐ ์์ด.
์๋น์ค๋ฅผ ๋ถ๋ฆฌํ๊ณ DI๋ฅผ ํ์ฉํ๋ฉด ๋ ๊ณ ๋ธ๋ก์ฒ๋ผ ์ฝ๋๋ฅผ ์์ ์์ฌ๋ก ๋๋ค ๋ถ์๋ค(์ฌ์ฌ์ฉ) ํ ์ ์๊ณ , ๋ฌด์๋ณด๋ค ํ
์คํธ ์ฝ๋ ์์ฑ์ด 100๋ฐฐ ์ฌ์์ ธ.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
์ปจํธ๋กค๋ฌ์ ๋ก์ง์ ๋ค ์ง๋ ๊ฒ (โ)
= ์จ์ดํฐ๊ฐ ์๋ ์ฃผ๋ฌธ์ ๋ฐ์๋ง์, ๋ณธ์ธ์ด ์ฃผ๋ฐฉ์ผ๋ก ๋ฌ๋ ค๊ฐ์ ํ๋ผ์ดํฌ์ ์ก๊ณ ๊ณ ๊ธฐ๋ฅผ ๊ตฝ๋ ๊ฑฐ์ผ. ๊ทธ๋ฌ๋ค ์๋ ์ค๋ฉด ๋ ๋ฐ์ด๋๊ฐ๊ณ ! ์๋น์ด ์๋ง์ง์ฐฝ์ด ๋๊ฒ ์ง?
์๋น์ค๋ฅผ ๋ง๋ค๊ณ ์์กด์ฑ์ ์ฃผ์
ํ๋ ๊ฒ (โ
)
= ์จ์ดํฐ๋ ์ฃผ๋ฌธ๋ง ๋ฐ๊ณ ์ฃผ๋ฐฉ์ ํ ์คํด. ์ฃผ๋ฐฉ์๋ '์ดํ๋ฆฌ์ ์
ฐํ', 'ํ์ ์
ฐํ(์๋น์ค๋ค)'๊ฐ ๊ฐ์์ ๋ฉ๋ดํ๋ฅผ ๋ค๊ณ ๋๊ธฐํ๊ณ ์์ด.
์จ์ดํฐ๊ฐ ์ถ๊ทผํ ๋ "์ ์ค๋ ์ดํ๋ฆฌ์ ์
ฐํ๋ ์ผํ ๊ฒ์!" ๋ผ๊ณ ํ๋ฉด, ์๋น ์ง๋ฐฐ์ธ(NestJS DI ์ปจํ
์ด๋) ์ด ์์์ ๊ทธ๋ ์ปจ๋์
์ด ์ ์ผ ์ข์ ์ดํ๋ฆฌ์ ์
ฐํ๋ฅผ ์จ์ดํฐ ํํธ๋๋ก ๋ฑ! ๋ฐฐ์ ํด ์ฃผ๋ ๊ฑฐ์ผ.
๐งฉ ์๋น์ค (Service) โ ์๋ฆฌ๋ฅผ ์ ๋ดํ๋ ์ ฐํ ๐ก
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
@Injectable()์ ์๋ฏธ๋ฅผ ์๊ณ , ๋น์ฆ๋์ค ๋ก์ง์ ์์น๋ฅผ ํ์ ํ ์ ์๋ค.
์๋น์ค๋ ๋น์ฆ๋์ค ๋ก์ง ์ ๋ด๋นํ๋ ํ๋ฒํ TypeScript ํด๋์ค์ผ. ๋จ ํ๋์ ์ฐจ์ด์ ์ ๋จธ๋ฆฌ ์์ @Injectable() ์ด๋ผ๋ ์๊ด์ ์ฐ๊ณ ์๋ค๋ ์ ์ด์ง.
import { Injectable } from '@nestjs/common';
// "์ด ํด๋์ค๋ NestJS ๋ฉ๋ด์ผ์ ๋ฑ๋ก๋ ์ ์ ์
ฐํ์
๋๋ค!"
@Injectable()
export class UsersService {
// ์ค์ ๋น์ฆ๋์ค ๋ก์ง
calculateTax(amount: number) {
return amount * 0.1;
}
}๐ ์ฉ์ด:
Provider(ํ๋ก๋ฐ์ด๋)
NestJS์์ DI(์์กด์ฑ ์ฃผ์ ) ์์คํ ์ด ๊ด๋ฆฌํ๋ ๋ชจ๋ ํด๋์ค๋ฅผ ํตํ์ด 'ํ๋ก๋ฐ์ด๋'๋ผ๊ณ ๋ถ๋ฌ. ์๋น์ค๋ ํ๋ก๋ฐ์ด๋์ ๊ฐ์ฅ ํํ 90%์ง๋ฆฌ ํํ์ผ ๋ฟ์ด์ผ. ๋์ค์๋ ํฉํ ๋ฆฌ๋ ํ๋ก๋ฐ์ด๋๊ณ , ์ค์ ๊ฐ ๋ฌธ์์ด๋ ํ๋ก๋ฐ์ด๋๋ก ๋ง๋ค ์ ์์ด.
๐งฉ ์์กด์ฑ ์ฃผ์ (DI) โ ๋๋ํ ์ธ์ฌ๊ด๋ฆฌ ์์คํ ๐ข
์ด ํํธ๊ฐ NestJS์ ์ฌ์ฅ์ด์ผ.
โ ์์กด์ฑ์ ์ง์ ๋ง๋ค๋ฉด ์๊ธฐ๋ ๋น๊ทน:
class UsersController {
// ์จ์ดํฐ๊ฐ ์ถ๊ทผํ ๋๋ง๋ค ์๊ธฐ ๋์ผ๋ก ์
ฐํ๋ฅผ ์ง์ ๊ณ ์ฉํจ
private usersService = new UsersService(new DatabaseService());
}์ด๋ฌ๋ฉด DatabaseService๊ฐ ๋ฐ๋๋ฉด ์ปจํธ๋กค๋ฌ์ ์ฝ๋๊น์ง ํจ๊ป ๋ฏ์ด๊ณ ์ณ์ผ ํด. ๊ฐํ ๊ฒฐํฉ(Tight Coupling)์ ์ ํ์ด์ง.
โ NestJS์ ๊ฐ๋ ฅํ DI ํด๊ฒฐ์ฑ :
@Controller('users')
export class UsersController {
// "์ง๋ฐฐ์ธ๋, ์ UsersService ํ ์ค ์๋ ์ฌ๋ ํ ๋ช
๋ฐฐ์ ํด์ฃผ์ธ์"
constructor(private readonly usersService: UsersService) {}
}์ฐ๋ฆฌ๋ ๋จ์ง constructor์ ํ๋ผ๋ฏธํฐ ํ์
ํ์(: UsersService)๋ง ์ ์ด์ฃผ๋ฉด ๋์ด์ผ.
NestJS๊ฐ ์คํ๋ ๋, ๋๋ํ ์ง๋ฐฐ์ธ(IoC Container)์ด ์ด ํ์
์ ๋ณด๊ณ ๋ฏธ๋ฆฌ ๋ง๋ค์ด๋ UsersService ์ธ์คํด์ค๋ฅผ ์์์ ์ ๋ฃ์ด์ค.
์ด๊ฑธ ์ ์ด์ ์ญ์ (IoC, Inversion of Control) ์ด๋ผ๊ณ ๋ถ๋ฌ! ๊ฐ๋ฐ์(๋)๊ฐ new๋ฅผ ์ ํ๊ณ ํ๋ ์์ํฌ๊ฐ ์์์ ํด์ค๋ค๋ ๋ป์ด์ผ.
ํ๋ก๋ฐ์ด๋์ ์๋ช ์ฃผ๊ธฐ (Scope)
๊ธฐ๋ณธ์ ์ผ๋ก ์ง๋ฐฐ์ธ์ ์
ฐํ๋ฅผ ๋ฑ 1๋ช
๋ง ๊ณ ์ฉํด์, ๋ชจ๋ ์จ์ดํฐ๊ฐ ๋๋ ค์ฐ๊ฒ ํด (** ์ฑ๊ธํค, Singleton**). ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋ผ๊ธฐ ์ํด์์ง.
ํ์ง๋ง ํน๋ณํ ์ต์
์ ์ค ์๋ ์์ด.
| ์ค์ฝํ | ์์ฑ | ์ธ์ ์ฐ๋? | ๋จ์ |
|---|---|---|---|
| ๊ธฐ๋ณธ(DEFAULT) | ์ฑ๊ธํค. ์ฑ ์์ ์ ํ ๋ฒ ์์ฑ | 99%์ ์๋น์ค, ๋ฌด์ํ ๋ก์ง | ์์ (๊ฐ์ฅ ๋น ๋ฆ) |
| ์์ฒญ(REQUEST) | ํด๋ผ์ด์ธํธ์ 1์์ฒญ๋ง๋ค ์๋ก ์์ฑ | ์ ์ ๋ณ ํ ํฐ์ด๋ ํ ๋ํธ ์๋ณ์ด ํ์ํ ๋ | ์ฑ๋ฅ ์ ํ (๋งค๋ฒ ์์ฑ/ํด์ ) |
| ์์(TRANSIENT) | ์ฃผ์ ๋ฐ์ ๊ณณ์ ๋ง๋ ๋๋ง๋ค ์๋ก ์์ฑ | ๊ฐ๊ฐ ์์ ํ ๊ฒฉ๋ฆฌ๋ ์ํ(State)๋ฅผ ๊ฐ์ ธ์ผ ํ ๋ | ๋ฉ๋ชจ๋ฆฌ ๊ธ์ฆ ์ฃผ์ |
์ฌ์ฉ๋ฒ: @Injectable({ scope: Scope.REQUEST })
๐งช ๋ฐ๋ผํด๋ณด๊ธฐ: ์ค์ CRUD ์๋น์ค ๊น์๋ณด๊ธฐ
๋ง๋ณด๋จ ์ฝ๋์ง. ์ค๋ฌด์์ ๊ฐ์ฅ ๋ง์ด ์ง๊ฒ ๋ , Drizzle ORM์ ๋ถ์ธ ์ ์ (์ฃผ์) ๊ฒ์ํ CRUD ๋ผ๋์ผ.
import { Injectable, NotFoundException } from '@nestjs/common';
import { eq } from 'drizzle-orm';
import { stocks, Stock, NewStock } from '@repo/schema';
@Injectable()
export class StocksService {
// 1. DB ์๋น์ค ์ฃผ์
๋ฐ๊ธฐ
constructor(private readonly db: DatabaseService) {}
// 2. ์์ฑ (Create)
async create(data: NewStock): Promise<Stock> {
const [stock] = await this.db.insert(stocks).values(data).returning();
return stock;
}
// 3. ๋จ์ผ ์กฐํ (Read)
async findBySymbol(symbol: string): Promise<Stock> {
const stock = await this.db.query.stocks.findFirst({
where: eq(stocks.symbol, symbol),
});
// ๐ฅ ์ค์: ์ปจํธ๋กค๋ฌ ๋์ ์๋น์ค์์ ์ผ์ฐ ์๋ฌ๋ฅผ ๋์ง๋ค!
if (!stock) {
throw new NotFoundException(`์ฆ๊ถ ์ฌ๋ณผ '${symbol}'์(๋ฅผ) ์ฐพ์ ์ ์์ต๋๋ค.`);
}
return stock;
}
// 4. ์ญ์ (Delete)
async remove(symbol: string): Promise<void> {
await this.findBySymbol(symbol); // ์๋ ํ์ธ๋ถํฐ ํ๊ณ (์์ผ๋ฉด ์์์ ์์ธ ๋ฐ์)
await this.db.delete(stocks).where(eq(stocks.symbol, symbol));
}
}๐ค ๋ฅ๋์ ํ์ ํธ๋ฆฌ๊ฑฐ: ์ฌ๊ธฐ์ ๋ง์ฝ
NotFoundException์ ์ฃผ์ ์ฒ๋ฆฌํ๋ฉด, ์ปจํธ๋กค๋ฌ์์ ๋น ๊ฐ์ ๋ฐ์์ ๋ ์ฌ์ฉ์์๊ฒ ์ด๋ค ์๋ต์ด ๋ด๋ ค๊ฐ๊น? (์ ๋ต: ๋น ๊ฐ์ฒด์ด๊ฑฐ๋, 200 OK์ ํจ๊ป ๋ด์ฉ์ด ์๋ ์๋ต์ด ๋ด๋ ค๊ฐ์ ๋ฒ๊ทธ๋ฅผ ์ ๋ฐํด! ์๋น์ค์์ ์ปท ์น๋ ๊ฒ ๋ง์.)
๐ผ ๋ฒ ์คํธ ํ๋ํฐ์ค์ ์ค๋ฌด ํ ๐ก
1. ๊ฑฐ๋ํ ์๋น์ค๋ฅผ ๋ ์๊ฒ ์ชผ๊ฐ๊ธฐ
UsersService ํ๋๊ฐ 2,000์ค์ด ๋์ด๊ฐ๋ฉด ์ํ ์ ํธ์ผ. ๊ด๋ จ๋ ์๋น์ค๋ค์ ๋ ์ชผ๊ฐ๊ณ , ๊ทธ ์๋น์ค๋ผ๋ฆฌ ์๋ก ์ฃผ์
๋ฐ๊ฒ ํด.
์: UserAuthService, UserProfileService, UserPointsService
2. ์ธํฐํ์ด์ค ๊ธฐ๋ฐ ํ๋ก๊ทธ๋๋ฐ (์ปค์คํ ํ๋ก๋ฐ์ด๋ ๊ฟํ)
์ธ๋ถ ๋ฉ์ผ ๋ฐ์ก ์๋น์ค(์: SendGrid)๋ฅผ ์ง์ ์ฃผ์ ๋ฐ์ผ๋ฉด, ๋์ค์ AWS SES๋ก ๊ฐ์ํ ๋ ์ปจํธ๋กค๋ฌ๋ฅผ ๋ค ์์ ํด์ผ ํด.
// 1. ๊ท๊ฒฉ์ ์ ํจ
export interface IEmailService {
send(to: string, content: string): Promise<void>;
}
// 2. ๊ตฌํ์ฒด
@Injectable()
export class SendGridEmailService implements IEmailService { ... }
// 3. ๋ชจ๋์์ '๋ฐ๊ฟ์น๊ธฐ' ์ค์ (์ฌ์ฉํ๋ ์ชฝ ์ฝ๋ ๋ณ๊ฒฝ X)
@Module({
providers: [
{
provide: 'EMAIL_SERVICE', // ์ด๋ฆํ
useClass: SendGridEmailService, // ์ค์ ์ธ ํด๋์ค (์ถํ ์ด๊ฑธ AwsEmailService๋ก ํ ์ค๋ง ๋ฐ๊พธ๋ฉด ๋!)
}
]
})์ฌ์ฉํ ๋๋ ์ปจํธ๋กค๋ฌ์์ @Inject('EMAIL_SERVICE') private emailService: IEmailService ๋ก ๋ฐ์ผ๋ฉด ๋ผ. ์ด๊ฑธ ์๋ฒฝํ ์ดํดํ๋ฉด ๋ ๋ ์ด์ ์ด๋ณด๊ฐ ์๋์ผ.
3. ์ํ ์์กด์ฑ ํด๊ฒฐ (Circular Dependency)
๊ฐ์ฅ ์ง์ฆ ๋๋ ์ํฉ: A ์๋น์ค๊ฐ B๋ฅผ ์ฃผ์ ๋ฐ๊ณ , B๊ฐ A๋ฅผ ๋ค์ ์ฃผ์ ๋ฐ๋ ์ํฉ. ๊ผฌ๋ฆฌ๋ฅผ ๋ฌด๋ ๋ฑ์ด์ง.
- **ํด๋ฒ 1 (๊ถ์ฅ):**A์ B๊ฐ ๊ณตํต์ผ๋ก ์์กดํ๋ C ์๋น์ค๋ฅผ ๋ง๋ค์ด์ ๋ถ๋ฆฌํ๋ค.
- ํด๋ฒ 2 (์ฝ๋ ์์ ): ๋ถ๋์ดํ๋ค๋ฉด
forwardRef๋ฅผ ์จ์ ํ๋ ์์ํฌ์ "์กฐ๊ธ๋ง ๊ธฐ๋ค๋ ธ๋ค๊ฐ ์ฃผ์ ํด์ค" ๋ผ๊ณ ๋ถํํ๋ค.
@Injectable()
export class ServiceA {
constructor(
@Inject(forwardRef(() => ServiceB))
private serviceB: ServiceB,
) {}
}โ ๏ธ ์๋ฌ ํด๊ฒฐ ์นดํ๋ก๊ทธ
โ Nest can't resolve dependencies of the UsersController (?).
์ธ์ ๋์ค๋๊ฐ?
์ฑ์ ์ผฐ๋๋ฐ ํฐ๋ฏธ๋์ ์๋ป๊ฑด ๊ธ์จ๋ก ์ ๋ด์ฉ์ด ์ถ๋ ฅ๋๋ฉฐ ์๋ฒ๊ฐ ์ค๋จ๋จ.
์์ธ:
์ปจํธ๋กค๋ฌ(์จ์ดํฐ)๊ฐ 1๋ฒ ํ๋ผ๋ฏธํฐ๋ก(0๋ฒ ์ธ๋ฑ์ค) ๋ฌด์ธ๊ฐ๋ฅผ ์ฃผ์
ํด๋ฌ๋ผ๊ณ ํ๋๋ฐ, ํด๋น ๋ชจ๋์ providers: [] ๋ฐฐ์ด์ ๊ทธ ํด๋์ค๊ฐ ๋น ์ ธ์์.
ํด๊ฒฐ์ฑ :
app.module.ts(๋๋ ํด๋น ๊ธฐ๋ฅ ๋ชจ๋) ์ด๊ธฐproviders: [UsersService]์ฒ๋ผ ๋ด๊ฐ ์ธ ์๋น์ค๋ฅผ ์ ๋๋ก ๋ฑ๋กํ๋์ง ์ฒดํฌ!
โ Maximum call stack size exceeded
์ธ์ ๋์ค๋๊ฐ?
์๋ฒ๋ฅผ ์ผ์๋ง์ ๋ฌดํ ๋ฃจํ๋ฅผ ๋๋ฉด์ ์น๋ช
์ ์ธ ์๋ฌ๋ฅผ ๋ฑ์.
์์ธ:
์์์ ๋งํ ์ํ ์ฐธ์กฐ ๋๋ฌธ์ด์ผ. Module A -> Module B -> Module A ๋ฌดํ ๋ฃจํ!
ํด๊ฒฐ์ฑ :
- ์ด๋ ๋ชจ๋๋ผ๋ฆฌ ๊ผฌ์๋์ง ํ์
(
forwardRef์ ์ฉํ๊ธฐ). - ๊ฐ์ฅ ์ข์ ๊ฑด ์ค๊ณ๋ฅผ ๊ณ ์ณ์ ์๋ฐฉํฅ ์์กด์ ๋จ๋ฐฉํฅ์ผ๋ก ์ชผ๊ฐ๋ ๊ฒ.
๐๏ธ ์นํธ์ํธ
- ๋จ์ ์๋น์ค ์ ์ธ:
@Injectable() export class MyService {} - ์์กด์ฑ ์ฃผ์
๋ฐ๊ธฐ:
constructor(private readonly appService: AppService) {} - ๋ชจ๋์ ๋ฑ๋กํ๊ธฐ:
@Module({ providers: [MyService] }) - ์ฑ๊ธํค ๋๊ธฐ:
@Injectable({ scope: Scope.REQUEST })
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ๋น์ ์ด ๊ฐ๋ฐ ์ค์ธ OrderService์์ ์ฅ๋ฐ๊ตฌ๋ ๋ฐ์ดํฐ๋ฅผ ์ํด CartService๋ฅผ ๊ฐ์ ธ๋ค ์ฐ๊ณ ์ถ๋ค. ์ฌ๋ฐ๋ฅธ ๋ฐฉ๋ฒ์?
- A)
cartService = new CartService();๋ผ๊ณ ์ง์ ํ ๋นํ๋ค. - B)
@Injectable()์๋์ ์์ฑ์ผ๋ก ์ ์ธํด๋๋ค. - C)
constructor(private readonly cartService: CartService) {}๋ฅผ ํตํด ์์กด์ฑ์ ์ฃผ์ ๋ฐ๋๋ค.
โ ์ ๋ต: C
๐ก **์ค๋ช
:**NestJS์ ํต์ฌ ์ฒ ํ์ ์ ์ด์ ์ญ์ (IoC)! new ํค์๋๋ ์ ๋ ์ฐ์ง ๋ง๊ณ ์ง๋ฐฐ์ธ์๊ฒ ๋น๋นํ ์๊ตฌํ ๊ฒ.
Q2. ๋ค์ ์ค ์๋ฌ๊ฐ ๋ฐ์ํ๋ ์์ธ์ ๋ฌด์์ธ๊ฐ?
@Injectable()
export class UserService {
constructor(private readonly authService: AuthService) {}
}
@Injectable()
export class AuthService {
constructor(private readonly userService: UserService) {}
}โ
์ ๋ต: ์ํ ์์กด์ฑ (Circular Dependency)
์ค๋ช
: ๋ญ๊ณผ ๋ฌ๊ฑ์ ๋ฌธ์ ์ง. ์ ์ ์๋น์ค๋ฅผ ๋ง๋ค๋ ค๋ค ๋ณด๋ ์ธ์ฆ์ด ํ์ํ๊ณ , ์ธ์ฆ์ ๋ง๋ค๋ ค๋ ์ ์ ๊ฐ ํ์ํด์ ๋ฌดํ ๋๊ธฐ์ ๋น ์ ธ. forwardRef๋ฅผ ์ฐ๊ฑฐ๋ ๊ณตํต ์๋น์ค๋ก ๋ถ๋ฆฌํด์ผ ํด!
Q3. UserService ํ์ผ์๋ @Injectable()์ ๋ฌ์๊ณ , ์ปจํธ๋กค๋ฌ์์๋ constructor๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ฃผ์
๋ฐ์๋ค. ๊ทธ๋ฐ๋ฐ๋ Nest can't resolve dependencies ์๋ฌ๊ฐ ๋๋ค๋ฉด, ์ด๋ค ํ์ผ์ ๋ฌด์จ ์ฝ๋๋ฅผ ๋นผ๋จน์ ๊ฒ์ผ๊น?
โ
์ ๋ต: user.module.ts ํ์ผ(ํด๋น ๋ชจ๋)์ @Module ๋ฐ์ฝ๋ ์ดํฐ ์ providers: [UserService] ๋ฐฐ์ด์ ๋ฑ๋กํ๋ ๊ฑธ ๊น๋จน์์!
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋์ DI๊ฐ ๋ง๋ฒ์ด ์๋๋ผ ๊ฐ์ฒด ์์ฑ ์ฑ
์์ Nest ์ปจํ
์ด๋์ ๋งก๊ธฐ๋ ์ฝ์์ด๋ผ๋ ๊ฑธ ๋ฐฐ์ ๋ค. ์์ ๊ฐ์ผ๋ฉด ํ์ํ ์๋น์ค๊ฐ ๋ณด์ด๋ฉด new๋ก ๋ง๋ค์์ ํ
๋ฐ, ์ด์ ๋ ๊ทธ๋ ๊ฒ ํ๋ฉด ํ
์คํธ, ์ค์ฝํ, ์ํ ์์กด์ฑ์์ ๋ฐ๋ก ๋น์ด ์๊ธด๋ค๋ ๊ฑธ ์๋ค.
์์๋ค ์ฃผ๋ฌธ ๋ก์ง์ฒ๋ผ ์ฅ๋ฐ๊ตฌ๋, ๊ฒฐ์ , ์๋ฆผ์ด ์๋ก ์ฝํ๋ ๊ธฐ๋ฅ์์๋ ์์กด์ฑ ๋ฐฉํฅ์ด ๊ณง ์ ์ง๋ณด์ ๋์ด๋์๋ค.
๐ก "์๋น์ค๋ฅผ ์ฃผ์ ๋ฐ๋๋ค๋ ๊ฑด ๊ฐ์ฒด๋ฅผ ๋น๋ฆฌ๋ ๊ฒ ์๋๋ผ, ์์กด์ฑ ๋ฐฉํฅ์ ํ์ด ๋ณผ ์ ์๊ฒ ์ ์ธํ๋ ์ผ์ด๋ค."
๋ด์ผ์ Nest can't resolve dependencies๋ฅผ ๋ง๋๋ฉด ์ด๋ ๋ชจ๋์ด provider๋ฅผ ์์ ํ๊ณ exportํด์ผ ํ๋์ง๋ถํฐ ํ์ธํด์ผ๊ฒ ๋ค.