[JAVA] Service & ServiceImpl

Soo·2023년 10월 23일
0

최근까지 개발을 하면서 Service만 만들어 기능을 구현해 왔었는데 웬만하면 ServiceImpl를 생성해서 사용하는게 좋다는 이야기를 듣고 분리해서 설계를 하고 그 이유에 대해 깊게 고민해보지 않았다가 ServiceImpl에 @Override가 생겨서 Override의 기능에 대해 공부해보다가 갑자기 구조를 분리하는 이유가 궁금해져서 기록을 남겨본다. (@Override에 대해서도 기록해보기)

❓ Service interface와 ServiceImpl class 구조를 사용하는 이유

현재 프로젝트에서 푸시 관련 기능을 개발 중이라 푸시를 예를 들어보겠다. 보통 프로젝트에서 Service를 만들 때, PushService와 같이 서비스를 인터페이스로 설계하고, PushServiceImpl라는 구현체인 클래스를 생성해서 사용하는 방식으로 설계된다고 한다.

이론상으로는 위와 같이 Service, ServiceImpl 패턴으로 설계해야하는 이유는 인터페이스와 구현체를 분리함으로써 구현체를 독립적으로 확장할 수 있고, 또한 구현체 클래스를 변경하거나 확장하더라도 이를 사용하는 클라이언트의 코드에 영향이 가지 않도록 하기 위함이다.

이 같은 추상화를 통한 구현방식은 객체지향의 특징 중 하나인 다형성과 객체지향의 5가지 원칙 중 하나인 Open Closed Principle의 설계방식이라고 할 수 있다.

그렇다면 Open Closed Principle은 무엇일까?

💡 OCP (Open Closed Principle)

  • 개방, 폐쇄 원칙이라고도 하며, '소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해 열려있어야 하고, 수정에 대해서는 닫혀있어야 한다'라는 프로그래밍 원칙이다.

🔴 위 추상화를 통한 구현 방식의 단점

  • 코드 구조가 복잡해지고, 복잡해진 구조만큼 코드를 분석하고 확인하는 과정에서 인터페이스를 거쳐 구현체들을 확인해야하는 번거로움들이 발생할 수 있다.

🟡 예시를 통한 단점 해결

// PushController
@PostMapping("/getPushInfo")
    public ResponseEntity<?> getPushInfo();
    }

getPushInfo()를 정의하는 Controller,

@Service
public class PushServiceImpl implements PushService {
	@Override
    public PushInfo getPushInfo(PushIdReq pushIdReq) {
    		System.put.println("get Push Info");
            }
       }

PushService의 getPushInfo 기능을 구현하는 PushServiceImpl

public interface PushService {
PushInfo getPushInfo(PushIdReq pushIdReq);
}

getPushInfo() 메서드를 구현하는 구현체 PushServiceImpl가 있다면 Service와 각 방법으로 getPushInfo() 메서드를 구현하는데 이때 getPushInfo() 메서드를 정의하는 것은 인터페이스인 PushService의 역할을 한다.

이러한 구조로 프로젝트를 설계했을 때, interface에서 정의한 기능을 새로운 방식으로 구현해야 한다면 사용해야하는 곳에서 구현체만 손쉽게 바꿀 수 있기 때문에 Service를 인터페이스로 만들고, 해당 기능을 ServiceImpl 라는 class로 구현하는 것입니다.

이처럼 Service를 interface로 만드는 목적은 하나의 역할을 여러 방식으로 구현한다. 이유는 인터페이스와 구현체의 분리를 통해 독립적으로 보다 자유로운 확장을 보장을 위해서이다.

하지만 위에서 이야기했던 것처럼 대부분의 프로젝트는 인터페이스와 구현체가 1:1로 만들어져 사용되고 있는데 이러한 상황에서 앞으로도 이러한 관습적인 추상화를 통한 Service, ServiceImpl 설계 방식을 무조건적으로 지켜야할까?

개발을 할 때, 남들이 쓰는 라이브러리를 사용한다고 똑같이 사용할 것이 아니라 라이브러리를 사용할 때에는 분명한 장단점이 있기 때문에 이 라이브러리를 도입하기 전 어떠한 단점이 있는데도 불구하고 꼭 도입해야할 만한 이유를 생각해보는 것이 중요하다고 생각했다. 설계방식 또한 마찬가지라고 생각하기 때문에..

결론적으로는,

자바는 모든 것이 객체로 이루어져 있기에 객체에 대한 설계와 이를 구현한 코드는 언제든지 변할 수 있다는 것을 생각해야한다. 그렇기 때문에 이를 대비해 지금 만들어서 사용중인 인터페이스와 구현체 클래스가 1:1 관계를 맺고 있을지 모르지만 서비스가 커지고 변화함에 따라서 얼마든지 구현체 클래스는 확장될 가능성도 생각을 해야한다. 그렇기 때문에 이러한 구조를 통해 나중을 생각해서 유연하게 대처할 수 있도록 대비해야 할 것이다.

profile
Soogineer's Devlog

0개의 댓글