← 블로그로 돌아가기

고급 JSON 구조: 복잡한 데이터 모델링 마스터하기

복잡한 데이터 구조를 JSON으로 효과적으로 표현하는 방법을 배워보세요. 그래프, 트리, 다형성, 참조 처리까지 완벽 정리.

Big JSON Team14분 소요advanced
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

14 분 읽기

# 고급 JSON 구조: 복잡한 데이터 모델링 마스터하기

JSON의 기본을 넘어 복잡한 데이터 구조를 효과적으로 표현하고 처리하는 고급 기법을 마스터하세요.

트리 구조

중첩 객체로 트리 표현

{

"id": 1,

"name": "루트",

"children": [

{

"id": 2,

"name": "자식1",

"children": [

{

"id": 4,

"name": "손자1",

"children": []

}

]

},

{

"id": 3,

"name": "자식2",

"children": []

}

]

}

재귀적 순회

function traverseTree(node, callback) {

callback(node);

if (node.children && node.children.length > 0) {

node.children.forEach(child => {

traverseTree(child, callback);

});

}

}

// 사용

traverseTree(tree, (node) => {

console.log(node.name);

});

깊이 우선 탐색 (DFS)

function dfs(node, targetId) {

if (node.id === targetId) {

return node;

}

for (const child of node.children || []) {

const result = dfs(child, targetId);

if (result) return result;

}

return null;

}

너비 우선 탐색 (BFS)

function bfs(root, targetId) {

const queue = [root];

while (queue.length > 0) {

const node = queue.shift();

if (node.id === targetId) {

return node;

}

if (node.children) {

queue.push(...node.children);

}

}

return null;

}

플랫 배열로 트리 표현

[

{ "id": 1, "name": "루트", "parentId": null },

{ "id": 2, "name": "자식1", "parentId": 1 },

{ "id": 3, "name": "자식2", "parentId": 1 },

{ "id": 4, "name": "손자1", "parentId": 2 }

]

장점:
  • 쿼리 쉬움
  • 업데이트 간편
  • 순환 참조 가능

플랫에서 트리로 변환:
function buildTree(flatData) {

const map = {};

const roots = [];

// 맵 생성

flatData.forEach(item => {

map[item.id] = { ...item, children: [] };

});

// 트리 구성

flatData.forEach(item => {

if (item.parentId === null) {

roots.push(map[item.id]);

} else {

map[item.parentId].children.push(map[item.id]);

}

});

return roots;

}

그래프 구조

인접 리스트

{

"nodes": [

{ "id": "A", "label": "노드 A" },

{ "id": "B", "label": "노드 B" },

{ "id": "C", "label": "노드 C" }

],

"edges": [

{ "from": "A", "to": "B", "weight": 5 },

{ "from": "A", "to": "C", "weight": 3 },

{ "from": "B", "to": "C", "weight": 2 }

]

}

그래프 순회

function buildAdjacencyList(graph) {

const adj = {};

graph.nodes.forEach(node => {

adj[node.id] = [];

});

graph.edges.forEach(edge => {

adj[edge.from].push({

to: edge.to,

weight: edge.weight

});

});

return adj;

}

function dfsGraph(adj, start, visited = new Set()) {

if (visited.has(start)) return;

console.log(start);

visited.add(start);

for (const neighbor of adj[start] || []) {

dfsGraph(adj, neighbor.to, visited);

}

}

// 사용

const adj = buildAdjacencyList(graph);

dfsGraph(adj, 'A');

최단 경로 (Dijkstra)

function dijkstra(graph, start) {

const adj = buildAdjacencyList(graph);

const distances = {};

const visited = new Set();

const pq = [{ node: start, distance: 0 }];

// 초기화

graph.nodes.forEach(node => {

distances[node.id] = Infinity;

});

distances[start] = 0;

while (pq.length > 0) {

pq.sort((a, b) => a.distance - b.distance);

const { node, distance } = pq.shift();

if (visited.has(node)) continue;

visited.add(node);

for (const neighbor of adj[node] || []) {

const newDist = distance + neighbor.weight;

if (newDist < distances[neighbor.to]) {

distances[neighbor.to] = newDist;

pq.push({ node: neighbor.to, distance: newDist });

}

}

}

return distances;

}

다형성 (Polymorphism)

타입 필드 사용

