본문 바로가기

접근 제어자

public과 private, protected가 클래스 안과 상속한 클래스, 바깥 코드에 각각 어떤 접근을 허용하는지 예제로 비교하고, 타입스크립트의 private와 자바스크립트의 # 비공개 필드의 차이, 매개변수 속성, 공개 메서드의 값 검사를 다룹니다.

은행 계좌 객체의 잔액을 바깥 코드가 아무렇게나 바꿀 수 있다면, 입금과 출금에 넣어 둔 검사는 쉽게 우회됩니다. 그래서 객체의 내부 상태는 감추고, 바깥에는 정해진 방법으로만 상태를 바꾸는 메서드를 공개합니다. 이렇게 내부를 감추고 필요한 부분만 드러내는 설계를 캡슐화(encapsulation)라고 합니다.

타입스크립트는 클래스 멤버마다 public, private, protected 가운데 하나의 접근 제어자를 붙여, 그 멤버를 어디서 직접 쓸 수 있는지 정합니다. 이 글은 세 제어자를 차례로 보고, 타입스크립트의 private와 자바스크립트의 # 비공개 필드가 어떻게 다른지, 접근을 막는 것과 값을 검사하는 것이 왜 별개인지 확인합니다. 예제는 ts-first 프로젝트에 access.ts를 만들고 include를 그 파일로 바꾸어 확인합니다.

public

public은 어디서나 접근할 수 있는 멤버이며, 제어자를 적지 않은 멤버도 모두 public입니다. 자바스크립트 클래스의 기본 동작과 같아서, 바깥 코드가 객체를 쓰는 방법, 곧 객체의 공개된 사용법을 이룹니다.

class Product {
  public name: string;
  price: number;

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

  public getDetails(): string {
    return `제품명: ${this.name}, 가격: ${this.price}원`;
  }
}

const laptop = new Product("노트북", 1200000);
console.log(laptop.getDetails());

laptop.price = 1150000;
console.log(laptop.price);
제품명: 노트북, 가격: 1200000원
1150000

name에는 public을 적었고 price에는 아무것도 적지 않았지만, 둘 다 바깥에서 읽고 바꿀 수 있습니다. 그래서 laptop.price처럼 바깥 코드가 가격을 직접 바꾸는 것도 막을 수 없으며, 이것이 곤란한 상태라면 다음의 private를 씁니다.

private

private 멤버는 그 멤버를 선언한 클래스 안에서만 쓸 수 있고, 바깥 코드는 물론 자식 클래스에서도 직접 쓸 수 없습니다. 계좌의 잔액을 private로 두면, 바깥 코드는 공개된 입금과 출금 메서드를 거쳐서만 잔액을 바꿀 수 있습니다.

class BankAccount {
  private _balance: number;

  constructor(initialBalance: number) {
    if (initialBalance < 0) {
      throw new Error("초기 잔액은 0보다 작을 수 없습니다.");
    }
    this._balance = initialBalance;
  }

  public getBalance(): number {
    return this._balance;
  }

  public deposit(amount: number): void {
    if (amount > 0) {
      this._balance += amount;
      console.log(`${amount}원 입금. 현재 잔액: ${this._balance}원`);
    } else {
      console.log("0원보다 큰 금액을 입금해 주세요.");
    }
  }

  public withdraw(amount: number): void {
    if (amount > 0 && this._balance >= amount) {
      this._balance -= amount;
      console.log(`${amount}원 출금. 현재 잔액: ${this._balance}원`);
    } else {
      console.log("잔액이 부족하거나 유효하지 않은 금액입니다.");
    }
  }
}

const myAccount = new BankAccount(10000);
myAccount.deposit(5000);
myAccount.withdraw(2000);
console.log(myAccount.getBalance());
myAccount.withdraw(20000);
5000원 입금. 현재 잔액: 15000원
2000원 출금. 현재 잔액: 13000원
13000
잔액이 부족하거나 유효하지 않은 금액입니다.

잔액은 10000에서 5000을 입금해 15000이 되고, 2000을 출금해 13000이 됩니다. 20000의 출금은 잔액보다 크므로 withdraw의 검사에 걸려 거절되고, 잔액은 13000을 유지합니다. 바깥에서 myAccount._balance를 읽으려 하면 Property '_balance' is private and only accessible within class 'BankAccount'.라는 TS2341이 나오므로, 잔액을 바꾸는 길은 공개된 두 메서드뿐입니다.

