go run의 첫 실행이 유독 느렸던 이유에 대한 고찰
go run의 첫 실행이 유독 느렸던 이유에 대한 고찰
Summary go run
요약
go run hello.go 를 작성했는데, 첫 빌드시에는 시간이 좀 지나야 출력되고, 그 다음부턴 바로바로 실행되었다. 그래서 같은 디렉토리에 빌드파일이 생기는건가? 하고 확인해보니, 또 그건 아니었다. 그렇다면, go의 빌드는 어떤식으로 이루어지고, 빌드파일되는 어디에 생성되는걸까?
go run
은 단순한 실행 명령이 아니라, 보이지 않는 곳에서 컴파일과 실행, 그리고 뒷정리까지 스스로 완료하는 영리한 자동화 도구였다. 처음의 느린 속도는 온전한 컴파일 과정 그 자체였고, 이후의 빠른 속도는 영리하게 남겨둔 캐시 덕분이었다. 모든 과정이 사용자가 인지하지 못하는 임시 공간에서 일어나기에, 결과물은 보이지 않았던 것이다.
|
|
|
|
|
|
go run hello.go
. 터미널에 명령어를 입력하고 잠시 정적이 흘렀다. 찰나의 순간이었지만, 인터프리터 언어의 즉각적인 반응에 익숙했던 나에게는 어색하게 긴 시간이었다. 곧이어 “Hello, Golang!“이 출력되었다. 혹시나 싶어 다시 같은 명령어를 입력하니, 이번에는 눈 깜짝할 사이에 결과가 나타났다.
이 경험은 나에게 자연스러운 의문을 남겼다. 첫 실행과 두 번째 실행의 속도 차이. 가장 먼저 떠오른 가설은 ‘컴파일’이었다. 첫 실행 시 코드가 컴파일되어 실행 파일, 즉 빌드 결과물이 어딘가에 생성되었고, 두 번째 실행부터는 그 결과물을 즉시 실행했기 때문에 빨라진 것이라고 추측했다. 지극히 합리적인 생각이었다.
|
|
그래서 ls
명령어로 현재 디렉터리를 확인했다. 하지만 디렉터리에는 hello.go
파일 외에 아무것도 없었다. 내 가설이 틀렸나? 컴파일이 되긴 한 걸까? 만약 컴파일이 되었다면 그 실행 파일은 대체 어디로 사라진 것일까. 보이지 않는 무언가가 있다는 생각에 머릿속이 복잡해지기 시작했다. 이것이 나의 궁금증의 시작이었다.
그 답은 예상치 못한 곳에 있었다. go run
은 내가 보고 있는 현재 디렉터리가 아닌, 시스템의 임시 폴더에 자신만의 작업 공간을 몰래 만들고 있었다. 리눅스 시스템이라면 /.cache
디렉터리 아래에 go-build
로 시작하는 무작위 이름의 폴더를 생성하는 식이다. 그리고 바로 그곳에서 우리가 아는 컴파일과 링크 과정이 모두 이루어진다. 소스 코드는 오브젝트 파일(.o)로 컴파일되고, 최종적으로 하나의 실행 가능한 바이너리 파일로 만들어진다.
|
|
이것이 첫 실행이 느렸던 이유, 즉 ‘왜(Why)?’에 대한 직접적인 답이다. go run
은 인터프리터처럼 소스 코드를 한 줄씩 읽어 실행하는 것이 아니라, 명백히 컴파일 언어로서의 정석적인 단계를 밟고 있었던 것이다.
그렇다면 실행 파일은 왜 보이지 않았을까? 그 해답은 go run
의 설계 목적에 있었다. 이 명령어의 본질은 ‘빠른 테스트와 실행’에 있다. 개발자가 소스 코드의 실행 결과만 잠시 확인하고 싶을 뿐, 그 과정에서 생성되는 바이너리 파일까지 관리하고 싶어 하지는 않을 것이라는 배려다. 그래서 go run
은 임시 디렉터리에 생성했던 실행 파일을 실행하여 결과를 터미널에 보여준 직후, 자신이 만들었던 임시 폴더와 그 안의 실행 파일을 모두 깨끗하게 삭제하며 흔적을 지운다. 이것이 ls
를 입력해도 아무것도 보이지 않았던 ‘어떻게(How)?’에 대한 설명이다.
마지막 의문. 그렇다면 왜 두 번째 실행부터는 빨라지는가. 임시 파일은 매번 삭제된다면서. 여기에 Go의 또 다른 영리함이 숨어있다. 바로 ‘빌드 캐시(Build Cache)‘다. Go는 1.10 버전부터 빌드 캐시 기능을 도입했다. 한번 컴파일한 패키지의 결과물은 사용자의 캐시 디렉터리(~/.cache/go-build
등)에 저장해 둔다. 그리고 다음 실행 시, 소스 코드에 변경 사항이 없다면 다시 컴파일하는 대신 캐시에 저장된 결과물을 가져다 쓴다. 첫 실행에서 만들어 둔 캐시 덕분에, 두 번째 go run
은 컴파일 단계를 거의 건너뛰고 링크 및 실행만 진행하면 되니 즉각적인 반응이 가능했던 것이다.
이러한 일련의 과정을 이해하고 나니 go build
와의 차이점 또한 명확해졌다. go run
이 개발 과정의 편의성을 위해 임시 공간에서 모든 것을 처리하고 사라지는 유령 같은 존재라면, go build
는 배포와 지속적인 사용을 위해 현재 디렉터리에 ‘hello’라는 이름의 실행 파일을 뚜렷하게 남기는 실체적인 명령어다. go run
이 과정의 자동화와 은닉에 초점을 맞췄다면, go build
는 결과물의 생성 그 자체에 집중한다.
결국, go run
의 느린 첫 실행에서 시작된 작은 궁금증은 Go 언어의 도구가 사용자를 얼마나 깊이 배려하며 설계되었는지 깨닫게 되는 계기가 되었다. 보이지 않는 곳에서 묵묵히 컴파일하고, 흔적을 남기지 않으려 스스로를 정리하며, 다음을 위해 조용히 캐시를 남겨두는 그 과정 전체가 개발자의 편의를 위한 정교한 자동화 시스템이었던 것이다. 단순한 명령어 하나에도 이러한 철학이 담겨있다는 사실이, 언어에 대한 이해를 한층 더 깊게 만들어 주었다. 이 기록이 나와 비슷한 궁금증을 가졌을 누군가에게 생각의 실마리가 되기를 바란다.
💬 댓글
GitHub 계정으로 로그인하여 댓글을 남겨보세요. GitHub 로그인
🔧 댓글 시스템 설정이 필요합니다
GitHub Discussions 기반 댓글 시스템을 활성화하려면:
GISCUS_SETUP_GUIDE.md
파일의 안내를 따라 설정 완료Repository 관리자만 설정할 수 있습니다. 설정이 완료되면 모든 방문자가 댓글을 남길 수 있습니다.