2026. 7. 16.
TypeScript Object is possibly 'undefined', 느낌표를 붙여도 될까? 빈 값 동작으로 고르는 해결법
TypeScript의 Object is possibly undefined 오류가 뜰 때 느낌표로 숨기지 않고 guard, optional chaining, 기본값을 고른 뒤 타입 검사와 빈 값 테스트로 확인하는 순서입니다.

Object is possibly 'undefined'라는 빨간 줄이 뜨면 가장 짧은 수정은 변수 뒤에 !를 붙이는 것처럼 보입니다. 하지만 이 기호는 값이 있다고 TypeScript에 주장할 뿐, 값이 없는 실행 순간을 막아 주지 않습니다.
먼저 할 일은 하나입니다. 오류가 난 식에서 어느 값에 | undefined가 붙었는지 확인하고, 그 값이 없을 때 화면이나 함수가 무엇을 해야 하는지 정하세요. 그 결정이 끝나면 guard, optional chaining, 기본값, assertion 가운데 맞는 도구가 자연스럽게 갈립니다.
네 가지 선택은 값이 없을 때의 행동이 다릅니다
| 값이 없을 때 원하는 행동 | 먼저 고를 코드 | 대표 상황 |
|---|---|---|
| 이후 코드를 실행하면 안 된다 | if (!value) return 또는 throw | 필수 사용자, 필수 설정 |
| 해당 작업만 건너뛴다 | value?.method() | 선택 콜백, 선택 UI |
| 의미 있는 대체값을 쓴다 | nullish coalescing | 표시 이름, 횟수, 설정값 |
| 외부 규칙으로 반드시 존재한다 | 검증 함수 뒤 제한적인 ! | 앱 시작 시 이미 검증한 설정 |
이 표의 핵심은 문법이 아니라 빈 값이 제품에서 어떤 의미인지입니다. 사용자 조회 실패가 곧 404라면 early return이 맞고, 부가 설명이 없을 뿐이라면 optional chaining이 맞을 수 있습니다.
먼저 어느 값이 undefined인지 찾으세요
다음 코드는 AI가 흔히 만드는 사용자 조회 예제입니다.
type User = { id: string; name: string };
function getUserName(users: User[], selectedId: string) {
const user = users.find((item) => item.id === selectedId);
return user.name;
}
Array.find는 일치하는 항목이 없으면 undefined를 돌려줄 수 있습니다. 따라서 빨간 줄의 원인은 name이 아니라 user입니다. 에디터에서 user에 마우스를 올렸을 때 User | undefined가 보이면 원인을 찾은 셈입니다.
필수 사용자를 못 찾았을 때 호출자에게 실패를 알리고 싶다면 먼저 분기합니다.
function getUserName(users: User[], selectedId: string) {
const user = users.find((item) => item.id === selectedId);
if (!user) {
throw new Error(`User not found: ${selectedId}`);
}
return user.name;
}
TypeScript는 throw 아래 경로에는 User만 남는다고 분석합니다. 이런 흐름 기반 narrowing은 단지 컴파일러를 달래는 기술이 아니라, 실패 상태를 코드에 드러내는 방법입니다. catch에서 받은 외부 값도 같은 원리로 다뤄야 하며, err is of type unknown을 Error로 좁히는 순서에서 이어서 연습할 수 있습니다.
optional chaining은 “없으면 건너뛰기”입니다
선택 콜백이라면 호출하지 않아도 정상일 수 있습니다.
type Options = {
onSaved?: (id: string) => void;
};
function finishSave(id: string, options: Options) {
options.onSaved?.(id);
}
?.는 바로 왼쪽 값이 null 또는 undefined일 때 그 접근이나 호출을 멈추고 undefined를 반환합니다. 체인 바깥의 계산까지 모두 멈추는 것은 아닙니다. 그래서 price?.amount / 100처럼 뒤에서 숫자 계산을 계속하면 여전히 안전하지 않을 수 있습니다.
기본값은 “없어도 이 값이면 의미가 같다”일 때 씁니다
type Cart = { itemCount?: number };
function renderCount(cart: Cart) {
const count = cart.itemCount == null ? 0 : cart.itemCount;
return `${count}개`;
}
여기서 0은 “상품 없음”이라는 실제 의미가 있으므로 좋은 기본값입니다. nullish coalescing 연산자(??)는 ||와 달리 원래 값이 0, 빈 문자열, false일 때 그것을 지우지 않고, null 또는 undefined일 때만 대체합니다. 위 예제의 명시적 분기를 const count = cart.itemCount ?? 0으로 줄일 수 있습니다.
반대로 사용자 이름이 없다고 무조건 "사용자"로 바꾸면 데이터 오류를 숨길 수 있습니다. 대체값이 도메인에서 정말 유효한지 먼저 판단하세요.
배열과 객체 인덱스도 실제로 비어 있을 수 있습니다
const labels: Record<string, string> = {
ready: "준비",
};
const label = labels[currentStatus];
noUncheckedIndexedAccess를 켜면 선언되지 않은 키 접근에 undefined가 추가됩니다. 이 오류는 귀찮은 장벽이 아니라 currentStatus가 사전에 없을 가능성을 보여 줍니다.
가능한 상태가 정해져 있다면 키 타입을 좁히는 편이 낫습니다.
type Status = "ready" | "done";
const labels: Record<Status, string> = {
ready: "준비",
done: "완료",
};
외부 문자열이라면 labels[currentStatus]가 없을 때 "알 수 없음"을 돌려주는 nullish fallback처럼 제품이 허용한 대체값을 명시하세요. 생성된 Next.js 타입 파일에 빨간 줄이 보이는 경우에는 그 파일을 직접 고치지 말고, .next/types 오류에서 원본 라우트를 찾는 순서처럼 원본 입력으로 거슬러 올라가야 합니다.
느낌표를 쓸 수 있는 경우는 아주 좁습니다
TypeScript 공식 문서는 postfix !가 런타임 동작을 바꾸지 않는다고 설명합니다. 따라서 아래처럼 값이 실제로 빠질 수 있는 코드에서는 쓰지 마세요.
const user = users.find((item) => item.id === selectedId)!;
return user.name;
사용자가 없으면 다음 줄에서 그대로 실패합니다. assertion이 필요한 경우라면 “왜 반드시 존재하는가”를 실행 가능한 검증으로 옮기세요.
function requireValue<T>(value: T | null | undefined, label: string): T {
if (value == null) {
throw new Error(`${label} is required`);
}
return value;
}
const root = requireValue(document.getElementById("root"), "#root");
이제 실패는 속성 접근 중간의 모호한 오류가 아니라, 어떤 값이 필요한지 밝히는 지점에서 발생합니다.
수정 뒤에는 타입 검사와 빈 값 입력을 함께 봅니다
프로젝트에 typecheck 스크립트가 있으면 그것을 우선 실행합니다.
npm run typecheck
별도 스크립트가 없다면 출력 파일을 만들지 않고 전체 타입을 검사합니다.
npx tsc --noEmit
통과만 확인하고 끝내지 마세요. 방금 처리한 값이 실제로 없을 때도 원하는 결과가 나오는지 확인합니다.
find가 일치 항목을 못 찾을 때 정해 둔 실패 분기로 가는가- 선택 콜백이 없을 때 나머지 저장 흐름은 계속되는가
- 숫자
0이 기본값 처리에서 사라지지 않는가 - 알 수 없는 객체 키가 사용자에게 정한 fallback으로 보이는가
npx tsc --noEmit이 오류 없이 종료되는가
다음 빨간 줄을 만나면 !부터 입력하지 말고, 에디터가 보여 주는 | undefined의 주인을 먼저 찾으세요. 그 값이 없을 때의 행동을 한 문장으로 정하면 수정 코드와 테스트가 함께 선명해집니다.
공식 확인 링크
- TypeScript Everyday Types: null, undefined, non-null assertion
- TypeScript Narrowing
- TypeScript 3.7: optional chaining과 nullish coalescing
- TSConfig noUncheckedIndexedAccess
이 글은 AI 코딩과 개발 학습의 일반 정보 제공 목적입니다. 도구, 모델, 커리큘럼, 요금은 버전과 시점에 따라 달라질 수 있으므로 실습이나 도입 전 공식 문서와 최신 릴리스 노트를 확인하세요.
다음으로 읽을 기사
같은 흐름으로 이어 읽기 좋은 기사만 추려 보여줍니다.
첫 번째 댓글을 남겨보세요
여러분의 생각이 다른 독자에게 도움이 됩니다.
댓글 0
이 글을 읽은 독자들의 생각을 나눠보세요.