| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 | 31 |
- 좋은 객체지향 설계 원칙
- 키움
- ResponseEntity
- 스프링 컨테이너
- 의존관계 자동 주입
- 405 METHOD_NOT_ALLOWED
- 기본 생성자
- 랜덤 포트
- JPA
- 스프링 IoC 컨테이너
- 통합 테스트
- entity
- 컴포넌트 스캔
- 테스트 코드
- 빈 스코프
- RunWith
- Controller
- Java Reflection API
- 정적 컨텐츠
- 의존성 주입
- Not Acceptable
- 가을야구
- 406 NOT_ACCEPTABLE
- 스프링
- SpringBootTest
- restTemplate
- jdbc template
- jdbc
- testresttemplate
- 스프링 데이터 JPA
- Today
- Total
코드네임 JY
[스프링 핵심] 좋은 객체지향 설계의 5가지 원칙 본문

미리보기
✅ 좋은 객체지향 설계의 5가지 원칙에 대해 알아보자!
🥗 좋은 객체지향 설계의 5가지 원칙
주어진 원리나 원칙이 있을 때, 해당 원리 및 원칙들을 따른다면 어느정도 보장된 퍼포먼스를 수행할 수 있다.
현실에서는 물론, 소프트웨어를 만드는 데에도 이런 규율들은 필요하다.
이는 나중에도 유지보수하기 쉽고, 확장이 쉬운 소프트웨어를 만들 수 있기 때문이다!
SRP (Single Responsibility Principle)
한 클래스는 하나의 책임만 가져야 한다. 변경이 있을 때, 파급이 적다면 SRP를 잘 따른 것이다!
예를 들어 회원가입 기능을 구현하는데, 해당 클래스에 가입에 대한 모든 과정을 다 때려박았다고 하자..
갑자기 중간 로직에 대해 손을 봐야할 일이 생겼다면? 회원가입 기능은 꽤나 핵심 기능인데, 해당 코드를 고쳐야 한다.
이처럼 변경이 생겼을 때, 파급을 적게 일으키기 위해서 클래스를 분리하여 기능을 구현하면 좋다!
OCP (Open-Closed Principle) ⭐️
확장에는 열려 있으나, 변경에는 닫혀 있어야 한다. 무슨 🐶 소리인지.. 확장은 알겠는데, 변경에 어떻게 닫혀있을 수가 있지?

Driver 클래스는 Car 인터페이스에 의존하고, SONATA 라는 구현체 클래스를 통해 Car 인터페이스를 구현할 수 있다.

그런데 갑자기 AVANTE 클래스로 구현체를 확장하고 싶다면?
위 경우에는 구현체만 갈아끼우면 되기 때문에 (SONATA → AVANTE), Driver 클래스를 손대지 않고 확장할 수 있다!

하지만 Driver 클래스가 Car 인터페이스에 의존하지 않고, 구현체인 SONATA 클래스에 바로 의존했다면?
Driver 클래스 내에 코드를 변경해야한다.. 그럼 클라이언트 코드를 변경해야한다는 의미인데, 이는 별로 좋지 않다!
정리하면,
"확장에는 열려 있으나" = 새로운 구현을 용이하게 할 수 있어야 한다!
"변경에는 닫혀 있어야" = 클라이언트 코드(혹은 기존 코드)를 변경하는 일은 없어야 한다!
라고 정리할 수 있겠다! 이처럼 구현체보다 인터페이스를 의존하면, OCP 원칙을 지킬 수 있다!
LSP (Liskov Substitution Principle)
하위(구현) 클래스는 인터페이스 규약을 지켜야한다. 인터페이스를 구현한 구현체를 믿고 사용하기 위해, 기능을 보장해야한다.

예를 들어, Car 인터페이스에 있는 pushAccel( ) 이라는 함수를 SONATA 클래스에서 Override해서 구현한다고 하자.
당연히..! 악셀을 밟으면 속력이 (+) 로 올라가야하는데, (-) 로 가도록 구현한다면..? 큰 일이 난다요... ㅋㅋㅋㅋㅋㅋ 🤔🤔
이건 단순히 컴파일에 성공하는 것을 넘어선다. 속력을 (+) 에서 (-) 로 바꾸어도, 코드에서 컴파일 에러는 발생하지 않을 것이다.
다만, 기능적인 부분에서 문제가 되기 때문에! (악셀을 밟았는데 속도가 당연히 늘어야지) 기능을 보장해야한다는 뜻이다!
ISP (Interface Segregation Principle)
인터페이스 여러 개가 범용 인터페이스 하나보다 낫다. 하나의 인터페이스가 변경되도라도, 나머지는 영향이 없도록 해야한다!