{

"shapes": [

{

"type": "circle",

"radius": 5,

"color": "red"

},

{

"type": "rectangle",

"width": 10,

"height": 5,

"color": "blue"

},

{

"type": "triangle",

"base": 8,

"height": 6,

"color": "green"

}

]

}

타입별 처리

function calculateArea(shape) {

switch (shape.type) {

case 'circle':

return Math.PI shape.radius 2;

case 'rectangle':

return shape.width shape.height;

case 'triangle':

return (shape.base shape.height) / 2;

default:

throw new Error(알 수 없는 타입: ${shape.type});

}

}

// 사용

const shapes = data.shapes;

const areas = shapes.map(calculateArea);

console.log('면적:', areas);

TypeScript에서 타입 가드

type Circle = {

type: 'circle';

radius: number;

};

type Rectangle = {

type: 'rectangle';

width: number;

height: number;

};

type Shape = Circle | Rectangle;

function isCircle(shape: Shape): shape is Circle {

return shape.type === 'circle';

}

function calculateArea(shape: Shape): number {

if (isCircle(shape)) {

return Math.PI shape.radius * 2;

} else {

return shape.width shape.height;

}

}

참조와 포인터

ID 기반 참조

{

"users": [

{ "id": 1, "name": "홍길동" },

{ "id": 2, "name": "김철수" }

],

"posts": [

{

"id": 101,

"title": "첫 게시물",

"authorId": 1,

"commentIds": [201, 202]

}

],

"comments": [

{

"id": 201,

"text": "좋은 글이네요",

"authorId": 2,

"postId": 101

},

{

"id": 202,

"text": "감사합니다",

"authorId": 1,

"postId": 101

}

]

}

참조 해결 (Resolution)

function resolveReferences(data) {

const users = new Map(data.users.map(u => [u.id, u]));

const posts = new Map(data.posts.map(p => [p.id, p]));

const comments = new Map(data.comments.map(c => [c.id, c]));

// 게시물에 작성자와 댓글 추가

return data.posts.map(post => ({

...post,

author: users.get(post.authorId),

comments: post.commentIds.map(id => ({

...comments.get(id),

author: users.get(comments.get(id).authorId)

}))

}));

}

const resolved = resolveReferences(data);

console.log(resolved[0].author.name); // "홍길동"

GraphQL 스타일 중첩

{

"post": {

"id": 101,

"title": "첫 게시물",

"author": {

"id": 1,

"name": "홍길동",

"posts": [

{ "id": 101, "title": "첫 게시물" }

]

},

"comments": [

{

"id": 201,

"text": "좋은 글이네요",

"author": {

"id": 2,

"name": "김철수"

}

}

]

}

}

시계열 데이터

타임스탬프 기반

{

"metrics": [

{

"timestamp": "2026-01-16T10:00:00Z",

"cpu": 45.2,

"memory": 68.5,

"disk": 72.1

},

{

"timestamp": "2026-01-16T10:01:00Z",

"cpu": 47.8,

"memory": 69.2,

"disk": 72.3

}

]

}

집계 및 분석

function aggregateByHour(metrics) {

const hourly = {};

metrics.forEach(metric => {

const hour = new Date(metric.timestamp).setMinutes(0, 0, 0);

if (!hourly[hour]) {

hourly[hour] = {

count: 0,

cpuSum: 0,

memorySum: 0,

diskSum: 0

};

}

hourly[hour].count++;

hourly[hour].cpuSum += metric.cpu;

hourly[hour].memorySum += metric.memory;

hourly[hour].diskSum += metric.disk;

});

return Object.entries(hourly).map(([hour, stats]) => ({

hour: new Date(parseInt(hour)),

avgCpu: stats.cpuSum / stats.count,

avgMemory: stats.memorySum / stats.count,

avgDisk: stats.diskSum / stats.count

}));

}

지리 데이터 (GeoJSON)

Point

{

"type": "Feature",

"geometry": {

"type": "Point",

"coordinates": [126.9780, 37.5665]

},

"properties": {

"name": "서울",

"population": 9776000

}

}

LineString

{

"type": "Feature",

"geometry": {

"type": "LineString",

"coordinates": [

[126.9780, 37.5665],

[129.0756, 35.1796]

]

},

"properties": {

"route": "서울-부산"

}

}

Polygon

{

"type": "Feature",

"geometry": {

"type": "Polygon",

"coordinates": [

[

[126.9, 37.5],

[127.0, 37.5],

[127.0, 37.6],

[126.9, 37.6],

[126.9, 37.5]

]

]

},

"properties": {

"name": "강남구"

}

}

