[2026] [Go 2주 완성 #04] Day 7: 다형성의 재해석, 인터페이스 - 가상 함수 없이 다형성 구현하기

[2026] [Go 2주 완성 #04] Day 7: 다형성의 재해석, 인터페이스 - 가상 함수 없이 다형성 구현하기

이 글의 핵심

C++ 가상 함수와 상속 대신 Go 인터페이스로 다형성을 구현하는 방법. implements 키워드 없는 암시적 인터페이스(Duck Typing)와 io.Reader, io.Writer 같은 소형 인터페이스 설계 패턴을 배웁니다.

시리즈 안내

📚 Go 2주 완성 시리즈 #04 | 전체 목차 보기

이 글은 C++ 개발자를 위한 2주 완성 Go 언어 커리큘럼Day 7 내용입니다.

이전: #03 객체지향 ← | → 다음: #05 에러 처리


들어가며: 명시적 상속에서 암시적 만족으로

C++에서 다형성을 구현하려면 기반 클래스를 상속하고 virtual 키워드로 가상 함수를 선언해야 했습니다. Go는 명시적 상속 선언이 없습니다. 메서드만 구현하면 자동으로 인터페이스를 만족합니다. “오리처럼 걷고 오리처럼 운다면 오리다”는 Duck Typing 철학입니다. 이 글에서 배울 내용:

  • 인터페이스의 정의와 암시적 만족
  • 표준 라이브러리의 핵심 인터페이스
  • 빈 인터페이스와 타입 단언·타입 스위치
  • io.Reader·io.Writer·error를 엮는 실전 패턴
  • 인터페이스 설계 패턴

C++ 개발자 관점: C++ 백그라운드에서 Go로 전환하며 겪은 차이점과 함정을 중심으로 설명합니다. 포인터, 동시성, 메모리 관리 등 핵심 개념을 비교하며 정리했습니다.

실무에서의 체감

C++ 위주로 서버를 다루던 환경에서 Go를 도입할 때 흔히 드는 인상은 문법과 툴체인이 단순해 보인다는 점입니다. 프로덕션에서는 그 단순함이 빌드·배포·동시성 코드 가독성으로 이어지는 경우가 많습니다. 자주 언급되는 장점:

  • 개발 속도: 팀·도메인에 따라 다르지만, 네트워크·CLI 코드를 빠르게 완성하기 쉬운 편입니다.
  • 안정성: GC가 있어 수동 할당 해제 부담이 줄어듭니다.
  • 배포: 단일 바이너리로 옮기기 쉬운 구조입니다.

목차

  1. 인터페이스 기본: 메서드 집합
  2. 암시적 인터페이스 만족 (Duck Typing)
  3. 표준 라이브러리 인터페이스
  4. 빈 인터페이스와 타입 단언
  5. 실전 패턴: 암묵적 구현·any·단언·표준 입출력·에러
  6. 인터페이스 설계 원칙
  7. 실습 과제

1. 인터페이스 기본: 메서드 집합

C++ vs Go: 다형성 구현

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// C++: 가상 함수로 다형성
class Shape {
public:
    virtual double Area() const = 0;
    virtual double Perimeter() const = 0;
    virtual ~Shape() = default;
};
class Circle : public Shape {
    double radius;
    
public:
    Circle(double r) : radius(r) {}
    
    double Area() const override {
        return 3.14159 * radius * radius;
    }
    
    double Perimeter() const override {
        return 2 * 3.14159 * radius;
    }
};
class Rectangle : public Shape {
    double width, height;
    
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    
    double Area() const override {
        return width * height;
    }
    
    double Perimeter() const override {
        return 2 * (width + height);
    }
};
// 다형성 사용
void printShapeInfo(const Shape& s) {
    std::cout << "Area: " << s.Area() << "\n";
    std::cout << "Perimeter: " << s.Perimeter() << "\n";
}
int main() {
    Circle c(5.0);
    Rectangle r(4.0, 6.0);
    
    printShapeInfo(c);
    printShapeInfo(r);
}

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 인터페이스로 다형성 (명시적 상속 불필요)
// 패키지 선언
package main
import (
    "fmt"
    "math"
)
// 인터페이스 정의
type Shape interface {
    Area() float64
    Perimeter() float64
}
// Circle 타입
type Circle struct {
    Radius float64
}
// Circle은 Area()와 Perimeter()를 구현하므로 자동으로 Shape 만족
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}
// Rectangle 타입
type Rectangle struct {
    Width, Height float64
}
// Rectangle도 Area()와 Perimeter()를 구현하므로 Shape 만족
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}
// 다형성 사용
func printShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f\n", s.Area())
    fmt.Printf("Perimeter: %.2f\n", s.Perimeter())
}
func main() {
    c := Circle{Radius: 5.0}
    r := Rectangle{Width: 4.0, Height: 6.0}
    
    printShapeInfo(c)
    printShapeInfo(r)
}

