·5분 읽기

JSON 깊은 중첩 평탄화 — flatten 함수 5가지 실전 패턴

API 응답·설정 파일·MongoDB 문서의 중첩 JSON을 평탄한 객체로 변환하는 5가지 패턴. 점 표기법·배열 인덱스·역방향 unflatten·에지 케이스까지 정리했어요.

JSON 깊은 중첩 평탄화 — flatten 함수 5가지 실전 패턴
{ }
JSON 포맷터 바로 사용하기
JSON을 예쁘게 정렬하고 검증하세요

JSON 평탄화, 왜 필요할까?

API 응답이나 MongoDB 문서를 받다 보면 중첩이 5단계, 10단계까지 깊어질 때가 있어요. 그대로 다루면 코드가 지저분. ``` { "user": { "profile": { "address": { "city": "Seoul" } } } } ``` 평탄화하면. ``` { "user.profile.address.city": "Seoul" } ``` 언제 쓰나. 1. **CSV/엑셀 변환** — 표 형식에 넣으려면 1단계 객체 필요 2. **MongoDB 부분 업데이트** — `$set: { 'user.profile.city': 'Seoul' }` 3. **로그 인덱싱** — Elasticsearch는 평탄한 키-값 인덱싱이 빠름 4. **i18n 번역 키** — `home.nav.signup` 같은 점 표기법이 표준 5. **설정 파일 비교** — 두 yaml/json 차이 비교 시 평탄화하면 한 줄씩 오늘은 5가지 실전 패턴을 코드와 함께 정리할게요.

패턴 1) 기본 평탄화 — 점 표기법

가장 흔한 패턴. 중첩 키를 점으로 연결. 입력. ``` { "a": 1, "b": { "c": 2, "d": { "e": 3 } } } ``` 출력. ``` { "a": 1, "b.c": 2, "b.d.e": 3 } ``` JavaScript 구현 (재귀). ``` function flatten(obj, prefix = '', result = {}) { for (const key in obj) { const newKey = prefix ? prefix + '.' + key : key; if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) { flatten(obj[key], newKey, result); } else { result[newKey] = obj[key]; } } return result; } ``` 주의. `null`·배열·Date는 객체로 안 들어감 (그대로 값으로). 이 처리가 빠지면 무한 재귀.

패턴 2) 배열 포함 — 인덱스 표기법

배열까지 평탄화하려면 인덱스를 키에 포함. 입력. ``` { "users": [ { "name": "김", "age": 30 }, { "name": "이", "age": 25 } ] } ``` 출력 (점 인덱스). ``` { "users.0.name": "김", "users.0.age": 30, "users.1.name": "이", "users.1.age": 25 } ``` 출력 (대괄호 인덱스 — JSONPath 스타일). ``` { "users[0].name": "김", "users[0].age": 30 } ``` JavaScript. ``` function flatten(obj, prefix = '', result = {}) { for (const key in obj) { const isArray = Array.isArray(obj); const newKey = isArray ? (prefix ? prefix + '[' + key + ']' : '[' + key + ']') : (prefix ? prefix + '.' + key : key); if (typeof obj[key] === 'object' && obj[key] !== null) { flatten(obj[key], newKey, result); } else { result[newKey] = obj[key]; } } return result; } ``` MongoDB는 `users.0.name` 점 표기법, JSONPath는 `users[0].name` 대괄호. 용도에 맞춰 선택.

패턴 3) unflatten — 역방향 복원

평탄한 객체를 다시 중첩으로. 점·대괄호 표기법을 파싱. 입력. ``` { "user.profile.city": "Seoul", "user.profile.zip": "03000" } ``` 출력. ``` { "user": { "profile": { "city": "Seoul", "zip": "03000" } } } ``` JavaScript. ``` function unflatten(flat) { const result = {}; for (const key in flat) { const parts = key.split('.'); let cur = result; for (let i = 0; i < parts.length - 1; i++) { if (!cur[parts[i]]) cur[parts[i]] = {}; cur = cur[parts[i]]; } cur[parts[parts.length - 1]] = flat[key]; } return result; } ``` 주의. `users.0.name`처럼 숫자가 섞이면 객체 vs 배열 구분 어려움. 정수면 배열로 만들지, 객체로 둘지 결정해야. 해결. `parseInt(part) >= 0 && !isNaN(part)`이면 배열 인덱스로 처리 — 단 `'01'` 같은 0-prefix 숫자는 객체 키로.

패턴 4) 깊이 제한 — 너무 깊으면 끊기

