CS/클린코드(cleancode)

10장. 클래스 - 자바스크립트 클린코드

rachel_13 2022. 11. 20. 16:26

프론트엔드에서 클래스란

모던 자바스크립트, ES6부터 객체 지향 프로그래밍을 위해 고안된 개념으로 프로토타입을 기반으로 한다.

클래스에 대해 간단하게 정리 해보자면,

 

- 클래스틑 class 키워드를 사용하여 정의한다.

- 클래스에서 정의할 수 있는 메서드는 constructor(생성자), 프로토타입 메서드, 정적 메서드가 있다.

- 클래스는 인스턴스를 생성하기 위한 생성자 함수이며, new 연산자와 함께 호출된다.

- 인스턴스는 클래스로부터 생성되는 객체이다.

- constructor : 인스턴스를 생성하고 초기화하는 메서드이다. (최초로 정의하는 곳이라고 이해하자)

- 프로토타입 메서드 : 인스턴스의 프로토타입에 존재하는 메서드. 인스턴스에서 상속받아 사용할 수 있다.

- 정적 메서드 : 인스턴스 생성을 하지 않아도 사용 가능한 메서드 (static 키워드 사용)

1. 클래스 체계

- 캡슐화

변수나 유틸리티 함수는 가능한 공개하지 않는 편이 낫다.

그렇다고  꼭 그래야 한다는 법도 없긴 하지만, 캡슐화를 풀어주는 것은 언제나 최후의 수단이다.

java에서는 public(공개), private(비공개-외부에서 접근 비허용), protected(상속된 자식에서만 허용, 테스트 코드에서 접근 허용) 이렇게 3가지로 변수,함수를 선언할 수 있다.

 

자바스크립트에서는 클래스 필드(field)를 통해 접근 제어를 수행할 수 있다. 

그러나 주의할 점은 자바스크립트의 경우 완전한 캡슐화는 어렵다는 점이다.

  • 클래스 필드(field) : 클래스 기반 객체 지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티

private 필드로 선언하기 위해서는 선두에 '#'을 붙여준다.

class Person {
    //private 필드 정의
    #firstname;
    #lastname;
    #age;
    
    constructor(firstname, lastname, age){
      this.#firstname = firstname;
      this.#lastname = lastname;
      this.#age = age;
    }
    
    //접근자 프로퍼티
    get name(){
      return (this.#firstname+this.#lastname).trim();
    }
    
  }
  
  const me = new Person('John ',' Doe', 20);
  console.log(me.name) //John Doe

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields

 

Private class features - JavaScript | MDN

Class fields are public by default, but private class members can be created by using a hash # prefix. The privacy encapsulation of these class features is enforced by JavaScript itself.

developer.mozilla.org

2. 클래스는 작아야 한다

- 응집도

하나의 목적을 가진 코드가 흩뿌려저 있을 때 응집도를 높여서 묶어두어야 한다.

응집도가 높다 = 클래스에 속한 메서드가 변수가 서로 의존하며, 논리적인 단위로 묶인다.

React에서 응집도를 높이기 위해서는 (*)선언형 프로그래밍을 해주는 것이 좋다.

왜냐하면, 디테일은 숨겨두고, 덩어리만 봐도 파악되는 핵심 코드들로 구성이 되어있다면, 코드의 의미를 빠르게 파악하고 넘어갈 수 있기 때문이다. 또한 선언형 프로그래밍은 '무엇' 만 바꿔주면 되어서, 재사용에도 용이하다.

*선언형 프로그래밍 :  무엇(what)에 집중하는 프로그래밍, 비선언형 프로그래밍은 캡슐화하여 명령형 프로그래밍(how)을 선언형으로 만들어준다.

 

TIP: 핵심데이터와 세부구현 나누기

다음은 한 컴포넌트 안에서 지속적으로 dispatch 함수를 이벤트에 걸어주는 코드를 재사용성이 높은 함수로 리팩토링한 것이다.

각 클린 이벤트마다 action명만 다르고 dispatch 하는 것은 동일하여서, actions명을 매개변수로 받는 함수로 만들었다.

버튼의 클릭이벤트의 걸리는 함수를 "선언"하였고, "무엇"은 actions명에 따라 바뀜으로써 재사용성을 높였다.

