JavaScript 클래스와의 차이점부터 접근 제한자, static, 추상 클래스까지 단계별로 정리합니다.
1. JS 클래스와 TS 클래스의 차이
JavaScript에서는 constructor에서 바로 this.color를 할당해도 문제가 없습니다. 하지만 TypeScript에서는 멤버 변수를 먼저 선언해야 합니다.
// ❌ TypeScript에서는 에러
class Car {
constructor(color) {
this.color = color; // color가 선언되지 않음
}
}
// ✅ 멤버 변수를 먼저 선언
class Car1 {
color: string;
constructor(color: string) {
this.color = color;
}
start() {
console.log("start");
}
}
const bmw1 = new Car1("red");
멤버 변수 선언 생략하는 방법
constructor 매개변수에 접근 제한자(public, private, protected)나 readonly를 붙이면 멤버 변수 선언을 생략할 수 있습니다.
class Car2 {
constructor(public color: string) {
this.color = color;
}
start() {
console.log("start");
}
}
const bmw3 = new Car2("red");
💡 public color: string이 매개변수 선언과 멤버 변수 선언을 동시에 처리해줍니다. 코드를 간결하게 유지할 수 있는 TypeScript만의 편의 문법입니다.
2. 접근 제한자 (Access Modifier)
클래스 내부의 프로퍼티와 메서드에 접근할 수 있는 범위를 제어합니다.
public (기본값)
어디서든 접근 가능합니다. 아무것도 표시하지 않으면 자동으로 public입니다.
private (또는 #)
해당 클래스 내부에서만 접근 가능합니다. 자식 클래스에서도 사용할 수 없습니다.
class Car3 {
private name: string = "car";
color: string;
constructor(color: string) {
this.color = color;
}
start() {
console.log("start");
console.log(this.name); // ✅ OK — 같은 클래스 내부
}
}
class Bmw3 extends Car3 {
constructor(color: string) {
super(color);
}
showName() {
console.log(super.name); // ❌ Error — private은 자식에서 접근 불가
}
}
protected
자식 클래스 내부에서는 접근 가능하지만, 클래스 인스턴스에서는 접근할 수 없습니다.
class Car {
protected name: string = "car";
}
class Bmw extends Car {
showName() {
console.log(this.name); // ✅ OK — 자식 클래스 내부
}
}
const z4 = new Bmw();
z4.name; // ❌ Error — 인스턴스에서 접근 불가
정리
접근 제한자 클래스 내부 자식 클래스 인스턴스
| public | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ❌ |
| private / # | ✅ | ❌ | ❌ |
3. readonly
readonly를 붙이면 값을 읽을 수만 있고 수정할 수 없습니다. 단, constructor 내부에서는 초기값을 할당할 수 있습니다.
class Car4 {
readonly name: string = "car";
color: string;
constructor(color: string, name: string) {
this.color = color;
this.name = name; // ✅ OK — constructor 내부에서는 할당 가능
}
}
const z5 = new Car4("red", "zzz");
z5.name = "aaa"; // ❌ Error — 읽기전용 프로퍼티는 수정 불가
💡 readonly는 "생성 시 한 번만 값을 정하고, 이후로는 변경하지 않겠다"는 의도를 명확히 표현합니다.
4. static (정적 멤버)
static으로 선언한 프로퍼티나 메서드는 인스턴스가 아닌 클래스 자체에 속합니다. 접근할 때 this가 아닌 클래스 이름을 사용해야 합니다.
class Car4 {
readonly name: string = "car";
color: string;
static wheels = 4;
constructor(color: string, name: string) {
this.color = color;
this.name = name;
}
start() {
console.log("start");
console.log(this.wheels); // ❌ Error — this로 접근 불가
console.log(Car4.wheels); // ✅ OK — 클래스 이름으로 접근
}
}
console.log(Car4.wheels); // 4
⚠️ static 멤버는 인스턴스마다 생성되지 않고 클래스에 하나만 존재합니다. 모든 인스턴스가 공유하는 값(바퀴 수, 설정값 등)에 적합합니다.
5. 추상 클래스 (Abstract Class)
abstract 키워드를 붙인 클래스는 직접 인스턴스를 만들 수 없고, 반드시 상속을 통해서만 사용합니다.
abstract class Car5 {
readonly name: string = "car";
color: string;
constructor(color: string) {
this.color = color;
}
start() {
console.log("start");
}
abstract doSome(): void; // 추상 메서드 — 구현부 없음
}
const car = new Car5("red"); // ❌ Error — 추상 클래스는 new로 생성 불가
추상 메서드 구현
추상 클래스 안의 추상 메서드는 상속받은 자식 클래스에서 반드시 구현해야 합니다.
class Bmw5 extends Car5 {
constructor(color: string) {
super(color);
}
doSome() {
console.log("Bmw만의 동작");
}
}
const myBmw = new Bmw5("blue");
myBmw.start(); // "start" — 부모에서 상속
myBmw.doSome(); // "Bmw만의 동작" — 자식에서 구현
💡 추상 클래스는 공통 구조는 정의하되, 세부 동작은 자식에게 위임하는 패턴입니다. start()처럼 공통 로직은 부모에 구현하고, doSome()처럼 자식마다 달라지는 로직은 추상 메서드로 선언합니다.
한눈에 보기
기능 문법 설명
| 멤버 변수 선언 | color: string; | TS에서는 반드시 선언 필요 |
| 매개변수 축약 | constructor(public color: string) | 선언과 할당을 동시에 |
| public | 기본값 | 어디서든 접근 가능 |
| protected | protected name | 자식 클래스까지만 접근 |
| private | private name / #name | 해당 클래스 내부만 접근 |
| readonly | readonly name | 수정 불가 (constructor 제외) |
| static | static wheels = 4 | 클래스 자체에 속하는 멤버 |
| 추상 클래스 | abstract class Car | 직접 생성 불가, 상속 전용 |
| 추상 메서드 | abstract doSome(): void | 자식에서 반드시 구현 |
'프론트' 카테고리의 다른 글
| TypeScript 유틸리티 타입(Utility Types) (1) | 2026.04.11 |
|---|---|
| TypeScript 제네릭(Generic) (0) | 2026.04.11 |
| TypeScript 리터럴 · 유니온 · 교차 타입 정리 (0) | 2026.04.11 |
| TypeScript 함수 타입 (0) | 2026.04.11 |
| TypeScript 인터페이스(Interface) (0) | 2026.04.11 |