멀티 모듈 프로젝트
여러 Go 모듈로 구성된 프로젝트에서 whatap-go-inst를 사용하는 방법을 설명합니다.
용어 정리
| 용어 | 설명 | 예시 |
|---|---|---|
| 모듈 | go.mod가 있는 단위 | module mycompany/user-api |
| 패키지 | 디렉토리 레벨 코드 그룹 | mycompany/user-api/pkg/auth |
| 외부 모듈 | go get으로 받은 의존성 | github.com/gin-gonic/gin |
| 로컬 모듈 | 같은 프로젝트 내 별도 모듈 | replace ../shared-lib |
패키지 유형별 처리
whatap-go-inst는 패키지 위치에 따라 계측 여부를 결정합니다.
toolexec 스킵 규칙
// 1. Go 표준 라이브러리 → 스킵
if strings.HasPrefix(path, os.Getenv("GOROOT")) {
return true // 변환 안 함
}
// 2. 외부 패키지 (go get) → 스킵
if strings.HasPrefix(path, os.Getenv("GOMODCACHE")) {
return true // 변환 안 함
}
// 3. 그 외 (내 코드) → 변환
return false
요약
| 패키지 유형 | 경로 | 계측 여부 |
|---|---|---|
| Go 표준 라이브러리 | $GOROOT/src/... | 스킵 |
| 외부 모듈 (go get) | $GOMODCACHE/... | 스킵 |
| 내 프로젝트 코드 | 로컬 경로 | 계측 |
| replace 로컬 모듈 | 로컬 경로 (../) | 계측 (fast 모드만) |
멀티 모듈 시나리오
시나리오: 3개의 분리된 모듈
C:/projects/
├── db-lib/ # 모듈 A: DB 로직 라이브러리
│ ├── go.mod # module mycompany/db-lib
│ ├── connection.go # sql.Open() 사용
│ └── query.go
│
├── web-lib/ # 모듈 B: 웹 서버 라이브러리
│ ├── go.mod # module mycompany/web-lib
│ ├── server.go # gin.Default() 사용
│ └── handler.go
│
└── main-app/ # 모듈 C: 메인 앱
├── go.mod # module mycompany/main-app
└── main.go # A, B를 import
모듈 C의 go.mod
module mycompany/main-app
go 1.21
require (
mycompany/db-lib v1.0.0
mycompany/web-lib v1.0.0
)
권장 접근 방식
방식 1: 모듈별 별도 inject (배포용)
운영 환경 배포에 권장합니다.
Step 1: 각 모듈 inject
# db-lib 계측
cd db-lib
whatap-go-inst inject -s . -o ../db-lib-instrumented
# web-lib 계측
cd web-lib
whatap-go-inst inject -s . -o ../web-lib-instrumented
# main-app 계측
cd main-app
whatap-go-inst inject -s . -o ../main-app-instrumented
Step 2: 계측된 버전으로 교체
// main-app-instrumented/go.mod
replace mycompany/db-lib => ../db-lib-instrumented
replace mycompany/web-lib => ../web-lib-instrumented
Step 3: 빌드
cd main-app-instrumented
go build ./...
방식 2: Monorepo 구조 (신규 프로젝트)
신규 프로젝트는 단일 모듈 구조가 가장 간단합니다.
myproject/
├── go.mod # 단일 모듈 mycompany/myproject
├── cmd/
│ └── main/
│ └── main.go # main 패키지
├── internal/
│ ├── db/
│ │ └── connection.go
│ └── web/
│ └── server.go
└── pkg/
└── shared/
└── utils.go
# 빌드
whatap-go-inst go build ./cmd/main
모드별 동작 비교
--wrap 모드
| 항목 | --wrap 모드 |
|---|---|
| main 모듈 | 계측됨 |
| replace 모듈 | 계측 안 됨 |
| 외부 모듈 (GOMODCACHE) | 스킵 |
| 사전 조건 | 없음 |
replace 처리
// --wrap 모드는 replace 경로만 조정
replace mycompany/db-lib => ../db-lib
// 임시 디렉토리 복사 후:
replace mycompany/db-lib => /original/path/to/db-lib // 원본 참조!
결과: replace 대상 모듈은 복사되지 않고 원본(비계측) 코드를 참조
replace 모듈도 계측하려면: 각 모듈을 별도로 inject 해야 합니다.
# 각 모듈 별도 inject
whatap-go-inst inject -s ../db-lib -o ../db-lib-instrumented
whatap-go-inst inject -s . -o ./instrumented
# instrumented/go.mod에서 replace 경로 수정
# replace mycompany/db-lib => ../db-lib-instrumented
주의사항
1. main이 없는 라이브러리 inject
main 함수가 없는 라이브러리도 inject할 수 있습니다.
| 항목 | 처리 |
|---|---|
trace.Init() | 추가 안 됨 (정상) |
sql.Open() → whatapsql.Open() | 변 환됨 |
gin.Default() + 미들웨어 | 추가됨 |
# 라이브러리 모듈 inject
cd db-lib
whatap-go-inst inject -s . -o ./instrumented
# 결과: whatapsql.Open()으로 변환됨
# trace.Init() 추가 안 됨 (main에서만 호출해야 함)
2. 의존성 전파
계측된 라이브러리를 사용하는 모듈도 whatap 의존성이 필요합니다.
// db-lib-instrumented/connection.go
import "github.com/whatap/go-api/instrumentation/.../whatapsql"
// main-app에서 빌드하려면 이 의존성이 필요
cd main-app
go get github.com/whatap/go-api@latest
FAQ
Q1: replace 모듈도 계측되나요?
A: 아니요, wrap 모드는 replace 모듈을 계측하지 않습니다. replace 경로만 조정하고 원본을 참조합니다. replace 모듈도 계측하려면 각 모듈을 별도로 inject 해야 합니다.
각 모듈 별도 inject
whatap-go-inst inject -s ../db-lib -o ../db-lib-inst
whatap-go-inst inject -s ../web-lib -o ../web-lib-inst
Q2: 외부 라이브러리(gin, gorm 등)를 계측할 수 있나요?
A: 아니요. 외부 라이브러리는 $GOMODCACHE에 있어서 스킵됩니다. 이는 의도된 동작입니다.
- 외부 라이브러리 수정 시 다른 프로젝트에 영향
- Go 모듈 시스템의 불변성 원칙
대신, 사용자 코드에서 외부 라이브러리 호출이 변환됩니다:
sql.Open()→whatapsql.Open()gin.Default()→ 미들웨어 자동 추가