Menu
Coddy logo textTech

계약으로서의 인터페이스

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 27번째.

인터페이스를 계약으로 생각하면 더 나은 Go 프로그램을 설계하는 데 도움이 됩니다. 함수가 인터페이스 타입을 인자로 받을 때, 그것은 다음과 같이 말하는 것과 같습니다: "이 계약을 충족하기만 한다면, 어떤 구체 타입(concrete type)을 전달하든 상관없습니다."

이러한 계약 기반의 사고방식은 특정 타입보다는 동작에 집중하는 함수를 작성할 수 있게 해줍니다. 다양한 소스로부터 데이터를 처리해야 하는 함수를 생각해 봅시다:

type DataSource interface {
    FetchData() string
}

func ProcessData(src DataSource) {
    data := src.FetchData()
    fmt.Println("Processing:", data)
}

ProcessData 함수는 데이터베이스 연결, API 클라이언트 또는 파일 리더를 받는지 여부를 알 필요도 없고 신경 쓰지도 않습니다. 입력이 FetchData()를 수행할 수 있기만 하면 됩니다. 이 계약을 준수하는 모든 타입은 작동합니다.

type Database struct{ ConnectionString string }
func (d Database) FetchData() string { return "data from DB" }

type APIClient struct{ Endpoint string }
func (a APIClient) FetchData() string { return "data from API" }

func main() {
    db := Database{ConnectionString: "localhost"}
    api := APIClient{Endpoint: "https://api.example.com"}
    
    ProcessData(db)   // 작동합니다!
    ProcessData(api)  // 역시 작동합니다!
}

이러한 계약 방식은 코드를 더욱 테스트 가능하게 만들어 줍니다. 테스트 중에 실제 데이터베이스나 네트워크 연결 없이도 인터페이스를 충족하는 모의 타입(mock type)을 생성할 수 있습니다. 계약이 충족되었으므로 테스트 대상 함수는 그 차이를 알지 못합니다.

challenge icon

챌린지

쉬움

인터페이스가 어떻게 계약(contract) 역할을 하는지 보여주는 알림 시스템을 구축해 보겠습니다. 알림을 보낸다는 것이 무엇을 의미하는지 정의하는 Notifier 인터페이스를 만들고, 이 계약을 준수하는 여러 알림 채널을 구현하게 됩니다.

코드는 두 개의 파일로 구성됩니다:

  • notifiers.go: 단일 메서드 Notify(message string) string을 가진 Notifier 인터페이스를 정의합니다. 그런 다음 이 계약을 충족하는 세 가지 구조체를 만듭니다:
    • Prefix 필드를 가진 ConsoleNotifier
    • Filename 필드를 가진 FileNotifier
    • URL 필드를 가진 WebhookNotifier
    각 구조체는 알림이 어떻게 전송되는지 설명하는 문자열을 반환하도록 Notify 메서드를 구현해야 합니다.
  • main.go: Notifier 값의 슬라이스와 메시지 문자열을 인자로 받는 BroadcastAlert 함수를 만듭니다. 이 함수는 각 노티파이어에서 Notify를 호출하고 결과를 출력해야 합니다. 입력에서 설정을 읽어 세 가지 노티파이어 타입을 모두 생성하고, 모든 노티파이어를 통해 알림을 브로드캐스트합니다.

다음 입력이 제공됩니다:

  • 1행: 콘솔 접두사(Prefix)
  • 2행: 파일 이름(Filename)
  • 3행: 웹훅 URL
  • 4행: 알림 메시지

Notify 메서드는 다음과 같은 형식의 문자열을 반환해야 합니다:

  • ConsoleNotifier: [Prefix] [message]
  • FileNotifier: Writing to [Filename]: [message]
  • WebhookNotifier: POST [URL]: [message]

예를 들어, ALERT:, log.txt, https://hooks.example.com, Server down!이 주어지면 출력은 다음과 같아야 합니다:

ALERT: Server down!
Writing to log.txt: Server down!
POST https://hooks.example.com: Server down!

이 설계의 장점은 BroadcastAlert 함수가 구체적인 노티파이어 타입을 알 필요도 없고 신경 쓸 필요도 없다는 것입니다. 단지 각 타입이 Notifier 계약을 충족하기만 하면 됩니다. 나중에 브로드캐스트 함수를 전혀 수정하지 않고도 SlackNotifierEmailNotifier를 추가할 수 있습니다.

치트 시트

Go의 인터페이스는 구체적인 타입을 지정하지 않고 필요한 동작을 정의하는 계약(contracts) 역할을 합니다. 함수가 인터페이스 타입을 매개변수로 받으면, 해당 인터페이스의 메서드를 구현하는 모든 타입을 사용할 수 있습니다.

메서드 시그니처를 지정하여 인터페이스를 정의합니다:

type DataSource interface {
    FetchData() string
}

인터페이스를 허용하는 함수는 계약을 충족하는 모든 타입과 함께 작동합니다:

func ProcessData(src DataSource) {
    data := src.FetchData()
    fmt.Println("Processing:", data)
}

여러 타입이 동일한 인터페이스를 구현할 수 있습니다:

type Database struct{ ConnectionString string }
func (d Database) FetchData() string { return "data from DB" }

type APIClient struct{ Endpoint string }
func (a APIClient) FetchData() string { return "data from API" }

func main() {
    db := Database{ConnectionString: "localhost"}
    api := APIClient{Endpoint: "https://api.example.com"}
    
    ProcessData(db)   // 작동합니다!
    ProcessData(api)  // 역시 작동합니다!
}

이러한 계약 기반 접근 방식은 다음을 가능하게 합니다:

  • 특정 타입보다는 동작에 집중하는 함수
  • 모의(mock) 구현을 통한 더 나은 테스트 가능성
  • 기존 코드를 수정하지 않고도 확장 가능

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
)

// BroadcastAlert는 모든 알림 도구(notifiers)를 통해 메시지를 전송합니다
// TODO: 다음 기능을 수행하도록 이 함수를 구현하세요:
// - Notifier 값의 슬라이스와 메시지 문자열을 인자로 받습니다
// - 각 알림 도구에서 Notify를 호출하고 결과를 출력합니다
func BroadcastAlert(notifiers []Notifier, message string) {
	// TODO: 알림 도구들을 순회하며 각 알림 결과를 출력하세요
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 콘솔 접두사(prefix)를 읽습니다
	scanner.Scan()
	prefix := scanner.Text()
	
	// 파일 이름을 읽습니다
	scanner.Scan()
	filename := scanner.Text()
	
	// 웹훅 URL을 읽습니다
	scanner.Scan()
	url := scanner.Text()
	
	// 알림 메시지를 읽습니다
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: 세 가지 알림 도구 타입의 인스턴스를 모두 생성하세요
	// - prefix를 사용한 ConsoleNotifier
	// - filename을 사용한 FileNotifier
	// - URL을 사용한 WebhookNotifier
	
	// TODO: 세 가지 알림 도구를 모두 포함하는 Notifier 슬라이스를 생성하세요
	
	// TODO: 알림 도구들과 메시지를 사용하여 BroadcastAlert를 호출하세요
	
	_ = prefix
	_ = filename
	_ = url
	_ = message
	fmt.Println("TODO: Implement the solution")
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