[Go] 인터페이스(2), 에러처리

김무연·2025년 2월 7일

GoLang의 문법 뿌시기

목록 보기
10/14

에러처리

함수가 결과와 에러를 함께 리턴한다면, 이 에러가 nil 인지를 판단해 에러의 존재를 체크할 수 있을것이다.

package errorhandle

import (
	"fmt"
	"log"
	"os"
)

func Errorhandle() {
	f, err := os.Open("/test.txt")

	if err != nil {
		log.Fatal(err.Error())
	}

	fmt.Println(f.Name())
}

log.Fatal() 메시지를 출력하고 프로그램을 종료시키는 함수이다. 내부 코드를 보면

func Fatalf(format string, v ...any) {
	std.Output(2, fmt.Sprintf(format, v...))
	os.Exit(1)
}

위와 같이 되어 있는데 os.Exit(1)을 통해 프로그램 종료를 시킨다.

error 인터페이스 with Custom error

fmt.Errorf() 함수를 이용하면 간단하게 err를 만들어 낼 수 있다. 여기서 err의 타입은 어떻게 될까?

err := fmt.Errorf("%s, %w", "main!!", foo())

코드를 타고 선언된 부분으로 들어가보면

func Errorf(format string, a ..any) error {}

... error을 한 번 더 진입해보면
type error interface {
	Error() string
}

위와 같이 타입이 선언되었음을 알 수 있다. 즉 error는 인터페이스이고, 메서드로 Error()만을 가지고 있다. 이를 이용해 custom error 타입을 만들 수 있다.

package interfaceerror

import "fmt"

type overHeat float64

func (o overHeat) Error() string {
	return fmt.Sprintf("over heat is : %0.2f", float64(o))
}

func checkTemp(act float64, crit float64) error {
	axcess := act - crit

	if axcess > 0 {
		temp := overHeat(axcess)
		return temp
	}
	return nil
}

func Interfaceerror() {
	err := checkTemp(38.5, 37.5)

	if err != nil {
		fmt.Println(err)
	}
}

위 코드에서 의문점이 하나 들었다. 분명 Error()메서드를 호출하지 않았는데도, Error() 메서드가 실행됐다. checkTemp 함수를 보면 error 인터페이스를 반환하도록 되어있다. error 인터페이스에는 Error() 메서드가 선언되어 있는데, error 인터페이스를 사용하기 위해 Error 메서드를 구현해주었다. 이 메서드는 overHeat라는 타입에 종속되어 있다. 따라서 checkTemp 함수의 return 값도 overHeat의 타입으로 conversion을 시켜주어야 한다.

그런 이후 return 된 error 인터페이스와 fmt 패키지가 만나게 되면 fmt 패키지 내부의 자동적으로 설계된 Error() 메서드를 실행시키게 된다. 이는 fmt 패키지 자체의 기능이다.

이렇게 error 인터페이스의 구현체를 직접 구현해 사용하면 에러가 어디에서 발생했는지 더 쉽게 처리가 가능할 것이다.

Stringer 인터페이스

자바에서는 toString() 메서드가 모두 상속되어있기 때문에 객체를 print 해도 문제없이 print가 된다.

그러나 Go는 사용자 정의 타입 모두 toString()을 상속하는 것은 아니다. 그렇기 때문에 이와 같은 기능을 하는 인터페이스를 구현하면 문제없이 작동될 것이다.

type Stringer interface {
	String() string
}

Stringer 인터페이스는 위와 같이 정의 되어 있다. String() string 메서드만 구현해주면 될 것이다.

package interfacestringer

import "fmt"

type coffee string

func (c coffee) String() string {
	return string(c) + "coffee pot"
}

func Interfacestringer() {
	coffeepot := coffee("cold brew")
	fmt.Println(coffeepot)
}

이 또한 String() 함수가 자동 실행되는데, 이는 fmt패키지에 정의된 Stringer 인터페이스 때문이다. Stringer 인터페이스는 String() 메서드를 구현하는 모든 타입에 대해 자동으로 작동되는데, fmt 패키지에서 인자의 타입이 Stringer 인터페이스를 구현하고 있으면, String() 메서드를 자동으로 호출해 문자열을 출력하게 된다.

따라서 String() 메서드만 직접 구현해 준다면 사용자 커스터 마이징 로그를 출력할 수 있을 것이다.

빈 인터페이스

fmt.Println() 내에 사용자 정의타입이 무엇이든 간에 모두 들어가는 것을 알 수 있다. 굳이 Stringer 인터페이스를 구현하지 않아도 기본 적인 타입들도 모두 들어갈 수 있는 것을 알 수 있다.

이는 모든 타입을 받는 인터페이스를 만들어서 인데 그것이 빈 인터페이스다 {}

func Println(a ...any) (n int, err error) {
	return Fprintln(os.Stdout, a...)
}

여기서 인자의 타입이 any라고 되어 있는데 이 any의 타입을 보면

type any = interface{}

위와 같다. 즉 빈 인터페이스를 선언한 것이다. 이러할 경우 어떤 타입이든 다 받을 수 있게 된다. 하지만 그냥 빈 인터페이스로 선언하면 헷갈릴 수 있으므로 any 라는 인터페이스를 만들어서 사용한다.

profile
Notion에 정리된 공부한 글을 옮겨오는 중입니다... (진행중)

0개의 댓글