🎉 berenickt 블로그에 온 걸 환영합니다. 🎉
Lang
TypeScript
07-클래스

1. JS의 class 문법

1
function 챔피언(q스킬, w스킬) {
2
this.q = q스킬
3
this.w = w스킬
4
}
5
6
const nunu = new 챔피언('잡아먹기', '눈덩이굴리기')
7
const garen = new 챔피언('돌격', '방패')
8
9
console.log('누누: ', nunu)
10
console.log('가렌": ', garen)
  • classobject 뽑는 기계
  • this기계로부터 생성되는 object = 인스턴스

위 코드를 ES6 class 문법으로 만들어보면,

1
class 챔피언 {
2
// constructor(생성자) = object 생성 기계
3
constructor(q스킬, w스킬) {
4
this.q = q스킬
5
this.w = w스킬
6
}
7
}
8
9
const nunu = new 챔피언('잡아먹기', '눈덩이굴리기')
10
const garen = new 챔피언('돌격', '방패')
11
12
console.log('누누: ', nunu)
13
console.log('가렌: ', garen)

2. JS의 prototype 문법

1
// prototype === 유전자, 자동으로 추가됨
2
function 챔피언() {
3
this.q = '잡아먹기'
4
this.w = '눈덩이굴리기'
5
}
6
7
챔피언.prototype.name = '누누'
8
9
let nunu = new 챔피언()
10
11
console.log(nunu.name) // 누누
12
console.log(nunu) // 챔피언 { q: '잡아먹기', w: '눈덩이굴리기' }

array 자료에 .sort()같은 것들을 붙일 수 있는 이유는? → 부모 유전자에 기록되어 있어서

1
let arr = [4, 2, 1]
2
let arr2 = new Array(4, 2, 1)
3
4
arr.sort()
5
arr2.sort() // [ 1, 2, 4 ]

모든 array 자료에 쓸 수 있는 함수를 추가하려면?

1
Array.prototype.추가할함수 = function () {}
2
let numArr = [4, 2, 1]
3
4
numArr.추가할함수()

3. 객체지향 개념

cf. 절차적 프로그래밍 vs 객체지향 프로그래밍

3.1 Imperative and Procedural Programming

ts_7_1

명령어과 절차형 프로그래밍

  • 객체지향의 반대되는 프로그래밍 패러다임
  • 데이터와 함수 위주로 구성하는 것
  • 맨 처음 실행되는 main함수
    • 그 아래 여러 함수들을 호출 가능
    • 함수 내부에서는 전역 변수의 데이터에 접근 가능
  • 대표적인 언어 : C

3.2 Object Oriented Programming

ts_7_2

객체지향 프로그래밍

  • 프로그램을 객체(Object)로 정의해서 객체들끼리 의사소통하고 디자인하고 코딩해나가는 패러다임
  • Object 단위로 만들기 떄문에 한 곳에서 문제가 생긴다면, 관련 Object만 수정하면 됨
  • 여러 번 반복되는 곳이 있다면, 관련 Object를 재사용할 수 있음
  • 무언가 새로운 기능이 필요하다면, 새로운 Object를 만들면 되서 확장성도 높음
  • 장점 : 생상성 높음, 높은 퀄리티 프로그램 작성 가능, 빠르게 기능 구현 가능, 유지보수성 향상

객체(object)속성(data)함수(행동)으로 구성됨

  • e.g. MediaPlay(음악 플레이어)
    • 속성(data)
      • music (음악 데이터)
    • 함수(function)
      • play(재생 기능)
      • stop(정지 기능)
  • 속성은 다른 말로 fields, property라고도 부름
  • 함수는 다른 말로 methods라고 부름
  • object를 정의하는 건 프로그래밍 언어마다 다르지만, 보통 class라고 부름

class(템플릿, 틀)

  • e.g. 붕어빵 만들기
    • class === 붕어빵 틀
    • object === 팥 붕어빵, 크림 붕어빵, …
  • e.g. 학생(student)
    • class === 학생 틀
    • object === 메시 학생, 호날두 학생
    • property === 학생 이름, 성적
    • method === 공부하다

4. 객체지향 원칙 4가지

  1. Encapsulation(캡슐화)
  2. Abstraction(추상화)
  3. Inheritance(상속성) : 상속을 이용해 코드 재사용성 높임
  4. Polymorphism(다형성) : 캡슐화, 추상황, 상속성을 이용해 구현

