본문 바로가기
스프링 관련/토비의 스프링 6

관심사의 분리 (Separation of Concerns)

by 문자메일 2024. 7. 23.

PaymentService는 여전히 클래스 관점에서 보자면 두 개의 다른 관심사를 가지고 있다.

클래스 관점에서 볼 때 변경이 되야 하는 시점과 이유가 제각각인 코드들이 혼재되어 있다.

이것들을 클래스 밖으로 분리하는 작업이 필요하다.

 

분리해야 하는 이유

1. 재사용 관점에서 좋지 않음 (한 번 만들어진 기능을 계속 가져다 사용하는 것)

2. 

 

 

상속을 통한 재사용

상속을 통한 재사용에서는 상위 클래스와 하위 클래스가 굉장히 강하게 결합이 되어 있다.

상위 클래스의 로직 변화가 하위 클래스가 그대로 영향을 받는다는 말

그리고 자바는 단일 상속만 지원하기에, 복잡한 상속 구조를 만들어서 여러가지를 조합하는 이런 것도 불가능하다.

클래스의 분리 강의 다이어그램

but 위 구조 만으로는 해당 클래스와, 그 클래스를 사용하는 Client에서 모두 변경이 필요하게 된다.

(왜냐하면 공통 자료형 ,인터페이스가 없기 때문)

 

인터페이스 도입

PaymentService는 기본적으로 ExRateProvider에만 의존한다.

PaymentService는 인터페이스 ExRateProvider가 변경되면 같이 변경된다.

다른 말로 인터페이스가 변경되지 않으면 변경되지 않는다.

 

아래 단계에서는 DI를 사용하지 않고 PaymentService에서 ExRateProvider 인터페이스를 직접 new 해서 사용했기 때문에, 

아래 그림에서 처럼 클래스 레벨에서 의존하고 있었다.

아래는 런타임 시에 실제 의존하는 오브젝트 다이어그램 이다.

(런타임에 내가 어떤 클래스의 오브젝트를 써야 하나 저것을 결정해주는 코드가 필요하다.)

 

위 그림에서는 paymentService 내에 인터페이스의 구현체를 바로 new 하는 로직이 들어가 있다.

 

관계설정 책임의 분리가 필요하다.

아래처럼 의존관계를 설정하는 코드를 다른 곳으로 분리시킨다.

밑 그림처럼 인터페이스 구현체를 다른 클래스에서 넣어주도록 다이어그램을 수정하면 된다.

 

 

오브젝트 팩토리

ObjectFactory 클래스를 이용해서 클라이언트가 사용할 PaymentService Object를 얻어오는 거다.

ObjectFactory 안에 얘가 어떤 ExchangeRateProvider를 사용할 것인가에 대한 모든 작업이 다 끝난 상태가 되어야 한다.

그래서 runtime object 사이의 의존관계를 설정하는 그 책임은, ObjectFactory에 넘기는 것이다.

 

위 Client 코드에는 두 가지 굉장히 다른 관심사가 섞여있다고 한다.

첫 번째는, Payment 서비스가 어떠한 클래스의 오브젝트를 이용해서 동작할 것인가 그 관계를 설정해주는 책임을 가진 코드이고,

두 번째는 Payment 서비스를 이용해서 실제로 업무(로직)을 진행하는 코드, 여기서는 프린트만 하고 말았지만, 아마 프론트엔드 쪽으로부터 어떤 요청을 받아서 준비한 다음에 API를 리턴해 주거나, 아니면 이제 뷰를 이용해서 웹화면을 만들어서 리턴하는 등의 뭐 그런 작업에 필요한 일을 하게 된다.

 

이렇게 두 개의 다른 관심사가 있는데, 이것을 분리해내면 좋겠다.

(1)이 변경될때마다 이 클라이언트 코드를 건드리고 싶지 않다는 것이다.

 

 

 

 

원칙과 패턴

지금까지 코드를 만들어 보면서 지속적으로 한 가지 원칙은 이야기를 했다.

'관심사의 분리'

관심사의 분리를 가지고 예제에서 지금까지 코드를 계속 발전시켜 왔다.

 

첫 번째 원칙

개방 폐쇄 원칙 (Open-Closed Principle / OCP)

: 클래스나 모듈은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.

-> (간단히) 어떤 클래스는 이 클래스의 기능을 확장할 때 그 클래스의 코드는 변경하면 안 된다.

-> 여기서 확장이라는게 무엇을 뜻하는지 정의할 필요가 있는 듯.

-> 기존에 사용하던 어떤 로직을 다른 로직으로 대체하려고 할 때 (이것을 확장이라고 하나?) 그 큰 목적의 기능 인스턴스를 사용하는 클라이언트에서는 코드 변화가 일어나면 안 된다는 말로 생각됨.

-> 그 말은 클라이언트에서 사용하는 '특정 기능'의 인스턴스를 만들어서 리턴해주는 클래스가 필요하다는 결론으로 남. (ObjectFactory 클래스 같은)

PaymentService가 OCP가 적용되어 있음.

 

 

두 번째 원칙

높은 응집도와 낮은 결합도 (High Coherence and low coupling)

: 응집도가 높다는 것은 하나의 모듈이 하나의 책임 또는 관심사에 집중되어 있다는 뜻,

 변화가 일어날 때 해당 모듈에서 변하는 부분이 크다.

 책임과 관심사다 다른 모듈과는 낮은 결합도, 즉 느슨하게 연결된 형태를 유지하는 것이 바람직하다.

 

세 번째

전략 패턴

: 자신의 기능 맥락(context)에서, 필요에 따라서 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴

 

 

네 번째

제어의 역전 / Inversion of Control

제어권 이전을 통한 제어관계 역전 - 프레임워크의 기본 동작 원리

 

 

 

 

 

 

'스프링 관련 > 토비의 스프링 6' 카테고리의 다른 글

오브젝트와 의존관계  (0) 2024.07.23

댓글