/개발/Cannot read properties of undefined 에러 30초에 잡는 실무 런북
개발JavaScriptTypeError

Cannot read properties of undefined 에러 30초에 잡는 실무 런북

'Cannot read properties of undefined (reading map/length/id)' 에러를 원문 패턴별로 분류해 30초에 원인을 특정합니다. optional chaining·기본값 구조분해·React 함정·TypeScript strict 차단까지 복붙 코드로 정리했습니다.

Cannot read properties of undefined 에러 30초에 잡는 실무 런북

TypeError: Cannot read properties of undefined 30초 만에 잡는 실무 런북

또 undefined? 이제 콘솔 뒤지지 말고 패턴으로 잡자

TypeError: Cannot read properties of undefined (reading 'map').

JavaScript, Node.js, React로 일하다 보면 하루에도 몇 번씩 마주치는 이 에러. 매번 콘솔을 뒤지고 console.log를 박아가며 원인을 추적하느라 시간을 쓰고 있다면, 이 글이 그 루틴을 끝내줄 겁니다.

핵심은 단순합니다. 에러 원문의 마지막 괄호 안 단어(reading '...')만 보면 원인 카테고리가 거의 확정됩니다. 그다음은 상황별 방어 코드를 복붙하면 재발까지 차단됩니다. 30초 안에 끝냅시다.

1. 에러 원문 패턴 3종 분류표

가장 먼저 볼 것은 reading 뒤에 붙은 이름입니다. 그게 무엇을 읽으려다 실패했는지 알려주고, 곧 원인을 특정해 줍니다.

에러 원문대표 원인30초 1차 조치
reading 'map' reading 'filter' reading 'forEach'배열 초기값이 undefined, 비동기 데이터 미도착useState([]), arr ?? []
reading 'length'문자열·배열이 미할당 상태str ?? '', list?.length ?? 0
reading 'id' reading 'name'객체 props/응답이 미전달obj?.id, const { id } = obj ?? {}

이 표만 외워도 절반은 끝납니다. 이제 각 케이스를 재현 코드 → 콘솔 출력 → 방어 후 코드 세트로 봅시다.

케이스 A: reading 'map' (배열 초기값 누락)

JavaScript
// 재현 코드
function UserList({ users }) {
  return users.map((u) => u.name); // users가 아직 undefined
}
UserList({});
CODE
Uncaught TypeError: Cannot read properties of undefined (reading 'map')
JavaScript
// 방어 후
function UserList({ users = [] }) {
  return (users ?? []).map((u) => u.name); // 항상 배열 보장
}

케이스 B: reading 'length' (문자열·배열 미할당)

JavaScript
// 재현 코드
function getInitial(name) {
  return name.length > 0 ? name[0] : '?'; // name 미전달
}
getInitial();
CODE
Uncaught TypeError: Cannot read properties of undefined (reading 'length')
JavaScript
// 방어 후
function getInitial(name = '') {
  return name.length > 0 ? name[0] : '?';
}

케이스 C: reading 'id' (객체 props 미전달)

JavaScript
// 재현 코드
function ProfileCard({ user }) {
  return `#${user.id} ${user.name}`; // user가 undefined
}
ProfileCard({});
CODE
Uncaught TypeError: Cannot read properties of undefined (reading 'id')
JavaScript
// 방어 후
function ProfileCard({ user }) {
  const { id = 0, name = '이름없음' } = user ?? {};
  return `#${id} ${name}`;
}

2. 스택트레이스에서 진짜 원인 라인 찾기

에러 메시지 아래 스택트레이스에서 최상단이 원인이 아닌 경우가 많습니다. React 내부 라이브러리 코드가 최상단에 뜨기 때문이죠. 찾아야 할 것은 at 라인 중 "내 코드 파일" 첫 줄입니다.

CODE
TypeError: Cannot read properties of undefined (reading 'map')
    at renderWithHooks (react-dom.js:16305)     ← 라이브러리, 무시
    at mountIndeterminateComponent (react-dom.js:20074)  ← 무시
👉  at UserList (UserList.jsx:12:18)             ← 여기! 내 코드 첫 줄
    at beginWork (react-dom.js:22270)

읽는 순서: 위에서 아래로 내려가다 처음 만나는 src/ 내 파일을 찾으면 됩니다. UserList.jsx:12:18 = 12번째 줄 18번째 컬럼. 번들된 프로덕션 코드에서 main.abc123.js:1:80421처럼 보인다면, 빌드 시 **소스맵(.map 파일)**을 활성화해두면 브라우저 DevTools가 원본 위치로 역추적해 줍니다.

3. 복붙 방어 패턴 4종

상황별로 그대로 붙여 쓰는 스니펫입니다.

JavaScript
// 1) optional chaining — 중간 경로가 없어도 undefined 반환(에러 X)
const city = user?.address?.city;

// 2) nullish 병합 — null/undefined일 때만 기본값
const count = data?.count ?? 0;

// 3) 기본값 구조분해 — props/응답 방어의 왕도
const { items = [], total = 0 } = response ?? {};