접근 제어자는 포함 경계마다 다른 계약을 연다 — 상태 추적

접근 제한 설명 중 실제 BankAccount는 10000→15000→13000과 20000 출금 거절을 포함한다. 계좌/요청/결과의 같은 역할로 메서드 조건이 상태를 바꾸고 거절이 보존하는 것을 추적할 가치가 있다.

public은 외부 접근을 허용하고 protected는 선언·파생 클래스의 허용 문맥, private는 선언 클래스의 허용 문맥으로 제한합니다. public 메서드로 필요한 동작을 노출할 수 있습니다.

TYPE / RUNTIME / CONTRACT

누가 멤버를 직접 사용할 수 있는지 비교합니다

public·protected·private는 허용된 코드 위치가 다릅니다. 아래는 TypeScript의 직접 멤버 접근이며 #private 런타임 은닉과는 별도로 읽습니다.

접근 주체에 따른 public/protected/private 접근을 비교하고 readonly 대입 제한은 접근 범위와 별도의 축으로 유지한다.external→public: Product 공개 필드·메서드 접근。derived→protected: Car 메서드는 protected 사용。declaration→private: private은 선언 클래스 내부만。methods→private: 외부 코드는 공개 메서드로 검증된 변경허용허용직접외부 코드외부 코드Product 사용파생 클래스파생 클래스Car extends Vehiclepublic 멤버public 멤버외부 · 파생 · 선언 내부protected speedprotected speed파생 · 선언 내부만BankAccount 선언 내부BankAccount 선언 내부private _balance 직접 접근외부의 공개 메서드외부의 공개 메서드deposit · withdraw 호출_balance_balanceprivate 필드검증 후 상태 변경readonly entryYearreadonly entryYear대입 제한은 접근과 별개
접근 주체에 따른 public/protected/private 접근을 비교하고 readonly 대입 제한은 접근 범위와 별도의 축으로 유지한다.external→public: Product 공개 필드·메서드 접근。derived→protected: Car 메서드는 protected 사용。declaration→private: private은 선언 클래스 내부만。methods→private: 외부 코드는 공개 메서드로 검증된 변경허용허용직접외부 코드외부코드Product사용파생 클래스파생클래스CarextendsVehiclepublic 멤버public멤버외부 ·파생 ·선언내부protected speedprotectedspeed파생 ·선언내부만BankAccount 선언 내부BankAccount 선언 내부private _balance 직접 접근외부의 공개 메서드외부의공개메서드deposit ·withdraw호출_balance_balanceprivate필드검증 후상태변경readonly entryYearreadonly entryYear대입 제한은 접근과 별개

외부 myCar.speed는 타입 오류이다. TypeScript private·readonly는 컴파일 검사이며 JavaScript #private 런타임 경계와 다르다.

코드·조건·비교표와 보충 설명

PUBLIC

외부 호출에 공개

Product.name·price·getDetails는 외부에서 접근합니다. price는 readonly가 아니므로 1200000에서 1150000으로 변경할 수 있습니다.

PROTECTED

파생 클래스가 활용

Car.drive는 상속한 speed와 accelerate에 접근해 60에서 70으로 올립니다. 외부 myCar.speed 직접 접근은 오류입니다.

PRIVATE

선언 클래스의 구현 안에서 사용

BankAccount는 _balance를 직접 노출하지 않고 입출금 메서드를 제공합니다. 실제 예제는 10000 → 15000 → 13000이고 20000 출금은 거부합니다.

READONLY / RUNTIME

변경 제한과 은닉은 별개

Student.entryYear는 public readonly 매개변수 속성입니다. TypeScript의 readonly·private는 검사 규칙입니다. #private는 별도의 JavaScript 런타임 기능이며 더 안쪽 단계가 아닙니다.

protected에는 파생 클래스의 receiver 규칙도 적용됩니다. 접근 제어자가 금액 검증을 대신하지 않으며, BankAccount의 조건은 작성된 메서드가 검사합니다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
초기와 입금잔액: 10000→15000 / 호출: deposit 5000초기 10000에서 5000을 입금해 15000이다public 동작이 작성된 조건으로 private 잔액을 바꾼다
허용 출금잔액: 15000→13000 / 호출: withdraw 20002000 출금 후 13000이다허용된 업무 조건일 때만 잔액을 차감한다
거절 출금잔액: 13000 유지 / 요청: 20000 거절20000 출금은 거절되고 13000을 유지한다private 접근 제한이 금액 검증을 대신하는 것은 아니다