4.1 Encapsulation(캡슐화)

  • 절자지향 프로그래밍에서는 데이터와 함수 등이 여러가지가 섞여있음
  • 흩어져 있는 데이터와 함수들 중에 연관있고, 관련있는 것끼리 묶어 놓는 것캡슐화라고 함
  • e.g. 감기 캡슐약 안에 여러 성분들
    • 어떤 성분이 들어있는지 몰라도, 일반인들은 감기약을 먹으면, 감기가 호전된다는 것을 인식함
  • 외부에서 보일 필요가 없는 데이터를 잘 숨겨놓음으로써 캡슐화 가능

3.2 Abstraction(추상화)

  • 내부에 복잡한 기능을 모르고 다 이해하지 못해도,
  • 외부에서 간단한 Interface를 통해서 쓸 수 있는 것을 추상화라고 함
  • e.g. 커피머신 작동원리
    • 커피머신이 어떻게 내부 동작을 몰라도, 커피 타먹을 수 있음
  • 외부에서는 내부가 어떻게 구현되어 있는지 신경쓰지 않고,
  • 지정된 외부에 보여줄 함수(Interface)를 통해 객체를 사용 가능

3.3 Inheritance(상속성)

  • e.g. 커피 머신이라는 class가 정의되어져 있음
    • 상속을 이용해 잘 만들어진 커피머신의 데이터와 함수를 그대로 갖고 와서,
    • 필요한 기능을 더해서 다른 종류의 새로운 커피 머신을 만들 수 있음
  • 부모 classparent, super, base class라고 부르기도 함
  • 자식 classchild, sub, derived class라고 부르기도 함
  • 이 관계를 IS-A관계라고도 부름
    • e.g. espresso machin is a coffee machine
    • e.g. coffee brewer is a coffee machine

다른 상속 예

  • 개 is a 동물
  • 고양이 is a 동물
  • 돼지 is a 동물

다른 상속 예

  • HTMLElement is a Element
  • Element is a Node
  • Node is a EventTarget
  • 즉, Document, Element, Text는 모두 EventTarget을 상속받기 때문에, 모든 요소들이 이벤트가 발생할 수 있는 것임

extends 키워드를 사용해서 상속을 구현함


3.3.1 상속의 문제점

  • 족보가 꼬인다는 말처럼, 상속이 깊어질 수록 서로 간의 관계가 복잡해짐
  • 상속은 수직적인 구조를 짜는 것을 의미합니다.
  • 상속의 치명적 문제점어떤 부모 클래스의 행동을 수정하게 되면, 이를 상속받는 모든 자식 클래스에도 영향을 미침
  • 더군다나 TS에서는 1가지 이상 부모 클래스를 상속받을 수 없음

3.3.2 Composition

레고를 할 때, 필요한 부품을 모아서 조립하는 것처럼, 상속보다 Composition을 선호하셈

  • cf. React에서도 상속을 이용한 class 컴포넌트 방식보다는
    • Composition 방식의 함수형 컴포넌트를 권장함

3.4 Polymorphism(다형성)

  • cf. poly (=many) | morphi (=form)
  • polymorhism = 다양한 형태
  • e.g. 어떤 종류의 동물이든 소리를 내는 함수(makeSound())를 가짐
  • e.g. makeCoffee()라는 함수만 알면 어떤 종류의 커피머신이든, 커피 만들 수 있음

4. 필드값 타입지정

class 내부에는 모든 자식 object들이 사용가능한 속성을 만들 수 있습니다. 예를 들어, 모든 Person 클래스의 자식들에게 data라는 속성을 부여해주고 싶으면,

1
class Person {
2
// class 중괄호 안에다가 변수처럼 만들면 됨 (let, const 키워드 X)
3
// class 안에 만드는 속성을 필드(field)라고 부름
4
data: number = 0
5
}
6
7
// Person이 모든 자식에게 data = 0을 하나씩 복사해줌
8
let john = new Person()
9
let kim = new Person()
10
11
console.log(john.data)
12
console.log(kim.data)

5. constructor 타입지정

1
// class === object 복사기계
2
class Person {
3
constructor() {
4
// 'this.뭐시기'를 쓰려면 필드값을 만들어줘야 함
5
this.name = 'kim'
6
this.age = 20
7
// error TS2339: Property 'age' does not exist on type 'Person'.
8
}
9
}

필드 값으로 name, age가 미리 정의되어있어야 constructor 안에서도 사용가능합니다.

1
class Person {
2
// 필드 값이 정의되어 있어야 함
3
name
4
age
5
constructor(a: string) {
6
this.name = a // 'hello'라는 값이 this.name에 들어감
7
this.age = 20
8
}
9
}
10
11
new Person('hello')

생산되는 object마다 각각 다른 이름을 부여하고 싶을 때 유용합니다.


6. methods 타입지정

class 내부엔 함수를 입력할 수 있습니다.

1
class Person {
2
// Person클래스의 prototype에 추가
3
// 모든 Person의 자식들은 add 라는 함수를 이용가능
4
add(숫자: number) {
5
console.log(숫자 + 1)
6
}
7
}

