[Typescript] Access modifier, readonly, static

이한슬·2024년 1월 20일

Typescript

목록 보기
6/7

Access modifier

접근 제한자는 클래스 기반의 객체 지향 프로그래밍에서 사용되는 개념이다. public, private, protected 3가지 접근 제한자가 존재한다.

  • public
    public은 어디에서나 접근이 가능하도록 해주며, 타입스크립트에서는 defaultpublic으로 설정되어 있기 때문에 public을 생략해도 자동으로 public으로 인식된다.
class Cafe {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }

  public hello() {
    console.log("hello");
  }
}

class Starbucks extends Cafe {
  constructor(name: string) {
    super(name);
  }

  public namePrint() {
    console.log(this.name);
    super.hello();
  }
}

const starbucks = new Starbucks("starbucks");
starbucks.namePrint();

출력

starbucks
hello

name, hello(), namePrint() 모두 public으로 선언되어 있다.
namePrint()를 살펴보면, namehello()public으로 선언되었기 때문에 외부에서도 자유롭게 접근이 가능했다.
namePrint()public으로 선언되어 있기 때문에, 외부에서 Starbucks 인스턴스를 생성하여 starbucks.namePrint()와 같은 접근이 자유롭게 가능하다.

class Cafe {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }
}

class Starbucks extends Cafe {
  constructor(protected name: string) {
    super(name);
  }
}

위와 같이 public이었던 name을 protected나 private로 오버라이딩 할 수 없다.

  • protected
    protected는 속한 클래스와 이를 상속한 하위 클래스에서 접근이 가능하며, 이외의 외부에서는 접근할 수 없다.
class Cafe {
  protected name: string;

  constructor(name: string) {
    this.name = name;
  }

  protected hello() {
    console.log("hello");
  }
}

class Starbucks extends Cafe {
  constructor(name: string) {
    super(name);
  }

  protected namePrint() {
    console.log(this.name);
    super.hello();
  }
}

const starbucks = new Starbucks("starbucks");
starbucks.namePrint();

출력

error TS2445: Property 'namePrint' is protected and only accessible within class 'Starbucks' and its subclasses.

이번에는 name, hello(), namePrint() 모두 protected로 선언해보았다.
namePrint()를 살펴보면, namehello()protected로 선언되었기 때문에 이들이 속한 클래스인 Cafe 클래스를 상속한 하위 클래스인 Starbucks 클래스의 namePrint 메소드에서 this.name, super.hello()와 같은 접근까지는 가능했다.
그러나 namePrint()protected로 선언되어 있기 때문에, 외부에서 Starbucks 인스턴스를 생성하여 starbucks.namePrint()와 같은 접근을 하려는 것이 문제가 된 것이다.
즉, Starbucks 클래스에서 protected로 선언된 namePrint 메소드를, protected의 범위를 벗어난 외부에서 Starbucks 클래스 인스턴스를 생성하여 호출하려 했기 때문에 에러가 발생했다.

class Cafe {
  protected name: string;

  constructor(name: string) {
    this.name = name;
  }
}

class Starbucks extends Cafe {
  constructor(public name: string) {
    super(name);
  }
}

위와 같이 protected였던 name을 Starbucks 클래스에서 public으로 오버라이딩 하게 되면 Starbucks 클래스의 name은 public으로 적용된다.
반대로 private로 오버라이딩 하는 것은 불가능하다.

  • private
    private는 해당 클래스의 내부에서만 접근이 가능하다.
class Cafe {
  private name: string;

  constructor(name: string) {
    this.name = name;
  }

  private hello() {
    console.log("hello");
  }
}

class Starbucks extends Cafe {
  constructor(name: string) {
    super(name);
  }

  private namePrint() {
    console.log(this.name);
    super.hello();
  }
}

const starbucks = new Starbucks("starbucks");
starbucks.namePrint();

출력