접근 제어자는 포함 경계마다 다른 계약을 연다 — 상태 추적

접근 제한 설명 중 실제 BankAccount는 10000→15000→13000과 20000 출금 거절을 포함한다. 계좌/요청/결과의 같은 역할로 메서드 조건이 상태를 바꾸고 거절이 보존하는 것을 추적할 가치가 있다.

public은 외부 접근을 허용하고 protected는 선언·파생 클래스의 허용 문맥, private는 선언 클래스의 허용 문맥으로 제한합니다. public 메서드로 필요한 동작을 노출할 수 있습니다.

TYPE / RUNTIME / CONTRACT

누가 멤버를 직접 사용할 수 있는지 비교합니다

public·protected·private는 허용된 코드 위치가 다릅니다. 아래는 TypeScript의 직접 멤버 접근이며 #private 런타임 은닉과는 별도로 읽습니다.

접근 주체에 따른 public/protected/private 접근을 비교하고 readonly 대입 제한은 접근 범위와 별도의 축으로 유지한다.external→public: Product 공개 필드·메서드 접근。derived→protected: Car 메서드는 protected 사용。declaration→private: private은 선언 클래스 내부만。methods→private: 외부 코드는 공개 메서드로 검증된 변경허용허용직접외부 코드외부 코드Product 사용파생 클래스파생 클래스Car extends Vehiclepublic 멤버public 멤버외부 · 파생 · 선언 내부protected speedprotected speed파생 · 선언 내부만BankAccount 선언 내부BankAccount 선언 내부private _balance 직접 접근외부의 공개 메서드외부의 공개 메서드deposit · withdraw 호출_balance_balanceprivate 필드검증 후 상태 변경readonly entryYearreadonly entryYear대입 제한은 접근과 별개
접근 주체에 따른 public/protected/private 접근을 비교하고 readonly 대입 제한은 접근 범위와 별도의 축으로 유지한다.external→public: Product 공개 필드·메서드 접근。derived→protected: Car 메서드는 protected 사용。declaration→private: private은 선언 클래스 내부만。methods→private: 외부 코드는 공개 메서드로 검증된 변경허용허용직접외부 코드외부코드Product사용파생 클래스파생클래스CarextendsVehiclepublic 멤버public멤버외부 ·파생 ·선언내부protected speedprotectedspeed파생 ·선언내부만BankAccount 선언 내부BankAccount 선언 내부private _balance 직접 접근외부의 공개 메서드외부의공개메서드deposit ·withdraw호출_balance_balanceprivate필드검증 후상태변경readonly entryYearreadonly entryYear대입 제한은 접근과 별개

외부 myCar.speed는 타입 오류이다. TypeScript private·readonly는 컴파일 검사이며 JavaScript #private 런타임 경계와 다르다.

코드·조건·비교표와 보충 설명

PUBLIC

외부 호출에 공개

Product.name·price·getDetails는 외부에서 접근합니다. price는 readonly가 아니므로 1200000에서 1150000으로 변경할 수 있습니다.

PROTECTED

파생 클래스가 활용

Car.drive는 상속한 speed와 accelerate에 접근해 60에서 70으로 올립니다. 외부 myCar.speed 직접 접근은 오류입니다.

PRIVATE

선언 클래스의 구현 안에서 사용

BankAccount는 _balance를 직접 노출하지 않고 입출금 메서드를 제공합니다. 실제 예제는 10000 → 15000 → 13000이고 20000 출금은 거부합니다.

READONLY / RUNTIME

변경 제한과 은닉은 별개

Student.entryYear는 public readonly 매개변수 속성입니다. TypeScript의 readonly·private는 검사 규칙입니다. #private는 별도의 JavaScript 런타임 기능이며 더 안쪽 단계가 아닙니다.

protected에는 파생 클래스의 receiver 규칙도 적용됩니다. 접근 제어자가 금액 검증을 대신하지 않으며, BankAccount의 조건은 작성된 메서드가 검사합니다.

이 예의 조건

  • 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
  • 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
