Javascript

[JS] JavaScript 메서드를 설계 관점에서 파헤쳐보자.

popeyes 2025. 4. 7. 21:29

 

Object.keys()는 되는데, 왜 obj.keys()는 안 되는 걸까?

 

 

우아한테크코스에서는 레벨이 끝날 때마다 레벨 인터뷰라는 시스템을 진행합니다.

일종의 회고이자 모의 면접 같은 시간인데, 이 과정에서 시지프 코치가 제게 흥미로운 질문을 하나 던졌습니다.

“조금 심화적인 질문일 수 있는데요. Object.keys()나 Object.create()는 바로 호출하는데, 왜 Array 관련 메서드는 Array.prototype.filter처럼 호출할까요? Object 메서드들은 static 메서드인 걸까요? JS의 설계 관점에서 생각해보면 좋을 것 같아요.”

이 질문에 선뜻 대답하지 못했고, 저는 그 순간 JavaScript에 대한 깊은 이해가 부족하다는 걸 느꼈습니다. 그래서 이 글을 통해 그 이유를 스스로 정리해보고자 합니다.


1. 자바스크립트의 프로토타입 체이닝

자바스크립트는 프로토타입 기반의 객체지향 언어입니다. 모든 객체는 자신의 부모 역할을 하는 프로토타입 객체를 갖고 있고, 이 연결고리를 통해 필요한 메서드를 찾습니다. 이를 프로토타입 체이닝이라고 하죠.

const arr = [1, 2, 3];
arr.filter(num => num > 1); 
// → arr → Array.prototype.filter

우리가 arr.filter()라고 쓸 수 있는 이유는 Array.prototype에 filter라는 메서드가 정의되어 있기 때문입니다.

이처럼 배열 인스턴스들은 자신들의 프로토타입에 정의된 메서드에 접근할 수 있습니다.


2. 프로토타입 체인의 최상위는 Object

모든 객체의 프로토타입 체인을 따라가다 보면 결국 Object.prototype에 도달합니다.

그리고 Object.prototype은 더 이상 부모가 없는, 프로토타입 체인의 최상단 객체입니다.

즉, 아래와 같은 구조로 생각해볼 수 있죠.

Objectnull
ArrayObjectnull
FunctionObjectnull
DateObjectnull

 

3. 그렇다면 Object.prototype.values가 존재한다면?

만약 우리가 흔히 쓰는 Object.values(obj)를 인스턴스 메서드로 만들 수 있다면 좋겠죠?

const obj = { a: 1 };
obj.values(); //  => [1] 이 되면 얼마나 직관적일까?

 

하지만 여기서 문제가 발생합니다. 예를 들어 Object.prototype.values를 정의하게 되면, 이 메서드는 모든 객체의 조상인 Object.prototype에 존재하게 됩니다.
그 말은 즉,

new Date().values(); // ???
(10).values();       // ???
false.values();      // ???

이런 식으로 원시값을 포함한 거의 모든 값이 .values() 메서드를 사용할 수 있게 되어버립니다. 이것은 설계 상 명확하지 않은 동작을 유발할 수 있으며, JS의 일관성도 해칠 수 있습니다.


4. 그래서 객체 관련 메서드는 'static 메서드'처럼 보이는 것이다

이러한 이유로 JavaScript는 Object 관련 메서드들을 모두 생성자 함수에 직접 정의해두었습니다.

Object.keys(obj);
Object.values(obj);
Object.freeze(obj);
Object.create(proto);

이 메서드들은 일종의 static 메서드처럼 동작하지만, 사실 ES5 시절에는 클래스가 없었기에 그냥 Object 함수에 직접 붙은 메서드일 뿐이었죠.

결국 이 설계는 "불필요한 오염을 막기 위한 프로토타입 체이닝의 고려"라고 볼 수 있습니다.


5. 반면 Array는 괜찮은 이유는?

Array.prototype.filter, map, reduce 등의 메서드는 배열 인스턴스에만 필요한 기능입니다. 그래서 이들은 Array.prototype에 정의되어 있어도 문제없습니다.

[1, 2, 3].filter(fn); // 가능
'abc'.filter(fn);     // 불가능 (문자열에는 filter가 없음)

문자열, 숫자, 불리언 등의 값은 Array.prototype과 아무 관계가 없기 때문에, 이 메서드들이 불필요하게 확장되는 일은 없습니다.


결론

  • Object.keys()는 왜 obj.keys()가 아닌가? → Object.prototype에 메서드를 넣으면 모든 값에서 호출 가능해지기 때문!
  • 그래서 Object 관련 메서드는 모두 정적(static) 메서드처럼 설계되었다.
  • Array.prototype은 배열 전용 메서드만 포함하고 있기 때문에, 인스턴스 메서드로 제공해도 문제 없음.

 

 

출처: 코어자바스크립트