[데브코스 FE] 2주차 세번째 수업 2024/07/24

2주차 화요일 수업에서는 

JS 문법 중 클래스, 상속, 접근제어자#, 생성자 함수 constructor에 대해 배웠다.

 

사실 이번에 배운내용은 나에게 너무 낯선 내용들이었다.

2년전 학교 수업때 자바에서 클래스를 마주친게 전부여서,

수업을 들으며 따라가고 이해하기 매우 벅찼다. 

 

수업때 어찌저찌 어느정도 이해했는데,

이번 블로그글으로 다시 복습하면서 내용을 상기시키고 정리해야겠다.

 


학습내용

  1. 클래스와 constructor
  2. 클래스 상속
  3. 캡슐화 by 접근제어자 #
  4. css 팁 ( transform 적용 못하는 경우 해결책 + 클래스 재사용)
  5. 수업 때 질문들 Q&A

1. 클래스와 constructor

 

JS에서는 ES6부터 Class 클래스가 도입되었다.

Class란,

객체를 생성하기 위한 템플릿을 정의하는 방법이다.

붕어빵 틀로 여러개의 붕어빵을 찍어내는 것처럼,

여러개의 객체를 생성하게 만들어주는 틀이 바로 클래스이다!

 

클래스를 사용하면 객체의 동작과 구조를 정의할 수 있다.

 

클래스의 구성요소는 다음과 같다.

 

 

  1. 클래스 선언: class 키워드를 사용하여 클래스를 정의한다.
  2. 생성자 (Constructor): 인스턴스 생성 시 호출되며, 초기 상태를 설정한다.
  3. 인스턴스 속성, 메서드: 인스턴스에서 속해있는 속성과 호출할 수 있는메서드이다.
  4. 정적 속성, 메서드 (Static Methods): 클래스 자체에 속하는 속성과, 이름을 통해 호출할 수 있는 메서드이다.
  5. 접근자 메서드 (Getters and Setters): 속성에 대한 접근과 설정을 제어한다.
  6. 상속 (Inheritance): 기존 클래스의 기능을 확장하여 새로운 클래스를 정의한다.

 

🔍1. Class 클래스 선언

 

'class 클래스이름' 형식처럼

'class' 키워드 뒤에 클래스 이름을 붙여준다.

class Person {
  // 구성요소들
}

 

🔍2. 생성자, constructor

constructor는 클래스의 인스턴스가 생성될 때 호출되는 메서드이다.

생성자 함수로도 불린다.

클래스와 인스턴스의 초기 상태를 설정하는 데 사용된다.

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

 

c나 c++, 기타 언어에서 변수의  초기화 값을 선언과 동시에 해주는 것처럼,

class의 인스턴스를 만들때 인자로 초기화값을 넘겨주면

constructor에서 클래스 초기 상태를 설정할 수 있다!

const Myclass = Person("ajin", 24) // 인스턴스

 

🔍3. 인스턴스 속성, 메서드

인스턴스 메서드는 클래스의 인스턴스에서 호출할 수 있는 메서드이다.

클래스가 붕어빵 틀이라고 한다면

인스턴스는, 붕어빵에 해당된다.

 

인스턴스 속성은 인스턴스에 속해있는 속성이다.

class Person {
  constructor(name, age) {
    this.name = name; // 속성
    this.age = age;
  }

  greet() { // 메서드
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

 

🔍4. 정적(static) 속성, 메서드

static으로 정의된 속성은 클래스 자체에 속한다.

따라서 인스턴스에서 접근할 수 없으며, 클래스 이름만을 통해 접근할 수 있다.

static으로 정의된 메서드도 클래스의 인스턴스가 아니라 클래스 자체에 속하기 때문에,

static 메서드는 클래스 이름만을 통해 호출할 수 있으며, 인스턴스에서 호출할 수 없다.

 

결론은, static은 인스턴스를 생성하지 않고도

바로 속성과 메서드를 꺼내쓸 수 있다!

class MathUtils {
  static pi = 3.14159;
   static add(x, y) {
    return x + y;
  }
}

console.log(MathUtils.pi); // 출력: 3.14159
console.log(MathUtils.add(5, 3)); // 출력: 8

 

🔍5. 접근자 메서드(Getter & Setter)

접근자 메서드는 객체의 속성에 접근하거나 속성을 설정하는 데 사용된다.

get과 set 키워드를 사용하여 정의한다.

 

getter는 말그대로 클래스 내부의 값을 '가져오는' 메서드이고,

setter는 말그대로 클래스 내부의 값을 '세팅한다, 설정한다'는 의미이다.

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  get area() {	// getter
    return this.width * this.height;
  }

  set dimensions({ width, height }) { // setter
    this.width = width;
    this.height = height;
  }
}

const rect = new Rectangle(10, 5);
console.log(rect.area); // 출력: 50 // getter 사용
rect.dimensions = { width: 15, height: 10 }; // setter 사용
console.log(rect.area); // 출력: 150

 

 

2. 클래스 상속

위의 1. 클래스 설명에서 

클래스를 구성하고있는 중요 요소에 '6.상속'도 있었다.

이어서 설명하겠다.

🔍6. 상속

클래스는 다른 클래스를 상속받을 수 있으며, 이를 통해 코드의 재사용성을 높일 수 있다.

주로 클래스들끼리 속성이나 메서드가 중복될때 사용한다.

 

상속은 extends 키워드를 사용하여 구현한다.

상속받은 클래스는 부모 클래스의 속성과 메서드를 사용할 수 있고,

자기 자신의 속성과 메서드로 부모와 별도로 가질 수 있다.

// 부모 클래스 Bungeoppang (붕어빵)
class Bungeoppang {
  constructor(filling) {
    this.filling = filling; // 붕어빵 속 재료
  }

