객체 지향 프로그래밍(Object-Oriented Programming, OOP) 은 프로그래밍의 한 패러다임(paradigm)이다. 즉 지배적인 사상, 접근 방식, 철학이라고 볼 수 있다.
이런 패러다임은 크게 2가지로 나뉘는데 흔히들 말하는 절차 지향 프로그래밍(Procedural Programming)과 객체 지향 프로그래밍(Object-Oriented Programming, OOP)이다.
그렇다면 객체 지향이란 결국엔 무엇인가?
기본적으로 프로그램은 코드 1줄, 1줄로 만들어진다. 그렇지 않은 프로그램은 존재하지 않는다고 볼 수 있다.
그런데 객체 지향은 그런 코드를 1줄, 1줄 단위가 아닌 객체라는 덩어리 단위로 관리하는 것을 선호한다는 것이다.
그렇다면 객체란 결국 무언인가?
핵심 개념
객체 (Object)
- 우리가 오브젝트(객체)라고 하면 일반적으로 참조 타입의 변수를 생각하지만 객체 지향에서 말하는 객체는 다른 의미이다.
- 객체 지향의 객체는 사전적 의미의 객체에 가깝다. 즉, 의사나 행위가 미치는 대상이라고 할 수 있다.
- 사람, 주문, 버튼, 고양이 등 현실 세계의 사물이나 개념을 프로그램 내에서 표현하고 그것을 객체라고 부르는 것이다.
- 예를 들면 사람 객체, 버튼 객체, 고양이 객체, 사과 객체. 이런식으로 말이다.
- 또한 그러한 객체는 속성과 행동이 존재한다.
- 속성 : 사과는 과일 속성을 가지고 있고, 식물 속성도 가지고 있고, red, green같은 색이라는 속성도 가지고 있다.
- 행동 : 사과는 익으면 색상을 변화 시킨다. 오래 두면 썩는다. 내부에 씨앗을 만든다. 같은 동적인 요소이다.
- 이것을 프로그래밍에 대입하게 되면 사과는 과일, 식물, 색상 같은 속성을 가지며 색상의 변화, 씨앗 생성 같은 메서드(행동)을 가지는 객체라고 할 수 있다.
- 객체 지향에서는 이런 객체라는 개념을 데이터(속성)와 데이터를 처리하는 함수(메서드)로 묶어서 하나의 단위로 관리한다.
- 또한 그런 객체를 1개의 단위로 사용해서 프로그램을 작동시키는 것을 지향한다.
- 여기서 지향이라는 말을 사용하는 것은 이상적인 목표가 저러한 것이지 현실적으로는 객체만 사용해서 프로그래밍을 하는 것은 불가능하기 때문이다.
그렇다면 객체 지향 프로그래밍은 그런 객체를 어떤 방식으로 사용할까?
캡슐화 (Encapsulation)
- 데이터(속성)와 데이터를 처리하는 메서드(함수)를 하나의 단위로 묶는 것을 의미한다.
- 흔히 말하는 함수 형태의 코드이다.
- 외부로부터 객체의 데이터에 직접적인 접근을 제한하고, 메서드를 통해서만 데이터에 접근하도록 하여 데이터의 무결성을 유지한다.
- 이런 캡슐화를 하기 위한 방법은 클래스 기반, 모듈 기반, 객체 리터럴, 클로저, 심볼 캡슐화 등의 방법이 있다.
추상화 (Abstraction)
- 객체의 핵심적인 기능만 인터페이스로 제공하고, 내부 구현은 숨긴다.
- 객체 내부의 코드를 몰라도 원하는 메서드만 호출해서 사용할 수 있게 만드는 방식이다.
- 예를 들어 우리가 청소기의 원리를 몰라도 버튼만 누르면 청소기를 키고 사용할 수 있는 것과 마찬가지이다.
상속 (Inheritance)
- 상위 클래스의 속성과 메서드를 하위 클래스가 물려받아 코드 재사용성을 높인다.
- 자동으로 되는 것은 아니고 extends 코드를 사용해서 상속 받았다는 사실을 명시해야한다.
- 상속을 받으면 하위 클래스는 상위 클래스의 모든 속성과 메서드를 상속받아 사용할 수 있다.
class Animal { // 상위 클래스
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + '가 소리를 냅니다.');
}
}
class Dog extends Animal { // 하위 클래스 (Animal 상속)
speak() {
console.log(this.name + '가 멍멍 짖습니다.'); // 메서드 오버라이딩
}
}
const myDog = new Dog('해피');
myDog.speak(); // 해피가 멍멍 짖습니다.
예를 들어 해당 코드에서 dog 클래스는 원래 name이라는 속성이 없지만 Animal 클래스의 속성을 상속 받아서 name 속성을 사용할 수 있다.
다형성 (Polymorphism):
- 하나의 인터페이스가 다양한 형태의 객체를 처리할 수 있도록 한다.
- 같은 이름의 메서드가 다양한 방식으로 동작할 수 있는 능력이다.
1. 프로토타입 기반 다형성 (메서드 오버라이딩)
// 상위 객체 (동물)
function Animal(name) {
this.name = name;
}
Animal.prototype.makeSound = function() {
console.log('알 수 없는 소리');
};
// 하위 객체 (개)
function Dog(name) {
Animal.call(this, name); // 상위 객체 생성자 호출
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
// 메서드 오버라이딩
Dog.prototype.makeSound = function() {
console.log('멍멍!');
};
// 하위 객체 (고양이)
function Cat(name) {
Animal.call(this, name); // 상위 객체 생성자 호출
}
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;
// 메서드 오버라이딩
Cat.prototype.makeSound = function() {
console.log('야옹!');
};
// 객체 생성 및 메서드 호출
const dog = new Dog('해피');
const cat = new Cat('나비');
dog.makeSound(); // 출력: 멍멍!
cat.makeSound(); // 출력: 야옹!
Animal이라는 상위 객체를 만들고, Dog와 Cat이라는 하위 객체를 만들어 makeSound 메서드를 재정의했다.
동일한 makeSound() 메서드 호출이지만, 실제 동작은 객체의 타입에 따라 다르게 실행된다.
구현 방법
1. 객체 리터럴 (Object Literal)
- 가장 기본적인 객체 생성 방식
- 중괄호 {} 를 사용하여 속성과 메서드를 정의
// 객체 리터럴을 사용한 객체 생성
const person = {
name: '홍길동',
age: 30,
sayHello: function() {
console.log(`안녕하세요, 제 이름은 ${this.name}입니다.`);
}
};
// 객체의 속성 및 메서드 접근
console.log(person.name); // 출력: 홍길동
person.sayHello(); // 출력: 안녕하세요, 제 이름은 홍길동입니다.
2. 생성자 함수 (Constructor Function)
- new 키워드를 사용하여 객체를 생성하는 함수
- this 키워드를 사용하여 객체의 속성과 메서드를 정의
// 생성자 함수 정의
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`안녕하세요, 제 이름은 ${this.name}입니다.`);
};
}
// new 키워드를 사용하여 객체 생성
const person1 = new Person('홍길동', 30);
const person2 = new Person('임꺽정', 25);
// 객체의 속성 및 메서드 접근
console.log(person1.name); // 출력: 홍길동
person2.sayHello(); // 출력: 안녕하세요, 제 이름은 임꺽정입니다.
3. 클래스 (Class)
- ES6에서 도입된 클래스 문법은 생성자 함수를 더욱 깔끔하게 표현
- extends 키워드를 사용하여 상속을 구현
// 클래스 정의
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`안녕하세요, 제 이름은 ${this.name}입니다.`);
}
}
// new 키워드를 사용하여 객체 생성
const person1 = new Person('홍길동', 30);
const person2 = new Person('임꺽정', 25);
// 객체의 속성 및 메서드 접근
console.log(person1.name); // 출력: 홍길동
person2.sayHello(); // 출력: 안녕하세요, 제 이름은 임꺽정입니다.
// 상속 예시
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 부모 클래스의 생성자 호출
this.grade = grade;
}
study() {
console.log(`${this.name} 학생은 ${this.grade}학년입니다.`);
}
}
const student1 = new Student('장보고', 18, 3);
student1.sayHello(); // 출력: 안녕하세요, 제 이름은 장보고입니다.
student1.study(); // 출력: 장보고 학생은 3학년입니다.
핵심 원칙 (SOLID)
객체 지향 설계의 5가지 핵심 원칙인 SOLID는 다음과 같다.
- 단일 책임 원칙 (Single Responsibility Principle, SRP): 하나의 클래스는 하나의 책임만 가져야 한다.
- 개방-폐쇄 원칙 (Open/Closed Principle, OCP): 확장에는 열려 있고, 변경에는 닫혀 있어야 한다.
- 리스코프 치환 원칙 (Liskov Substitution Principle, LSP): 하위 타입은 언제나 상위 타입을 대체할 수 있어야 한다.
- 인터페이스 분리 원칙 (Interface Segregation Principle, ISP): 클라이언트가 필요하지 않은 인터페이스에 의존하지 않도록 한다.
- 의존 역전 원칙 (Dependency Inversion Principle, DIP): 추상화에 의존하고, 구체적인 구현에 의존하지 않도록 한다.
이러한 OOP 개념과 원칙들을 활용하여 자바스크립트 개발을 한다면, 효율적이고 유지 보수가 용이하며 확장성이 뛰어난 코드를 작성할 수 있다.
'자바 스크립트(java script) > 자바 스크립트 기초' 카테고리의 다른 글
ES6 클래스(class) (0) | 2025.03.26 |
---|---|
다형성(Polymorphism) (0) | 2025.03.25 |
얕은 비교, 깊은 비교 (0) | 2025.03.24 |
클로저(closure) (0) | 2025.03.24 |
자바스크립트 이벤트 루프 / 동기와 비동기 (0) | 2025.03.24 |