Skip to content

Commit

Permalink
feat: chapter18
Browse files Browse the repository at this point in the history
  • Loading branch information
gleaming9 committed Nov 17, 2024
1 parent e451b1c commit 464c459
Show file tree
Hide file tree
Showing 50 changed files with 1,593 additions and 9 deletions.
20 changes: 18 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,21 @@ jobs:
test:
# Ubuntu 최신 버전을 실행 환경으로 사용
runs-on: ubuntu-latest

services:
mysql:
image: mysql:8
options: >-
--health-cmd "mysqladmin ping -h localhost"
--health-interval 20s
--health-timeout 10s
--health-retries 10
ports:
- 3306:3306
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: todo
MYSQL_USER: todo
MYSQL_PASSWORD: todo
steps:
# Go 언어 설치 및 설정
- uses: actions/setup-go@v3
Expand All @@ -23,7 +37,9 @@ jobs:

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

- run: |
go install github.com/sqldef/sqldef/cmd/mysqldef@latest
mysqldef -u todo -p todo -h 127.0.0.1 -P 3306 todo < ./_tools/mysql/schema.sql
# Go 테스트 실행
- run: go test ./... -coverprofile=coverage.out
# ./... : 현재 디렉토리와 모든 하위 디렉토리의 테스트 실행
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ ps: ## 실행중인 컨테이너 확인
test: ## 테스트 실행
go test -race -shuffle=on ./...

dry-migrate: ## 마이그레이션 실행 전 미리보기
mysqldef -u todo -p todo -h 127.0.0.1 -P 33306 todo --dry-run < ./_tools/mysql/schema.sql

migrate: ## 마이그레이션 실행
mysqldef -u todo -p todo -h 127.0.0.1 -P 33306 todo < ./_tools/mysql/schema.sql

help: ## 옵션 보기
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
43 changes: 43 additions & 0 deletions _chapter18/.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 _chapter18/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.git
.DS_Store
34 changes: 34 additions & 0 deletions _chapter18/.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"
51 changes: 51 additions & 0 deletions _chapter18/.github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# 워크플로우 트리거 설정
on:
# main 브랜치에 푸시할 때만 워크플로우가 실행되도록 설정
push:
branches:
- "main"
# 모든 브랜츠에서 PR이 생성되거나 업데이트 될 때 실행
pull_request:

# GitHub Actions UI에서 표시될 워크플로우 이름
name: test
#실행할 작업들을 정의
jobs:
test:
# Ubuntu 최신 버전을 실행 환경으로 사용
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8
options: >-
--health-cmd "mysqladmin ping -h localhost"
--health-interval 20s
--health-timeout 10s
--health-retries 10
ports:
- 3306:3306
env:
MYSQL_ALLOW_EMPTY_PASSWORD: yes
MYSQL_DATABASE: todo
MYSQL_USER: todo
MYSQL_PASSWORD: todo
steps:
# Go 언어 설치 및 설정
- uses: actions/setup-go@v3
with:
go-version: '>=1.18'

# 레포지토리 코드 체크아웃
- uses: actions/checkout@v3 # 현재 레포지토리의 코드를 가져오는 액션
- run: |
go install github.com/sqldef/sqldef/cmd/mysqldef@latest
mysqldef -u todo -p todo -h 127.0.0.1 -P 3306 todo < ./_tools/mysql/schema.sql
# Go 테스트 실행
- run: go test ./... -coverprofile=coverage.out
# ./... : 현재 디렉토리와 모든 하위 디렉토리의 테스트 실행
# -coverprofile=coverage.out : 테스트 결과를 coverage.out 파일에 저장

# 테스트 커버리지 리포트 생성
- name: report coverage
uses: k1LoW/octocov-action@v0 #octocov 액션 사용
# octocov는 테스트 커버리지를 분석하고 시각화된 리포트를 생성
43 changes: 43 additions & 0 deletions _chapter18/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#최종 배포할 실행 파일 만드는 과정
#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 -trimpath -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"]

## docker-compose down -v <= 실행중인 컨테이너를 멈추고 매핑된 볼륨들을 제거
35 changes: 35 additions & 0 deletions _chapter18/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.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 ./...

