/go/analysis: Go 팀의 정적 분석 프레임워크, 작은 재사용 가능한 모듈로 구성됨

개발 & 코딩 Jul 26, 2026북마크에 추가

/go/analysis: Go 팀의 정적 분석 프레임워크, 작은 재사용 가능한 모듈로 구성됨
삽화 : Momiji Shirogane

고(Go) 팀이 유지 관리하는 공식 프레임워크 덕분에 `go vet`, `staticcheck` 등과 같은 도구는 수십 줄의 코드로 린터를 작성할 수 있습니다.

실제 사례

20만 줄 규모의 Go 프로젝트를 물려받았습니다. 코드 리뷰에서 지난 6개월간 goroutine leak을 일으키는 패턴이 계속 통과되었습니다: defer cancel() 없이 생성된 context.WithTimeout입니다. 아무도 읽을 때 이를 발견하지 못했습니다. go vet도 이를 경고하지 않았습니다. 자체 린터에 규칙을 추가하려면 다른 언어였다면 일주일은 족히 걸렸을 것입니다.

Go에서는 하루 evening만에 Analyzer를 작성할 수 있습니다. 언어 팀이 오랫동안 유지해온 거의 은밀하지만 근본적인 패키지 덕분입니다: golang.org/x/tools/go/analysis.

왜 중요한가

go/analysis 패키지(« Go Analysis Framework »)는 새로운 것이 아닙니다 - 이미 pkg.go.dev/golang.org/x/tools/go/analysis에 게시되고 문서화되어 있지만, 해커 뉴스에서 꾸준히 언급됩니다(마지막 언급: 2026-07-26). 많은 Go 개발자들이 아직 이 패키지를 모르고 있기 때문입니다. 하지만 이것こそ Go 정적 분석 에코시스템의 기반 인프라입니다: go vet은 이 위에 구축되며, staticcheck(Dominik Honnef)가 사용하고, gopls(공식 언어 서버)는 여기에 진단을 연결하며, 현대 CI의 대부분의 린터 - 특히 golangci-lint -는 단순히 쌓인 Analyzer들의 집합에 불과합니다.

프레임워크의 핵심은 단순합니다: 하나의 분석기 = 하나의 struct. 이 struct는 이름, 문서, 다른 분석기에 대한 의존성, 그리고 *analysis.Pass 객체(Pass는 파싱된 코드, 타입 체커, 파일, 주석을 포함)를 받는 Run 함수를 선언합니다. 그리고 pass.Report를 통해 진단을 보고합니다. 그게 전부입니다.

내부 작동 원리

세 가지 아이디어가 이 아키텍처를 지탱합니다:

  1. 구성 요소별 모듈성. 분석기는 Requires: []*analysis.Analyzer{inspect.Analyzer}를 선언할 수 있습니다: 프레임워크는 의존성 그래프를 해결하고 사전 요구 분석기의 결과를 Pass.ResultOf에 주입합니다. 결과적으로 여러분의 비즈니스 분석기는 파일을 다시 파싱하지 않고, inspect(한 번의 타입 AST walk로 전체 체인을 처리하는)의 결과를 소비합니다.

  2. 단일 드라이버, 다양한 부트스트래핑.Analyzer를 작성하면 singlechecker.Main(myAnalyzer)는 독립형 CLI 바이너리로, multichecker.Main(a1, a2, a3)는 다중 체크 바이너리로, unitchecker.Main(...)go vet -vettool=...에 연결됩니다. 어떠한 글루 코드도 필요 없습니다: 동일한 Analyzer 코드가 로컬, CI, gopls를 통한 IDE, golangci-lint에서 실행됩니다.

  3. 타입화된 자동 수정. 진단은 SuggestedFix를 가질 수 있습니다: token.Pos에 고정된 텍스트 대체입니다. gopls는 에디터에서 한 번의 클릭으로 이를 적용합니다. 예를 들어 staticcheck가 « errors.Is(err, sql.ErrNoRows)를 더 idiomatic한 체크로 대체하세요 »라고 제안하고 IDE가 이를 깔끔히 대체할 수 있게 해주는 것이 바로 이 기능입니다.

규모를 보여드리기 위해: singlechecker - *Analyzer를 완전한 바이너리로 변환하는 함수 -는 한 파일에 담깁니다. 게시 가능한 Go 린터의 최소 골격은 대략 40줄입니다(문서와 테스트 포함). ESLint 플러그인을 처음부터 작성하는 것과는 비교할 수 없을 정도로 가볍습니다.

실무에서의 변화

실제 프로젝트에서 세 가지 흔한 사용 사례:

  • 기업 내부 규칙. « net/http를 직접 임포트하지 말고, 트레이싱을 위한 래퍼를 사용하세요. » 30줄, *ast.ImportSpec을 검사하는 Analyzer, fixture 파일에 대한 analysistest.Run 테스트 한 번, 그리고 다음날 CI에 규칙이 연결됩니다.
  • 대규모 코드 마이그레이션. 500개 파일에 걸친 deprecated API 이름 바꾸기 등 대규모 리팩터링: SuggestedFix가 있는 Analyzergo vet -vettool=./monfix -fix ./...으로 작업을 변환합니다. 인간이 개입할 필요가 없습니다.
  • 커스텀 데이터플로우 분석. 프레임워크는 ctrlflow(제어 흐름 그래프), buildssa(Go 코드의 SSA 표현)와 같은 동반 패키지를 제공합니다. 이를 활용해 비정상적인 데이터플로우 분석이 가능합니다. 예를 들어 sql.Rows 누수 감지는 AST가 아닌 SSA에서 제대로 수행됩니다.

알아두어야 할 한계

두 가지 맹점을 기억하세요:

  • 패키지 내 분석. 기본적으로 Analyzer는 한 번에 하나의 패키지만 봅니다. 패키지 간 분석(whole-program)은 추가 작업이 필요합니다 - analysis.Fact를 통해 가능하지만(패키지가 심볼에 대한 사실을 내보내고 다른 패키지에서 이를 재사용할 수 있게 해줌), 작성 난이도가 한 단계 올라갑니다.
  • 동적 플러그인 에코시스템 없음.go vetAnalyzer를 런타임 구성으로 « 플러그인 »할 수 없습니다: 바이너리에 컴파일해야 합니다. 이는 Go가 동적 플러그인을 선호하지 않기 때문이지, 누락이 아닙니다.

기억해야 할 점

  • go/analysis는 Go의 정적 분석과 관련된 모든 것의 공식 API입니다 - 집 lint부터 자동 코드 마이그레이션까지.
  • 분석기 = Run(*Pass)를 가진 하나의 struct. 나머지(CLI, go vet 통합, gopls 통합, CI)는 제공됩니다.
  • 아직 셸이나 sed로 품질 규칙을 작성하고 있다면, 그날 밤을 투자할 가치가 충분합니다.
Resources

인공지능이 작성하고 사람의 편집 감독하에 검수한 기사입니다.

편집팀
이 기사가 도움이 되었나요?

3 명이 이 기사를 좋아합니다

좋아요
K
Kaito KuroganeSenior Dev Writer
Senior polyvalent developer, backend Go + frontend TS, open source contributor.
공유:
LIVERadio Geek Kitsune
눌러서 청취, 모두에게 같은 소리
0··
// 편성표
// all stations
// 트랙 공유 →
토픽
탐색
정보