JSON 병합 방법
2025/05/20
8분 읽기

JSON 병합 방법

다양한 프로그래밍 언어에서 JSON 객체를 병합하는 종합 가이드

JSON 병합 소개

JSON 객체 병합은 데이터 처리, 구성 관리 및 API 상호 작용에서 기본적인 작업입니다. 사용자 설정 결합, 구성 파일 병합 또는 API 응답 집계와 같은 작업을 할 때 JSON 객체를 올바르게 병합하는 방법을 이해하는 것은 현대 개발에 중요합니다.

JSON 병합은 두 개 이상의 JSON 객체를 단일 통합 객체로 결합하는 프로세스를 의미합니다. 이 프로세스는 평면 객체의 경우 간단할 수 있지만 중첩된 구조, 배열 및 충돌하는 값을 다룰 때 더 복잡해집니다.

기본 JSON 병합 개념

구현 세부 사항으로 들어가기 전에, JSON 병합과 관련된 몇 가지 핵심 개념을 이해하는 것이 중요합니다:

얕은 병합과 깊은 병합

  • 얕은 병합: 최상위 속성만 결합됩니다. 두 객체가 동일한 속성을 포함할 때, 두 번째 객체의 값이 첫 번째 객체의 값을 덮어씁니다.
  • 깊은 병합: 병합 작업이 객체 트리를 재귀적으로 탐색하여 중첩된 객체를 대체하는 대신 결합합니다.

충돌 값에 대한 병합 전략

JSON 객체를 병합할 때, 충돌하는 값은 여러 방법으로 처리할 수 있습니다:

전략설명사용 사례
마지막 승리마지막 객체의 값이 이전 값을 덮어씁니다기본 구성
첫 번째 승리첫 번째 객체의 값이 보존됩니다사용자 설정 보존
사용자 정의 로직다른 속성에 특정 로직 적용복잡한 비즈니스 규칙
충돌 시 오류충돌이 감지되면 오류 발생중요한 데이터 무결성

JavaScript에서 JSON 병합 방법

JavaScript는 JSON 객체를 병합하기 위한 몇 가지 내장 메서드를 제공합니다:

Object.assign() 사용

Object.assign() 메서드는 얕은 병합을 수행하여 소스 객체의 모든 열거 가능한 자체 속성을 대상 객체에 복사합니다.

const json1 = { name: "John", age: 30 };
const json2 = { city: "New York", age: 31 };

const merged = Object.assign({}, json1, json2);
console.log(merged);
// 출력: { name: "John", city: "New York", age: 31 }

동일한 키를 가진 속성은 매개변수 목록에서 나중에 오는 객체에 의해 덮어쓰여진다는 점에 유의하세요.

스프레드 연산자(...) 사용

스프레드 연산자는 객체를 병합하는 더 간결한 방법을 제공합니다:

const json1 = { name: "John", age: 30 };
const json2 = { city: "New York", age: 31 };

const merged = { ...json1, ...json2 };
console.log(merged);
// 출력: { name: "John", city: "New York", age: 31 }

이 방법도 중복 키를 나중 객체의 값으로 덮어씁니다.

JSON 객체 깊은 병합

Object.assign()과 스프레드 연산자 모두 얕은 병합을 수행합니다. 중첩된 객체의 경우 깊은 병합 구현이 필요합니다:

재귀적 깊은 병합 함수

function deepMerge(target, source) {
  const output = Object.assign({}, target);
  
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      if (isObject(source[key])) {
        if (!(key in target)) {
          Object.assign(output, { [key]: source[key] });
        } else {
          output[key] = deepMerge(target[key], source[key]);
        }
      } else {
        Object.assign(output, { [key]: source[key] });
      }
    });
  }
  
  return output;
}

function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item));
}

// 사용 예
const json1 = { 
  name: "John", 
  address: { 
    city: "New York", 
    zip: 10001 
  } 
};

const json2 = { 
  name: "Jane", 
  address: { 
    state: "NY" 
  } 
};

const merged = deepMerge(json1, json2);
console.log(merged);
// 출력: { 
//   name: "Jane", 
//   address: { 
//     city: "New York", 
//     zip: 10001, 
//     state: "NY" 
//   } 
// }

라이브러리를 사용한 JSON 병합

더 복잡한 병합 시나리오의 경우 확립된 라이브러리 사용을 고려하세요:

Lodash의 merge와 mergeWith

Lodash는 깊은 병합을 위한 강력한 함수를 제공합니다:

