패키지 수준의 캡슐화
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 48번째.
이전 레슨에서는 필드 수준의 가시성을 다뤘지만, Go의 캡슐화는 실제로 패키지 수준에서 작동합니다. 같은 패키지에 있는 모든 항목은 어느 파일에 있든 관계없이 내보내지 않은 필드와 함수에 액세스할 수 있습니다.
여러 파일로 구성된 패키지를 생각해 보세요:
// account/account.go
package account
type Account struct {
balance int // 비공개
}
func (a *Account) Deposit(amount int) {
a.balance += amount
}
// account/helper.go
package account
func ResetAccount(a *Account) {
a.balance = 0 // 작동함 - 같은 패키지
}
두 파일 모두 account 패키지에 속하므로, helper.go는 내보내지 않은 balance field에 직접 액세스할 수 있습니다. 이는 의도된 동작입니다. 이를 통해 내부 액세스를 유지하면서 패키지 로직을 여러 파일로 나눌 수 있습니다.
하지만 패키지 외부의 코드는 내보내지 않은 멤버에 액세스할 수 없습니다:
// main.go
package main
import "account"
func main() {
acc := &account.Account{}
acc.Deposit(100) // 작동함 - 내보낸 메서드
// acc.balance = 50 // 오류 - 내보내지 않은 필드
// account.ResetAccount // ResetAccount가 내보내졌다면 작동함
}
이 패키지 수준의 경계는 Go의 주요 캡슐화 메커니즘입니다. 내부 코드가 자유롭게 협력하는 응집력 있는 단위로 패키지를 설계하는 한편, 외부 코드는 Exported API를 통해서만 상호 작용합니다. 각 패키지를 명확한 공개 인터페이스를 갖춘 독립적인 모듈이라고 생각하세요.
챌린지
쉬움내보내지 않은 필드에 직접 액세스하여 동일한 패키지 내 여러 파일이 협력하는 방식을 보여 주면서, 외부 코드에서는 해당 데이터를 숨기는 재고 관리 시스템을 만들어 보겠습니다.
main 패키지에서 함께 작동하는 세 개의 파일을 만듭니다.
product.go: 내보낸Name필드와 내보내지 않은quantity필드(int)를 포함하는Product구조체를 정의합니다.[Name]: [quantity] in stock형식을 반환하는 내보낸 메서드Info() string을 추가합니다.inventory.go: 내보내지 않은quantity필드에 직접 액세스하여 제품과 함께 작동하는 helper 함수를 만듭니다(동일한 패키지에 있으므로 가능합니다). 다음을 Implement합니다.Restock(p *Product, amount int)- 제품의 수량에 amount를 더합니다.Sell(p *Product, amount int) bool- 충분한 재고가 있으면 수량에서 amount를 빼고, 성공하면true를 반환하며 재고가 enough하지 않으면false를 반환합니다.NewProduct(name string, initialQty int) *Product- given 이름과 initial 수량으로 제품을 creates하는 constructor입니다.
main.go: 입력에서 제품 정보를 읽고, constructor를 사용하여 제품을 만들고, 재고 작업을 수행한 다음 결과를 표시합니다. 각 작업 후 제품 info를 출력하여 업데이트된 상태를 보여 줍니다.
다음 입력이 제공됩니다.
- 1번째 줄: Product 이름
- 2번째 줄: Initial 수량(정수)
- 3번째 줄: Restock amount(정수)
- 4번째 줄: Sell amount(정수)
제품을 만든 후 initial info를 출력합니다. 그런 다음 restock하고 info를 다시 출력합니다. 마지막으로 sell을 시도하고, 성공하면 업데이트된 info를 출력하고, quantity가 enough하지 않으면 Sale failed: insufficient stock을 출력합니다.
예를 들어 Laptop, 10, 5, 8이 주어지면 출력은 다음과 같아야 합니다.
Laptop: 10 in stock
Laptop: 15 in stock
Laptop: 7 in stock또한 Phone, 3, 2, 10이 주어지면 출력은 다음과 같아야 합니다.
Phone: 3 in stock
Phone: 5 in stock
Sale failed: insufficient stock여기서 핵심은 inventory.go가 product.go와 동일한 패키지에 있기 때문에 내보내지 않은 quantity 필드를 직접 읽고 수정할 수 있다는 점입니다. 이것이 바로 패키지 수준 캡슐화입니다. 내부 협력은 자유롭게 이루어지는 동시에 데이터는 외부 패키지로부터 보호됩니다.
직접 해보기
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 제품 이름 읽기
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// 초기 수량 읽기
initialQtyStr, _ := reader.ReadString('\n')
initialQty, _ := strconv.Atoi(strings.TrimSpace(initialQtyStr))
// 재입고 수량 읽기
restockStr, _ := reader.ReadString('\n')
restockAmount, _ := strconv.Atoi(strings.TrimSpace(restockStr))
// 판매 수량 읽기
sellStr, _ := reader.ReadString('\n')
sellAmount, _ := strconv.Atoi(strings.TrimSpace(sellStr))
// TODO: NewProduct 생성자를 사용하여 새 제품 생성
// TODO: 초기 제품 정보 출력
// TODO: 제품 재고를 보충하고 정보 출력
// TODO: 판매를 시도하고 결과 출력
// 판매가 성공하면 업데이트된 정보 출력
// If sale fails, print "Sale failed: insufficient stock"
fmt.Println() // 실제 출력으로 대체
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서직접 연습해 보세요: 온라인 Go 컴파일러