스프링?
2000년 초, EJB라는 복잡한 틀에서 벗어나, POJO를 지향하는 "Spring" 이 개발되었다.
스프링 프레임워크 : 스프링의 기본 구조. 객체지향 설계를 잘 할 수 있도록 도와주는 도구
스프링 부트 : 복잡한 스프링 프레임워크 설정을 간단하게 할 수 있도록 도와주는 도구 (실무에서 대부분 사용)
스프링 데이터 : noSQL이던, RDB건간에 기본적인 CRUD는 비슷하기 때문에, 이를 도와주는 도구
스프링 세션 : 세션을 편리하게 쓸 수 있도록 도와주는 도구
스프링 시큐리티 : 보안과 관련된 설정을 쉽게 할 수 있는 스프링 도구
스프링 Rest Docs : API 문서를 편리하게 작성할 수 있도록 도와주는 도구
스프링 배치 : 변경해야하는 DATA가 수천만개일 때, 이를 나누어서 차례차례 업데이트 한다. (실시간 사용성을 보장하기 위해) 이때, 자동으로 업데이트 및 변경 (=배치)하게 도와주는 도구
스프링 클라우드 : 클라우드 환경에서 작업할 수 있도록 도와주는 도구
스프링 vs 스프링부트
스프링 부트는 내장 톰캣 서버가 있어 톰캣을 별도 설치하고 설정할 필요가 없다.
손쉬운 빌드가 가능하도록 starter를 잘 구성하여, 선택만 하면 자동으로 기본 세팅이 완료되도록 설계되었다.
외부 라이브러리를 설치할 경우, 가장 안정적이고 알맞은 버전을 선택하여 최상의 라이브러리 버전을 제공한다.
스프링과 스프링부트는 별도의 것이 아니며, 스프링 프레임워크를 편리하게 사용하도록 만든 것이 부트라고 보면 된다.
스프링 핵심 개념 - 객체지향
EJB가 아닌 스프링에 다들 열광한 이유, "객체지향" 이라는 특징을 굉장히 잘 살린 프레임워크이기 때문이다.
객체지향이란
객체지향이란, 다형성(Polymorphism)을 가진 것을 의미한다. 역할과 구현을 분리한다.
Client와 Server 중 하나가 바뀌어도 다른 것에 영향을 주지 않는 설계 방식이다.
다형성 예시 ) 자동차 종류가 바뀌어도, 제조사가 바뀌어도 운전자는 운전 경험이 있다면 바뀔 필요가 없다.
-> 레고 블럭 조립하듯, 컴퓨터 부품 갈아끼우듯 부분 변경이 가능하다!
-> 역할 : 인터페이스(운전자) , 구현 : 인터페이스를 구현한 객체(자동차)
클라이언트나 서버'만' 있는 객체는 없다.
클라이언트 : 요청하는사람
서버 : 요청에 응답하는 사람
* 클라이언트는 동시에 서버가 될 수 있다. (ex. 클라이언트 -> 클라이언트/서버 -> 서버 )
인터페이스란
동일한 기능을 가진 것들을 '정형화'하기 위해 강제하는 것이라고 볼 수 있다.
동일한 목적을 가진 객체를 아무 규칙 없이 생성한다면, 객체를 수정하거나 제어할 때 문제가 생기기 때문이다.
학생 인터페이스라면 이름, 학번, 과, 듣는 학점, 생년월일 등을 지정하는 것을 말한다.
이렇게 인터페이스로 정형화하여 객체를 생성하게 된다면, 유지보수성이 굉장히 올라가게 된다.
그러므로, 인터페이스를 유지보수하기 좋도록 안정적으로 설계하는 것이 객체지향 설계에서 가장 중요하다.
다형성이란
다형성의 본질 : 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다.
클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
스프링의 제어의 역전(Ioc), 의존관계 주입(DI)은 다형성을 활용해, 역할과 구현을 편리하게 다룰 수 있도록 지원한다.
객체지향 설계 5원칙 (SOLID)
- SRP: 단일 책임 원칙(single responsibility principle)
- OCP: 개방-폐쇄 원칙 (Open/closed principle)
- LSP: 리스코프 치환 원칙 (Liskov substitution principle)
- ISP: 인터페이스 분리 원칙 (Interface segregation principle)
- DIP: 의존관계 역전 원칙 (Dependency inversion principle)
SRP 단일 책임 원칙 (single responsibility principle)
- 하나의 클래스는 하나의 책임만 가져야 한다.
- 책임의 중요한 판단 기준은 변경이다.
- 변경이 있을 때 파급 효과가 적으면 적을수록 좋다.(UI 하나 변경했는데, 애플리케이션을 다 고쳐야 한다면? 잘못된 설계이다. )
OCP 개방-폐쇄 원칙 (Open/closed principle)
- 가장 중요한 원칙이다!
- 소프트웨어 요소에는 확장에는 열려 있으나, 변경에는 닫혀 있어야 한다.
- 다형성을 활용하여 코드 변경 없이 기능을 추가한다.
- 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현한다.
- 다형성을 사용하고, OCP 원칙을 지키기 위해서는 객체를 생성하고 연관 관계를 맺어주는 별도의 설정자가 필요하다. =Spring
LSP: 리스코프 치환 원칙 (Liskov substitution principle)
- 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
- 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다는 것, 다형성을 지원하기 위한 원칙, 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙이 필요하다.
- 단순히 컴파일에 성공하는 것이 개발의 완성이 아니라, 정확한 기능을 개발해야 한다.
ISP: 인터페이스 분리 원칙 (Interface segregation principle)
- 기능을 여러개로 쪼갠 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
- 예) 자동차 인터페이스가 존재한다고 가정해 보자.
- 자동차 인터페이스를 운전 인터페이스, 정비 인터페이스로 두 개로 분리하는 것이 더 낫다.
- 두 개로 분리할 경우, 사용자 클라이언트를 운전자 클라이언트, 정비사 클라이언트로 분리할 수 있다.
- 만약 정비와 관련된 기능을 바꿔야 해서 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않는다.
- 인터페이스의 기능이 명확해지고 대체 가능성이 높아진다.
DIP: 의존관계 역전 원칙 (Dependency inversion principle)
- 중요한 원칙!
- 프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안 된다.” 의존성 주입은 이 원칙을 따르는 방법 중 하나다.
- 쉽게 말해 클라이언트 코드가 구현 클래스에 의존하지 말고, 인터페이스에 의존해야 한다는 뜻이다. (MemberService가 MemberRepository 인터페이스에만 의존해야 한다.)
- 예) 운전자는 자동차 역할에 대해서 알아야 한다.
- 역할(Role)에 의존하게 해야 한다는 것과 같은 의미이다.
- 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다.
- 만약 클라이언트가 구현체에 의존하게 된다면 변경이 아주 어려워진다.
객체지향의 핵심은 다형성이나, 다형성만으로는 OCP,DIP 지키기가 어렵다.
이를 위해 스프링이 개발 한 것이 DI(Dependency Injection) : 의존성 주입
스프링 특징과 개발에 유의할 점
- 역할과 구현을 분리하여 생각해야한다.
- 기능 확장은 용이해야하지만, 변경은 어려워야한다.
- 다형성은 가장 중요한 개념이며, 의존성주입으로 다형성만으로는 지키기 어려운 SOLID원칙을 지킬 수 있다.
- 인터페이스는 자세하게 쪼개면 쪼갤수록 좋다. (하지만 현실적으로 추상화 비용 존재)
- 기능 확장할 가능성이 없을 경우엔 구체 클래스를 사용한다.