[클린코드 TS] 객체와 자료구조

moonee·2021년 7월 31일
0

클린코드

목록 보기
3/5

1. Getter 와 Setter 사용하기

타입스크립트에서 getter/setter를 지원해준다.
그렇다면 왜 getter와 setter를 사용해서 데이터에 접근해야하는걸까 ?

  • 객체의 무결성을 보장하기 위해서
    무결성이란? 객체에 저장된 데이터 값과 그것이 표현하는 현실 세계의 실제 값이 일치하는 정확성을 의미한다.
  • Setter : 예를 들어 Car 객체의 Speed 데이터는 0 이상이어야 한다라는 조건이 있는데, Speed를 외부에서 -1 로 변경 할 수 있다면 이는 객체의 무결성이 깨지는 것이다.
  • Getter : 외부에서 바로 데이터에 접근하지 않고 getter를 이용하여 객체에서 가공된 데이터를 반환해줄 수 있다.

Bad

type Car = {
  speed: number;
  // ...
}

const value = 100;
const car: Car = {
  speed: 0,
  // ...
};

if (value < 0) {
  throw new Error('Cannot set negative speed.');
}

account.speed = value;
  • 검증 로직에 대한 명세가 추가 될 경우 계속해서 전체 코드를 변경해줘야한다.

Good

class Car {
  private carSpeed: number = 10;

  get speed(): number {
    return this.carSpeed;
  }

  set speed(value: number) {
    if (value < 0) {
      throw new Error('Cannot set negative speed.');
    }

    this.carSpeed = value;
  }

  // ...
}
const car = new Car();
account.car = 100;
  • 검증 로직에 대한 명세가 추가되어도 setter 영역만 수정하면 된다.


2. private / protected 멤버 객체 생성하기

  • 타입스크립트에서는 public private protected 접근 제어자를 지원한다.

  • public : 모든 class에서 접근 가능

  • private : 자신이 포함된 class에서만 접근 가능

  • protected : 상속받은 class와 자신이 속한 class에서만 접근 가능

그렇다면 왜 접근제어자를 사용해야할까?

  • 보안을 높이기 위해서이다. 사용자의 비밀번호나, 비밀번호 수정과 같이 유저 정보를 수정하는 변수 혹은 메서드를 여러 클래스에서 접근 가능할 경우 보안에 취약하다. 따라서 접근 제어자를 설정하여 외부에서 접근하지 못하도록 설정해줘야 한다.

Bad

class Circle {
  radius: number;
 
  constructor(radius: number) {
    this.radius = radius;
  }

  perimeter() {
    return 2 * Math.PI * this.radius;
  }

  surface() {
    return Math.PI * this.radius * this.radius;
  }
}

Good

class Circle {
  constructor(private readonly radius: number) {
  }

  perimeter() {
    return 2 * Math.PI * this.radius;
  }

  surface() {
    return Math.PI * this.radius * this.radius;
  }
}


3. 불변성지키기

  • readonly : 타입스크립트의 타입 시스템은 interface/class의 개별 속성을 readonly로 표시 할 수 있다. readonly로 표시된 속성은 변조가 불가능하고 참조만 가능하다.

Bad

interface Config {
  host: string;
  port: string;
  db : string;
} 

Good

readonly host:string;
readonly port:string;
readonly db:string;

ReadOnlyArray

  • 배열의 경우는 ReadOnlyArray<T> 를 사용하여 읽기 전용 배열 생성 가능
  • push() fill()과 같은 배열의 변경을 막는다.
const arr = ReadonlyArray<number>=[1,3,5];
arr = [] // 에러
arr.push(100) // 에러

Const Assertion

  • 타입스크립트는 let, const 에 대한 타입 추론을 아래와 같이 제공한다.
 let hello = 'hello'; // 추론된 타입 'String'
 const world = 'world'; // 추론된 타입 'world'
  • 왜 이렇게 추론이 되는가? let 변수에는 다른 값을 대입 할 수 있기 때문이고, const 변수에는 다른 값을 대입 할 수 없기 때문이다.
  • 그렇다면 객체는 어떻게 추론 될까?
  const obj = {
  	hello:'hello'
  } 
  // const obj = { hello:string } 
  • 객체 자체가 const 로 선언되었음에도 불구하고 객체 내부 프로퍼티에 대한 추론은 let 과 같이 진행된다. 당연히 객체 내부 프로퍼티는 수정가능하기 때문이다.
  • 따라서 객체타입의 경우 불변성의 문제가 발생한다. 외부에서 프로퍼티를 수정할 수 있기 때문이다. 이를 보완해주는 것이 Const Assertion 이다. Const Assertion 을 사용하면 let이나 참조형const 변수 처럼 타입 추론을 한다.
  const obj = {
  	hello:'hello'
  }  as const;
  
   // const obj = { hello:'hello' } 
  obj.hello = 'world' // 에러 
  


4. Type vs Interface 올바르게 사용하기

Type을 사용하면 좋은 경우

  • 합집합 혹은 교집합이 필요 할 때
  type EmailConfig = { // ... }
  type DbConfig = { //... }
  
  type Config = EmailConfig | DbConfig;

Interface를 사용하면 좋은 경우

  • extends 혹은 implements가 필요 할 때
  interface Shape { //... }
  
  interface CircleShape extends Shape { //... } 
  class Circle implements CircleShape { //... } 


참고

profile
기록

0개의 댓글