사용자 정의 유형이
String() string
메서드를 구현하면 fmt 함수(예: fmt.Println, fmt.Printf("%v"), 로그 패키지)에서 해당 값을 사용할 때 이 메서드가 호출되어 문자열 표현을 얻습니다.
사용자 정의 엔터티 유형에 대해서는 String()을 구현하는 것이 좋으며, 이는 fmt의 표준 출력 (%v) 대신 의미 있는 사람이 읽을 수 있는 출력을 얻기 위해서입니다. 출력을 간결하고 사용 맥락에 적절하게 유지하고, 가능하다면 민감한 데이터 유출 없이 안전하게 만드는 것이 좋습니다.
구현 예시:
type Order struct { ID string Amount int } func (o Order) String() string { return fmt.Sprintf("Order[%s]: %d", o.ID, o.Amount) } func main() { o := Order{"A123", 99} fmt.Println(o) } // 출력: Order[A123]: 99
메서드가 잘못 구현되거나 부적절하게 구현된 경우, 이는 무의미하거나 위험한 로그 출력을 야기하고, 버그를 찾는 데 어려움을 겪게 만듭니다.
만약 귀하의 유형이 String() 메서드를 구현했지만 값을 사용하지 않고 포인터로 사용한다고 가정할 때, String()이 호출될까요?
답변: 만약 포인터 유형에서 String()이 구현되어 있다면 메서드가 호출됩니다. 값에 대해서만 구현되어 있지만 코드에서 해당 유형이 포인터일 경우, Go의 자동 역참조가 호출되어 메서드가 호출됩니다. 일반적으로 이는 문제가 되지 않지만, 메서드가 포인터 리시버에서만 구현되어 있고 이를 값에 대해 호출하면 컴파일 오류가 발생합니다.
예시:
type X struct{} func (x *X) String() string { return "ptr" } fmt.Println(X{}) // 오류: X는 String을 구현하지 않음 fmt.Println(&X{}) // 정상, 메서드가 호출될 것
이야기
큰 프로젝트에서 오류 유형이 String()을 포인터 리시버만에 대해 구현했습니다 (
func (err *MyErr) String() string). 이로 인해 오류 값을 반환할 때(포인터가 아닌) 로그에 유용하지 않은{}가 출력되었습니다. 버그를 오랫동안 발견하지 못했습니다.
이야기
하나의 유형이 민감한 데이터를 저장하고 있었으며, String()의 잘못된 구현으로 인해(필드를 노출시킴) 사용자 비밀번호가 프로덕션 로그에 나타나는 문제가 있었습니다. 보안 감사가 필요했습니다.
이야기
자동으로 생성된 String() 사용으로 인해 구조체가 업데이트될 때 새로운 출력 형식이 이전 것과 일치하지 않아 로그가 읽기 어려워지고, 타 시스템에 의한 로그 파싱이 손상되었습니다.