The hell of EJB….
EJB(Enterprise Java Beans): 자바당 정파 기술
- 이론은 좋은데 실무적으로 사용하기에 복잡하고 힘들면서, 느리다.
Spring(스프링)
- EJB 컨테이너 대체
Hibernate
- EJB 엔티티빈 기술을 대체
- JPA 새로운 표준 정의
객체 지향 특징
추상화, 캡슐화, 상속, 다형성
객체지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 “객체”들의 모임으로 파악하고자 하는 것이다. 각각의 개체는 메시지를 주고 받고, 데이터를 처리할 수 있다. ⇒ 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 sw 개발에 많이 사용된다.
유연하고, 변경이 용이하다?
- 레고 블럭 조립하듯이
- 부품을 갈아 끼우듯이
- 컴포넌트를 부품처럼 쉽게 교체한다.
다형성의 실세계 비유
[다형성]
운전자와 자동차 예제
자동차가 바뀌어도 운전자한테 가는 영향은 크지 않다. 운전자는 자동자의 역할/조작법(인터페이스)에 대해서만 알고 있고, 여기에 의존한다. 그렇다면 왜 자동차 역할을 만들고 모델을 분리한걸까?
클라이언트는 자동차의 세부 구조를 정확하게 몰라도 된다. ‘자동차 역할’만 잘 알고 있다면 어떠한 모델을 사용하든 ‘운전’이라는 행동을 통해 자동차 역할을 수행할 수 있다. 이를 통해 자동차 세계는 무한히 확장 가능하다.
⇒ 역할과 구현이 구분되어, 새로운 자동차 모델이 나와도 클라이언트에 영향을 주지않고 클라이언트는 서비스를 이용할 수 있는 것이 세계의 확장면에서 무한한 가능성을 갖는다.
- 클라이언트는 인터페이스만 알면 된다.
- 내부구조가 변경되어도, 어떠한 모델이 와도 영향 x, 심지어 내부 구조를 몰라도 된다.
[한계]
- 역할(인터페이스) 자체가 변하면, 클라이언트, 서버 모두에 큰 변경이 발생한다.
- 자동차를 비행기로 변경해야 한다면..?
따라서, 인터페이스(API 등)를 안정적으로 잘 설계하는 것이 중요하다.
*이외에 공연 무대(배우와 역할), 키보드, 마우스, 세상의 표준 인터페이스들, 정렬 알고리즘, 할인 정책 로직 등의 예시가 있다.
⇒ 자바는 이러한 다형성을 활용한 언어이다.
⭐️ 역할 = 인터페이스
구현 = 인터페이스를 구현한 클래스, 구현 객체
객체를 설계할 때 역할과 구현을 분리해서 설계한다.
JAVA overriding(오버라이딩)과 polymorphism(다형성)
※ JAVA 게시물 링크
다형성의 본질
- 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다???
- 다형성의 본질을 이해하려면 협력이라는 객체사이의 관계에서 시작해야 한다.
- 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
객체의 협력
- 혼자 있는 객체는 없다. 모든 객체는 서로 협력한다.
- 수많은 객체 클라이언트(요청)와 객체 서버(응답)은 서로 협력 관계를 가진다.
좋은 객체 지향 설계의 5가지 원칙(SOLID)
클린코드로 유명한 로버트 마틴이 좋은 객체 지향 설계의 5가치 원칙을 정리한 내용이다.
- SRP(Single Responsibility principle): 기준을 “변경”으로 두고, 하나의 클래스를 변경했을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.
- OCP(Open / Closed Principle): sw 요소는 확장에는 열려 있으나 변경에는 닫혀있어야 한다. 예컨대 인터페이스를 구현한 새로운 클래스를 생성하여 새로운 기능을 구현하는 것(다형성)은 기존 코드를 변경하는 것이 아닌 확장하는 것이다.
- 다형성을 사용해도 OCP 원칙을 지킬 수 없는 경우
- MemberService(클라이언트)가 MemoryMemberRepository(서버) → JdbcMemberRepository(서버/DB)로 코드를 변경하는 경우
- ⇒ 이럴 경우는 객체를 생성하고, 연관관례를 맺어주는 별도의 조립, 설정자가 필요한데 이걸 Spring의 컨테이너가 해결해준다. (이 부분은 코드를 쭉 짜보면서 DI 컨테이너 등이 나오면 이 내용을 상기하여 유심히 보자.,,)
- LSP 리스코프 치환 원칙(Liskov Substitution Principle): 단순히 컴파일 단계에서의 Error가 아니라 기능적으로 인터페이스 규약을 지켜야 한다는 원칙이다.
- ex) 나침반을 북쪽으로 향하고 있으면 북쪽이 나와야 하는데 남쪽을 가르켜도 작동 or not 작동에는 문제가 없지만 논리적인 규약에 맞지 않는 기능이므로 리스코프 치환 원칙을 위반한다.
- ISP 인터페이스 분리 원칙(Interface Segregation Principle): 하나의 인터페이스에 여러 기능을 묶지않고(범용 인터페이스) 특정 클라이언트를 위해서 분리를 한다고 할지라도, 인터페이스를 세분화하는 원칙이다. 즉, 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다는 뜻이다. 인터페이스가 명확해지고 대체 가능성이 높아진다.
- DIP 의존관계 역전 원칙(Dependency Inversion Principle): 클라이언트는 구현이 아닌 역할에 의존한다. 프로그래머는 추상화에 의존해야 하고, 구체화에 의존하면 안된다는 원칙이다. 인터페이스에 의존하고, 구현 클래스에 의존하면 안된다는 뜻과 일맥상통하다.
- MemberService가 인터페이스에 의존하지만 구현 클래스에도 동시에 의존한다. MemberService가 new로 MemberyMemberRepository를 할당함으로써 구현 클래스에도 의존하게 되는 상태가 된다. 그래서 코드를 변동하게 되는 것.. → DIP 위반
- 의존한다? = 저 코드에 대해서 “알고있는 상태”다.
- ⇒ MemberService는 MemberRepository 인터페이스에만 의존하도록 설계해야 한다. (이 부분도 코드 짜면서 계속 살펴볼 내용)
public class MemberService {
// private MemberRepository memberRepository = new MemoryMemberRepository();
private MemberRepository memberRepository = new JdbcMemberRepository();
}
결론 | 다형성만으로는 구현 객체를 변경할 때 클라이언트 코드도 함께 변경되므로 쉽게 부품을 갈아 끼우듯이 개발할 수 없다. 즉, OCP와 DIP를 지킬 수 없다. 이 부분이 앞으로 공부할 Spring과 객체지향에서 배울 내용이다.
Spring은 다음 기술로 다형성 + OCP, DIP를 가능하게 지원한다.
- Spring의 DI(Dependency Injection): 의존성 주입
- DI 컨테이너: Java 객체들을 컨테이너 안에 넣어두고 의존 관계를 주입해주는 것
배운 내용을 활용하여 실무에서 주의/활용할 수 있는 것
- 모든 설계에 역할과 구현을 분리하자. 이상적으로는 모든 설계에 인터페이스를 부여하자.
- 기능을 확장할 가능성이 없다면 구현 클래스를 직접 사용하고, 향후 꼭 필요할 때 리팩터링해서 인터페이스를 도입하는 것도 방법이다.
'Dev > Spring & JPA' 카테고리의 다른 글
[Spring] 처리율 제한(Rate Limit)으로 악의적인 공격 차단하기 (0) | 2024.11.23 |
---|---|
[Spring] HTTP Request Client(webclient, feignclient) (10) | 2024.11.10 |
[spring] null 처리를 위한 spring의 Stringutils (2) | 2023.12.21 |
[spring] @Async와 SimpleAsyncTaskExecutor, TaskExecutor 그리고 thread pool (0) | 2023.10.11 |