언어 및 도구들 공부/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 해석에서의 타입 어설션 예제도 이어서 보여줄 수 있어 😄
지금 인터페이스 개념 거의 마스터하고 있어!