전체 상태와 변화 근거
장면현재 관찰값설명변화 이유
초기와 입금잔액: 10000→15000 / 호출: deposit 5000초기 10000에서 5000을 입금해 15000이다public 동작이 작성된 조건으로 private 잔액을 바꾼다
허용 출금잔액: 15000→13000 / 호출: withdraw 20002000 출금 후 13000이다허용된 업무 조건일 때만 잔액을 차감한다
거절 출금잔액: 13000 유지 / 요청: 20000 거절20000 출금은 거절되고 13000을 유지한다private 접근 제한이 금액 검증을 대신하는 것은 아니다

위 그림은 이 잔액의 변화를 단계별로 따라가고, 세 제어자가 바깥 코드와 자식 클래스(그림의 파생 클래스), 선언한 클래스 안에 각각 어떤 접근을 허용하는지 비교합니다. 바깥 코드는 public 멤버만, 자식 클래스는 public과 protected 멤버를, 선언한 클래스 안에서는 private까지 모두 직접 쓸 수 있습니다. readonly는 이와 별개로 대입만 막는 표시여서, 접근의 범위와는 따로 생각합니다.

private와 # 비공개 필드

타입스크립트의 private는 검사에서만 접근을 막습니다. 컴파일한 자바스크립트에서 private 표시는 지워지고 _balance는 평범한 속성으로 남으며, 타입스크립트도 myAccount["_balance"]처럼 대괄호로 접근하는 것은 허용합니다. 실행 중에도 정말 감추어야 한다면, 이름 앞에 #를 붙이는 자바스크립트의 비공개 필드(private field)를 씁니다.

class SecretBox {
  #code: string;

  constructor(code: string) {
    this.#code = code;
  }

  hint(): string {
    return `${this.#code.length}자리 코드`;
  }
}

const box = new SecretBox("0420");
console.log(box.hint());
console.log(Object.keys(box), Object.keys(myAccount));
4자리 코드
[] [ '_balance' ]

Object.keys는 객체가 가진 속성의 이름을 모아 돌려주는데, #code는 실행 중에도 감추어져 있어 빈 배열이 나오고, private인 _balance는 그대로 보입니다. 클래스 밖에서 box.#code를 쓰면 Property '#code' is not accessible outside class 'SecretBox' because it has a private identifier.라는 TS18013이 나오고, 이 제한은 자바스크립트 엔진도 지킵니다. 검사에서만 직접 접근을 막으면 충분한지, 실행 중에도 감추어야 하는지에 따라 둘 가운데 하나를 고르며, 어느 쪽도 잘못된 입금액을 대신 검사해 주지는 않습니다.

더 깊이: 다른 인스턴스의 비공개 멤버와 # 필드의 실제 동작

같은 클래스의 다른 인스턴스가 서로의 private 멤버를 읽을 수 있는지는 언어마다 다릅니다. 공식 Handbook에 따르면 Java와 C#, C++, Swift, PHP는 허용하고 Ruby는 허용하지 않으며, 타입스크립트는 허용하는 쪽입니다. 그래서 BankAccount 안의 메서드는 매개변수로 받은 다른 계좌의 _balance도 읽을 수 있습니다. 자바스크립트의 # 필드도 같아서, 클래스 본문 안이라면 정적 메서드에서든 바깥에서 만든 인스턴스에서든 other.#code를 읽을 수 있습니다.

# 필드는 일반 속성과 다르게 동작하는 곳이 더 있습니다. MDN의 비공개 요소 설명에 따르면, 그 필드를 갖지 않은 객체에서 # 필드를 읽으면 undefined가 아니라 TypeError가 납니다. 그래서 어떤 객체가 그 클래스로 만든 것인지는 #code in obj처럼 in 연산자로 먼저 확인할 수 있습니다. 또 # 필드는 대괄호로 접근하거나 Object의 메서드로 다룰 수 없고, Object.freeze의 영향도 받지 않으며, structuredClone으로 복사해도 따라오지 않습니다.

타입스크립트는 target이 ES2021 이하이면 # 필드를 WeakMap으로 흉내 내는 코드를 출력하고, ES2022 이상에서는 # 문법을 그대로 둡니다. Handbook은 실행 중에도 비공개를 지키는 이런 검사가 성능에 영향을 줄 수 있다고도 적습니다. 정말 바깥의 악의적인 코드로부터 값을 지켜야 한다면 private가 아니라 클로저나 WeakMap, # 필드처럼 실행 중에도 감추는 방법을 써야 한다는 것이 Handbook의 권고입니다.

