////
Search
Duplicate
🕖

스프링 3대 프로그래밍 모델

생성일
2022/08/09 14:44
태그
IOC/DI(Inversion of control)
컨트롤러가 의존성을 사용은 하지만, 직접 관리하지 않고 어디선가 관리하는 일을하고 만들어 줄 수 있도록 생성자를 통해 받아옵니다. 의존 역전/의존성 주입은 @Autowired나 XML 설정을 통해서 강한 결합을 느슨한 결합으로 변경해주며, 코드를 유연하게 해줍니다.
AOP
관점 지향 프로그래밍으로서 공통된 로직을 추출하여 메소드의 다양한 시점에 실행할 수 있게 해주며, 코드를 줄여주고, 개발자가 공통 로직을 배제하고 핵심 관심사에 집중할 수 있도록 해준다.
PSA
Portable Service Abstraction 으로 일관성 있는 서비스 추상화 입니다. 서비스 추상화의 대표적인 예를 JDBC로 들 수 있으며, 어떠한 데이터 베이스를 사용하더라도 일관성있는 방식으로 제어할 수 있도록 공통의 인터페이스를 제공하는 것이 서비스 추상화라고 한다.
IOC/DI - 제어의 역전 / 의존성 주입
스프링에서는 제어의 역전을 의존성 주입이라고도 한다. 이를 더 잘 이해하기 위해서는 의존성을 이해해야한다.
프로그래밍에서 의존성이란?
의존성은 어렵게 생각하지 않아도 단순한게 예를 들자면 다음과 같다.
운전자는 자동차를 생산한다 = new Car();
자동차는 내부적으로 타이어를 생산한다 = Car 객체 생성자에서 new Tire();
Car 객체 생성자에서 new 를 실행함으로 Car가 Tire에 의존한다고 볼 수 있다. 이렇게 의존이라는 것은 전체가 부분에 의존하는 것을 표현하며, 좀 더 깊게 들어가서 의존 관계 사이를 집합 관계와 구성 관계로 구분할 수 있으며, 의존 관계를 어떻게 맺냐에 따라서 강한 결합이 되느냐, 느슨한 결합이냐를 이야기할 수 있게된다.
집합 관계
부분이 전체와 다른 생성 주기를 가질 수 있다.
구성 관계
부분은 전체와 같은 생명 주기를 갖는다.

강한 결합

객체 내부에서 다른 객체를 생성하는 것은 강한 결합도를 가지는 구조이다. A 클래스 내부에서 B라는 객체를 직접 생성하고 있다면, B 객체를 C 객체로 바꾸고 싶은 경우에 A클래스도 수정해야하는 방식이기 때문에 강한 결합이다.
위에서도 동일하게 자동차 내부에서 타이어를 생성하는 것은 다른 타이어를 생성하고자 해도 코드를 수정해야되는 상황이 발생한다.

느슨한 결합

객체를 주입 받는다는 것은 외부에서 생성된 객체를 인터페이스를 통해서 넘겨받는 것이다. 이렇게 하면 결합도를 낮출 수 있고, 런타임시에 의존 관계가 결정되기 때문에 유연한 구조를 가진다.
SOLID 원칙에서 O에 해당하는 Open Closed Principle 을 지키기 위해서 디자인 패턴 중 전략 패턴을 사용하게 되는데, 생성자 주입을 사용하게 되면 전략패턴을 사용하게 된다.

객체의 주입

객체를 주입하는 방법은 생성자를 통한 주입, setter을 통한 주입이 있다. 각 방식에 대해서 살펴보고 어떠한 문제점들이 발생하는지 확인해보자.
먼저 객체 내부에서 생성하는 코드 부터 확인하자.
package 의존성주입; public interface Dress { String getSeason(); } public class FWSeasonDress implements Dress{ @Override public String getSeason() { return "F/W 신상 드레스"; } } public class SSSeasonDress implements Dress{ @Override public String getSeason() { return "S/S 신상 드레스"; } } public class Person { Dress dress; public Person() { dress = new FWSeasonDress(); } public String getDress() { return "입은 옷은 " + dress.getSeason(); } }
Java
복사
사람은 옷을 입게된다. 이 코드에서 확인해보면 사람이 생성될 때 입을 옷이 결정이 된다. 따라서 코드가 유연하지 못하다는 의미이다. 이제 생성자 주입과 수정자 주입을 확인해보자

생성자를 통한 의존성 주입

