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. 형 변환 알고리즘
엔진은 다음 순서에 따라 메서드를 찾아 실행합니다.
- *
obj[Symbol.toPrimitive](hint)*가 있는지 확인하고 있으면 호출합니다. - 없다면, Hint가 **
"string"*인 경우:obj.toString()→obj.valueOf()순으로 호출합니다.
- 없다면, 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를 매일 쓰지는 않습니다. 하지만 Vite나 Webpack 같은 도구들의 내부 소스나, 고도로 설계된 라이브러리(예: 수치 연산 라이브러리)에서는 객체끼리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. 구식 방식: toString과 valueOf
심볼이 없던 시절부터 존재하던 메서드입니다. 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"와 동일하게 처리하여 숫자를 우선적으로 반환하려 시도합니다.