🦆

Interface — Ruby의 duck typing이 타입 시스템을 만났을 때

명시적 implements 없이, 메서드만 맞으면 자동으로 인터페이스를 만족한다

Ruby의 duck typing: "quack 메서드가 있으면 오리다." 런타임에 메서드가 있는지 확인한다. 없으면 NoMethodError.

Go의 interface: "quack 메서드가 있으면 Quacker 타입이다." 컴파일 타임에 확인한다. 없으면 컴파일 에러.

핵심 차이는 이거다. 같은 철학이지만, Ruby는 런타임에 "되면 좋고", Go는 컴파일 타임에 "안 되면 배포 못 한다."

암묵적 구현 — implements가 없다

Java에서는 class Dog implements Animal이라고 명시한다. Go에서는 명시하지 않는다:

type Quacker interface { Quack() string }

type Duck struct{}
func (d Duck) Quack() string { return "꽥" }
// Duck은 자동으로 Quacker 인터페이스를 만족한다

Duck이 Quacker를 구현한다는 선언이 어디에도 없다. Quack() string 메서드가 있으니까 자동으로 만족. Ruby의 duck typing 철학을 타입 시스템으로 가져온 거다.

빈 인터페이스 = Ruby의 Object

interface{}(Go 1.18+에서는 any)는 모든 타입이 만족하는 인터페이스다. Ruby의 Object 클래스와 같다. 모든 값을 받을 수 있지만, 타입 안전성을 포기하는 거다.

io.Reader, io.Writer — 작은 인터페이스의 힘

Go의 표준 라이브러리는 아주 작은 인터페이스를 기본 단위로 쓴다. io.ReaderRead(p []byte) (n int, err error) 메서드 하나만 요구한다. 파일, HTTP 응답, 문자열, 압축 스트림 — 전부 이 하나의 인터페이스를 만족한다.

Ruby의 IO 객체가 read 메서드를 가진 것과 비슷하지만, Go에서는 이게 타입 시스템으로 강제된다.

Ruby에서 Go로

1

Ruby: duck typing (런타임 확인) → Go: interface (컴파일 타임 duck typing)

2

Java: implements 명시 → Go: 암묵적 구현 (메서드만 맞으면 자동 만족)

3

Ruby: Object (모든 것의 부모) → Go: interface{}/any (모든 타입 수용)

4

인터페이스는 작게 — io.Reader처럼 메서드 1~2개가 이상적

장점

  • Ruby의 duck typing 철학을 유지하면서 컴파일 타임 안전성까지 보장
  • implements 선언 불필요 — 패키지 간 의존성이 느슨해진다

단점

  • 어떤 struct가 어떤 interface를 만족하는지 코드만 보면 알기 어렵다
  • interface{}(any)를 남용하면 Ruby의 동적 타이핑과 다를 바 없어진다

사용 사례

Ruby의 respond_to? 패턴을 Go의 인터페이스 타입 체크로 전환할 때