JavaScript / concat()으로 배열 합치기와 원본 보존

Array.concat()은 기존 배열 뒤에 값이나 다른 배열의 요소를 이어 붙여 새 배열을 반환합니다. 원본 배열은 바꾸지 않습니다. 인수로 일반 값을 전달하면 그 값이 한 요소가 되고, 배열을 전달하면 배열의 요소가 한 단계 펼쳐져 이어집니다.

값과 배열을 인수로 전달할 때

배열 first에 문자열 두 개를 전달하면 결과에 두 요소가 추가됩니다. 다른 배열을 전달해도 그 배열의 요소가 순서대로 추가됩니다. 여러 인수를 함께 전달할 수도 있습니다.

const first = ['Lorem', 'Ipsum'];
const second = ['Dolor', 'Amet'];

console.log(first.concat('Dolor', 'Amet'));
// ['Lorem', 'Ipsum', 'Dolor', 'Amet']
console.log(first.concat(second));
// ['Lorem', 'Ipsum', 'Dolor', 'Amet']
console.log(first); // ['Lorem', 'Ipsum']

반환값을 변수에 저장하거나 사용하지 않으면 원본은 그대로이므로 아무 변화도 보이지 않습니다. concat()에 인수를 주지 않으면 원본의 얕은 복사본이 만들어집니다. MDN의 Array.concat() 문서에서 반환 규칙을 확인할 수 있습니다.

중첩 배열은 자동으로 계속 펼쳐지지 않습니다

concat()은 전달된 배열의 바깥쪽 요소만 펼칩니다. 배열 속의 배열까지 재귀적으로 평탄화하지 않습니다. 배열 자체를 결과의 한 요소로 넣고 싶다면 한 번 더 감싸 전달할 수 있습니다.

const first = ['A'];
const second = ['B', 'C'];
console.log(first.concat(second));   // ['A', 'B', 'C']
console.log(first.concat([second])); // ['A', ['B', 'C']]
console.log(first.concat([['D']]));  // ['A', ['D']]

결과가 중첩 배열이면 화면에 단순히 문자열로 출력할 때 구조를 알아보기 어렵습니다. 예제에서는 JSON.stringify()로 한 단계 안의 배열 구조가 보이도록 표시합니다. 구조를 깊이 평탄화하려는 목적이라면 flat()처럼 그 작업을 명시하는 메서드를 검토하세요.

전체 예제: 인수 형태와 원본 비교

아래 코드를 UTF-8로 array-concat-example.html에 저장하고 브라우저에서 엽니다. 입력을 쉼표로 구분해 배열로 전달하거나, 문자열 하나로 전달하거나, 배열 자체를 한 요소로 감싸 전달할 수 있습니다. 각 경우 원본과 결과를 함께 보여 줍니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>concat 인수 형태 비교</title>
</head>
<body>
<label for="values">추가할 내용(쉼표로 구분)</label>
<input id="values" type="text" value="Dolor, Amet">
<label for="mode">추가 방식</label>
<select id="mode">
  <option value="array">배열의 요소를 이어 붙이기</option>
  <option value="single">입력 문자열 하나를 붙이기</option>
  <option value="nested">배열 자체를 한 요소로 넣기</option>
</select>
<button id="run" type="button">결과 확인</button>
<button id="reset" type="button">처음으로</button>
<pre id="output" aria-live="polite"></pre>
<script>
const first = ['Lorem', 'Ipsum'];
const input = document.querySelector('#values');
const mode = document.querySelector('#mode');
const output = document.querySelector('#output');

function show() {
  const value = input.value.trim();
  if (value === '') {
    output.textContent = '추가할 값을 입력하세요.';
    return;
  }
  const parts = value.split(',').map(part => part.trim());
  let joined;
  if (mode.value === 'single') {
    joined = first.concat(value);
  } else if (mode.value === 'array') {
    joined = first.concat(parts);
  } else {
    joined = first.concat([parts]);
  }
  output.textContent = `원본: ${JSON.stringify(first)}\n` +
    `결과: ${JSON.stringify(joined)}`;
}

document.querySelector('#run').addEventListener('click', show);
document.querySelector('#reset').addEventListener('click', () => {
  input.value = 'Dolor, Amet';
  mode.value = 'array';
  show();
});
show();
</script>
</body>
</html>

기본 모드는 배열의 요소를 이어 붙여 ["Lorem","Ipsum","Dolor","Amet"]를 만듭니다. 문자열 하나를 선택하면 "Dolor, Amet"이 한 요소이고, 배열 자체를 넣으면 ["Dolor","Amet"]이 중첩 요소 하나입니다. 어떤 모드에서도 원본은 두 요소 그대로입니다. ‘처음으로’를 누르면 입력과 모드가 복원됩니다.

새 배열과 원본의 내부 객체는 공유될 수 있습니다

concat()은 얕은 복사입니다. 문자열이나 숫자 값은 새 배열에 복사되지만 객체 요소는 같은 객체를 참조합니다. 새 배열에서 객체 내부의 속성을 바꾸면 원본 배열에서도 변경이 보입니다. 배열의 칸 자체에 다른 객체를 대입하는 것과 구별해야 합니다.

const source = [{ name: '민지' }];
const result = source.concat({ name: '하나' });
result[0].name = '유나';
console.log(source[0].name); // '유나'
console.log(source.length);  // 1

원본에 요소를 실제로 추가하려면 push()를 사용할 수 있습니다. push()는 원본을 변경하고 새 길이를 반환하므로 concat()과 결과가 다릅니다. 원본 보존이 필요한지, 중첩 객체까지 독립적으로 수정해야 하는지에 따라 선택하세요.