6.1 문제

  • { model : '소나타', price : 3000 } object를 복사해주는 class 만들기
  • 복사된 object 자료들은 tax()라는 함수로 현재 object에 저장된 price의 10분의1을 출력

동작 예

1
let car1 = new Car('소나타', 3000)
2
console.log(car1) // { model : '소나타', price : 3000 }
3
console.log(car1.tax()) // 300

6.2 정답

1
class Car {
2
model: string
3
price: number
4
constructor(a: string, b: number) {
5
this.model = a
6
this.price = b
7
}
8
9
tax(): number {
10
return this.price * 0.1
11
}
12
}
13
14
let car1 = new Car('소나타', 3000)
15
console.log(car1) // { model : '소나타', price : 3000 }
16
console.log(car1.tax()) // 300

7. 상속 구현하려면 extends

1
class User {
2
x = 10
3
}
4
5
class NewUser extends User {
6
// User 클래스의 기능들 복붙해줌
7
}

새로운 NewUser class 만들 때, User에 있는 기능을 상속받아서 사용할 수 있습니다. 비슷한 class를 많이 만들 때, 사용합니다.


8. 접근제한자

접근 제한자를 선언한 속성과 메소드에 대한 접근 가능성은 다음과 같습니다.

접근 가능성publicprotectedprivate
클래스 내부
자식 클래스 내부
클래스 인스턴스

8.1 public

  • public이 붙은 속성은 자식 object들이 마음대로 사용하고 수정 가능
  • 필드값 같은걸 그냥 만들면 public이 기본값임
  • cf. public 키워드는 class 내의 prototype 함수에도 붙일 수 있음
1
class User {
2
// 자식 클래스나 클래스 인스턴스에서 접근 가능
3
public name: string
4
5
constructor() {
6
this.name = 'kim'
7
}
8
}
9
10
let 유저1 = new User()
11
유저1.name = 'park'

8.2 private

  • private 키워드를 붙이면 자식 object들이 수정 불가능
  • class 중괄호 안에서만 수정 및 사용가능
  • cf. 바닐라 JS에서도 속성옆에 # 붙이면 private 속성이 됨
1
class User {
2
public name: string
3
// 해당 클래스 이외의 위치에서는 접근 불가능
4
private familyName: string
5
6
constructor() {
7
this.name = 'kim'
8
let hello = this.familyName + '안뇽' // 가능
9
}
10
}
11
12
let 유저1 = new User()
13
유저1.name = 'park' // public 가능
14
유저1.familyName = 456 // private라 접근 불가 에러
  • cf. class 내의 함수에도 붙일 수 있음
  • private 부여된 속성을 class 밖에서 수정해야할 경우
    • private 속성을 수정하는 함수를 class 안에 만들어서 함수로 실행

8.2.1 private 속성을 바깥에서 수정

1
class Person {
2
public name: string
3
private familyName: string // private 속성
4
5
constructor() {
6
this.name = '메시'
7
this.familyName = 'kim'
8
}
9
// private 속성을 바깥에서 수정하려면,
10
// private 속성을 수정하는 함수를 class 안에서 만들어야 함
11
// class 바깥에서 함수를 이용하면 간접적으로 수정가능
12
changeSecret(lastName: string) {
13
this.familyName = lastName
14
}
15
}
16
17
let 집안 = new Person()
18
console.log(집안)
19
// 유저1.familyName = 'park'; // 그냥 private 속성을 수정하려면 에러남
20
집안.changeSecret('park')
21
console.log(집안)

외부에서 실수로 수정하면, 안되는 속성들은 private를 붙이면 됩니다. private 속성을 수정하려면, 매번 함수를 만들어서 수정해야하니 약간의 안전장치를 더해서 개발이 가능합니다. 이렇게 하면 버그를 예방할 수 있스빈다.


8.2.2 public, private 생략

public, private 키워드를 쓰면, constructor 안에서 this.name = name 이런걸 생략할 수 있습니다.

1
class Person {
2
name
3
constructor(name: string) {
4
this.name = name
5
}
6
}
7
let 사람1 = new Person('john')

위 코드와 아래 코드는 같은 역할을 하는 코드입니다.

1
class Person {
2
constructor(public name: string) {}
3
}
4
let 사람1 = new Person('john')

8.3 protected

  • protected 키워드를 붙이면, 자식 class까지 사용 가능
  • extends 된 class 안에서도 사용가능하게 약간 보안을 풀어줌
