instance 라고 부르는 객체를 다량으로! 안전하고! 정확하게! 만들어 내기 위한 [설계도]! 다른 비유로는 붕어빵을 대량 생산하는 [붕어빵틀]
클래스로 뭘 할 수 있는데??
constructor 를 이용하여 객체의 프로퍼티를 정해줄 수 있고, 그 클래스에서 사용할 수 있는 메서드를 마음대로 만들 수 있다!!
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(this.name + " 님 안녕하세요");
}
printMyAge() {
console.log(`내 나이는 ${this.age}살이에요`);
}
}
const person1 = new Person("김래준", "30"); // 이름이 김래준이고 나이가 30인 인스턴스 생성
person1.sayHello(); // 출력: 김래준님 안녕하세요
먼저 class 라는 키워드로 Person 이라는 이름의 붕어빵틀을 제작,
constructor에 name 과 age를 넣을것이라 입력,
sayHello 와 printMyAge 라는 메서드를 작성,
const person1 에 new 라는 키워드와 붕어빵틀명을 명시하고 인스턴스를 생성하여 할당.
대단히 편리하고 유용한것 치고는 사용법도 꽤 직관적이다 !
Q. 근데 유저가 인스턴스 생성할때 이상한 짓을 하면 어떡해?
name을 공란으로 둔다거나, age를 '서른살' 이렇게 적을 수도 있잖아?
A. 맞다. 사용자는 반드시 우리가 열심히 만든 코드를 부술 방법을 찾아 내고야 말 것이다. 그래서 Getter 와 Setter 가 필요한 것이다.
Setter 는 입력된 값의 유효성을 검토할 수 있게 해준다.
Getter에 대해서는 후술한다.
Getter & Setter 를 사용한 예시
class Car {
constructor(modelName, modelYear, type, price) {
this._modelName = modelName;
this._modelYear = modelYear;
this._type = type;
this._price = price;
}
get modelName() {
return this._modelName;
}
//setters로 입력값에 대한 검증이 가능하게 한다
set modelName(value) {
//유효성검사
if (value.length <= 0) {
console.log("[오류] 모델명이 입력되지 않았습니다");
return;
} else if (typeof value !== "string") {
console.log("[오류] 입력된 모델명이 문자형이 아닙니다");
return;
}
this._modelName = value;
}
//~~~~~~~~~~~~~~~~~~~~(중략)~~~~~~~~~~~~~~~~~~~~~~~
makeNoise() {
console.log(`${this._modelName}: 빵!`);
}
printModelYear() {
console.log(`${this._modelName}은 ${this._modelYear}년식 입니다.`);
}
}
코드가 너무 길어져서 modelName 에 대한 getter와 setter 한쌍만 남겨놓고 나머지는 생략했다.
setter가 하는일은 명확해 보인다. 이상한 값을 걸러내는것. 그리고 이상한 값이 입력되었을때는 return
으로 코드가 그 아래로 진행되지 않도록 막는것.
마지막으로 유효한 값일 경우에는 그것을 modelName에 할당하는것.
중요)
getter 와 setter 쓸때는 this에 접근하는 property 이름 앞에 _를 붙이는게 약속이다.
getter 와 setter를 쓸때 이걸 붙이지 않으면 Maximum call stack size exceeded
오류가 발생한다. 이유는 setter에서 무한 순환에 빠지기 때문.
Q. 이제 Getter랑 Setter 사용법도 알겠고, Setter가 왜 필요한지도 확실히 알았다. 근데 여전히 Getter 가 뭘 하는지는 모르겠다. Getter 가 왜 필요한거지?
이상한 입력값을 걸러주는 setter와 달리 getter가 하는 일은 아직 명확하지 않아 보인다. 그저 property 를 리턴하기만 할 뿐이라면 car1._modelName
으로도 접근할수 있지 않은가?
실제로 위 예시 코드에서 get modelName(){}
부분을 통째로 주석 처리해도 다른 객체 다루듯이 ._modelName
으로 접근이 가능함을 확인했다. 그럼 Getter 는 왜 존재하지??
getter 가 필요한 이유를 알려면 배경지식이 조금 필요하다.
- 상술한 _
붙이기는 프로퍼티를 private 로 만들겠단 의미이다.
- 그런데 _
는 프로그래머들끼리 약속한 것일뿐 실제로 JS가 실행될때 유의미한 차이를 만들지는 않는다. 그냥 '이거 직접 access 해서 건들지마' 라고 다른 프로그래머에게 알려주는 마커 같은 거다.
- 그래서 private 프로퍼티라고는 하지만 사실 전혀 private 하지 않고 그렇기 때문에 car1._modelName
으로 getter 없이 접근이 가능했던것!
-그런데 ES6 에서 드디어 진짜 private 프로퍼티를 만드는 것이 가능해졌고 그건 #
를 붙여주는 것으로 실현된다!
이제 Getter가 필요한 이유를 설명할 수 있다.
#
를 이용하여 작성된 private property는 getter를 이용하지 않으면 아예 접근조차 할 수 없기 때문이다.
#
를 이용한 private property가 설정된 예시
class Car {
#modelName;
#modelYear;
#type;
#price; // 이렇게 class body 안에서 declare 해줘야만 한다
constructor(modelName, modelYear, type, price) {
this.#modelName = modelName;
this.#modelYear = modelYear;
this.#type = type;
this.#price = price;
}
get modelName() {
return this.#modelName;
}
set modelName(value) {
if (value.length <= 0) {
console.log("[오류] 모델명이 입력되지 않았습니다");
return;
} else if (typeof value !== "string") {
console.log("[오류] 입력된 모델명이 문자형이 아닙니다");
return;
}
this.#modelName = value;
}
//~~~~~~~~(후략)~~~~~~~~~~~~
const car1 = new Car("Taycan", "2023", "EV", 5000); //인스턴스 생성
이제 여기선 정말로 getter가 필요해졌다. getter 덕분에 car1.modelName
을 사용할 수 있는 것이며, getter 부분을 주석처리하면 console.log(car1.modelName);
찍어봐도 undefined
가 나온다.
마치 CSS에서 부모요소의 style 을 자식이 상속 받듯이 class 도 자식을 만들고, 자식이 부모의 메서드와 constructor 를 상속받을 수 있다.
Class 상속 간단 예시
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} says!`);
}
}
class Dog extends Animal {
//이렇게 부모 메서드를 재정의 하는걸 overriding 이라고 함.
speak() {
console.log(`${this.name} barks!`);
}
}
const cutePuppy = new Dog("didi");
cutePuppy.speak(); // didi barks!
class Dog 는 Animal 의 자식이다. 이를 나타내기 위해 extends
키워드를 사용,
부모의 constructor를 그대로 재사용 하기 때문에 Dog 내에는 constructor 를 생략,
부모의 speak 메서드를 override 하여 원하는 식으로 변형.
class Car {
constructor(modelName, modelYear, type, price) {
this.modelName = modelName;
this.modelYear = modelYear;
this.type = type;
this.price = price;
}
class EV extends Car {
//부모 constructor를 그대로 쓸수 없다(전기차니까 type이 필요없음)
//그래서 여기선 constructor 를 써줘야한다.(type 안쓴거 주의! 대신 chargeTimed이 생겼다)
constructor(modelName, modelYear, price, chargeTime) {
// Car(부모 클래스)에게도 알려주기!
super(modelName, modelYear, "e", price);
//super에 부모 constructor 를 그대로 가져오되, type만 e로 고정시켰다.
this._chargeTime = chargeTime;
makeNoise() {
console.log(`${this.modelName}: 빵!`);
}
printModelYear() {
console.log(`${this.modelName}은 ${this.modelYear}년식 입니다.`);
}
}
set chargeTime(value) {
this._chargeTime = value;
}
get chargeTime() {
return this._chargeTime;
}
}
const eleCar1 = new EV("테슬라", "2023", 9000, 60);
마지막 줄에서 new EV 만드는걸 주목해서 보자.
부모 constructor 에 들어갈 요소중 type을 빼고, 대신 충전시간 항목을 적었다.
EV class의 인스턴스도 부모 class 의 메서드를 전부 쓸 수 있다.
super
키워드를 빼먹지 않도록 조심하자.
지금까지는 class를 인스턴스(객체)를 생성하는 용도로만 사용했다.
하지만 class를 정적 메서드를 만들고 사용하는 데도 쓸수 있다.
static method 예시
class Calculator {
static add(a, b) {
console.log("[계산기] 더할게요");
return a + b;
}
static subtract(a, b) {
console.log("[계산기] 뺄게요");
return a - b;
}
}
console.log(Calculator.add(3, 5)); //
console.log(Calculator.subtract(3, 5));
// 인스턴스화 시키지 않았지만 스태틱 이라는 키워드로 메소드를 바로 호출할수 있었다!
Document object model의 줄임말이다.
DOM은 브라우저에 기본적으로 내장되어 있는 API 중 하나이다 ! 라고 말해도 틀린 말은 아니다.
그럼 API는 무엇인가? API는 다른 시스템에서 제공하는 기능을 사용할 수 있도록 도와주는 중간자 역할이다. 예를 들어, 날씨 정보를 이용하고 싶으면 날씨 서비스의 API를 호출하여 날씨 정보를 받아오는것. 이런 의미에서 DOM과 관련된 API는 DOM 객체에 접근하고 제어할 수 있도록 도와준다.
JS의 런타임에는 두가지가 있다. 하나는 브라우저, 다른 하나는 Node. DOM은 브라우저 환경에서 쓸 수 있다. node 환경에서는 사용 불가.
DOM은 노드를 갖는다. 그리고 node는 속성과 메서드를 갖는다.
DOM은 모두 노드로 이루어져있기 때문에 전부 부모노드-자식노드 관계로 정리할 수 있다.
지금 왼쪽 콘솔창에서 한게 다 DOM 조작이다.
새로운 항목을 만들 수도 있고, 그 항목을 원하는 위치에 넣을 수도 있다(append)
존재하는 항목의 내용을 바꿀수도 있고(innerText,innerHTML)
속성을 변경할 수도 있다(setAttribute)
지난번 팀 프로젝트때 DOM 조작으로 대상의 class name을 설정했다 지웠다 하면서 그 대상에 적용될 CSS 속성을 다르게 했던것도 같은 맥락이다.(night mode 기능)
너무 유용하고 멋진걸 배웠는데 심지어 그게 나름대로 이해가 가서 정말 기쁘다! 클래스로 객체를 슉슉 만들어내는건 분명 엄청 멋진 일이다.
중간에 getter가 왜 존재하는지 의문이 생겨서 튜터님에게도 물어보고 검색도 해보고 해서 getter 의 존재 이유 뿐만 아니라 #를 이용한 private 필드의 생성까지 다 알게 된 것은 무척이나 큰 소득이다.
class 이외에도 closure 와 콜백지옥, promise, generator, async/await 등도 배웠는데 이들은 아직 스스로 설명이 가능할 정도로 제대로 이해하지 못했다. 당연히 이들에 대해서도 최대한 빠르게 공부해서 나름대로 정리하려는 시도를 할 것이다.
학습과 별개로 하루종일 너무 졸렸는데 체력관리에 좀더 신경 쓰지 않으면 안되겠다는 생각이 든다. 좀더 일찍 자야지!