LogoSEO Jing
  • All Posts
  • SEO Jing
  • okayJing
  • KD Team
  • CLAB Coreteam
  • Study

Contact Me

© 2026 SEOJing. All rights reserved.

JavaScript프론트엔드자바스크립트 퀴즈북Type ConversionSymbol.toPrimitive

자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와 ToPrimitive 흐름

2026년 6월 25일·9분 읽기

오늘의 문제

다음 코드는 무엇을 출력할까요?

js
const point = {
  x: 3,
  y: 4,
  valueOf() {
    return 5;
  },
  toString() {
    return "(3, 4)";
  },
};

console.log(point + 1);
console.log(`${point}`)

Post Q&A

오케이징에게 물어보기

자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와 ToPrimitive 흐름 전체를 기준으로 질문과 피드백을 받아요.답을 본 뒤에는 이 내용을 댓글로 달아서 서징에게도 물어볼 수 있어요. 작성자가 직접 볼 수 있어요!

0/500

포스트 목록

/study/javascript-quizbook
파일 3개, 폴더 0개
자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을 수 없을까자바스크립트 퀴즈북 리마인드 Day 2: 같은 값인지 묻는 네 가지 방법자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와 ToPrimitive 흐름

같은 섹션의 대표 이미지

3 posts · latest first
객체가 ToPrimitive를 거쳐 + 연산자의 문자열 연결 또는 숫자 덧셈으로 갈라지는 흐름 다이어그램
Study26. 06. 25.

자바스크립트 퀴즈북 리마인드 Day 3: + 연산자와.

객체가 원시값으로 바뀌는 ToPrimitive 흐름, + 연산자의 문자열 연결과 숫자 덧셈 분기, Symbol.toPrimitive가 코드 리뷰에서 왜 중요한지 정리합니다.

26. 06. 25.SEOJing
;

정답은 이렇습니다.

text
6
(3, 4)

둘 다 객체를 문자열이나 숫자처럼 사용한 것처럼 보이지만, 내부에서 선택한 변환 경로가 다릅니다. point + 1은 기본 hint로 원시값을 만들고, 일반 객체에서는 보통 valueOf() 쪽이 먼저 의미를 가집니다. 템플릿 리터럴은 문자열 문맥이므로 toString() 쪽으로 가는 감각이 더 강합니다.

오늘의 핵심은 “JavaScript는 이상하게 변환한다”가 아닙니다. 코드 리뷰에서 더 실용적인 질문은 이겁니다.

text
이 값은 언제, 어떤 hint로, 어떤 primitive가 되는가?

흔한 착각: +는 숫자 덧셈이다

+는 가장 헷갈리는 연산자입니다. 숫자끼리 만나면 덧셈입니다.

js
1 + 2; // 3

하지만 둘 중 하나라도 문자열이 되면 연결입니다.

js
"1" + 2; // "12"
1 + "2"; // "12"

여기까지는 많이 압니다. 진짜 문제는 객체가 끼는 순간입니다.

js
[] + {}; // "[object Object]"
{
}
+[]; // 문맥에 따라 다르게 보일 수 있음
new Date(0) + 1; // 문자열 연결처럼 보이는 결과

이런 예제를 외우는 것은 별로 도움이 되지 않습니다. 대신 한 단계 앞을 봐야 합니다. 객체는 +에 바로 참여하지 않고 먼저 primitive로 변환됩니다. 그 결과가 문자열이면 연결, 아니면 숫자 쪽으로 갑니다.


ToPrimitive 흐름을 한 장으로 보기

객체가 ToPrimitive를 거쳐 + 연산자의 문자열 연결 또는 숫자 덧셈으로 갈라지는 흐름

객체가 원시값으로 바뀌는 흐름은 대략 이렇게 읽으면 됩니다.

  1. 연산이나 API가 객체에게 primitive를 요구한다.
  2. Symbol.toPrimitive가 있으면 가장 먼저 호출된다.
  3. 없으면 문맥에 따라 valueOf()와 toString()이 시도된다.
  4. primitive가 나오면 그 다음 연산 규칙이 적용된다.
  5. +에서는 결과 중 하나가 문자열이면 문자열 연결, 아니면 numeric 연산으로 간다.

중요한 점은 변환과 연산을 분리해서 읽는 것입니다.

js
const obj = {
  valueOf() {
    return 10;
  },
};

obj + 5;
// obj -> 10
// 10 + 5 -> 15
js
const obj = {
  toString() {
    return "10";
  },
};

obj + 5;
// obj -> "10"
// "10" + 5 -> "105"

같은 obj + 5라도 객체가 어떤 primitive를 반환하느냐에 따라 완전히 다른 코드가 됩니다.


Symbol.toPrimitive: 변환 지점을 직접 선언하기

Symbol.toPrimitive는 객체가 primitive로 바뀔 때 호출되는 well-known symbol입니다.

js
const price = {
  amount: 12000,
  [Symbol.toPrimitive](hint) {
    if (hint === "string") return "12,000원";
    return this.amount;
  },
};

Number(price); // 12000
`${price}`; // "12,000원"
price + 1000; // 13000

이 API를 실무에서 자주 직접 작성하진 않습니다. 그래도 알아야 하는 이유가 있습니다. 라이브러리나 도메인 객체가 “숫자처럼”, “문자열처럼” 동작할 때 변환 규칙이 객체 내부에 숨을 수 있기 때문입니다.

