STUDY NOTE · 개념 정리
객체지향 정리: 4대 특성과 SOLID 원칙
Java
객체지향 프로그래밍(OOP)은 프로그램을 데이터와 그 데이터를 다루는 행동을 묶은 객체들의 협력으로 구성하는 방식이다. 4대 특성은 객체를 만드는 도구이고, SOLID는 변경에 강한 설계를 위한 원칙이다.
1. 절차지향 vs 객체지향
| 구분 | 절차지향 | 객체지향 |
| 중심 | 순서(함수의 흐름) | 객체(데이터 + 행동) |
| 데이터와 함수 | 분리 | 하나의 객체로 묶음 |
| 변경 영향 | 데이터 구조가 바뀌면 관련 함수 전부 수정 | 객체 내부만 수정 (캡슐화) |
| 적합한 곳 | 단순하고 순차적인 처리 | 규칙이 많고 자주 바뀌는 도메인 |
2. 4대 특성
| 특성 | 의미 | Java에서 |
| 캡슐화 | 데이터를 숨기고 메서드로만 다루게 함 | private 필드 + 의미 있는 메서드 |
| 상속 | 기존 클래스를 확장해 재사용 | extends |
| 다형성 | 같은 메시지에 객체마다 다르게 반응 | 오버라이딩, 인터페이스 |
| 추상화 | 공통된 핵심만 뽑아 표현 | abstract class, interface |
캡슐화는 getter/setter를 만드는 게 아니다. order.setStatus(CANCELED) 대신 order.cancel()을 두고, 취소 가능한 상태인지 검사하는 규칙을 객체 안에 두는 것이 캡슐화다.
3. 오버로딩 vs 오버라이딩
| 구분 | 오버로딩 | 오버라이딩 |
| 의미 | 같은 이름, 다른 매개변수 | 부모 메서드를 자식이 재정의 |
| 위치 | 같은 클래스 | 상속 관계 |
| 결정 시점 | 컴파일 타임 | 런타임 (동적 바인딩) |
| 다형성 | 정적 다형성 | 동적 다형성 |
4. 추상 클래스 vs 인터페이스
| 구분 | 추상 클래스 | 인터페이스 |
| 상속 | 하나만 (extends) | 여러 개 구현 가능 (implements) |
| 상태(필드) | 가질 수 있음 | 상수만 |
| 관계 | is-a (공통 기반 코드 공유) | can-do (역할·능력 정의) |
| 메서드 | 일반 + 추상 메서드 | 추상 + default/static 메서드 (Java 8+) |
5. SOLID 원칙
| 원칙 | 이름 | 핵심 |
| S | 단일 책임 원칙 (SRP) | 클래스는 변경할 이유가 하나여야 한다 |
| O | 개방-폐쇄 원칙 (OCP) | 확장에는 열려 있고, 수정에는 닫혀 있어야 한다 |
| L | 리스코프 치환 원칙 (LSP) | 자식은 부모를 대체해도 프로그램이 올바르게 동작해야 한다 |
| I | 인터페이스 분리 원칙 (ISP) | 쓰지 않는 메서드에 의존하지 않도록 인터페이스를 작게 나눈다 |
| D | 의존 역전 원칙 (DIP) | 구체 클래스가 아니라 추상(인터페이스)에 의존한다 |
6. 원칙별 예시
SRP: OrderService가 주문 저장, 결제, 알림 발송, 엑셀 생성까지 하면 결제사 변경, 알림 문구 변경 모두 이 클래스를 고치게 된다. 책임별로 나눈다.
OCP: 결제 수단이 늘 때마다 if (type == TOSS) ... else if (type == CARD)를 고치는 대신, PaymentClient 인터페이스를 두고 구현 클래스를 추가한다. 기존 코드는 수정하지 않는다.
LSP: 정사각형이 직사각형을 상속하면 setWidth만 바꿔도 높이가 같이 바뀌어, 직사각형을 기대한 코드가 깨진다. 상속은 "is-a"보다 "부모 자리에 넣어도 약속을 지키는가"로 판단한다.
ISP: 하나의 MemberService 인터페이스에 조회·가입·관리자 기능이 다 있으면 조회만 필요한 쪽도 전부에 의존한다. 역할별 인터페이스로 나눈다.
DIP: OrderService가 new TossPaymentClient()를 직접 만들지 않고 PaymentClient 인터페이스를 주입받는다. 스프링 DI가 이 원칙을 실천하는 도구다.
7. 상속보다 조합 (Composition over Inheritance)
| 구분 | 상속 | 조합 |
| 관계 | is-a | has-a |
| 결합도 | 강함 (부모 변경이 자식에 전파) | 약함 (인터페이스로 연결) |
| 변경 시점 | 컴파일 타임에 고정 | 런타임에 교체 가능 |
| 캡슐화 | 부모 내부 구현에 의존하기 쉬움 | 공개된 메서드만 사용 |
코드 재사용만을 위한 상속은 피하고, 필요한 기능을 가진 객체를 필드로 두고 위임하는 편이 변경에 강하다.
8. 실제로 어떻게 적용되나
- 권한 체크 리팩터링: 화면마다
if (role == ADMIN || userId == ...)를 하드코딩하면 권한 규칙이 바뀔 때 모든 곳을 고쳐야 한다. 권한 판단을 하나의 객체(권한 서비스)로 모으면 SRP와 OCP를 동시에 지키게 된다. - 전략 패턴: 배송비 계산 방식(무료, 정액, 지역별)을 인터페이스로 두고 구현을 갈아끼운다. 다형성과 OCP의 가장 흔한 적용이다.
- 테스트 용이성: DIP를 지키면 실제 결제 클라이언트 대신 가짜 구현을 넣어 유닛 테스트할 수 있다.
- 도메인 메서드: 상태 변경을 setter로 열어두지 않고
cancel(),confirm()같은 메서드로만 허용하면 잘못된 상태 전이를 객체가 스스로 막는다.
9. 면접 질문으로 정리
- Q. 객체지향의 4대 특성은? 캡슐화, 상속, 다형성, 추상화다. 데이터와 행동을 묶어 숨기고, 공통 부분을 재사용·추상화하며, 같은 메시지에 객체별로 다르게 반응하게 한다.
- Q. 다형성이란? 같은 타입(인터페이스)으로 여러 구현 객체를 다룰 수 있고, 실제 객체에 따라 다른 동작이 실행되는 성질이다. 오버라이딩과 동적 바인딩으로 구현된다.
- Q. SOLID를 설명해주세요. 단일 책임, 개방-폐쇄, 리스코프 치환, 인터페이스 분리, 의존 역전 원칙으로, 변경이 생겨도 영향 범위를 좁히는 설계 원칙이다.
- Q. 추상 클래스와 인터페이스의 차이는? 추상 클래스는 상태와 공통 구현을 가질 수 있고 단일 상속만 가능하며, 인터페이스는 역할을 정의하고 다중 구현이 가능하다.