API์ฉ Go ์ปจํ ์คํธ ํ์์์: HTTP ํธ๋ค๋ฌ์์ SQL๊น์ง
API์์ Go ์ปจํ ์คํธ ํ์์์์ HTTP ํธ๋ค๋ฌ์์ SQL ํธ์ถ๋ก ๊ธฐํ์ ์ ๋ฌํด ๋ฉ์ถฐ๋ฒ๋ฆฌ๋ ์์ฒญ์ ๋ง๊ณ ๋ถํ ์์๋ ์๋น์ค๋ฅผ ์์ ์ ์ผ๋ก ์ ์งํ๋๋ก ๋์ต๋๋ค.

์์ฒญ์ด ๋ฉ์ถ๋ ์ด์ (๊ทธ๋ฆฌ๊ณ ๋ถํ ์ ์ ํด๋ก์ด๊ฐ)\n\n์์ฒญ์ด "๋ฉ์ท๋ค(stuck)"๋ ๊ฒ์ ๋ฐํํ์ง ์๋ ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์์ ๋ ๋ฐ์ํฉ๋๋ค: ๋๋ฆฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ, ํ์์ ๋งํ ์ฐ๊ฒฐ, DNS ๋ฌธ์ , ๋๋ ํธ์ถ์ ๋ฐ์์ง๋ง ์๋ต์ ํ์ง ์๋ ์์ ์๋น์ค ๋ฑ์
๋๋ค.\n\n์ฆ์์ ๋จ์ํด ๋ณด์
๋๋ค: ์ผ๋ถ ์์ฒญ์ด ์์ํ ๊ฑธ๋ฆฌ๊ณ , ๊ทธ ๋ค๋ก ์ ์ ๋ ๋ง์ ์์ฒญ์ด ์์
๋๋ค. ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ฆ๊ฐํ๊ณ ๊ณ ๋ฃจํด ์๊ฐ ๋๋ฉฐ, ์ฝ๊ฒ ๋น์์ง์ง ์๋ ์ด๋ฆฐ ์ฐ๊ฒฐ ๋๊ธฐ์ด์ ๋ณด๊ฒ ๋ฉ๋๋ค.\n\n๋ถํ๊ฐ ๊ฑธ๋ฆฌ๋ฉด ๋ฉ์ถ ์์ฒญ์ ์ด์ค์ผ๋ก ํด๋ฅผ ๋ผ์นฉ๋๋ค. ์์
์๋ฅผ ๋ฌถ์ด๋๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ์ด๋ ๋ฝ ๊ฐ์ ๊ทํ ์์์ ์ ์ ํฉ๋๋ค. ๊ทธ ๋๋ฌธ์ ๋ณดํต ๋น ๋ฅธ ์์ฒญ๋ ๋๋ ค์ง๊ณ , ๊ฒน์นจ์ด ์๊ธฐ๋ฉฐ ๊ธฐ๋ค๋ฆผ์ด ๋ ๋์ด๋ฉ๋๋ค.\n\n์ฌ์๋์ ํธ๋ํฝ ์คํ์ดํฌ๋ ์ด ์
์ํ์ ์
ํ์ํต๋๋ค. ํด๋ผ์ด์ธํธ๊ฐ ํ์์์๋์ด ์ฌ์๋ํ๋ ๋์ ์๋ ์์ฒญ์ ์ฌ์ ํ ์คํ ์ค์ผ ์ ์์ผ๋ฏ๋ก ๊ฐ์ ์์
์ ๋ํด ๋ ๋ฐฐ ๋น์ฉ์ ์ง๋ถํ๊ฒ ๋ฉ๋๋ค. ์งง์ ์ง์ฐ ๋์ ์ฌ๋ฌ ํด๋ผ์ด์ธํธ๊ฐ ์ฌ์๋ํ๋ฉด ํ๊ท ํธ๋ํฝ์ด ๊ด์ฐฎ๋๋ผ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๊ณผ๋ถํ์ํค๊ฑฐ๋ ์ฐ๊ฒฐ ํ๋์ ๋๋ฌํ ์ ์์ต๋๋ค.\n\nํ์์์์ ๋จ์ํ ์ฝ์์
๋๋ค: "์ฐ๋ฆฌ๋ X ์ด์ ๊ธฐ๋ค๋ฆฌ์ง ์๊ฒ ๋ค." ์ด๋ ๋น ๋ฅด๊ฒ ์คํจ(fail fast)ํ๊ณ ์์์ ํด์ ํ๋ ๋ฐ ๋์์ด ๋์ง๋ง ์์
์ ๋ ๋นจ๋ฆฌ ๋๋๊ฒ ํ์ง๋ ์์ต๋๋ค.\n\n๋ํ ์์
์ด ์ฆ์ ์ค๋จ๋๋ค๋ ๋ณด์ฅ๋ ์๋๋๋ค. ์๋ฅผ ๋ค์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ๊ณ์ ์คํํ ์ ์๊ณ , ์์ ์๋น์ค๋ ์ทจ์๋ฅผ ๋ฌด์ํ ์ ์์ผ๋ฉฐ, ์ฝ๋ ์์ฒด๊ฐ ์ทจ์๊ฐ ๋ฐ์ํ์ ๋ ์์ ํ์ง ์์ ์ ์์ต๋๋ค.\n\nํ์์์์ด ๋ณด์ฅํ๋ ๊ฒ์ ํธ๋ค๋ฌ๊ฐ ๊ธฐ๋ค๋ฆผ์ ๋ฉ์ถ๊ณ ๋ช
ํํ ์ค๋ฅ๋ฅผ ๋ฐํํ๋ฉฐ ๋ณด์ ํ ์์์ ํด์ ํ ์ ์๋ค๋ ์ ์
๋๋ค. ์ด ์ ํ๋ ๋๊ธฐ๋ ๋ช ๋ฒ์ ๋๋ฆฐ ํธ์ถ์ด ์ ์ฒด ์ฅ์ ๋ก ๋ฒ์ง๋ ๊ฒ์ ๋ง์์ค๋๋ค.\n\nGo ์ปจํ
์คํธ ํ์์์์ ๋ชฉํ๋ ์์ง์์ ๊ฐ์ฅ ๊น์ ํธ์ถ๊น์ง ํ๋์ ๊ณต์ ๋ ๋ฐ๋๋ผ์ธ์ ๊ฐ๋ ๊ฒ์
๋๋ค. HTTP ๊ฒฝ๊ณ์์ ํ ๋ฒ ์ค์ ํ๊ณ ๋์ผํ ์ปจํ
์คํธ๋ฅผ ์๋น์ค ์ฝ๋ ์ ๋ฐ์ ์ ๋ฌํ๋ฉฐ database/sql ํธ์ถ์๋ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ธ์ ๊ธฐ๋ค๋ฆผ์ ๋ฉ์ถฐ์ผ ํ๋์ง ์๊ฒ ๋ฉ๋๋ค.\n\n## Go์ ์ปจํ
์คํธ๋ฅผ ์ฌ์ด ์ฉ์ด๋ก\n\ncontext.Context๋ ํ์ฌ ๋ฌด์จ ์ผ์ด ์งํ ์ค์ธ์ง ์ค๋ช
ํ๊ธฐ ์ํด ์ฝ๋ ์ ์ฒด์ ์ ๋ฌํ๋ ์์ ๊ฐ์ฒด์
๋๋ค. "์ด ์์ฒญ์ด ์์ง ์ ํจํ๊ฐ?", "์ธ์ ํฌ๊ธฐํด์ผ ํ๋?", "์์ฒญ ๋ฒ์์ ์ด๋ค ๊ฐ์ด ์ด ์์
๊ณผ ํจ๊ป ๊ฐ์ผ ํ๋?" ๊ฐ์ ์ง๋ฌธ์ ๋ตํฉ๋๋ค.\n\nํต์ฌ ์ฅ์ ์ ์์คํ
์ ๊ฐ์ฅ์๋ฆฌ(HTTP ํธ๋ค๋ฌ)์์ ๋ด๋ฆฐ ํ ๋ฒ์ ๊ฒฐ์ ์ด ๋์ผํ ์ปจํ
์คํธ๋ฅผ ์ ๋ฌํ๋ฉด ๋ชจ๋ ํ์ ๋จ๊ณ๋ค์ ๋ณดํธํ ์ ์๋ค๋ ์ ์
๋๋ค.\n\n### ์ปจํ
์คํธ๊ฐ ๋ด๋ ๊ฒ\n\n์ปจํ
์คํธ๋ ๋น์ฆ๋์ค ๋ฐ์ดํฐ๋ฅผ ์ํ ์ฅ์๊ฐ ์๋๋๋ค. ์ ์ด ์ ํธ์ ์๋์ ์์ฒญ ๋ฒ์ ์ ๋ณด: ์ทจ์, ๋ฐ๋๋ผ์ธ/ํ์์์, ๋ก๊ทธ์ฉ ์์ฒญ ID ๊ฐ์ ์์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ํ ๊ฒ์
๋๋ค.\n\nํ์์์๊ณผ ์ทจ์์ ์ฐจ์ด๋ ๊ฐ๋จํฉ๋๋ค: ํ์์์์ ์ทจ์์ ํ ์ด์ ์
๋๋ค. ์๋ฅผ ๋ค์ด 2์ด ํ์์์์ ์ค์ ํ๋ฉด 2์ด๊ฐ ์ง๋๋ฉด ์ปจํ
์คํธ๋ ์ทจ์๋ฉ๋๋ค. ํ์ง๋ง ์ฌ์ฉ์๊ฐ ํญ์ ๋ซ๊ฑฐ๋ ๋ก๋๋ฐธ๋ฐ์๊ฐ ์ฐ๊ฒฐ์ ๋๊ฑฐ๋ ์ฝ๋๊ฐ ์์ฒญ์ ์ค๋จํ๊ธฐ๋ก ๊ฒฐ์ ํ๋ฉด ์ปจํ
์คํธ๋ ๋ ์ผ์ฐ ์ทจ์๋ ์๋ ์์ต๋๋ค.\n\n์ปจํ
์คํธ๋ ๋ณดํต ์ฒซ ๋ฒ์งธ ๋งค๊ฐ๋ณ์๋ก ๋ช
์์ ์ผ๋ก ์ ๋ฌ๋์ด ํจ์ ํธ์ถ์ ๋ฐ๋ผ ํ๋ฆ
๋๋ค: func DoThing(ctx context.Context, ...). ํธ์ถ ์ง์ ์ ๊ณ์ ๋ณด์ด๊ธฐ ๋๋ฌธ์ "์์ด๋ฒ๋ฆฌ๊ธฐ" ์ด๋ ต์ต๋๋ค.\n\n๋ฐ๋๋ผ์ธ์ด ๋ง๋ฃ๋๋ฉด ๊ทธ ์ปจํ
์คํธ๋ฅผ ๊ด์ฐฐํ๋ ๊ฒ์ ๋น ๋ฅด๊ฒ ๋ฉ์ถฐ์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด QueryContext๋ฅผ ์ฌ์ฉํ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๋ context deadline exceeded ๊ฐ์ ์ค๋ฅ์ ํจ๊ป ์กฐ๊ธฐ์ ๋ฐํํด์ผ ํ๊ณ , ํธ๋ค๋ฌ๋ ์๋ฒ๊ฐ ์์
์๋ฅผ ๋ชจ๋ ์์งํ ๋๊น์ง ๋งค๋ฌ๋ฆฌ์ง ์๊ณ ํ์์์์ผ๋ก ์๋ตํ ์ ์์ด์ผ ํฉ๋๋ค.\n\n์ข์ ์ฌ๊ณ ๋ชจ๋ธ์ ์ด๋ ์ต๋๋ค: ํ๋์ ์์ฒญ, ํ๋์ ์ปจํ
์คํธ, ์ด๋๋ ์ ๋ฌํ์ธ์. ์์ฒญ์ด ์ฃฝ์ผ๋ฉด ์์
๋ ํจ๊ป ๋ฉ์ถฐ์ผ ํฉ๋๋ค.\n\n## HTTP ๊ฒฝ๊ณ์์ ๋ช
ํํ ๋ฐ๋๋ผ์ธ ์ค์ ํ๊ธฐ\n\n์๋ํฌ์๋ ํ์์์์ ์๋์ํค๋ ค๋ฉด ์๊ณ๊ฐ ์ด๋์ ์์๋๋์ง ๊ฒฐ์ ํด์ผ ํฉ๋๋ค. ๊ฐ์ฅ ์์ ํ ์์น๋ HTTP ์์ง ๋ฐ๋ก ์ง์ ์
๋๋ค. ๊ทธ๋ฌ๋ฉด ๋ชจ๋ ํ์ ํธ์ถ(๋น์ฆ๋์ค ๋ก์ง, SQL, ๋ค๋ฅธ ์๋น์ค)์ด ๋์ผํ ๋ฐ๋๋ผ์ธ์ ์์๋ฐ์ต๋๋ค.\n\n๊ทธ ๋ฐ๋๋ผ์ธ์ ์ฌ๋ฌ ๊ณณ์์ ์ค์ ํ ์ ์์ต๋๋ค. ์๋ฒ ์์ค ํ์์์์ ์ข์ ๊ธฐ๋ณธ ๋ณดํธ๋ง์ด๊ณ ๋๋ฆฐ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ณดํธํด ์ค๋๋ค. ๋ฏธ๋ค์จ์ด๋ ๋ผ์ฐํธ ๊ทธ๋ฃน ์ ๋ฐ์ ์ผ๊ด์ฑ์ ์ฃผ๊ธฐ์ ํ๋ฅญํฉ๋๋ค. ํธ๋ค๋ฌ ๋ด๋ถ์์ ์ค์ ํ๋ ๊ฒ๋ ๋ช
ํํ๊ณ ๊ตญ์์ ์ธ ์ค์ ์ด ํ์ํ ๋ ๊ด์ฐฎ์ต๋๋ค.\n\n๋๋ถ๋ถ์ API์์๋ ๋ฏธ๋ค์จ์ด๋ ํธ๋ค๋ฌ์์์ ์์ฒญ๋ณ ํ์์์์ด ๊ฐ์ฅ ์ดํดํ๊ธฐ ์ฝ์ต๋๋ค. ํ์ค์ ์ผ๋ก ์ค์ ํ์ธ์: ์ฌ์ฉ์๋ ๋๋ฆฌ๊ฒ ๋งค๋ฌ๋ฆฌ๋ ๊ฒ๋ณด๋ค ๋น ๋ฅด๊ณ ๋ช
ํํ ์คํจ๋ฅผ ์ ํธํฉ๋๋ค. ๋ง์ ํ์ ์ฝ๊ธฐ ์์
์ ๋ํด ์งง์ ์์ฐ(์: 1โ2์ด)์, ์ฐ๊ธฐ์๋ ์ข ๋ ๊ธด ์์ฐ(์: 3โ10์ด)์ ์ฌ์ฉํฉ๋๋ค.\n\n๊ฐ๋จํ ํธ๋ค๋ฌ ํจํด์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:\n\ngo\nfunc (s *Server) getReport(w http.ResponseWriter, r *http.Request) {\n ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)\n defer cancel()\n\n report, err := s.reports.Generate(ctx, r.URL.Query().Get(\"id\"))\n if err != nil {\n http.Error(w, err.Error(), http.StatusGatewayTimeout)\n return\n }\n\n json.NewEncoder(w).Encode(report)\n}\n\n\n์ด ํจํด์ ํจ๊ณผ์ ์ผ๋ก ์ ์งํ๋ ๋ ๊ฐ์ง ๊ท์น:\n\n- ํญ์ cancel()์ ํธ์ถํด ํ์ด๋จธ์ ์์์ด ๋น ๋ฅด๊ฒ ํด์ ๋๋๋ก ํ์ธ์.\n- ํธ๋ค๋ฌ ์์์ ์์ฒญ ์ปจํ
์คํธ๋ฅผ context.Background()๋ context.TODO()๋ก ๋์ฒดํ์ง ๋ง์ธ์. ๊ทธ๋ฌ๋ฉด ์ฒด์ธ์ด ๋๊ฒจ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ์ถ๊ณผ ์ธ๋ถ ์์ฒญ์ด ํด๋ผ์ด์ธํธ๊ฐ ๋ ๋ ๋ค์๋ ์์ํ ์คํ๋ ์ ์์ต๋๋ค.\n\n## ์ฝ๋๋ฒ ์ด์ค ์ ๋ฐ์ ์ปจํ
์คํธ ์ ํํ๊ธฐ\n\nHTTP ๊ฒฝ๊ณ์์ ๋ฐ๋๋ผ์ธ์ ์ค์ ํ ํ ์ค์ ์์
์ ๊ทธ ๋์ผํ ๋ฐ๋๋ผ์ธ์ด ๋ธ๋กํนํ ์ ์๋ ๋ชจ๋ ๊ณ์ธต์ ๋๋ฌํ๋๋ก ํ๋ ๊ฒ์
๋๋ค. ์์ด๋์ด๋ ํ๋์ ์๊ณ๊ฐ ํธ๋ค๋ฌ, ์๋น์ค ์ฝ๋, ๋คํธ์ํฌ๋ ๋์คํฌ์ ์ ๊ทผํ๋ ๋ชจ๋ ๊ณณ์์ ๊ณต์ ๋๋ ๊ฒ์
๋๋ค.\n\n์ผ๊ด์ฑ์ ์ ์งํ๋ ๊ฐ๋จํ ๊ท์น: ๊ธฐ๋ค๋ฆด ์ ์๋ ๋ชจ๋ ํจ์๋ context.Context๋ฅผ ๋ฐ์์ผ ํ๊ณ , ์ด๊ฒ์ด ์ฒซ ๋ฒ์งธ ์ธ์๊ฐ ๋์ด์ผ ํฉ๋๋ค. ํธ์ถ ์ง์ ์์ ๋ช
ํํด์ง๊ณ ์ต๊ด์ด ๋ฉ๋๋ค.\n\n### ์ค์ฉ์ ์ธ ์๊ทธ๋์ฒ ํจํด\n\n์๋น์ค์ ๋ ํฌ์งํ ๋ฆฌ์๋ DoThing(ctx context.Context, ...) ๊ฐ์ ์๊ทธ๋์ฒ๋ฅผ ์ ํธํ์ธ์. ์ปจํ
์คํธ๋ฅผ ๊ตฌ์กฐ์ฒด ์์ ์จ๊ธฐ๊ฑฐ๋ ํ์ ๋ ์ด์ด์์ context.Background()๋ก ๋ค์ ๋ง๋ค์ง ๋ง์ธ์. ๊ทธ๋ฌ๋ฉด ํธ์ถ์์ ๋ฐ๋๋ผ์ธ์ด ์กฐ์ฉํ ์ฌ๋ผ์ง๋๋ค.\n\ngo\nfunc (h *Handler) CreateOrder(w http.ResponseWriter, r *http.Request) {\n ctx := r.Context()\n\n if err := h.svc.CreateOrder(ctx, r.Body); err != nil {\n // map context errors to a clear client response elsewhere\n http.Error(w, err.Error(), http.StatusRequestTimeout)\n return\n }\n}\n\nfunc (s *Service) CreateOrder(ctx context.Context, body io.Reader) error {\n // parsing or validation can still respect cancellation\n select {\n case <-ctx.Done():\n return ctx.Err()\n default:\n }\n\n return s.repo.InsertOrder(ctx, /* data */)\n}\n\n\n### ์กฐ๊ธฐ ์ข
๋ฃ๋ฅผ ๊น๋ํ๊ฒ ์ฒ๋ฆฌํ๊ธฐ\n\nctx.Done()๋ฅผ ์ ์์ ์ธ ์ ์ด ๊ฒฝ๋ก๋ก ์ทจ๊ธํ์ธ์. ๋์์ด ๋๋ ๋ ๊ฐ์ง ์ต๊ด:\n\n- ๋น์ฉ์ด ํฐ ์์
์ ์์ํ๊ธฐ ์ ๊ณผ ๊ธด ๋ฃจํ ํ์ ctx.Err()๋ฅผ ํ์ธํ์ธ์.\n- ctx.Err()๋ฅผ ์์ชฝ์ผ๋ก ๋ณ๊ฒฝ ์์ด ๋ฐํํด ํธ๋ค๋ฌ๊ฐ ๋น ๋ฅด๊ฒ ์๋ตํ๊ณ ์์ ๋ญ๋น๋ฅผ ๋ฉ์ถ๊ฒ ํ์ธ์.\n\n๋ชจ๋ ๋ ์ด์ด๊ฐ ๋์ผํ ctx๋ฅผ ์ ๋ฌํ๋ฉด ํ๋์ ํ์์์์ผ๋ก ํ์ฑ, ๋น์ฆ๋์ค ๋ก์ง, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๊ธฐ๊ฐ ํ ๋ฒ์ ์ฐจ๋จ๋ ์ ์์ต๋๋ค.\n\n## database/sql ์ฟผ๋ฆฌ์ ๋ฐ๋๋ผ์ธ ์ ์ฉํ๊ธฐ\n\nHTTP ํธ๋ค๋ฌ์ ๋ฐ๋๋ผ์ธ์ด ์ค์ ๋์๋ค๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์
์ด ์ค์ ๋ก ๊ทธ ์ ํธ๋ฅผ ๋ฃ๋์ง ํ์ธํ์ธ์. database/sql์์๋ ๋งค๋ฒ ์ปจํ
์คํธ ์ธ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์ปจํ
์คํธ ์์ด Query()๋ Exec()๋ฅผ ํธ์ถํ๋ฉด ํด๋ผ์ด์ธํธ๊ฐ ํฌ๊ธฐํ ๋ค์๋ API๊ฐ ๋๋ฆฐ ์ฟผ๋ฆฌ๋ฅผ ๊ณ์ ๊ธฐ๋ค๋ฆด ์ ์์ต๋๋ค.\n\n์ผ๊ด๋๊ฒ ์ฌ์ฉํ์ธ์: db.QueryContext, db.QueryRowContext, db.ExecContext, db.PrepareContext(๊ทธ๋ฆฌ๊ณ ๋ฐํ๋ statement์์ QueryContext/ExecContext).\n\ngo\nfunc (s *Store) GetUser(ctx context.Context, id int64) (*User, error) {\n row := s.db.QueryRowContext(ctx,\n `SELECT id, email FROM users WHERE id = $1`, id,\n )\n var u User\n if err := row.Scan(&u.ID, &u.Email); err != nil {\n return nil, err\n }\n return &u, nil\n}\n\nfunc (s *Store) UpdateEmail(ctx context.Context, id int64, email string) error {\n _, err := s.db.ExecContext(ctx,\n `UPDATE users SET email = $1 WHERE id = $2`, email, id,\n )\n return err\n}\n\n\n๋์น๊ธฐ ์ฌ์ด ๋ ๊ฐ์ง๊ฐ ์์ต๋๋ค.\n\n์ฒซ์งธ, SQL ๋๋ผ์ด๋ฒ๊ฐ ์ปจํ
์คํธ ์ทจ์๋ฅผ ์กด์คํด์ผ ํฉ๋๋ค. ๋ง์ ๋๋ผ์ด๋ฒ๊ฐ ๊ทธ๋ ์ง๋ง, ์คํ์์ ๋๋ฆฐ ์ฟผ๋ฆฌ๋ฅผ ์๋์ ์ผ๋ก ์คํํด ๋ฐ๋๋ผ์ธ ์ด๊ณผ ์ ๋น ๋ฅด๊ฒ ์ทจ์๋๋์ง ํ์ธํ์ธ์.\n\n๋์งธ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ธก ํ์์์์ ๋ฐฑ์คํฑ์ผ๋ก ๊ณ ๋ คํ์ธ์. ์๋ฅผ ๋ค์ด Postgres๋ ์ข
์ข
statement timeout์ผ๋ก ๋ถ๋ฆฌ๋ ๋ฌธ์ฅ๋ณ ์ ํ์ ์ ์ฉํ ์ ์์ต๋๋ค. ์ด๋ ์ฑ ๋ฒ๊ทธ๋ก ์ปจํ
์คํธ ์ ๋ฌ์ด ๋๋ฝ๋ ๊ฒฝ์ฐ์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ๋ณดํธํฉ๋๋ค.\n\n์์
์ด ํ์์์ ๋๋ฌธ์ ์ค๋จ๋๋ฉด ์ผ๋ฐ SQL ์ค๋ฅ์ ๋ค๋ฅด๊ฒ ์ฒ๋ฆฌํ์ธ์. errors.Is(err, context.DeadlineExceeded)์ errors.Is(err, context.Canceled)๋ฅผ ํ์ธํ๊ณ (๊ฐ๋ฅํ๋ฉด) 504 ๊ฐ์ ๋ช
ํํ ์๋ต์ผ๋ก ๋งคํํ์ธ์. ์ด๋ ๊ฒ ํ๋ฉด ๋ฌธ์ ๋ฅผ "๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ๋ง๊ฐ์ก๋ค"๊ณ ์๋ชป ์ฒ๋ฆฌํ์ง ์๊ฒ ๋ฉ๋๋ค. AppMaster์ฒ๋ผ Go ๋ฐฑ์๋๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ์๋ ์ด ์ค๋ฅ ๊ฒฝ๋ก๋ฅผ ๊ตฌ๋ถํ๋ฉด ๋ก๊ทธ์ ์ฌ์๋ ์ ์ฑ
์ ์ดํดํ๊ธฐ ์ฌ์์ง๋๋ค.\n\n## ํ์ ํธ์ถ: HTTP ํด๋ผ์ด์ธํธ, ์บ์, ๊ธฐํ ์๋น์ค\n\nํธ๋ค๋ฌ์ SQL ์ฟผ๋ฆฌ๊ฐ ์ปจํ
์คํธ๋ฅผ ์กด์คํ๋๋ผ๋ ํ์ ํธ์ถ์ด ์์ํ ๊ธฐ๋ค๋ฆฌ๋ฉด ์์ฒญ์ด ์ฌ์ ํ ๊ฑธ๋ฆด ์ ์์ต๋๋ค. ๋ถํ๊ฐ ๊ฑธ๋ฆฌ๋ฉด ๋ช ๊ฐ์ง ๊ฑธ๋ฆฐ ๊ณ ๋ฃจํด์ด ์์ฌ ์ฐ๊ฒฐ ํ์ด ๊ณ ๊ฐ๋๊ณ ์์ ์ง์ฐ์ด ์ ์ฒด ์ ์ง๋ก ๋ฒ์ง ์ ์์ต๋๋ค. ํด๊ฒฐ์ฑ
์ ์ผ๊ด๋ ์ ํ์ ๋ช
ํํ ๋ฐฑ์คํฑ์
๋๋ค.\n\n### ์์๋ฐ์ด๋ HTTP\n\n๋ค๋ฅธ API๋ฅผ ํธ์ถํ ๋ ๋์ผํ ์ปจํ
์คํธ๋ก ์์ฒญ์ ์์ฑํ๋ฉด ๋ฐ๋๋ผ์ธ๊ณผ ์ทจ์๊ฐ ์๋์ผ๋ก ์ ๋ฌ๋ฉ๋๋ค.\n\ngo\nreq, err := http.NewRequestWithContext(ctx, "GET", url, nil)\nif err != nil { /* handle */ }\nresp, err := httpClient.Do(req)\n\n\n์ปจํ
์คํธ๋ง ๋ฏฟ์ง ๋ง์ธ์. ์ฝ๋๊ฐ ์ค์๋ก background ์ปจํ
์คํธ๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ DNS/TLS/์ ํด ์ฐ๊ฒฐ์ด ๋ฉ์ถ ๊ฒฝ์ฐ์ ๋๋นํด HTTP ํด๋ผ์ด์ธํธ์ ํธ๋์คํฌํธ๋ฅผ ๊ตฌ์ฑํ์ธ์. http.Client.Timeout์ ์ ์ฒด ํธ์ถ์ ์ํ์ผ๋ก ์ค์ ํ๊ณ , ๋ค์ด์ผ/ํธ๋์
ฐ์ดํฌ/์๋ต ํค๋ ํ์์์์ ์ค์ ํ๋ฉฐ ์์ฒญ๋ง๋ค ์ ํด๋ผ์ด์ธํธ๋ฅผ ๋ง๋ค์ง ๋ง๊ณ ์ฌ์ฌ์ฉํ์ธ์.\n\n### ์บ์์ ํ\n\n์บ์, ๋ฉ์์ง ๋ธ๋ก์ปค, RPC ํด๋ผ์ด์ธํธ๋ ์ฐ๊ฒฐ ํ๋, ์๋ต ๋๊ธฐ, ๊ฐ๋ ์ฐฌ ํ์์ ๋ธ๋กํน, ๋ฝ ๋๊ธฐ ๊ฐ์ ์์ฒด ๋๊ธฐ ์ง์ ์ ๊ฐ์ง๋๋ค. ์ด๋ฌํ ์์
์ด ctx๋ฅผ ๋ฐ๋์ง ํ์ธํ๊ณ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ค ํ์์์์ด ์๋ค๋ฉด ํจ๊ป ์ฌ์ฉํ์ธ์.\n\n์ค์ฉ์ ์ธ ๊ท์น: ์ฌ์ฉ์์ ์์ฒญ์ 800ms๋ง ๋จ์๋ค๋ฉด 2์ด ๊ฑธ๋ฆด ์ ์๋ ํ์ ํธ์ถ์ ์์ํ์ง ๋ง์ธ์. ๊ฑด๋๋ฐ๊ฑฐ๋ ์ดํ(degrade)ํ๊ฑฐ๋ ์ ํ์ ํ๋์ ๋ํด ๋ถ๋ถ ์๋ต์ ๋ฐํํ์ธ์.\n\nํ์์์์ด API์์ ๋ฌด์์ ์๋ฏธํ๋์ง ๋ฏธ๋ฆฌ ๊ฒฐ์ ํ์ธ์. ๋๋ก๋ ๋น ๋ฅธ ์ค๋ฅ๊ฐ ์ณ๊ณ , ๋๋ก๋ ์ ํ์ ํ๋์ ๋ํด ๋ถ๋ถ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๊ฑฐ๋ ์บ์์ ์ค๋๋ ๋ฐ์ดํฐ๋ฅผ ๋ช
ํํ ํ์ํ๋ ๊ฒ์ด ์ณ์ต๋๋ค.\n\nAppMaster ๊ฐ์ ๋๊ตฌ๋ก Go ๋ฐฑ์๋๋ฅผ ์์ฑํ๋ ๊ฒฝ์ฐ(์์ฑ๋ ์ฝ๋ ํฌํจ) ์ด ์ฐจ์ด๋ "ํ์์์์ด ์๋ค"์ "ํ์์์์ด ํธ๋ํฝ ๊ธ์ฆ ์์๋ ์์คํ
์ ์ผ๊ด๋๊ฒ ๋ณดํธํ๋ค"์ ์ฐจ์ด์
๋๋ค.\n\n## ๋จ๊ณ๋ณ: ์๋ํฌ์๋ ํ์์์์ผ๋ก API ๋ฆฌํฉํฐ๋งํ๊ธฐ\n\nํ์์์์ ์ํ ๋ฆฌํฉํฐ๋ง์ ํ ๊ฐ์ง ์ต๊ด์ผ๋ก ๊ท๊ฒฐ๋ฉ๋๋ค: HTTP ์์ง์์ ๋์ผํ context.Context๋ฅผ ๋ฐ์ ๋ธ๋กํนํ ์ ์๋ ๋ชจ๋ ํธ์ถ๊น์ง ์ ๋ฌํ์ธ์.\n\n์ค์ฉ์ ์ธ ์์
์์๋ ์์์ ์๋๋ก์
๋๋ค:\n\n- ํธ๋ค๋ฌ์ ํต์ฌ ์๋น์ค ๋ฉ์๋๊ฐ ctx context.Context๋ฅผ ๋ฐ๋๋ก ๋ณ๊ฒฝํ์ธ์.\n- ๋ชจ๋ DB ํธ์ถ์ QueryContext๋ ExecContext๋ก ์
๋ฐ์ดํธํ์ธ์.\n- ์ธ๋ถ ํธ์ถ(HTTP ํด๋ผ์ด์ธํธ, ์บ์, ํ)๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ฒ๋ฆฌํ์ธ์. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ctx๋ฅผ ๋ฐ์ง ์์ผ๋ฉด ๋ํํ๊ฑฐ๋ ๊ต์ฒดํ์ธ์.\n- ํ์์์์ ์์ ์๋ฅผ ๊ฒฐ์ ํ์ธ์. ์ผ๋ฐ ๊ท์น: ํธ๋ค๋ฌ๊ฐ ์ ์ฒด ๋ฐ๋๋ผ์ธ์ ์ค์ ํ๊ณ , ํ์ ๋ ์ด์ด๋ ํ์ํ ๋๋ง ๋ ์งง์ ๋ฐ๋๋ผ์ธ์ ์ค์ ํฉ๋๋ค.\n- ์์ง์์๋ context.DeadlineExceeded์ context.Canceled๋ฅผ ๋ช
ํํ HTTP ์๋ต์ผ๋ก ๋งคํํด ์ค๋ฅ๊ฐ ์์ธก ๊ฐ๋ฅํ๊ฒ ํ์ธ์.\n\n๋ ์ด์ด ์ ๋ฐ์ ๊ฑธ์น ํํ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:\n\ngo\nfunc (h *Handler) GetOrder(w http.ResponseWriter, r *http.Request) {\n ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)\n defer cancel()\n\n order, err := h.svc.GetOrder(ctx, r.PathValue(\"id\"))\n if errors.Is(err, context.DeadlineExceeded) {\n http.Error(w, \"request timed out\", http.StatusGatewayTimeout)\n return\n }\n if err != nil {\n http.Error(w, \"internal error\", http.StatusInternalServerError)\n return\n }\n _ = json.NewEncoder(w).Encode(order)\n}\n\nfunc (r *Repo) GetOrder(ctx context.Context, id string) (Order, error) {\n row := r.db.QueryRowContext(ctx, `SELECT id,total FROM orders WHERE id=$1`, id)\n // scan...\n}\n\n\nํ์์์ ๊ฐ์ ์ง๋ฃจํ๊ฒ(์ผ๊ด๋๊ฒ) ์ค์ ํ์ธ์. ํธ๋ค๋ฌ์ ์ด 2์ด๊ฐ ์๋ค๋ฉด DB ์ฟผ๋ฆฌ๋ JSON ์ธ์ฝ๋ฉ๊ณผ ๊ธฐํ ์์
์ ์ํ ์ฌ์ ๋ฅผ ๋จ๊ฒจ 1์ด ์ดํ๋ก ์ ์งํ์ธ์.\n\n์๋์ ์ฆ๋ช
ํ๋ ค๋ฉด ํ์์์์ ๊ฐ์ ํ๋ ํ
์คํธ๋ฅผ ์ถ๊ฐํ์ธ์. ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ctx.Done()๊น์ง ๋ธ๋กํ ๋ค ctx.Err()๋ฅผ ๋ฐํํ๋ ํ์ดํฌ ๋ ํฌ๋ฅผ ๋ง๋๋ ๊ฒ์
๋๋ค. ํ
์คํธ๋ ํธ๋ค๋ฌ๊ฐ ํ์ดํฌ ์ง์ฐ ์ดํ๊ฐ ์๋๋ผ ๋น ๋ฅด๊ฒ 504์ ๋ฐํํ๋์ง ํ์ธํด์ผ ํฉ๋๋ค.\n\nAppMaster๋ก Go ๋ฐฑ์๋๋ฅผ ์์ฑํ๋ค๋ฉด ๊ท์น์ ๋์ผํฉ๋๋ค: ํ๋์ ์์ฒญ ์ปจํ
์คํธ๋ฅผ ๋ชจ๋ ๊ณณ์ ์ฐ๊ณ ๋ฐ๋๋ผ์ธ ์์ ๊ถ์ ๋ช
ํํ ํ์ธ์.\n\n## ๊ด์ฐฐ์ฑ: ํ์์์์ด ์๋ํ๋์ง ์ฆ๋ช
ํ๊ธฐ\n\nํ์์์์ ์ค์ ๋ก ์ผ์ด๋๋ ๊ฒ์ ๋ณผ ์ ์์ด์ผ๋ง ๋์์ด ๋ฉ๋๋ค. ๋ชฉํ๋ ๊ฐ๋จํฉ๋๋ค: ๋ชจ๋ ์์ฒญ์ ๋ฐ๋๋ผ์ธ์ด ์๊ณ ์คํจํ์ ๋ ์๊ฐ์ด ์ด๋์ ์๋ชจ๋์๋์ง ์ ์ ์์ด์ผ ํฉ๋๋ค.\n\n๋ฌดํดํ๋ฉด์๋ ์ ์ฉํ ๋ก๊ทธ๋ถํฐ ์์ํ์ธ์. ์ ์ฒด ์์ฒญ ๋ณธ๋ฌธ์ ๋คํํ๋ ๋์ ์์ฒญ ID(๋๋ ํธ๋ ์ด์ค ID), ๋ฐ๋๋ผ์ธ์ด ์ค์ ๋์ด ์๋์ง์ ์ฃผ์ ์ง์ ์์ ๋จ์ ์๊ฐ, ์์
์ด๋ฆ(ํธ๋ค๋ฌ, SQL ์ฟผ๋ฆฌ ์ด๋ฆ, ์์๋ฐ์ด๋ ํธ์ถ ์ด๋ฆ), ๊ฒฐ๊ณผ ๋ถ๋ฅ(ok, timeout, canceled, other error) ์ ๋๋ง ๊ธฐ๋กํ๋ฉด ๋๋ฆฐ ๊ฒฝ๋ก๋ฅผ ์ถ์ ํ๊ธฐ ์ถฉ๋ถํฉ๋๋ค.\n\nํ๋์ ๋ช
ํํ ๋ณด์ฌ์ฃผ๋ ๋ช ๊ฐ์ง ์ง์ค๋ ๋ฉํธ๋ฆญ์ ์ถ๊ฐํ์ธ์:\n\n- ์๋ํฌ์ธํธ ๋ฐ ์ข
์์ฑ๋ณ ํ์์์ ์นด์ดํธ\n- ์์ฒญ ์ง์ฐ(p50/p95/p99)\n- ์ธํ๋ผ์ดํธ ์์ฒญ ์\n- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์ง์ฐ(p95/p99)\n- ์ค๋ฅ์จ(์ ํ๋ณ ๋ถ๋ฆฌ)\n\n์ค๋ฅ๋ฅผ ์ฒ๋ฆฌํ ๋ ์ฌ๋ฐ๋ฅด๊ฒ ํ๊น
ํ์ธ์. context.DeadlineExceeded๋ ๋ณดํต ์์ฐ์ ์ด๊ณผํ๋ค๋ ์๋ฏธ์ด๊ณ , context.Canceled๋ ํด๋ผ์ด์ธํธ๊ฐ ๋ ๋ฌ๊ฑฐ๋ ์์ ํ์์์์ด ๋จผ์ ๋ฐ์ํ๋ค๋ ๋ป์ผ ์ ์์ต๋๋ค. ๋์ ์์ธ๊ณผ ํด๊ฒฐ์ฑ
์ด ๋ค๋ฅด๋ฏ๋ก ๋ถ๋ฆฌํด์ ๊ธฐ๋กํ์ธ์.\n\n### ํธ๋ ์ด์ฑ: ์๊ฐ์ด ์ด๋๋ก ๊ฐ๋์ง ์ฐพ๊ธฐ\n\nํธ๋ ์ด์ค ์คํฌ์ HTTP ํธ๋ค๋ฌ์์ database/sql์ QueryContext ํธ์ถ๊น์ง ๋์ผํ ์ปจํ
์คํธ๋ฅผ ๋ฐ๋ผ์ผ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด ์์ฒญ์ด 2์ด์์ ํ์์์ ๋๊ณ ํธ๋ ์ด์ค ์์ DB ์ฐ๊ฒฐ ๋๊ธฐ์์ 1.8์ด๊ฐ ์์๋ ๊ฒ์ด ๋ณด์ด๋ฉด ์ด๋ ์ฟผ๋ฆฌ ํ
์คํธ ๋ฌธ์ ๊ฐ ์๋๋ผ ํ ํฌ๊ธฐ๋ ๋๋ฆฐ ํธ๋์ญ์
๋ฌธ์ ๋ฅผ ๊ฐ๋ฆฌํต๋๋ค.\n\nํ์์์๋ณ ๋์๋ณด๋(์๋ํฌ์ธํธ๋ณ ํ์์์, ์์ ๋๋ฆฐ ์ฟผ๋ฆฌ ๋ฑ)๋ฅผ ๋ด๋ถ์ ์ผ๋ก ๊ตฌ์ถํ๋ฉด ํ๊ท๋ฅผ ๋นจ๋ฆฌ ๋ฐ๊ฒฌํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค. AppMaster ๊ฐ์ ๋
ธ์ฝ๋ ๋๊ตฌ๋ ๊ด์ฐฐ์ฑ ๊ธฐ๋ฅ์ ๋ณ๋ ์์ง๋์ด๋ง ํ๋ก์ ํธ๋ก ์งํํ์ง ์๊ณ ๋ ๋น ๋ฅด๊ฒ ๋ฐฐํฌํ ๋ ์ ์ฉํ ์ ์์ต๋๋ค.\n\n## ํ์์์์ ๋ฌด๋ ฅํํ๋ ํํ ์ค์๋ค\n\n๋๋ถ๋ถ์ "๊ฐ๋ ์ฌ์ ํ ๋ฉ์ถ๋ค" ๋ฒ๊ทธ๋ ๋ช ๊ฐ์ง ์์ ์ค์์์ ์ต๋๋ค.\n\n- ์ค๊ฐ์ ์๊ณ๋ฅผ ์ฌ์ค์ ํ๋ ๊ฒ. ํธ๋ค๋ฌ๊ฐ 2์ด ๋ฐ๋๋ผ์ธ์ ์ค์ ํ๋๋ฐ ๋ ํฌ์งํ ๋ฆฌ๊ฐ ์์ฒด ํ์์์์ ์๋ก ๋ง๋ค๊ฑฐ๋(๋๋ ์์ ํ์์์ ์์ด) ํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ํด๋ผ์ด์ธํธ๊ฐ ๋ ๋ ๋ค์๋ ์คํ๋ ์ ์์ต๋๋ค. ๋ค์ด์จ ctx๋ฅผ ๊ทธ๋๋ก ์ ๋ฌํ๊ณ , ๋ถ๋ช
ํ ์ด์ ๊ฐ ์์ ๋๋ง ๋ ์งง๊ฒ ์กฐ์ด๋ ์์ ์ปจํ
์คํธ๋ฅผ ๋ง๋์ธ์.\n- ๋ฉ์ถ์ง ์๋ ๊ณ ๋ฃจํด ์์. context.Background()๋ก ์์
์ ์์ํ๊ฑฐ๋ ctx๋ฅผ ๋ฒ๋ฆฌ๋ฉด ์์ฒญ์ด ์ทจ์๋ ๋ค์๋ ์์
์ด ๊ณ์ ์คํ๋ฉ๋๋ค. ๊ณ ๋ฃจํด์ ์์ฒญ ctx๋ฅผ ์ ๋ฌํ๊ณ select๋ก ctx.Done()์ ๊ฐ์ํ์ธ์.\n- ์ค์ ํธ๋ํฝ์ ๋นํด ๋๋ฌด ์งง์ ๋ฐ๋๋ผ์ธ. 50ms ํ์์์์ ๋ก์ปฌ์์๋ ๊ด์ฐฎ์ ๋ณด์ฌ๋ ํ๋ก๋์
์ ์์ ์คํ์ดํฌ์์๋ ์คํจ๋ฅผ ์ด๋ํด ์ฌ์๋์ ์ถ๊ฐ ๋ถํ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์ ์ ์ง์ฐ๊ณผ ์ฌ์ ๋ฅผ ๊ณ ๋ คํด ํ์์์์ ์ ํ์ธ์.\n- ์ค์ ์ค๋ฅ๋ฅผ ์จ๊ธฐ๋ ๊ฒ. context.DeadlineExceeded๋ฅผ ์ผ๋ฐ 500์ผ๋ก ์ฒ๋ฆฌํ๋ฉด ๋๋ฒ๊น
๊ณผ ํด๋ผ์ด์ธํธ ๋์์ด ์
ํ๋ฉ๋๋ค. ํ์์์ ์๋ต์ ๋ช
ํํ ํ๊ณ "ํด๋ผ์ด์ธํธ๊ฐ ์ทจ์ํจ"๊ณผ "์์ฐ ์ด๊ณผ๋ก ํ์์์๋จ"์ ์ฐจ์ด๋ฅผ ๋ก๊ทธ์ ๋จ๊ธฐ์ธ์.\n- ์กฐ๊ธฐ ์ข
๋ฃ ์ ์์ ๋ซ๊ธฐ ๋๋ฝ. ์กฐ๊ธฐ ๋ฐํ ์์๋ defer rows.Close()์ context.WithTimeout์ cancel ํธ์ถ์ ํ์คํ ํ์ธ์. ์ ์ถ๋ rows๋ ๋จ์ ์์
์ ๋ถํ ์ ์ฐ๊ฒฐ์ ๊ณ ๊ฐ์ํฌ ์ ์์ต๋๋ค.\n\n๊ฐ๋จํ ์: ์๋ํฌ์ธํธ๊ฐ ๋ฆฌํฌํธ ์ฟผ๋ฆฌ๋ฅผ ํธ๋ฆฌ๊ฑฐํฉ๋๋ค. ์ฌ์ฉ์๊ฐ ํญ์ ๋ซ์ผ๋ฉด ํธ๋ค๋ฌ ctx๋ ์ทจ์๋ฉ๋๋ค. ๋ง์ฝ SQL ํธ์ถ์ด background ์ปจํ
์คํธ๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ฟผ๋ฆฌ๋ ๊ณ์ ์คํ๋์ด ์ฐ๊ฒฐ์ ์ก์๋๊ณ ๋ชจ๋๋ฅผ ๋๋ฆฌ๊ฒ ํฉ๋๋ค. ๋์ผํ ctx๋ฅผ QueryContext์ ์ ๋ฌํ๋ฉด ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ์ถ์ด ์ค๋จ๋์ด ์์คํ
์ด ๋ ๋นจ๋ฆฌ ํ๋ณตํฉ๋๋ค.\n\n## ์์ ์ ์ธ ํ์์์ ๋์์ ์ํ ๋น ๋ฅธ ์ฒดํฌ๋ฆฌ์คํธ\n\nํ์์์์ ์ผ๊ด๋์ด์ผ๋ง ๋์์ด ๋ฉ๋๋ค. ํ๋์ ๋๋ฝ๋ ํธ์ถ์ด ๊ณ ๋ฃจํด์ ๋ฐ์๊ฒ ํ๊ณ DB ์ฐ๊ฒฐ์ ์ก์๋๋ฉฐ ๋ค์ ์์ฒญ๋ค์ ๋๋ฆฌ๊ฒ ๋ง๋ญ๋๋ค.\n\n- ์์ง(๋๊ฐ HTTP ํธ๋ค๋ฌ)์์ ํ๋์ ๋ช
ํํ ๋ฐ๋๋ผ์ธ์ ์ค์ ํ์ธ์. ์์ฒญ ๋ด๋ถ์ ๋ชจ๋ ๊ฒ์ ์ด๋ฅผ ์์ํด์ผ ํฉ๋๋ค.\n- ์๋น์ค์ ๋ ํฌ์งํ ๋ฆฌ ๋ ์ด์ด์ ๋์ผํ ctx๋ฅผ ์ ๋ฌํ์ธ์. ์์ฒญ ์ฝ๋์์ context.Background()๋ฅผ ํผํ์ธ์.\n- DB์์๋ ํญ์ ์ปจํ
์คํธ ์ธ์ ๋ฉ์๋(QueryContext, QueryRowContext, ExecContext)๋ฅผ ์ฌ์ฉํ์ธ์.\n- ์์๋ฐ์ด๋ ํธ์ถ(HTTP, ์บ์, ํ)์๋ ๋์ผํ ctx๋ฅผ ๋ถ์ด์ธ์. ์์ ์ปจํ
์คํธ๋ฅผ ๋ง๋ ๋ค๋ฉด ๋ ๊ธธ๊ฒ ํ์ง ๋ง๊ณ ๋ ์งง๊ฒ ๋ง๋์ธ์.\n- ์ทจ์์ ํ์์์์ ์ผ๊ด๋๊ฒ ์ฒ๋ฆฌํ์ธ์: ๋ช
ํํ ์ค๋ฅ๋ฅผ ๋ฐํํ๊ณ ์์
์ ๋ฉ์ถ๋ฉฐ ์ทจ์๋ ์์ฒญ ๋ด์์ ์ฌ์๋ ๋ฃจํ๋ฅผ ๋ง๋ค์ง ๋ง์ธ์.\n\n๊ทธ๋ค์ ๋ถํ ํ
์คํธ๋ก ๋์์ ๊ฒ์ฆํ์ธ์. ํ์์์์ด ํธ๋ฆฌ๊ฑฐ๋์ง๋ง ์์์ ์ถฉ๋ถํ ๋นจ๋ฆฌ ํด์ ํ์ง ๋ชปํ๋ฉด ์ฌ์ ํ ์ ๋ขฐ์ฑ์ด ๋จ์ด์ง๋๋ค.\n\n๋์๋ณด๋๋ ํ์์์์ ํ๊ท ๊ฐ ์์ ์จ๊ธฐ์ง ๋ง๊ณ ๋ช
ํํ ๋ณด์ฌ์ค์ผ ํฉ๋๋ค. ๋ช ๊ฐ์ง ์ ํธ๋ฅผ ์ถ์ ํ๋ฉด "๋ฐ๋๋ผ์ธ์ด ์ค์ ๋ก ๊ฐ์ ๋๊ณ ์๋๊ฐ?"๋ผ๋ ์ง๋ฌธ์ ๋ตํ ์ ์์ต๋๋ค: ์์ฒญ ํ์์์๊ณผ DB ํ์์์(๋ณ๊ฐ), ์ง์ฐ ๋ฐฑ๋ถ์์(p95/p99), DB ํ ํต๊ณ(์ฌ์ฉ ์ค ์ฐ๊ฒฐ ์, ๋๊ธฐ ํ์, ๋๊ธฐ ์๊ฐ), ๊ทธ๋ฆฌ๊ณ ์ค๋ฅ ์์ธ ๋ถํด(context deadline exceeded ๋ ๊ธฐํ ์คํจ).\n\nAppMaster ๊ฐ์ ํ๋ซํผ์ผ๋ก ๋ด๋ถ ๋๊ตฌ๋ฅผ ๋ง๋ ๋ค๋ฉด ๋์ผํ ์ฒดํฌ๋ฆฌ์คํธ๋ ๋ชจ๋ Go ์๋น์ค์ ์ ์ฉ๋ฉ๋๋ค: ๊ฒฝ๊ณ์์ ๋ฐ๋๋ผ์ธ์ ์ ์ํ๊ณ , ์ ๋ฌํ๊ณ , ๋ฉํธ๋ฆญ์ผ๋ก ํ์ธํ์ธ์.\n\n## ์์ ์๋๋ฆฌ์ค์ ๋ค์ ๋จ๊ณ\n\n์ด๊ฒ ํจ๊ณผ๋ฅผ ๋ณด๋ ์ผ๋ฐ์ ์ธ ๊ณณ์ ๊ฒ์ ์๋ํฌ์ธํธ์
๋๋ค. ์๋ฅผ ๋ค์ด GET /search?q=printer๊ฐ ํฐ ๋ฆฌํฌํธ ์ฟผ๋ฆฌ๋ก DB๊ฐ ๋ฐ์ ๋ ๋๋ ค์ง๋ค๊ณ ๊ฐ์ ํด๋ณด์ธ์. ๋ฐ๋๋ผ์ธ์ด ์๋ค๋ฉด ๋ค์ด์ค๋ ๊ฐ ์์ฒญ์ด ๊ธด SQL ์ฟผ๋ฆฌ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉฐ ๋๊ธฐํฉ๋๋ค. ๋ถํ๊ฐ ๊ฑธ๋ฆฌ๋ฉด ์ด๋ฐ ๋ฉ์ถ ์์ฒญ๋ค์ด ์์ฌ ๊ณ ๋ฃจํด๊ณผ ์ฐ๊ฒฐ์ ๋ฌถ์ด๋์ด API ์ ์ฒด๊ฐ ์ ์งํ ๊ฒ์ฒ๋ผ ๋๊ปด์ง ์ ์์ต๋๋ค.\n\nHTTP ํธ๋ค๋ฌ์์ ๋ช
ํํ ๋ฐ๋๋ผ์ธ์ ์ค์ ํ๊ณ ๋์ผํ ctx๋ฅผ ๋ ํฌ์งํ ๋ฆฌ๊น์ง ์ ๋ฌํ๋ฉด ์์ฐ์ด ์์ง๋ ๋ ์์คํ
์ ๊ธฐ๋ค๋ฆผ์ ๋ฉ์ถฅ๋๋ค. ๋ฐ๋๋ผ์ธ์ด ๋๋ฌํ๋ฉด DB ๋๋ผ์ด๋ฒ๊ฐ(์ง์๋๋ ๊ฒฝ์ฐ) ์ฟผ๋ฆฌ๋ฅผ ์ทจ์ํ๊ณ ํธ๋ค๋ฌ๋ ๋ฐํํ๋ฉฐ, ์๋ฒ๋ ์ค๋๋ ์์ฒญ๋ค ๋๋ฌธ์ ์๋ก์ด ์์ฒญ์ ์๋นํ์ง ๋ชปํ๋ ๋์ ๊ณ์ ์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.\n\n๋ฌธ์ ๊ฐ ๋ฐ์ํด๋ ์ฌ์ฉ์์๊ฒ ๋ณด์ด๋ ๋์์ด ๋ ๋์์ง๋๋ค. 30~120์ด ๋์ ๋ฉ์ถฐ์๋ค๊ฐ ์์ฑํ๊ฒ ์คํจํ๋ ๋์ ํด๋ผ์ด์ธํธ๋ ๋น ๋ฅด๊ณ ์์ธก ๊ฐ๋ฅํ ์ค๋ฅ(๋ณดํต 504 ๋๋ ๊ฐ๋จํ ๋ฉ์์ง "request timed out")๋ฅผ ๋ฐ์ต๋๋ค. ๋ ์ค์ํ๊ฒ๋ ์์คํ
์ด ๋นจ๋ฆฌ ํ๋ณตํฉ๋๋ค.\n\n์ด ๊ท์น์ ์๋ํฌ์ธํธ์ ํ ์ ๋ฐ์ ์ ์ฐฉ์ํค๊ธฐ ์ํ ๋ค์ ๋จ๊ณ ์ ์:\n\n- ์๋ํฌ์ธํธ ์ ํ๋ณ ํ์ค ํ์์์(๊ฒ์ vs ์ฐ๊ธฐ vs ๋ด๋ณด๋ด๊ธฐ)์ ์ ํ์ธ์.\n- ์ฝ๋ ๋ฆฌ๋ทฐ์์ QueryContext์ ExecContext ์ฌ์ฉ์ ์๊ตฌํ์ธ์.\n- ๊ฐ์ฅ์๋ฆฌ์์ ํ์์์ ์ค๋ฅ๋ฅผ ๋ช
ํํ(์ํ ์ฝ๋, ๊ฐ๋จํ ๋ฉ์์ง) ์ฒ๋ฆฌํ์ธ์.\n- ํ์์์๊ณผ ์ทจ์์ ๋ํ ๋ฉํธ๋ฆญ์ ์ถ๊ฐํด ํ๊ท๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ์ธ์.\n- ๋ชจ๋ ํธ๋ค๋ฌ๊ฐ ๋์ผํ๊ฒ ๋์ํ๋๋ก ์ปจํ
์คํธ ์์ฑ๊ณผ ๋ก๊น
์ ๊ฐ์ธ๋ ํฌํผ๋ฅผ ํ๋ ์์ฑํ์ธ์.\n\nAppMaster๋ก ์๋น์ค์ ๋ด๋ถ ๋๊ตฌ๋ฅผ ๊ตฌ์ถํ๋ค๋ฉด ์ด๋ฌํ ํ์์์ ๊ท์น์ ์์ฑ๋ Go ๋ฐฑ์๋, API ํตํฉ, ๋์๋ณด๋ ์ ๋ฐ์ ์ผ๊ด๋๊ฒ ์ ์ฉํ ์ ์์ต๋๋ค. AppMaster๋ appmaster.io์ ์ ๊ณต๋๋ฉฐ(๋
ธ์ฝ๋, ์ค์ Go ์์ค ์ฝ๋ ์์ฑ), ์๋์ผ๋ก ๋ชจ๋ ๊ด๋ฆฌ์ ๋๊ตฌ๋ฅผ ์ง์ ๋ง๋ค์ง ์๊ณ ๋ ์ผ๊ด๋ ์์ฒญ ์ฒ๋ฆฌ์ ๊ด์ฐฐ์ฑ์ ํ๋ณดํ ๋ ์ค์ฉ์ ์ธ ์ ํ์ด ๋ ์ ์์ต๋๋ค.
์์ฃผ ๋ฌป๋ ์ง๋ฌธ
์์ฒญ์ด โ๋ฉ์ถฐ๋ฒ๋ ธ๋ค(stuck)โ๋ ๊ฒ์ ๋๋ฆฐ SQL ์ฟผ๋ฆฌ, ํ์์ ๋งํ ์ฐ๊ฒฐ, DNS ๋ฌธ์ , ๋๋ ์๋ตํ์ง ์๋ ์์ ์๋น์ค์ฒ๋ผ ๋ฐํํ์ง ์๋ ๋ฌด์ธ๊ฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๊ณ ์์ ๋๋ฅผ ๋งํฉ๋๋ค. ๋ถํ๊ฐ ๊ฑธ๋ฆฌ๋ฉด ์ด๋ฐ ๋ฉ์ถ ์์ฒญ๋ค์ด ์์ฌ ์์ ์์ ์ฐ๊ฒฐ์ ๋ฌถ์ด๋๊ณ ์์ ์ง์ฐ์ด ์ ์ฒด ์ฅ์ ๋ก ๋ฒ์ง ์ ์์ต๋๋ค.
์ ์ฒด ๋ฐ๋๋ผ์ธ์ HTTP ๊ฒฝ๊ณ์์ ์ค์ ํ๊ณ ๊ทธ ๊ฐ์ ctx๋ฅผ ๋ธ๋กํนํ ์ ์๋ ๋ชจ๋ ๋ ์ด์ด๋ก ์ ๋ฌํ์ธ์. ์ด ๊ณต์ ๋ ๋ฐ๋๋ผ์ธ์ด ๋ช ๋ฒ์ ๋๋ฆฐ ์์
์ด ์์์ ์ค๋ ์ ์ ํด ์ ๋ฐ์ ์ธ ํ์์์์ผ๋ก ์ด์ด์ง๋ ๊ฒ์ ๋ง์ต๋๋ค.
ctx, cancel := context.WithTimeout(r.Context(), d)๋ฅผ ์ฌ์ฉํ๊ณ ํธ๋ค๋ฌ(๋๋ ๋ฏธ๋ค์จ์ด)์์ ํญ์ defer cancel()์ ํธ์ถํ์ธ์. cancel ํธ์ถ์ ํ์ด๋จธ๋ฅผ ํด์ ํ๊ณ ์์ฒญ์ด ์ผ์ฐ ๋๋ฌ์ ๋ ๊ธฐ๋ค๋ฆผ์ ๋นจ๋ฆฌ ๋ฉ์ถ๋๋ก ๋์ต๋๋ค.
์์ฒญ ์ฝ๋์์ context.Background()๋ context.TODO()๋ก ๋์ฒดํ๋ฉด ์ทจ์์ ๋ฐ๋๋ผ์ธ ์ฒด์ธ์ด ๋๊น๋๋ค. ๊ทธ๋ฌ๋ฉด SQL์ด๋ ์ธ๋ถ HTTP ๊ฐ์ ํ์ ์์
์ด ํด๋ผ์ด์ธํธ๊ฐ ๋ ๋ ๋ค์๋ ๊ณ์ ์คํ๋์ด ํ์์์์ ๋ฌด์ฉ์ง๋ฌผ๋ก ๋ง๋ญ๋๋ค.
context.DeadlineExceeded์ context.Canceled๋ ์ ์์ ์ธ ์ ์ด ๊ฒฐ๊ณผ๋ก ์ทจ๊ธํด ์์๋ก ๊ทธ๋๋ก ์ ๋ฌํ์ธ์. ๊ฐ์ฅ์๋ฆฌ์์๋ ๋ณดํต ํ์์์์ ๋ํด 504 ๊ฐ์ ๋ช
ํํ ์๋ต์ผ๋ก ๋งคํํด ํด๋ผ์ด์ธํธ๊ฐ ๋ฌด์์ ์ฌ์๋ํ์ง ์๋๋ก ํฉ๋๋ค.
ํญ์ ์ปจํ
์คํธ๋ฅผ ์ง์ํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ธ์: QueryContext, QueryRowContext, ExecContext, PrepareContext. Query()๋ Exec()์ฒ๋ผ ์ปจํ
์คํธ ์๋ ํธ์ถ์ ์ฌ์ฉํ๋ฉด ํธ๋ค๋ฌ๋ ํ์์์๋ ์ ์์ด๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํธ์ถ์ ๊ณ์ ๋ธ๋กํน๋์ด ์ฐ๊ฒฐ์ ์ก์๋๊ฒ ๋ฉ๋๋ค.
๋ง์ ๋๋ผ์ด๋ฒ๊ฐ ์ปจํ ์คํธ ์ทจ์๋ฅผ honorํ์ง๋ง, ์ง์ ๋๋ฆฐ ์ฟผ๋ฆฌ๋ฅผ ์คํํด ๋ฐ๋๋ผ์ธ์ด ์ด๊ณผ๋์์ ๋ ๋น ๋ฅด๊ฒ ์ทจ์๋๋์ง ํ์ธํ์ธ์. ๋ํ ์ผ๋ถ ์ฝ๋ ๊ฒฝ๋ก๊ฐ ์ปจํ ์คํธ ์ ๋ฌ์ ์๋ ๊ฒฝ์ฐ๋ฅผ ๋๋นํด DB ์ชฝ ๋ฌธ์ฅ(statement) ํ์์์์ ๋ฐฑ์คํฑ์ผ๋ก ๋๋ ๊ฒ์ด ์ข์ต๋๋ค.
์์๋ฐ์ด๋ ์์ฒญ์ ๋ง๋ค ๋ http.NewRequestWithContext(ctx, ...)๋ก ์์ฑํด ๋์ผํ ๋ฐ๋๋ผ์ธ๊ณผ ์ทจ์ ์ ํธ๊ฐ ์ ๋ฌ๋๊ฒ ํ์ธ์. ๋ํ ๋๊ตฐ๊ฐ ์ค์๋ก background ์ปจํ
์คํธ๋ฅผ ์ฐ๊ฑฐ๋ DNS/TLS/์ ํด ์ฐ๊ฒฐ์ด ๋ฉ์ถ ๊ฒฝ์ฐ๋ฅผ ๋๋นํด http.Client.Timeout๊ณผ ํธ๋์คํฌํธ์ ๋ค์ด์ผ/ํธ๋์
ฐ์ดํฌ/์๋ต ํค๋ ํ์์์์ ์ค์ ํด ๋๋ ๊ฒ์ด ์์ ํฉ๋๋ค.
ํ์ ๋ ์ด์ด์์ ์๊ฐ์ ๋๋ฆฌ๋ ์๋ก์ด ์ปจํ ์คํธ๋ฅผ ์์ฑํ์ง ๋ง์ธ์. ์์ ์ปจํ ์คํธ๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค๋ฉด ๋ถ๋ชจ๋ณด๋ค ๋ ์งง๊ฒ ์ค์ ํ์ธ์. ์์ฒญ์ ์๊ฐ์ด ๊ฑฐ์ ๋จ์ง ์์๋ค๋ฉด ์ ํ์ ํ์ ํธ์ถ์ ๊ฑด๋๋ฐ๊ณ , ๋ถ๋ถ ์๋ต์ ๋ฐํํ๊ฑฐ๋ ๋น ๋ฅด๊ฒ ์คํจํ๋ ์ชฝ์ด ๋ซ์ต๋๋ค.
์๋ํฌ์๋ ํ์์์์ด ์๋ํ๋์ง ํ์ธํ๋ ค๋ฉด ์๋ํฌ์ธํธ์ ์ข
์์ฑ๋ณ๋ก ํ์์์๊ณผ ์ทจ์๋ฅผ ๋ถ๋ฆฌํด ์ถ์ ํ๊ณ , ์ง์ฐ ๋ฐฑ๋ถ์์(p95/p99)์ ์ธํ๋ผ์ดํธ ์์ฒญ ์๋ฅผ ๋ชจ๋ํฐ๋งํ์ธ์. ํธ๋ ์ด์ค์์๋ ํธ๋ค๋ฌ์์ QueryContext ๊ฐ์ DB ํธ์ถ๊น์ง ๋์ผํ ์ปจํ
์คํธ๊ฐ ์ด์ด์ง๋์ง ํ์ธํ๋ฉด ์๊ฐ์ด ์ด๋์ ์๋ชจ๋๋์ง ํ์
ํ๊ธฐ ์ฝ์ต๋๋ค.


