How-toVS Code

Go 모듈·gopls·Delve와 테스트

Go 프로젝트 인식부터 테스트 디버깅, race 검사, 프로파일링과 여러 모듈 운영까지 연결합니다.

업데이트 검증 근거 자료이 페이지 편집

확장 선택과 일괄 설치 범위는 언어별 확장·Profile 설치를 참고합니다.

이 문서는 단일 Go 모듈의 개발·디버깅 가이드입니다. 포맷만 먼저 연결하려면 gofmt 실습을 사용합니다. 여기서는 import 정리도 gopls에 맡기므로 두 실습의 저장 설정을 동시에 병합하지 않습니다.

Go는 표준 CLI를 그대로 IDE에 연결하기 좋은 언어다. 모듈을 잘못 열거나 Go 버전이 어긋나면 이 장점이 사라진다. 먼저 터미널의 Go, 언어 서버 gopls, 디버거 Delve가 같은 프로젝트를 보는지 확인한다.

확장과 도구를 연결한다

공식 Go 확장, ID golang.go를 설치한다. Go: Install/Update Tools에서 필요한 gopls·dlv를 설치하고 Go: Locate Configured Go Tools로 실제 경로를 확인한다. PATH에 여러 버전이 있으면 터미널과 확장의 선택이 다를 수 있다. 팀이 고정한 도구 버전은 설치·갱신 기록에도 남긴다. Go 확장 문서

go version
go env GOMOD GOWORK GOPATH GOTOOLCHAIN
go test ./...

위 명령은 go.mod가 있는 프로젝트 루트에서 실행한다. GOMOD가 기대한 경로가 아니면 먼저 연 폴더와 cwd를 고친다. Windows에서도 같은 Go 명령을 사용할 수 있다. 단, 이 문서의 파일 비교 셸 예제는 POSIX 기준이다.

gopls에 포맷과 import를 맡긴다

settings.json의 Go 블록을 다음처럼 구성한다. 이 학습 예제는 표준 gofmt 방식을 사용한다. gofumpt나 별도의 줄 길이 formatter는 추가하지 않는다.

{
    "go.useLanguageServer": true,
    "go.formatTool": "default",
    "[go]": {
        "editor.defaultFormatter": "golang.go",
        "editor.formatOnSave": true,
        "editor.formatOnPaste": false,
        "editor.formatOnType": false,
        "editor.codeActionsOnSave": {
            "source.fixAll": "never",
            "source.organizeImports": "explicit"
        }
    }
}

Go 확장은 gopls를 기본 formatter로 사용할 수 있다. Format과 Organize Imports는 구분되는 요청이므로 둘을 함께 사용하되 또 다른 goimports 저장 확장은 추가하지 않는다. 회사 모듈을 별도 import 그룹으로 나눌 때는 gopls의 local 설정과 CI의 goimports -local 값을 맞춘다. 현재 확장 설정 표와 gopls 원문의 키 표기가 다를 수 있으므로 Settings 자동 완성도 확인한다. Go 확장 설정, gopls formatting 설정

CI에서 import 추가·제거까지 검사하려면 버전을 고정한 goimports를 별도 도구로 사용한다. gofmt만으로 누락된 import가 추가되지는 않는다. 아래 goimports 명령은 해당 도구가 설치되어 있고 소스 디렉터리가 .인 작은 단일 모듈의 예제다.

goimports -w .
gofmt -l .
go test ./...
go vet ./...

vendor·생성 파일이 있는 저장소는 팀의 source 목록과 제외 기준으로 범위를 좁힌다. gofmt -l은 출력이 생겨도 보통 성공 종료하므로 CI에서는 출력이 비어 있는지 검사해야 한다. goimports의 -l도 같은 방식으로 다룬다. gofmt, goimports

gofumpt를 팀 기준으로 채택했다면 gopls의 gofumpt 옵션과 CLI 버전을 함께 맞춘다. VS Code에서만 더 엄격한 포맷을 쓰는 구성이 되지 않도록 포맷 충돌 해결의 두 번 실행 검사를 수행한다.

작은 모듈로 탐색·테스트·디버깅 익히기

빈 학습 폴더에서 go mod init example.com/vscode-lab을 실행한다. main.go:

package main

import "fmt"

func total(price, quantity int) int {
	return price * quantity
}

func main() {
	fmt.Println(total(1200, 3))
}

main_test.go:

package main

import "testing"

func TestTotal(t *testing.T) {
	if got := total(1200, 3); got != 3600 {
		t.Fatalf("total = %d, want 3600", got)
	}
}

