그리고 왜 서비스에 DI, IoC 이런 개념이 들어왔을까??
먼저 서비스가 의존성 주입을 통해 만들어지는지 확인해 보자.
보통의 디자인 패턴은 결합도를 낱추고 유지보수를 편하게 하기 위해서 등장한다고 한다.
근데 도대체 결합도가 높은 코드가 뭐길래 이게 이렇게 중요한 것인가?
프로젝트를 진행하면서 꼭 겪었던 일이다. 이 코드를 수정하면, 저 코드도 무조건 수정해야 하는 경우가 종종 있었다. 우린 이런 경우를 '결합도가 높다' 라고 표현을 한다.
예를들자면,
class RealEmailService {
sendEmail(email: string, content: string): void {
console.log(`Sending email to ${email} with API key ${this.apiKey}: ${content}`);
}
}
이런 서비스 클래스를 일전에 만들었었다.
그런데 이 클래스에서 예를 들어 하나의 변수가 필요해 진 상황이다.
그럼 우리는 당연히,
class RealEmailService {
private apiKey: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
}
sendEmail(email: string, content: string): void {
console.log(`Sending email to ${email} with API key ${this.apiKey}: ${content}`);
}
}
이런 코드를 짤텐데, 평소와 같이 아무생각 없이 코드를 짜면 이런 형태로 짜게 될 것이다.
class AuthService {
private emailService: RealEmailService;
constructor() {
this.emailService = new RealEmailService('my-api-key'); // 생성자 인자 추가
}
login(email: string): void {
this.emailService.sendEmail(email, 'Login successful!');
}
}
이런식으로 말이다. 문제점이 조금 보이나? 매번 새로운 생성자에 필요한 인자들이 변경된다면, 우리는 일일히 코드를 쫒아다니면서 수정을 해야한다. 이런 상태를 결합도가 높다라고 표현을 한다.
그럼 이것을 어떻게 줄일까?
import { Controller, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { EmailService } from './email.service';
@Controller('auth')
export class AuthController {
constructor(
private readonly userService: UserService,
private readonly emailService: EmailService,
) {}
}
import { Controller, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { EmailService } from './email.service';
@Controller('auth')
export class AuthController {
constructor(
private readonly userService: UserService,
private readonly emailService: EmailService,
) {}
@Post('login')
async login(@Body('email') email: string): Promise<string> {
const user = this.userService.getUser(email);
this.emailService.sendEmail(user.email, 'Login successful!');
return 'Login email sent';
}
}
이렇게 컨트롤러의 생성자의 인자로 서비스를 넘겨주고, 하나의 모듈에서 서비스 생성자에 필요한 인자들을 통합해 관리하면 결합도를 낮추면서, 코드의 유지보수성을 올릴 수 있게 된다.
가장 중요한건 컨트롤러와의 차이일 것이다. 가장 헷갈리는 부분이 왜 컨트롤러와 서비스를 나누어 개발하냐인데, 우리는 이것을 Seperation fo Concerns라고 부른다. 즉, 같은 요청에도 컨트롤러와, 서비스가 하는 일을 나누어 생각하는 것이다.
그래서, 보통 우리가 컨트롤러를 만들때 고려하는 점은,
정도가 있을 것 같다. 즉, 컨트롤러는 어떤 요청을 비즈니스 로직과 연결시키는 다리라고 생각하면 된다.
반대로, 서비스는 우리의 비즈니스 로직을 실현하는 곳이다. 주로
하는 곳이다.