Go

Go 1.18에서 `any`로 더 간결하게: `interface{}`의 진화

드리프트2 2024. 10. 6. 22:22

 

Go 1.18에서 any로 더 간결하게: interface{}의 진화

안녕하세요, 여러분.

 

오늘은 Go 1.18에서 도입된 중요한 기능 중 하나인 any 타입에 대해 깊이 있게 알아보겠습니다.

 

Go 언어는 1.18 버전부터 interface{}를 대체하는 새로운 타입 에일리어스인 any를 도입했습니다.

 

이 변화는 단순한 문법적 추가로 보일 수 있지만, Go 언어의 가독성 및 유지보수성을 높이는 데 중요한 역할을 하게 될 것입니다.

 

오늘 강의에서는 any의 도입 배경과 실제로 어떻게 활용할 수 있는지에 대해 설명드리겠습니다.

 

그리고 왜 interface{}를 대체할 만한 의미 있는 변화로 받아들여야 하는지도 함께 살펴보겠습니다.

 


 

1. any: 새로운 타입 에일리어스

먼저, any가 무엇인지부터 살펴보겠습니다.

 

사실 any는 완전히 새로운 기능이 아닙니다.

 

anyinterface{}와 동일한 역할을 하는 타입 에일리어스로, Go 1.18부터 사용할 수 있습니다.

 

즉, any모든 타입을 받을 수 있는 빈 타입을 의미하며, 코드의 가독성을 높이기 위해 도입된 것입니다.

 

Go의 전역 스코프, 즉 유니버스 블록에서 다음과 같은 선언을 추가했다고 생각하면 됩니다.

 

type any = interface{}

 

이 선언은 기존에 우리가 알고 있던 interface{}와 동일하게 작동하지만, "모든 타입을 의미하는" 더 직관적인 이름을 제공하게 됩니다.

 


 

2. 왜 any인가? interface{}와의 차이점

그렇다면 왜 굳이 interface{} 대신 any를 도입했을까요?

 

그 이유는 가독성명확한 의도 표현 때문입니다.

 

우리가 코드에서 interface{}를 사용할 때, 그 의미는 모든 타입을 받아들이겠다는 것입니다.

 

하지만 interface{}라는 단어는 이 개념을 직관적으로 전달하지 못하는 경향이 있습니다.

 

반면, any는 그 자체로 "어떤 타입이든 가능하다"는 의미를 전달하기 때문에, 더 짧고 명확하게 의도를 표현할 수 있습니다.

 

// 기존의 interface{} 사용
var v interface{}
v = 12345
v = "abcde"

// 새로운 any 사용
var v any
v = 12345
v = "abcde"

 

위와 같이 interface{} 대신 any를 사용하면, 코드를 읽는 사람에게 더 명확한 의도를 전달할 수 있습니다.

 

특히 Go 언어를 처음 배우는 사람들에게 any는 더 직관적인 선택이 될 수 있습니다.

 


 

3. any의 활용: 변수, 함수, 구조체에서의 사용

anyinterface{}의 모든 사용처에서 대체가 가능합니다.

 

즉, 변수를 선언할 때, 함수의 매개변수나 반환값을 정의할 때, 그리고 구조체 필드를 정의할 때 등, interface{}를 사용할 수 있는 모든 곳에서 any를 사용할 수 있습니다.

 

다음은 몇 가지 예시입니다.

// 변수 선언
var x any
x = 42
x = "Hello"

// 함수의 매개변수와 반환값
func Process(value any) any {
    return value
}

// 구조체 필드에서 사용
type Container struct {
    field any
}

 

이처럼 any는 기존의 interface{}와 동일하게 동작하면서도 더 깔끔하고 직관적인 코드 작성을 가능하게 합니다.

 


 

4. any와 제네릭: 더 강력한 타입 시스템의 시작

Go 1.18에서 가장 큰 변화 중 하나는 제네릭(Generic) 기능의 도입입니다.

 

제네릭을 통해 우리는 더 유연하고 재사용 가능한 코드를 작성할 수 있습니다.

 

그리고 any는 제네릭과 결합되어 더 강력한 타입 시스템을 구축하는 데 중요한 역할을 합니다.

 