핵심 차이점:

  • Go는 implements 키워드가 없습니다
  • 메서드만 구현하면 자동으로 인터페이스를 만족합니다
  • 타입과 인터페이스 간의 결합도가 낮습니다

2. 암시적 인터페이스 만족 (Duck Typing)

Duck Typing의 장점

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 나중에 인터페이스 추가 가능
package main
import "fmt"
// 기존 타입 (인터페이스 모름)
type Dog struct {
    Name string
}
func (d Dog) Speak() string {
    return "Woof!"
}
type Cat struct {
    Name string
}
func (c Cat) Speak() string {
    return "Meow!"
}
// 나중에 인터페이스 정의 (기존 타입 수정 불필요)
type Speaker interface {
    Speak() string
}
func makeSpeak(s Speaker) {
    fmt.Println(s.Speak())
}
func main() {
    dog := Dog{Name: "Buddy"}
    cat := Cat{Name: "Whiskers"}
    
    // Dog와 Cat은 Speaker를 만족 (명시적 선언 없이)
    makeSpeak(dog)
    makeSpeak(cat)
}

C++과의 비교:

  • C++: 기반 클래스를 미리 정의하고 상속해야 함
  • Go: 나중에 인터페이스를 추가해도 기존 타입 수정 불필요

3. 표준 라이브러리 인터페이스

Go 표준 라이브러리는 작고 강력한 인터페이스로 가득합니다.

io.Reader와 io.Writer

아래 코드는 go를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.

// Go: io.Reader 인터페이스
type Reader interface {
    Read(p []byte) (n int, err error)
}
// io.Writer 인터페이스
type Writer interface {
    Write(p []byte) (n int, err error)
}

활용 예시: 다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: io.Reader 활용
package main
import (
    "bytes"
    "fmt"
    "io"
    "os"
    "strings"
)
func processData(r io.Reader) error {
    data, err := io.ReadAll(r)
    if err != nil {
        return err
    }
    fmt.Println(string(data))
    return nil
}
func main() {
    // 1. 파일에서 읽기
    f, _ := os.Open("file.txt")
    defer f.Close()
    processData(f)  // *os.File은 io.Reader
    
    // 2. 문자열에서 읽기
    sr := strings.NewReader("Hello from string")
    processData(sr)  // *strings.Reader는 io.Reader
    
    // 3. 바이트 버퍼에서 읽기
    buf := bytes.NewBufferString("Hello from buffer")
    processData(buf)  // *bytes.Buffer는 io.Reader
}

fmt.Stringer 인터페이스

다음은 cpp를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// C++: operator<< 오버로딩
class Person {
    std::string name;
    int age;
    
public:
    Person(const std::string& n, int a) : name(n), age(a) {}
    
