Skip to content

Commit

Permalink
Update chapter15
Browse files Browse the repository at this point in the history
  • Loading branch information
gleaming9 committed Nov 14, 2024
1 parent a8fe1f5 commit c2860ac
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 2 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 워크플로우 트리거 조건 설정
on:

pull_request:
paths:
- "**.go"
- .github/workflows/golangci.yml

jobs:
golangci-lint:
name: golangci-lint
runs-on: ubuntu-latest
steps:
# 1단계: 코드 체크아웃
- name : Check out code into the Go module directory
uses: actions/checkout@v3

# 2단계: golangci-lint 실행
- name: golangci-lint
uses: reviewdog/action-golangci-lint@v2
with:
# GitHub Actions에서 자동으로 제공하는 토큰
github_token: '${{ secrets.GITHUB_TOKEN }}'

# golangci-lint 실행 옵션
golangci_lint_flags: "--config=./.golangci.yml ./..."
# --config: 설정 파일 위치 지정
# ./...: 현재 디렉토리와 모든 하위 디렉토리의 코드를 검사

# lint에서 에러가 발생하면 워크플로우를 실패 처리
fail_on_error: true

# 리뷰 결과를 GitHub PR에 코멘트로 남김
reporter: "github-pr-review"
35 changes: 35 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 워크플로우 트리거 설정
on:
# main 브랜치에 푸시할 때만 워크플로우가 실행되도록 설정
push:
branches:
- "main"
# 모든 브랜츠에서 PR이 생성되거나 업데이트 될 때 실행
pull_request:

# GitHub Actions UI에서 표시될 워크플로우 이름
name: test
#실행할 작업들을 정의
jobs:
test:
# Ubuntu 최신 버전을 실행 환경으로 사용
runs-on: ubuntu-latest

steps:
# Go 언어 설치 및 설정
- uses: actions/setup-go@v3
with:
go-version: '>=1.18'

# 레포지토리 코드 체크아웃
- uses: actions/checkout@v3 # 현재 레포지토리의 코드를 가져오는 액션

# Go 테스트 실행
- run: go test ./... -coverprofile=coverage.out
# ./... : 현재 디렉토리와 모든 하위 디렉토리의 테스트 실행
# -coverprofile=coverage.out : 테스트 결과를 coverage.out 파일에 저장

# 테스트 커버리지 리포트 생성
- name: report coverage
uses: k1LoW/octocov-action@v0 #octocov 액션 사용
# octocov는 테스트 커버리지를 분석하고 시각화된 리포트를 생성
2 changes: 1 addition & 1 deletion _chapter14/section56/main.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package section56

