[Spring] 객체 지향 설계를 극대화한 스프링의 핵심 개념 정리
Index
📌 스프링 탄생 배경
- EJB의 한계
- 스프링의 등장
📌 스프링의 핵심
📌 객체 지향 5가지 원칙과 스프링
- 스프링 IoC와 DI
- 스프링은 객체지향 원칙을 잘 준수해요
📌 정리
📌 1. 스프링의 탄생 배경
1-1. EJB 한계
지금은 알 필요가 없는 EJB읜 한계를 극복하기 위해 로드 존슨 형님이 스프링을 제안하였고 이를 개발했습니다. (유겐 휠러 형님이 사실상 스프링 대부분의 코드 지분을 가지고 있음)
EJB(Enterprise Java Beans)에 대해서 본인은 이쪽 세대도 아니었고 한 번도 사용해본 적이 없었기 때문에 EJB 개념조차 제대로 알지 못합니다. 하지만 스프링이 왜 태어났는지에 대해서 찾아보거나 강의를 듣게 되면 EJB는 빠짐없이 등장합니다. 대략적으로 스프링이 탄생하기까지의 과정을 정리해보겠습니다.
옛날(?)에 Java로 애플리케이션을 개발할 때 EJB 스펙이 널리 보급되던 시절이 있었다고 합니다. EJB로 개발할 때 가장 큰 문제점이 배보다 배꼽이 더 커진다는 것이었는데요. 쉽게 말하자면 나는 고객의 요구대로 어떤 로직을 개발하고 적용하고 싶은데 로직 개발하는 것보다 그 외의 요소들(환경적인 요소들), 설정해야 되는 것들이 대부분의 비중을 차지했습니다.
다른 단점도 있는데요, 아마 이게 가장 치명적이지 않나 싶습니다. 코드를 짤 때에 EJB 인터페이스에 의존해서 개발을 했기 때문에 EJB에 종속적인 코드가 될 수 밖에 없다는 단점이 있습니다. 그래서 특정 기술, 스펙에 의존적으로 개발할 수 밖에 없기에 유연한 개발은 애초에 불가능했다고 합니다. 그런 측면에서 EJB는 객체지향적이지 않다고 할 수 있습니다.
그래서 EJB에 얽매인 코드에서 순수한 Java 코드로 돌아가자 해서 POJO(Plain Old Java Object) 개념이 등장하게 됩니다.
옛날 Java가 그립다! 복잡한 EJB에서 벗어나서 순수한 Java로 돌아가자!
1-2. 스프링의 등장
로드 존슨 형님이 EJB 못 써먹겠다, 여기서 탈피하자고 선언하면서 책을 출간하는데(그게 바로 전설적인 J2EE Development Without EJB) 이것이 바로 스프링의 모태가 됩니다. 로드 존슨 형님이 주목했던 것은 EJB없이 개발하면서 확장가능한, 유연한 개발 방법론이었습니다. 이 내용이 유명해지면서 다양하게 확장되는데 지금의 스프링으로 이어지게 된다고 보시면 됩니다.
http://www.yes24.com/Product/Goods/798826(전설의 그 책 참고)
📌 2. 스프링의 핵심
스프링의 핵심은 자바 언어 기반의 OOP(객체 지향 프로그래밍)의 강점을 극대화한 프레임워크입니다. 즉 EJB 녀석이 할 수 없었던 객체 지향 프로그래밍 방식으로 개발할 수 있게 스프링이 개발 방법의 틀을 만들어준 것입니다.
그럼 객체 지향 프로그래밍이란 무엇인지 알아야 하는데 이 내용은 예전에 제가 작성했던 게시글을 참고하시면 될 것 같습니다.
https://beaniejoy.tistory.com/47
(나의 영원한 마스터, 구글에게 "객체지향 프로그래밍이란" 검색하셔도 됩니다.)
객체지향 프로그래밍 특징에는 네 가지가 있는데 캡슐화, 추상화, 상속, 다형성이 있습니다. 그 중에 스프링에서 가장 중요한 객체 지향 특징은 다형성(Polymorphism)입니다.
다형성은 인터페이스와 구현체를 생각하시면 됩니다. 인터페이스. 그냥 궁금해서 사전적 정의를 찾아봤는데요.
서로 다른 두 장치 혹은 사용자와 장치 사이에 이어주는 부분이라고 정의되어 있습니다. "연결"에 초점을 맞추면 될 것 같습니다. 실제 세계에서 예시를 들면, 우리가 살아가면서 어떤 장치를 사용하는데 대부분 그 작동 원리를 몰라도 기능을 잘 사용합니다. 자동차를 가장 많이 예시로 드는데요. 우리가 자동차의 엑셀, 브레이크, 기어 변경 등 여러 기능들이 어떻게 작동하는지 몰라도 해당 기능들을 잘 사용할 수 있습니다. (혹여나 차종이 바뀌어도 잘 사용합니다.)
public interface Car {
void accelerate();
void stop();
//...
}
public class BenZGClass implements Car { /*...*/ }
public class VolvoXC implements Car { /*...*/ }
저의 드림카인 벤츠 G class든 볼보 XC든 저는 엑셀과 브레이크 기능을 이용하기만 하면 됩니다. 여기서 확장 가능한 프로그래밍을 가능하게 해줍니다. 스프링은 OOP의 특징, 특히 다형성을 잘 활용해서 확장 가능하고 유연한 프로그래밍을 가능하도록 설계되어 있습니다.
public class CarService {
private CarRepository carRepository = new JdbcCarRepository();
// private CarRepository carRepository = new MyBatisCarRepository();
// private CarRepository carRepository = new JpaCarRepository();
}
사용자 입장에서 어떤 DB 접근 기술을 사용할 것인지 유연하게 선택할 수 있습니다. 인터페이스 참조가 해당 기술 구현체를 받아서 로직을 수행할 것이기 때문에 구현체만 잘 주입시키면 됩니다.
(심지어 스프링에서 DI를 사용하면 CarService에서 코드 변경 지점이 하나도 없이 DB 접근기술을 유연하게 변경할 수 있습니다.)
📌 3. 객체 지향 5가지 원칙과 스프링
SOLID라 불리는 객체 지향의 5가지 원칙이 있습니다.
- SRP - Single Responsibility Principle, 단일 책임 원칙
- OCP - Open Closed Principle, 개방 폐쇄 원칙
- LSP - Liskov Substitution Principle, 리스코프 치환 원칙
- ISP - Interface Segregation Principle, 인터페이스 분리 원칙
- DIP - Dependency Inversion Principle, 의존 역전 원칙
주니어 기술 면접 단골 질문인데요. 이부분은 따로 공부 정리할겸 게시글을 작성해보도록 하겠습니다.
(이것도 구글 형님한테 "객체 지향 5가지 원칙"이란 검색하시면 됩니다.)
스프링은 IoC, DI 개념을 통해 위의 원칙들을 가능하게 해줍니다.
3-1. 스프링 IoC와 DI
IoC는 IOC 위원이 아닌 Inversion of Control, 즉 제어의 역전이라고 직역할 수 있습니다. 스프링은 애플리케이션을 처음 실행할 때 자바 설정 파일, xml 설정파일, 혹은 어노테이션을 통해 정의한 Bean 객체들을 생성하고 특정 공간에 저장합니다. 이것이 바로 스프링 빈(Spring Bean)입니다.
쉽게 말해서 어떤 설계 도면이 있는데 그 설계 도면의 재료가 바로 스프링 빈이 된다고 보시면 됩니다. 객체 지향 프로그래밍에서의 재료는 객체이고 객체단위로 움직이기 때문에 재료가 될 클래스를 도면에 적고 스프링이 이 도면을 보고 재료들을 객체로 생성하고 가지고 있게 됩니다.
여기서 눈치 채셨겠지만 스프링이 도면을 보고 직접 객체 생성한다고 되어 있는데 여기서 IoC 개념이 나오게 된다고 보시면 됩니다.
public class Example {
public String run() {
// 사용자가 직접 객체를 생성
Source source = new ConcreteSource();
Instrument instrument = new Hammer();
instrument.process(source);
//...
}
}
자바 코드로 개발할 때 사용자가 직접 객체를 생성하고 그 객체를 가지고 조리하는데 이는 사용자에게 제어권이 있다고 볼 수 있습니다. 하지만 객체 지향 원칙에서 단일 책임 원칙이 있는데 하나의 클래스 안에 메소드 로직뿐만 아니라 객체 생성이라는 역할까지 수행한다는 점에서 단일 책임 원칙을 위반한다고 볼 수 있습니다.
public class Example {
// 아래 설정파일에서 스프링이 생성해준 빈 객체를 가져다 사용하기만 하면된다.
@Autowired
private Source source;
@Autowired
private Instrument instrument;
public String run() {
instrument.process(source);
//...
}
}
@Configuration
public class Config {
@Bean
public Source source() {
return new ConcreteSource();
}
@Bean
public Instrument instrument() {
return new Hammer();
}
}
스프링은 사용자가 직접 했던 객체 생성 역할을 대신 수행해줍니다. 설정파일에 설계도면의 재료가 될 Bean들을 정의만 해주면 스프링은 source, instrument 구현 객체를 대신 생성해서 따로 저장해둡니다. 객체 생성 역할을 따로 분리했다는 점에서 단일 책임 원칙을 준수한다고 볼 수 있습니다.
사용자한테 제어권이 있었던 것이 제 3자한테 넘어갔다 해서 제어의 역전(Inversion of Control)이라고 부르기도 합니다.
(왜 역전이라는 표현을 썼는지는 모르겠지만 원래 우리가 가지고 있던 제어권이 뒤집어져서 다른 사람에게 넘어갔으니 역전됐다는 표현을 사용한 것 같네요,,,)
참고로 객체 생성한 스프링 빈들을 특정 공간에 저장해둔다고 했는데 이 특정 공간을 가리켜 IoC 컨테이너(스프링 컨테이너)라고 부릅니다.
그럼 DI는 뭘까요?
DI는 Dependency Injection, 의존성 주입이라고 하는데요. 의존성이라는 개념이 무언가 어디에 의존하고 있다라고 생각이 드는데 맞습니다. DI는 위의 예시에서도 찾아 볼 수 있습니다.
public class Example {
@Autowired
private Source source;
@Autowired
private Instrument instrument;
//...
}
Example이라는 클래스는 Source, Instrument를 가져다 로직에 사용하고 있습니다. 즉 Example 클래스 혼자서는 아무것도 할 수 없고 Source, Instrument가 있어야만 제대로된 로직을 수행할 수 있습니다. 이런 점에서 혼자서 수행할 수 없고 다른 것에 의존적이라고 표현할 수 있고 "Example은 Source, Intrument에 의존한다" 라고 표현합니다.
스프링은 어떤 설계 도면을 읽어서 재료들을 적절히 사용한다고 되어있는데 설계 도면은 바로 의존성을 참고합니다. 스프링은 빈 대상으로 설정한 클래스 전부 스캔해서 해당 클래스가 의존하고 있는 클래스들을 싹다 알아냅니다. 앞서 스프링은 스프링 빈 객체를 미리 생성해서 스프링 컨테이너에 저장해둔다고 했는데, 생성해둔 빈 객체들을 의존하고 있는 클래스에 스프링이 알아서 주입시켜줍니다. 이를 DI, 의존성 주입이라고 보시면 됩니다.
3-2. 스프링은 객체지향 원칙을 잘 준수해요
스프링은 IoC, DI 개념을 통해 위의 원칙들을 가능하게 해줍니다. 몇 개만 보자면 확장에는 열려있고 변경에 대해서는 닫혀 있어야 한다는 OCP를 스프링은 DI를 통해 준수하고 있습니다.
public class Example {
@Autowired
private Source source;
@Autowired
private Instrument instrument;
//...
}
@Configuration
public class Config {
//...
@Bean
public Instrument instrument() {
return new Sledgehammer();
}
}
Instrument를 망치에서 오함마로 구현체를 변경해야 할 때 Example 클래스는 코드를 변경할 필요가 없습니다. 즉 변경은 하지 않으면서 다른 Instrument 구현체로 확장해서 교체할 수 있다는 점에서 OCP(개방 폐쇄 원칙)를 아주 잘 수행하고 있습니다.
또한 위에서 언급했듯이 스프링은 SRP(단일 책임 원칙)도 준수하고 있음을 알 수 있었고,
Example 클래스는 Source, Instrument 인터페이스에 의존하고 있다는 점에서 구체적인 것이 아닌 추상화된 것에 의존한다는 원칙인 DIP(의존 역전 원칙)도 준수하고 있다고 볼 수 있습니다.
📌 정리
스프링은 자바 기반의 객체 지향 프로그래밍의 꽃이라고 많이들 부릅니다. 객체 지향 프로그래밍의 특징을 극대화하고 객체 지향 원칙들을 준수하면서 개발하게끔 해준다는 점에서 그렇게 부르는 것 같습니다. EJB의 기나긴 겨울이 지나 따뜻한 봄이 왔다해서 스프링이라고 이름을 지었다고 하던데, 저는 스프링 부트를 사용하면서 진짜 이게 봄이라는 생각을 혼자 했던 것 같습니다.
자바라는 것이 옛날에는 딱딱하고 무겁고 어렵게만 여겨졌는데 스프링이 나오고 스프링 부트가 나오면서 개발하는 사용자 측면에서 가볍고 쉽게 애플리케이션을 개발할 수 있게 되었고 우리나라는 특히 스프링을 아주 많이 사용하는 듯 보입니다.
스프링은 객체 지향의 막강한 파워를 기반으로 해서 넓디 넓은 스프링만의 생태계를 구축했고 지금도 오픈소스로 계속해서 기능을 확장하고 있습니다. 스프링의 넓은 생태계에 같이 동참하면 좋을 것 같네요.
스프링 개발자로서 기저에 숨겨져있는 객체 지향 프로그래밍에 대해서 잘 알아야겠다고 생각해서 강의도 듣고 책도 읽으며 블로그로 한 번 정리해보며 다시 기억하는 시간을 가져보았네요. 틀린 부분 있으면 언제든 코멘트 부탁드리겠습니다. 감사합니다.
'Spring' 카테고리의 다른 글
[Spring Core #2] 스프링 컨테이너와 스프링 빈 (스프링 핵심 원리 강의정리) (0) | 2022.07.12 |
---|---|
[Spring Core #1] 스프링의 객체 지향 원리 적용 (스프링 핵심 원리 강의정리) (0) | 2022.07.01 |
[Spring] 2. jackson을 이용한 data binding 이해하기(생성자, constructor) (7) | 2022.04.26 |
[Spring] 1. jackson을 이용한 data binding 이해하기(ObjectMapper, field & getter/setter) (2) | 2022.04.01 |
Spring Batch @Scope의 Thread Safe한 특징 (0) | 2022.03.04 |
댓글
댓글(Github)
다른 글
-
[Spring Core #2] 스프링 컨테이너와 스프링 빈 (스프링 핵심 원리 강의정리)
[Spring Core #2] 스프링 컨테이너와 스프링 빈 (스프링 핵심 원리 강의정리)
2022.07.12 -
[Spring Core #1] 스프링의 객체 지향 원리 적용 (스프링 핵심 원리 강의정리)
[Spring Core #1] 스프링의 객체 지향 원리 적용 (스프링 핵심 원리 강의정리)
2022.07.01 -
[Spring] 2. jackson을 이용한 data binding 이해하기(생성자, constructor)
[Spring] 2. jackson을 이용한 data binding 이해하기(생성자, constructor)
2022.04.26 -
[Spring] 1. jackson을 이용한 data binding 이해하기(ObjectMapper, field & getter/setter)
[Spring] 1. jackson을 이용한 data binding 이해하기(ObjectMapper, field & getter/setter)
2022.04.01