사용자의 유즈 케이스(Use Case)와 워크플로우(Workflow)를 명확히 정의하고 이해할 수 있도록 도와줍니다.
비즈니스 로직이 API 뒤에 숨겨져 있으므로, 서비스 계층의 코드를 자유롭게 수정하거나 리팩터링할 수 있습니다.
저장소 패턴(Repository Pattern) 및 가짜 저장소(Fake Repository)와 조합하면 높은 수준의 테스트를 작성할 수 있습니다.
서비스 계층의 단점
서비스 계층 또한 다른 추상화 계층이므로, 잘못 사용하면 코드의 복잡성을 증가시킬 수 있습니다.
한 서비스 계층이 다른 서비스 계층에 의존하는 경우, 의존성 관리가 복잡해질 수 있습니다.
서비스 계층에 너무 많은 기능을 넣으면 빈약한 도메인 모델(Anemic Domain Model)과 같은 안티 패턴이 생길 수 있습니다.
사용자의 요구사항을 처리하는 실세 중에 실세!!
DB 정보가 필요할 때는 Repository에게 요청합니다.
3계층 아키텍처 - posts.service.js
// src/services/posts.service.js
import { PostsRepository } from '../repositories/posts.repository.js';
export class PostsService {
postsRepository = new PostsRepository();
findAllPosts = async () => {
// 저장소(Repository)에게 데이터를 요청합니다.
const posts = await this.postsRepository.findAllPosts();
// 호출한 Post들을 가장 최신 게시글 부터 정렬합니다.
posts.sort((a, b) => {
return b.createdAt - a.createdAt;
});
// 비즈니스 로직을 수행한 후 사용자에게 보여줄 데이터를 가공합니다.
return posts.map((post) => {
return {
postId: post.postId,
nickname: post.nickname,
title: post.title,
createdAt: post.createdAt,
updatedAt: post.updatedAt,
};
});
};
createPost = async (nickname, password, title, content) => {
// 저장소(Repository)에게 데이터를 요청합니다.
const createdPost = await this.postsRepository.createPost(
nickname,
password,
title,
content,
);
// 비즈니스 로직을 수행한 후 사용자에게 보여줄 데이터를 가공합니다.
return {
postId: createdPost.postId,
nickname: createdPost.nickname,
title: createdPost.title,
content: createdPost.content,
createdAt: createdPost.createdAt,
updatedAt: createdPost.updatedAt,
};
};
}
이번 서비스 계층(Service Layer)에서 PostsService 클래스가 PostsRepository의 findAllPosts, createPost 메서드를 호출하는 것을 확인할 수 있는데요, 해당 코드는 서비스가 비즈니스 로직을 수행하는 데 필요한 데이터를 저장소 계층(Repository Layer)에게 요청하여 가져오는 것을 확인 할 수 있습니다.
또한, 서비스 계층에서는 return posts.map(post => {}); 와 같이 데이터를 가공하는 작업이 이루어집니다. 만약, 저장소 계층에서 받은 데이터를 그대로 클라이언트에게 전달한다면, 사용자의 비밀번호와 같은 민감한 정보까지 노출되는 보안 문제가 발생하여, 서버의 보안성이 떨어지는 결과를 낳게된답니다.