[Spring] 인터페이스 특징과 ISP(인터페이스 분리 원칙)에 대한 정리
- Java 인터페이스의 특징
- OOP의 인터페이스 분리 원칙
- 인터페이스 상속
- 정리
📌 1. Java 인터페이스 특징
Java 인터페이스는 다형성을 가장 잘 보여주는 존재입니다.
public interface RemoteController { void volumeUp(); void volumeDown(); }
리모컨이라는 인터페이스를 가지고 간단한 예시를 들어보겠습니다.
@Component public class SRemoteController implements RemoteController { @Override public void volumeUp() { // volume up 로직 System.out.println("S사 리모컨 볼륨을 1단계 높였습니다."); } @Override public void volumeDown() { // volume down 로직 System.out.println("S사 리모컨 볼륨을 1단계 낮췄습니다."); } }
이런식으로 RemoteController
라는 인터페이스의 구현체인 SRemoteController
를 만들 수 있습니다. 이렇게 되면 SRemoteController
는 RemoteController
라는 일종의 타입으로 객체를 받을 수 있게 됩니다. 이것이 제가 생각하는 다형성의 핵심입니다.
@ExtendWith(SpringExtension.class) @SpringBootTest class RemoteControllerTest { @Autowired RemoteController remoteController; @Test public void volumeUp() { remoteController.volumeUp(); } @Test public void volumeDown() { remoteController.volumeDown(); } }
JUnit Test에서 리모컨을 테스트한 것이지만 RemoteControllerTest
는 리모컨을 사용하는 클라이언트에 해당한다고 볼 수 있습니다. @Autowired로 DI를 받아 올 때 RemoteController
로 받아옵니다. 즉 RemoteController
의 구현체를 Component로 지정만 하면 어느 것이든 교체할 수 있게 됩니다.
📌 2. OOP의 인터페이스 분리 원칙
ISP(Interface Segregation Principle)은 인터페이스 분리 원칙으로 OOP의 SOLID 기본 원칙 중 하나이자 SRP 개념과 어떻게 보면 비슷한 원칙입니다. 클라이언트의 관심사에 따라 알맞게 인터페이스를 분리해주는 원칙입니다.
@Component("allRemoteController") public class AllFuncRemoteController implements RemoteController, ConsoleController { @Override public void connect() { // 콘솔에 접속 로직 System.out.println("복합 리모컨을 이용해 콘솔 접속을 진행합니다."); } @Override public void selectContent() { // 콘솔 게임 선택 로직 System.out.println("복합 리모컨을 이용해 콘솔 게임을 선택합니다."); } @Override public void setUp() { // 콘솔 환경설정 조작 로직 System.out.println("복합 리모컨을 이용해 콘솔 환경설정을 조작합니다."); } @Override public void volumeUp() { // volume up 로직 System.out.println("복합 리모컨 볼륨을 1단계 높였습니다."); } @Override public void volumeDown() { // volume down 로직 System.out.println("복합 리모컨 볼륨을 1단계 낮췄습니다."); } }
이상한 예시이지만 예를 들어 위의 코드처럼 여러 기능을 갖춘 리모컨이 있다고 가정해보겠습니다. 이 기능들 중에서 A라는 클라이언트는 콘솔을 조작할 수 있는 기능들에 관심이 있고 B라는 클라이언트는 기존의 볼륨 조절 기능에 관심이 있습니다. 이 때 인터페이스는 클라이언트 A, B에게 AllFuncRemoteController
를 본인의 관심사에 맞게 바라볼 수 있는 일종의 창(window)을 제공해줍니다.
// For Client A. public interface ConsoleController { void connect(); void selectContent(); void setUp(); } // For Client B. public interface RemoteController { void volumeUp(); void volumeDown(); }
클라이언트 A, B는 각각 ConsoleController
, RemoteController
인터페이스라는 창을 통해 AllFuncRemoteController
구현체를 사용할 수 있습니다. 여기서 또 등장하는 것이 인터페이스 최소주의 원칙인데 클라이언트에게 인터페이스를 통해 메서드를 외부에 제공할 때 최소한의 메서드만 제공하라는 원칙입니다. ConsoleController
에만 관심있는 A에게 RemoteController
기능들도 같이 끼워서 제공할 필요가 없다는 것입니다. 오로지 관심 영역 경계 안에 해당하는 최소한의 기능들만 제공하는 것이 좋습니다.
인터페이스 분리 원칙의 장점은 모든 클라이언트가 본인의 관심 영역에 해당하는 기능들만 접근하고 불필요한 기능들의 간섭 없이 이들을 유지할 수 있다는 것입니다.
📌 3. 인터페이스 상속
인터페이스 분리 원칙을 통해 새로운 클라이언트가 등장했을 때 또 다른 관심 영역에 대해 새로운 인터페이스를 생성하는 방식으로 제공해줄 수 있었습니다. 이런 방법 말고도 기존 인터페이스 상속을 통해 기능을 확장하는 방법도 존재합니다.
public interface ChannelRemoteController extends RemoteController{ void goChannelUp(); void goChannelDown(); } @Component public class MyChAndVolRemoteController implements ChannelRemoteController { @Override public void goChannelUp() { // 채널 up 로직 System.out.println("내 리모컨을 통해 채널 하나 높였습니다."); } @Override public void goChannelDown() { // 채널 down 로직 System.out.println("내 리모컨을 통해 채널 하나 내렸습니다."); } @Override public void volumeUp() { // volume up 로직 System.out.println("내 리모컨으로 볼륨을 1단계 높였습니다."); } @Override public void volumeDown() { // volume down 로직 System.out.println("내 리모컨으로 볼륨을 1단계 낮췄습니다."); } }
이런 방식으로 기존의 인터페이스 기능들로부터 확장하는 방식으로 새로운 클라이언트에게 확장된 기능들을 제공해줄 수 있습니다.
인터페이스 상속을 사용하는 상황은 다음과 같습니다.
- 새로운 클라이언트가 상위 인터페이스를 그대로 사용하면서 새로운 기능들을 추가 확장하고 싶을 때
- 기존 상위 인터페이스를 구현하고 있는 서브 클래스들이 많아서 상위 인터페이스를 수정하고 싶어도 수정하기 힘들 때
📌 4. 정리
인터페이스는 DI를 하는데 있어서 웬만하면 사용해야 하는 중요한 창입니다. 중요한 만큼 인터페이스를 어떤 방식으로 사용하는지에 대해 어딘가 어색한 예시를 들어 알아보았습니다. 이것 말고도 Spring은 인터페이스를 정말 다양한 방식으로 접목해서 사용하고 있는데 위의 방식들도 핵심이라 할 정도로 중요한 내용이니 이 글을 시작으로 인터페이스에 대해 더 깊게 알아보는 시간을 가지면 좋을 것 같네요. :)
틀린 내용이 있을 수 있습니다. 이에 대한 코멘트 언제나 환영입니다!
'Spring' 카테고리의 다른 글
[Spring] 이벤트 처리를 위한 스케줄러 작업 조정(ShedLock 활용) (0) | 2021.09.22 |
---|---|
MyBatis insert 구문 사용시 mapping 주의 사항 (0) | 2021.09.07 |
[Spring] Interceptor, Resolver 객체 전달을 통한 리팩토링 (0) | 2021.08.02 |
Outbox 패턴을 이용한 메일 발송 구현해보기 (0) | 2021.07.08 |
[OOP] 객체 지향 프로그래밍은 무엇일까 (0) | 2021.06.21 |
댓글
댓글(Github)
다른 글
-
MyBatis insert 구문 사용시 mapping 주의 사항
MyBatis insert 구문 사용시 mapping 주의 사항
2021.09.07📌 MyBatis 사용함에 있어서 살짝의 아쉬움…. (그래도 좋다!) MyBatis를 사용하면서 SQL 구문과 객체 관점을 매핑해준다는 점에서 많은 장점이 있습니다. 지연 로딩 등의 여러 유용한 기능들도 제공해주기도 하고 JPA와 같이 사용하면서 성능 조절을 할 수 있다는 점도 장점이라고 할 수 있겠네요. 하지만 제가 사용하면서 느낀점은 개발할 때 Entity Class와 Mapper xml 설정 내용 둘다 유심히 잘 봐야한다는 점에서 번거로움이 있었습니다. 특히 xml 설정 내용은 철자 하나하나 정확하게 작성해야 하고 java 코드가 아니라 쌩 SQL 구문을 이용해야 한다는 점에서 살짝 불편함을 느꼈습니다. 그 중 제가 자주 에러를 마주했던 부분에 대해 기록하고자 합니다. 📌 기본적인 INSERT 쿼… -
[Spring] Interceptor, Resolver 객체 전달을 통한 리팩토링
[Spring] Interceptor, Resolver 객체 전달을 통한 리팩토링
2021.08.02Overview Interceptor와 Resolver 기존 방식 리팩토링 진행 후 코드 정리 📌 1. Overview 최근 프로젝트를 개발하면서 로그인 인증 처리와 세션 연장 처리 부분을 Spring Security 사용하지 않고 구현해보는 과정을 진행하였습니다. Interceptor를 사용하였고 인증된 UserSession 객체를 handler parameter에 전달하기 위해 HandlerMethodArgumentResolver를 사용하였습니다. 문제는 각각의 interceptor와 Resolver 구현체에서 Redis 조회가 이루어져 비용측면에서 비효율적인 부분이 보였습니다. 이를 개선해본 내용들을 블로그에 글로 정리해봤습니다. 📌 2. Interceptor와 Resolver Interceptor… -
Outbox 패턴을 이용한 메일 발송 구현해보기
Outbox 패턴을 이용한 메일 발송 구현해보기
2021.07.08Github Repo https://github.com/beaniejoy/resetpw-outbox-scheduler Overview Eventual Consistency & Strong Consistency At Least Once? Outbox 패턴 코드 구현 📌 1. Overview 프로젝트 개발 중에 비밀번호 초기화 관련한 메일 발송 기능을 구현하는 과정이 있었습니다. 여기서 개념도 익힐겸 Eventual Consistency 고려해서 ALO(At Least Once) 방식을 적용한 메일 발송 기능을 구현해봤습니다. 📌 2. Eventual Consistency? Strong Consistency? 위 두 개의 Consistency 개념은 Multiple Replicas of a Database … -
[OOP] 객체 지향 프로그래밍은 무엇일까
[OOP] 객체 지향 프로그래밍은 무엇일까
2021.06.21Spring은 객체 지향의 꽃이라고 들었는데…. 면접에서도 단골 질문 메뉴인 객체 지향은 무엇일까. 📌 객체 지향(Object Oriented) 개발자들이 더욱 편하게 개발할 수 있는 방법론을 고안하는 과정에서 나온 결과물 중 하나라고 생각 현실 세계를 프로그래밍적으로 잘 표현할 수 있는 모델 But, 우리는 개발자이므로 사물 관련된 것보다 추상적 대상에 대한 클래스화에 초점 (본인 생각으로는 확장과 분리를 잘 하게끔 만들어주는 좋은 틀이라 생각, OCP를 생각해보면 그렇습니다.) 구조적 프로그래밍 함수(function)가 가장 중요한 요소(함수 단위) Divide and Conquer(분할 정복)에 기반해 명령어를 논리적 단위로 나눠 블록화해 작성 객체지향 프로그래밍 등장 Object(객체) 단위로 …
댓글을 사용할 수 없습니다.