
Go interface는 동작(Behavior)을 정의하는 추상 타입입니다.
인터페이스는 구조체나 다른 타입들이 특정 메서드를 구현했는지를 확인하여 다형성을 지원합니다.
이를 통해 Go는 객체 지향 프로그래밍의 중요한 요소 중 하나인 다형성(Polymorphism)을 효과적으로 제공합니다.
여러 타입이 있고 각 타입에 대한 동작의 의미는 같으나 코드는 분리해야 할 때 사용하면 좋다.
예를 들면, 사각형, 원 등 여러 도형에 대하여 넓이를 구한다는 동작의 의미는 동일하지만 도형 별로 코드는 분리되어야 한다.
각 타입에 대해 리시버를 지정한 메소드를 전부 정의해도 좋지만, 인터페이스를 두고 구현하여 코드를 재활용할 수 있도록 해보자.
그러면 같이 일하는 사람이 좋아할 것이다.
아래는 인터페이스에 대한 주요 내용과 예제 코드입니다.
Go 언어 인터페이스 기본 개념
- 인터페이스 정의
- 인터페이스는 메서드 시그니처(메서드 이름, 매개변수, 반환 타입)를 정의합니다.
- 인터페이스를 구현하기 위해 구조체는 정의된 메서드들을 구현하면 됩니다. 명시적으로 “implements” 키워드가 필요하지 않습니다.
- 빈 인터페이스 (Empty Interface)
interface{}
는 어떤 타입도 저장할 수 있는 빈 인터페이스를 의미합니다.- 모든 타입은
interface{}
를 구현합니다.
- 유형 단언(Type Assertion)
- 인터페이스 타입에서 구체적인 타입으로 값을 변환하려면 유형 단언을 사용할 수 있습니다.
- 다형성 (Polymorphism)
- 인터페이스를 사용하여 다양한 구조체를 공통적으로 처리할 수 있습니다.
예제 코드 1: 기본 인터페이스 사용법
package main
import "fmt"
// 인터페이스 정의
type Shape interface {
Area() float64
Perimeter() float64
}
// 구조체 정의 및 메서드 구현
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * 3.14 * c.Radius
}
func main() {
// Shape 인터페이스 사용
var s Shape
s = Rectangle{Width: 10, Height: 5}
fmt.Printf("Rectangle Area: %.2f\n", s.Area())
fmt.Printf("Rectangle Perimeter: %.2f\n", s.Perimeter())
s = Circle{Radius: 7}
fmt.Printf("Circle Area: %.2f\n", s.Area())
fmt.Printf("Circle Perimeter: %.2f\n", s.Perimeter())
}
예제 코드 2: 빈 인터페이스와 다형성
package main
import "fmt"
// 모든 타입을 받을 수 있는 빈 인터페이스
func PrintAnything(i interface{}) {
fmt.Println(i)
}
func main() {
PrintAnything(42) // 정수
PrintAnything("Hello Go") // 문자열
PrintAnything(3.14) // 실수
}
empty interface의 경우 매우 활용도가 높습니다.
Go 언어의 유연함을 체감할 수 있는데, javascript처럼 어떠한 타입이 되었든 값을 취급할 수 있는 변수가 필요할 때 유용합니다.
예제 코드 3: 타입 선언(Type Assertion)
package main
import "fmt"
func main() {
var i interface{} = "Hello"
// 타입 선언
s, ok := i.(string)
if ok {
fmt.Printf("i is a string: %s\n", s)
} else {
fmt.Println("i is not a string")
}
// 타입 언 실패 예
n, ok := i.(int)
if !ok {
fmt.Printf("Type assertion failed. i is not an int: %v\n", n)
}
}
type assertion은 empty interface 변수의 값에 대한 타입을 확인하는 데에 활용할 수 있습니다.
필자의 업무 예시
API를 구성하여 마케팅과 관련된 특정 엔드포인트(POST method)에 값이 들어오면 DB에 저장하는 코드가 있습니다.
해당 API는 특정한 권한이 필요하지 않은 API이기 때문에 어뷰징(혹은 어노잉)이 발생하였습니다. ( 세상에는 이상한 사람이 너무 많습니다. )
따라서, 규칙으로 정한 외 타입의 값을 지속적으로 전달받았을 때 해당 IP를 차단 및 경고 메시지를 노출하는 기능을 개발했습니다.이 때 empty interface와 type assertion을 활용했습니다.
예제 코드 4: 인터페이스로 다형성 구현
package main
import "fmt"
// Animal 인터페이스 정의
type Animal interface {
Speak() string
}
// Dog 구조체와 메서드 구현
type Dog struct{}
func (d Dog) Speak() string {
return "Woof!"
}
// Cat 구조체와 메서드 구현
type Cat struct{}
func (c Cat) Speak() string {
return "Meow!"
}
// Main 함수
func main() {
animals := []Animal{Dog{}, Cat{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
다음 글: Go 패키지(package) – 업무에 사용하는 Go 언어 14 →