total의 References로 main과 테스트의 호출자를 찾고 F2로 subtotal로 바꿔 본다. 테스트 위의 Run/Debug 또는 Testing 뷰에서 TestTotal을 실행한다. gopls가 제공하는 Extract·interface 구현 등 Code Action은 선택한 문맥에서 확인한다. Go 편집·테스트 기능

launch.json에서 파일 한 개보다 package 디렉터리를 실행한다. 같은 package의 다른 파일에 있는 함수·빌드 태그를 놓치지 않기 위해서다.

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Go: package",
            "type": "go",
            "request": "launch",
            "mode": "debug",
            "program": "${workspaceFolder}",
            "cwd": "${workspaceFolder}"
        },
        {
            "name": "Go: TestTotal",
            "type": "go",
            "request": "launch",
            "mode": "test",
            "program": "${workspaceFolder}",
            "args": ["-test.run", "^TestTotal$", "-test.v"]
        }
    ]
}

프로젝트 진입점이 cmd/api라면 첫 program${workspaceFolder}/cmd/api로 바꾼다. 테스트를 rename했다면 launch의 필터도 갱신한다. 테스트 디버깅의 인자는 테스트 바이너리에 전달되므로 go test -run과 달리 -test.run을 사용한다. Delve 기반 Go debugging

total 안에서 멈춘 뒤 Call Stack과 goroutine을 확인한다. production binary attach에서 변수가 보이지 않으면 최적화·소스 경로·바이너리 빌드 버전을 먼저 확인한다. 일반적인 로컬 실습은 확장의 launch debug 모드가 만드는 debug build로 시작한다.

테스트·race·성능을 구분한다

go test ./... -run '^TestTotal$' -count=1
go test ./... -cover
go test -race ./...
go vet ./...

-count=1은 테스트 캐시 영향을 제거해 재현할 때 사용한다. -race는 지원 플랫폼과 C toolchain 등 실행 조건을 확인하고 사용한다. 단순 테스트 통과와 race 검출, benchmark 결과는 서로 다른 근거다. Go test 옵션, Race detector

CPU 병목은 추측으로 고치지 않는다. benchmark가 정의된 package에서 CPU profile을 만들고 pprof로 분석한다. 아래는 BenchmarkTotal이 존재하는 프로젝트에 적용하는 예시다.

go test -run '^$' -bench '^BenchmarkTotal$' -cpuprofile cpu.out .
go tool pprof -http=127.0.0.1:8081 cpu.out

프로파일 결과는 실행 환경과 workload를 함께 기록하고 산출물을 커밋하지 않는다. 기능 테스트를 유지한 채 동일 조건으로 변경 전후를 비교한다. Go diagnostics

여러 모듈과 추가 lint

독립 go.mod 여러 개를 함께 편집한다면 go.work로 필요한 모듈을 묶는다. 기존 workspace에 임의의 replace를 계속 추가하는 대신 현재 개발 관계를 명시한다. go.work를 팀에 커밋할지 개인 개발용으로 둘지는 CI의 실행 모델과 함께 결정한다. Go multi-module tutorial

go.work 루트가 모듈이 아닐 때 go test ./... 하나로 모든 모듈이 테스트된다고 가정하지 않는다. 예를 들어 services/api·libs/shared를 각각 cwd로 하는 Tasks에서 go test ./...를 실행한다. local workspace 성공 뒤 각 모듈이 CI의 의존성 해석으로도 성공하는지 확인한다.

golangci-lint는 팀이 필요한 분석을 묶는 선택지다. 설치 버전과 .golangci.yml의 major를 맞추고 현재 버전의 config verify·run 절차를 따른다. 저장 시 --fix와 별도 formatter를 함께 돌리지 않는다. editor의 빠른 gopls 진단과 전체 lint를 중복 알림 없이 운영한다. golangci-lint quick start

증상확인할 것
외부 package를 못 찾음GOMOD·GOWORK·인증·module cache·프록시
일부 파일만 오류build tag·GOOS·GOARCH·CGO·테스트 전용 파일
Delve 실행 실패dlv 경로·Go 버전 호환·실행 아키텍처
CLI 성공, IDE 오류확장이 선택한 Go·gopls 경로와 Output
import 순서만 CI와 다름goimports local 그룹·gofumpt 버전·중복 action

영상: Go — Writing and debugging fast, reliable and efficient software — Visual Studio Code, 2021-06-17. 편집·테스트·디버깅 흐름을 따라 하고 설치 옵션은 현재 공식 문서로 확인한다.