1. OOP 기본 개념 이해
1.1. 객체 (Object)
현실 세계의 물체나 개념을 소프트웨어로 표현한 것. 예시: 자동차, 사람 등.
1.2. 클래스 (Class)
객체를 생성하기 위한 설계도. 객체의 속성과 메서드를 정의. 예시: class Car { ... }
1.3. 속성 (Attribute)
객체가 가지는 데이터. 객체의 상태를 나타냄. 예시: 자동차의 색상, 속도.
1.4. 메서드 (Method)
객체가 수행할 수 있는 행동. 객체의 기능을 정의. 예시: start(), stop() 같은 함수.
클래스 (Class): 공장
객체 (Object): 자동차
속성 (Attribute): 자동차의 특성(색상, 모델, 연비 등)
메서드 (Method): 자동차의 기능(시동, 가속, 제동 등)
![](https://blog.kakaocdn.net/dn/c2KS3k/btsL7jUjiXj/Km74q44d0igKeOesO6xoe0/img.png)
OOP를 적용하는 이유
객체 지향 프로그래밍(OOP)을 활용하면, 코드의 구조를 더 체계적으로 정리하고,
유지보수와 확장성이 뛰어난 '좋은 설계'를 지향할 수 있다
좋은 설계란?
1. 요구하는 기능을 정확히 수행하며,
2. 추후 변경, 확장이 용이하며,
3. 타인이 봐도 이해하기 쉬운, 즉 가독성이 높으며,
4. 쉽게 재사용이 가능한 설계 (ex:특정 기능 등 모듈화)
OOP는 여러 언어에서 지원하고 적용 가능하기 때문에 OOP를 배우면 다양한 언어 환경에서 좋은 설계를 하기 쉬워진다!
"좋은 설계"를 더 깊이 이해하려면 SOLID 원칙같은 개념을 추가로 공부하면 좋습니다.
SOLID 원칙 | 설명 | 초보자가 꼭 배워야 하는 이유 |
S 단일 책임 원칙 (SRP) | 하나의 클래스는 하나의 책임만 가져야 한다 | 초보자들은 보통 "한 클래스에 모든 기능을 몰아넣는" 실수를 하므로, 이 원칙을 이해해야 함 |
O 개방-폐쇄 원칙 (OCP) | 기존 코드를 수정하지 않고 확장할 수 있어야 한다 | 유지보수성을 높이는 핵심 원칙, 실전에서 매우 자주 사용됨 |
L 리스코프 치환 원칙 (LSP) | 부모 클래스를 자식 클래스로 대체해도 정상 동작해야 한다 | 상속을 잘못 사용하면 코드가 깨질 수 있으므로 반드시 알아야 함 |
I 인터페이스 분리 원칙 (ISP) | 클라이언트가 필요하지 않은 메서드에 의존하면 안 된다 | 불필요한 코드 의존성을 줄이는 중요한 원칙 |
D 의존성 역전 원칙 (DIP) | 구체적인 구현이 아닌, 추상화에 의존해야 한다 | 유지보수성 |
맥도날드 개장!
맥도날드 개장을 위해서는 햄버거 , 즉 객체가 필요함
백종원의 솔루션을 거쳐 딱 한개의 햄버거만 필요하다면 이렇게 코딩해도 문제 x
const hamburger1 = {
name: "빅맥",
price: 5800
calorie: 600,
}
현실
const hamburger1 = {
name: "배토디",
price: 5800
calorie: 600,
}
const hamburger2 = {
name: "슈비버거",
price: "5800"
calorie: 600,
}
const hamburger3 = {
name: "빅맥",
price: 7400
calorie: 700,
}
const hamburger4 = {
name: "상스치콤",
price: 11900,
calorie: 1100
}
........
같은 코드의 반복 >> 노가다
오타, 누락등의 실수 가능성
새로운 속성 추가 시 또 노가다
>> 햄버거 공장 개념의 Class만 추가해줘도 쉽게 햄버거, 즉 객체를 쉽게 생성하고 업데이트 할 수 있다.
공장을 통해 햄버거(객체) 양산 중...
class Hamburger = {
constructor(name, price, calorie){
this.name = name;
this.price = price;
this.calorie = calorie;
}
}
const hamburger1 = new Hamburger("배토디", 5800, 600)
const hamburger2 = new Hamburger("슈비버거", 6500, 900)
const hamburger3 = new Hamburger("빅맥", 7400, 700)
const hamburger4 = new Hamburger("상스치콤", 11900, 1100)
...
2. OOP의 핵심 원칙
2.1. 캡슐화 (Encapsulation)
객체 내부의 세부 정보를 감추고, 외부에서 접근할 수 없도록 제한
장점: 데이터 보호, 코드 유지보수 용이성
class Hamburger {
#price; // private 속성
constructor(name, price) {
this.name = name;
this.#price = price;
}
getPrice() {
return this.#price; // 허용된 메서드를 통해서만 접근 가능
}
setPrice(newPrice) {
if (newPrice > 0) {
this.#price = newPrice;
} else {
console.log("가격은 0원 이상이어야 합니다!");
}
}
}
const hamburger1 = new Hamburger("빅맥", 7000);
console.log(hamburger1.price) // undefined
console.log(hamburger1.getPrice()); // 7000
hamburger1.setPrice(10000);
console.log(hamburger1.getPrice()); // 10000
2.2. 상속 (Inheritance)
기존 클래스의 속성과 메서드를 새로운 클래스가 물려받는 것
장점: 코드 재사용성 증가, 코드 중복 감소, 자식클래스에서 새롭게 확장 가능
class Hamburger {
constructor(name, price) {
this.name = name;
this.price = price;
}
getInfo() {
return `${this.name}: ${this.price}원`;
}
}
// 자식 클래스: 치즈버거
class CheeseBurger extends Hamburger {
constructor(price) {
super("치즈버거", price); // 부모 클래스 속성 상속
this.extraCheese = true;
}
addCheese() {
console.log(`${this.name}에 치즈 추가`);
}
}
const cheeseBurger = new CheeseBurger(8000);
console.log(cheeseBurger.getInfo()); // "치즈버거: 8000원"
cheeseBurger.addCheese(); // "치즈버거에 치즈 추가!"
2.3. 추상화 (Abstraction)
공통된 특성을 모아 상위 개념으로 정의하고 불필요한 세부 사항 생략
장점: 복잡성 감소, 시스템 이해 용이
// 추상 클래스
class Hamburger {
constructor(name, price) {
if (new.target === Hamburger) {
throw new Error("추상 클래스는 직접 생성할 수 없습니다!");
}
this.name = name;
this.price = price;
}
getInfo() {
return `${this.name}: ${this.price}원`;
}
}
// 자식 클래스: 치즈버거
class CheeseBurger extends Hamburger {
constructor(price) {
super("치즈버거", price);
// 부모 클래스의 생성자를 super를 통해 호출
}
...
// 메서드 추가 가능 (확장 가능)
}
const cheeseBurger = new CheeseBurger(8500);
console.log(cheeseBurger.getInfo()); // "치즈버거: 8500"
2.4. 다형성 (Polymorphism)
동일한 메서드가 객체에 따라 다르게 동작하는것
장점: 유연한 코드 작성, 다양한 객체 간의 상호작용 용이
class Hamburger {
constructor(name, price) {
this.name = name;
this.price = price;
}
getInfo() {
return `${this.name}: ${this.price}원`;
}
}
class CheeseBurger extends Hamburger {
getInfo() {
return `${this.name} (치즈 추가) - 가격: ${this.price}원`;
}
}
// 부모 클래스와 같은 이름의 메서드를 재정의하여 사용 >> 오버라이딩
class SpicyBurger extends Hamburger {
getInfo() {
return `${this.name} (매운맛 추가) - 가격: ${this.price}원`;
}
}
// 다양한 햄버거 객체 생성
const hamburger1 = new CheeseBurger("치즈버거", 8500);
const hamburger2 = new SpicyBurger("매운버거", 9000);
// 같은 메서드지만, 서로 다르게 동작함
console.log(hamburger1.getInfo()); // 치즈버거 (치즈 추가) - 가격: 8500원
console.log(hamburger2.getInfo()); // 매운버거 (매운맛 추가) - 가격: 9000원
OOP 핵심 원칙 |
키워드 |
예시 |
캡슐화 |
중요한 건 숨기고,
|
주민번호 뒷자리는 비밀,
|
상속 |
기본 기능을 물려받아 확장 |
엄마 요리에, 몰래 MSG 추가 |
추상화 |
필요한 기능만 보여주고,
|
버튼 딸깍으로 리모콘 사용,
|
다형성 |
같은 요청이라도,
|
게임패드 X버튼을 눌러도,
|
![](https://blog.kakaocdn.net/dn/9UQ2S/btsL7vfSkOs/bw3dAkHqjnQI4fCtUF5S8k/img.png)
3. 다형성, 오버 라이딩과 오버 로딩
class Hamburger {
constructor(name, price) {
this.name = name;
this.price = price;
}
// 오버로딩처럼 동작하는 getInfo()
getInfo(showPrice) {
if (showPrice) {
return `${this.name}: ${this.price}원`;
}
return `${this.name}`;
}
}
class CheeseBurger extends Hamburger {
getInfo(showPrice, hasCheese) {
if (hasCheese) {
return `${this.name} (치즈 추가) - ${showPrice ? `가격: ${this.price}원` : ""}`;
}
return super.getInfo(showPrice);
}
}
class SpicyBurger extends Hamburger {
getInfo(showPrice, isSpicy) {
if (isSpicy) {
return `${this.name} (매운맛 추가) - ${showPrice ? `가격: ${this.price}원` : ""}`;
}
return super.getInfo(showPrice);
}
}
// 다양한 햄버거 객체 생성
const hamburger1 = new CheeseBurger("치즈버거", 8500);
const hamburger2 = new SpicyBurger("매운버거", 9000);
// 같은 메서드(getInfo)지만 인자 개수에 따라 다르게 동작
console.log(hamburger1.getInfo(true)); // "치즈버거: 8500원"
console.log(hamburger1.getInfo(true, true)); // "치즈버거 (치즈 추가) - 가격: 8500원"
console.log(hamburger1.getInfo(false, true)); // "치즈버거 (치즈 추가)"
console.log(hamburger2.getInfo(true)); // "매운버거: 9000원"
console.log(hamburger2.getInfo(true, true)); // "매운버거 (매운맛 추가) - 가격: 9000원"
console.log(hamburger2.getInfo(false, true)); // "매운버거 (매운맛 추가)"
이러한 부모 클래스의 메서드를 같은 이름으로 사용하지만, 자식클래스에서 재정의하여 사용하는 것을 오버라이딩
>> 기존의 메서드를 재정의 한다.
>>> 상속 관계에서 동일한 메소드 명으로 새로운 기능을 동작하고 싶을 경우 사용
같은 메서드/생성자를 사용하지만 매개변수 개수나 값에 따라 다르게 동작하도록 구현하는 것을 오버로딩
>> 같은 이름의 메서드를 같은 클래스 내에서 여러 개 정의한다.
>>> 같은 클래스 or 인터페이스에서 동일한 메소드 명으로 다양한 기능을 동작하게 하고 싶을 경우 사용
오버로딩 또한 같은 getInfo 메서드를 사용하지만 다른 결과를 도출하고 있기 때문에, 오버라이딩과 오버로딩 모두 다형성을 구현하는 방식으로 볼 수 있다.
이러한 다형성을 활용하여 유연성 높고 확장성, 유지보수성이 높은 코드 작성이 가능해집니다.
사진 제공 : 오혜성
'TIL' 카테고리의 다른 글
내일배움캠프 15주차 금요일 TIL (0) | 2025.02.07 |
---|---|
15주차 내일배움캠프 화요일 TIL (0) | 2025.02.04 |
내일배움캠프 15주차 월요일 TIL (0) | 2025.02.03 |
내일배움캠프 14주차 금요일 TIL (0) | 2025.01.31 |
내일배움캠프 13주차 금요일 TIL (0) | 2025.01.24 |