protected

protected 멤버는 선언한 클래스와, 그 클래스를 상속한 자식 클래스 안에서 쓸 수 있습니다. 바깥 코드에는 감추되, 자식 클래스가 부모의 상태나 도우미 메서드를 이어서 써야 할 때 씁니다.

class Vehicle {
  protected speed: number;

  constructor(initialSpeed: number) {
    this.speed = initialSpeed;
  }

  protected accelerate(amount: number): void {
    this.speed += amount;
    console.log(`속도 증가: ${this.speed}`);
  }

  public getCurrentSpeed(): number {
    return this.speed;
  }
}

class Car extends Vehicle {
  private gear: number;

  constructor(initialSpeed: number, initialGear: number) {
    super(initialSpeed);
    this.gear = initialGear;
  }

  public drive(): void {
    console.log(`현재 기어: ${this.gear}`);
    this.accelerate(10);
    console.log(`차량이 ${this.speed}km/h로 주행 중입니다.`);
  }
}

const myCar = new Car(60, 3);
myCar.drive();
console.log(myCar.getCurrentSpeed());
현재 기어: 3
속도 증가: 70
차량이 70km/h로 주행 중입니다.
70

Car는 extends로 Vehicle을 상속했으므로, drive 안에서 부모의 protected 멤버인 accelerate와 speed를 쓸 수 있습니다. 생성자의 super(initialSpeed)는 부모의 생성자를 부르는 코드이며, 상속은 다음 글에서 자세히 봅니다. 바깥에서 myCar.speed를 읽으면 Property 'speed' is protected and only accessible within class 'Vehicle' and its subclasses.라는 TS2445가 나오므로, 바깥 코드는 공개된 getCurrentSpeed로만 속도를 읽습니다.

매개변수 속성과 readonly

앞 글에서 본 매개변수 속성에는 세 제어자와 readonly를 모두 쓸 수 있습니다. 매개변수 앞에 붙인 제어자가 그대로 그 속성의 접근 범위가 됩니다.

class Student {
  constructor(
    public studentId: string,
    private _studentName: string,
    protected grade: number,
    readonly entryYear: number,
  ) {}

  getInfo(): string {
    return `ID: ${this.studentId}, 이름: ${this._studentName}, 학년: ${this.grade}`;
  }

  get studentName(): string {
    return this._studentName;
  }
}

const s1 = new Student("S001", "김민수", 3, 2022);
console.log(s1.getInfo());
s1.studentId = "S002";
console.log(s1.studentId, s1.entryYear);
ID: S001, 이름: 김민수, 학년: 3
S002 2022

studentId는 public이어서 바깥에서 바꿀 수 있고, _studentName과 grade는 각각 private와 protected여서 바깥에서 읽으면 TS2341과 TS2445가 나옵니다. readonly entryYear는 제어자를 따로 적지 않았으므로 public이고 읽을 수는 있지만, s1.entryYear = 2023;처럼 대입하면 TS2540으로 진단됩니다.

접근을 막아도 값은 검사해야 한다

private는 바깥 코드가 잔액을 직접 바꾸지 못하게 할 뿐, 공개 메서드가 받은 값이 올바른지는 검사하지 않습니다. 앞의 BankAccount에서 deposit(Infinity)는 Infinity > 0이 참이어서 검사를 통과하고 잔액을 Infinity로 바꾸며, NaN은 amount > 0이 거짓이라 거절되지만 그것은 우연한 결과입니다. 다음은 원 단위의 정수만 다루기로 범위를 정하고, 값이 정말 쓸 수 있는 정수인지 검사하는 별도의 예제입니다.

class WholeWonAccount {
  private balance: number;

  constructor(initial: number) {
    if (!Number.isSafeInteger(initial) || initial < 0) {
      throw new RangeError("초기 잔액은 안전한 0 이상의 정수여야 합니다.");
    }
    this.balance = initial;
  }

  deposit(amount: number): void {
    const next = this.balance + amount;
    if (!Number.isSafeInteger(amount) || amount <= 0 || !Number.isSafeInteger(next)) {
      throw new RangeError("입금액과 결과 잔액은 안전한 정수여야 합니다.");
    }
    this.balance = next;
  }

