자바 스크립트(java script)/자바 스크립트 기초

객체 지향 프로그래밍(OOP, Object-Oriented Programming)

lamarcK 2025. 3. 25. 18:56

객체 지향 프로그래밍(Object-Oriented Programming, OOP) 은 프로그래밍의 한 패러다임(paradigm)이다. 즉 지배적인 사상, 접근 방식, 철학이라고 볼 수 있다.

 

이런 패러다임은 크게 2가지로 나뉘는데 흔히들 말하는 절차 지향 프로그래밍(Procedural Programming)과 객체 지향 프로그래밍(Object-Oriented Programming, OOP)이다.

 

그렇다면 객체 지향이란 결국엔 무엇인가?

기본적으로 프로그램은 코드 1줄, 1줄로 만들어진다. 그렇지 않은 프로그램은 존재하지 않는다고 볼 수 있다.

그런데 객체 지향은 그런 코드를 1줄, 1줄 단위가 아닌 객체라는 덩어리 단위로 관리하는 것을 선호한다는 것이다.

 

그렇다면 객체란 결국 무언인가?

핵심 개념

객체 (Object)

  1. 우리가 오브젝트(객체)라고 하면 일반적으로 참조 타입의 변수를 생각하지만 객체 지향에서 말하는 객체는 다른 의미이다.
  2. 객체 지향의 객체는 사전적 의미의 객체에 가깝다. 즉, 의사나 행위가 미치는 대상이라고 할 수 있다.
  3. 사람, 주문, 버튼, 고양이 등 현실 세계의 사물이나 개념을 프로그램 내에서 표현하고 그것을 객체라고 부르는 것이다.
  4. 예를 들면 사람 객체, 버튼 객체, 고양이 객체, 사과 객체. 이런식으로 말이다.
  5. 또한 그러한 객체는 속성과 행동이 존재한다.
  6. 속성 : 사과는 과일 속성을 가지고 있고, 식물 속성도 가지고 있고, red, green같은 색이라는 속성도 가지고 있다.
  7. 행동 : 사과는 익으면 색상을 변화 시킨다. 오래 두면 썩는다. 내부에 씨앗을 만든다. 같은 동적인 요소이다.
  8. 이것을 프로그래밍에 대입하게 되면 사과는 과일, 식물, 색상 같은 속성을 가지며 색상의 변화, 씨앗 생성 같은 메서드(행동)을 가지는 객체라고 할 수 있다.
  9. 객체 지향에서는 이런 객체라는 개념을 데이터(속성)와 데이터를 처리하는 함수(메서드)로 묶어서 하나의 단위로 관리한다.
  10. 또한 그런 객체를 1개의 단위로 사용해서 프로그램을 작동시키는 것을 지향한다.
  11. 여기서 지향이라는 말을 사용하는 것은 이상적인 목표가 저러한 것이지 현실적으로는 객체만 사용해서 프로그래밍을 하는 것은 불가능하기 때문이다.

그렇다면 객체 지향 프로그래밍은 그런 객체를 어떤 방식으로 사용할까?

캡슐화 (Encapsulation)

  1. 데이터(속성)와 데이터를 처리하는 메서드(함수)를 하나의 단위로 묶는 것을 의미한다.
  2. 흔히 말하는 함수 형태의 코드이다.
  3. 외부로부터 객체의 데이터에 직접적인 접근을 제한하고, 메서드를 통해서만 데이터에 접근하도록 하여 데이터의 무결성을 유지한다.
  4. 이런 캡슐화를 하기 위한 방법은 클래스 기반, 모듈 기반, 객체 리터럴, 클로저, 심볼 캡슐화 등의 방법이 있다.

추상화 (Abstraction)

  1. 객체의 핵심적인 기능만 인터페이스로 제공하고, 내부 구현은 숨긴다.
  2. 객체 내부의 코드를 몰라도 원하는 메서드만 호출해서 사용할 수 있게 만드는 방식이다.
  3. 예를 들어 우리가 청소기의 원리를 몰라도 버튼만 누르면 청소기를 키고 사용할 수 있는 것과 마찬가지이다.

상속 (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 개념과 원칙들을 활용하여 자바스크립트 개발을 한다면, 효율적이고 유지 보수가 용이하며 확장성이 뛰어난 코드를 작성할 수 있다.