--- 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`, 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(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 = { kind: 'some'; value: T }; type None = { kind: 'none' }; type Maybe = Some | None; const some = (value: T): Maybe => ({ kind: 'some', value }); const none = (): Maybe => ({ kind: 'none' }); function map(m: Maybe, f: (t: T) => U): Maybe { return m.kind === 'none' ? m : some(f(m.value)); } function getOr(m: Maybe, 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 / Option` | 변환 체이닝이 잦음 | 복잡한 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]]