3. 1. 실행환경 구축(Docker)
바이너리 파일이란? 컴퓨터가 직접 실행할 수 있는 형태의 프로그램 파일.
Go 언어로 작성한 코드는 빌드(build)를 통해 소스 코드 -> 단일 바이너리 파일로 변환
단일 바이너리 파일이란? 하나의 독립된 파일로, 모든 코드와 의존성이 포함되어
다른 라이브러리나 환경 설정 없이 실행 가능.
따라서 이 파일 하나로 다양한 시스템에서 바로 실행할 수 있다.
즉, Go의 바이너리는 독립적으로 실행할 수 있어 배포에 유리하다.
4. 1. 실행환경 구축
컨테이너란? 컨테이너는 프로그램이 실행되는 격리된 환경을 제공하는 기술
이를 통해 각 프로그램이 자신의 환경을 갖게 되고,
다른 시스템에 영향을 주지 않으며 실행된다.
대표적인 컨테이너 플랫폼은 Docker.
•컨테이너에는 프로그램 실행에 필요한 파일 + 라이브러리 + 환경 설정 등이 포함.
•컨테이너를 사용하면 개발자가 만든 프로그램을 다양한 서버에서 일관되게 실행
5. 1. 실행환경 구축
Docker의 멀티 스테이지 빌드 (Multi-stage Build)란?
여러 단계(스테이지)를 거쳐 이미지를 빌드하는 방법.
예를 들어, 소스 코드 컴파일과 같은 복잡한 작업을 첫 번째 스테이지에서 하고, 최종 스
테이지에는 실제로 실행에 필요한 파일만 남기도록 구성
-> 중간에 사용한 빌드 도구와 소스 코드가 최종 이미지에 포함되지 않음
-> 이미지 크기를 감소, 배포에 필요한 파일만 포함, 보안 측면에서도 유리
6. 1. 실행환경 구축
따라서 컨테이너를 작성할 때도 빌드된 바이너리만 컨테이너에 포함시키면 된다.
Docker의 멀티 스테이지 빌드를 사용
•빌드 전의 소스코드 등은 불필요하므로 중간 빌드 스테이지에서 빌드
•컨테이너 이미지 크기 감소, 배포에 필요한 파일만 포함
dockerignore: 도커가 불필요한 파일을 이미지에 포함하지 않도록 하기 위한 설정 파일
7. 1. 실행환경 구축
Dockerfile의 세 가지 빌드 스테이지
•첫 번째 스테이지 (deploy-builder)
• 릴리스용 빌드를 생성하는 스테이지
• golang:1.23.1-bullseye 이미지를 사용해
Go 애플리케이션을 빌드하는 스테이지
• go.mod와 go.sum 파일을 복사해 의존성을 설치한 후,
최종적으로 실행 파일(app)을 빌드
교재와 다른 부분
8. 1. 실행환경 구축
Dockerfile의 세 가지 빌드 스테이지
•두 번째 스테이지 (deploy)
• 빌드한 바이너리를 릴리스하기 위한 컨테이너 생성
스테이지
• debian:bullseye-slim 이미지를 사용해 최종 배포용 컨
테이너를 구성
• 첫 번째 스테이지(deploy-builder)에서 생성된 실행 파
일(app)만 복사해 배포 이미지를 가볍게 유지
• CMD ["./app"]로 실행하도록 설정
9. 1. 실행환경 구축
Dockerfile의 세 가지 빌드 스테이지
•세 번째 스테이지 (dev)
• 로컬에서 개발할 때에 사용할 컨테이너 생성 스테이지
• 로컬 개발을 위한 환경으로, Go 파일을 변경할 때마다
자동으로 다시 빌드하고 실행하는 자동 새로고침 환경
• air라는 오픈소스 툴을 설치하고, 이 명령을 통해 파일
이 변경될 때마다 애플리케이션이 자동으로 새로 빌드
및 실행
교재와 다른 부분
교재와 다른 부분
10. 1. 실행환경 구축
•.air.toml: 자동 새로고침 설정 파일로, air가 감지해야 할 파일 확장자나 제외할 디렉터리,
실행할 명령어 등을 지정
•Go 파일이나 HTML 템플릿 등이 변경될 때 이를 감지해 애플리케이션을 다시 빌드하고 실행
dev 스테이지의 컨테이너는 로컬 장치에서 실행하기 위한 것
air 명령을 실행하면 소스 코드 파일이 변경될 때마다 이를 감지해서 go build 명령을 실행하고
Go 프로그램을 재실행해준다. 로컬 장치의 디렉터리를 마운트해두면 컨테이너에서 파일을 편
집하지 않아도 자동 새로고침이 이루어진다.
11. 1. 실행환경 구축
-> 루트 디렉터리를 현재 디렉터리로 설정
->빌드된 바이너리 파일 저장 위치
->실행 시 필요한 환경 변수 및 실행
인수를 설정. 80번 포트를 인수로 넘김
->이와 같은 확장자 변경 시 빌드 트리거
->특정 패턴을 지정해 제외
->빌드 로그를 air.log 파일에 기록.
/tmp/main에 바이너리 파일을 생성
12. 1. 실행환경 구축
로그 출력
색상으로 구분
•그러나 실무에서는, 자동 새로고침 개발 환경을 준비하고
curl 명령어로 동작을 확인하기보다, 테스트 코드를 작성해
Go의 HTTP 핸들러에 요청을 보내 동작을 확인하는 방식이
더 효율적.
13. 1. 실행환경 구축
도커 컴포즈 설정
•docker-compose.yml 파일은 여러 서비스를 로컬 환경에서 손쉽게 실행하기 위한 설정 파일
•이 설정은 개발용 자동 새로고침 환경을 위한 설정으로, 컨테이너의 /app 디렉터리에 로컬 디렉터리를 마운트
•로컬의 18000번 포트를 컨테이너의 80번 포트에 연결해 웹 애플리케이션을 테스트할 수 있게 함
-> 이 파일에는 app 이라는 이름으로 자동 새로고침
개발 환경의 컨테이너를 실행하도록 정의
-> 로컬 파일이 컨테이너에 실시간으로 반영
->호스트의 18000 포트를 컨테이너의 80 포트와 연결
14. 1. 실행환경 구축
도커 컴포즈 빌드 및 실행
1. docker compose build --no-cache 명령을 실행해 변경된 코드로 로컬 개발용 컨테이너를 빌드
2. docker compose up 명령으로 컨테이너를 실행
3. 별도의 cmd 창을 열어서 curl localhost:18000/hello 명령을 실행하면 로컬에서 요청이 전송되는 것을
확인 할 수 있다.
4. 확인이 끝났다면 docker compose up 명령을 실행한 명령줄에서 docker compose down를 눌러 컨테이너를
종료한다.
16. 1. 실행환경 구축
Makefile이란?
Go 프로젝트에서 반복적으로 사용하는 명령어들을 간편하게 실행할 수 있도록 정리해 둔 파일.
각 명령어는 프로젝트의 빌드, 실행, 종료, 로그 확인, 테스트 등 여러 작업을 쉽게 관리할 수 있도록 도와준다.
이 책에서도 지금까지 여러 명령을 옵션 인수와 함께 실행해왔다.
각 명령을 Makefile에 작성해두면 반복 실행을 효율적으로 할 수 있다.
20. 2. Github actions를 활용한 실행환경 구축
깃허브나 깃랩 등을 이용한 풀 리퀘스트(PR) 기반의 개발에서는 추가, 변경한 코드에 대해 사람이 직접
리뷰를 한다. 이후에 지속적 통합 환경(CI환경)을 설정해서 테스트 코드나 정적 분석을 자동 실행한다.
깃허브 레포 > settings > actions 에서 workflow permissions을
첫번째 걸로 체크, 맨 아래도 체크
21. 2. 실행환경 구축
반드시 두 설정파일 모두 루트 디렉터리 기준으로 해야 깃허브 액션이 파일을 인식할 수 있다.
다음 설정 파일들은 GitHub Actions를 통해 PR에 테스트 커버리지를 자동으로 기록하고, 코드와 테스
트의 비율을 측정하며 결과를 주석으로 남기는 워크플로를 설정한 것이다. 이 워크플로는
**k1LoW/octocov-action**이라는 액션을 사용하여 커버리지와 테스트 정보를 관리한다.
테스트와 코드 커버리지 자동 실행
.github/workflows/ 디렉터리 아래에 test.yml, 루트 디렉터리에 .octocov.yml을 만든다.
22. 2. 실행환경 구축
test.yml : 테스트를 자동으로 실행하고 코드 커버리지 보고서를 생성하기 위한 설정
-> 코드를 체크아웃하여 현재 작업 디렉토리에 가져옵니다
디렉토리로 이동한 뒤, 해당 경로에서 go test 명령어를 실행하여 모든 테스트를 수행합니다.
-> 커버리지 보고서를 생성
23. 2. 실행환경 구축
octocov.yml : octocov-action의 설정 파일로, 커버리지 보고서, 코드 대비 테스트 비율, 테스트 실행 시간,
그리고 PR에 기록할 주석의 조건 등을 설정.
코드 커버리지 파일 경로를 지정
코드와 테스트 파일의 비율을 추적하여
코드의 양에 비해 충분히 테스트가 작성되었는지 확인
-> 코드 변경에 따른 커버리지 차이(diff) 를 추적하고, 그 결과를 데이터스토어에 저장.
기본 브랜치(main 또는 master)에 병합될 때, 커버리지 보고서를 생성하고
데이터를 Artifact 스토어에 저장
24. 2. 실행환경 구축
이 깃허브 액션을 설정한 상태에서 PR을 만들면, 아래와 같이 커버리지 결과가 기록된다.
25. 2. 실행환경 구축
workflows/golangci.yml과 .golangci.yml은 GitHub Actions에서 golangci-lint를 사용하여
PR에서 코드 품질을 자동으로 검토하고, 리뷰 코멘트로 결과를 표시하는 역할을 한다.
깃허브 액션에서 해당 명령을 실행할 때는 reviewdog/actions-golangci-lint를 사용한다. 이를 사용하면
정적 분석에서 오류가 보고되었을 때 PR의 해당 코드에 기록해준다.
정적 분석 자동 실행
29. 환경변수로부터 설정 정보를 읽어오는 패키지를 추가하면, 코드 변경 없이도 실행 환경에 따라 다양한 설정
을 적용할 수 있음
3. http 서버를 약한 결합 구성으로 변경
단원이 바뀌었으니 go mod 이름을 변경해 독립적인 모듈로 취급해주자. + Makefile의 경로도 수정해준다.
config 패키지를 다운로드하기 위해 go get명령은 section60에서 하고, go mod tidy를 해 최적화를 해준다.
30. Config/config.go
3. http 서버를 약한 결합 구성으로 변경
-> 환경변수를 쉽게 관리할 수 있도록 도와주는 패키지
-> Config: 설정 정보를 저장하는 구조체
•Env: 현재 애플리케이션의 실행 환경을 나타내는 문자열로,
TODO_ENV라는 환경변수에서 값을 읽어온다.
•Port: 애플리케이션이 수신 대기할 포트를 나타내는 정수
PORT라는 환경변수에서 값을 읽어온다
-> New(): 새로운 Config 구조체 인스턴스를 생성하고
환경변수로부터 값을 파싱하여 설정합니다.
32. 3. http 서버를 약한 결합 구성으로 변경
Config package 사용해서 main.go 수정
Import 문에 github.com/${GITHUB_USERNAME}/${REPONAME}/config 패키지 추가
33. 3. http 서버를 약한 결합 구성으로 변경
도커 구성기 설정 변경
TODO_ENV라는 환경 변수와 PORT 지정
실행 시 포트 번호가 변경되므로 ports로 지정한
컨테이너의 포트 번호도 변경한다.
34. 3. http 서버를 약한 결합 구성으로 변경
make down 명령으로 종료 후, 다시 make up을 한 후 make logs 명령으로 컨테이너의 실행 로그를 확인한다.
<- 다른 터미널 창을 열어 curl
localhost:18000/hello를 실행해 확인
36. 3. http 서버를 약한 결합 구성으로 변경
서버 또는 컨테이너가 종료될 때는 애플리케이션 프로세스가 종료 시그널을 받음
특정 처리 중에 시그널을 받은 경우, 애플리케이션 프로세스는 처리가 정상적으로 종료되기 전까지 종료돼
서는 안된다.
시그널을 받으면 정상종료하도록 main.go의 run 함수를 변경해보면
명령줄에서 동작을 처리하기 위해
핸들러에 time.Sleep 추가
37. 3. http 서버를 약한 결합 구성으로 변경
Make build로 배포용 컨테이너 작성
28000 포트 지정 후 docker run으로 동작 확인용 컨테이너 실행.
38. 즉, time curl -I localhost:28000/hello 명령을 통해 헤더만 요청한 상황에서 서버가 응답을 반환하기 전에
중단되었을 때의 동작을 설명하는 예제이다.
서버가 중단되더라도 응답 헤더는 이미 전송되어 클라이언트가 확인할 수 있으며, 본문(Hello, hello!) 은
전송되지 않거나 불완전할 수 있다는 점이 중요.
3. http 서버를 약한 결합 구성으로 변경
time curl -I localhost:28000/hello을 명령하고 5초가 경과하기 전에 ctrl+C로 docker run 명령을 중단
43. Server 타입에는 run함수 내에서 수행한 HTTP 핸들러 정의는 구현하지 않는다.
따라서 어떤 처리를 어떤 url path로 공개할지 라우팅하는 NewMux 함수를 mux.go에 구현한다.
3. http 서버를 약한 결합 구성으로 변경
반환값이 http.Handler 인터페이스를 사용
하므로 내부 구현에 의존하지 않는 함수임
Http 서버가 실행중인 확인하기 위한
/health 엔드포인트 선언
44. 3. http 서버를 약한 결합 구성으로 변경
httptest.NewRecoder 함수를 사용하면
ResponseWriter 인터페이스를 충족하는 타입
의 값을 얻을 수 있음.
이후 ServeHTTP 함수에 전달 후 Result 매서드
를 실행하면 클라이언트가 받은 응답 내용이
포함된 http.Response 타입값을 얻을 수 있음
mux_test.go (일부)
45. 3. http 서버를 약한 결합 구성으로 변경
Server 타입과 NewMux 함수를 사용해 run
함수를 리팩터링
46. 3. http 서버를 약한 결합 구성으로 변경
NewMux 함수에 정의한 /health 엔드포인트에 요청을 전송해서 HTTP 서버가 정상적으로 실행되는지 확인
51. $ docker compose build --no-cache
$ docker compose up
$Docker compose up까지 완료했다면
별도의 터미널을 열어서
$ curl localhost:18000/hello 명령을 실행하면 로컬
에서 요청이 전송되는 것을 확인할 수 있다.
확인이 끝났다면 docker compose up 명령을 실
행한 명령줄에서 $ docker compose down를 눌러
컨테이너를 종료한다.
57. Chapter 별로 코드 작성하시는 분 참고)
단원이 바뀌었으니 go mod 이름을 변경해 독립적인 모듈로 취급해주자. + Makefile의 경로도 수정해준다.
config 패키지를 다운로드하기 위해 go get명령은 section60에서 하고, go mod tidy를 해 최적화를 해준다.
Ch.16
58. $ go get –u “github.com/caarlos0/env/v6”
$ mkdir config
config/config.go
config/config_test.go
59. main.go 의 run 함수 수정
s:=&http.Server{ 전까지 위와 같이 수정해준다
main.go 의 main은 run만 호출하도록 수정
<- 받는 인자 하나 삭제
<- 이 줄부터 끝까지 추가
61. docker-compose.yml 수정
+
+
+
+
도커 구성기로 컨테이너 실행했다면 $make down으로 종료.
$make up 명령을 사용해 재실행
$make logs 명령으로 실행 로그 확인
포트번호 설정 확인
다른 터미널에서 $curl localhost:18000/hello 실행