무슨 말이냐! CoffeeShop 인터페이스를 사람인 Manager 인터페이스와, 기계인 Machine 인터페이스로 나눴다고 해보자.
예를 들어, Machine 인터페이스에 문제가 생겨서 이를 수정해야하는 이슈가 발생해도, Barista 클래스에는 영향을 주지 않는다.
만약 Manager, Machine으로 인터페이스를 나누지 않고 한 번에 묶어서 이를 구현했다면, 묶여진 기존 코드 자체를 손 봐야 한다.
기존 코드를 변경하는 것은 좋지 않다고 말한 것을 기억해보자! 인터페이스를 나누어 구현해서, 기존 코드의 터치를 줄일 수 있다!
DIP (Dependency Inversion Principle) ⭐️
DIP에서 'I' 가 의미하는 것은 'Injection' 이 아니다! 'Inversion'은 '역전'이고, 'Injection'은 '주입'인데, 단어를 하나하나 뜯어보자.
📚 Dependency
일반적으로 자바에서 A 클래스가 B 클래스의 인스턴스를 생성해서 가지고 있다면,
class class_A {
class_B b = new class_B();
}
class class_B {}
다음과 같은 의존(Dependency) 관계를 가지고 있다고 할 수 있다. "A가 B에 의존한다" 라고 말할 수 있다.

📚 Inversion
그런데 이런 Dependency에 Inversion이 일어난다는 말은 무엇이냐! 'Dependency Inversion'에 대한 정의를 보자.
"High-level modules should not import anything from low-level modules. They should both depend on abstractions."
"Abstractions should not depend on concrete implementations. Concrete implementations should depend on abstractions"
High-level? Low-level? 이게 무슨 말이냐! 레벨이 Low인 이유는 관련된 Dependency가 없는 것을 뜻한다.
반면 High인 이유는 Dependency를 통해서 Low-level 모듈들을 이용해 구현되는 것을 뜻한다.
(쉽게 말해서, Dependency 없으면 Low-level, 있으면 High-level 이라고 생각하자!)
위 정의를 정리해서 해석해보면,
첫째 줄 : "High-level, Low-level 모두 추상화에 의존해야한다" (→ 클래스 상관없이 모두 추상화에 의존해라)
둘째 줄 : "추상화는 구체적인 실행 의존하면 안 되고, 구체적인 실행이 추상화에 의존해야한다" (→ 구현체에 의존하지마라)
첫째 + 둘째 줄 : "구현 클래스에 의존하지 말고, 추상화(=인터페이스)에 의존해야한다!!"
📚 Dependency Inversion (not Injection!! 😡😡)
기존의 의존관계(= A 클래스가 B 클래스의 인스턴스를 생성해서 갖고있음)를 뒤집는(= 인터페이스에 의존으로)다는 것이다!
클래스끼리 직접 인스턴스를 생성하는 방식인 구현 클래스에 의존하지 않고, 인터페이스로 의존관계를 뒤집는다는 뜻이다!
구체적인 설명이 길었다. 하지만 이 한 문장을 설명하기 위한 빌드업이었다. "구현체에 의존하지 말고, 인터페이스에 의존해라!"
추상화에 의존해야지, 구체화에 의존하면 안 된다. 이 원리는 사실 OCP에서 어느정도 힌트를 얻을 수 있다.

위 예시처럼, Driver 클래스가 (구현체인 SONATA 클래스 말고) Car 인터페이스에 의존하도록 설계하면,
OCP 원칙인 "확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다"를 지킬 수 있으며, 쉽게 구현체를 변경할 수도 있다.
헷갈리지 말자! Dependency 'Inversion'을 구현할 수 있는 방식 중 한 가지가 Dependency 'Injection'인 것이다.
스프링 컨테이너가 의존관계 주입(Injection)을 담당해주면, 위 예시처럼 알맞게 의존관계 'Inversion'이 일어날 수 있는 것이다!
'백엔드 공부' 카테고리의 다른 글
| [스프링 핵심] 싱글톤 패턴 (0) | 2023.01.04 |
|---|---|
| [스프링 핵심] 스프링 컨테이너 (0) | 2023.01.03 |
| [스프링 핵심] 다형성 (0) | 2022.12.22 |
| [스프링 입문] AOP (0) | 2022.12.15 |
| [스프링 입문] 스프링 통합 테스트 (0) | 2022.12.15 |