    friend std::ostream& operator<<(std::ostream& os, const Person& p) {
        os << p.name << " (" << p.age << ")";
        return os;
    }
};
// 사용
Person p("Alice", 30);
std::cout << p << "\n";

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: fmt.Stringer 인터페이스
package main
import "fmt"
type Person struct {
    Name string
    Age  int
}
// fmt.Stringer 인터페이스 만족
func (p Person) String() string {
    return fmt.Sprintf("%s (%d)", p.Name, p.Age)
}
func main() {
    p := Person{Name: "Alice", Age: 30}
    fmt.Println(p)  // "Alice (30)" - String() 자동 호출
}

error 인터페이스

다음은 go를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: error 인터페이스
type error interface {
    Error() string
}
// 커스텀 에러 타입
type ValidationError struct {
    Field string
    Value string
}
func (e ValidationError) Error() string {
    return fmt.Sprintf("validation failed: %s = %s", e.Field, e.Value)
}
// 사용
func validate(email string) error {
    if !strings.Contains(email, "@") {
        return ValidationError{
            Field: "email",
            Value: email,
        }
    }
    return nil
}
func main() {
    if err := validate("invalid"); err != nil {
        fmt.Println(err)  // "validation failed: email = invalid"
    }
}

4. 빈 인터페이스와 타입 단언

interface{} (any)

// C++: void* (타입 안전하지 않음)
void* ptr = new int(42);
int* p = static_cast<int*>(ptr);  // 수동 캐스팅

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: interface{} 또는 any (타입 안전)
package main
import "fmt"
func printAny(v interface{}) {
    fmt.Printf("Value: %v, Type: %T\n", v, v)
}
func main() {
    printAny(42)
    printAny("hello")
    printAny(3.14)
    printAny([]int{1, 2, 3})
}

타입 단언 (Type Assertion)

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 타입 단언
package main
import "fmt"
func process(v interface{}) {
    // 안전한 타입 단언 (ok 패턴)
    if s, ok := v.(string); ok {
        fmt.Println("String:", s)
        return
    }
    
    if i, ok := v.(int); ok {
        fmt.Println("Int:", i)
        return
    }
    
    fmt.Println("Unknown type")
}
func main() {
    process("hello")  // "String: hello"
    process(42)       // "Int: 42"
    process(3.14)     // "Unknown type"
}

타입 스위치 (Type Switch)

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 타입 스위치 (여러 타입 처리)
package main
import "fmt"
func describe(v interface{}) {
    switch t := v.(type) {
    case string:
        fmt.Printf("String: %s (length %d)\n", t, len(t))
    case int:
        fmt.Printf("Int: %d\n", t)
    case bool:
        fmt.Printf("Bool: %t\n", t)
    case []int:
        fmt.Printf("Int slice: %v\n", t)
    default:
        fmt.Printf("Unknown type: %T\n", t)
    }
}
func main() {
    describe("hello")
    describe(42)
    describe(true)
    describe([]int{1, 2, 3})
    describe(3.14)
}

5. 실전 패턴: 암묵적 구현·any·단언·표준 입출력·에러

암묵적 구현(implicit implementation)을 실무에서 쓰는 법

  • 인터페이스는 보통 “소비하는 쪽”에서 정의합니다. “이 함수는 io.Reader만 받는다”처럼 필요한 메서드만 밝히면, 그 메서드를 가진 모든 타입이 넘어올 수 있습니다.
  • 구현체 타입은 인터페이스 이름을 몰라도 됩니다. 덕분에 기존 타입을 수정하지 않고 테스트용 목(mock)이나 어댑터만 추가하는 식으로 확장하기 좋습니다.
  • 관용구로 “인터페이스는 받고, 구조체는 반환한다”(accept interfaces, return structs)가 자주 인용됩니다. 공개 API는 좁은 인터페이스로 받고, 생성자는 구체 타입 포인터를 돌려줍니다. 다음은 간단한 go 코드 예제입니다. 함수를 통해 로직을 구현합니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// HTTP 핸들러 예: ResponseWriter와 Request는 인터페이스/구조체 조합으로 설계됨
