logo

DowanKim

25. 객체를 원시형으로 변환하기

2026년 4월 14일

자바스크립트

자바스크립트에서 객체는 상당히 고집이 셉니다. 논리 평가 시에는 무조건 true를 반환하고, 연산이나 출력이 필요할 때만 비로소 몸을 낮춰 **원시형(Primitive)**으로 변신합니다


🎯 1. 세 가지 타입의 'Hint'

자바스크립트 엔진은 객체를 어떤 자료형으로 바꿀지 결정할 때 **'Hint'**라는 기준을 사용합니다.

Hint상황예시
"string"문자열을 기대하는 연산alert(obj), anotherObj[obj] = 123
"number"수학 연산을 수행할 때Number(obj), obj1 - obj2, +obj
"default"연산자가 타입을 확신하지 못할 때obj1 + obj2, obj == 1

💡 참고: Date 객체를 제외한 거의 모든 내장 객체는 "default""number"를 동일하게 처리합니다.


⚙️ 2. 형 변환 알고리즘

엔진은 다음 순서에 따라 메서드를 찾아 실행합니다.

  1. *obj[Symbol.toPrimitive](hint)*가 있는지 확인하고 있으면 호출합니다.
  2. 없다면, Hint가 **"string"*인 경우:
    • obj.toString()obj.valueOf() 순으로 호출합니다.
  3. 없다면, Hint가 "number" 또는 **"default"*인 경우:
    • obj.valueOf()obj.toString() 순으로 호출합니다.

💎 3. 현대적인 방식: Symbol.toPrimitive

