1. 객체지향 프로그래밍(OOP)란?
객체지향 프로그래밍(Object-Oriented Programming, OOP)은 **객체(Object)**를 중심으로 프로그램을 구성하는 프로그래밍 패러다임이다. 객체란 데이터(속성, 상태)와 메서드(동작, 행동)를 포함하는 독립적인 모듈을 의미하며, 객체 간의 관계를 통해 프로그램이 동작한다.
📌 OOP의 핵심 개념
객체지향 프로그래밍은 다음과 같은 4가지 개념을 기반으로 한다.
- 캡슐화(Encapsulation)
- 객체의 데이터를 외부에서 직접 접근하지 못하도록 보호하고, 필요한 경우 메서드를 통해 접근을 제한하는 개념
- **정보 은닉(Information Hiding)**을 통해 유지보수성과 보안성을 높일 수 있음
- 예제: private, protected 접근 제어자를 사용하여 클래스 내부 데이터 보호
- 추상화(Abstraction)
- 객체의 핵심적인 속성과 동작만을 표현하고, 불필요한 세부 사항은 숨기는 개념
- 추상 클래스(Abstract Class)와 인터페이스(Interface)를 활용하여 설계 가능
- 예제: Car 클래스를 정의할 때, "차량의 브랜드", "타이어 개수" 등의 핵심 속성만 포함하고 내부 엔진 구조는 숨김
- 상속(Inheritance)
- 기존 클래스를 확장하여 새로운 클래스를 만들 수 있는 기능
- 코드의 재사용성 증가, 유지보수 용이
- 예제: class Dog extends Animal → Dog 클래스가 Animal 클래스를 상속받아 "걷는다", "먹는다" 등의 기능을 공유
- 다형성(Polymorphism)
- 같은 인터페이스나 부모 클래스를 상속받은 객체가 서로 다른 방식으로 동작할 수 있는 기능
- 오버로딩(Overloading): 같은 이름의 메서드를 매개변수 형태에 따라 다르게 사용
- 오버라이딩(Overriding): 부모 클래스의 메서드를 자식 클래스에서 재정의하여 사용하는 방식
- 예제: Animal 클래스에서 sound() 메서드를 정의하고, Dog 클래스와 Cat 클래스에서 각각 "멍멍", "야옹" 출력하도록 오버라이딩
2. OOP의 장점과 한계
1) OOP의 장점
- 코드 재사용성 증가 → 상속을 통해 기존 코드를 재사용 가능
- 유지보수 용이 → 코드가 모듈화되어 있어 수정이 쉬움
- 확장성 증가 → 새로운 기능을 추가하기 쉬움
- 데이터 보안 강화 → 캡슐화를 통해 외부 접근을 제한
2) OOP의 한계
- 설계가 어렵고 시간이 많이 걸림
- 객체 간의 관계가 많아지면 복잡도가 증가할 수 있음
- 성능 오버헤드 발생 가능 (객체 생성 및 메모리 관리 부담)
📌 결론: OOP는 유지보수성과 확장성이 뛰어나지만, 올바른 설계가 필수적이다. 이 문제를 해결하기 위해 SOLID 원칙이 등장하였다.
3. SOLID 원칙이란?
SOLID 원칙은 객체지향 설계를 보다 효과적으로 하기 위한 5가지 원칙을 의미한다. 이 원칙을 따르면 유지보수성이 높고 확장성이 뛰어난 코드 작성이 가능하다.
📌 SOLID 원칙 5가지
원칙 |
설명 |
S: 단일 책임 원칙 (SRP) | 클래스는 단 하나의 책임만 가져야 한다. |
O: 개방-폐쇄 원칙 (OCP) | 기존 코드를 변경하지 않고 기능을 확장할 수 있어야 한다. |
L: 리스코프 치환 원칙 (LSP) | 자식 클래스는 부모 클래스의 기능을 변경 없이 확장해야 한다. |
I: 인터페이스 분리 원칙 (ISP) | 인터페이스는 특정 기능에 맞게 분리하여 설계해야 한다. |
D: 의존 역전 원칙 (DIP) | 구체적인 구현이 아닌, 추상적인 개념에 의존해야 한다. |
4. SOLID 원칙 상세 설명
4-1. 단일 책임 원칙(Single Responsibility Principle, SRP)
- 클래스는 하나의 기능만 담당해야 하며, 하나의 변경 이유만 가져야 함
- 여러 개의 책임을 가지면 유지보수가 어려워지고, 코드 수정 시 예상치 못한 오류가 발생할 가능성이 높아짐
❌ 잘못된 예시 (책임이 여러 개)
class Report {
public void generateReport() { /* 보고서 생성 */ }
public void printReport() { /* 보고서 출력 */ }
}
✅ 올바른 예시 (책임을 분리)
class ReportGenerator {
public void generateReport() { /* 보고서 생성 */ }
}
class ReportPrinter {
public void printReport() { /* 보고서 출력 */ }
}
4-2. 개방-폐쇄 원칙(Open-Closed Principle, OCP)
- 확장은 가능하지만, 기존 코드는 변경하지 않아야 함
- 기존 기능을 수정하지 않고도 새로운 기능을 추가할 수 있어야 함
✅ 올바른 예시 (추상 클래스를 사용하여 확장 가능하도록 설계)
abstract class Payment {
abstract void pay();
}
class CreditCardPayment extends Payment {
void pay() { System.out.println("신용카드 결제"); }
}
class PayPalPayment extends Payment {
void pay() { System.out.println("PayPal 결제"); }
}
4-3. 리스코프 치환 원칙(Liskov Substitution Principle, LSP)
- 자식 클래스는 부모 클래스를 대체할 수 있어야 한다.
- 자식 클래스가 부모 클래스의 기능을 변경하면 안 됨
❌ 잘못된 예시 (자식 클래스가 부모의 기능을 변경함)
class Rectangle {
int width, height;
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
}
class Square extends Rectangle {
void setWidth(int w) { width = height = w; } // 부모 클래스의 기능을 변경
}
4-4. 인터페이스 분리 원칙(Interface Segregation Principle, ISP)
- 클라이언트가 사용하지 않는 기능을 강요하지 않아야 한다.
- 인터페이스를 기능별로 나누어 최소한의 기능만 포함해야 한다.
❌ 잘못된 예시 (불필요한 메서드 포함)
interface Worker {
void work();
void eat(); // 모든 Worker가 식사하는 기능이 필요하지 않을 수도 있음
}
✅ 올바른 예시 (인터페이스를 분리함)
interface Workable {
void work();
}
interface Eatable {
void eat();
}
4-5. 의존 역전 원칙(Dependency Inversion Principle, DIP)
- 구체적인 구현이 아닌, 추상적인 인터페이스에 의존해야 함
- 상위 모듈이 하위 모듈에 의존하지 않고, 둘 다 추상화된 개념에 의존해야 한다.
✅ 올바른 예시 (인터페이스를 활용하여 유연한 구조 설계)
interface Database {
void connect();
}
class MySQLDatabase implements Database {
public void connect() { System.out.println("MySQL 연결"); }
}
class Application {
private Database db;
Application(Database db) { this.db = db; }
}
'컴퓨터공학' 카테고리의 다른 글
JWT(JSON Web Token) 인증 방식 개념 (0) | 2025.03.18 |
---|---|
REST API란? RESTful한 설계 원칙 (0) | 2025.03.17 |
동적 프로그래밍(Dynamic Programming)의 개념과 예제 (0) | 2025.03.17 |
그래프(Graph) 자료구조와 최단 경로 알고리즘 (다익스트라, 플로이드 워셜) (0) | 2025.03.16 |
해시 테이블(Hash Table)의 구조와 활용 사례 (0) | 2025.03.16 |