제네릭에서 any는 모든 타입을 허용하는 제약으로 사용될 수 있습니다.

 

이는 기존에 interface{}를 사용해 타입 제한을 걸었던 방식과 동일하지만, 더 명확한 의사 전달을 가능하게 합니다.

 

// `any`를 사용한 제네릭 함수
func Print[T any](value T) {
    fmt.Println(value)
}

 

이 함수는 어떤 타입이든 매개변수로 받아들여 출력하는 제네릭 함수입니다.

 

이때 T의 제약으로 any를 사용했기 때문에, 모든 타입이 허용됩니다.

 

기존 방식으로는 interface{}를 사용했겠지만, 이제는 any를 사용해 더 명확하게 표현할 수 있습니다.

 


 

5. 기존 코드와의 호환성

Go 1.18에서 any가 도입되었지만, 기존에 작성된 interface{} 코드와 완벽한 호환성을 유지합니다.

 

즉, interface{}가 사용된 코드는 여전히 문제없이 작동하며, 필요에 따라 any로 변경할 수 있습니다.

 

다음은 interface{}any를 혼용하는 예시입니다.

func OldFunc(v interface{}) {
    fmt.Println(v)
}

func main() {
    var v any = 123
    OldFunc(v)  // `any` 타입을 `interface{}`로 전달 가능
}

 

이처럼 anyinterface{}는 상호 변환이 가능하며, 기존 코드에 큰 영향을 주지 않습니다.

 

즉, any점진적으로 도입할 수 있는 변화라는 장점이 있습니다.

 


 

6. 제네릭과 any의 조합이 필요한 경우

제네릭을 사용할 때는 언제 any를 사용해야 하고 언제 더 구체적인 타입 제약을 사용해야 할까요?

 

이에 대한 판단은 함수나 메서드가 처리하려는 데이터의 특성에 따라 달라집니다.

 

예를 들어, 다음과 같은 함수가 있다고 가정해봅시다.

 

func First(elements []interface{}) interface{} {
    return elements[0]
}

 

이 함수는 interface{} 타입의 슬라이스에서 첫 번째 요소를 반환하는데, 이는 매우 비효율적입니다.

 

왜냐하면 사용자는 슬라이스를 넘길 때 타입 변환을 해야 하기 때문이죠.

 

이런 경우에는 제네릭을 사용하는 것이 더 나은 선택입니다.

 

func First[T any](elements []T) T {
    return elements[0]
}

 

이제 이 함수는 어떤 타입의 슬라이스든 받아들일 수 있으며, 타입 변환 없이 첫 번째 요소를 반환합니다.

 

제네릭과 any의 조합은 이렇게 더 유연하고 강력한 코드를 작성할 수 있게 도와줍니다.

 


 

7. any를 사용할 때와 제네릭을 사용할 때

any는 단순히 interface{}를 대체할 수 있는 타입 에일리어스입니다.

 

따라서 모든 타입을 받아들여야 하는 경우에는 any를 사용하는 것이 적합합니다.

 

예를 들어, 다음과 같은 경우에는 any가 적절한 선택입니다.

var storage = make(map[string]any)

func Store(key string, value any) {
    storage[key] = value
}

func Load(key string) any {
    return storage[key]
}

 

이 코드는 다양한 타입의 값을 저장하고 불러오는 간단한 예시입니다.

 

이처럼 제네릭이 필요 없는 경우에는 any로 충분히 간단하고 명확한 코드를 작성할 수 있습니다.

 


 

8. 마무리: any가 가져올 변화

오늘 강의를 통해 Go 1.18에서 도입된 any가 무엇인지, 그리고 이를 어떻게 활용할 수 있는지 알아보았습니다.

 

any는 단순한 문법적 변화처럼 보일 수 있지만, Go 언어의 가독성과 유지보수성을 크게 향상시킬 수 있는 중요한 변화입니다.

 

특히 제네릭과 결합했을 때, 더 유연하고 직관적인 코드를 작성할 수 있습니다.

 

이제 여러분도 코드에서 interface{}를 보게 된다면, 이를 any로 바꿔보세요.

 

더 가독성 좋은 코드를 작성할 수 있을 것입니다.

 

감사합니다.