서론
Go 언어에서 이 소문자로 시작하는 필드는 private 필드로 외부 패키지에서 접근할 수 없다. 그렇다고 private 필드에 접근이 완전히 불가능한 것은 아니다. 하지만 private 필드는 외부에 공개되지 않기 때문에 패키지가 업데이트될 때 내부 구현이 변경될 수 있으며 이로 인해 해당 필드에 의존하는 코드가 예상치 못한 동작을 하거나 오류가 발생할 위험이 있다. 따라서 테스트 목적으로만 사용하거나 해당 필드가 제거되거나 변경될 경우를 대비해 오류 처리를 할 수 있는 상황에서만 사용해야 한다. 이 글에서는 reflect 패키지를 사용하여 외부 패키지의 private 필드에 접근하는 방법을 간략하게 정리해본다.
private 모든 값 가져오기
package main
import (
"fmt"
"time"
)
func main() {
loc, err := time.LoadLocation("Asia/Seoul")
if err == nil {
internal := fmt.Sprintf("%+#v\n", loc)
fmt.Println(internal)
}
}
출력 결과
&time.Location{name:"Asia/Seoul", zone:[]time.zone{time.zone{name:"LMT", offset:30472, isDST:false}, time.zone{name:"KST", offset:30600, isDST:false}, time.zone{name:"JST", offset:32400, isDST:false}, time.zone{name:"KDT", offset:36000, isDST:true}, time.zone{name:"KST", offset:32400, isDST:false}, time.zone{name:"KDT", offset:34200, isDST:true}, time.zone{name:"KDT", offset:36000, isDST:true}}, tx:[]time.zoneTrans{time.zoneTrans{when:-1948782472, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-1830414600, index:0x2, isstd:false, isutc:false}, time.zoneTrans{when:-767350800, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:-681210000, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:-672228000, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:-654771600, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:-640864800, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:-623408400, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:-609415200, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:-588848400, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:-577965600, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:-498128400, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-462702600, index:0x5, isstd:false, isutc:false}, time.zoneTrans{when:-451733400, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-429784200, index:0x5, isstd:false, isutc:false}, time.zoneTrans{when:-418296600, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-399544200, index:0x5, isstd:false, isutc:false}, time.zoneTrans{when:-387451800, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-368094600, index:0x5, isstd:false, isutc:false}, time.zoneTrans{when:-356002200, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-336645000, index:0x5, isstd:false, isutc:false}, time.zoneTrans{when:-324552600, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-305195400, index:0x5, isstd:false, isutc:false}, time.zoneTrans{when:-293103000, index:0x1, isstd:false, isutc:false}, time.zoneTrans{when:-264933000, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:547578000, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:560883600, index:0x4, isstd:false, isutc:false}, time.zoneTrans{when:579027600, index:0x3, isstd:false, isutc:false}, time.zoneTrans{when:592333200, index:0x4, isstd:false, isutc:false}}, extend:"KST-9", cacheStart:592333200, cacheEnd:9223372036854775807, cacheZone:(*time.zone)(0xc0000c2080)}
time 패키지의 Location 구조체는 접근 가능한 필드 없이 String 메소드만 접근할 수 있다. 하지만 형식 지정자를 사용하면 구조체 내 많은 필드의 값을 얻어올 수 있다. 이 문자열을 파싱해서 원하는 필드의 값을 얻을 수도 있을 것이다. 하지만 reflect 패키지를 사용하면 원하는 필드의 값만 가져올 수 있어 파싱을 구현할 필요가 없다.
reflect 패키지를 사용해서 private 필드 값 가져오기
package main
import (
"fmt"
"reflect"
"time"
)
func main() {
loc, err := time.LoadLocation("Asia/Seoul")
if err == nil {
v := reflect.ValueOf(*loc)
extend := v.FieldByName("extend")
fmt.Printf("extend: %s (%t)\n", extend.String(), extend.CanSet())
}
}
출력 결과
extend: KST-9 (false)
reflect 패키지를 사용해서 private 필드의 이름으로 값을 얻을 수 있다. Set 또는 SetString과 같은 값 변경 메소드도 있지만 CanSet 메소드의 반환 값이 false인 경우 호출하면 panic이 발생한다. 또한 패키지 개발자의 의도와 다르게 값을 변경하면 의도치 않은 동작으로 이어질 수 있다. 값 변경이 필요하다고 판단될 경우는 이슈나 PR을 통해서 해당 필드의 공개 여부를 요청하는 것이 바람직하다.
'Language > Go' 카테고리의 다른 글
[golang] sync.WaitGroup으로 고루틴 작업 기다리기 (0) | 2024.11.09 |
---|---|
[golang] html/template 패키지로 동적 페이지 처리하기 (3) | 2024.11.07 |
[golang] json.Marshal에서 HTML 이스케이프 문제 (3) | 2024.10.31 |
[golang] cgo에서 Go와 C 언어 간의 버퍼 전달 (2) | 2024.10.29 |
[golang] 동일한 문자열 메모리 공유 (1) | 2024.10.28 |