가장 강력하고 명확한 방법입니다. 하나의 메서드에서 모든 Hint를 분기 처리할 수 있습니다.

  • 아래코드 설명

    1. 정체 1: 계산된 프로퍼티 이름 (Computed Property Name)

    객체 리터럴 {} 안에서 키를 대괄호 [...]로 감싸는 것은 **"이 안에 있는 변수나 표현식의 결과를 프로퍼티 이름으로 쓰겠다"**는 뜻입니다.

    • let key = "name"; let obj = { [key]: "John" }; 이라고 하면 obj{ name: "John" }이 됩니다.
    • 질문하신 코드에서는 Symbol.toPrimitive라는 특별한 심볼(Symbol) 값을 키로 사용하기 위해 대괄호를 쓴 것입니다.

    2. 정체 2: Symbol.toPrimitive (시스템 심볼)

    Symbol.toPrimitive는 자바스크립트 엔진 내부에 미리 정의되어 있는 **'시스템 심볼'**입니다. 엔진은 객체를 원시값(문자열, 숫자 등)으로 변환해야 할 때, 객체에 이 심볼을 키로 가진 메서드가 있는지 가장 먼저 확인합니다.

    변환의 3가지 힌트 (Hint)

    엔진은 상황에 따라 hint라는 인자를 이 함수에 자동으로 넘겨줍니다.

    Hint상황결과
    "string"문자열이 필요할 때 (alert 등){name: "John"} 반환
    "number"수학 연산이 필요할 때 (+, - 등)1000 (money) 반환
    "default"확신이 없을 때 (+ 이항 연산 등)1000 (money) 반환

    3. 코드 분석: 왜 이렇게 쓰나요?

    질문하신 코드를 다시 뜯어보면 이렇습니다.

    [Symbol.toPrimitive](hint) { // 1. 만약 엔진이 "이 객체 문자열로 좀 바꿔줘"라고 하면 (hint == "string") // 2. 이름 정보가 담긴 문자열을 내뱉고, // 3. 그게 아니라면(숫자나 기본 연산) 돈(money) 정보를 내뱉어라! return hint == "string" ? `{name: "${this.name}"}` : this.money; }

    이 메서드가 있으면 객체끼리 더하거나, 객체에 숫자를 더하는 등 객체를 마치 숫자나 문자처럼 자유자재로 다룰 수 있게 됩니다.

  • 객체의 메서드를 정의한다는 것


    🔍 왜 이렇게 생겼을까? (문법 해부)

    이 코드는 **'계산된 프로퍼티'**와 **'메서드 단축 구문'**이 합쳐진 형태입니다.

    1. [ ... ] : 키(Key)를 동적으로 결정하기

    보통 객체 키는 name: "John"처럼 바로 적지만, 심볼(Symbol) 같은 특수한 값을 키로 쓰고 싶을 때는 [ ] 안에 그 값을 넣어야 합니다.

    • Symbol.toPrimitive라는 시스템 심볼 값 자체를 메서드의 이름(키)으로 쓰겠다는 뜻입니다.

    2. (hint) { ... } : 메서드 단축 구문

    과거에는 sayHi: function() { ... }라고 길게 썼지만, 모던 자바스크립트에서는 : function을 생략하고 바로 메서드이름() { ... }으로 쓸 수 있습니다.


    🛠️ 일반 메서드 vs 시스템 메서드

    우리가 만드는 일반 메서드와 자바스크립트 엔진이 찾는 시스템 메서드를 비교해 보면 이해가 빠릅니다.

    구분일반 메서드 (예: sayHi)시스템 메서드 (예: Symbol.toPrimitive)
    이름표문자열 ("sayHi")심볼 (Symbol.toPrimitive)
    호출 주체개발자가 직접 (user.sayHi())자바스크립트 엔진이 필요할 때 자동으로
    목적객체의 커스텀 행동 정의객체의 기본 동작(형 변환 등) 재정의

    💡 엔진과의 "비밀 약속"

    이 메서드를 정의한다는 건, 자바스크립트 엔진에게 이렇게 말하는 것과 같습니다.

    "자바스크립트 엔진아, 나중에 니가 이 객체를 숫자로 계산하거나 문자열로 출력해야 할 일이 생기면, 엉뚱한 [object Object] 같은 거 내뱉지 말고 내가 여기 정의해둔 이 메서드를 실행해서 그 결과값을 써!"

    그래서 우리가 alert(user)라고만 써도 엔진은 이 '메서드'가 있는지 확인하고, 있으면 알아서 실행해서 그 결과값을 가져가는 것이죠.


    🎙️ 기술 면접 대비 (Interview Questions)

    Q. 객체 리터럴 안에서 대괄호 []를 사용하는 '계산된 프로퍼티'는 언제 주로 사용하나요?

    답변: 프로퍼티 이름이 런타임에 결정되는 변수이거나, **심볼(Symbol)**처럼 특수한 자료형을 객체의 키로 사용해야 할 때 주로 사용합니다. 이를 통해 객체 구조를 훨씬 동적이고 유연하게 설계할 수 있습니다.


    💡 Tech Lead의 한 줄 인사이트

    사실 실무에서는 Symbol.toPrimitive를 매일 쓰지는 않습니다. 하지만 ViteWebpack 같은 도구들의 내부 소스나, 고도로 설계된 라이브러리(예: 수치 연산 라이브러리)에서는 객체끼리 obj1 + obj2가 가능하게 만들기 위해 이 메서드를 아주 요긴하게 사용하곤 합니다.

let user = { name: "John", money: 1000, [Symbol.toPrimitive](hint) { return hint == "string" ? `{name: "${this.name}"}` : this.money; } }; alert(user); // hint: string -> {name: "John"} alert(+user); // hint: number -> 1000 alert(user + 500); // hint: default -> 1500

📜 4. 구식 방식: toStringvalueOf

심볼이 없던 시절부터 존재하던 메서드입니다. Symbol.toPrimitive가 없으면 이 친구들이 등판합니다.

  • toString(): 기본적으로 "[object Object]"를 반환합니다.
  • valueOf(): 기본적으로 객체 자신을 반환합니다. (엔진은 이를 무시하고 다음 메서드를 찾습니다.)

실무 최적화 팁

대부분의 경우 toString 하나만 구현해도 충분합니다. 엔진은 valueOf가 유의미한 원시값을 반환하지 않으면 자동으로 toString을 호출하기 때문입니다. 또한 toString은 디버깅과 로깅 시 사람이 읽기 좋은 정보를 제공한다는 강력한 장점이 있습니다.


🔄 5. 추가 형 변환 (Two-Step Conversion)

객체가 원시값으로 변환된 후에도 연산자의 요구에 따라 한 번 더 변환될 수 있습니다.

let obj = { toString() { return "2"; } // 객체가 문자열 "2"로 변함 }; alert(obj * 2); // 4 (문자열 "2"가 다시 숫자 2로 변환되어 연산됨) alert(obj + 2); // "22" (이항 덧셈은 문자열 연결이 우선됨)

🎙️ 기술 면접 대비 (Interview Questions)

Q1. 객체 형 변환에서 "boolean" hint는 왜 없나요? (기초)

답변: 자바스크립트에서 모든 객체(빈 객체 포함)는 논리 평가 시 **무조건 true**가 됩니다. 따라서 불린형으로의 변환 과정 자체가 존재하지 않으며, 오직 문자형과 숫자형으로의 변환만 관리하면 됩니다.

Q2. valueOf가 있는데도 toString만 주로 쓰는 이유는 무엇인가요? (중급)

답변: 기본적으로 valueOf는 객체 자신을 반환하도록 설계되어 있어 원시형 변환에 큰 역할을 하지 못하는 경우가 많습니다. 반면 toString은 모든 Hint 상황에서 최후의 보루(Fallback)로 동작하며, 디버깅 시 가독성 있는 정보를 제공하므로 실무에서 더 효율적입니다.

Q3. obj1 + obj2 연산 시 hint는 무엇이 되며, 그 이유는 무엇인가요? (심화)

답변: Hint는 **"default"**가 됩니다. 이항 덧셈 연산자(+)는 피연산자에 따라 문자열 합치기나 숫자 더하기를 모두 수행할 수 있어 타입을 확신할 수 없기 때문입니다. 다만 대부분의 내장 객체는 "default""number"와 동일하게 처리하여 숫자를 우선적으로 반환하려 시도합니다.