Go에서 fmt.Stringer 및 error 인터페이스는 구조체 값이 문자열로 변환되는 방식과 오류를 구현하는 방식을 관리하는 데 사용됩니다. 이러한 인터페이스는 로깅, 출력 및 오류 처리의 보편적인 방법을 제공하여 코드를 더 유연하고 이해하기 쉽게 만듭니다.
문제의 역사:
Go의 초기 버전부터 Stringer 인터페이스는 구조체 출력을 아름답고 제어된 방식으로 하는 데 중요한 역할을 했습니다. Error 인터페이스는 코드의 모든 레벨에서 오류를 처리하는 데 근본적인 역할을 했습니다.
문제:
프로그래머는 이러한 메소드를 구현하지 않거나 비표준적으로 구현하여 정보가 부족한 출력 또는 예기치 않은 오류 메시지를 받는 경우가 많습니다. 또한 잘못된 구현은 재귀적 출력, 패닉 및 읽기 어려운 오류를 초래할 수 있습니다.
해결책:
코드 예:
package main import "fmt" type User struct { Name string ID int } func (u User) String() string { return fmt.Sprintf("User<%d:%s>", u.ID, u.Name) } type MyError struct { Msg string } func (e MyError) Error() string { return "MyError: " + e.Msg } func main() { u := User{Name: "Bob", ID: 10} fmt.Println(u) // String() 호출 err := MyError{Msg:"fail"} fmt.Println(err) // Error() 호출 }
주요 특징:
String() 또는 Error()를 값 수신자로 구현해야 합니까, 아니면 포인터를 사용할 수 있습니까?
두 가지 방법 모두 허용되지만, 포인터 수신자와 값 수신자의 구현은 메소드가 작동하는 객체 유형에 영향을 미칩니다. 일반적으로 변경 가능한 구조체에 대해 포인터 수신자를 사용합니다.
func (u *User) String() string {...}
String() 또는 Error() 내부에서 fmt.Sprintf를 사용할 수 있습니까?
예, 하지만 무한 재귀를 피하기 위해 주의해야 합니다(예: String() 내부에 동일한 유형의 구조체 출력). 내부에서 다시 String()을 호출할 경우 fmt.Print의 사용을 피하는 것이 좋습니다.
func (u User) String() string { return fmt.Sprintf("%v", u.Name) } // 안전함
Error() 메소드가 빈 문자열을 반환하면 어떻게 됩니까?
빈 문자열을 가진 오류는 유효한 error 값으로 간주되지만 로그 기록의 의미가 사라집니다. error 인터페이스는 빈 문자열에 대한 행동을 정의하지 않지만, 일반적으로 항상 유용한 메시지를 제공하는 것이 좋습니다.
개발자가 %+v를 통해 구조체를 출력하지만 String()을 구현하지 않아 필드의 엉망진창 덤프가 로그에 기록됩니다.
장점:
단점:
팀 리더가 모든 공용 구조체에 대해 String() 및 Error()를 구현하도록 팀에 지시합니다. 결과적으로 비즈니스 로직은 오류를 중앙 집중식으로 처리하며 관리 및 디버깅 로그가 읽기 쉽게 됩니다.
장점:
단점: