-
go 언어에서 deep copy 사용Dev 2025. 3. 24. 08:02728x90
Go 언어는 대부분 값 타입(value semantics)이 기본이지만, 슬라이스, 맵, 포인터, 구조체 포인터 등을 다루다 보면 얕은 복사(shallow copy)로 인한 예기치 않은 공유 문제가 발생할 수 있어요.
이럴 때 deep copy(깊은 복사)가 필요해집니다.✅ 언제 Deep Copy가 필요한가?
🔹 1. 공유된 메모리로 인한 부작용을 피하고 싶을 때
- 슬라이스나 맵은 참조 타입이기 때문에 복사해도 같은 메모리 영역을 가리킵니다.
- 이를 수정하면 원본 데이터도 함께 변경되죠.
original := []int{1, 2, 3} copy := original copy[0] = 100 fmt.Println(original) // [100 2 3] ← 원본이 변경됨!
이런 부작용을 막기 위해서는 deep copy로 실제 데이터를 새롭게 복사해야 해요.
🔹 2. 다른 고루틴과 안전하게 데이터 분리하고 싶을 때
- 병행성 상황에서 공유된 맵/슬라이스를 동시에 쓰면 Race Condition이 발생할 수 있어요.
- 복사해서 쓰면 고루틴 간 충돌을 방지할 수 있습니다.
🔹 3. 입력 데이터 원본을 보존해야 하는 상황
- 함수나 API에서 입력 데이터를 수정하되, 원본 데이터를 나중에 다시 사용해야 할 때.
- 구조체가 중첩되거나 포인터 필드가 포함되어 있을 경우, 얕은 복사로는 부족합니다.
✅ Go에서 Deep Copy 구현 방식
Go에는 deepcopy() 같은 내장 함수는 없지만, 아래 방식으로 직접 구현할 수 있어요:
🔸 1. 슬라이스 복사
src := []int{1, 2, 3} dst := make([]int, len(src)) copy(dst, src)
🔸 2. 맵 복사
original := map[string]int{"a": 1} clone := make(map[string]int) for k, v := range original { clone[k] = v }
🔸 3. 구조체 Deep Copy (수동)
type User struct { Name string Tags []string } func DeepCopyUser(u User) User { tagsCopy := make([]string, len(u.Tags)) copy(tagsCopy, u.Tags) return User{ Name: u.Name, Tags: tagsCopy, } }
🔸 4. 구조체 복사 (범용 - JSON 활용)
import "encoding/json" func DeepCopy[T any](src T) (T, error) { var dst T bytes, err := json.Marshal(src) if err != nil { return dst, err } err = json.Unmarshal(bytes, &dst) return dst, err }
※ 단점: 성능 느림, omitempty 영향, 인터페이스 필드는 제대로 복사 안 될 수 있음
🔍 참고: 실무에서 Deep Copy가 필요한 예시
- Redis 캐시에서 가져온 데이터를 수정할 때 원본을 보존해야 할 경우
- 상태(state)를 보존한 채 변경 이력을 관리하는 시스템 (예: Undo/Redo 기능)
- 백업 또는 스냅샷 기능을 구현할 때
✅ 요약
Go에서는 슬라이스, 맵, 포인터를 사용할 때 얕은 복사로 인해 공유 문제가 발생할 수 있고, 이를 방지하기 위해 deep copy가 필요합니다.
구현은 직접 해야 하며, 구조에 따라 copy(), 루프, JSON 활용 등의 방식으로 처리합니다.728x90'Dev' 카테고리의 다른 글
Go 언어에서 Concurrency (동시성) 과 Parallelism (병렬성) (0) 2025.03.24 Go 언어 기원 및 설계 철학 (0) 2025.03.20 Go에서 함수와 상수 이름의 시작 규칙 (대문자 vs. 소문자) (0) 2025.03.03 Go 언어의 Goroutine이란? (0) 2025.03.02 go언어에서 Java의 클래스, 속성, 메소드는 어떻게 구현이되? (1) 2025.03.02