---
id: null-safety-patterns
title: Null 안전 패턴 (Null Safety Patterns)
category: Coding
status: draft
canonical_id: null-safety-patterns
aliases: [optional, maybe, nullable, undefined-handling, NPE]
duplicate_of: null
source_trust_level: B
confidence_score: 0.85
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
last_reinforced: 2026-05-09
review_reason: ""
merge_history: []
tags: [coding, type-safety, null, undefined, vibe-coding]
raw_sources: ["P-Reinforce session 2026-05-09 — bulk Coding seed batch 1"]
tech_stack:
  language: "TypeScript / Kotlin / Rust / Swift"
  applicable_to: ["모든 도메인"]
applied_in: []
---

# Null 안전 패턴

> Null/undefined 는 "값이 없음"이라는 정보를 담지 못하는 가장 빈약한 표현. **타입에서 nullable 을 명시적으로 분리**하고, **언래핑 지점을 명확히** 만들어라.

## 📖 핵심 개념

Tony Hoare가 "the billion dollar mistake" 라고 부른 null 참조 문제. 모던 언어들은 다음 중 하나로 대응:

1. **타입 시스템에서 nullable 분리**: TypeScript `T | null`, Kotlin `T?`, Swift `T?`
2. **Optional 타입**: Rust `Option<T>`, Haskell `Maybe a`
3. **Strict mode**: TS의 `strictNullChecks`, Kotlin 디폴트, Swift 디폴트

핵심은 "이 값은 없을 수 있다"를 **타입이 명시적으로 표현하고 컴파일러가 강제**하는 것.

## 💻 코드 패턴

### 1. nullable 명시 (TypeScript with strictNullChecks)

```ts
// ❌ 모호함 — name 이 항상 있는지 가끔 없는지 모름
function greet(user: { name: string }) { return `Hi, ${user.name}`; }

// ✅ 명시
function greet(user: { name: string | null }) {
  if (user.name === null) return 'Hi, friend';
  return `Hi, ${user.name}`;
}
```

### 2. Optional Chaining + Nullish Coalescing

```ts
// 깊은 경로 안전 접근
const city = order?.shipping?.address?.city ?? 'Unknown';

// ❌ 주의: || 는 falsy("" / 0) 도 fallback 으로 처리
const port = config.port || 3000; // port가 0이면 3000 됨!
const port = config.port ?? 3000; // null/undefined 만 fallback ✓
```

### 3. 타입 가드 + 좁히기

```ts
function isPresent<T>(x: T | null | undefined): x is T {
  return x !== null && x !== undefined;
}

const items = users.map(u => u.email).filter(isPresent); // string[]
```

### 4. Maybe / Option 모나드 패턴 (TS)

```ts
type Some<T> = { kind: 'some'; value: T };
type None = { kind: 'none' };
type Maybe<T> = Some<T> | None;

const some = <T>(value: T): Maybe<T> => ({ kind: 'some', value });
const none = (): Maybe<never> => ({ kind: 'none' });

function map<T, U>(m: Maybe<T>, f: (t: T) => U): Maybe<U> {
  return m.kind === 'none' ? m : some(f(m.value));
}
function getOr<T>(m: Maybe<T>, fallback: T): T {
  return m.kind === 'some' ? m.value : fallback;
}
```

복잡한 변환 체인이 많을 때만 도입. 단순한 `T | null` 로 충분한 경우엔 over-engineering.

### 5. Non-null assertion (`!`) 의 좁은 사용

```ts
const el = document.getElementById('root')!; // OK if you control the HTML

// ❌ 위험
function findUser(id: string) {
  return users.find(u => u.id === id)!; // 못 찾으면 런타임 사고
}

// ✅
function findUser(id: string): User {
  const u = users.find(u => u.id === id);
  if (!u) throw new NotFoundError(id);
  return u;
}
```

## 🤔 의사결정 기준

| 시그니처 | 의미 | 사용 시점 |
|---|---|---|
| `T` | 항상 값 있음 | 강한 보장이 필요할 때 (도메인 invariant) |
| `T \| null` | 값이 의도적으로 없을 수 있음 | "선택 사항" 의미 명확할 때 |
| `T \| undefined` | 아직 초기화 안 됨 / 키 없음 | object property optional 표현 |
| `T \| null \| undefined` | 둘 다 가능 | 외부 데이터 (API 응답) — 즉시 정규화 권장 |
| `Maybe<T> / Option<T>` | 변환 체이닝이 잦음 | 복잡한 nullable pipeline |

## ❌ 안티패턴

- **`any` 로 회피**: 타입 시스템 우회. 컴파일은 통과하지만 런타임 폭사.
- **Non-null assertion 남발 (`x!.y!.z!`)**: 컴파일러를 침묵시키지만 보장 없음. 매번 가드 또는 스키마 검증.
- **`||` 로 falsy 까지 fallback**: 0, "", false 가 의미 있는 값일 때 사고. `??` 사용.
- **null 과 undefined 둘 다 보내기**: 한 코드베이스에서 한 컨벤션 (보통 `undefined` 권장 — JSON 직렬화 시 사라져 명시적).
- **null check 잊고 nested access**: `obj.a.b.c` 가 `obj.a` null 일 때 폭사. optional chaining `?.` 사용.
- **외부 API 응답을 그대로 nullable 채로 도메인 깊숙이 전파**: 경계에서 zod / io-ts 같은 스키마로 정규화.

## 🤖 LLM 활용 힌트

- LLM에게 함수 작성: "**strictNullChecks 가정. 가능한 nullable 은 시그니처에 명시**" 라고 요청.
- 외부 API 파싱: "**zod 같은 라이브러리로 nullable 을 즉시 도메인 타입으로 정규화**" 명시.
- TypeScript 옵션: 프로젝트 `tsconfig.json` 에 `"strict": true` 또는 최소 `"strictNullChecks": true` 가 있어야 이 모든 패턴이 의미 있음.

## 🧪 검증 상태

- verification_status: `conceptual`
- Kotlin / Swift / Rust 의 default null safety 모델, TypeScript strictNullChecks 가 표준.
- 적용 사례 발견 시 `applied_in` 추가.

## 🔗 관련 문서

- [[Guard_Clauses]]
- [[Tagged_Union_Discriminated_Types]]
- [[Error_Handling_Result_vs_Throw]]