  getBalance(): number {
    return this.balance;
  }
}

const wholeAccount = new WholeWonAccount(10000);
wholeAccount.deposit(5000);
console.log(wholeAccount.getBalance());
15000

Number.isSafeInteger는 값이 정수이면서 절댓값이 2의 53제곱보다 작아 정확히 표현되는 범위에 있는지 확인하므로, Infinity와 NaN, 소수를 모두 거절합니다. 결과 잔액 next도 같은 범위에 있는지 확인한 뒤에만 balance에 넣으므로, 검사에 실패한 호출은 잔액을 바꾸지 않고 RangeError를 던집니다. 이 예제는 통화나 동시에 일어나는 거래, 저장 같은 실제 금융 시스템의 문제를 다루지 않으며, 접근의 제한과 값의 검사가 서로 다른 일이라는 점만 보여 줍니다. 제어자의 규칙은 공식 Handbook의 멤버 가시성에 정리되어 있습니다.

정리

  • public은 어디서나, protected는 선언한 클래스와 자식 클래스 안에서, private는 선언한 클래스 안에서만 직접 쓸 수 있으며, 제어자를 적지 않으면 public입니다.
  • 바깥에서 private 멤버를 쓰면 TS2341, protected 멤버를 쓰면 TS2445가 나옵니다.
  • 타입스크립트의 private는 검사에서만 접근을 막고 실행 중에는 평범한 속성이며, 실행 중에도 감추려면 # 비공개 필드를 씁니다.
  • 매개변수 속성에 붙인 제어자가 그 속성의 접근 범위가 되고, readonly는 접근의 범위와 별개로 대입만 막습니다.
  • 접근 제어자는 상태를 바꾸는 길을 공개 메서드로 모을 뿐이므로, 그 메서드가 받은 값은 따로 검사해야 합니다.

연습 문제

1. 거절되는 출금

myAccount의 잔액이 13000인 상태에서 withdraw(14000)을 부르면 무엇이 출력되고 잔액은 얼마가 되나요? wholeAccount의 잔액이 15000일 때 deposit(0)과 deposit(NaN)은 각각 어떻게 되나요?

해설 보기

14000은 잔액보다 크므로 잔액이 부족하거나 유효하지 않은 금액입니다.가 출력되고 잔액은 13000을 유지합니다. wholeAccount의 deposit(0)은 amount <= 0에, deposit(NaN)은 Number.isSafeInteger에 걸려 둘 다 RangeError를 던지며, balance에 넣기 전에 멈추므로 잔액은 15000 그대로입니다. 오류를 던진 뒤에도 잔액을 읽어 보려면 각 호출을 try와 catch로 감싸 실행합니다.

2. 바깥에서 할 수 있는 일

Student의 s1에서 s1.entryYear = 2023;과 s1.studentName을 읽는 코드, s1.grade를 읽는 코드는 각각 검사를 통과하나요?

해설 보기

entryYear는 readonly이므로 대입이 TS2540으로 진단됩니다. studentName은 공개된 getter여서 통과하고 김민수를 돌려줍니다. grade는 protected이므로 바깥에서 읽으면 TS2445가 나오며, readonly와 protected 모두 검사의 규칙일 뿐 실행 중의 객체를 얼리지는 않습니다.

3. 상속한 클래스에서 private 쓰기

Vehicle의 speed를 private로 바꾸면 Car의 drive 메서드에서 무엇이 달라지나요?

해설 보기

private 멤버는 선언한 클래스 안에서만 쓸 수 있으므로, Car의 drive에서 this.speed를 읽는 줄이 Property 'speed' is private and only accessible within class 'Vehicle'.로 진단됩니다. accelerate는 여전히 protected이므로 부를 수 있고, 속도는 Vehicle 안의 accelerate가 바꾸므로 그대로 동작합니다. 자식 클래스가 상태를 직접 읽어야 한다면 protected로 두거나, getCurrentSpeed 같은 메서드로 읽게 합니다.

다음 글 상속과 구현에서는 이 글의 Car extends Vehicle처럼 클래스를 상속하는 방법과, 클래스가 인터페이스의 모양을 지키도록 하는 implements를 비교합니다.