TT Lab
Get started
Learn Learning paths Courses

Building Servers in Go

Build a Server With Only the Standard Library

Continue in TT Lab

Goal

Build an HTTP server with only the standard library and see what Go enforces. Before choosing a framework, check what net/http already gives you.

Why it matters

People new to Go say that if err != nil is tedious. But that length is not a cost; it is the price of explicitness. The type tells you which lines can fail, and if you want to ignore a failure, you have to write down that you are ignoring it.

In this lab you will feel both the cost and the benefit. You will also build on purpose the places where incidents really happen in Go (goroutine leaks, cancellation that is not propagated).

Environment

There is no network, so go get does not work. This lab uses only the standard library, so that is fine; in fact, that is the point of this course.

go mod init <이름>     모듈 시작
go build -o srv .      빌드
go vet ./...           흔한 실수 찾기
gofmt -l .             포맷이 어긋난 파일 나열

Steps

  1. Initialize the module → /root/work/gosrv/go.mod
  2. A /health server
  3. Errors as values (wrapping with %w)
  4. errors.Is/As → /root/work/gosrv/04-errors.txt
  5. JSON response → /root/work/gosrv/05-json.txt
  6. Propagate cancellation with context → /root/work/gosrv/06-ctx.txt
  7. Goroutine leak → /root/work/gosrv/07-leak.txt
  8. go vet / gofmt → /root/work/gosrv/08-vet.txt

Notes

Create the module

Initialize the module → /root/work/gosrv/go.mod

mkdir -p /root/work/gosrv && cd /root/work/gosrv && go mod init labhub.local/gosrv. There is no network, so you cannot download external packages; this lab uses only the standard library. You are done once go.mod exists.

One HTTP server

A /health server

In main.go, use net/http to create /health and have it listen on 8080. Build with go build -o srv ., start it with ./srv &, and check it with curl -s localhost:8080/health. The standard library gives you this much without any framework.

Return errors as values

Errors as values (wrapping with %w)

Write parseAge(s string) (int, error) so that it returns an error when the input is not a number. When you wrap, use %w, as in fmt.Errorf("나이 파싱(%s): %w", s, err). If you wrap with %v, you lose the original and can no longer ask about it with errors.Is.

Ask about the cause

errors.Is/As → /root/work/gosrv/04-errors.txt

Add code that uses errors.Is or errors.As to identify the cause of a wrapped error, and save its output to /root/work/gosrv/04-errors.txt. The goal is to see with your own eyes that you can pull the original out of a wrapped error.

Return JSON

JSON response → /root/work/gosrv/05-json.txt

Respond with a struct using encoding/json. If you do not add field tags (json:"name"), the field names go out capitalized as they are, because of Go's export rule. Save the result of curl -s localhost:8080/api | jq . to /root/work/gosrv/05-json.txt.

Propagate cancellation

Propagate cancellation with context → /root/work/gosrv/06-ctx.txt

Write a slow handler, use r.Context() and watch its Done(), and make it stop working when the client disconnects. Disconnect with curl --max-time 1 and check whether the cancellation is printed in the server log. Save the output to /root/work/gosrv/06-ctx.txt.

See a goroutine leak

Goroutine leak → /root/work/gosrv/07-leak.txt

Create a goroutine that leaks on purpose (send to a channel that has no receiver), print runtime.NumGoroutine() before and after, and save the output to /root/work/gosrv/07-leak.txt. A number that goes up and does not come back down is the leak.

Ask the tools first

go vet / gofmt → /root/work/gosrv/08-vet.txt

Run go vet ./... and gofmt -l ., and save the result to /root/work/gosrv/08-vet.txt. If both are silent, you pass. Go does not argue about formatting: gofmt is the correct answer, and that is why the topic disappears from code reviews.