일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- kube-prometheus-stack
- typescript
- Infra
- UnBuffered channel
- javascript
- 캡슐화
- Intellij
- GoF
- apollo router
- gitops
- go
- intellij ide
- Kubernetes
- 티스토리챌린지
- 코사인 유사성 메트릭스
- esbuild
- 사설 ip
- body size
- 오블완
- m4 pro
- cosine similarity metric
- 디자인패턴
- elasticsearch
- 배포 파이프라인
- golang
- http 413
- goland
- 배포 프로세스
- 구조체
- AWS
Archives
- Today
- Total
Fall in IT.
Go언어에서 구조체의 필드는 Public? Private? 어떤게 맞을까? 본문
반응형
Go 언어로 개발을 하다 보면, 구조체(struct)를 설계할 때 필드를 공개(public)로 해야 할지, 비공개(private)로 해야 할지 고민되는 경우가 많다. 캡슐화를 위해 private으로 설계했다가, 실제 사용 시 불편해지는 경험도 흔하죠. 그렇다면 Go에서는 어떤 기준으로 필드의 접근 범위를 설정하는 것이 좋을까?
이 글에서는 Go 언어의 철학에 기반해, 구조체 필드를 언제 public으로 두고 언제 private으로 설정하는 것이 적절한지를 설명하고자 한다.
Go 언어의 철학: 실용성과 단순성
Go 언어는 "실용적이고 단순한 언어"를 목표로 설계되었습니다. 창시자 중 한 명인 Rob Pike는 다음과 같이 말했다:
"불필요한 복잡성을 피하라"
"명확성이 미묘한 기교보다 낫다"
이 철학은 구조체 필드의 공개 여부를 결정할 때도 중요한 기준이 된다.
기본 가이드라인
✅ 권장되는 방식:
- 실제로 비공개로 유지해야 할 명확한 이유가 있는 필드만 private으로 설정
- 유효성 검사나 부가 로직이 필요한 경우에만 setter 메서드 사용
- 단순 데이터 구조체는 그냥 public 필드 사용
❌ 피해야 할 방식:
- 모든 필드를 무조건 private으로 설정
- 단순 getter/setter 남용
- 과도한 캡슐화
표준 라이브러리 예시
Go 표준 라이브러리에서도 대부분의 구조체는 필드를 public으로 노출한다:
// time 패키지
type Time struct {
wall uint64
ext int64
loc *Location
}
// http 패키지
type Request struct {
Method string
URL *url.URL
Header Header
Body io.ReadCloser
// ... 등등
}
이처럼 불필요한 캡슐화는 하지 않고, 필요한 경우에만 필드를 숨긴다.
예시: ExcelRow 구조체 설계
type ExcelRow struct {
// 대부분의 필드는 단순 데이터이므로 public 유지
IssueNumber string
PageID string
Path string
// 특별한 규칙이나 검증이 필요한 필드만 private + setter
status string
}
// 상태값 검증이 필요한 경우에만 setter 사용
func (r *ExcelRow) SetStatus(status string) error {
if !isValidStatus(status) {
return fmt.Errorf("invalid status: %s", status)
}
r.status = status
return nil
}
언제 private 필드가 필요할까? (예시 포함)
✅ 유효성 검증이 필요한 경우
type BankAccount struct {
accountHolder string
balance float64
}
func (b *BankAccount) Deposit(amount float64) error {
if amount <= 0 {
return errors.New("입금액은 0보다 커야 합니다")
}
b.balance += amount
return nil
}
✅ 내부 상태가 서로 연관된 경우
type Cache struct {
data map[string]string
lastUpdated time.Time
}
func (c *Cache) Set(key, value string) {
c.data[key] = value
c.lastUpdated = time.Now()
}
✅ 동시성 제어가 필요한 경우
type Counter struct {
count int64
mu sync.Mutex
}
func (c *Counter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
✅ 불변성을 보장해야 하는 경우
type Configuration struct {
environment string
apiKey string
}
func NewConfiguration(env, key string) (*Configuration, error) {
if env == "" || key == "" {
return nil, errors.New("environment와 apiKey는 필수입니다")
}
return &Configuration{
environment: env,
apiKey: key,
}, nil
}
func (c *Configuration) Environment() string {
return c.environment
}
✅ 내부 계산 결과를 캐시하는 경우
type Circle struct {
radius float64
area float64
}
func (c *Circle) SetRadius(r float64) {
c.radius = r
c.area = math.Pi * r * r
}
func (c *Circle) Area() float64 {
return c.area
}
public으로도 충분한 경우
📦 단순 데이터 전송 객체 (DTO)
type UserDTO struct {
ID string
Name string
Email string
CreatedAt time.Time
}
⚙️ 설정값 구조체
type Config struct {
Host string
Port int
Username string
Password string
}
이러한 구조체들은 다음과 같은 특징이 있어 굳이 private으로 만들 이유가 없다.
- 단순 데이터 저장 용도
- 유효성 검사가 필요 없음
- 필드 간 연관 관계 없음
- 동시성 제어 필요 없음
✅ 결론
Go 언어는 불필요한 추상화나 캡슐화를 지양하며 실용성과 단순함을 우선합니다.
- 단순한 데이터 구조체는 public 필드로 설계하는게 좋다.
- 유효성 검사, 불변성, 동시성 제어 등이 필요한 경우에만 private 필드 + 메서드를 사용하는게 좋다.
반응형