  const checkRewardStateAndFetchData = (rewardState: (data) => any) => {
    dispatch(
      rewardState(data)
    );
  };
 <div
   css={styles.btnRoundStyle(play)}
   onClick={() => checkRunningStateAndFetch(rewardStart)}
/>

 

- 단일 책임 원칙

하나의 함수는 하나의 일만 한다!

  • 이를 위해서는 한 가지 일만 하는 명시적인 함수명을 사용하는 것도 중요하겠다.
  • 한 가지 일만 하는 기능성 컴포넌트
    • ex) 버튼 클릭 시 이벤트에 여러가지 기능이 들어가 있는 경우

(before) 

{EXERCISE_TYPES.map((exercise) => (
          <div
            key={exercise.id}
            onClick={() => {
              dispatch(changeProcess("running"));
               if (exercise.id === 1) {
                 dispatch(getRewardType("P"));
               } else {
                 dispatch(getRewardType("G"));
               }
            }}
          >
            <img
              src={exercise.imgUrl}
              alt="exercise"
            />
            <div>
              <h2>{exercise.title}</h2>
              <p>{exercise.subtitle}</p>
            </div>
          </div>
))}

(after)

{EXERCISE_TYPES.map((exercise) => (
    <ExcerciseTab exercise={exercise} key={exercise.id}/>;
))}
const ExerciseTab = (exercise)=> {
    const isProcessChange = (process) => {dispatch(changeProcess(process))};
    const whatIsRewardType = (type) => {dispatch(getRewardType(type))};
    const exerciseType = {exercise.id === 1 ? "P" : "G"}


    return (
            <ChageProcess onClick={isProcessChange('start')}>
            //dispatch로 process를 변경하는 세부 구현은 상위 컴포넌트에 숨겨둔다.
              <div
                onClick={() => {
                //그리고 버튼 클릭시에는 API 콜에만 신경쓸 수 있도록 한다.
                  whatIsRewardType(exerciseType);
                }}
              >
                <img
                  src={exercise.imgUrl}
                  alt="exercise"
                />
                <div>
                  <h2>{exercise.title}</h2>
                  <p>{exercise.subtitle}</p>
                </div>
              </div>
           </ChageProcess>
)};

이는 중요한 개념만 남기고 추상화 하는 것과도 연관이 있다.

추상화 시에도 유의할 점이 추상화 단계가 널뛰면 파악하기 어려워진다.

같은 레벨이라고 생각하고 접근했다가 생각지도 못한 로직을 만날 수도 있기 때문에, 이는 바람직하지 않다.

 

위의 코드도 추상화 단계가 맞는지 전체로직에서 점검해 볼 필요가 있다.

결국에는 중복을 줄이고, 재사용성을 높이면 
추상화, 캡슐화, 가독성, 선언적 프로그래밍이 가능해지는 것이다.

3. 변경하기 쉬운 클래스

- 변경으로부터 격리한다.

  • 상세한 구현에 의존하는 코드는 테스트가 어렵다.
  • 인터페이스와 추상 클래스를 사용해 구현에 미치는 영향을 최소화해야 한다.
  • 확장에 개방적이고, 수정에 폐쇄적인 클래스를 만들어야 한다.
  • 테스트가 가능할 정도로 시스템 결합도를 낮추면, 유연성과 재사용성이 올라간다.

 

 

참고: https://youtu.be/edWbHp_k_9Y  → 너무 잘 설명되어 있다. 강추..!

'CS > 클린코드(cleancode)' 카테고리의 다른 글

12장. 창발성(emergence)  (0) 2022.11.28
9장. 단위테스트  (0) 2022.11.28
7장. 오류 처리 - 자바스크립트 클린코드  (0) 2022.11.06
6장. 객체와 자료구조  (0) 2022.11.06
5장. 형식 맞추기  (0) 2022.10.30