func Handler(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "ok") // w는 io.Writer를 만족
}

interface{}any

  • Go 1.18 이전에는 빈 인터페이스interface{}로 썼고, 이후 anyinterface{}의 별칭입니다. 스타일 가이드는 보통 any 선호를 권합니다.
  • 모든 타입any에 대입 가능하지만, 그만큼 컴파일 타임 검사가 사라지므로 이후에 타입 단언·타입 스위치로 좁혀야 합니다.
  • JSON 디코딩처럼 런타임에만 타입이 정해지는 데이터에서 특히 자주 씁니다(과제 4와 연결).

타입 단언과 타입 스위치 — 실전 체크리스트

상황권장
한두 가지 타입만 올 수 있음v.(ConcreteType) 또는 s, ok := v.(string)
여러 타입을 한 함수에서 분기switch v := x.(type) { ....}
nil 인터페이스 값동적 타입이 없어 단언이 실패하거나 주의 필요 — “nil 인터페이스” 트랩은 #05에서도 다룸
다음은 간단한 go 코드 예제입니다. 코드를 직접 실행해보면서 동작을 확인해보세요.
// 단언 실패 시 패닉을 피하려면 반드시 ok 형태 사용
if n, ok := v.(int); ok {
    _ = n
}

io.Reader, io.Writer, error로 이어 붙이기

표준 라이브러리는 아주 작은 인터페이스를 조합해 큰 동작을 만듭니다.

  • io.Copy(dst Writer, src Reader): 읽기·쓰기 스트림을 연결. 파일·버퍼·네트워크·bytes.Reader 모두 동일하게 처리됩니다.
  • io.MultiWriter: 한 번의 Write를 여러 Writer(파일 + 해시 등)로 동시에 보냅니다.
  • error: 메서드 Error() string 하나뿐인 인터페이스라 어떤 커스텀 타입이든 에러로 쓸 수 있습니다. 도메인별 필드를 실어 보내고, 호출부에서는 errors.As로 복원합니다(#05). 다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.
package main
import (
    "bytes"
    "fmt"
    "io"
    "strings"
)
func main() {
    r := strings.NewReader("hello")
    var buf bytes.Buffer
    if _, err := io.Copy(&buf, r); err != nil {
        fmt.Println(err)
        return
    }
    // buf는 io.Writer를 구현 — 동일 패턴으로 파일·해시 등에도 연결 가능
    fmt.Println(buf.String())
}

한 줄 요약: 입출력은 io.Reader/Writer로 추상화하고, 실패는 error로 명시하며, 타입이 섞일 때만 any와 단언으로 좁힌다.

6. 인터페이스 설계 원칙

작은 인터페이스 (Small Interfaces)

Go의 철학: “인터페이스는 작을수록 좋다” 다음은 go를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ✅ 좋은 예: 작은 인터페이스
type Reader interface {
    Read(p []byte) (n int, err error)
}
type Writer interface {
    Write(p []byte) (n int, err error)
}
type Closer interface {
    Close() error
}
// 필요하면 조합
type ReadWriter interface {
    Reader
    Writer
}
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

아래 코드는 go를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 나쁜 예: 큰 인터페이스
type Database interface {
    Connect() error
    Disconnect() error
    Query(sql string) ([]Row, error)
    Insert(table string, data map[string]interface{}) error
    Update(table string, id int, data map[string]interface{}) error
    Delete(table string, id int) error
    BeginTransaction() error
    CommitTransaction() error
    RollbackTransaction() error
}
// 너무 많은 메서드 - 구현하기 어렵고 테스트하기 힘듦

인터페이스 분리 원칙

다음은 go를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ✅ 좋은 예: 인터페이스 분리
type Querier interface {
    Query(sql string) ([]Row, error)
}
type Inserter interface {
    Insert(table string, data map[string]interface{}) error
}
type Transactional interface {
    BeginTransaction() error
    CommitTransaction() error
    RollbackTransaction() error
}
// 필요한 기능만 요구
func fetchData(q Querier) ([]Row, error) {
    return q.Query("SELECT * FROM users")
}

7. 실습 과제

과제 1: 도형 인터페이스

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 다양한 도형 구현
package main
import (
    "fmt"
    "math"
)
type Shape interface {
    Area() float64
    Perimeter() float64
}
type Circle struct {
    Radius float64
}
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}
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 Triangle struct {
    A, B, C float64  // 세 변의 길이
}
func (t Triangle) Area() float64 {
    // 헤론의 공식
    s := (t.A + t.B + t.C) / 2
    return math.Sqrt(s * (s - t.A) * (s - t.B) * (s - t.C))
}
func (t Triangle) Perimeter() float64 {
    return t.A + t.B + t.C
}
func printShapeInfo(s Shape) {
    fmt.Printf("Type: %T\n", s)
    fmt.Printf("Area: %.2f\n", s.Area())
    fmt.Printf("Perimeter: %.2f\n", s.Perimeter())
    fmt.Println()
}
func main() {
    shapes := []Shape{
        Circle{Radius: 5},
        Rectangle{Width: 4, Height: 6},
        Triangle{A: 3, B: 4, C: 5},
    }
    
    for _, shape := range shapes {
        printShapeInfo(shape)
    }
}

