자바스크립트에서 객체 지향 프로그래밍 이해하기

프린이·2024년 10월 2일


✏ 객체 [Object]

'어떤 대상'
➡ 컴퓨터, 폰, 책, 옷, 사람, 자동차 ... 등등
👀눈에 보이는 무엇이든 객체가 됨✨

객체 = 속성 + 행동 🙆‍♀️
💡 속성 : 객체의 특징이나 상태


🧸 행동 : 객체의 동작, 기능, 행위 등


✏ 객체 지향 프로그래밍 [Object Oriented Programming]

객체 지향 프로그래밍 관점에서 프로그램이란 객체들을 모아둔 것이고
➡ 객체들이 서로 상호 작용 하는 과정을 프로그래밍 하는 것😎

ex ) 노트북 ↔ 사람

키보드를 누르는 행동, 글자를 보여주는 행동
👉 '노트북' & '사람'이 상호작용 해야만 이루어짐🤜💥🤛

🧩 객체 지향 프로그래밍 구현하기

  1. 일단 어떤 객체들이 필요한지 정의
  2. 각 객체들의 속성과 행동을 정의
  3. 객체들 간의 상호작용을 정의

✏ 클래스

객체들의 (속성 & 행동을 코드로 적어둔) 집합체

1
2
3
4
5
6
7
8
9
10
11
class Notebook(색상, 해상도, 무게, 디스플레이) {
    this.색상 = 색상;
    this.해상도 = 해상도;
    this.무게 = 무게;
    this.디스플레이 = 디스플레이;
    this.화면켜짐 = function() {화면 켜기};
    this.키보드입력 = function() {키보드 입력받기};
}
 
var galaxyBook = new Notebook('네이비', '2560x1600', '1.2kg', '15인치');
var macBook = new Notebook('스페이스그레이', '2560x1600', '1.8kg', '16인치');
cs

✏ 객체 지향 프로그래밍의 특징 4가지

💊

캡슐화


한 객체에서 비슷한 역할을 하는 속성,
메서드를 캡슐처럼 한 곳(클래스)에 담는 것

클래스 == 캡슐
장점 : 객체를 계속해서 생성하지 않아도 되기에 '재사용 가능'

목적 : '정보 은닉'
👉 정보 숨기는 것👻
객체의 구체적인 정보(변수, 메서드)를 외부에
노출시키지 않고 접근할 수 없게 보호하는 방법😯💡

캡슐화를 통해 정보 은닉하면
그 정보는 오직 객체 내의 함수 내부에서만 접근 가능✅

