Build a Server With Only the Standard Library
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
- Initialize the module →
/root/work/gosrv/go.mod - A
/healthserver - Errors as values (wrapping with
%w) errors.Is/As→/root/work/gosrv/04-errors.txt- JSON response →
/root/work/gosrv/05-json.txt - Propagate cancellation with context →
/root/work/gosrv/06-ctx.txt - Goroutine leak →
/root/work/gosrv/07-leak.txt go vet/gofmt→/root/work/gosrv/08-vet.txt
Notes
- After you start the server, clean it up with
kill %1. If port 8080 stays in use, the next build cannot start. - Error messages start with a lowercase letter and do not end with a period, because they are embedded inside other messages.
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.