ABOUT ME

Today
Yesterday
Total
  • [JS] JavaScript 메서드를 설계 관점에서 파헤쳐보자.
    Javascript 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은 배열 전용 메서드만 포함하고 있기 때문에, 인스턴스 메서드로 제공해도 문제 없음.

     

     

    출처: 코어자바스크립트

Designed by Tistory.