버전 관리

명시적 버전

{

"version": "2.0",

"data": {

"users": [

{

"id": 1,

"name": "홍길동",

"email": "hong@example.com"

}

]

}

}

마이그레이션

function migrate(data) {

const version = data.version || '1.0';

switch (version) {

case '1.0':

// v1 to v2

data = migrateV1toV2(data);

// fall through

case '2.0':

// v2 to v3

data = migrateV2toV3(data);

break;

default:

console.log('최신 버전');

}

return data;

}

function migrateV1toV2(data) {

return {

version: '2.0',

data: {

users: data.users.map(user => ({

id: user.id,

name: user.name,

email: user.contact // 필드 이름 변경

}))

}

};

}

스키마 검증

JSON Schema로 복잡한 구조 검증

{

"$schema": "http://json-schema.org/draft-07/schema#",

"type": "object",

"properties": {

"nodes": {

"type": "array",

"items": {

"type": "object",

"properties": {

"id": { "type": "string" },

"type": {

"enum": ["circle", "rectangle"]

}

},

"required": ["id", "type"],

"allOf": [

{

"if": {

"properties": { "type": { "const": "circle" } }

},

"then": {

"properties": {

"radius": { "type": "number", "minimum": 0 }

},

"required": ["radius"]

}

},

{

"if": {

"properties": { "type": { "const": "rectangle" } }

},

"then": {

"properties": {

"width": { "type": "number", "minimum": 0 },

"height": { "type": "number", "minimum": 0 }

},

"required": ["width", "height"]

}

}

]

}

}

}

}

성능 최적화

인덱싱

class IndexedData {

constructor(data) {

this.data = data;

this.indexes = {};

}

createIndex(field) {

this.indexes[field] = new Map();

this.data.forEach((item, index) => {

const value = item[field];

if (!this.indexes[field].has(value)) {

this.indexes[field].set(value, []);

}

this.indexes[field].get(value).push(index);

});

}

findByIndex(field, value) {

if (!this.indexes[field]) {

throw new Error(인덱스가 없습니다: ${field});

}

const indices = this.indexes[field].get(value) || [];

return indices.map(i => this.data[i]);

}

}

// 사용

const indexed = new IndexedData(users);

indexed.createIndex('city');

const seoulUsers = indexed.findByIndex('city', '서울');

캐싱

class CachedQuery {

constructor(data) {

this.data = data;

this.cache = new Map();

}

query(predicate) {

const key = predicate.toString();

if (this.cache.has(key)) {

console.log('캐시 히트');

return this.cache.get(key);

}

const result = this.data.filter(predicate);

this.cache.set(key, result);

return result;

}

clearCache() {

this.cache.clear();

}

}

압축 기법

키 압축

// 원본

const original = [

{ "firstName": "홍", "lastName": "길동", "age": 30 },

{ "firstName": "김", "lastName": "철수", "age": 25 }

];

// 압축

const compressed = {

"keys": ["firstName", "lastName", "age"],

"data": [

["홍", "길동", 30],

["김", "철수", 25]

]

};

// 압축 해제

function decompress(compressed) {

return compressed.data.map(values => {

const obj = {};

compressed.keys.forEach((key, i) => {

obj[key] = values[i];

});

return obj;

});

}

델타 인코딩

// 시계열 데이터

const timeseries = [

{ timestamp: 1642348800, value: 100 },

{ timestamp: 1642348860, value: 102 },

{ timestamp: 1642348920, value: 105 }

];

// 델타 인코딩

const delta = {

base: { timestamp: 1642348800, value: 100 },

deltas: [

{ timestamp: 60, value: 2 },

{ timestamp: 60, value: 3 }

]

};

결론

고급 JSON 구조를 마스터하면 복잡한 데이터를 효율적으로 표현하고 처리할 수 있습니다. 적절한 구조 선택이 성능과 유지보수성에 큰 영향을 미칩니다.

핵심 요약:
  • ✅ 트리는 중첩 또는 플랫 구조로
  • ✅ 그래프는 인접 리스트로
  • ✅ 다형성은 타입 필드로
  • ✅ 참조는 ID 기반으로
  • ✅ 성능은 인덱싱과 캐싱으로

지금 바로 JSON Simplify에서 복잡한 JSON을 시각화해보세요!

Share:

관련 글

Read in English