타입스크립트를 사용하다 보면 종종 변수나 객체가 null 또는 undefined일 가능성을 처리해야 하는 상황을 마주하게 됩니다. 이때 non-null assertion (!) 연산자를 활용하면 코드의 가독성을 유지하면서 특정 값이 반드시 null 또는 undefined가 아님을 컴파일러에게 확신시킬 수 있습니다.
목차
위의 목차를 클릭하면 해당 글로 자동 이동 합니다.
1. non-null assertion이란?
Non-Null Assertion(!)은 TypeScript에서 사용되는 특별한 연산자로, 컴파일러에게 "이 값은 절대 null이나 undefined가 될 수 없다"는 것을 단언합니다.
value!;
위와 같이 변수 뒤에 !를 붙이면 해당 변수가 null 또는 undefined가 아니라는 확신을 나타냅니다.
2. Non-Null Assertion 적용
React 프로젝트에 TypeScript를 적용하던 중 아래 코드에서 타입 에러가 발생했습니다.
const root = ReactDOM.createRoot(document.getElementById("root"));
document.getElementById의 반환 값이 HTMLElement | null 타입으로 정의되어 있기 때문에 발생합니다. ReactDOM의 createRoot 메서드는 null 값을 허용하지 않으므로 TypeScript에서 타입 검사를 통과하지 못합니다.
Non-Null Assertion 적용 예시
const root = ReactDOM.createRoot(document.getElementById("root")!);
!(Non-null assertion operator)를 사용해 TypeScript에게 이 값이 절대 null 또는 undefined가 아님을 보증해 타입 에러가 사라졌습니다. 하지만 이는 런타임 에러를 발생시킬 수 있으므로 보다 안전한 방법을 사용하는 것이 좋습니다.
3. Non-Null Assertion(!) 주의점
3.1. 런타임 에러 가능성
Non-Null Assertion(!)을 사용하면 컴파일러는 더 이상 해당 값의 null 여부를 검사하지 않지만, 런타임에서 null 또는 undefined라면 에러가 발생합니다.
위 코드는 value를 선언만 하고 초기화하지 않아서 undefined일 수 있다는 타입 에러가 발생합니다.
Non-Null Assertion을 적용해 보겠습니다.
타입 에러가 사라진 것을 볼 수 있습니다. 컴파일을 통해 확인해 볼까요?
런타임에서 value가 undefined이기 때문에 에러가 발생합니다. 따라서 Non-Null Assertion(!)은 정말 값이 null이 아니라고 확신할 수 있을 때만 사용해야 합니다.
3.2. 타입 안정성 저하
- TypeScript의 가장 큰 장점은 코드 작성 중에 타입 안전성을 보장함으로써 잠재적인 버그를 예방하는 것입니다. 그러나 Non-Null Assertion(!)는 타입 검사 과정을 무시하기 때문에 컴파일러가 원래 감지할 수 있는 오류를 놓치게 만듭니다.
- "이 값이 반드시 존재한다"는 확신을 가지고 Non-Null Assertion(!)를 사용했더라도, 코드가 복잡해지거나 다른 개발자가 추가로 작업하게 되면 Non-Null Assertion(!)로 단언했던 조건이 더 이상 보장되지 않을 수 있습니다.
4. Non-Null Assertion을 대체할 방법
4.1. 옵셔널 체이닝
TypeScript 3.7 이후 도입된 옵셔널 체이닝(?.)과 null 병합 연산자(??)를 사용하면 보다 안전하고 읽기 쉬운 코드를 작성할 수 있습니다.
function printLength(value?: string) {
console.log(value?.length ?? 0); // 값이 undefined이면 0 반환
}
4.2. 타입 가드를 사용해 안전성 확보
타입 가드는 값을 명시적으로 검사하므로 런타임 에러를 방지할 수 있습니다.
function getLength(value?: string): number {
if (value) {
return value.length;
}
throw new Error("value가 undefined | null 입니다.");
}
4.3. 기본값 사용
기본값 설정을 통해 null이나 undefined를 처리합니다.
function getLength(value?: string): number {
return (value ?? "").length; // value가 null/undefined일 경우 빈 문자열로 대체
}
4.4. 타입 좁히기(Narrowing)
TypeScript의 타입 좁히기를 활용하여 컴파일러가 값이 안전하다고 추론할 수 있게 합니다.
function printUserId(userId?: number) {
if (userId !== undefined) {
console.log(userId.toString()); // 타입 좁히기로 안전한 접근
} else {
console.log("유저 아이디가 없습니다.");
}
}
Non-Null Assertion(!)은 편리한 도구지만, 과도하게 사용하면 TypeScript의 타입 시스템을 무력화할 위험이 있습니다. 특정 값이 null이나 undefined가 아님을 확신할 때만 사용해야 하며, 대신 타입 가드, 옵셔널 체이닝, 기본값 설정, 타입 좁히기 등 더 안전한 대안을 적극적으로 고려하는 것이 좋습니다.
"컴파일러 경고를 피하는 대신, 문제의 원인을 해결하자"라는 마음가짐으로 같이 공부해 봐요🔥🔥🔥
'개발 공부 일지 > TypeScript' 카테고리의 다른 글
[TS] any 타입 - 편리함과 위험함 사이 (3) | 2024.11.15 |
---|---|
[TS] 타입 추론과 타입 단언 (9) | 2024.11.13 |
[TS] 타입스크립트의 유용한 유틸리티 타입 (3) | 2024.11.11 |
[TS] 타입스크립트 주요 기능과 기본 문법 소개 (5) | 2024.11.09 |
[TS] 타입스크립트 : 자바스크립트에 타입을 더하다 (0) | 2024.11.08 |