import (
"context"
Expand Down
2 changes: 1 addition & 1 deletion _chapter14/section56/main_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package section56

import (
"context"
Expand Down
43 changes: 43 additions & 0 deletions _chapter15/.air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
root = '.'
tmp_dir = 'tmp'

[build]
cmd = "go build -o ./tmp/main ." #Go 프로젝트를 빌드
bin = "./tmp/main" #빌드된 실행 파일 경로
#실행 파일 실행 시 필요한 환경 변수와 인자 설정 -> 80번 포트 사용하도록 인수 지정
full_bin = "APP_ENV=dev APP_USER = air ./tmp/main 80"

# 파일 변경 감지 설정 -> 변경을 감지할 파일 확장자
include_ext = ["go", "tpl", "tmpl", "html"]
# 감지하지 않을 디렉터리 목록
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules", "_tools", "cert", "testutil"]
include_dir = []
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = true
exclude_underscore = false
follow_symlink = false

#로깅 설정 : 로그파일 경로, 파일 변경 감지 후 재빌드 대기 시간
log = "air.log"
delay = 1000

#오류 처리 설정
stop_on_error = true
send_interrupt = false
kill_delay = 500

[log]
time = false

[color]
#로그 출력 시 사용할 색상 설정
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"

#프로그램 종료 시 임시 디렉터리 삭제 여부
[misc]
clean_on_exit = true

2 changes: 2 additions & 0 deletions _chapter15/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.git
.DS_Store
42 changes: 42 additions & 0 deletions _chapter15/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#최종 배포할 실행 파일 만드는 과정
#multi-stage build에서 이 단계의 이름을 'deploy-builder'로 지정
FROM golang:1.23.1-bullseye AS deploy-builder

#app 디렉토리 생성 및 작업 디렉토리로 설정
WORKDIR /app

#종속성 파일(go.mod go.sum)만 복사해서 캐시 활용
#소스코드가 변경되어도 종속성이 변경되지 않았다면 도커의 캐시 활용
COPY go.mod go.sum ./
#프로젝트의 모든 종속성 다운로드
RUN go mod download

#전체 소스 파일을 /app으로 복사하고 최적화된 바이너리 생성
COPY . .
RUN go build -trimmpath -ldflags="-w -s" -o app

# 최종 배포 단계 : 실제 운영 환경에서 실행될 최소한의 이미지 생성
#경량화된 Degian 이미지 사용
FROM debian:bullseye-slim AS deploy

#시스템 패키지 최신화
RUN apt-get update

#첫 번째 단계에서 만든 실행 파일만 복사
COPY --from=deploy-builder /app/app .

#컨테이너 시작 시 애플리케이션 자동 실행
CMD ["./app"]

#개발자의 로컬 환경을 위한 설정
FROM golang:1.23 AS dev

#/app 디렉토리를 작업공간으로 설정
WORKDIR /app

#air 도구 설치 (코드 변경 시 자동 재빌드 지원)
RUN go install github.com/air-verse/air@latest

#개발 서버 자동 시작
CMD ["air"]

28 changes: 28 additions & 0 deletions _chapter15/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.PHONY: help build build-local up down logs ps test
.DEFAULT_GOAL := help

DOCKER_TAG := latest
build: ## Build the docker image
docker build -t gleaming9/go_todo_app:${DOCKER_TAG} --target deploy ./

build-local: ## Build the docker image for local development
docker compose build --no-cache

up: ## 자동 새로고침을 사용한 도커 컴포즈 실행
docker compose up -d

down: ## 도커 컴포즈 종료
docker compose down

logs: ## 도커 컴포즈 로그 출력
docker compose logs -f

ps: ## 실행중인 컨테이너 확인
docker compose ps

test: ## 테스트 실행
go test -race -shuffle=on ./...

help: ## 옵션 보기
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
25 changes: 25 additions & 0 deletions _chapter15/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
services:

# 'app'이라는 이름의 서비스(컨테이너)를 정의
app:
# 사용할 Docker 이미지 이름 : 'gotodo'
image: gotodo

build:
#빌드할 파일들이 있는 위치를 지정 : .. <- 한칸 위 폴더에 go.mod 위치
#go_todo_app 폴더를 컨텍스트로 설정
context: ..
dockerfile: _chapter15/Dockerfile
# target=dev로 설정하여 Dockerfile의 dev stage를 빌드
args:
target: dev

# 로컬 컴퓨터의 파일과 컨테이너 안의 파일을 연결, 코드 수정 시 바로 컨테이너에 반영
volumes:
- ../:/app # go_todo_app 루트 디렉터리를 컨테이너의 /app에 마운트

# 컨테이너가 사용할 포트를 지정
ports:
# 웹 브라우저에서 localhost:18000으로 접속하면 컨테이너의 80번 포트로 연결
# 왼쪽은 내 컴퓨터의 포트, 오른쪽은 컨테이너 안의 포트
- "18000:80"
56 changes: 56 additions & 0 deletions _chapter15/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package _chapter15

import (
"context"
"fmt"
"golang.org/x/sync/errgroup"
"log"
"net"
"net/http"
"os"
)

func run(ctx context.Context, l net.Listener) error {
s := &http.Server{
// 인수로 받은 net.Listener를 이용하므로 Addr 필드는 지정하지 않습니다.
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}),
}
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
// Serve 메서드로 변경합니다.
if err := s.Serve(l); err != nil &&
err != http.ErrServerClosed {
log.Printf("failed to close: %+v", err)
return err
}
return nil
})

<-ctx.Done()
if err := s.Shutdown(context.Background()); err != nil {
log.Printf("failed to shutdown: %+v", err)
}

return eg.Wait()
}

func main() {
// go run . 18080
if len(os.Args) != 2 {
log.Printf("need port number\n")
os.Exit(1)
}

p := os.Args[1]
l, err := net.Listen("tcp", ":"+p)
if err != nil {
log.Fatalf("failed to listen port %s: %v", p, err)
}

if err := run(context.Background(), l); err != nil {
log.Printf("failed to terminate server: %v", err)
os.Exit(1)
}
}
53 changes: 53 additions & 0 deletions _chapter15/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package _chapter15

import (
"context"
"fmt"
"golang.org/x/sync/errgroup"
"io"
"net"
"net/http"
"testing"
)

func TestRun(t *testing.T) {
// net/http 에서는 포트 번호에 0을 지정하면 사용 가능한 포트 번호를 동적으로 선택합니다.
l, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("failed to listen port %v", err)
}
ctx, cancel := context.WithCancel(context.Background())
eg, ctx := errgroup.WithContext(ctx)
eg.Go(func() error {
return run(ctx, l)
})

in := "message"
url := fmt.Sprintf("http://%s/%s", l.Addr().String(), in)
// 어떤 포트 번호로 리슨중인지 확인합니다.
t.Logf("try request to %q", url)
rsp, err := http.Get(url)

// 이후 코드 동일
if err != nil {
t.Errorf("failed to get: %+v", err)
}
defer rsp.Body.Close()

got, err := io.ReadAll(rsp.Body)
if err != nil {
t.Fatalf("failed to read body: %v", err)
}

// HTTP 서버의 반환값을 검증합니다.
want := fmt.Sprintf("Hello, %s!", in)
if string(got) != want {
t.Errorf("want %q, but got %q", want, got)
}

// run 함수를 종료합니다.
cancel()
if err := eg.Wait(); err != nil {
t.Fatal(err)
}
}

0 comments on commit c2860ac

Please sign in to comment.