🕐 ~8 분 읽기
📝 이 글에 대한 메모
이 포스트는 OOP와 SOLID 원칙에 대한 제 개인적인 학습 노트를 기반으로 합니다. 이 노트를 더 읽기 쉽고 유용하게 만들기 위해 — 제 자신과 다른 사람들에게 — 제가 인공지능과 협력하여 확장하고 올바른 블로그 형식으로 구조화하는 데 도움을 받았습니다. 아이디어, 학습 여정, 이해는 제 것이며, 인공지능은 작성과 표현을 도와주었습니다.
좋은 코드는 우연으로 일어나지 않습니다. 협업하기 좋은 코드 베이스 뒤에는 어떻게 구조화되고 연결되어야 하는지를 가이드하는 일련의 아이디어가 있습니다.
이 기사는 그 전체 여정을 다룹니다 — OOP가 무엇인지, 객체가 어떻게 관계를 맺는지, 나쁜 설계가 무엇인지, 그리고 마지막으로 모든 것을 연결하는 다섯 가지 SOLID 원칙입니다.
자, 시작해 봅시다.
제 1부: OOP가 무엇인가요?
객체 지향 프로그래밍 (OOP)는 현실을 반영하는 코드를 작성하는 방법입니다. 긴 명령 목록 대신 관련 데이터와 행동을 객체로 그룹화합니다.
🤖 로봇 장난감을 생각해보세요. 그것은 속성 (색상, 높이, 배터리)과 행동을 가지고 있습니다. (걷기, 말하기, 흔들기). 객체 지향 프로그래밍(OOP)에서 그 로봇은 객체이며, 그것을 만들기 위해 사용되는 청사진은 클래스.
두 가지 주요 용어:
- 클래스 — 청사진(템플릿)
- 객체 — 청사진에서 만들어진 실제 인스턴스
네 대지의 기초
| 기초 | 이것이 무엇을 합니다 |
|---|---|
| 상속 | 자식 클래스는 부모 클래스에서 속성과 행동을 상속받습니다 |
| 캡슐화 | 내부 데이터는 숨겨지며 외부에서는 통제된 접근만 허용됩니다 |
| 추상화 | 복잡한 내부 구조는 숨겨지며 필요한 것만 노출됩니다 |
| 다형성 | 동일한 행동이 객체에 따라 다르게 동작할 수 있습니다 |
제 2 부: 객체 간의 관계
객체는 고립되어 있지 않습니다. 그들은 서로 연결되고 협력하며 의존합니다 — 그리고 관계의유형이 중요합니다.
연관성
두 객체는 서로 알고 상호작용할 수 있습니다. 세 가지 주요 크기로 나뉩니다:
- 일대일 — 한 사람당 한 개의 여권
- 일대다 — 한 선생님, 많은 학생들
- 다대다 — 많은 학생들, 많은 과정
내부 연관성에서, 하나의 객체가 다른 객체를 "소유"할 때, 관계는 다음과 같습니다:
- 취합 (희미) — 자식은 부모 없이도 생존할 수 있습니다. 가방에는 책이 들어 있지만, 가방을 버리면 책은 여전히 존재합니다.
- 구성 (밀착) — 자식은 부모 없이는 존재할 수 없습니다. 집에는 방이 있지만, 집을 철거하면 방은 더 이상 존재하지 않습니다.
종속성
일시적이고 사용 기반의 관계. 한 클래스가 메서드 내에서 다른 클래스를 사용하지만 영구적으로 보유하지 않는다. 연관성보다 약하다.
일반화 & 구현
- 일반화 — 여러 클래스에서 공유된 특징을 하나의 슈퍼클래스로 모으기 (상속)
- 구현 — 인터페이스를 구현하는 클래스로, 정의된 계약을 이행할 것을 약속합니다
제 3 부: 나쁜 설계의 모습
규칙을 배우기 전에, 당신이 방어해야 할 대상을 이해하는 것이 도움이 됩니다. Robert C. Martin은 나쁜 설계의 세 가지 증상을 식별했습니다
🪨 경직성 — 시스템을 변경하기 어렵습니다. 하나를 건드리면 다른 많은 것들이 업데이트해야 합니다. 개발자들은 변경을 두려워하게 됩니다.
🍪 취약성 — 변경을 할 때 예상치 못한 곳에서 시스템이 깨집니다. 결제 모듈의 버그를 고치고도 어딘가 이메일 알림이 작동하지 않습니다.
🏗️ 불동자 — 유용한 구성 요소는 재사용할 수 없습니다. 그들은 주변과 너무 얽혀 있어서 추출하는 것이 처음부터 다시 작성하는 것보다 더 오래 걸립니다.
세 가지 모두 동일한 근본 원인을 공유합니다: 구성 요소 간의 의존성 관리가 부족입니다.
부분 4: SOLID
SOLID는 위에 언급된 문제를 직접 해결하는 다섯 가지 원칙의 집합입니다. 각각은 특정 실패 모드를 대상으로 합니다.
S — 단일 책임 원칙
"모듈은 오직 한 가지, 그리고 오직 한 가지 역할에 책임져야 합니다."
각 클래스는 정확히 한 가지 변경 이유를 가지고 있어야 합니다 — 그것은 한 가지 역할(한 그룹의 이해관계자)을服务于합니다.
👨🍳 동시에 주방장, 계산원, 경비원, 배달원을 하는 레스토랑 종사자는 문제를 야기할 것입니다. 하나의 역할에 변화가 생기면 모든 것을 방해합니다.
수업을 나누세요. 각자 하나의 책임만.
O — 개방/폐쇄 원칙
"소프트웨어 아티팩트는 확장에 열려 있고 수정에는 닫혀 있어야 합니다."
새로운 행동을 추가하세요.을 으로 확장한다 — 기존에 작동하는 코드를 편집하지 않고.
🔌 전원 케이블은 새로운 기기들을 연결할 수 있게 하지만, 스트립 자체를 재배선할 필요가 없다. 그게 OCP다.
요구사항이 바뀌면, 새로운 코드를 기존 코드 옆에 삽입한다 — 이미 작동하는 것을 다시 쓰지 않는다.
L — 리스코프 대체 원칙
"S가 T의 하위 타입이라면, T 타입의 객체를 S 타입의 객체로 대체할 수 있으며, 이는 프로그램의 올바름성을 변경하지 않습니다."
하위 클래스는 완전히 대체 가능해야 합니다. 부모 클래스가 하는 모든 약속은 자식 클래스가 지켜야 합니다.
🤖 부모 로봇이 요리할 수 있다면, 자식 로봇도 요리할 수 있어야 합니다 — 에러를 던지거나 침묵적으로 아무것도 하지 않으면 안 됩니다.
만약 당신의 서브클래스가 비어 있는 메서드, 스텀프 구현, 또는 "지원되지 않음" 예외를 던지면 — 그것은 리스코프 위반입니다.
I — 인터페이스 분리 원칙
"클라이언트는 사용하지 않는 인터페이스에 의존하도록 강제되어서는 안 됩니다."
필요하지 않은 메서드를 구현하도록 클래스를 강제하는 두껍은 인터페이스를 만들지 마세요.인터페이스를 분리하세요를 더 작고 집중된 것들로 나누세요.
🍴 손님들이 스튜만 주문했을 때 막대뼈다귀, 스튜숟, 채찍, 그리고 폰두 스테이커를 다 주지 마세요.
더 작은 인터페이스 = 좁은 의존성 = 변경 사항이 지역화됩니다.
D — 의존성 반전 원칙
"고수준 모듈은 저수준 모듈에 의존해서는 안 됩니다. 둘 다 추상화에 의존해야 합니다."
비즈니스 로직은 구체적인 구현에 하드웨이어드되어서는 안 됩니다. 인터페이스와 통신해야 하며, 저수준의 세부 사항은 이러한 인터페이스를 구현해야 합니다.
🤖 로봇의 팔에 스푼을 부착하지 마세요. 팔에 표준 커넥터를 제공하고, 도구를 삽입하고 제거할 수 있도록 하세요.
이것이 인프라스트럭처(데이터베이스, API, 파일 시스템)를 비즈니스 로직을 건드리지 않고 교체할 수 있게 만드는 이유입니다.
빠른 참조: SOLID 요약
| 원칙 | 해결하는 문제 | 묻어야 할 핵심 질문 |
|---|---|---|
| SRP | 너무 많은 일을 하는 클래스 | "이 클래스는 한 가지 이상의 이해관계자 그룹을 위해 서비스를 제공하나요?" |
| OCP | 기존 코드를 기능 추가을 위해 수정 | "이걸 확장으로 추가하는 대신 수정으로 추가할 수 있을까요?" |
| LSP | 불완전한 상속 계층 | "자식을 부모로 바꾸면서도 아무것도 깨지지 않을 수 있을까요?" |
| ISP | 사용하지 않는 메소드가 많은 인터페이스 | "이 클래스가 필요하지 않은 것을 구현하도록 강제되어 있나요?" |
| DIP | 비즈니스 로직이 인프라에 고정되어 있습니다 | "나의 하이레벨 코드가 그것이 필요하지 않은 구체적인 클래스에 의존하고 있나요?" |
시작점은 어디인가
이것들을 처음 적용하는 경우:
- SRP로 시작하세요 — "과도하게 많은 일을 하는" 수업을 찾아 나누세요
- LSP 위반을 확인하세요 — 비어 있는 메서드는 경고 신호입니다
- 인프라 경계에서 인터페이스를 도입하세요 — 그것이 DIP의 실제 적용입니다
이 원칙들은 체크리스트가 아닙니다. 코드를 작성할 때마다 질문해야 할 질문입니다.
원래 게시된 곳은 sanudin.dev입니다