const _ = require('lodash');

const json1 = { user: { name: "John", data: [1, 2] } };
const json2 = { user: { age: 30, data: [3, 4] } };

// 기본 깊은 병합
const merged1 = _.merge({}, json1, json2);
console.log(merged1);
// 출력: { user: { name: "John", age: 30, data: [3, 4] } }

// mergeWith를 사용한 사용자 정의 병합
const merged2 = _.mergeWith({}, json1, json2, (objValue, srcValue) => {
  if (Array.isArray(objValue)) {
    return objValue.concat(srcValue);
  }
});
console.log(merged2);
// 출력: { user: { name: "John", age: 30, data: [1, 2, 3, 4] } }

deepmerge 패키지

deepmerge npm 패키지는 깊은 병합을 위해 특별히 설계되었습니다:

const deepmerge = require('deepmerge');

const json1 = { user: { name: "John", hobbies: ["reading"] } };
const json2 = { user: { age: 30, hobbies: ["swimming"] } };

// 기본 병합(배열 연결)
const merged = deepmerge(json1, json2);
console.log(merged);
// 출력: { user: { name: "John", age: 30, hobbies: ["reading", "swimming"] } }

// 사용자 정의 배열 병합
const overwriteMerge = (destinationArray, sourceArray) => sourceArray;
const options = { arrayMerge: overwriteMerge };
const mergedCustom = deepmerge(json1, json2, options);
console.log(mergedCustom);
// 출력: { user: { name: "John", age: 30, hobbies: ["swimming"] } }

다른 언어에서의 JSON 병합

Python

내장 사전 업데이트 메서드 사용:

import json

json1_str = '{"name": "John", "age": 30}'
json2_str = '{"city": "New York", "age": 31}'

# JSON 문자열을 사전으로 파싱
json1 = json.loads(json1_str)
json2 = json.loads(json2_str)

# 사전 병합
merged = {**json1, **json2}  # Python 3.5+

# JSON 문자열로 다시 변환
merged_json = json.dumps(merged)
print(merged_json)
# 출력: {"name": "John", "city": "New York", "age": 31}

Python에서의 깊은 병합:

def deep_merge(dict1, dict2):
    result = dict1.copy()
    for key, value in dict2.items():
        if key in result and isinstance(result[key], dict) and isinstance(value, dict):
            result[key] = deep_merge(result[key], value)
        else:
            result[key] = value
    return result

json1 = {"user": {"name": "John", "settings": {"theme": "dark"}}}
json2 = {"user": {"age": 30, "settings": {"notifications": True}}}

merged = deep_merge(json1, json2)
print(merged)
# 출력: {'user': {'name': 'John', 'settings': {'theme': 'dark', 'notifications': True}, 'age': 30}}

Ruby

Hash#merge 메서드 사용:

require 'json'

json1_str = '{"name": "John", "age": 30}'
json2_str = '{"city": "New York", "age": 31}'

# JSON 문자열을 해시로 파싱
json1 = JSON.parse(json1_str)
json2 = JSON.parse(json2_str)

# 해시 병합
merged = json1.merge(json2)

# JSON 문자열로 다시 변환
merged_json = JSON.generate(merged)
puts merged_json
# 출력: {"name":"John","city":"New York","age":31}

Ruby에서의 깊은 병합:

require 'json'

# Ruby의 내장 deep_merge
require 'active_support/core_ext/hash/deep_merge'

json1 = JSON.parse('{"user": {"name": "John", "settings": {"theme": "dark"}}}')
json2 = JSON.parse('{"user": {"age": 30, "settings": {"notifications": true}}}')

merged = json1.deep_merge(json2)
puts JSON.generate(merged)
# 출력: {"user":{"name":"John","settings":{"theme":"dark","notifications":true},"age":30}}

특수 병합 사례

배열 병합

배열을 포함하는 JSON 객체를 병합할 때 여러 전략이 있습니다:

  1. 대체: 나중 배열이 이전 배열을 완전히 대체
  2. 연결: 두 배열의 요소 결합
  3. 인덱스별 병합: 동일한 위치에 있는 배열 요소 병합
  4. ID별 병합: 식별자 필드를 기반으로 배열 요소 병합
// 배열 연결 예제
const json1 = { tags: ["important", "urgent"] };
const json2 = { tags: ["completed", "archived"] };

const merged = {
  ...json1,
  tags: [...json1.tags, ...json2.tags]
};
console.log(merged);
// 출력: { tags: ["important", "urgent", "completed", "archived"] }

