標準ライブラリだけでサーバを作る
한국어 원문으로 표시합니다.
목표
표준 라이브러리만으로 HTTP 서버를 만들면서 Go 가 무엇을 강제하는지 봅니다.
프레임워크를 고르기 전에 net/http 가 이미 무엇을 주는지 확인합니다.
왜 중요한가
Go 를 처음 쓰는 사람은 if err != nil 이 지겹다고 말합니다. 그런데 그 길이는
비용이 아니라 명시성의 가격입니다 — 어느 줄이 실패할 수 있는지가 타입에
적혀 있고, 무시하려면 무시한다고 적어야 합니다.
이 실습에서는 그 대가와 이득을 둘 다 만져 봅니다. 그리고 Go 에서 실제로 사고가 나는 자리(고루틴 누수, 취소 미전파)를 직접 만들어 봅니다.
환경
네트워크가 없어 go get 은 동작하지 않습니다. 이 실습은 표준 라이브러리만
쓰므로 상관없습니다 — 오히려 그게 이 코스의 논점입니다.
go mod init <이름> 모듈 시작
go build -o srv . 빌드
go vet ./... 흔한 실수 찾기
gofmt -l . 포맷이 어긋난 파일 나열
단계
- 모듈 초기화 →
/root/work/gosrv/go.mod /health서버- 에러를 값으로 (
%w로 감싸기) errors.Is/As→/root/work/gosrv/04-errors.txt- JSON 응답 →
/root/work/gosrv/05-json.txt - context 로 취소 전파 →
/root/work/gosrv/06-ctx.txt - 고루틴 누수 →
/root/work/gosrv/07-leak.txt go vet/gofmt→/root/work/gosrv/08-vet.txt
참고
- 서버를 띄운 뒤에는
kill %1로 정리합니다. 8080 이 물려 있으면 다음 빌드가 못 뜹니다. - 에러 메시지는 소문자로 시작하고 마침표로 끝내지 않습니다 — 다른 메시지 안에 끼워 넣어지기 때문입니다.
모듈을 만든다
모듈 초기화 → /root/work/gosrv/go.mod
mkdir -p /root/work/gosrv && cd /root/work/gosrv && go mod init labhub.local/gosrv. 네트워크가 없으므로 외부 패키지는 못 받습니다 — 이 실습은 표준 라이브러리만 씁니다. go.mod 가 생기면 됩니다.
HTTP 서버 한 개
/health 서버
main.go 에 net/http 로 /health 를 만들고 8080 에서 듣게 합니다. go build -o srv . 로 빌드한 뒤 ./srv & 로 띄우고 curl -s localhost:8080/health 로 확인하세요. 프레임워크 없이 이만큼이 표준 라이브러리에 있습니다.
에러를 값으로 돌려준다
에러를 값으로 (%w 로 감싸기)
parseAge(s string) (int, error) 를 만들어 숫자가 아니면 에러를 돌려주게 합니다. 감쌀 때는 fmt.Errorf("나이 파싱(%s): %w", s, err) 처럼 %w 를 쓰세요. %v 로 감싸면 원본을 잃어 errors.Is 로 물어볼 수 없게 됩니다.
원인을 물어본다
errors.Is/As → /root/work/gosrv/04-errors.txt
errors.Is 또는 errors.As 로 감싼 에러의 원인을 판별하는 코드를 넣고, 그 출력을 /root/work/gosrv/04-errors.txt 에 저장하세요. 감싼 에러에서 원본을 꺼낼 수 있다는 것을 눈으로 확인하는 것이 목적입니다.
JSON 을 돌려준다
JSON 응답 → /root/work/gosrv/05-json.txt
encoding/json 으로 구조체를 응답합니다. 필드 태그(json:"name")를 붙이지 않으면 대문자 그대로 나갑니다 — Go 의 export 규칙 때문입니다. curl -s localhost:8080/api | jq . 결과를 /root/work/gosrv/05-json.txt 에 저장하세요.
취소를 전파한다
context 로 취소 전파 → /root/work/gosrv/06-ctx.txt
느린 핸들러를 만들고 r.Context() 의 Done() 을 감시해 클라이언트가 끊으면 일을 멈추게 하세요. curl --max-time 1 로 끊고 서버 로그에 취소가 찍히는지 봅니다. 출력을 /root/work/gosrv/06-ctx.txt 에 저장하세요.
고루틴이 새는 것을 본다
고루틴 누수 → /root/work/gosrv/07-leak.txt
일부러 새는 고루틴(받는 쪽 없는 채널로 보내기)을 만들고 runtime.NumGoroutine() 을 전후로 찍어 /root/work/gosrv/07-leak.txt 에 저장하세요. 숫자가 늘어난 채로 안 줄어드는 것이 누수입니다.
도구에게 먼저 물어본다
go vet / gofmt → /root/work/gosrv/08-vet.txt
go vet ./... 과 gofmt -l . 을 돌려 결과를 /root/work/gosrv/08-vet.txt 에 저장하세요. 둘 다 조용하면 통과입니다. Go 는 포맷을 논쟁하지 않습니다 — gofmt 가 정답이고, 그래서 리뷰에서 그 얘기가 사라집니다.