과제 2: 커스텀 Writer

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 커스텀 Writer 구현
package main
import (
    "bytes"
    "fmt"
    "io"
    "os"
)
// 대문자로 변환하는 Writer
type UpperWriter struct {
    w io.Writer
}
func NewUpperWriter(w io.Writer) *UpperWriter {
    return &UpperWriter{w: w}
}
// io.Writer 인터페이스 만족
func (uw *UpperWriter) Write(p []byte) (n int, err error) {
    upper := bytes.ToUpper(p)
    return uw.w.Write(upper)
}
func main() {
    // 표준 출력을 대문자로
    uw := NewUpperWriter(os.Stdout)
    
    fmt.Fprintln(uw, "hello world")  // "HELLO WORLD"
    
    // io.Copy 같은 함수와 조합 가능
    io.WriteString(uw, "go is awesome\n")  // "GO IS AWESOME"
}

과제 3: 인터페이스 조합

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 여러 인터페이스 조합
package main
import (
    "fmt"
    "io"
    "os"
)
// 여러 인터페이스를 만족하는 타입
type File struct {
    *os.File
}
// File은 io.Reader, io.Writer, io.Closer를 모두 만족
// (os.File이 이미 구현하고 있으므로 임베딩으로 자동 만족)
func processReadWriteCloser(rwc io.ReadWriteCloser) {
    // Read, Write, Close 모두 사용 가능
    defer rwc.Close()
    
    data := []byte("test data")
    rwc.Write(data)
}
func main() {
    f, err := os.Create("temp.txt")
    if err != nil {
        panic(err)
    }
    
    // *os.File은 io.ReadWriteCloser 만족
    processReadWriteCloser(f)
}

과제 4: 타입 단언 실습

다음은 go를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 함수를 통해 로직을 구현합니다, 에러 처리를 통해 안정성을 확보합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// Go: 타입 단언으로 JSON 파싱
package main
import (
    "encoding/json"
    "fmt"
)
func parseJSON(jsonStr string) {
    var data interface{}
    
    if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
        fmt.Println("Parse error:", err)
        return
    }
    
    // 타입 스위치로 처리
    switch v := data.(type) {
    case map[string]interface{}:
        fmt.Println("Object:")
        for key, value := range v {
            fmt.Printf("  %s: %v\n", key, value)
        }
    case []interface{}:
        fmt.Println("Array:")
        for i, item := range v {
            fmt.Printf("  [%d]: %v\n", i, item)
        }
    default:
        fmt.Printf("Other type: %T\n", v)
    }
}
func main() {
    parseJSON(`{"name":"Alice","age":30}`)
    parseJSON(`[1, 2, 3, 4, 5]`)
}

