인스턴스가 아닌 클래스에 속하는 static 멤버
클래스에 작성하는 메서드 대부분은 특정 인스턴스를 대상으로 동작합니다. 특정 user라든가, 특정 circle처럼요. 반면 자바스크립트의 정적 멤버(static)는 좀 다릅니다. 인스턴스가 아니라 클래스 자체에 속하기 때문에, 호출할 때도 인스턴스가 아닌 클래스 이름으로 직접 호출합니다.
double은 MathUtils 자체에 속해 있지, MathUtils의 인스턴스에 붙어 있지 않습니다. 인스턴스를 만들어봤자 소용없어요 — m.double은 undefined가 나옵니다. 이건 일반 메서드와 정반대예요. 일반 메서드는 인스턴스(정확히는 프로토타입)에 존재하고, 클래스 쪽에서는 보이지 않죠.
머릿속에 이렇게 그려두면 편합니다. class 키워드는 두 가지를 만들어냅니다 — new MathUtils()로 꺼내 쓰는 인스턴스 메서드 묶음과, MathUtils로 바로 호출하는 정적 메서드(static method) 묶음이에요. static 키워드는 어떤 멤버가 어느 쪽 바구니에 들어갈지를 정해줍니다.
대표적인 활용: 팩토리 메서드
자바스크립트에서 static 메서드를 쓰는 가장 흔한 이유는 바로 대체 생성자 를 만들기 위해서입니다. 진짜 constructor는 정해진 인자를 받지만, 실제로 객체를 만들다 보면 다른 방식이 필요할 때가 많잖아요 — JSON에서 만들거나, DB row에서 만들거나, URL에서 만드는 식으로요.
fromJSON은 user를 사용하는 게 아니라 user를 만들어내는 메서드죠. 바로 이럴 때 static 메서드가 딱 들어맞습니다. 대안으로 parseUser 같은 독립 함수를 따로 빼둘 수도 있지만, 클래스에 붙여두면 관련 동작이 한곳에 모이고 호출하는 쪽에서도 의도가 한눈에 드러납니다.
자바스크립트 정적 속성(Static Properties)
클래스 자체에 데이터를 붙여둘 수도 있습니다:
Circle.PI는 모든 원(circle)에서 공유되는 값입니다. 인스턴스마다 복사되는 게 아니라 클래스 자체에 붙어 있는 상수죠. 인스턴스 메서드 안에서 이 값에 접근할 때는 this가 아니라 클래스 이름으로(Circle.PI) 참조해야 합니다.
정적 속성은 설정값, 인스턴스 간에 공유하는 캐시, 카운터, 클래스 레벨 상수 같은 곳에서 유용하게 쓰입니다.
static 메서드 안의 this는 클래스 자신
일반 메서드 안에서 this는 인스턴스를 가리키지만, static 메서드 안에서 this는 클래스 자체를 가리킵니다:
increment 안의 this.count는 곧 Counter.count를 의미해요. 처음엔 좀 어색하게 느껴질 수 있지만, 바로 이 동작 덕분에 정적 메서드 상속이 제대로 작동합니다. this는 메서드를 정의한 클래스가 아니라, 메서드를 호출한 클래스를 가리키거든요.
자바스크립트 static 상속
정적 메서드는 서브클래스로 상속됩니다. 게다가 this가 메서드를 호출한 클래스를 가리키기 때문에, 팩토리 메서드를 만들면 자동으로 올바른 서브클래스의 인스턴스를 반환해 줍니다.
Animal.create는 내부에서 new this(name)을 사용합니다. Dog.create("Rex")를 호출하면 this가 Dog가 되므로 new this(name)은 Dog 인스턴스를 만들어 냅니다. 만약 여기서 new Animal(name)이라고 적었다면 항상 Animal 인스턴스만 생성되어 이 패턴이 깨져버리겠죠. 정적 메서드 안에서 this가 클래스 자신을 가리키도록 설계된 가장 큰 이유가 바로 이것입니다.
static 메서드 vs 인스턴스 메서드: 같은 로직 비교해 보기
동일한 로직을 두 가지 방식으로 나눠 작성하면 이렇게 달라집니다.
둘 다 계산 자체는 똑같습니다. 차이는 데이터를 어디서 가져오느냐인데, 인스턴스 메서드는 this.celsius에서 읽어오고, static 메서드는 인자로 값을 받습니다. "이 객체가 하는 동작"이라면 인스턴스 메서드를, "입력값만 주면 이 클래스가 계산해 주는 것"이라면 static 메서드를 선택하면 됩니다.
static 블록으로 초기화하기
static 속성을 초기화할 때 표현식 한 줄로는 부족한 경우가 있습니다. 반복문을 돌려야 하거나, 조건 분기가 필요하거나, 값들이 서로 의존하는 상황이 대표적이죠. 이럴 때 쓰라고 만들어진 것이 바로 자바스크립트의 static 블록입니다:
static { ... } 블록은 클래스가 정의될 때 딱 한 번 실행되며, 블록 안에서 this는 클래스 자신을 가리킨다. 여러 단계에 걸친 초기화가 필요할 때 쓰면 좋고, 값 하나만 대입하는 정도라면 그냥 static 필드로 쓰는 편이 훨씬 깔끔하다.
프라이빗 static 멤버
static 필드도 # 접두사를 붙이면 프라이빗으로 선언할 수 있다. 이렇게 하면 클래스 내부에서만 접근이 가능하다:
#nextId는 클래스 내부에 완전히 봉인돼 있습니다. 외부 코드에서 IdGenerator.next()는 호출할 수 있지만, 카운터 자체를 들여다보거나 초기화할 수는 없죠. 프라이빗 필드는 곧 별도 페이지에서 자세히 다루겠지만, 일단 static과 #을 함께 쓸 수 있다는 점은 알아두면 좋습니다.
static을 남용하면 안 되는 경우
정적 메서드는 관련된 헬퍼들을 한곳에 묶는 데 편리하지만, 그렇다고 모든 유틸리티를 클래스로 감쌀 이유가 되지는 않습니다. 독립적인 함수들로 가득 찬 파일이 있다면, 그냥 함수들을 export 하세요. 네임스페이스를 만들겠다고 전부 static 메서드로 채운 클래스로 감쌀 필요는 없습니다. 그런 역할은 이미 모듈이 더 깔끔하게 해주고 있거든요.
다음과 같은 상황이라면 static이 제격입니다:
- 그 함수가 정말로 해당 클래스에 속하는 경우 (팩토리 메서드, 변환기, 그 타입 전용 유효성 검사기 등).
- 모든 인스턴스가 공유해야 하는 상태가 필요한 경우.
- 서브클래스가 그 동작을 오버라이드하거나 상속받을 가능성이 있는 경우.
이런 경우가 아니라면, 그냥 일반 함수가 훨씬 단순하고 좋은 선택입니다.
다음 주제: 프라이빗 필드
앞서 #nextId를 잠깐 봤는데, 이게 바로 자바스크립트의 프라이빗 필드 문법입니다. 인스턴스 멤버든 정적 멤버든 모두 적용할 수 있고, 클래스 내부의 구현 세부사항을 감추는 가장 현대적인 방법이죠. 바로 다음 페이지에서 다뤄보겠습니다.
자주 묻는 질문
자바스크립트에서 정적 메서드(static method)가 뭔가요?
정적 메서드는 클래스의 인스턴스가 아니라 클래스 자체에 속한 메서드입니다. static 키워드로 선언하고, MyClass.doThing()처럼 클래스 이름으로 바로 호출해요. 인스턴스에서 this.doThing()으로는 접근할 수 없고, 오직 클래스에서만 호출할 수 있습니다.
정적 메서드와 인스턴스 메서드, 언제 어떤 걸 써야 하나요?
특정 인스턴스의 상태를 읽거나 바꿀 필요가 없지만 클래스와 개념적으로 연관된 기능이라면 정적 메서드가 어울립니다. 대표적인 예가 User.fromJSON(...) 같은 팩토리 메서드, Math.max 같은 유틸리티, 그리고 클래스를 네임스페이스처럼 쓰는 상수들이죠. 반대로 this로 인스턴스에 접근해야 한다면 인스턴스 메서드로 만들어야 합니다.
정적 메서드에서 인스턴스 속성에 접근할 수 있나요?
직접은 안 됩니다. 정적 메서드 안의 this는 인스턴스가 아니라 클래스 자체를 가리키기 때문에, this.name은 인스턴스 필드가 아니라 정적 속성을 읽게 됩니다. 인스턴스 데이터가 필요하다면 인자로 직접 넘겨줘야 해요. 예: static summarize(user) { return user.name; }.
자바스크립트에서 정적 메서드도 상속되나요?
네, 상속됩니다. 자식 클래스가 부모 클래스를 extends하면 부모의 정적 메서드도 자식 클래스에서 그대로 호출할 수 있어요. 이때 정적 메서드 내부의 this는 호출한 쪽 클래스를 가리키기 때문에, 서브클래스에서도 팩토리 패턴이 자연스럽게 동작합니다.