프로젝트를 진행하면서 설계할 때 자연스럽게 service와 serviceImpl로 설계를 했다.
큰 생각이 없다가 강의를 들으면서 문득 왜 나누는 거지란 의문이 들어서 블로그를 찾아보며 정리해 보았다.
이유
이유는 인터페이스와 구현체를 분리함으로 코드의 구조화, 확장성, 유지보수성을 개선하기 위함으로 생각된다.
일반적으로 Service는 비즈니스 로직을 처리하는 인터페이스를 의미하고, ServiceImpl은 실제 비즈니스 로직을 구현하는 클래스이다.
- 코드의 구조화: Service 인터페이스와 ServiceImpl 구현 클래스를 사용하여 코드를 논리적으로 구조화함으로 코드를 더 읽기 쉽고 이해하기 쉬운 형태로 구성할 수 있다.
- 확장성: Service 인터페이스를 기반으로 다양한 구현체를 만들 수 있다. 이는 다형성을 활용해 유연한 확장이 가능하게 한다. 예를 들어, 동일한 Service 인터페이스를 구현하는 다른 ServiceImpl클래스를 만들어서 다른 비즈니스 로직을 수행하는 서비스를 추가할 수 있다.(이와 같은 방법이 SOLID 원칙 중에 OCP에 해당한다고 생각한다.)
- SRP(단일 책임 원칙): Service 인터페이스 비즈니스 로직에 대한 추상화를 담당하고, ServiceImpl 클래스는 실제 비즈니스 로직에 대한 추상화를 담당한다. ServiceImpl 클래스는 실제 비즈니스 로직을 구현하는 역할을 수행한다. 이를 통해 코드의 가독성과 유지보수성이 향상된다.
- Loose Coupling: 객체 간의 결합도를 낮춰 변화에 유연한 개발을 하기 위함이다. 하나의 인터페이스를 구현하는 여러 구현체가 있고 기능에 따라 적절한 구현체가 들어가서 다형성을 주기 위함이다.
OCP (Open Closed Principle)
개방, 폐쇄 원칙이라고도 하며, 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다는 원칙
SRP (Single Responsibility Principle)
하나의 클래스는 단 하나의 책임을 가져야 한다는 원칙
그럼 이걸 언제 써야 하는가?
비밀번호 변경 방식을 예시로 들어보자.
- 비밀번호 기반으로 비밀번호를 변경하는 기능
- 비밀번호를 잃어버려서 다른 인증 기반으로 비밀번호를 변경하는 기능
// 비밀번호를 바꾸는 인터페이스
public interface ChangePasswordService {
public void change(MemberId id, PasswordDto.ChangeRequest dto);
}
// 비밀번호 기반으로 비밀번호를 변경하는 기능
public class ByPasswordChangePasswordService implements ChangePasswordService {
private MemberFindService memberFindService;
@Override
public void change(MemberId id, PasswordDto.ChangeRequest dto) {
if (dto.getPassword().equals("비밀번호가 일치하는지 판단 로직...")) {
final Member member = memberFindService.findById(id);
final String newPassword = dto.getNewPassword().getValue();
member.changePassword(newPassword);
}
}
}
// 비밀번호를 잃어버렸을 때 다른 인증 기반으로 비밀번호를 변경하는 기능
public class ByAuthChangePasswordService implements ChangePasswordService {
private MemberFindService memberFindService;
@Override
public void change(MemberId id, PasswordDto.ChangeRequest dto) {
if (dto.getAuthCode().equals("인증 코드가 적합한지 로직 추가...")) {
final Member member = memberFindService.findById(id);
final String newPassword = dto.getNewPassword().getValue();
member.changePassword(newPassword);
// 필요로직...
}
}
}
네이밍 컨벤션은 이대로 괜찮은가(feat. impl, I)
검색하다 이런 글도 봤다.
대표적으로 자바 Collection의 List 인터페이스를 구현한 ArrayList, LinkedList에서는 impl이라는 접미사는 찾아볼 수 없습니다
아래는 스택오버플로우에서 발췌한 구문입니다.
Look to the Java standard library itself. Do you see IList, ArrayListImpl, LinkedListImpl? No, you see List and ArrayList, and LinkedList. Here is a nice article about this exact question. Any of these silly prefix/suffix naming conventions all violate the DRY principle as well.
네이밍 컨벤션이 잘못된 거라고 말하고 있다. 근데 이미 이런 식으로 오랜 기간 사용해 와서 바꿀 수 있을까 의문이다.
나는 아직 크게 프로젝트를 한 적이 없어서 service와 serviceImpl로 나눌 필요는 없었던 것 같다. 하지만 코드의 분리와 확장은 언제 필요할지 모르니 계속 이런 방법으로 하는 게 좋을 것 같다.
출처:
https://junior-datalist.tistory.com/243
https://jeonyoungho.github.io/posts/spring%EC%97%90%EC%84%9C-Service-ServiceImpl%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%98%EB%82%98/
'Spring' 카테고리의 다른 글
DDD(Domain Driven Desgin) (0) | 2023.07.25 |
---|---|
프로젝트 패키지 구조는 어떻게 나누는게 좋을까 (0) | 2023.07.14 |
OAuth (0) | 2023.05.06 |
Redis (0) | 2023.05.05 |
JWT (0) | 2023.05.03 |