이번 글에서는 스프링의 중요한 콘셉트인 IoC(제어의 역전), DI(의존성 주입), AOP(관점 지향 프로그래밍), PSA(이식 가능한 서비스 추상화)에 대해 알아보겠습니다.
💡 이 글을 읽게 됨으로써 얻을 수 있는 지식은 아래와 같습니다.
- IoC(제어의 역전)이 무엇인지
- DI(의존성 주입)이 무엇인지
- 스프링 컨테이너와 빈이 무엇인지
- AOP(관점 지향 프로그래밍)이 무엇인지
- PSA(이식 가능한 서비스 추상화)이 무엇인지
위 네 개념을 쉽게 이해하고 싶어하시는 분들에게 도움이 되고자 스프링 부트 3 백엔드 개발자 되기 - 자바 편의 일부 내용을 공개하기로 했습니다. 이 내용은 책의 2.2장을 거의 그대로 옮겨온 내용으로, 많은 도움이 되었으면 좋겠습니다. 🙏
IoC는 Inversion of Control을 줄인 표현입니다. 직역하면 제어의 역전이죠. 이 말이 조금 어렵게 들리겠지만 자바를 공부하신 분이라면 충분히 이해할 수 있는 말입니다. 여러분이 지금까지 자바 코드를 작성해 객체를 생성할 때는 객체가 필요한 곳에서 직접 생성했을 겁니다. 다음을 보면 B 객체를 사용하기 위해 클래스 A에서 객체를 직접 생성합니다.
제어의 역전은 다른 객체를 직접 생성하거나 제어하는 것이 아니라 외부에서 관리하는 객체를 가져와 사용하는 것을 말합니다. 이 말도 듣기에는 어렵게 들릴 수 있는데요. 코드를 보면 훨씬 이해가 쉽습니다. 방금 본 위 예제에 제어의 역전을 적용하면 다음과 같이 코드의 형태로 바뀝니다.
이전과는 다르게 클래스 B 객체를 직접 생성하는 것이 아니므로, 어딘가에서 받아와서 사용하고 있다고 추측해볼 수 있죠. 이처럼 제어의 역전을 적용하면 객체를 외부에서 관리하게 되고, 실제로 사용할 때에는 외부에서 제공해주는 객체를 받아오게 되죠. 스프링에서는 제어의 역전 개념을 중요한 컨셉으로 삼고 있으며, 방금까지 설명한 내용에서 외부(=객체를 관리하고 관리하는 주체)를 “스프링 컨테이너”라고 합니다.
앞에서 설명한 것처럼 스프링에서는 객체들을 관리하기 위해 제어의 역전을 사용합니다. 그리고 제어의 역전을 구현하기 위해 사용하는 방법이 DI입니다. 여기서 DI라는 개념이 등장하게 되는데요. DI는 Dependency Injection를 줄인 표현이고, 직역하면 의존성 주입입니다.
DI는 어떤 클래스가 다른 클래스에 의존한다는 뜻입니다. 조금 어려운 표현이라고 생각하겠지만 이것도 코드를 통해보면 매우 쉽습니다. 다음은 IoC/DI를 기초로 하는 스프링 코드입니다.
여기에서 사용하는 @Autowired라는 애너테이션은 스프링 컨테이너에 있는 빈이라는 것을 주입하는 역할을 하는데요. 빈은 쉽게 말해 스프링 컨테이너에서 관리하는 객체를 말합니다. 빈은 바로 다음에 설명할 개념이므로 우선은 이 정도만 이해하고 넘어가도 됩니다. 이전 코드에서는 개발자가 직접 B 객체를 생성했지만 다음 코드는 어딘가에서 B b;라고 선언했을 뿐 직접 객체를 생성하지는 않고 있습니다. 다시 말해 객체를 주입받고 있습니다.
이렇게 코드를 작성해도 프로그램은 잘 동작합니다. 그 이유는 스프링 컨테이너라는 곳에서 객체를 주입했기 때문입니다. 쉽게 말해 스프링 컨테이너가 B 객체를 만들어서 클래스 A에 준 겁니다.
앞서 언급했던 것처럼 스프링은 스프링 컨테이너를 제공합니다. 스프링 컨테이너는 빈을 생성하고 관리합니다. 즉, 빈이 생성되고 소멸되기까지의 생명주기를 스프링 컨테이너가 관리하는 것이죠. 또한 개발자가 @Autowired 같은 애너테이션을 사용해 빈을 주입받을 수 있게 DI를 지원하기도 합니다. 그럼 여기서 빈은 도대체 무엇일까요?
앞에서도 설명했지만 빈은 스프링 컨테이너가 생성하고 관리하는 객체입니다. 바로 앞에서 본 코드에서 @Autowired로 주입받은 B 객체가 바로 빈인데요, 스프링은 빈을 스프링 컨테이너에 등록하기 위해 XML 파일에서 설정하거나, 애너테이션으로 등록하는 등의 여러 방법을 제공합니다. 다시 말해 빈을 등록하는 방법은 여러 가지가 있다는 뜻이죠.
예를 들어 MyBean이라는 클래스에 @Component 애너테이션을 붙이면 MyBean 클래스가 빈으로 등록됩니다. 이후 스프링 컨테이너에서 이 클래스를 관리하죠. 이때 빈의 이름은 클래스 이름의 첫 글자를 소문자로 바꿔 관리합니다. 지금의 경우 ‘myBean’이겠네요.
앞으로 빈이라는 단어가 자주 접하게 될겁니다. 이 때 어렵게 생각할 필요 없이 그냥 ‘스프링의 객체’라고 생각하면 됩니다.
스프링에서 또 하나 중요한 개념으로 AOP가 있습니다. AOP는 Aspect Oriented Programming을 줄인 표현입니다. 직역하면 관점 지향 프로그래밍이죠. 조금 의미를 풀어 설명하자면 프로그래밍에 대한 관심을 핵심 관점, 부가 관점으로 나누어서 관심 기준으로 모듈화하는 것을 의미합니다. 이것도 이해가 조금 어려울 테니 좀 더 쉬운 예를 통해 설명해보겠습니다.
예를 들어 계좌 이체, 고객 관리하는 프로그램이 있을 때 각 프로그램에는 로깅 로직, 즉, 지금까지 벌어진 일을 기록하기 위한 로직과 여러 데이터를 관리하기 위한 데이터베이스 연결 로직이 포함됩니다. 이런 여러 기능중에서도 소프트웨어 시스템의 주요 기능이나 핵심 비즈니스 로직과 관련된 “핵심 관점”과 핵심 관점 이외의 기능이나 시스템 전반에 걸쳐 중복되거나 공통적으로 발생하는 “부가 관점”을 나눌 수 있는데요. 여기에서 핵심 관점은 계좌 이체, 고객 관리 로직이고, 부가 관점은 로깅, 데이터베이스 연결 로직입니다. 실제 프로그램의 기능으로 로직을 정리하면 다음 그림과 같겠네요.
그림을 보면 로깅, 데이터베이스 연결은 모두 계좌 이체와 고객 관리에 필요합니다. 여기에 AOP를 적용하면 부가 관점에 해당하는 로직을 모듈화해 앞에서 본 그림처럼 개발할 수 있게 해줍니다. 다시 말해 부가 관점 코드를 핵심 관점 코드에서 분리할 수 있게 해주죠. 그 결과 프로그래머는 핵심 관점 코드에만 집중할 수 있게 될 뿐만 아니라 프로그램의 변경과 확장에도 유연하게 대응 할 수 있게 됩니다.
마지막으로 알아볼 스프링의 콘셉트는 이식 가능한 서비스 추상화입니다. 이후 이식 가능한 서비스 추상화는 PSA라고 부르겠습니다. PSA는 Portable Service Abstraction을 줄인 표현인데요, 풀어서 설명하자면 스프링에서 제공하는 다양한 기술들을 추상화해 개발자가 쉽게 사용하는 인터페이스를 말합니다.
예를 들어 설명해보겠습니다. 스프링에서 데이터베이스에 접근하기 위한 기술로는 JPA, MyBatis, JDBC 같은 것들이 있는데요. 여기에서 어떤 기술을 사용하든 일관된 방식으로 데이터베이스에 접근하도록 인터페이스를 지원합니다. 또 다른 예시로는 WAS도 PSA의 예시 중 하나라고 볼 수 있는데요. 코드는 그대로 두고 WAS를 톰캣이 아닌 언더토우, 네티와 같은 다른 곳에서 실행해도 기존 코드를 그대로 사용할 수 있으니까요.
지금까지 스프링의 콘셉트인 IoC, DI, AOP, PSA에 알아보았습니다. 이 기술들을 기반으로 스프링이 만들어졌습니다. 스프링 프레임워크는 IoC/DI를 통해 객체 간의 의존 관계를 설정하고, AOP를 통해 핵심 관점과 부가 로직을 분리해 개발하며, PSA를 통해 추상화된 다양한 서비스들을 일관된 방식으로 사용하도록 합니다.
지금까지 공부한 내용을 다음과 같이 정리할 수 있습니다.