ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • go 언어에서 deep copy 사용
    Dev 2025. 3. 24. 08:02
    728x90

    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
Designed by Tistory.