Cannot set properties of undefined (setting 'x') — 원인·해결·예방
자바스크립트에서 값을 대입하다가 "TypeError: Cannot set properties of undefined (setting 'x')"이 뜨면, undefined인 값에 x라는 속성을 설정(할당)하려 했다는 뜻이다. 앞서 다룬 읽기(reading) 에러들과 사촌지간인데, 이번엔 값을 꺼내는 게 아니라 넣으려다 터진 것이다. obj.a.b = 1을 했는데 obj.a가 아직 없거나, 배열의 빈 자리에 속성을 대입하거나, 객체를 만들지도 않고 그 안에 값을 넣으려 할 때 난다. 중요한 차이가 하나 있다. 읽기 에러는 옵셔널 체이닝(?.)으로 방어할 수 있지만, 할당은 그렇지 않다. 옵셔널 체이닝은 대입식 왼쪽에 쓸 수 없기 때문이다. 그래서 해법은 "값을 넣기 전에 객체를 먼저 만드는" 쪽이다. 이 글은 이 에러의 의미, 읽기 에러와의 차이, 원인과 해결을 정리한다.

이 글의 구성
🔎 에러 메시지의 의미
이 에러는 TypeError로, undefined인 값에 속성을 대입하려 할 때 발생한다. obj.x = value 같은 코드에서 점 왼쪽의 obj가 undefined이면, "없는 것에 값을 넣을 수 없다"며 엔진이 멈춘다. 값을 읽는 게 아니라 쓰는(설정하는) 동작이라는 점이 앞선 읽기 에러들과 다른 지점이다.
메시지의 (setting 'x')가 단서다. reading이 아니라 setting이라고 명시돼 있어, "x라는 속성에 값을 넣으려다 막혔다"는 뜻이다. 정작 undefined였던 건 x가 아니라 그 앞의 대상이다. obj.config.mode = "dark"에서 이 에러가 났다면, mode가 아니라 obj.config가 undefined였던 것이다. 예전 브라우저는 같은 상황을 "Cannot set property 'x' of undefined"라는 옛 문구로 보여줬는데, 의미는 같다.
핵심 — 담을 그릇이 아직 없다
읽기 에러가 "빈 상자에서 꺼내려 했다"면, 이 에러는 "없는 상자에 넣으려 했다"이다. 값을 넣을 대상 객체가 아직 만들어지지 않은 것이다. 그래서 해결은 "넣기 전에 그릇부터 준비한다"로 요약된다. 메시지의 (setting 'x')에서 x의 한 단계 왼쪽 값이 undefined인지부터 확인한다.
⚖️ setting vs reading의 차이
읽기 에러와 쓰기 에러는 메시지도 비슷하고 원인도 닮았지만, 해결 도구가 결정적으로 다르다. 이 차이를 모르면 엉뚱한 방법으로 막으려다 또 막힌다.
| 구분 | reading (읽기) | setting (쓰기) |
|---|---|---|
| 동작 | obj.x 값을 꺼냄 | obj.x = 값 을 넣음 |
| 옵셔널 체이닝(?.) | 방어 가능 (obj?.x) | 불가 (대입 왼쪽에 못 씀) |
| 해결 방향 | ?. · ?? 로 방어 | 대상 객체를 먼저 생성 |
핵심은 옵셔널 체이닝을 대입식 왼쪽에 쓸 수 없다는 점이다. obj?.x = 1처럼 쓰면 "대입식 왼쪽이 잘못됐다(Invalid left-hand side in assignment)"는 문법 에러가 난다. 값을 읽을 때는 obj?.x로 조용히 undefined를 받으면 되지만, 값을 넣을 때는 넣을 대상 자체가 반드시 존재해야 하기 때문이다. 그래서 쓰기 에러의 해법은 방어가 아니라 준비, 즉 대상 객체를 먼저 만드는 것이다.
🧩 자주 나오는 발생 원인 4가지
원인 1 — 중간 객체를 안 만들고 깊이 대입
가장 흔하다. obj.a.b = 1을 하는데 obj는 있어도 obj.a가 없는 경우다. 중간 단계(obj.a)가 undefined라 그 안의 b에 값을 못 넣는다. 중첩 구조를 한 번에 대입하려다 생긴다.
원인 2 — 객체·변수를 선언하지 않음
담을 객체를 만들지 않은 채 그 속성에 바로 대입한 경우다. let config;처럼 선언만 하고 {}로 초기화하지 않으면 config는 undefined라, config.mode에 값을 넣으려다 터진다. (변수를 아예 선언조차 안 했다면 이 TypeError가 아니라 ReferenceError가 난다.)
원인 3 — 배열 빈 자리에 속성 대입
arr[i]가 아직 없는데 arr[i].name = x처럼 그 안에 대입한 경우다. 해당 인덱스에 객체를 먼저 넣지 않아 arr[i]가 undefined인 상태다. 반복문으로 데이터를 채울 때 자주 생긴다.
원인 4 — this가 undefined
클래스·엄격 모드 함수에서 메서드를 떼어내 호출하면 this가 undefined가 되어, this.x = 값에서 이 에러가 난다. 이벤트 핸들러나 콜백으로 메서드를 넘길 때 this를 잃는 경우다.
💻 재현과 진단
전형적인 재현이다. 중간 객체가 없는데 그 안에 대입하는 경우다.
// obj는 있지만 config는 없음
const obj = {};
obj.config.mode = "dark";
// ✗ TypeError: Cannot set properties of undefined (setting 'mode')
// 한 단계 왼쪽 확인 — undefined 면 범인
console.log(obj.config); // undefined
진단은 읽기 에러와 같다. (setting 'x')의 한 단계 왼쪽 값을 찍어 undefined인지 본다. 콘솔의 스택 추적으로 어느 줄에서 났는지 확인하고, 그 대입식의 대상 객체가 그 시점에 만들어져 있는지 점검한다.
// 배열 빈 자리에 대입 → setting 에러
const rows = [];
rows[0].name = "김개발"; // rows[0]이 undefined
// ✗ Cannot set properties of undefined (setting 'name')
🛠 해결 — 객체를 먼저 만들기
이 에러의 해법은 "값을 넣기 전에 대상 객체를 준비하는 것"이다. 옵셔널 체이닝은 여기서 쓸 수 없으니, 객체 생성이 정답이다.
// ✓ 중간 객체를 먼저 만든다
const obj = {};
obj.config = obj.config || {}; // 없으면 빈 객체로
obj.config.mode = "dark";
// ✓ 널 병합 대입(??=) — 없을 때만 생성
obj.config ??= {};
obj.config.mode = "dark";
상황별로 정리하면 이렇다. 공통 원칙은 "대입 경로의 각 단계가 실제로 존재하도록 보장한다"는 것이다.
상황별 해결 방법
깊은 중첩이 자주 필요하다면, 매번 단계별로 만드는 대신 처음부터 구조를 통째로 정의해 두는 편이 깔끔하다. const obj = { config: { mode: "" } }처럼 리터럴로 뼈대를 잡아 두면, 이후 obj.config.mode 대입에서 중간 단계가 없어 막힐 일이 없다.
읽기는 옵셔널 체이닝으로 피하지만, 쓰기는 못 피한다. 값을 넣기 전에 담을 그릇부터 만들어라.
— 대입 왼쪽엔 ?.를 못 쓴다
💬 자주 묻는 질문 5가지
Q1reading 에러랑 뭐가 다른가요?
reading은 undefined에서 값을 꺼내려다, setting은 undefined에 값을 넣으려다 나는 에러입니다. 원인(점 왼쪽이 undefined)은 같지만 해결이 다릅니다. 읽기는 옵셔널 체이닝으로 방어할 수 있지만, 쓰기는 대상 객체를 먼저 만들어야 합니다.
Q2obj?.x = 1 로 막으면 안 되나요?
안 됩니다. 옵셔널 체이닝은 대입식 왼쪽에 쓸 수 없어 "Invalid left-hand side in assignment"라는 문법 에러가 납니다. 값을 넣으려면 넣을 대상이 실제로 있어야 하기 때문이죠. obj.x = obj.x || {} 나 ??= 로 대상을 먼저 만드세요.
Q3??= 는 어떻게 동작하나요?
널 병합 대입(??=)은 왼쪽 값이 null이나 undefined일 때만 오른쪽 값을 대입합니다. obj.config ??= {}는 config가 없을 때만 빈 객체로 만들고, 이미 있으면 그대로 둡니다. 기존 데이터를 덮어쓰지 않고 안전하게 초기화할 때 유용합니다.
Q4반복문으로 배열을 채우다 나요.
arr[i]가 아직 없는데 arr[i].name에 바로 대입한 경우입니다. 해당 인덱스에 객체를 먼저 넣어야 합니다. arr[i] = {}로 만든 뒤 arr[i].name = 값을 대입하거나, arr.push({ name: 값 })처럼 완성된 객체를 통째로 넣으세요.
Q5this.x = 값 에서 나요.
클래스·엄격 모드 함수에서 메서드를 떼어 호출해 this가 undefined가 된 경우입니다. 이벤트 핸들러·콜백으로 메서드를 넘길 때 자주 생기죠. bind로 this를 고정하거나 화살표 함수로 감싸 원래 객체를 가리키게 하면 풀립니다.
📌 결론
"Cannot set properties of undefined (setting 'x')"은 undefined인 값에 속성을 대입하려다 나는 TypeError다. 읽기 에러와 원인은 같지만, 값을 넣는 동작이라 해결이 다르다. 옵셔널 체이닝은 대입식 왼쪽에 못 쓰므로, 값을 넣기 전에 담을 객체를 먼저 만들어야 한다.
실무 원칙은 다음과 같다.
✏️ 대입 대상을 확인한다. (setting 'x')의 한 단계 왼쪽 값이 undefined인지 console.log로 보고, 그 대상이 존재하는지 점검한다.
🧱 그릇을 먼저 만든다. obj.a = obj.a || {} 나 ??=로 중간 객체를 보장하고, 깊은 중첩은 리터럴로 구조를 통째로 정의한다.
🚫 옵셔널 체이닝에 기대지 않는다. ?.는 읽기 방어용이지 대입엔 쓸 수 없다. 배열은 요소를, this는 바인딩을 먼저 챙긴다.
setting undefined 에러 체크리스트
본 글은 자바스크립트 TypeError(set of undefined)의 일반적 원인과 해결 방법을 정리한 자료다. 코드 수정은 영향 범위를 확인한 뒤 신중히 적용한다.
#JavaScript #TypeError #Cannotsetproperties #undefined #자바스크립트에러 #객체초기화 #널병합대입 #nullish #옵셔널체이닝 #중첩객체 #this바인딩 #프론트엔드 #JS디버깅 #런타임에러 #웹개발
'IT' 카테고리의 다른 글
| Assignment to constant variable — const 재할당 원인·해결·예방 (0) | 2026.07.01 |
|---|---|
| Unexpected end of JSON input — JSON.parse 원인·해결·예방 (1) | 2026.07.01 |
| Cannot read properties of null (reading 'x') — 원인·해결·예방 (0) | 2026.07.01 |
| Maximum call stack size exceeded — 무한 재귀 원인·해결 (0) | 2026.06.30 |
| Uncaught (in promise) — 처리 안 된 Promise 거부 원인·해결 (0) | 2026.06.30 |
댓글