생성자를 통한 주입 방법은 매우 간단하다. 기존의 생성자 코드를 아래와 같이 변경해주면 된다.
package 의존성주입; public class Person { Dress dress; public Person(Dress dress) { this.dress = dress; } public String getDress() { return "입은 옷은 " + dress.getSeason(); } }
Java
복사
Person의 생성자 부분이 변경되었다. 이렇게 되면 외부에서 Person 객체가 생성될 때 입을 옷을 결정해 줄 수 있다.
코드가 유연해졌다는 의미이다. 기존의 Person 클래스 코드 변경없이 새로 추가되는 시즌의 옷들을 갈아 입혀 줄 수 있게 되었다. 좀 더 실무적인 이야기를 해보자면, 새로운 W/W 시즌의 옷이 나왔을 때 코드의 변경은 Person 객체를 변경해주는 부분과 새롭게 추가되는 W/W 객체만 컴파일 해서 배포하면 된다.

Setter(수정자)를 통한 주입

생성자를 통한 의존성 주입을 현실 세계로 예를 들어보면 한번 사람이 태어나서 입을 수 있는 옷은 고정되어 변경할 방법이 없다는 것이다. 이러한 부분에서 유연성이 떨어지기 때문에 유연성 높은 코드를 작성하고자 한다면 이를 생성자가 아닌 속성을 통해서 의존성 주입을 해야한다.
사실 이러한 부분에서 프로그래밍 진영에서는 생성자를 통한 것이 좋은가? 아니면 속성을 통한 주입이 좋은가에 대한 이야기가 많다. 따라서 사실 무엇이 더 좋다기 보단 상황에 맞는 DI를 선택하는 것이 올바르다고 볼 수 있다.
코드를 변경해보면 아래와 같이 바뀐다.
package 의존성주입; public class Person { Dress dress; public void setDress(Dress dress) { this.dress = dress; } public Dress getDress() { return dress; } public String getDressSeason() { return "입은 옷은 " + dress.getSeason(); } } public class Driver { public static void main(String[] args) { Dress dress = new FWSeasonDress(); Person person = new Person(); person.setDress(dress); } }
Java
복사
생성자가 사라졌고, dress 속성에 대한 접근자와 설정자 메소드가 생겼다. 따라서 언제든지 사람의 옷을 변경 가능하도록 유연한 코드로 변경하게 되었다.

AOP(Aspect-Oriented Programming): 관전 지향 프로그래밍

스프링은 DI가 의존성의 주입이라면 , AOP는 코드 주입이라고 할 수 있다. 여러 모듈을 개발하다보면 모듈들에서 공통적으로 등장하는 로직이 존재한다. 예를 들어서 입금 출금 이체와 같은 부분에서 보완적인 부분이나 트랜잭션 로그를 남기고자 하는 코드 부분들이 분명히 공통적으로 등장할 것이다.
이렇게 공통적으로 등장하는 부분은 횡단 관심사(cross-cutting concern)라고 한다. 일반적으로 코드는 핵심 관심사 + 횡단 관심사로 구성된다. 따라서 이러한 부분을 @Aspect 어노테이션을 통해서 추출하여 특정 메소드가 호출될 때 특정 시점에 동작하도록 할 수 있는 것이다.
이를 통해서 스프링이 얻고자 하는 부분은 어떤 것인가? 공통적으로 등장하는 횡단 관심사를 어느 한 사람이 잘 정의하여 코드를 작성했다면, 다른 개발자들은 이를 재사용할 수 있을 것이다. 이를 통해서 기존의 횡단 관심사를 계속해서 코딩해야 되는 불편함이 사라지고 오직 개발자들은 핵심 관심사에만 집중하여 개발을 할 수 있게되는 것이다. 또한 핵심 관심사에만 집중함으로 자연스럽게 SRP을 적용할 수 있게된다.

PSA(Portable Service Abstraction) : 일관성 있는 서비스 추상화

PSA 는 일관성있는 추상화이다. 서비스 추상화의 대표적인 예로 JDBC를 두는데 이렇나 표준 스펙 덕분에 개발자는 오라클을 사용하든, MySQL을 사용하든, MS-SQL 을 사용하던 어떠한 데이터베이스를 사용하던 공통된 방식으로 코드를 작성할 수 있다. 데이터베이스 종류에 관계없이 같은 방식으로 제어할 수 있는 이유는 디자인 패턴에서 설명했던 어댑터 패턴을 활용했기 때문이다. 이처럼 어댑터 패턴을 적용해 같은 일을 하는 다수의 기술을 공통의 인터페이스로 제어할 수 있게 한 것을 서비스 추상화라고 한다.

같은 태그의 다른 글 보기

Search
SpringBoot