언어 및 도구들 공부/Go
Go) interface와 type assertion
Dr.Mlem
2025. 4. 7. 10:12
A1.
좋아! 😎
지금부터 interface{}(빈 인터페이스)와 일반 인터페이스 타입(예: io.Reader, fmt.Stringer 등)의 차이점을 명확히 정리해줄게!
✅ 먼저 정리부터!
구분 | interface{} | 일반 인터페이스( fmt.Stringer, io.Reader 등) |
의미 | 빈 인터페이스 | 메서드를 요구하는 인터페이스 |
메서드 | 없음 | 특정 메서드가 반드시 구현돼야 함 |
어떤 타입이 담기나? | 모든 타입 | 해당 인터페이스를 구현한 타입만 |
유연성 | 매우 높음 (모든 타입 수용) | 제한적 (명시된 메서드 구현 필요) |
사용 예 | fmt.Println, sync.Pool, JSON 처리 등 | I/O, 문자열 출력, 인터페이스 기반 설계 등 |
🔍 interface{}: 빈 인터페이스
func anything(v interface{}) {
fmt.Println(v)
}
- interface{}는 메서드 요구사항이 없기 때문에 모든 타입이 할당 가능
- 실전에서 범용 함수, 다형성 처리, 유틸리티 함수 등에 자주 사용됨
예시
anything(123)
anything("hi")
anything([]int{1,2,3})
🔍 일반 인터페이스: 메서드가 정의돼 있음
type Stringer interface {
String() string
}
- 이건 fmt.Stringer 인터페이스처럼 특정 메서드를 구현한 타입만 사용할 수 있어
- 예를 들어 이 인터페이스는 String() 메서드가 있어야 함
예시
type Person struct {
Name string
}
func (p Person) String() string {
return "My name is " + p.Name
}
func printString(s fmt.Stringer) {
fmt.Println(s.String())
}
- Person 타입은 String() 메서드를 구현했기 때문에 fmt.Stringer를 만족함
- printString(p) 이런 식으로 호출 가능
✅ 핵심 차이 요약
항목 | interface{} | 일반 인터페이스 (예: Stringer) |
메서드 정의 | ❌ 없음 | ✅ 있음 |
어떤 타입이 할당 가능? | 어떤 타입이든 가능 | 오직 요구 메서드를 구현한 타입만 가능 |
사용 목적 | 범용적인 함수, 저장소, 유틸 처리 | 다형성 인터페이스 설계, 역할 기반 추상화 |
예시 | sync.Pool, fmt.Println(v interface{}) | fmt.Stringer, io.Reader, error 등 |
🎯 비유로 정리하자면
비유 | 설명 |
interface{} | 누구나 입장 가능한 "공공장소" |
일반 인터페이스 | 특정 조건을 만족해야 들어올 수 있는 "회원제 공간" (ex: Stringer는 String() 메서드가 있어야 함) |
✅ 한 줄 요약
🔹 interface{}는 모든 타입을 담을 수 있는 범용 컨테이너
🔹 일반 인터페이스는 특정 메서드를 가진 타입만을 위한 계약(interface)
이제 interface{}는 모든 걸 수용하는 범용 그릇,
일반 인터페이스는 "기능을 갖춘 타입"만 담는 전문 그릇이란 걸 딱 잡고 가면 돼!
필요하면 두 인터페이스를 이용한 예제를 하나 만들어서 비교해볼 수도 있어! 😄
Q2.
타입 어설션이란 말이 나와서 물어볼게. 해당 내용은 무엇을 의미하는거지?
A2.
좋아, 이건 Go에서 인터페이스(interface{} 포함)를 다룰 때 반드시 알아야 할 핵심 개념이야!
**“타입 어설션(type assertion)”**은 아주 자주 등장하는 문법이라 지금 짚고 가는 게 진짜 잘한 선택이야 😊
✅ 한 줄 정의
**타입 어설션(type assertion)**은
interface{} 안에 담긴 실제 값의 구체적인 타입을 꺼내는 방법이야.
📌 왜 필요한가?
Go에서 interface{}는 어떤 타입이든 받을 수 있지만,
👉 꺼낼 때는 정확한 타입을 알아야 함!
그래서 이렇게 써:
v := x.(T)
- x: interface{} 타입 변수
- T: 네가 기대하는 실제 타입
- v: T 타입으로 꺼낸 값
🔍 예제
var x interface{} = "hello"
s := x.(string) // ✅ 타입 어설션: x 안의 값을 string으로 꺼냄
fmt.Println(s) // "hello"
n := x.(int) // ❌ 런타임 에러: x는 int가 아니라 string임
✅ 안전하게 쓰는 방법: v, ok := x.(T)
v, ok := x.(string)
if ok {
fmt.Println("문자열:", v)
} else {
fmt.Println("string이 아님")
}
- ok는 true 또는 false
- 어설션이 성공했는지 여부를 알려줌
- 실패해도 프로그램이 죽지 않음 (이게 실전에서 훨씬 안전한 방식!)
✅ 언제 쓰나?
상황 | 설명 |
interface{} 안에서 실제 타입을 꺼낼 때 | 가장 흔한 사용 |
빈 인터페이스로 처리한 뒤, 실제 타입에 따라 분기할 때 | switch + type assertion |
예: sync.Pool, fmt.Println, any, json.Unmarshal 결과 | 전부 interface{} 기반 처리 |
✅ 타입 스위치도 있음
var x interface{} = 3.14
switch v := x.(type) {
case int:
fmt.Println("int:", v)
case float64:
fmt.Println("float64:", v)
case string:
fmt.Println("string:", v)
default:
fmt.Println("알 수 없는 타입")
}
→ 이건 type switch라고 부르는 고급 문법인데,
타입 어설션을 switch문처럼 여러 경우로 나눠서 처리할 수 있어!
🎯 한 줄 요약
타입 어설션은 interface{} 안에 있는 값을 **"내가 원하는 실제 타입으로 꺼내는 방법"**이야.
v := x.(T) ← 실패하면 런타임 에러
v, ok := x.(T) ← 실패해도 안전하게 처리 가능
필요하다면 type switch, interface{} + JSON 해석에서의 타입 어설션 예제도 이어서 보여줄 수 있어 😄
지금 인터페이스 개념 거의 마스터하고 있어!