프로그래밍Java 개발자

Java에서 객체의 얕은 복사(shallow copy)와 깊은 복사(deep copy)의 차이점 설명해 주세요. 이를 어떻게 구현할 수 있으며, 이러한 차이를 이해하는 것이 중요한 상황은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

Java에서 얕은 복사(shallow copy)란 객체를 복사할 때 중첩된 객체의 참조만 복사하고 실제 객체는 복사하지 않는 것입니다. 따라서 중첩된 객체의 변경은 복사본과 원본 모두에 반영됩니다. 깊은 복사(deep copy)는 모든 중첩된 객체의 새로운 독립적인 복사본을 생성하는 것을 의미합니다.

얕은 복사 예시 (clone 사용)

class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { return super.clone(); } } // 사용 예 Person p1 = new Person("Ivan", new Address("Moscow")); Person p2 = (Person) p1.clone(); p2.address.city = "Petersburg"; System.out.println(p1.address.city); // "Petersburg" 출력 — 얕은 복사

깊은 복사 예시

class Address implements Cloneable { String city; Address(String city) { this.city = city; } protected Object clone() throws CloneNotSupportedException { return new Address(this.city); } } class Person implements Cloneable { String name; Address address; Person(String name, Address address) { this.name = name; this.address = address; } protected Object clone() throws CloneNotSupportedException { Person cloned = (Person) super.clone(); cloned.address = (Address) address.clone(); return cloned; } }

깊은 복사는 객체의 완전히 독립적인 복사본을 생성할 수 있게 해줍니다.

함정 질문.

질문: 임의의 복잡한 객체에 대해 깊은 복사를 보장하기 위해 clone() 메소드를 사용할 수 있을까요?

답변: 아닙니다, 기본 clone()은 얕은 복사만 수행합니다. 깊은 복사를 위해서는 모든 중첩 클래스에서 clone()을 재정의하고 모든 참조 필드를 명시적으로 복제해야 합니다. 또한 많은 컬렉션과 표준 클래스는 깊은 복제를 지원하지 않습니다.

주제에 대한 미세한 차이를 모르고 생긴 실제 오류 사례.


이야기

사용자 및 그 주소 컬렉션이 깊은 복사 없이 clone() 메소드로 복제되었습니다. 한 사용자가 주소를 변경한 후 다른 사용자도 주소가 변경되었고, 이는 참조가 복제되었기 때문입니다. 데이터가 사용자 간에 "흘러" 다녔습니다.


이야기

중첩된 컬렉션을 가진 복잡한 객체를 직렬화할 때 clone()을 사용하고 깊은 복사를 사용하지 않았습니다. 직렬화에서 복원 후 데이터가 일관성이 없게 되어 NPE가 발생했습니다.


이야기

REST API에서 DTO 객체가 기본적으로 BeanUtils.copyProperties()를 통해 복제되었고, 이로 인해 클라이언트 간에 동일한 컬렉션이 공유되었습니다. 결과적으로 사용자가 다른 사람의 데이터를 보고 편집할 수 있었습니다.