  // 붕어빵을 만드는 메서드
  make() {
    console.log(`Making a Bungeoppang with ${this.filling} filling.`);
  }
}

// 자식 클래스 RedBeanBungeoppang (팥 붕어빵)
class RedBeanBungeoppang extends Bungeoppang {
  constructor() {
    super('red bean'); // 부모 클래스의 생성자를 호출하여 팥 속을 설정
  }
}

// 인스턴스 생성 및 메서드 호출
const redBeanBungeoppang = new RedBeanBungeoppang();

redBeanBungeoppang.make();  // 출력: Making a Bungeoppang with red bean filling.

 

상속을 공부하면서 'super'라는 키워드도 처음 알게 되었다.

super()는, 부모 클래스의 생성자를 사용할때 사용하는 메서드이다.

 

위 코드에서는 '붕어빵'이라는 부모 클래스를,

'팥 붕어빵'이라는 자식 클래스가 상속받아

자식 클래스에서 부모 클래스의 생성자 함수를 사용하여

붕어빵의 속재료를 설정해준다.

 

이때 'extends'키워드를 사용하여

자식 클래스인 '팥붕어빵' 클래스가 "앞으로 내 부모 클래스는 붕어빵으로 하겠어~"

라고 상속받겠다고 선언한 것도 확인할 수 있다.

 

3. 캡슐화 by 접근제어자 #

클래스의 속성들 중에는, 가끔씩 중요한 정보가 있어서,

외부 노출을 피하고 싶을때가 있다.

이럴때 접근제어자 '#'라는 우물정자를 사용하여

캡슐화를 진행한다.

 

캡슐화(Encapsulation)는 객체의 상태(데이터)를 외부에서 직접 접근하지 못하게 하고,

객체 내부에서만 접근할 수 있도록 하는 것이다.

JavaScript에서 # 기호를 사용하여 프라이빗(private) 필드를 정의하면,

해당 필드는 클래스 외부에서 직접 접근할 수 없다.

이는 데이터 무결성을 유지하고, 클래스의 구현 세부 사항을 숨기는 데 도움을 준다.

🔍캡슐화를 쓰기 전

// 카운터 만들기
class Counter {
    constructor(count) {
        this.count = count;
    }
    inc() {
        this.count++;
        console.log(this.count);
    }
}

const counter = new Counter(0);

counter.count;	// 0
counter.inc(); // 1씩 증가
counter.inc(); // 1씩 증가
counter.count // 2

 

캡슐화를 하기 전에는,

클래스 외부에서 >>counter.count<< 를 이용하여

클래스 내부 값 'count'에 직접 접근할 수 있다.

🔍캡슐화를 쓰고난 후

// 카운터 만들기
class Counter {
  #value;
  constructor(count) {
    if (count < 0 || typeof count !== "number" || isNaN(count)) {
      //throw new Error("초기값은 0 이상의 숫자만 가능합니다.");
      this.#value = 0;
    } else {
      this.#value = count;
    }
  }
  get value() { // getter
    return this.#value;
  }
  inc() {
    this.#value++;
  }
}

const counter = new Counter(0);

counter.inc(); // 1씩 증가
counter.inc(); // 1씩 증가
console.log(counter.value); // 🚨error!!!!!

 

접근제어자 '#'를 사용하여

클래스 내부의 'value' 속성에 캡슐화를 해주었다.

 

캡슐화를 진행해주면, 클래스 외부에서

>>counter.value<< 를 이용하여 접근해도

error가 발생하여 직접 접근하지 못한다.

 

캡슐화가 적용된 필드값에는 그러면 어떻게 접근하냐?!! 할 수 있는데

앞에서 배운 'getter' 메서드를 이용하여 접근하면 된다.

위 예시에서는 value() 메서드를 이용하면

클래스 외부에서도 value 값에 접근할 수 있다.

 

4. css 팁 / transform 적용 못하는 경우 해결책 / 클래스 재사용

🔍transform이 적용이 안돼요! 해결책이 있나요?

저번주 수업 중에서, 

강사님이 css transform을 적용 못하는 경우에 대해 설명해준 적이 있었다.

 

display:none 상태에서 display:block으로 없다가 있게 된 것은

transform 적용이 되지 않는 대표적인 경우이다.

 

이럴땐, 다음과 같이 하면 transform과 비슷하게 적용시킬 수 있다.

 

지금 예시는 홈페이지의 '헤더'부분의 메뉴들에 마우스를 올렸을 때만

소메뉴들이 보여야 하는 상황이다.

 

1. 소메뉴들은 마우스가 hover될때만 보여야하므로

visibility:hidden 과 opacity:0을 미리 적용해준다.

 

2. hover시에 소메뉴들이 보여야하므로

hover 상태일때 visibility:visible 로 변경해주고, opacity:1로 변경해준다.

 

.mainMenu > li:hover ul {
  visibility: visible;
  opacity: 1;
}
.mainMenu ul {
  ...
  visibility: hidden;
  opacity: 0;
  transition: 0.3s;
}

 

이때 부드러운 효과를 원한다면

transition도 0.3초 정도로 적당히 적용해주면 된다.

 

🔍 재사용이 가능한 효율적인 클래스도 있나요?

자주 반복되는 패턴이나 형태를 동일한 클래스이름로 관리하면

원할때마다 div에 동일한 클래스 이름을 붙여

레이아웃이나 스타일 등을 일관적이게 관리할 수 있다!

 

예를들어서,

5개의 <section>이 있는데

4개의 구역에서 container의 margin과 max-width가 동일하게 보인다면

 

class="sec1 mw"

class="sec2 mw"

...

 

처럼 동일한 클래스 이름을 동시에 부여하면 된다!

.mw {
  /* 1300 이상 넘어가면 메뉴 너비 유지*/
  max-width: 1300px;
  margin: auto;
  border: 1px solid lightgreen;
}

 

이 방법은 마치 tailwind css 방식과 비슷하다는 생각이 든다!.

tailwind css를 올해 프로젝트에서 써봤는데

class 이름으로 스타일을 부여하는 방식이었다.

아마 이러한 동작원리이지 않을까 하는 내 개인적인 유추이다!

 

5. 수업때 Q&A 

Q: 혹시 # << 에 대해서도 제가 이해가 잘되지않아서 어떤경우에 사용하는지 설명해주실수 있을까요?

더보기

A:  변수 앞에 붙인 #은 해당 속성이 클래스 내부에서만 사용되는 프라이빗 속성임을 나타내기 위해 붙인 것입니다. 자바스크립트 언어의 자체 기은은 아니고, 개발자 간의 약속(관례)이라고 생각하시면 될 것 같습니다. 

Q: css에서, align-items와 align-contents의 차이는 무엇인가요?

더보기

A: align - contents 는 수직축 기준으로 모든 줄의 요소들 정렬, align - items 는 수직직 축 하나에 대해서만 정렬됩니다. 줄이 1개인 경우, 1개의 줄이 수직으로 정렬되는 효과가 있습니다. 반면 줄이 여러개인경우, 여러개의 줄이 수직축 하나에 대해서만 정렬됩니다. 각 줄마다의 요소들이 전부 정렬되는 효과를 원한다면 align-contents를 사용하여야 합니다. 

Q: header에 fixed가 아닌 sticky를 붙이는 것은 어떤가요? sticky를 붙였을 때에는 main에 margin-top을 지정하지 않아도 되는 것 같은데 fixed를 사용하는 이유가 있을까요?

더보기

A: 현재는 헤더 위에 부모요소가 main밖에 없기때문에 sticky를 사용해도 상관없지만, 만약 부모요소가 생겨버린다면 sticky가 원하는대로 동작하지 않을 수도(헤더 상단 고정) 있습니다.  가장 가까운 부모 요소의 position:relative 에 대해서 sticky가 동작하기 때문입니다. 그리고 position fixed는 bottom:0도 되므로 상단에 계속 고정되어있어야하는 헤더의 목적에 더 맞는 방법이 됩니다.


자바스크립트의 클래스와 관련된 개념...

상속, getter & setter, static, instance, 접근제어자 # 등...

최다 처음보는 개념이었지만

한 번 더보니 수업시간에 헷갈렸던 내용들도 더 이해가 되었다!

 

그리고 평소에 css 코드를 작성하면서

너무 길어지는게 문제여서 어떻게 효율적으로 코드를 관리하나, 고민했었는데

컴포넌트에서 동일한 스타일이 반복되는 부분을 캐치하여

동일한 클래스 이름을 부여하면 효율성있게 코드를 작성할 수 있겠구나!

라는 점을 깨달았다.