Cannot read properties of undefined (reading 'x') — 원인·해결·예방
자바스크립트를 쓰다 보면 콘솔에서 가장 자주 만나는 빨간 줄이 이것이다. "TypeError: Cannot read properties of undefined (reading 'x')". 해석하면 "undefined인 값에서 x라는 속성을 읽으려 했다"는 뜻이다. 분명 객체가 있을 줄 알았는데 그 자리가 undefined라, 그 안의 속성에 손을 뻗는 순간 터진다. API 응답이 아직 안 왔거나, 객체 경로에 오타가 있거나, 함수가 아무것도 반환하지 않았을 때 흔하다. 다행히 메시지가 친절하다. 괄호 안의 (reading 'x')가 "어느 속성에서 막혔는지"를 콕 집어 준다. 이 글은 이 에러의 의미, 원인, 그리고 옵셔널 체이닝을 비롯한 해결·예방을 정리한다.

이 글의 구성
🔎 에러 메시지의 의미
이 에러는 TypeError의 한 종류다. 자바스크립트에서 값이 undefined인데 그 값의 속성(property)에 접근하려 하면 발생한다. undefined는 "아무것도 없음"을 뜻하는데, 없는 것에서 무언가를 꺼내려 하니 엔진이 멈추고 에러를 던진다.
메시지를 한 조각씩 보면 명확하다. Cannot read properties of undefined는 "undefined의 속성을 읽을 수 없다"는 뜻이고, 뒤따르는 (reading 'x')는 "그중에서도 x라는 속성을 읽으려다 막혔다"는 구체적 단서다. 즉 코드 어딘가에서 무언가.x를 했는데, 그 무언가가 undefined였던 것이다. 참고로 예전 브라우저는 같은 상황을 "Cannot read property 'x' of undefined"라는 옛 문구로 보여줬는데, 의미는 동일하다.
핵심 — 점 왼쪽이 undefined였다
user.profile.name에서 이 에러가 났다면, name이 문제가 아니라 그 앞의 user.profile이 undefined였다는 뜻이다. (reading 'name')은 "name을 읽으려는 순간" 막혔다고 알려줄 뿐, 정작 비어 있던 건 점 바로 왼쪽 값이다. 그래서 디버깅은 "막힌 속성의 한 단계 왼쪽"을 들여다보는 데서 시작한다.
🧩 자주 나오는 발생 원인 4가지
원인 1 — 비동기 데이터가 아직 안 옴
가장 흔하다. API 응답이 도착하기 전에 화면이 먼저 그려지면서 data.user처럼 접근하는 경우다. 처음 렌더링 시점엔 data가 undefined라 속성 접근에서 터진다. React 같은 프레임워크에서 특히 잦다.
원인 2 — 객체 경로 오타·잘못된 중첩 접근
res.data.items인데 res.datas.items로 잘못 쓰거나, 한 단계를 빼먹은 경우다. 중간 경로가 undefined가 되면서 그다음 속성에서 에러가 난다. 깊은 중첩일수록 자주 생긴다.
원인 3 — 함수가 값을 반환하지 않음
return을 빠뜨린 함수는 undefined를 돌려준다. 그 결과를 받아 .property로 접근하면 에러다. find()가 일치 항목을 못 찾아 undefined를 반환했을 때도 마찬가지다.
원인 4 — 배열 빈 인덱스·구조 분해 실패
arr[5]가 없는데 arr[5].name을 읽거나, 구조 분해에서 없는 객체를 풀어 쓰는 경우다. 빈 배열의 첫 요소(arr[0])에 접근하는 실수도 흔하다.
💻 재현과 (reading 'x') 읽는 법
전형적인 재현 코드다. 중첩 객체에서 중간 경로가 비어 있을 때 터진다.
// user는 있지만 profile은 아직 없음
const user = { name: "김개발" };
// profile이 undefined → 그 안의 name 접근에서 터짐
console.log(user.profile.name);
// ✗ TypeError: Cannot read properties of undefined (reading 'name')
메시지 읽는 법이 핵심이다. (reading 'name')은 "name을 읽다 막혔다"는 뜻이고, 진짜 undefined였던 건 그 한 단계 왼쪽인 user.profile이다. 콘솔의 에러를 클릭하면 몇 번째 줄에서 났는지 스택 추적이 나오니, 그 줄에서 "막힌 속성의 왼쪽"을 확인하면 된다.
// ✓ 어디서 끊기는지 한 단계씩 찍어 확인
console.log(user); // { name: "김개발" }
console.log(user.profile); // undefined ← 여기가 범인
🛠 해결 방법 — 옵셔널 체이닝 외
가장 깔끔한 현대적 해법은 옵셔널 체이닝(?.)이다. ?. 바로 앞 값이 undefined나 null이면 더 들어가지 않고 안전하게 undefined를 돌려준다. 주의할 점은 ?.가 "바로 앞 한 단계"만 보호한다는 것이다. a.b?.c에서 a가 undefined면 a.b를 먼저 평가하다 여전히 터진다. 그래서 깊은 체인은 위험한 단계마다 a?.b?.c처럼 붙여 줘야 한다.
// ✓ 옵셔널 체이닝 — profile이 없어도 안전
console.log(user.profile?.name); // undefined (에러 X)
// ✓ 기본값까지 — 없으면 대체값
const name = user.profile?.name ?? "이름 없음";
// ✓ 배열·함수 호출에도
arr?.[0]?.name;
obj.method?.();
상황별로 손이 더 가는 가드도 함께 알아 두면 좋다. 어떤 자리에선 옵셔널 체이닝보다 명시적 분기·기본값이 더 읽기 쉽다.
상황별 방어 방법
옵셔널 체이닝은 강력하지만 만능은 아니다. 에러를 막아 주는 것과 데이터가 실제로 있는 건 다른 문제다. 화면에 계속 "이름 없음"만 뜬다면, ?.로 에러는 가렸어도 정작 profile이 비어 있는 근본 원인은 그대로다. 그래서 방어와 함께 "왜 비었는지"를 늘 같이 본다.
⚖️ undefined vs null의 차이
이 에러의 사촌으로 "Cannot read properties of null (reading 'x')"이 있다. 메시지가 거의 같지만 비어 있던 값의 종류가 다르다. 둘을 구분하면 원인 추적이 빨라진다.
| 값 | 의미 | 주로 어디서 |
|---|---|---|
| undefined | 값이 아직 할당되지 않음 | 없는 속성·미반환 함수·비동기 전 |
| null | "비어 있음"을 일부러 넣음 | DOM 못 찾은 querySelector, 명시적 초기화 |
쉽게 말해 undefined는 "아직 안 채워짐", null은 "비었다고 의도적으로 표시함"이다. 예를 들어 document.querySelector가 요소를 못 찾으면 null을 반환하므로, 그 결과에 .value를 바로 접근하면 "of null" 쪽 에러가 난다. 다행히 옵셔널 체이닝(?.)과 널 병합(??)은 undefined와 null을 똑같이 안전하게 처리하므로, 해결 도구는 양쪽에 공통으로 쓸 수 있다.
(reading 'x')는 막힌 속성을 알려줄 뿐, 비어 있던 건 점 왼쪽이다. 옵셔널 체이닝으로 막되, 왜 비었는지는 따로 고쳐라.
— 에러를 가리는 것과 데이터가 있는 건 다르다
💬 자주 묻는 질문 5가지
Q1(reading 'name')이면 name이 문제인가요?
아닙니다. name을 읽으려는 순간 막혔다는 뜻이고, 정작 undefined였던 건 그 앞(점 왼쪽) 값입니다. user.profile.name에서 이 에러가 나면 profile이 비어 있던 것이죠. 디버깅은 막힌 속성의 한 단계 왼쪽부터 확인하세요.
Q2옵셔널 체이닝만 붙이면 끝인가요?
에러는 막아 주지만 근본 해결은 아닙니다. ?.는 값이 없으면 undefined로 단락할 뿐, 그 값이 왜 비었는지는 그대로입니다. 화면에 데이터가 안 뜬다면 오타·미반환·비동기 타이밍 같은 진짜 원인을 따로 고쳐야 합니다.
Q3API 데이터에서 자꾸 나요.
응답이 도착하기 전 첫 렌더링에서 객체가 undefined라 생기는 전형적 경우입니다. 데이터 도착 전에는 로딩 상태를 먼저 보여주고, 접근 시 옵셔널 체이닝과 기본값을 함께 쓰세요. 초기 상태를 빈 객체나 빈 배열로 두는 것도 방법입니다.
Q4?? 와 || 는 뭐가 다른가요?
|| 는 undefined·null뿐 아니라 0·빈 문자열·false 같은 falsy 값도 모두 대체합니다. ?? (널 병합)는 오직 undefined·null일 때만 대체하죠. 수량 0이나 빈 문자열을 유효한 값으로 살려야 한다면 ?? 를 쓰는 게 안전합니다.
Q5"of null" 에러는 또 뭔가요?
메시지는 같고 비어 있던 값이 null인 경우입니다. undefined가 "아직 안 채워짐"이라면 null은 "비었다고 의도적으로 표시함"이죠. querySelector가 요소를 못 찾으면 null을 반환하는 게 대표적입니다. 해결법(옵셔널 체이닝·널 병합)은 양쪽에 똑같이 통합니다.
📌 결론
"Cannot read properties of undefined (reading 'x')"는 undefined인 값에서 속성을 읽으려다 나는 TypeError다. 메시지의 (reading 'x')는 막힌 속성을, 진짜 빈 값은 그 한 단계 왼쪽을 가리킨다. 그래서 디버깅은 "막힌 속성의 왼쪽"을 들여다보는 데서 시작한다.
실무 원칙은 다음과 같다.
🔍 메시지를 정확히 읽는다. (reading 'x')의 x가 아니라 점 왼쪽 값이 undefined였다. 스택 추적으로 그 줄을 찾아 한 단계씩 확인한다.
🛡 옵셔널 체이닝으로 방어한다. ?.와 ?? 로 중첩 접근을 안전하게 만들되, 에러를 가리는 것과 데이터가 있는 건 다름을 기억한다.
⏳ 근본 원인을 고친다. 오타·미반환 함수·비동기 타이밍 중 무엇인지 짚고, 데이터 도착 전에는 로딩 상태와 초기값으로 막는다.
undefined 속성 에러 체크리스트
본 글은 자바스크립트 TypeError의 일반적 원인과 해결 방법을 정리한 자료다. 코드 수정은 영향 범위를 확인한 뒤 신중히 적용한다.
#JavaScript #TypeError #Cannotreadproperties #undefined #자바스크립트에러 #옵셔널체이닝 #optionalchaining #널병합 #nullishcoalescing #프론트엔드 #JS디버깅 #런타임에러 #null과undefined #웹개발 #코딩초보
'IT' 카테고리의 다른 글
| SyntaxError: Unexpected token — 원인·해결·예방 (0) | 2026.06.30 |
|---|---|
| ReferenceError: x is not defined — 원인·해결·예방 (1) | 2026.06.29 |
| HTTP 상태 코드 완전 정리 — 1xx~5xx 분류와 4xx·5xx 차이 (1) | 2026.06.28 |
| Mixed Content 에러 — 혼합 콘텐츠·HTTPS 속 HTTP 차단 원인·해결 (0) | 2026.06.28 |
| HTTP 415 Unsupported Media Type — 미디어 타입 미지원 원인·해결 (0) | 2026.06.28 |
댓글