2024-05-28 [Spring 에서 Service Layer 의 책임]
강의를 따라 하다 보니 이전 과제를 진행했을 때, 거의 모든 Method 들을 Service Layer에서 구현했었는데, 사실 이는 좋지 않은 방법이라는 피드백을 들었습니다. 이게 정확히 무슨 말이고, 구체적으로 어떤 문제를 야기할 수 있는지, 이를 해결할 수 있는 방법은 뭐가 있는지 작성해보았습니다.
서비스 레이어에 많은 메소드를 넣는 것의 문제점
1. 단일 책임 원칙(SRP) 위반
단일 책임 원칙을 따르면, 하나의 클래스가 단 하나의 책임 만을 가지고 있어야 합니다. 모든 클래스는 단 한 가지의 책임을 부여 받아, 수정할 이유가 단 한 가지여야 함을 의미합니다. 즉, 클래스에 속해있는 멤버들과 메소드는 모두 공통적으로 하나의 서비스를 위해 필요해야만 합니다.
때문에 서비스 클래스가 너무 많은 메소드를 가지고 있으면 이 클래스가 여러 책임을 가지게 될 가능성이 높아 SRP 를 위반하게 됩니다.
2. 단위 테스트
단위 테스트란? 애플리케이션을 구성하는 하나의 기능이 올바르게 동작하는지를 독립적으로 테스트하는 것으로, '어떤 기능이 실행되면 어떤 결과가 나온다' 정도로 테스트를 진행하는 것을 의미합니다.
단위 테스트를 진행하기 위해 모킹(Mock) 모듈을 작성하여 테스트를 진행하는데 각 메소드가 서로 의존성을 가질 경우, 테스트 시 모킹을 설정하는 것이 어려워 질 수 있습니다.
3. 결합도 증가
서비스 클래스는 Repository 를 의존하도록 구현하면서 결합도를 낮추는 방식을 사용하고 있습니다. Repository 계층에 있는 로직들이 어떻게 구동되는지 전혀 알지 못하고 추상화된 메소드 이름으로만 사용합니다.
그만큼 각 계층간, 그리고 다른 모델과 결합도를 낮추는 것이 중요한데, 서비스 클래스가 많은 메소드를 가지게 되면 다른 클래스들과의 결합도가 높아져 시스템이 유연성을 잃게 됩니다. 이는 결국 변경에 취약한 구조를 초래할 수 있습니다.
(** 내용 추가)
사실 현시점에 현실적으로 뭐가 문제인지 파악하고, 해결하는 방법을 찾는 것은 어렵게 느껴진다.
그래서 '다른 사람들이 서비스 계층에서 마주한 문제점들은 무엇인지', '내가 현재 진행 중인 프로젝트 내 서비스 레이어에는 어떤 문제가 있을지' 들여다보다 아래와 같이 여러 개의 Repository 를 의존하고 있다는 사실을 파악했다.
@Service
class PostServiceImpl(
private val postRepository: PostRepository,
private val userRepository: UserRepository,
private val hashTagRepository: HashTagRepository,
private val postTagRepository: PostTagRepository,
private val postLikeRepository: PostLikeRepository,
) : PostService {
override fun getPostById(postId: Long, authUser: AuthUser): PostResponse {
val foundPost = postRepository.findByIdOrNull(postId) ?: throw ModelNotFoundException("Post", postId)
foundPost.id?.let { postId ->
val like = postLikeRepository.findByPostIdAndUserId(postId, authUser.id)
foundPost.heartStatus = like != null
} ?: throw IllegalStateException("Post ID should not be null")
return PostResponse.from(foundPost)
}
예시로 getPostById 를 테스트 하려고 하면, PostServiceImpl 이 의존 중인 모든 repository 를 채워 넣어 줘야 합니다.
심지어 getPostById 는 postRepository 와 postLikeRepository 만 의존하여 사용 중입니다.
쉽게 말해, 서비스가 과도한 책임을 가지고 있어, 메소드 테스트의 어려움이 있고,
관련 문제를 해결하는 아이디어를 찾다 보니 아래와 같은 글을 발견하여 링크를 첨부합니다.
하나의 Service가 여러 Repository에 의존할 때
부제: 역할이 모호한 코드 명확하게 리팩토링하기
kchung1995.github.io
Facade (퍼사드) 패턴을 이해한 뒤, 추후 해당 서비스를 분리해보는 연습을 해봐야겠습니다.
계속 계속 찾아봐도 어려워 .. 조언을 주시면 감사하겠슴다 ..