dry-migrate: ## 마이그레이션 실행 전 미리보기
mysqldef -u todo -p todo -h 127.0.0.1 -P 33306 todo --dry-run < ./_tools/mysql/schema.sql

migrate: ## 마이그레이션 실행
mysqldef -u todo -p todo -h 127.0.0.1 -P 33306 todo < ./_tools/mysql/schema.sql

help: ## 옵션 보기
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
2 changes: 2 additions & 0 deletions _chapter18/_tools/mysql/conf.d/mysql.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[mysql]
default_character_set=utf8mb4
4 changes: 4 additions & 0 deletions _chapter18/_tools/mysql/conf.d/mysqld.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[mysqld]
default-authentication-plugin=mysql_native_password
charater_set_server=utf8mb4
sql_mode=TRADIIONAL,NO_AUTO_VALUE_ON_ZERO,ONLY_FULL_GROUP_BY
21 changes: 21 additions & 0 deletions _chapter18/_tools/mysql/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CREATE TABLE `user`
(
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '사용자 식별자',
`name` varchar(20) NOT NULL COMMENT '사용자명',
`password` VARCHAR(80) NOT NULL COMMENT '패스워드 해시',
`role` VARCHAR(80) NOT NULL COMMENT '역할',
`created` DATETIME(6) NOT NULL COMMENT '레코드 작성 시간',
`modified` DATETIME(6) NOT NULL COMMENT '레코드 수정 시간',
PRIMARY KEY (`id`),
UNIQUE KEY `uix_name` (`name`) USING BTREE
) Engine=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='사용자';

CREATE TABLE `task`
(
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '태스크 식별자',
`title` VARCHAR(128) NOT NULL COMMENT '태스크 타이틀',
`status` VARCHAR(20) NOT NULL COMMENT '태스크 상태',
`created` DATETIME(6) NOT NULL COMMENT '레코드 작성 시간',
`modified` DATETIME(6) NOT NULL COMMENT '레코드 수정 시간',
PRIMARY KEY (`id`)
) Engine=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='태스크';
19 changes: 19 additions & 0 deletions _chapter18/clock/clock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package clock

import "time"

type Clocker interface {
Now() time.Time
}

type RealClock struct{}

func (r RealClock) Now() time.Time {
return time.Now()
}

type FixedClocker struct{}

func (fc FixedClocker) Now() time.Time {
return time.Date(2022, 5, 10, 12, 34, 56, 0, time.UTC)
}
24 changes: 24 additions & 0 deletions _chapter18/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package config

import "github.com/caarlos0/env/v6"

// Config 구조체는 애플리케이션 설정을 위한 구조체이다.
// 환경 변수에서 값을 가져와서 필드를 초기화한다.
type Config struct {
Env string `env:"TODO_ENV" envDefault:"dev"`
Port int `env:"PORT" envDefault:"80"`
DBHost string `env:"TODO_DB_HOST" envDefault:"127.0.0.1"`
DBPort int `env:"TODO_DB_PORT" envDefault:"33306"`
DBUser string `env:"TODO_DB_USER" envDefault:"todo"`
DBPassword string `env:"TODO_DB_PASSWORD" envDefault:"todo"`
DBName string `env:"TODO_DB_NAME" envDefault:"todo"`
}

// New 함수는 Config 구조체를 생성하고 환경 변수를 파싱하여 필드를 초기화
func New() (*Config, error) {
cfg := &Config{} // config 구조체 초기화
if err := env.Parse(cfg); err != nil {
return nil, err
}
return cfg, nil
}
23 changes: 23 additions & 0 deletions _chapter18/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package config

import (
"fmt"
"testing"
)

func TestNeow(t *testing.T) {
wantPort := 3333
t.Setenv("PORT", fmt.Sprint(wantPort))

got, err := New()
if err != nil {
t.Fatalf("cannot create config: %v", err)
}
if got.Port != wantPort {
t.Errorf("want %d, but %d", wantPort, got.Port)
}
wantEnv := "dev"
if got.Env != wantEnv {
t.Errorf("want %s, but %s", wantEnv, got.Env)
}
}
Loading

0 comments on commit 464c459

Please sign in to comment.