10단계 이상 중첩되면 키가 너무 길어져 가독성 저하. 깊이 제한. ``` function flatten(obj, prefix = '', result = {}, depth = 0, maxDepth = 3) { for (const key in obj) { const newKey = prefix ? prefix + '.' + key : key; if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key]) && depth < maxDepth - 1) { flatten(obj[key], newKey, result, depth + 1, maxDepth); } else { result[newKey] = obj[key]; } } return result; } ``` 사용 예. `flatten(data, '', {}, 0, 2)` → 2단계까지만 평탄화, 그 이후는 중첩 객체 그대로 값으로. 사례. Elasticsearch 인덱싱은 보통 3~5단계 권장. 그 이상은 검색 성능 저하 + mapping 폭증. CSV 변환은 2단계까지면 충분한 경우 많음. lodash의 `_.flatten`은 1단계만 평탄화. 깊은 평탄화는 `_.flattenDeep`. 객체 기준 평탄화는 `flat` npm 라이브러리 사용 권장.

패턴 5) 키 변환 — 케이스 통일

API 응답이 camelCase·snake_case 섞여 있으면 평탄화하면서 통일. 입력. ``` { "user_info": { "firstName": "길동", "last_name": "홍" } } ``` 출력 (모두 snake_case). ``` { "user_info.first_name": "길동", "user_info.last_name": "홍" } ``` JavaScript. ``` function toSnake(key) { return key.replace(/[A-Z]/g, c => '_' + c.toLowerCase()).replace(/^_/, ''); } function flattenSnake(obj, prefix = '', result = {}) { for (const key in obj) { const newKey = prefix ? prefix + '.' + toSnake(key) : toSnake(key); if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) { flattenSnake(obj[key], newKey, result); } else { result[newKey] = obj[key]; } } return result; } ``` 케이스 변환 자세한 가이드는 [대소문자 변환 — camelCase·snake_case·kebab-case](/blog/case-converter-camel-snake-kebab-5-differences)에서 이어 보세요.

에지 케이스 — null·undefined·순환참조

실전에서 부딪히는 6가지 함정. 1. **null 값**: `typeof null === 'object'` 함정. `obj[key] === null` 체크 필수 2. **undefined**: 명시적으로 키에 들어있을 때 — JSON.stringify는 빼버림. 평탄화는 둘 중 선택 3. **Date 객체**: `typeof === 'object'`라 재귀 들어가면 ISO 문자열로. `instanceof Date` 체크 4. **순환참조**: a.b = a면 무한 재귀. WeakSet으로 방문 추적 5. **빈 객체 `{}`**: 평탄화 시 키가 사라짐. 옵션으로 빈 객체 보존 여부 결정 6. **배열 안 객체**: `[1, {a:2}, 3]` 같은 혼합. 인덱스 표기법으로 처리 WeakSet으로 순환 방지. ``` function flatten(obj, prefix = '', result = {}, seen = new WeakSet()) { if (seen.has(obj)) return result; seen.add(obj); ... } ``` 빈 객체 보존. `Object.keys(obj).length === 0`일 때 `result[prefix] = {}` 추가.

Toolkio JSON 포맷터로 평탄화 시각화

코드 짜기 전에 [Toolkio JSON 포맷터](https://toolkio.com/tools/json-formatter)에 붙여넣고 트리뷰로 구조 파악하면 평탄화 후 어떻게 될지 미리 그림이 그려져요. 작업 흐름. 1. JSON 붙여넣기 → 포맷팅 + 트리뷰 표시 2. 깊이 단계 카운트 (예: 4단계) 3. 평탄화 후 키 개수 예상 4. JavaScript 코드로 변환 후 결과 다시 붙여넣어 검증 주의. 평탄화 후 키 개수가 너무 많으면(1만 개+) Elasticsearch mapping explosion 위험. 깊이 제한 또는 특정 path만 평탄화 권장. MongoDB `$set` 업데이트 만들 때도 활용. 평탄화한 객체를 그대로 `$set`에 넘기면 부분 업데이트가 한 번에. 더 자세한 JSON 활용은 [JSON 포맷터 5가지 활용](/blog/json-formatter-5-uses-api-debug-developer)에서, 디버깅은 [JSON 린트·디버그 5단계](/blog/json-lint-debug-5-steps-syntax-error)에서 이어 보세요.

지금 바로 JSON 포맷터를 사용해보세요

무료이며, 브라우저에서 바로 실행됩니다.

JSON 포맷터