// ID별 배열 병합 예제
const users1 = { users: [{ id: 1, name: "John" }, { id: 2, name: "Jane" }] };
const users2 = { users: [{ id: 1, age: 30 }, { id: 3, name: "Bob", age: 25 }] };

function mergeArraysById(arr1, arr2, idKey) {
  const merged = [...arr1];
  
  arr2.forEach(item2 => {
    const item1Index = merged.findIndex(item1 => item1[idKey] === item2[idKey]);
    
    if (item1Index >= 0) {
      merged[item1Index] = { ...merged[item1Index], ...item2 };
    } else {
      merged.push(item2);
    }
  });
  
  return merged;
}

const mergedUsers = {
  users: mergeArraysById(users1.users, users2.users, 'id')
};

console.log(mergedUsers);
// 출력: {
//   users: [
//     { id: 1, name: "John", age: 30 },
//     { id: 2, name: "Jane" },
//     { id: 3, name: "Bob", age: 25 }
//   ]
// }

null 및 undefined 값 처리

객체를 병합할 때 nullundefined 값을 처리하는 방법을 결정해야 합니다:

const json1 = { name: "John", age: null, city: undefined };
const json2 = { age: 30 };

// 기본 동작(null 값은 복사되고, undefined는 무시됨)
const merged1 = Object.assign({}, json1, json2);
console.log(merged1);
// 출력: { name: "John", age: 30 }

// 깊은 병합에서의 사용자 정의 처리
function customDeepMerge(target, source) {
  const output = Object.assign({}, target);
  
  if (isObject(target) && isObject(source)) {
    Object.keys(source).forEach(key => {
      // 소스의 null 값 건너뛰기
      if (source[key] === null) return;
      
      if (isObject(source[key])) {
        if (!(key in target)) {
          output[key] = source[key];
        } else {
          output[key] = customDeepMerge(target[key], source[key]);
        }
      } else {
        output[key] = source[key];
      }
    });
  }
  
  return output;
}

JSON 병합을 위한 명령줄 도구

jq 사용

jq는 JSON 파일을 병합할 수 있는 강력한 명령줄 JSON 프로세서입니다:

# 두 JSON 파일 병합
jq -s '.[0] * .[1]' file1.json file2.json > merged.json

# 사용자 정의 배열 처리가 있는 깊은 병합
jq -s '.[0] * .[1] | .array = (.[0].array + .[1].array)' file1.json file2.json > merged.json

Node.js 사용

Node.js로 JSON 파일을 병합하는 간단한 스크립트를 만들 수 있습니다:

const fs = require('fs');
const _ = require('lodash');

// JSON 파일 읽기
const file1 = JSON.parse(fs.readFileSync('file1.json', 'utf8'));
const file2 = JSON.parse(fs.readFileSync('file2.json', 'utf8'));

// 객체 병합
const merged = _.merge({}, file1, file2);

// 결과 쓰기
fs.writeFileSync('merged.json', JSON.stringify(merged, null, 2));

JSON 병합 모범 사례

  1. 중복 키에 대해 명확히 하기: 선택한 방법이 키 충돌을 어떻게 처리하는지 이해하기
  2. 불변성 고려하기: 기존 객체를 수정하는 대신 새 객체 생성하기
  3. 깊은 병합 신중하게 처리하기: 중첩된 객체에 적절한 재귀 메서드나 라이브러리 사용하기
  4. 병합된 결과 검증하기: 최종 객체가 유효한 구조를 가지고 있는지 확인하기
  5. 경계 사례로 테스트하기: 빈 객체, null 값, 깊게 중첩된 구조
  6. 병합 전략 문서화하기: 충돌이 어떻게 해결되는지 명확히 하기
  7. 성능 고려하기: 큰 객체의 경우 일부 깊은 병합 구현이 비효율적일 수 있음

결론

JSON 객체 병합은 일반적이지만 미묘한 작업입니다. 적절한 병합 전략은 특정 요구 사항, 데이터 구조 및 언어 환경에 따라 달라집니다. 다양한 병합 기술과 그 의미를 이해함으로써 데이터 무결성을 유지하고 애플리케이션의 요구 사항을 충족하면서 다양한 소스의 데이터를 효과적으로 결합할 수 있습니다.

작성자

avatar for Corey
Corey

카테고리

뉴스레터

커뮤니티에 가입하세요

최신 뉴스와 업데이트를 받으려면 뉴스레터를 구독하세요