-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.git | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Build stage | ||
# 최신 버전으로 수정 ****** | ||
FROM golang:1.23.1 AS deploy-builder | ||
|
||
WORKDIR /app | ||
|
||
# go.mod, go.sum 파일 복사 및 의존성 다운로드 | ||
COPY go.mod go.sum ./ | ||
RUN go mod download | ||
|
||
COPY . . | ||
# 바이너리 빌드 | ||
RUN go build -trimpath -ldflags "-w -s" -o app | ||
|
||
#-------------------------------- | ||
# Deployment stage | ||
# 최신 GLIBC 버전을 사용하기 위해 우분투로 교체 | ||
|
||
FROM ubuntu:latest as deploy | ||
|
||
RUN apt-get update && apt-get install -y libgcc-s1 | ||
|
||
# 빌드된 바이너리를 복사 | ||
COPY --from=deploy-builder /app/app . | ||
|
||
# 실행 명령 | ||
CMD ["./app"] | ||
|
||
#-------------------------------- | ||
# Development stage with Air for live reloading | ||
|
||
# ******** 최신 버전으로 수정 *********** | ||
FROM golang:1.23.1 as dev | ||
WORKDIR /app | ||
RUN go install github.com/air-verse/air@latest | ||
CMD ["air"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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: ## 배포용 도커 이미지 빌드 + 내 모듈 이름에 맞게 수정 ********** | ||
docker build -t myeunee/golangstudy-chapter16-section62:${DOCKER_TAG} --target deploy ./ | ||
|
||
build-local: ## 로컬 환경용 도커 이미지 빌드 | ||
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}' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
root = "." | ||
tmp_dir = "tmp" | ||
|
||
[build] | ||
cmd = "go build -o ./tmp/main ." | ||
# 'cmd'에서 바이너리 파일 지정 | ||
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"] | ||
exclude_regex = ["_test.go"] | ||
exclude_unchanged = true | ||
follow_symlink = true | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package config | ||
|
||
import ( | ||
"github.com/caarlos0/env/v6" | ||
) | ||
|
||
type Config struct { | ||
Env string `env:"TODO_ENV" envDefault:"dev"` | ||
Port int `env:"PORT" envDefault:"80"` | ||
} | ||
|
||
func New() (*Config, error) { | ||
cfg := &Config{} | ||
if err := env.Parse(cfg); err != nil { | ||
return nil, err | ||
} | ||
return cfg, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package config | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
func TestNew(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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
version: "3.9" | ||
services: | ||
app: | ||
image: gotodo | ||
build: | ||
args: | ||
- target=dev | ||
environment: | ||
TODO_ENV: dev | ||
PORT: 8080 | ||
volumes: | ||
- .:/app | ||
ports: | ||
- "18000:8080" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module github.com/myeunee/GolangStudy/chapter16/section62 | ||
|
||
go 1.23.1 | ||
|
||
require ( | ||
github.com/caarlos0/env/v6 v6.10.1 | ||
golang.org/x/sync v0.8.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II= | ||
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc= | ||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= | ||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"net" | ||
"os" | ||
|
||
"github.com/myeunee/GolangStudy/chapter16/section62/config" | ||
) | ||
|
||
func main() { | ||
if err := run(context.Background()); err != nil { | ||
log.Printf("failed to terminated server: %v", err) | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
// 리팩토링한 run 함수 | ||
func run(ctx context.Context) error { | ||
cfg, err := config.New() | ||
if err != nil { | ||
return err | ||
} | ||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", cfg.Port)) | ||
if err != nil { | ||
log.Fatalf("failed to listen port %d: %v", cfg.Port, err) | ||
} | ||
url := fmt.Sprintf("http://%s", l.Addr().String()) | ||
log.Printf("start with: %v", url) | ||
mux := NewMux() | ||
s := NewServer(l, mux) | ||
return s.Run(ctx) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package main | ||
|
||
import "net/http" | ||
|
||
func NewMux() http.Handler { | ||
mux := http.NewServeMux() | ||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { | ||
w.Header().Set("Content-Type", "application/json; charset=utf-8") | ||
// 정적 분석 오류를 회피하기 위해 명시적으로 반환값을 버린다. | ||
_, _ = w.Write([]byte(`{"status": "ok"}`)) | ||
}) | ||
return mux | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package main | ||
|
||
import ( | ||
"io" | ||
"net/http" | ||
"net/http/httptest" | ||
"testing" | ||
) | ||
|
||
func TestNewMux(t *testing.T) { | ||
w := httptest.NewRecorder() | ||
r := httptest.NewRequest(http.MethodGet, "/health", nil) | ||
sut := NewMux() | ||
sut.ServeHTTP(w, r) | ||
resp := w.Result() | ||
t.Cleanup(func() { _ = resp.Body.Close() }) | ||
if resp.StatusCode != http.StatusOK { | ||
t.Error("want status code 200, but", resp.StatusCode) | ||
} | ||
got, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
t.Fatalf("failed to read body: %v", err) | ||
} | ||
want := `{"status": "ok"}` | ||
if string(got) != want { | ||
t.Errorf("want %q, but got %q", want, got) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"net" | ||
"net/http" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
|
||
"golang.org/x/sync/errgroup" | ||
) | ||
|
||
type Server struct { | ||
srv *http.Server | ||
l net.Listener | ||
} | ||
|
||
func NewServer(l net.Listener, mux http.Handler) *Server { | ||
return &Server{ | ||
srv: &http.Server{Handler: mux}, | ||
l: l, | ||
} | ||
} | ||
|
||
func (s *Server) Run(ctx context.Context) error { | ||
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) | ||
defer stop() | ||
eg, ctx := errgroup.WithContext(ctx) | ||
eg.Go(func() error { | ||
// http.ErrServerClosed는 | ||
// http.Server.Shutdown()이 정상 종료한 것을 보여주는 것으로 아무 문제 없음 | ||
if err := s.srv.Serve(s.l); err != nil && | ||
err != http.ErrServerClosed { | ||
log.Printf("failed to close: %+v", err) | ||
return err | ||
} | ||
return nil | ||
}) | ||
|
||
<-ctx.Done() | ||
if err := s.srv.Shutdown(context.Background()); err != nil { | ||
log.Printf("failed to shutdown: %+v", err) | ||
} | ||
// 정상 종료를 기다림 | ||
return eg.Wait() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"net" | ||
"net/http" | ||
"testing" | ||
|
||
"golang.org/x/sync/errgroup" | ||
) | ||
|
||
func TestServer_Run(t *testing.T) { | ||
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) | ||
mux := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:]) | ||
}) | ||
|
||
eg.Go(func() error { | ||
s := NewServer(l, mux) | ||
return s.Run(ctx) | ||
}) | ||
|
||
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() | ||
// run함수의 반환값을 검증 | ||
if err := eg.Wait(); err != nil { | ||
t.Fatal(err) | ||
} | ||
} |
Binary file not shown.