글의 목적
김영한님의 스프링 핵심 원리 - 기본편의 섹션 1 수업을 듣고 공부한 스프링의 탄생 배경과 필요성을 스스로 정리해보고 이해하는 것!
본문 한줄 요약
스프링은 EJB의 한계를 극복하고 POJO 즉, 순수한 자바 형태의 객체 지향 설계를 지원하기 위해 탄생한 프레임워크이다.
본문
Chapter 1. 자바 진영의 추운 겨울
때는 바야흐로 2000년대 초반, 자바 표준 모델로서의 EJB(Enterprise Java Bean)가 자바의 생태계를 군림하던 때였다. 자바 진영의 개발자들은 어렵고 복잡한 EJB로 인해 크고 작은 난항을 겪곤 했다. EJB가 어깨를 펼치고 자바 생태계를 활보할 때마다, 개발자들의 야근 마일리지도 함께 쌓여 갔다. 개발자들은 끝도 모르고 몰려오는 야근에, 그들에게 허락된 유일한 낙인 커피로 하루하루를 근근이 버틸 따름이었다.
2000년대 초반, 자바 생태계는 EJB라는 거대한 폭군이 드리운 그늘 속에서 아주 추운 겨울을 견디고 있었다.
EJB(Enterprise Java Bean)
스프링, JPA 등등의 기능들이 모두 다 합쳐진 일종의 '자바 종합선물세트'이다. 자바 표준 기술이었기 때문에 광범위하게 보급이 될 수 있었고 인스턴스 풀링, 설정에 의한 트랜잭션 처리 등의 고급기술들을 지원하였기 때문에 금융권 같은 분야에서 많이 쓰였다. 그러나 프레임워크가 너무나도 복잡하고 난해했고, 심지어 무겁고 느리기까지 했기 때문에 시간이 지남에 따라 많은 개발자들이 외면하는 기술이 되어버렸다.
Chapter 2. 봄이 오다
자바 진영에 혁명의 불씨가 오른 것은 2000년 9월이었다. 마틴 파울러(Martin Fowler)가 그와 동료들의 컨퍼런스를 준비하다 탄생한 POJO(Plain Old Java Object)라는 용어는 단어의 뜻 그대로 '순수하고 오래된 자바 오브젝트'를 지향한다는 의미를 가지고 있었는데, 이는 특히 당대에 아성을 떨치던 EJB를 겨냥하며 자바가 특정 프레임워크에 종속되는 패턴을 극복하고 순수한 본래 형태로 돌아가야 한다는 일종의 개혁적인 메세지를 담고 있었다.
그렇게 자바의 표준 모델 EJB의 견고한 영향력 아래, POJO는 조금씩 개발자들 사이에서 공감대를 얻으며 확산되기 시작하였다. 그리고 마침내, 자바 진영에 새로운 봄을 알리는 두 가지 사건이 발생하게 되니 그것은 바로 하이버네이트와 훗날 스프링이라는 이름으로 불리게 될 로드 존슨의 코드의 탄생이었다.
하이버네이트 그리고 게빈 킹(Gavin King)
현재는 레드햇에서 프로그래밍 모델, 프레임워크, 플랫폼 구축 등을 이끌고 있는 게빈 킹은 당시 EJB에서 제공하는 Entity Bean에 많은 문제점이 있다는 생각을 하고 있었다. 그는 이에 하이버네이트(Hibernate)라는 새로운 기술을 만들게 되었고 이는 EJB의 Entity Bean을 대체할 수 있던 기술이 되었다. 당시 EJB Entity Bean의 문제점에 시름하고 있던 개발자들이 많았던 터라 하이버네이트는 배포 이후 꾸준히 점유율을 높일 수 있었고 결국 자바 표준 논의 기관은 게빈 킹을 스카웃하여 JPA라는 기능을 만들어 자바 표준으로 삼게 된다. JPA는 하이버네이트를 계승한 기술이다.
로드 존슨(Rod Johnson)과 그의 코드
로드 존슨은 그의 책에서 EJB의 문제점을 지적하는 한편 EJB 없이 순수한 자바만으로도 좋은 객체지향 어플리케이션을 개발할 수 있다는 것을 보여주었다. 그는 이를 위해 3만여 줄의 코드를 세상에 공개하였는데, 이 코드는 사실상 현재 스프링의 축을 이루는 핵심 기능의 모체가 되었다. 이후, 유겐 휠러(Juergen Hoeller)와 얀 카로프(Yann Caroff)가 로드 존슨에게 이 책을 기반으로 한 오픈소스 프로젝트를 제안하게 되면서 스프링 프레임워크가 탄생하게 된다.
Chpater 3. 새로운 시대가 열리다 : 스프링의 시대
스프링은 매우 거대하다. 당초에 스프링을 개발한 개발자들이 스프링이라는 단어는 모호한 단어다, 라고 표현했을 만큼 스프링은 정말로 거대하고 복잡하다. 스프링은 일반적으로 3가지의 표현 중 하나로 사용되는데 그는 아래와 같다.
- 스프링 DI 컨테이너 기술
- 스프링 프레임워크
- 스프링 생태계
실제로 스프링 공식 홈페이지에 방문해보면 스프링 안에 정말 많은 프로젝트가 포함되어 있다는 것을 발견할 수 있다. 위의 용례처럼 스프링은 단순히 스프링 프레임워크를 표현하기도 하지만 스프링 부트, 스프링 데이터, 스프링 세션, 스프링 시큐러티 등 스프링을 구성하는 수많은 생태계를 의미하기도 하는 것이다.
그렇다면 이렇게 모호한 스프링을 정의하거나 표현할 수 있는 핵심적인 개념은 무엇일까. 왜 스프링은 2000년대 초반에 아성을 펼치던 골리앗 EJB를 재치고 자바 진영의 거인이 될 수 있었을까.
앞서 Chapter 2에서 POJO의 흐름속에서 스프링이 탄생했다는 이야기를 하였다. POJO는 순수한 자바로 돌아가자는 운동이었고, 스프링은 그 기반 위에서 탄생했다. 즉, 스프링은 자바 언어 기반의 프레임워크이다. 위의 질문에 대한 답은 이것이다. 스프링은 자바 언어 기반의 프레임워크이다는 것.
자바 언어는 객체 지향 언어이다. 자바를 공부할 때 배울 수 있는 다형성, 캡슐화, 추상화, 상속성 같은 속성들 말이다. 스프링은 객체 지향 언어로서의 자바 언어의 특징을 잘 살려줄 수 있는 프레임워크다. 즉 POJO의 흐름대로 순수한 자바의 모양대로 프로그래밍을 할 수 있도록 도와주는 툴이라는 의미이다.
Chapter 4. 객체 지향? 그게 왜 중요해? 그리고 스프링이랑 그게 뭐가 어떻게 연관되는데?
객체 지향 프로그래밍의 핵심은 코드의 부분 부분을 추출하여 모듈화할 수 있다는 데 있다. 가령 레고 블록을 맞춰 끼우는 것처럼 특정 로직들을 쪼개서 모듈화한 후 나중에 조립을 하는 식이다. 따라서 객체 지향의 프로그래밍은 코드와 코드가 서로 의존적이거나 종속적이지 않게 설계할 수 있는데 이는 다수의 개발자들이 하나의 프로젝트에 참여해 대규모의 작업을 하기 용이하게 해준다. 사전에 역할 설정을 제대로 한다는 조건이 있다면 개발자들이 각각의 임무에 따라 개발을 할 때, 다른 개발자들의 코드에 영향을 받지 않기 때문이다.
한 가지 예를 들어볼까. 여기 로미오와 줄리엣의 공연을 기획하는 기획자가 있다. 배우들을 섭외했는데 배우들의 스케줄이 모두 달라 공연마다 남자 주인공과 여자 주인공이 바뀌는 상황이 발생한다. 이 상황이 공연에 영향을 줄까? 정상적인 경우라면 영향을 주지 않는다. 주인공을 연기하는 배우들은 '주인공을 연기하는 배우'이지 그 자체가 주인공이 아니기 때문이다. 배우들이 대본을 철저히 숙지하고, 대본에 의해 극을 꾸리고 움직인다면 공연마다 상대 주인공이 바뀌는 것은 문제가 되지 않을 것이다.
위의 예는 '주인공'이라는 역할과 '연기를 하는 배우'라는 역할을 구현하는 대상을 나눠 배우가 바뀌는 상황이 공연에 영향을 주지 않도록 유도한 것으로 이를 프로그래밍에 대입하면 인터페이스와 인터페이스의 구현체를 나눈 다형성을 실현한 것이 된다. 맞다, 객체 지향의 특징에 나오는 바로 그 다형성! 스프링은 의존성 주입(DI), 제어권 역전(IoC)라는 기능들을 활용해 다형성을 살린 프로그래밍을 도울 수 있다. 이게 스프링이 유명해진 핵심적인 이유이다.
Chapter 5. 다 좋아, 다 좋은데. 그거 스프링 없으면 못해? 왜 굳이 스프링이야?
가장 중요한 것은 여기에서 시작된다. 당초에 POJO는 곧 '순수한 자바'를 추구하는 운동이었는데 EJB를 밀어내고 새로운 프레임워크가 도입되었다. 이건 마치 범을 몰아내고 새로운 초원의 지배자 사자가 등장한 격이다. 도대체 EJB와 스프링이 다른 점이 무엇인가?
사실 좋은 객체 지향을 위해서는 지켜져야 하는 원칙 5가지가 있다. 이는 로버트 마틴(Robert Martin)이 그의 저서 'Clean Code'에서 제시한 원칙으로 이후 마이클 페더스가 그 원칙들의 앞 글자를 따 현재는 SOLID라고 불리고 있다. 각각의 요소들을 하나하나 살펴보자.
SRP(Single Responsible Principle, 단일 책임 원칙)
- 한 클래스는 단 하나의 책임만을 가져야 한다.
OCP(Open Closed Principle, 개방 폐쇄 원칙)
- 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
- 자신의 확장에는 열려 있고, 주변의 변화에는 닫혀 있어야 한다는 뜻
- 앞서 설명한 연극의 예처럼 하나의 요소를 변경할 때 다른 요소가 영향을 받아서는 안됨.
LSP(Liskov Substitution Principle, 리스코프 치환 원칙)
- 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 교체가 가능해야 한다.
- 인터페이스와 클래스의 관계, 상위 클래스와 하위 클래스의 관계가 논리적인가?
- ex) '앞으로가즈아'라는 인터페이스를 구현했는데 기능이 뒤로 가는 기능이라면? 원칙 위배
ISP(Interface Segregation Principle, 인터페이스 분리 원칙)
- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- SRP(단일 책임 원칙)처럼 인터페이스도 하나의 책임만을 주어라.
- 어떤 클래스를 구현할 때 각 상황에 맞는 기능만 제공하도록 해라.
DIP(Dependency Inversion Principle, 의존관계 역전 원칙)
- 추상화된 것은 구체적인 것에 의존하면 안된다. 구체적인 것이 추상화된 것에 의존해야 한다.
- 상위에는 가장 추상적인 것(변화에 민감하지 않은 것, 수정 가능성이 낮은 것)을 배치하라.
- 하위에 구체적인 것(변화에 민감한 것, 수정 가능성이 높은 것)을 배치하라.
- OCP(개방 폐쇄 원칙)과 유사한 원칙
이렇게 객체 지향 설계를 위해 지켜야 하는 5가지의 원칙이 있다. 문제는 순수한 자바로는 위의 5가지 원칙을 모두 준수할 수 없다는 것. 구체적으로는 OCP와 DIP를 지킬 수 없다.
생각해보자, 자바는 다형성을 구현할 때 아래와 같은 문법을 많이 사용한다.
- 인터페이스 (객체) = new 클래스();
- 상위클래스 (객체) = new 하위클래스();
위의 코드를 보면 인터페이스(혹은 상위클래스)가 클래스(혹은 하위클래스)에 의존한다는 사실을 알 수 있다. 인터페이스를 데이터타입으로 갖는 객체는 클래스가 변화할 때 영향을 받는다.
2번 원칙 개방 폐쇄 원칙에 의하면 한 개의 소프트웨어 요소는 다른 소프트웨어 요소의 변화에 영향을 받아서는 안된다고 되어 있다. 그러나 만약 클래스를 폐기하고 다른 구체적인 클래스를 구현하는 상황이 발생한다면 위 코드의 수정이 불가피하다. 2번 원칙 OCP를 위배한 것이다.
또한 위 코드는 추상적인 인터페이스가 구체적인 클래스에 의존하고 있다. 클래스가 변화하면 인터페이스 데이터타입을 갖는 객체도 변화하기 때문이다. 5번 원칙 DIP를 위배한 것이다.
스프링은 DI와 IoC 등을 통해 자바적인 코드를 짜면서도 위의 원칙들을 위배하지 않을 수 있도록 도와준다. 그렇기 때문에 스프링을 쓰는 것이고, 이는 스프링이 EJB와는 달리 POJO의 기조를 철저히 따라가고 있음을 보여주는 예시이다.
'JAVA' 카테고리의 다른 글
JVM의 Garbage Collector 동작 원리 (0) | 2022.02.14 |
---|---|
[Java] Array와 ArrayList의 차이점 (0) | 2022.02.11 |
컬렉션 프레임워크(collection framework) 상속 관계 도표 사이트 (0) | 2021.09.23 |
JAVA 문법 : 다형성(Polymorphism) (0) | 2021.09.15 |
JAVA 문법 : 클래스와 객체 (0) | 2021.09.15 |