// 4) 배열·객체 초기화 — map 전에 항상 배열 보장
(list ?? []).map(render);

?. vs &&, ?. vs ?? 차이

혼동하기 쉬운 조합을 표로 정리합니다.

표현식xundefined일 때x0/''/false일 때용도
x && x.bundefinedfalsy값 그대로 반환렌더 조건
x?.bundefined (에러 X)0.b 시도(주의)안전 접근
x ?? [][]원래 값 유지 (0, '' 살림)기본값
x || [][][]로 덮어씀 (버그 위험)지양
a?.b ?? [][]안전 접근 + 기본값베스트 조합

포인트: 0이나 빈 문자열을 유효값으로 다뤄야 한다면 || 대신 반드시 ??를 쓰세요. 실무에서 "가격 0원이 기본값으로 덮여버리는" 버그가 바로 이 차이에서 나옵니다.

4. React 특화 함정 + TypeScript 재발 차단

useEffect 데이터 페칭 타이밍

React에서 reading 'map'이 터지는 가장 흔한 이유는 첫 렌더 시점에 데이터가 아직 없기 때문입니다. 마운트 순서를 보면 명확합니다.

CODE
① 마운트 → ② 1차 렌더(data = undefined) 💥 여기서 터짐
        → ③ useEffect 실행(fetch 시작)
        → ④ 응답 도착 → setState
        → ⑤ 재렌더(data = 실제 값) ✅

즉 fetch가 끝나기 전인 ②단계에서 data.map(...)을 하면 죽습니다. 해결은 초기값과 조건부 렌더링입니다.

JSX
function Posts() {
  const [posts, setPosts] = useState([]); // 초기값 [] 필수

  useEffect(() => {
    fetch('/api/posts').then(r => r.json()).then(setPosts);
  }, []);

  // data && <X/> 로 렌더 가드
  return posts?.length ? posts.map(p => <li key={p.id}>{p.title}</li>)
                       : <p>로딩 중…</p>;
}

실무 경험 한마디: React 19의 use 훅과 Suspense가 확산되면서 로딩 상태 처리 패턴이 바뀌고 있지만, "초기값을 배열/객체로 잡아라"는 원칙은 여전히 유효합니다. 팀 컨벤션으로 "리스트 state의 초기값은 무조건 []"를 못 박아두면 이 에러의 80%가 사라집니다.

TypeScript strictNullChecks로 컴파일 단계에서 막기

tsconfig.json"strictNullChecks": true(또는 "strict": true)를 켜면 런타임이 아니라 컴파일 시점에 잡힙니다.

TypeScript
// before: strictNullChecks 없이 — 통과되지만 런타임에서 터짐
function total(items: number[]) {
  return items.length;
}

// after: strictNullChecks: true
function total(items?: number[]) {
  return items.length;
  //     ~~~~~ Error: 'items' is possibly 'undefined'. ← 컴파일 에러!
}
// 컴파일러가 강제로 방어 코드를 작성하게 만든다
function totalSafe(items?: number[]) {
  return items?.length ?? 0; // ✅
}

최근엔 신규 프로젝트가 strict 모드를 기본 채택하는 흐름이라, 이 설정 하나로 undefined 에러를 배포 전에 걸러낼 수 있습니다.

결론: 오늘 바로 적용할 체크리스트

  • 리스트 state 초기값은 무조건 useState([])
  • props 받을 때 const { items = [] } = props로 기본값 구조분해
  • .map 전에 (list ?? []).map(...)
  • 기본값엔 || 대신 ??
  • tsconfigstrictNullChecks: true
  • 스택트레이스는 "내 파일 첫 줄"부터 읽기

자주 묻는 질문 (FAQ)

Q. 서버 응답은 분명 오는데 왜 undefined가 뜨나요? A. 응답이 도착하는 시점(④)보다 첫 렌더(②)가 먼저 실행되기 때문입니다. 응답 자체는 정상이어도 초기 렌더 순간엔 값이 없습니다. state 초기값을 []/{}로 주고 조건부 렌더링으로 로딩 상태를 처리하면 해결됩니다.

Q. optional chaining(?.)만 붙이면 끝인가요? A. ?.는 "에러 없이 undefined 반환"까지만 해줍니다. 이후 .map이나 .length를 이어 쓰면 또 터지므로, list?.map(...)처럼 체인을 이어가거나 (list ?? []).map(...)으로 기본값까지 함께 보장해야 완전합니다.

Q. ??||는 그냥 취향 차이 아닌가요? A. 아닙니다. ||0, '', false도 falsy로 보고 덮어쓰지만, ??는 오직 null/undefined만 대체합니다. 숫자 0이나 빈 문자열이 유효값인 경우 ||를 쓰면 데이터가 뭉개지는 버그가 생기니 기본값엔 ??를 쓰세요.

✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.

초안 · AI (Content Reviewer)·검토 · Nodelog 편집자·발행 ·

댓글

첫 번째 댓글을 남겨보세요.