본문 바로가기
Language/Java

[JAVA] Cloneable 인터페이스와 clone() 메서드 사용법 : 얕은 복사의 함정과 해결책

by Papa Martino V 2026. 1. 20.
728x90

Cloneable 인터페이스와 clone() 메서드
Cloneable 인터페이스와 clone() 메서드

 

자바에서 객체를 복제(Clone)하는 작업은 단순히 변수를 대입하는 것과는 완전히 다른 차원의 이야기입니다. 자바는 객체의 복제를 지원하기 위해 java.lang.Cloneable 인터페이스와 Object.clone() 메서드를 제공합니다. 하지만 이 기능은 자바 설계 초기부터 존재했던 '오래된 유산'이며, 사용법이 매우 독특하고 까다롭기로 유명합니다. 오늘은 실무에서 Cloneable을 올바르게 구현하는 방법부터, 왜 많은 시니어 개발자들이 이 방식 대신 다른 대안을 권장하는지 그 기술적 이유까지 심도 있게 다뤄보겠습니다.

1. Cloneable 인터페이스와 clone() 메서드의 메커니즘

가장 먼저 이해해야 할 점은 Cloneable 인터페이스에는 아무런 추상 메서드가 없다는 사실입니다. 이는 단순히 해당 클래스가 복제 가능하다는 것을 JVM에 알리는 '마커 인터페이스(Marker Interface)' 역할을 합니다. 실제 복제 로직은 Object.clone()에 구현되어 있습니다. 만약 Cloneable을 구현하지 않은 클래스에서 clone()을 호출하면 CloneNotSupportedException이 발생합니다. 이는 인터페이스 설계 원칙에 비추어 볼 때 매우 이례적인 구조입니다.

2. 올바른 clone() 메서드 구현 단계

표준적인 clone() 구현을 위해서는 다음과 같은 단계를 거쳐야 합니다.

  • Cloneable 인터페이스를 클래스 선언부에 명시합니다.
  • clone() 메서드를 public으로 오버라이딩합니다 (Object의 기본값은 protected).
  • 메서드 내부에서 super.clone()을 호출하여 복제본을 얻습니다.
  • 필요에 따라 참조 타입 필드에 대해 깊은 복사(Deep Copy) 로직을 추가합니다.

3. 얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy) 비교

Object.clone()이 기본적으로 수행하는 복사의 성격을 이해하는 것이 가장 중요합니다.

구분 내용 특징
기본 동작 얕은 복사 (Shallow Copy) 필드 값을 그대로 복사함 (참조값 포함)
객체 독립성 불완전 내부 가변 객체 수정 시 원본과 복사본이 동시에 변함
추천 사용처 Primitive Type만 가진 클래스 복잡한 구조에서는 사이드 이펙트 위험 큼

4. Sample Example: 깊은 복사를 포함한 clone() 구현


public class Point implements Cloneable {
    private int x;
    private int y;
    private int[] data; // 가변 참조 타입 필드

    public Point(int x, int y, int[] data) {
        this.x = x;
        this.y = y;
        this.data = data;
    }

    @Override
    public Point clone() {
        try {
            // 1. 기본 얕은 복사 수행
            Point cloned = (Point) super.clone();
            
            // 2. 가변 객체(배열 등)에 대한 깊은 복사 수행 (이 단계가 없으면 데이터를 공유함)
            if (this.data != null) {
                cloned.data = this.data.clone();
            }
            
            return cloned;
        } catch (CloneNotSupportedException e) {
            // Cloneable을 구현했으므로 발생하지 않음
            throw new AssertionError();
        }
    }
}
    

5. 독창적인 인사이트: Cloneable의 한계와 대안

사실 자바의 거장 조슈아 블로크(Joshua Bloch)는 Cloneable 시스템이 결함이 많다고 지적합니다. 생성자를 통하지 않고 객체를 생성하며, 불필요한 체크드 예외를 던지며, final 필드의 동작과도 충돌하기 때문입니다. 따라서 현대적인 자바 개발에서는 다음과 같은 대안을 더 권장합니다.

  • 복사 생성자(Copy Constructor): public Point(Point other) { ... }
  • 복사 팩토리(Copy Factory): public static Point newInstance(Point other) { ... }

이 방식들은 인터페이스에 의존하지 않으며 더 안전하고 명확합니다.

6. 결론

Cloneable은 자바의 역사적인 이유로 남아있는 도구입니다. 기존 라이브러리와의 호환성을 위해 사용법을 익히는 것은 중요하지만, 새로운 시스템을 설계할 때는 복사 생성자를 사용하는 것이 유지보수와 안정성 측면에서 훨씬 유리합니다. 만약 꼭 clone()을 써야 한다면, 반드시 내부 가변 객체들에 대한 깊은 복사가 이루어졌는지 다시 한번 점검하시기 바랍니다.


내용 출처:
- Java Platform, Standard Edition 17 API Specification: java.lang.Cloneable
- Effective Java 3rd Edition (Joshua Bloch) - Item 13: Override clone judiciously
- Oracle Java Documentation: Object.clone() methods

728x90