💁‍♀️ JS에서 캡슐화를 구현하는 방법은 다양,
주로 비공개 변수와 메서드 만드는 방식


  1. #을 사용한 클래스의 private 필드 ( ES6+ 방법 )
  2. ES6 이후엔 #을 사용해 클래스 내부에서만
    접근 가능한 프라이빗 필드를 정의할 수 있음

    ▶ 특징 : 클래스 외부에서 접근 불가능❌
    오직 클래스 내부에서만 접근 가능⭕

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Person {
      #age; // private 필드 선언
     
      constructor(name, age) {
        this.name = name;
        this.#age = age;
      }
     
      getAge() {
        return this.#age; // 클래스 내부에서 접근 가능
      }
     
      setAge(newAge) {
        if (newAge > 0) {
          this.#age = newAge;
        }
      }
    }
     
    const person = new Person('Alice', 30);
    console.log(person.name); // 'Alice'
    console.log(person.getAge()); // 30
    console.log(person.#age); // Error: private 필드에 직접 접근 불가
    cs
  3. 클로저를 사용한 캡슐화
  4. 클로저 사용하여 변수나 메서드를 외부에서 접근할 수 있도록 만드는 방법
    이 방식은 클래스 이전의 JS에서 많이 사용 되었으며 여전히 유용🙆‍♀️

    ▶ 특징 : 내부 변수는 외부에서 직접 접근할 수 없고
    내부 함수에서만 접근 가능✅

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    function Person(name, age) {
      let _age = age; // private 변수
     
      this.name = name;
     
      this.getAge = function() {
        return _age; // 클로저를 통해 접근 가능
      };
     
      this.setAge = function(newAge) {
        if (newAge > 0) {
          _age = newAge; // 클로저를 통해 수정 가능
        }
      };
    }
     
    const person = new Person('Bob', 40);
    console.log(person.name); // 'Bob'
    console.log(person.getAge()); // 40
    console.log(person._age); // undefined, 외부에서 접근 불가
    cs
  5. Symbol을 사용한 고유한 속성
  6. 심볼 사용해 객체의 고유한 속성 만들어
    외부에서 직접 접근하기 어렵게 만드는 방법
    심볼은 객체 내에서 접근 가능하지만
    외부에서 직접 그 키를 알기 어려워 일종의 비공개 필드 만들 수 있음

    ▶ 특징 : 심볼 사용해 외부에서 속성 탐색하기 어렵게 만들지만
    완전한 정보 은닉은 아님❌

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    const _age = Symbol('age'); // Symbol로 고유한 속성 생성
     
    class Person {
      constructor(name, age) {
        this.name = name;
        this[_age] = age; // Symbol 속성
      }
     
      getAge() {
        return this[_age]; // 클래스 내부에서 Symbol 속성 접근 가능
      }
    }
     
    const person = new Person('Charlie', 50);
    console.log(person.name); // 'Charlie'
    console.log(person.getAge()); // 50
    console.log(person[_age]); // 직접 접근이 어려움 (완전히 은닉되진 않음)
    cs
  7. 모듈 패턴을 사용한 캡슐화
  8. 모듈 패턴은 클로저 & 즉시 실행 함수 표현식(IIFE) 활용해
    변수와 메서드를 모듈처럼 캡슐화 하는 방법
    특정 함수나 객체 내에서마 접근할 수 있는 비공개 상태 유지 가능

    ▶ 특징 : 비공개 함수나 메서드는
    모듈 내부에서만 접근 가능하며 외부에선 노출❌

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    const PersonModule = (function() {
      let _age = 0; // private 변수
     
      return {
        setAge(newAge) {
          if (newAge > 0) {
            _age = newAge;
          }
        },
        getAge() {
          return _age; // 내부에서 접근 가능
        }
      };
    })();
     
    PersonModule.setAge(35);
    console.log(PersonModule.getAge()); // 35
    console.log(PersonModule._age); // undefined, 외부에서 접근 불가
    cs
🎈

추상화

복잡한 문제 ➡ 단순화 시키는 작업 🤭
객체들의 공통된 특징들만 뽑아내는 기법 ❗

🤔프로그래밍에서 추상화 어떻게 구현❓
👉 가장 필요하다고 생각하는 부분만 묶어두면 됨🧵
( 객체들이 공통적으로 갖고 있는 속성이나 메서드가 될 수 있음 )

객체 & 클래스의 관점
👉 각 객체들은 구체적인 값 부여되어 실제하는 개념,
클래스는 객체들이 가져야할 특성이나 기능들을 정의해둔 추상적인 개념

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 추상 클래스
class Car {
  
  constructor(make, model) {
    this.make = make;  // 제조사
    this.model = model;  // 모델
  }
 
  // 추상 메서드 (구현하지 않고 서브클래스에서 구현하도록 강제)
  start() {
    throw new Error('이 메서드는 서브클래스에서 구현해야 합니다.');
  }
}
 
// 서브클래스: 전기차
class ElectricCar extends Car {
  start() {
    return `${this.make} ${this.model} 전기차가 시작 되었습니다.`;
  }
}
 
// 서브클래스: 스포츠카
class SportsCar extends Car {
  start() {
    return `${this.make} ${this.model} 스포츠카가 시작 되었습니다.`;
  }
}
 
// 객체 생성
const tesla = new ElectricCar('테슬라', '모델 S');
const ferrari = new SportsCar('페라리', '488 GTB');
 
console.log(tesla.start()); // 테슬라 모델 S 전기차가 시작 되었습니다.
console.log(ferrari.start()); // 페라리 488 GTB 스포츠카가 시작 되었습니다.
cs
👩‍🦱➡👶

상속 extends

부모 클래스의 속성과 기능을 그래로 이어받아 사용할 수 있게 하고
기능의 일부분을 변경해야 할 경우 상속 받은 자식 클래스에서
해당 기능만 다시 수정(정의)하여 사용할 수 있게 하는 것 ❗

▶ 장점 : 코드 재사용하기에 중복 제거🔥

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 기본 클래스: Animal
class Animal {
  constructor(name) {
    this.name = name; // 동물의 이름
  }
 
  speak() {
    return `${this.name}가 소리를 냅니다.`;
  }
}
 
// 서브클래스: Dog
class Dog extends Animal {
  speak() {
    return `${this.name}가 멍멍합니다!`;
  }
}
 
// 서브클래스: Cat
class Cat extends Animal {
  speak() {
    return `${this.name}가 야옹합니다!`;
  }
}
 
// 객체 생성 및 메서드 호출
const dog = new Dog('바둑이');
const cat = new Cat('냥이');
 
console.log(dog.speak()); // 바둑이 가 멍멍합니다!
console.log(cat.speak()); // 냥이 가 야옹합니다!
cs
💁‍♀️

다형성

Overriding(오버라이딩) : 부모클래스 메서드를 자식 클래스에서 재정의하는 것

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 부모 클래스: Animal
class Animal {
  // 기본 메서드
  speak() {
    return '동물이 소리를 냅니다.';
  }
}
 
// 자식 클래스: Dog
class Dog extends Animal {
  // 부모 클래스의 speak 메서드를 오버라이드
  speak() {
    return '강아지가 멍멍합니다!'; // 오버라이딩된 메서드
  }
}
 
// 자식 클래스: Cat
class Cat extends Animal {
  // 부모 클래스의 speak 메서드를 오버라이드
  speak() {
    return '고양이가 야옹합니다!'; // 오버라이딩된 메서드
  }
}
 
// 객체 생성
const dog = new Dog();
const cat = new Cat();
 
// 메서드 호출
console.log(dog.speak()); // 강아지가 멍멍합니다! (오버라이딩된 메서드 호출)
console.log(cat.speak()); // 고양이가 야옹합니다! (오버라이딩된 메서드 호출)
cs

Overloading(오버로딩) : 같은 이름으로 다양한 매개변수, 리턴타입의 메소드를 여러개 정의하는 것

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Animal {
  // 메서드: speak (오버로딩)
  speak(a, b) {
    // 매개변수의 개수에 따라 다른 동작 수행
    if (typeof a === 'string' && typeof b === 'undefined') {
      return `${a}가 소리를 냅니다.`; // 단일 문자열 입력
    } else if (typeof a === 'string' && typeof b === 'string') {
      return `${a}와 ${b}가 함께 소리를 냅니다.`; // 두 개의 문자열 입력
    } else {
      return '유효하지 않은 입력입니다.'; // 잘못된 입력 처리
    }
  }
}
 
// 객체 생성
const animal = new Animal();
 
// 메서드 호출
console.log(animal.speak('고양이'));             // 고양이가 소리를 냅니다. (단일 입력)
console.log(animal.speak('고양이', '강아지'));   // 고양이와 강아지가 함께 소리를 냅니다. (두 개의 입력)
console.log(animal.speak(5));                    // 유효하지 않은 입력입니다. (잘못된 입력)
cs
profile
안녕하세요! 퍼블리싱 & 프론트엔드 개발 공부 블로그 입니다!

0개의 댓글