정리: Day 7 학습 체크리스트

완료해야 할 항목

  • 인터페이스는 메서드 집합임을 이해
  • 명시적 implements 없이 자동 만족 (Duck Typing)
  • io.Reader, io.Writer, fmt.Stringer 등 표준 인터페이스 활용
  • 빈 인터페이스(interface{}, any)와 타입 단언
  • 타입 스위치로 여러 타입 처리
  • 작은 인터페이스 설계 원칙
  • 실습 과제 4개 완료

C++에서 Go로 전환 포인트

C++Go비고
virtual 함수인터페이스 메서드명시적 vs 암시적
명시적 상속암시적 만족Duck Typing
기반 클래스 포인터인터페이스 변수더 유연
RTTI (dynamic_cast)타입 단언더 간결
다중 상속인터페이스 조합더 안전

1주 차 완료!

축하합니다! 1주 차를 완료했습니다. 지금까지 배운 내용:

  • ✅ Go 기본 문법과 철학
  • ✅ 포인터와 자료구조 (Slice, Map)
  • ✅ 구조체와 메서드
  • ✅ 인터페이스와 다형성 2주 차에서는 Go의 진짜 강점인 동시성 프로그래밍을 배웁니다!

📚 시리즈 네비게이션

이전 글목차다음 글
← #03 객체지향📑 전체 목차#05 에러 처리 →
Go 2주 완성 시리즈:
커리큘럼#01 기본 문법#02 자료구조#03 객체지향#04 인터페이스#05 에러 처리#06 고루틴·채널#07 테스팅#08 REST API#09 context·우아한 종료

한 줄 요약: Go 인터페이스는 명시적 상속 없이 메서드만 구현하면 자동으로 만족됩니다. 작은 인터페이스로 높은 재사용성을 얻으세요.

같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

Go interface, Duck Typing, io.Reader, 타입 단언 any, Go 다형성, C++ 가상함수 비교, Golang 인터페이스, Go 2주 완성 등으로 검색하시면 이 글이 도움이 됩니다.

실전 팁

실무에서 바로 적용할 수 있는 팁입니다.

디버깅 팁

  • 문제가 발생하면 먼저 컴파일러 경고를 확인하세요
  • 간단한 테스트 케이스로 문제를 재현하세요

성능 팁

  • 프로파일링 없이 최적화하지 마세요
  • 측정 가능한 지표를 먼저 설정하세요

코드 리뷰 팁

  • 코드 리뷰에서 자주 지적받는 부분을 미리 체크하세요
  • 팀의 코딩 컨벤션을 따르세요

실전 체크리스트

실무에서 이 개념을 적용할 때 확인해야 할 사항입니다.

코드 작성 전

  • 이 기법이 현재 문제를 해결하는 최선의 방법인가?
  • 팀원들이 이 코드를 이해하고 유지보수할 수 있는가?
  • 성능 요구사항을 만족하는가?

코드 작성 중

  • 컴파일러 경고를 모두 해결했는가?
  • 엣지 케이스를 고려했는가?
  • 에러 처리가 적절한가?

코드 리뷰 시

  • 코드의 의도가 명확한가?
  • 테스트 케이스가 충분한가?
  • 문서화가 되어 있는가? 이 체크리스트를 활용하여 실수를 줄이고 코드 품질을 높이세요.

자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. C++ 가상 함수와 상속 대신 Go 인터페이스로 다형성을 구현하는 방법. implements 키워드 없는 암시적 인터페이스(Duck Typing)와 io.Reader, io.Writer 같은 소형 인터페이스 설계 패… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.

관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3