본문은 김영한님의 스프링 핵심원리 - 기본편의 내용을 담고있습니다.
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., 스프링 핵심 원리를 이해하고, 성장하는 백엔드 개발자가 되어보세요! 📢
www.inflearn.com
스프링이란 ?
자바계열 표준기술 EJB(Enterprise Java Beans)
이론상 좋은데, 개발자들이 죽어남. 너무 복잡하고 쓰기 어렵기까지 한데, 느림..
스프링 생태계
여러가지 기술들의 모음
필수적인 스프링 프레임워크 / 스프링 부트
선택적으로 스프링 데이터 / 세션 / 시큐리티 / Rest Docs / 배치 / 클라우드 등등..
결국 가장 중요한건 스프링 프레임워크
핵심기술 : 스프링 DI 컨테이너, AOP, 이벤트, 기타 ( 강의 타겟 내용 )
웹기술 : 스프링 MVC, 스프링 WebFlux
데이터 접근 기술 : 트랜잭션, JDBC, ORM 기술, XML지원
기술 통합 : 캐시, 이메일, 원격접근, 스케쥴링
테스트 : 스프링 기반 테스트 지원
언어 : 코틀린, 그루비 등등..
스프링 부트
- 스프링을 편리하게 사용할 수 있도록 지원, 최근에는 기본적으로 사용함.
- Tomcat 같은 웹 서버를 내장해서 별도의 웹 서버 설치가 필요하지 않음.
- 손쉬운 빌드 구성을 위한 starter 종속성 제공
- 스프링과 3rd parth(외부) 라이브러리 자동 구성 : 라이브러리 버전 호환성 테스트 되어있음.
- 메트릭, 상태확인, 외부 구성 같은 프로덕션 준비 기능 제공
- 관례에 의한 간결한 설정 ( 설정이 진짜 힘들었음. default 설정이 있음 : 필요하면 커스터마이징 )
스프링 단어 ?
: 문맥에 따라 다르게 사용됨
- 스프링 DI 컨테이너 기술 ( bean관리 등등.. )
- 스프링 프레임워크
- 스프링 부트, 스프링 프레임 워크 등을 모두 포함한 스프링 생태계
스프링은 왜 만들었나요 ?
핵심 개념
이 기술을 왜 만들었는가 ?
이 기술의 핵심 컨셉은?
스프링의 핵심 개념, 컨셉?
- 자바 언어 기반의 프레임 워크
- 자바 언어의 가장 큰 특징 = 객체 지향 언어
- 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크
- 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크
좋은 객체 지향 프로그래밍이란?
객체 지향 프로그래밍
: 객체들의 모임 , 각각의 객체 협력함. 유연하고 변경이 용이
유연하고 변경이 용이하다 ?
- 레고블럭 조립하듯
- 키보드, 마우스 갈아 끼우듯이
- 컴퓨터 부품 갈아 끼우듯이
다형성 ( Polymorphism )
실세계와 비유하기에 용이하지는 않지만 그래도 이해해보자.
역할과 구현으로 세상을 구분해보자.
운전자 - 자동차 ( K3 / 아반떼 / 테슬라 모델3 )
운전자는 자동차가 바뀌어도 운전 할 수 있음.
자동차의 역할은 존재하고 자동차의 구현이 바뀌었다고 해서 문제가 생기지 않음
운전자는 자동차의 인터페이스만 알고 있으면 됨. ( 내부 구조를 몰라도 된다 )
완전히 새로운 자동차가 나와도 자동차 역할만 그대로면 무한히 확장 가능하다는 의미.
클라이언트에 영향을 주지 않고 새로운 기능을 추가할 수 있음을 의미.
공연무대 (로미오와 줄리엣)
로미오 역할 / 줄리엣 역할이 있는 거고 배우는 누가 들어와도 상관없는것임.
역할을 수행할 대본만 충실하면 배우는 대체 가능함.
이런것이 바로 유연하고, 변경 용이하다는 점임.
역할과 구현으로 분리하면..
세상이 단순해지고, 유연해지고 , 변경도 편리해진다.
- 클라이언트는 대상의 역할(인터페이스)만 알면된다.
- 구현 대상의 내부 구조를 몰라도 된다.
- 내부 구조가 변경되어도 영향을 받지 않는다.
- 대상 자체를 변경해도 영향을 받지 않는다.
자바 언어에서는 다형성을 활용
- 역할 = 인터페이스
- 구현 = 인터페이스를 구현한 클래스, 구현 객체
객체를 설계할 때 역할과 구현을 명확히 분리
인터페이스를 먼저 부여하고, 역할을 수행하는 구현 객체를 만드는 것이 중요.
객체의 협력이라는 관계부터 생각
- 혼자있는 객체는 없다.
- 클라이언트 : 요청 , 서버 : 응답
자바 언어의 다형성
- 오버라이딩을 떠올려보면 save()라는 인터페이스가 있으면 save할 수 있는 객체만 있으면 갈아 끼울 수 있음.
public class MemberService {
private MemberRepository memberRepository = new MemoryMemberRepository();
}
public class MemberService {
// 갈아끼워질 수 있다.
//private MemberRepository memberRepository = new MemoryMemberRepository();
private MemberRepository memberRepository = new JdbcMemberRepository();
}
다형성의 본질
- 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다.
- 다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야함.
- 클라이언트를 변경하지 않고, 서버의 구현기능을 유연하게 변경할 수 있다.
정리
- 실세계의 역할과 구현이라는 편리한 컨셉을 다형성을 통해 객체 세상으로 가져올 수 있다.
- 유연, 변경 용이
- 확장 가능설계
- 클라이언트에 영향을 주지 않는 변경
- 인터페이스를 안정적으로 잘 설계하는게 중요. ( 이게 깨지면 무너짐 )
한계
- 인터페이스 자체가 변하면, 클라이언트/서버 모두 변경해야되서 심각해짐
- 자동차가 비행기가 된다던지, 대본자체가 변경되던지, USB 키보드가 아니라 다른 연결방식이 된다던지 등등..
- 결국 인터페이스를 안정적으로 잘 설계하는 것이 중요하다.
스프링과 객체 지향
- 다형성이 가장 중요하다.
- 스프링은 다형성을 극대화해서 이용할 수 있게 도와줌.
- 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원
- 스프링을 사용하면 다형성을 매우 쉽게 다룰 수 있게 해준다.
좋은 객체 지향 설계의 5가지 원칙 (SOLID)
클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가지 원칙을 정리함.
1. SRP : 단일 책임 원칙 ( Single responsibility principle)
- 한 클래스는 하나의 책임만 가져야한다.
- 책임이 모호하다. 중요한 판단의 기준은 변경이다.
- 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이라 볼 수 있다.
- ex) UI를 바꿨을 뿐인데 SQL 문부터 다 고쳐야 한다면 ?
- 책임의 범위를 조절하는 것이 묘미.
2. OCP: 개방-폐쇄 원칙 ( Open/Closed principle) ** 중요
- 소프트웨어 요소는 확장에는 열려있으나, 변경에는 닫혀있어야 한다.
- 다형성을 생각하면 이해하기 쉽다.
- 인터페이스는 변경안되고, 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현
- 문제점
: 구현객체를 변경하려면 클라이언트 코드를 변경해야한다.
: 분명 다형성을 사용했지만 OCP원칙을 지킬 수 없다.
-> 객체를 생성하고 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다. ( 이걸 스프링이 해주는 것 )
3. LSP: 리스코프 치환 원칙 ( Liskov substitution principle)
- 객체는 프로그램의 정확성을 깨드리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다.
- 예를들어 자동차 인터페이스의 엑셀을 구현하는데, 엑셀을 밟으면 앞으로 가야하는데, 뒤로 가게끔 구현을 한다면 프로그램의 정확성을 위배한 것이기에 LSP를 지키지 못한 것이다.
- 단순 컴파일을 의미하는 것이 아님.
4. ISP: 인터페이스 분리 원칙 (Interface segregation principle)
- 특정 클라이언트를 위한 인터페이스 여러개가 범용 인터페이스 하나보다 낫다.
- 자동차 인터페이스 하나로 퉁치는 것보다 운전 인터페이스 , 정비 인터페이스로 분리하면
- 사용자 클라이언트는 운전자 / 정비사 클라이언트로 분리할 수 있게됨.
- 인터페이스가 명확해지고, 대체 가능성이 높아진다.
5. DIP: 의존관계 역전 원칙 (Dependency inversion principle)
- 프로그래머는 "추상화에 의존해야지, 구체화에 의존하면 안된다"
- 의존성 주입은 이원칙을 따르는 방법 중 하나이다.
- 쉽게 말해서 클라이언트가 interface만 알고, 구현에 대해서는 몰라야한다는 것.
- 역활에 의존해야지, 구현에 의존해야함.
정리
- 객체지향의 핵심은 다형성
- 다형성 만으로는 쉽게 부품을 갈아끼우듯이 개발할 수 없다.
- 다형성 만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경된다.
- 다형성 만으로는 OCP, DIP를 지킬 수 없다.
- 뭔가가 더 필요하다...
다시 스프링으로
- 스프링은 다음 기술로 다형성 + OCP, DIP를 가능하게 지원
: DI(Dependency Injection) : 의존관계, 의존성 주입
: DI 컨테이너 제공
- 클라이언트 코드의 변경없이 기능 확장
- 쉽게 부품을 교체하듯이 개발
스프링 없던 시절
- OCP, DIP 원칙을 지키면서 개발을 해보니, 너무 할일이 많아 프레임워크로 만들어버림
- 순수하게 자바로 OCP, DIP 원칙들을 지키면서 개발을 해보면, 스프링 프레임 워크를 만들게 된다( DI컨테이너 )
- 이제 스프링이 왜 만들어졌는지 코드로 이해해보자.
정리
- 모든 설계에 역할과 구현을 분리하자.
- 클라이언트가 사용할 인터페이스만 만들어두고, 구현체는 유연하게 변경할 수 있도록 만드는 것이 좋은 객체지향설계
- 이상적으로는 모든 설계에 인터페이스를 부여하는게 좋지만 문제가 생김
실무 고민
- 인터페이스를 도입하면 추상화라는 비용이 발생함
- 기능을 확장할 가능성이 없다면, 구현 클래스를 직접 사용하고, 향후에 인터페이스로 확장하는 것을 고려하는것이 좋다.
책추천
객체지향 책 추천 : 객체지향의 사실과 오해
스프링 책 추천 : 토비의 스프링
JPA 책 추천 : 자바 ORM 표준 JPA 프로그래밍