리뷰에서는 이런 질문을 하면 됩니다.

text
이 객체는 문자열 문맥과 숫자 문맥에서 같은 의미인가?
Symbol.toPrimitive/valueOf/toString이 비즈니스 규칙을 숨기고 있지는 않은가?

예를 들어 금액 객체가 문자열로는 "12,000원"을 반환하고 숫자로는 12000을 반환하는 것은 그럴듯합니다. 반대로 객체를 더했더니 내부 ID를 숫자로 바꾸는 식이라면, 읽는 사람이 연산 결과를 추적하기 어렵습니다.


폼 입력에서 자주 만나는 변환

프론트엔드에서는 객체 변환보다 폼 입력 변환이 더 자주 나옵니다.

js
Number(" "); // 0
Number(""); // 0
Number("3.14"); // 3.14
Number("3,000"); // NaN
Boolean({}); // true
Boolean([]); // true

공백 문자열이 0이 되는 이유는 숫자 변환 과정에서 공백이 trim된 뒤 빈 문자열처럼 취급되기 때문입니다. 빈 객체와 빈 배열은 Boolean 문맥에서 모두 truthy입니다. 그래서 이런 코드는 위험합니다.

js
const value = Number(input.value);

if (!value) {
  showEmptyMessage();
}

0, NaN, 빈 입력이 한꺼번에 섞입니다. 리뷰에서는 변환과 검증을 분리하는 편이 낫습니다.

js
const raw = input.value.trim();

if (raw === "") {
  showEmptyMessage();
} else {
  const value = Number(raw);
  if (Number.isNaN(value)) showInvalidNumberMessage();
}

이 코드는 길어졌지만 의도는 더 선명합니다. “비어 있음”과 “숫자로 파싱 실패”를 같은 falsey 처리에 맡기지 않습니다.


+를 리뷰할 때 보는 네 가지

1. 문자열을 만들려는 코드인가

js
const label = count + "개";

동작은 하지만 의도는 템플릿 리터럴이 더 잘 보입니다.

js
const label = `${count}개`;

단순 취향 문제가 아닙니다. 템플릿 리터럴은 “문자열을 만들고 있다”는 문맥을 코드에 남깁니다.

2. 숫자 덧셈 전에 파싱이 끝났는가

js
const total = input.value + 1000;

input.value는 문자열입니다. 여기서는 덧셈이 아니라 문자열 연결이 됩니다. 먼저 숫자 변환과 실패 처리를 분리해야 합니다.

3. 객체 변환 규칙이 숨어 있나

js
const next = money + fee;

money가 숫자인지, 금액 객체인지, BigInt인지 모르면 이 한 줄은 안전하지 않습니다. 도메인 객체라면 money.add(fee)처럼 의도를 드러내는 API가 더 나을 수 있습니다.

4. == 비교와 변환이 붙어 있나

Day 2에서 본 equality 문제는 변환 문제와 붙습니다.

js
if (input.value == 0) {
  // "", " ", false와 얽힐 수 있다
}

비교 전에 어떤 변환이 일어나는지 설명할 수 없다면, 비교식을 더 명시적으로 바꾸는 편이 좋습니다.


오늘의 정리

+를 보면 바로 덧셈으로 읽지 말고 두 단계를 나눠야 합니다.

  1. 피연산자가 primitive인지 확인한다.
  2. 객체라면 ToPrimitive 흐름을 탄다.
  3. 결과 중 하나가 문자열이면 문자열 연결이다.
  4. 그렇지 않으면 numeric 연산으로 간다.
  5. 폼 입력처럼 문자열에서 시작하는 값은 파싱/빈값/실패 처리를 분리한다.

이 흐름을 기억하면 괴상한 퀴즈를 많이 외우지 않아도 됩니다. 코드 리뷰에서 필요한 것은 “이 값이 어떤 primitive가 되는가”를 설명하는 능력입니다.


참고문헌

  • 프런트엔드 레벨을 높이는 자바스크립트 퀴즈북, Chapter 01–02 타입/형변환 관련 문제와 진규의 Notion 복습 노트
  • MDN Web Docs, Symbol.toPrimitive와 type conversion 관련 문서
  • ECMAScript Language Specification, ToPrimitive / Addition operator / Abstract Operations

퀴즈

Quiz1 / 4
Q.다음 코드의 결과로 맞는 것은 무엇일까요?
js
const obj = { valueOf() { return 2; }, toString() { return 'two'; } };
console.log(obj + 3);
==, ===, Object.is, SameValueZero의 차이를 정리한 JavaScript equality 다이어그램
Study26. 06. 24.

자바스크립트 퀴즈북 리마인드 Day 2: 같은 값인지 묻는 네.

==, ===, Object.is, SameValueZero가 각각 어떤 비교 알고리즘을 쓰는지 정리하고, includes와 indexOf, Map/Set 키 비교에서 생기는 프론트엔드 리뷰 포인트를 잡습니다.

26. 06. 24.SEOJing
JavaScript Number, safe integer, NaN, -0, BigInt를 한 장으로 정리한 다이어그램
Study26. 06. 23.

자바스크립트 퀴즈북 리마인드 Day 1: 숫자는 왜 가끔 믿을.

자바스크립트의 Number가 왜 정수처럼 보여도 부동소수점 모델 위에서 움직이는지, NaN과 -0, safe integer, BigInt를 프론트엔드 코드 리뷰 관점에서 다시 정리합니다.

26. 06. 23.SEOJing