Java 배열 clone() 설명: 예제와 모범 사례를 통한 심층 분석

발행: (2025년 12월 14일 오후 10:49 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

배열에 대한 clone() 메서드가 정확히 무엇인가요?

즉시 기억해야 할 핵심: 배열에 대해 clone()얕은 복사를 수행합니다.

얕은 복사 vs. 깊은 복사: 핵심 개념

  • 얕은 복사 – 새로운 배열 객체가 생성되지만, 그 요소들은 원본 배열에 저장된 동일한 객체에 대한 참조입니다. 한 배열을 통해 가변 객체를 수정하면 다른 배열에서도 그 변경이 반영됩니다.
  • 깊은 복사 – 원본 배열 그 안에 포함된 모든 객체를 완전히 독립적인 복제본으로 만듭니다. 하나를 변경해도 다른 쪽에는 영향을 주지 않습니다.

원시 타입 배열(int[], char[], …)의 경우 배열이 실제 값을 저장하므로 구분이 의미가 없습니다. 이 경우 clone()은 사실상 독립적인 복사본을 제공합니다.

객체 배열(String[], Employee[], …)은 참조를 저장하므로 얕은 복사는 그 참조들만 복제합니다.

직접 해보는 예제

예제 1 – 원시 타입 배열

import java.util.Arrays;

public class CloneDemo {
    public static void main(String[] args) {
        int[] originalScores = {95, 87, 92, 64};
        int[] clonedScores = originalScores.clone();

        System.out.println("Original Array: " + Arrays.toString(originalScores));
        System.out.println("Cloned Array:   " + Arrays.toString(clonedScores));

        // Change the cloned array
        clonedScores[0] = 100;

        System.out.println("\nAfter modifying cloned array:");
        System.out.println("Original Array: " + Arrays.toString(originalScores)); // Still 95
        System.out.println("Cloned Array:   " + Arrays.toString(clonedScores));   // Now 100
    }
}

출력

Original Array: [95, 87, 92, 64]
Cloned Array:   [95, 87, 92, 64]

After modifying cloned array:
Original Array: [95, 87, 92, 64]
Cloned Array:   [100, 87, 92, 64]

예제 2 – 함정! (객체 배열)

class Player {
    String name;
    int level;

    Player(String name, int level) {
        this.name = name;
        this.level = level;
    }
}

public class CloneGotcha {
    public static void main(String[] args) {
        Player[] originalTeam = {
            new Player("Alex", 10),
            new Player("Sam", 15)
        };
        Player[] clonedTeam = originalTeam.clone();

        System.out.println("Before change:");
        System.out.println("Original[0]: " + originalTeam[0].level); // 10
        System.out.println("Cloned[0]:   " + clonedTeam[0].level);   // 10

        // Modify the object referenced by the cloned array
        clonedTeam[0].level = 99;

        System.out.println("\nAfter changing clonedTeam[0].level to 99:");
        System.out.println("Original[0]: " + originalTeam[0].level); // Oops! 99!
        System.out.println("Cloned[0]:   " + clonedTeam[0].level);   // 99
    }
}

출력

Before change:
Original[0]: 10
Cloned[0]:   10

After changing clonedTeam[0].level to 99:
Original[0]: 99
Cloned[0]:   99

실제 활용 사례: clone()이 빛을 발하는 곳

Getter에서 방어적 복사

public class Configuration {
    private String[] settings;

    public String[] getSettings() {
        return settings.clone(); // Caller cannot modify the internal array
    }
}

깊은 복사를 위한 빌딩 블록

clone()은 더 복잡한 깊은 복사 루틴의 첫 단계가 될 수 있으며, 이후 배열에 들어있는 가변 객체들을 수동으로 복사하면 됩니다.

모범 사례 및 피해야 할 함정

  • 객체 배열은 얕은 복사라고 가정하세요. 배열이 가변 객체를 담고 있다면 clone()만으로는 충분하지 않은 경우가 많습니다.
  • 수동 깊은 복사를 수행하세요. 독립적인 객체가 필요할 때는 다음과 같이 구현할 수 있습니다 (예: Player 배열):
Player[] deepCopyTeam = new Player[originalTeam.length];
for (int i = 0; i (original)` or `list.toArray()` for copying.
  • Q: 언제 절대 clone()을 사용하면 안 될까요?
    A: 가변 객체를 포함한 배열을 깊게 복사해야 할 때, 혹은 의도를 더 명확히 하고 싶을 때는 Arrays.copyOf나 수동 복사와 같은 대안을 사용하는 것이 안전합니다.

  • Q: 성능은 어떨까요?
    A: clone()은 네이티브 배열 복사이며 얕은 복사에서는 일반적으로 빠릅니다. 깊은 복사는 추가 작업이 필요하므로 복제하는 데이터 양에 비례해 느려집니다.

결론: 사용해야 할까?

  • clone()을 사용하세요 원시 타입 배열, 불변 객체 배열(String, Integer 등) 혹은 얕은 복사만으로 충분할 때.
  • clone()을 피하세요 가변 객체의 독립적인 복사가 필요할 때.
  • 대안을 고려하세요 Arrays.copyOf, System.arraycopy, 혹은 명시적 루프 등을 사용하면 의도가 더 명확하고 깊은 복사 제어가 용이합니다.

clone()이 어떻게 동작하는지, 그리고 그 한계가 무엇인지 이해하는 것은 올바르고 효율적인 Java 코드를 작성하는 데 필수적입니다.

Back to Blog

관련 글

더 보기 »

왜 디자인 패턴?

디자인 패턴은 무작위로, “모두에게 맞는” 방식으로 억지로 조합한 단축키처럼 사용하도록 만든 것이 아니라는 점을 이해하는 것이 중요합니다.