error TS2341: Property 'name' is private and only accessible within class 'Cafe'.
error TS2341: Property 'hello' is private and only accessible within class 'Cafe'.
error TS2341: Property 'namePrint' is private and only accessible within class 'Starbucks'.

protected가 이해되었다면 private는 더 쉽다. 이번에는 name, hello(), namePrint() 모두 private로 선언되었다.
namePrint()를 살펴보면, (1) 먼저, Cafe 클래스에서 private로 선언된 name을 외부에서 this.name으로 접근하려고 했기 때문에 에러가 발생했다.
(2) Cafe 클래스에서 private로 선언된 hello 메소드를 외부에서 super.hello()로 접근하려고 했기 때문에 에러가 발생했다.
(3) Starbucks 클래스에서 private로 선언된 namePrint 메소드를 외부에서 Starbucks 클래스 인스턴스를 생성하여 starbucks.namePrint()로 접근하려고 했기 때문에 오류가 발생했다.
즉, 클래스 내부 이외의 곳에서 접근하려고 하면 모두 에러를 발생시켜 3곳 모두 에러가 발생된 것이다.

class Cafe {
  private name: string;

  constructor(name: string) {
    this.name = name;
  }
}

class Starbucks extends Cafe {
  constructor(protected name: string) {
    super(name);
  }
}

위와 같이 private였던 name을 protected나 public으로 오버라이딩 할 수 없다.

표로 정리해보면 다음과 같다.

본인 클래스하위 클래스클래스 외부
public
protected
private

readonly

값을 확인하고는 싶지만 수정이 되지 않아야 한다면 readonly를 사용할 수 있다.
접근제한자 readonly 속성이름의 형태로 사용할 수 있으며, readonly가 붙은 속성은 수정이 불가능한 읽기 전용이 된다.

class Cafe {
  constructor(
    public readonly name: string,
    public readonly discount: number
  ) {}
}

const starbucks = new Cafe('starbucks', 10)
starbucks.name = 'twosome'

출력

error TS2540: Cannot assign to 'name' because it is a read-only property.

Cafe 클래스의 속성들을 모두 readonly로 선언하여 마지막과 같이 name 속성을 수정하려고 하면 오류가 발생한다.
다만, public으로 접근제한자가 설정되었기 때문에, console.log(starbucks.name)과 같은 것은 가능하다.

static

일반적으로는 클래스의 속성이나 메소드에 접근하기 위해서는 new를 이용하여 객체를 생성하여 접근하게 된다.
그러나 static을 사용해서 객체를 생성하지 않고도 속성이나 메소드에 접근할 수 있다.
접근제한자 static 변수명 or 메소드명으로 정적으로 속성이나 메소드를 정의할 수 있다.

class Cafe {
  public static cafeName: string;

  constructor() {
    Cafe.cafeName = "starbucks";
  }

  public static hello() {
    console.log("hello");
  }
}

Cafe.cafeName = "twosome";
Cafe.hello();

출력

hello

static을 사용하여 cafeNamehello()를 정적으로 선언하였기 때문에 Cafe 객체를 생성하지 않고도 Cafe.cafeName, Cafe.hello()과 같이 접근이 가능하다.
다만, static 변수의 경우 constructor 안에서 선언이 불가능하며, constructor 안에서도 this.cafeName이 아닌 Cafe.cafeName처럼 접근해야 한다.

정적 메소드에서는 정적 변수만 접근할 수 있다. 정적 메소드에서 일반 변수를 접근하려고 하면 오류가 발생한다.

class Cafe {
  public cafeName: string;

  constructor() {
    this.cafeName = "starbucks";
  }

  public static hello() {
    this.cafeName = "twosome";
  }
}

Cafe.hello();

출력

error TS2339: Property 'cafeName' does not exist on type 'typeof Cafe'.
profile
궁금하면 일단 먹어보는 소프트웨어 전공생

0개의 댓글