Go 라이브러리 설계 원칙: 저장소에서 리소스까지
프로그래밍은 애플리케이션과 이를 지원하는 라이브러리를 만드는 과정입니다. 라이브러리는 주로 소스 코드로 구성된 공유 가능한 리소스의 모음입니다. 용어를 명확히 구분하면 견고한 컴포넌트 설계에 도움이 됩니다.
저장소는 파일 시스템 구조 내에 코드 파일들을 포함합니다. Go의 패키지는 단일 작업을 수행하는 .go 파일들의 그룹으로, 각 파일 상단에 반드시 package 선언이 필요합니다. 모듈은 패키지들을 함께 묶으며, 의존성은 go.mod에 고정됩니다.
라이브러리는 코드 중복 없이 코드 공유를 가능하게 합니다. 예를 들어: MaxInt 함수를 foo와 baz라는 서로 다른 패키지에 복사하는 것은 단일 진실 공급원이 없으면 비효율적입니다. 패키지는 컴파일과 임포트 문제를 해결합니다.
package foo
func MaxInt(a, b int) int {
if a > b {
return a
}
return b
}
컴포넌트 격리는 다른 코드에 대한 지식을 차단하고, 분해는 컴파일러와의 상호작용을 단순화합니다.
라이브러리의 리소스
라이브러리는 코드뿐만 아니라 외부 리소스도 포함합니다: 동적 리소스와 정적 리소스로 구분됩니다.
동적 리소스는 런타임에 의존합니다. 예시로 Gorgonia의 cu 패키지를 통한 CUDA를 이용한 GPU 접근이 있습니다:
d := cu.CurrentDevice()
ctx := cu.NewContext(d, cu.SchedAuto|cu.MapHost)
mem, err := ctx.MemAlloc(1024)
여기서 ctx는 GPGPU 작업을 위한 GPU 디스크립터입니다. 그 상태는 컴파일 시점에 알 수 없습니다.
정적 리소스는 컴파일러가 인지합니다. 예시로 willshakes 패키지의 셰익스피어 텍스트가 있습니다:
package willshakes
const AllsWellThatEndsWell = `제1막 제1장
백작 부인.
내 아들을 내게서 떼어내는 것은, 두 번째 남편을 묻는 일이오.
...`
const MacBeth = `제1막 제1장
첫 번째 마녀.
우리 셋이 다시 언제 만날까
...`
접근 방법: willshakes.MacBeth. 마찬가지로 폰트나 MNIST 같은 데이터셋에도 적용됩니다.
라이브러리 분류
라이브러리는 리소스에 따라 분류됩니다:
- 코드 라이브러리: 패키지/모듈 내의 공유 소스 코드.
- 드라이버 라이브러리: 드라이버를 감싼 래퍼 (CUDA용 cu, OpenGL용 go-gl).
- 리소스 라이브러리: 정적 데이터 (willshakes) 또는 로더 (Go의 mnist).
| 유형 | 예시 | 특징 |
|------|---------|----------------|
| 코드 | 유틸리티 패키지 | 정적 코드 |
| 드라이버 | cu | 동적 접근 |
| 리소스 | willshakes | 내장 상수 |
드라이버는 크로스-랭귀지 상호작용을 위한 추상화를 추가합니다.
좋은 라이브러리의 특성
좋은 라이브러리는 두 가지 대화를 용이하게 합니다: 기계(컴파일러/런타임)와의 대화, 그리고 개발자와의 대화입니다.
기준:
- 격리: 컴포넌트가 외부 라이브러리를 인지하지 않음.
- 분해: 명확한 기능적 경계.
- 조합: 애플리케이션에 쉽게 통합 가능.
- 예측 가능성: 문서와 일치하는 동작.
- 성능: 타협 없이 최소한의 오버헤드.
라이브러리에서는 package main을 피하고 유틸리티에 집중하세요. 엣지 케이스를 테스트하고 스레드 안전성을 보장하세요.
핵심 요약
- 라이브러리는 단순한 저장소나 패키지가 아닌 공유 가능한 리소스입니다.
- 정적(상수)과 동적(드라이버) 리소스를 분리하세요.
- 개발자가 단일 작업에 집중할 수 있도록 격리를 보장하세요.
- 컴파일을 위해 Go 패키지를, 의존성 관리를 위해 모듈을 사용하세요.
- 조합을 위해 설계하세요: 쉬운 임포트와 사용.
— Editorial Team
아직 댓글이 없습니다.