1
class User {
2
// protected
3
// - private와 동일하게 class 안에서만 사용이 가능
4
// - User의 자식들도 함부로 사용이 불가능
5
protected x = 10
6
}
7
8
class NewUser extends User {
9
// x 속성이 private라면 접근 불가능
10
// x 속성이 protected라면 접근 가능
11
doThis() {
12
this.x = 20
13
}
14
}
  • class 여러개 만들 때 class 끼리 공유할 수 있는 속성을 만들고 싶으면 protected
  • class 하나 안에서만 쓸 수 있는 속성을 만들고 싶으면 private

8.4 static 제한자

  • static 제한자를 붙이면, 자식에게 물려주지 않음
  • 클래스 안에 있는 변수와 함수는 모두 클래스로부터 새로 생성되는 **object(a.k.a. 인스턴스)**에 부여됩니다.
  • class로부터 생성되는 object가 사용할 필요가 없는 변수들을 만들어놓고 싶을 때 사용
  • 하나의 클래스에 하나만 적용됨
1
class User {
2
// x와 y는 User로 생성된 object들만 사용가능
3
x = 10
4
y = 20
5
}
6
7
let john = new User()
8
john.x // 가능
9
User.x // 불가능

class에 직접 변수나 함수를 부여하고 싶으면, static 키워드를 왼쪽에 붙여주면 됩니다.

1
class User {
2
static x = 10 // static을 쓰면 자식에게 안물려줌
3
y = 20
4
}
5
6
let john = new User()
7
john.x // 불가능
8
User.x // 가능
  • cf. 함수도 static 붙이기 가능
  • extends로 class를 복사할 경우 static 붙은 것들도 따라옴
  • cf. staticprivate, protected, public 키워드와 동시 사용가능

8.4.1 예시

1
class User {
2
static skill = 'js'
3
intro = User.skill + '전문가입니다'
4
}
5
6
const 철수 = new User()
7
console.log(철수) // User { intro: 'js전문가입니다' }
8
9
User.skill = 'python' // python으로 수정
10
11
const 민수 = new User()
12
console.log(민수) // User { intro: 'python전문가입니다' }

class 내부의 기본 변수같은걸 저렇게 수정할 일은 별로 없습니다. 수정하고 싶으면 private 쓰고, 그 다음에 수정함수를 만들어서 사용하는게 더 안전한 방법입니다.


8.5 접근제한자 예시

8.5.1 예시 1

1
class User {
2
// static가 붙으면 자식들은 사용 불가능
3
private static x = 10 // class 내부에서만 수정가능
4
public static y = 20 // class 내부 외부 상관없이 수정가능
5
6
protected z = 30
7
// private 키워드와 유사하게 class 내부에서만 사용 가능
8
// extends로 복사한 class 내부에서도 사용 가능
9
}

8.5.2 예시 2

1
class User {
2
private static x = 10
3
public static y = 20
4
5
// static은 class에 직접 부여되는 속성
6
// static 속성을 수정하거나 가져다 쓰고 싶으면, '클래스명.속성명'
7
static addOne(파라미터: number) {
8
User.x += 파라미터
9
}
10
11
static printX() {
12
console.log(User.x)
13
}
14
}
15
User.addOne(3)
16
User.addOne(10)
17
User.printX() // 23

9. 추상 클래스 : abstract

클래스 앞에 abstract를 붙이면,

  • 새로운 인스턴스를 만들 수 없고,
  • 반드시 상속을 통해서만 사용할 수 있습니다.

추상 클래스 내부의 메서드에도 abstract를 붙일 수 있습니다

  • 상속받은 클래스에서 반드시 추상 메서드의 내용을 만들어야만 정상 동작함
  • 단순히 이름만 선언해주고 구체적인 기능은 상속받은 쪽에서 만들어주는 것
1
// 추상 class
2
abstract class Car {
3
color: string
4
constructor(color: string) {
5
this.color = this.color
6
}
7
start() {
8
console.log('시작')
9
}
10
abstract doSomething(): void // 추상 메서드 내부의 메서드
11
}
12
13
// 💥 Error : 'Car' 클래스에서 상속된 추상 멤버 'doSomething'을(를) 구현하지 않습니다
14
class BMW extends Car {
15
constructor(color: string) {
16
super(color)
17
}
18
}

상속받은 쪽에서 doSomething 메서드를 선언해야 합니다.

1
abstract class Car {
2
color: string
3
constructor(color: string) {
4
this.color = this.color
5
}
6
start() {
7
console.log('시작')
8
}
9
abstract doSomething(): void // 추상 메서드
10
}
11
12
// 추상 클래스를 상속받은 자식 클래스
13
class BMW extends Car {
14
constructor(color: string) {
15
super(color)
16
}
17
// 상속받은 자식 클래스에서 추상 메서드 기능 구현
18
doSomething() {
19
alert(3)
20
}
21
}