Ver3.0 ๐ Go์ธ์ด ๋ฌธ์ํ์ godoc์ผ๋ก API ๋ฌธ์ ์๋ ์์ฑํ๊ธฐ

๐ Go์ธ์ด ๋ฌธ์ํ์ godoc์ผ๋ก API ๋ฌธ์ ์๋ ์์ฑํ๊ธฐ
์๋ ! ๐ ์ค๋์ Go ์ธ์ด๋ก ๊ฐ๋ฐํ ๋ ์ ๋ง ์ค์ํ๋ฐ ๋ง์ ๊ฐ๋ฐ์๋ค์ด ๊ฐ๊ณผํ๋ ๋ฌธ์ํ์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊น ํด. ํนํ godoc์ด๋ผ๋ ๊ฐ๋ ฅํ ๋๊ตฌ๋ฅผ ํ์ฉํด์ API ๋ฌธ์๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์น๊ตฌ์๊ฒ ์ค๋ช ํ๋ฏ ์ฝ๊ฒ ํ์ด๋ณผ๊ฒ!
์ฌ์ค ์ฝ๋ ์์ฑํ๋ ๊ฒ๋ ๋ฐ์๋ฐ ๋ฌธ์๊น์ง ๋ฐ๋ก ์์ฑํ๋ ค๋ฉด ๋จธ๋ฆฌ๊ฐ ์ํ์์? ๐ ๊ทธ๋ฐ๋ฐ Go๋ ์ด ๋ฌธ์ ๋ฅผ ์ ๋ง ์ฐ์ํ๊ฒ ํด๊ฒฐํ์ด. ์ฝ๋์ ์ฃผ์๋ง ์ ๋๋ก ๋ฌ๋ฉด ์๋์ผ๋ก ๋ฉ์ง ๋ฌธ์๊ฐ ์์ฑ๋๊ฑฐ๋ ! ๋ง์น ๋ง๋ฒ์ฒ๋ผ ๋ง์ด์ผ. โจ
๐ ์ ๋ฌธ์ํ๊ฐ ์ค์ํ ๊น?
๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํ๊ธฐ ์ ์, ์ ๋ฌธ์ํ๊ฐ ์ค์ํ์ง๋ถํฐ ์ง๊ณ ๋์ด๊ฐ์. ๋๋ ์ฒ์์ "์ฝ๋๋ง ์ ์ง๋ฉด ๋์ง ๋ญ~" ๋ผ๊ณ ์๊ฐํ๊ฑฐ๋ . ๊ทธ๋ฐ๋ฐ 6๊ฐ์ ํ์ ๋ด๊ฐ ์ง ์ฝ๋๋ฅผ ๋ค์ ๋ณด๋ฉด... ์ด๊ฒ ๋ญ์ง? ๐ค ํ๋ ๊ฒฝํ ๋ค๋ค ์์ง?
2. ํ์๋ค๊ณผ์ ํ์ - ๋ค๋ฅธ ์ฌ๋์ด ๋ด ์ฝ๋๋ฅผ ์ดํดํ๊ณ ์ฌ์ฉํ ์ ์๊ฒ
3. ์ ์ง๋ณด์ ํจ์จ์ฑ - ๋ฒ๊ทธ ์์ ์ด๋ ๊ธฐ๋ฅ ์ถ๊ฐ๊ฐ ํจ์ฌ ์ฌ์์ ธ
4. ํ๋กํ์ ๋ํจ - ์คํ์์ค๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ณต๊ฐํ ๋ ํ์์ ์ด์ผ
5. ์จ๋ณด๋ฉ ์๊ฐ ๋จ์ถ - ์๋ก์ด ํ์์ด ๋น ๋ฅด๊ฒ ์ ์ํ ์ ์์ด
ํนํ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๊ฐ๋ฐ ๊ด๋ จ ์ฌ๋ฅ์ ๊ณต์ ํ๊ฑฐ๋ ํ๋ก์ ํธ๋ฅผ ์งํํ ๋, ์ ์ ๋ฆฌ๋ ๋ฌธ์๋ ๋น์ ์ ์ ๋ฌธ์ฑ์ ๋ณด์ฌ์ฃผ๋ ๊ฐ๋ ฅํ ๋ฌด๊ธฐ๊ฐ ๋ผ. ํด๋ผ์ด์ธํธ๋ ํ์ ์๋ค์ด "์ด ์ฌ๋์ ํ๋ก๊ตฌ๋!" ํ๊ณ ๋๋ผ๊ฒ ๋๊ฑฐ๋ . ๐ผ
๐ฏ Go์ ๋ฌธ์ํ ์ฒ ํ
Go ์ธ์ด๋ฅผ ๋ง๋ ๊ตฌ๊ธ ์์ง๋์ด๋ค์ ์ฒ์๋ถํฐ ๋ฌธ์ํ๋ฅผ ์ธ์ด์ ํต์ฌ ๊ธฐ๋ฅ์ผ๋ก ์ค๊ณํ์ด. ๊ทธ๋ค์ ์ฒ ํ์ ๊ฐ๋จํด:
์ด ์ฒ ํ ๋๋ถ์ Go์์๋ ๋ณ๋์ ๋ณต์กํ ๋ฌธ์ํ ๋๊ตฌ๋ ๋งํฌ์ ์ธ์ด๋ฅผ ๋ฐฐ์ธ ํ์๊ฐ ์์ด. ๊ทธ๋ฅ ํ๋ฒํ ์ฃผ์์ ์์ฑํ๋ฉด ๋ผ! ๐
Go ๋ฌธ์ํ์ ํต์ฌ ์์น
Go์ ๋ฌธ์ํ๋ ๋ช ๊ฐ์ง ๊ฐ๋จํ ์์น์ ๋ฐ๋ผ:
1. ์ ์ธ ๋ฐ๋ก ์์ ์ฃผ์ ์์ฑ
ํจ์, ํ์
, ๋ณ์, ์์ ๋ฑ์ ์ ์ธํ๊ธฐ ๋ฐ๋ก ์ ์ ์ฃผ์์ ๋ฌ๋ฉด ๊ทธ๊ฒ ๋ฐ๋ก ๋ฌธ์๊ฐ ๋ผ. ๋ง์น ํฌ์คํธ์์ ๋ถ์ด๋ฏ์ด ๋ง์ด์ผ! ๐
2. ์ฒซ ๋ฌธ์ฅ์ด ์์ฝ
์ฃผ์์ ์ฒซ ๋ฌธ์ฅ์ ์ ์ธํ๋ ๋์์ ์ด๋ฆ์ผ๋ก ์์ํด์ผ ํด. ์ด๊ฒ ๋ฌธ์์ ์์ฝ์ด ๋๊ฑฐ๋ .
3. ์์ ํ ๋ฌธ์ฅ์ผ๋ก ์์ฑ
๋จํธ์ ์ธ ํค์๋๊ฐ ์๋๋ผ ์์ ํ ๋ฌธ์ฅ์ผ๋ก ์์ฑํด์ผ ํด. ๋ง์นจํ๋ ์์ง ๋ง๊ณ !
4. ๊ฐ๊ฒฐํ๊ณ ๋ช
ํํ๊ฒ
์ฅํฉํ ์ค๋ช
๋ณด๋ค๋ ํต์ฌ์ ๋ช
ํํ๊ฒ ์ ๋ฌํ๋ ๊ฒ ์ค์ํด.
๐ ๏ธ godoc ์๊ฐ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก godoc์ ๋ํด ์์๋ณด์! godoc์ Go ์ฝ๋์์ ๋ฌธ์๋ฅผ ์ถ์ถํ๊ณ ํฌ๋งทํ ํด์ ๋ณด์ฌ์ฃผ๋ ๋๊ตฌ์ผ. ๐ง
godoc์ ๋ ๊ฐ์ง ๋ชจ๋
godoc์ ํฌ๊ฒ ๋ ๊ฐ์ง ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ ์ ์์ด:
godoc ์ค์นํ๊ธฐ
Go 1.13 ์ด์ ๋ฒ์ ์๋ godoc์ด ๊ธฐ๋ณธ์ผ๋ก ํฌํจ๋์ด ์์๋๋ฐ, ์ดํ ๋ฒ์ ๋ถํฐ๋ ๋ณ๋๋ก ์ค์นํด์ผ ํด. ๊ฑฑ์ ๋ง, ์์ฒญ ๊ฐ๋จํด! ๐
go install golang.org/x/tools/cmd/godoc@latest
์ด ๋ช
๋ น์ด ํ๋๋ฉด ๋! ์ค์น๊ฐ ์๋ฃ๋๋ฉด godoc ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ ์ ์์ด.
go doc ๋ช
๋ น์ด๊ฐ ๊ธฐ๋ณธ ์ ๊ณต๋ผ. godoc๊ณผ go doc์ ๋น์ทํ์ง๋ง ์ฝ๊ฐ ๋ค๋ฅธ ๋๊ตฌ์ผ. go doc์ ์ปค๋งจ๋๋ผ์ธ ์ ์ฉ์ด๊ณ , godoc์ ์น ์๋ฒ ๊ธฐ๋ฅ๋ ์ ๊ณตํด.
โ๏ธ ํจ๊ณผ์ ์ธ ์ฃผ์ ์์ฑ๋ฒ
์ด์ ์ค์ ์ผ๋ก ๋ค์ด๊ฐ๋ณด์! Go์์ ๋ฌธ์ํ๋ฅผ ์ํ ์ฃผ์์ ์ด๋ป๊ฒ ์์ฑํ๋์ง ๊ตฌ์ฒด์ ์ผ๋ก ์์๋ณผ๊ฒ. ๐จ
ํจํค์ง ์ฃผ์
๋ชจ๋ ํจํค์ง๋ ํจํค์ง ์ฃผ์์ ๊ฐ์ ธ์ผ ํด. ์ด๊ฑด ํจํค์ง๊ฐ ๋ฌด์์ ํ๋์ง ์ค๋ช
ํ๋ ๊ฑฐ์ผ. ๋ณดํต ํจํค์ง์ ๋ฉ์ธ ํ์ผ(๊ด๋ก์ ์ผ๋ก doc.go)์ ์์ฑํด.
// Package calculator provides basic arithmetic operations.
// It supports addition, subtraction, multiplication, and division
// with proper error handling for edge cases like division by zero.
//
// Example usage:
//
// result, err := calculator.Add(5, 3)
// if err != nil {
// log.Fatal(err)
// }
// fmt.Println(result) // Output: 8
//
package calculator
๋ณด๋ค์ํผ ํจํค์ง ์ฃผ์์ Package๋ก ์์ํ๊ณ , ํจํค์ง๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ๊ฐ๋ตํ๊ฒ ์ค๋ช
ํด. ์์ ์ฝ๋๋ฅผ ํฌํจํ๋ฉด ๋ ์ข์! ๐
ํจ์ ์ฃผ์
ํจ์ ์ฃผ์์ ํจ์ ์ ์ธ ๋ฐ๋ก ์์ ์์ฑํ๊ณ , ํจ์ ์ด๋ฆ์ผ๋ก ์์ํด์ผ ํด. ์ด๊ฒ ์ ๋ง ์ค์ํ ๊ท์น์ด์ผ!
// Add returns the sum of two integers.
// It performs simple addition without overflow checking.
func Add(a, b int) int {
return a + b
}
// Divide divides the numerator by the denominator.
// It returns an error if the denominator is zero.
func Divide(numerator, denominator float64) (float64, error) {
if denominator == 0 {
return 0, errors.New("division by zero")
}
return numerator / denominator, nil
}
ํจ์๊ฐ ์๋ฌ๋ฅผ ๋ฐํํ ์ ์๋ค๋ฉด, ์ด๋ค ๊ฒฝ์ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋์ง ๋ช ํํ ์ค๋ช ํด์ฃผ๋ ๊ฒ ์ข์. ์ฌ์ฉ์๊ฐ ์๋ฌ ์ฒ๋ฆฌ๋ฅผ ์ ๋๋ก ํ ์ ์๋๋ก ๋ง์ด์ผ! ๐จ
ํ์ ์ฃผ์
๊ตฌ์กฐ์ฒด๋ ์ธํฐํ์ด์ค ๊ฐ์ ํ์ ๋ ๋ฌธ์ํ๊ฐ ํ์ํด. ๊ฐ ํ๋์ ๋ํ ์ค๋ช ๋ ์ถ๊ฐํ ์ ์์ด.
// User represents a user in the system.
// It contains basic information needed for authentication and identification.
type User struct {
// ID is the unique identifier for the user.
ID int
// Username is the user's login name.
// It must be unique across the system.
Username string
// Email is the user's email address.
// It's used for notifications and password recovery.
Email string
// CreatedAt is the timestamp when the user was created.
CreatedAt time.Time
}
๊ตฌ์กฐ์ฒด์ ๊ฐ ํ๋์ ์ฃผ์์ ๋ฌ๋ฉด ์ฌ์ฉ์๊ฐ ๊ฐ ํ๋์ ๋ชฉ์ ์ ๋ช ํํ ์ดํดํ ์ ์์ด. ํนํ ํ๋ ์ด๋ฆ๋ง์ผ๋ก๋ ์๋ฏธ๊ฐ ๋ถ๋ถ๋ช ํ ๋ ์ ์ฉํ์ง! ๐
์์์ ๋ณ์ ์ฃผ์
์์๋ ๋ณ์๋ ๋ฌธ์ํํ ์ ์์ด. ํนํ ๊ณต๊ฐ(exported) ์์๋ ๋ฐ๋์ ๋ฌธ์ํํด์ผ ํด.
// MaxRetries is the maximum number of retry attempts for failed operations.
const MaxRetries = 3
// DefaultTimeout is the default timeout duration for network requests.
const DefaultTimeout = 30 * time.Second
// ErrNotFound is returned when a requested resource is not found.
var ErrNotFound = errors.New("resource not found")
์์ ์ฝ๋ ์์ฑ
Go์ ๋ฌธ์ํ์์ ์ ๋ง ๋ฉ์ง ๊ธฐ๋ฅ ์ค ํ๋๊ฐ ๋ฐ๋ก ์์ ์ฝ๋์ผ! ์์ ๋ ๋ณ๋์ ํ
์คํธ ํ์ผ์ ์์ฑํ๋๋ฐ, ํจ์ ์ด๋ฆ์ Example๋ก ์์ํ๋ฉด ๋ผ.
// calculator_test.go ํ์ผ์ ์์ฑ
package calculator_test
import (
"fmt"
"yourmodule/calculator"
)
// ExampleAdd demonstrates basic addition.
func ExampleAdd() {
result := calculator.Add(2, 3)
fmt.Println(result)
// Output: 5
}
// ExampleDivide demonstrates division with error handling.
func ExampleDivide() {
result, err := calculator.Divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result)
// Output: 5
}
// Output: ์ฃผ์์ ํน๋ณํ ์๋ฏธ๋ฅผ ๊ฐ์ ธ. ํ
์คํธ๋ฅผ ์คํํ ๋ ์ค์ ์ถ๋ ฅ์ด ์ด ์ฃผ์๊ณผ ์ผ์นํ๋์ง ๊ฒ์ฆํด! ์ผ์์ด์กฐ์ง? ๋ฌธ์ํ๋ ๋๊ณ ํ
์คํธ๋ ๋๊ณ ! ๐ฏ
์:
ExampleUser_Save()๋ User ํ์
์ Save ๋ฉ์๋ ์์ ๊ฐ ๋ผ.
๐ godoc ์คํํ๊ธฐ
์, ์ด์ ์ฃผ์๋ ์ด์ฌํ ๋ฌ์์ผ๋ ์ค์ ๋ก ๋ฌธ์๋ฅผ ์์ฑํด๋ณด์! ๐
์ปค๋งจ๋๋ผ์ธ์์ ๋ฌธ์ ๋ณด๊ธฐ
๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ go doc ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๋ ๊ฑฐ์ผ:
# ํ์ฌ ํจํค์ง์ ๋ฌธ์ ๋ณด๊ธฐ
go doc
# ํน์ ํจํค์ง์ ๋ฌธ์ ๋ณด๊ธฐ
go doc fmt
# ํน์ ํจ์์ ๋ฌธ์ ๋ณด๊ธฐ
go doc fmt.Println
# ๋ ์์ธํ ์ ๋ณด ๋ณด๊ธฐ
go doc -all fmt
ํฐ๋ฏธ๋์์ ๋ฐ๋ก ํ์ธํ ์ ์์ด์ ์ ๋ง ํธํด! ์ฝ๋ฉํ๋ค๊ฐ ๊ถ๊ธํ ๊ฒ ์์ผ๋ฉด ๋ฐ๋ก๋ฐ๋ก ํ์ธํ ์ ์๊ฑฐ๋ . โก
์น ์๋ฒ๋ก ๋ฌธ์ ๋ณด๊ธฐ
๋ ๋ฉ์ง ๋ฐฉ๋ฒ์ ์น ์๋ฒ๋ฅผ ๋์ฐ๋ ๊ฑฐ์ผ. ์ด๋ ๊ฒ ํ๋ฉด ๋ธ๋ผ์ฐ์ ์์ ์์๊ฒ ํฌ๋งท๋ ๋ฌธ์๋ฅผ ๋ณผ ์ ์์ด!
# godoc ์น ์๋ฒ ์์ (๊ธฐ๋ณธ ํฌํธ 6060)
godoc -http=:6060
# ๋ค๋ฅธ ํฌํธ ์ฌ์ฉ
godoc -http=:8080
์๋ฒ๊ฐ ์์๋๋ฉด ๋ธ๋ผ์ฐ์ ์์ http://localhost:6060์ผ๋ก ์ ์ํด๋ด. ์ง์! โจ ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฌธ์๋ถํฐ ๋ด๊ฐ ์์ฑํ ํจํค์ง ๋ฌธ์๊น์ง ๋ชจ๋ ๋ณผ ์ ์์ด.
โข ํจํค์ง ๊ฐ ๋งํฌ๋ก ์ฝ๊ฒ ํ์ ๊ฐ๋ฅ
โข ์์ ์ฝ๋๊ฐ ํ์ด๋ผ์ดํ ๋์ด ํ์๋ผ
โข ๊ฒ์ ๊ธฐ๋ฅ ์ ๊ณต (์ธ๋ฑ์ค ์์ฑ ํ์)
โข ์์ค ์ฝ๋๋ ํจ๊ป ๋ณผ ์ ์์ด
๊ฒ์ ๊ธฐ๋ฅ ํ์ฑํ
๊ฒ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด ์ธ๋ฑ์ค๋ฅผ ์์ฑํด์ผ ํด. ์ฒ์์ ์๊ฐ์ด ์ข ๊ฑธ๋ฆฌ์ง๋ง, ํ ๋ฒ๋ง ํ๋ฉด ๋ผ!
# ์ธ๋ฑ์ค์ ํจ๊ป ์๋ฒ ์์
godoc -http=:6060 -index
# ์ธ๋ฑ์ค ์
๋ฐ์ดํธ ๊ฐ๊ฒฉ ์ค์ (๊ธฐ๋ณธ 5๋ถ)
godoc -http=:6060 -index -index_interval=10m
๐ฆ ์ค์ ํ๋ก์ ํธ ๋ฌธ์ํ
์ด๋ก ์ ์ถฉ๋ถํ ๋ฐฐ์ ์ผ๋, ์ด์ ์ค์ ํ๋ก์ ํธ๋ฅผ ๋ฌธ์ํํด๋ณด์! ๊ฐ๋จํ REST API ์๋ฒ๋ฅผ ์๋ก ๋ค์ด๋ณผ๊ฒ. ๐๏ธ
ํ๋ก์ ํธ ๊ตฌ์กฐ
myapi/
โโโ doc.go
โโโ server.go
โโโ handlers/
โ โโโ doc.go
โ โโโ user.go
โ โโโ user_test.go
โโโ models/
โ โโโ doc.go
โ โโโ user.go
โโโ utils/
โโโ doc.go
โโโ validator.go
๋ฉ์ธ ํจํค์ง ๋ฌธ์ (doc.go)
// Package myapi provides a RESTful API server for user management.
//
// This package implements a complete HTTP server with endpoints for
// creating, reading, updating, and deleting user records. It includes
// input validation, error handling, and JSON serialization.
//
// # Architecture
//
// The API is organized into three main components:
// - handlers: HTTP request handlers
// - models: Data structures and business logic
// - utils: Helper functions and utilities
//
// # Quick Start
//
// To start the server:
//
// server := myapi.NewServer(":8080")
// if err := server.Start(); err != nil {
// log.Fatal(err)
// }
//
// # API Endpoints
//
// POST /users - Create a new user
// GET /users/:id - Get user by ID
// PUT /users/:id - Update user
// DELETE /users/:id - Delete user
//
// For more detailed information, see the handlers package.
package myapi
ํจํค์ง ๋ฌธ์์์ #์ผ๋ก ์์ํ๋ ์ค์ ์น์
ํค๋๊ฐ ๋ผ. ๋ฌธ์๋ฅผ ๊ตฌ์กฐํํ๋ ๋ฐ ์ ์ฉํ์ง! ๐
ํธ๋ค๋ฌ ๋ฌธ์ํ
// Package handlers implements HTTP request handlers for the API.
package handlers
import (
"encoding/json"
"net/http"
"myapi/models"
)
// UserHandler handles HTTP requests related to user operations.
// It provides methods for CRUD operations on user resources.
type UserHandler struct {
store models.UserStore
}
// NewUserHandler creates a new UserHandler with the given store.
// The store parameter must not be nil.
func NewUserHandler(store models.UserStore) *UserHandler {
return &UserHandler{store: store}
}
// CreateUser handles POST requests to create a new user.
// It expects a JSON payload with user information in the request body.
//
// Request body example:
//
// {
// "username": "john_doe",
// "email": "john@example.com"
// }
//
// Response codes:
// - 201: User created successfully
// - 400: Invalid request body or validation error
// - 500: Internal server error
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
var user models.User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.store.Create(&user); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
}
HTTP ํธ๋ค๋ฌ๋ฅผ ๋ฌธ์ํํ ๋๋ ์์ฒญ ํ์, ์๋ต ์ฝ๋, ์๋ฌ ์ํฉ ๋ฑ์ ๋ช ํํ ์ค๋ช ํ๋ ๊ฒ ์ค์ํด. API๋ฅผ ์ฌ์ฉํ๋ ์ฌ๋์ด ํค๋งค์ง ์๋๋ก ๋ง์ด์ผ! ๐บ๏ธ
๋ชจ๋ธ ๋ฌธ์ํ
// Package models defines data structures and business logic.
package models
import "time"
// User represents a user in the system.
// All fields are required unless marked as optional.
type User struct {
// ID is the unique identifier for the user.
// It's automatically generated when the user is created.
ID int `json:"id"`
// Username is the user's login name.
// It must be unique and contain only alphanumeric characters.
Username string `json:"username"`
// Email is the user's email address.
// It must be a valid email format.
Email string `json:"email"`
// CreatedAt is the timestamp when the user was created.
CreatedAt time.Time `json:"created_at"`
// UpdatedAt is the timestamp of the last update.
// This field is optional and may be nil.
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
// UserStore defines the interface for user data persistence.
// Implementations must be safe for concurrent use.
type UserStore interface {
// Create adds a new user to the store.
// It returns an error if the username or email already exists.
Create(user *User) error
// GetByID retrieves a user by their ID.
// It returns ErrNotFound if the user doesn't exist.
GetByID(id int) (*User, error)
// Update modifies an existing user.
// It returns ErrNotFound if the user doesn't exist.
Update(user *User) error
// Delete removes a user from the store.
// It returns ErrNotFound if the user doesn't exist.
Delete(id int) error
}
// ErrNotFound is returned when a requested user is not found.
var ErrNotFound = errors.New("user not found")
์ธํฐํ์ด์ค๋ฅผ ๋ฌธ์ํํ ๋๋ ๊ฐ ๋ฉ์๋์ ๊ณ์ฝ(contract)์ ๋ช ํํ ํด์ผ ํด. ์ด๋ค ์ ๋ ฅ์ ๋ฐ๊ณ , ์ด๋ค ์ถ๋ ฅ์ ๋ด๊ณ , ์ด๋ค ์๋ฌ๋ฅผ ๋ฐํํ๋์ง ๋ง์ด์ผ. ๐ค
์์ ํ ์คํธ ์์ฑ
// user_test.go
package handlers_test
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"myapi/handlers"
"myapi/models"
)
// ExampleUserHandler_CreateUser demonstrates creating a new user.
func ExampleUserHandler_CreateUser() {
// ํ
์คํธ์ฉ ์คํ ์ด ์์ฑ
store := models.NewMemoryStore()
handler := handlers.NewUserHandler(store)
// ์์ฒญ ์์ฑ
user := models.User{
Username: "testuser",
Email: "test@example.com",
}
body, _ := json.Marshal(user)
req := httptest.NewRequest("POST", "/users", bytes.NewReader(body))
w := httptest.NewRecorder()
// ํธ๋ค๋ฌ ์คํ
handler.CreateUser(w, req)
fmt.Println(w.Code)
// Output: 201
}
// ExampleUser demonstrates basic user creation.
func ExampleUser() {
user := models.User{
ID: 1,
Username: "john_doe",
Email: "john@example.com",
}
fmt.Printf("User: %s (%s)\n", user.Username, user.Email)
// Output: User: john_doe (john@example.com)
}
์์ ํ ์คํธ๋ ์ค์ ๋ก ๋์ํ๋ ์ฝ๋์ฌ์ผ ํด. ๊ทธ๋์ผ ๋ฌธ์๊ฐ ํญ์ ์ต์ ์ํ๋ฅผ ์ ์งํ ์ ์๊ฑฐ๋ ! ๐
๐จ ๊ณ ๊ธ ๋ฌธ์ํ ๊ธฐ๋ฒ
๊ธฐ๋ณธ์ ๋ง์คํฐํ์ผ๋, ์ด์ ์ข ๋ ๊ณ ๊ธ ๊ธฐ๋ฒ๋ค์ ์์๋ณด์! ๐
์ฝ๋ ๋ธ๋ก ํฌ๋งทํ
์ฃผ์์์ ์ฝ๋ ๋ธ๋ก์ ํํํ๋ ค๋ฉด ๋ค์ฌ์ฐ๊ธฐ๋ฅผ ์ฌ์ฉํด. ํญ์ด๋ ์คํ์ด์ค 4๊ฐ๋ก ๋ค์ฌ์ฐ๋ฉด ์ฝ๋ ๋ธ๋ก์ผ๋ก ์ธ์๋ผ.
// Process handles the data processing workflow.
//
// Example usage:
//
// processor := NewProcessor()
// result, err := processor.Process(data)
// if err != nil {
// return err
// }
// fmt.Println(result)
//
// The processor supports multiple data formats including JSON and XML.
func Process(data []byte) ([]byte, error) {
// implementation
}
๋งํฌ์ ์ฐธ์กฐ
godoc์ ์๋์ผ๋ก ํ์ , ํจ์, ํจํค์ง ์ด๋ฆ์ ๋งํฌ๋ก ๋ง๋ค์ด์ค. ๊ทธ๋ฅ ์ด๋ฆ์ ์ธ๊ธํ๊ธฐ๋ง ํ๋ฉด ๋ผ!
// ParseUser parses user data from JSON.
// It uses the User type defined in the models package.
// Returns ErrInvalidFormat if the JSON is malformed.
//
// See also: User, ValidateUser, ErrInvalidFormat
func ParseUser(data []byte) (*User, error) {
// implementation
}
Deprecated ํ์
๋ ์ด์ ์ฌ์ฉํ์ง ์๋ ํจ์๋ ํ์
์ Deprecated:๋ก ํ์ํด์ค. ์ฌ์ฉ์๋ค์ด ๋์์ ์ฐพ์ ์ ์๋๋ก!
// OldFunction does something in the old way.
//
// Deprecated: Use NewFunction instead. OldFunction will be removed in v2.0.
func OldFunction() {
// old implementation
}
// NewFunction does something in the new, improved way.
// It replaces OldFunction with better performance and cleaner API.
func NewFunction() {
// new implementation
}
๋ฒ์ ์ ๋ณด์ ๋ณ๊ฒฝ ์ด๋ ฅ
ํจํค์ง ๋ฌธ์์ ๋ฒ์ ์ ๋ณด๋ ์ฃผ์ ๋ณ๊ฒฝ ์ฌํญ์ ํฌํจํ๋ฉด ์ข์.
// Package myapi provides a RESTful API server.
//
// # Version History
//
// v1.2.0 (2024-01-15)
// - Added support for pagination
// - Improved error messages
// - Fixed memory leak in connection pool
//
// v1.1.0 (2023-12-01)
// - Added user authentication
// - New endpoint for bulk operations
//
// v1.0.0 (2023-10-01)
// - Initial release
//
package myapi
๐ง ๋ฌธ์ํ ์๋ํ์ CI/CD ํตํฉ
ํ๋กํ์ ๋ํ ํ๋ก์ ํธ๋ผ๋ฉด ๋ฌธ์ ์์ฑ์ ์๋ํํ๋ ๊ฒ ์ข์. ํนํ ์คํ์์ค ํ๋ก์ ํธ๋ ํ ํ๋ก์ ํธ์์๋ ํ์์ ์ด์ง! ๐ค
GitHub Actions๋ก ์๋ ๋ฐฐํฌ
GitHub Actions๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๋๊ฐ ํธ์๋ ๋๋ง๋ค ์๋์ผ๋ก ๋ฌธ์๋ฅผ ์์ฑํ๊ณ GitHub Pages์ ๋ฐฐํฌํ ์ ์์ด.
# .github/workflows/docs.yml
name: Generate Documentation
on:
push:
branches: [ main ]
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Install godoc
run: go install golang.org/x/tools/cmd/godoc@latest
- name: Generate documentation
run: |
godoc -html=mypackage > docs/index.html
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
Makefile๋ก ๊ฐํธํ๊ฒ
Makefile์ ๋ง๋ค์ด๋๋ฉด ๋ฌธ์ ์์ฑ์ด ํจ์ฌ ํธํด์ ธ.
# Makefile
.PHONY: docs docs-serve docs-open
# ๋ฌธ์ HTML ์์ฑ
docs:
@echo "Generating documentation..."
@godoc -html=./mypackage > docs/index.html
@echo "Documentation generated in docs/index.html"
# ๋ฌธ์ ์๋ฒ ์์
docs-serve:
@echo "Starting documentation server on http://localhost:6060"
@godoc -http=:6060
# ๋ฌธ์ ์๋ฒ ์์ํ๊ณ ๋ธ๋ผ์ฐ์ ์ด๊ธฐ
docs-open:
@echo "Opening documentation in browser..."
@(sleep 2 && open http://localhost:6060/pkg/mypackage) &
@godoc -http=:6060
์ด์ make docs๋ง ์
๋ ฅํ๋ฉด ๋ฌธ์๊ฐ ์์ฑ๋๊ณ , make docs-serve๋ก ์๋ฒ๋ฅผ ๋์ธ ์ ์์ด! ํธํ์ง? ๐
๐ ๋ฌธ์ ํ์ง ์ฒดํฌ
๋ฌธ์๋ฅผ ์์ฑํ๋ค๋ฉด ํ์ง๋ ํ์ธํด์ผ๊ฒ ์ง? ๋ช ๊ฐ์ง ์ ์ฉํ ๋๊ตฌ๋ค์ ์๊ฐํ ๊ฒ! ๐
go vet์ผ๋ก ๊ธฐ๋ณธ ์ฒดํฌ
# ์ฝ๋ ๊ฒ์ฌ (๋ฌธ์ ๊ด๋ จ ๊ฒฝ๊ณ ํฌํจ)
go vet ./...
# ๋ ์์ธํ ๊ฒ์ฌ
go vet -v ./...
golint๋ก ์คํ์ผ ์ฒดํฌ
golint๋ Go ์ฝ๋์ ์คํ์ผ์ ์ฒดํฌํ๋๋ฐ, ๋ฌธ์ํ ๊ด๋ จ ๊ฒฝ๊ณ ๋ ํด์ค.
# golint ์ค์น
go install golang.org/x/lint/golint@latest
# ์ ์ฒด ํ๋ก์ ํธ ๊ฒ์ฌ
golint ./...
# ํน์ ํจํค์ง๋ง ๊ฒ์ฌ
golint ./handlers
golint๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒ๋ค์ ์ฒดํฌํด:
โข ์ฃผ์์ด ์ฌ๋ฐ๋ฅธ ํ์์ธ์ง (์ด๋ฆ์ผ๋ก ์์ํ๋์ง)
โข ์ฃผ์์ด ์์ ํ ๋ฌธ์ฅ์ธ์ง
โข ํจํค์ง ์ฃผ์์ด ์๋์ง
golangci-lint๋ก ์ข ํฉ ๊ฒ์ฌ
golangci-lint๋ ์ฌ๋ฌ ๋ฆฐํฐ๋ฅผ ํตํฉํ ๊ฐ๋ ฅํ ๋๊ตฌ์ผ. ๋ฌธ์ํ ๊ด๋ จ ๊ฒ์ฌ๋ ํฌํจ๋ผ ์์ด!
# golangci-lint ์ค์น
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# ์คํ
golangci-lint run
# ํน์ ๋ฆฐํฐ๋ง ์คํ
golangci-lint run --enable=godot,godox
๐ ์ค๋ฌด ํ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
์ด์ ์ค๋ฌด์์ ๋ฐ๋ก ์จ๋จน์ ์ ์๋ ํ๋ค์ ๊ณต์ ํ ๊ฒ! ๋ด๊ฐ ์ค์ ๋ก ํ๋ก์ ํธํ๋ฉด์ ๋ฐฐ์ด ๊ฒ๋ค์ด์ผ. ๐
1. ์ผ๊ด์ฑ ์๋ ์คํ์ผ ์ ์ง
โข ์ฃผ์์ ํญ์ ์์ ํ ๋ฌธ์ฅ์ผ๋ก
โข ์ฒซ ๋ฌธ์ฅ์ ์์ฝ์ผ๋ก, ์ ์ธ ์ด๋ฆ์ผ๋ก ์์
โข ์์ ์ฝ๋๋ ์ค์ ๋ก ๋์ํ๋ ์ฝ๋๋ก
โข ์๋ฌ ์กฐ๊ฑด์ ๋ช ํํ๊ฒ ์ค๋ช
2. ์ฌ์ฉ์ ๊ด์ ์์ ์์ฑ
๋ฌธ์๋ ์ฝ๋๋ฅผ ์์ฑํ ์ฌ๋์ด ์๋๋ผ ์ฌ์ฉํ ์ฌ๋์ ์ํ ๊ฑฐ์ผ. ๋ด๋ถ ๊ตฌํ๋ณด๋ค๋ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง์ ์ด์ ์ ๋ง์ถฐ!
// โ ๋์ ์: ๊ตฌํ ์ธ๋ถ์ฌํญ์ ์ง์ค
// Connect establishes a TCP connection using net.Dial
// and stores the connection in the internal conn field.
// โ
์ข์ ์: ์ฌ์ฉ๋ฒ์ ์ง์ค
// Connect establishes a connection to the database.
// It returns an error if the connection fails or times out.
// The connection is automatically retried up to 3 times.
3. ์์ ๋ ํ์ค์ ์ผ๋ก
์์ ์ฝ๋๋ ์ค์ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ๋ฐ์ํด์ผ ํด. ๋๋ฌด ๊ฐ๋จํ๊ฑฐ๋ ๋นํ์ค์ ์ธ ์์ ๋ ์คํ๋ ค ํผ๋์ ์ค ์ ์์ด.
// โ ๋๋ฌด ๋จ์ํ ์์
// Example:
// result := Add(1, 2)
// โ
ํ์ค์ ์ธ ์์
// Example:
// // ์ฌ์ฉ์ ์
๋ ฅ ์ฒ๋ฆฌ
// input1, _ := strconv.Atoi(os.Args[1])
// input2, _ := strconv.Atoi(os.Args[2])
//
// result := Add(input1, input2)
// fmt.Printf("Result: %d\n", result)
4. ์๋ฌ ์ฒ๋ฆฌ ๋ฌธ์ํ
Go์์๋ ์๋ฌ ์ฒ๋ฆฌ๊ฐ ์ค์ํ๋๊น, ์ด๋ค ์๋ฌ๊ฐ ๋ฐ์ํ ์ ์๋์ง ๋ช ํํ ํด์ค์ผ ํด.
// ReadConfig reads and parses the configuration file.
//
// It returns the following errors:
// - ErrFileNotFound: if the config file doesn't exist
// - ErrInvalidFormat: if the file is not valid YAML
// - ErrPermissionDenied: if the file cannot be read
//
// Example:
//
// config, err := ReadConfig("config.yaml")
// if errors.Is(err, ErrFileNotFound) {
// // handle missing file
// }
func ReadConfig(path string) (*Config, error) {
// implementation
}
5. ์ฑ๋ฅ ๊ด๋ จ ์ ๋ณด ํฌํจ
ํจ์์ ์๊ฐ ๋ณต์ก๋๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ๊ฐ์ ์ฑ๋ฅ ์ ๋ณด๋ ์ ์ฉํด.
// Sort sorts the slice in ascending order.
// It uses quicksort algorithm with O(n log n) average time complexity.
// The sort is not stable and modifies the slice in-place.
//
// For stable sorting, use StableSort instead.
func Sort(data []int) {
// implementation
}
6. ์ค๋ ๋ ์์ ์ฑ ๋ช ์
๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์์๋ ์ค๋ ๋ ์์ ์ฑ์ด ์ค์ํด. ๋ฐ๋์ ๋ช ์ํด์ค!
// Cache provides an in-memory cache with expiration support.
// All methods are safe for concurrent use by multiple goroutines.
type Cache struct {
mu sync.RWMutex
items map[string]*item
}
// Set adds or updates a value in the cache.
// This method is safe for concurrent use.
func (c *Cache) Set(key string, value interface{}) {
// implementation
}
๐ ์ค์ ํ๋ก์ ํธ: ์์ ํ ๋ฌธ์ํ ์์
์, ์ด์ ๋ฐฐ์ด ๊ฑธ ๋ชจ๋ ์ข ํฉํด์ ์์ ํ ํ๋ก์ ํธ๋ฅผ ๋ฌธ์ํํด๋ณด์! ์ค์ ๋ก **์ฌ๋ฅ๋ท** ๊ฐ์ ํ๋ซํผ์์ ์ฌ๋ฅ์ ๊ณต์ ํ ๋ ์ด๋ฐ ์์ค์ ๋ฌธ์๊ฐ ์์ผ๋ฉด ์ ๋ง ์ ๋ฌธ์ ์ผ๋ก ๋ณด์ด๊ฑฐ๋ ! ๐
์์ฑ๋ ํจํค์ง ๊ตฌ์กฐ
// Package taskqueue provides a simple, thread-safe task queue
// with priority support and worker pool management.
//
// # Overview
//
// This package implements a priority-based task queue that allows
// you to submit tasks and process them asynchronously using a pool
// of worker goroutines. Tasks are executed in priority order, with
// higher priority tasks being processed first.
//
// # Features
//
// - Thread-safe operations
// - Priority-based task scheduling
// - Configurable worker pool size
// - Graceful shutdown support
// - Task timeout handling
// - Comprehensive error reporting
//
// # Quick Start
//
// Create a new queue and start processing tasks:
//
// queue := taskqueue.New(taskqueue.Config{
// Workers: 5,
// BufferSize: 100,
// })
//
// queue.Start()
// defer queue.Stop()
//
// // Submit a task
// err := queue.Submit(taskqueue.Task{
// ID: "task-1",
// Priority: taskqueue.PriorityHigh,
// Handler: func(ctx context.Context) error {
// // Your task logic here
// return nil
// },
// })
//
// # Architecture
//
// The queue consists of three main components:
//
// 1. Queue: Manages task submission and distribution
// 2. Worker Pool: Executes tasks concurrently
// 3. Priority Heap: Maintains task ordering
//
// # Performance Considerations
//
// The queue uses a heap-based priority queue with O(log n) insertion
// and removal. Worker goroutines are pre-allocated and reused for
// efficiency. Memory usage scales with the buffer size and number
// of pending tasks.
//
// # Thread Safety
//
// All public methods are safe for concurrent use. Internal
// synchronization is handled using mutexes and channels.
//
package taskqueue
import (
"context"
"errors"
"sync"
"time"
)
// Priority represents the priority level of a task.
// Higher values indicate higher priority.
type Priority int
const (
// PriorityLow is for non-urgent background tasks.
PriorityLow Priority = 1
// PriorityNormal is the default priority for regular tasks.
PriorityNormal Priority = 5
// PriorityHigh is for urgent tasks that should be processed quickly.
PriorityHigh Priority = 10
// PriorityCritical is for critical tasks that must be processed immediately.
PriorityCritical Priority = 100
)
// Task represents a unit of work to be executed by the queue.
// Tasks are executed asynchronously by worker goroutines.
type Task struct {
// ID is a unique identifier for the task.
// It's used for tracking and logging purposes.
ID string
// Priority determines the execution order.
// Tasks with higher priority are executed first.
Priority Priority
// Handler is the function that performs the actual work.
// It receives a context that may be cancelled if the task times out.
Handler func(context.Context) error
// Timeout specifies the maximum duration for task execution.
// Zero means no timeout. Default is 30 seconds.
Timeout time.Duration
// Retries specifies the number of retry attempts on failure.
// Zero means no retries. Default is 0.
Retries int
}
// Config holds configuration options for the queue.
type Config struct {
// Workers is the number of worker goroutines.
// Default is the number of CPU cores.
Workers int
// BufferSize is the capacity of the internal task buffer.
// Larger values allow more tasks to be queued but use more memory.
// Default is 100.
BufferSize int
// DefaultTimeout is the default timeout for tasks without a specific timeout.
// Default is 30 seconds.
DefaultTimeout time.Duration
// ShutdownTimeout is the maximum time to wait for tasks to complete during shutdown.
// Default is 10 seconds.
ShutdownTimeout time.Duration
}
// Queue manages task submission and execution.
// It maintains a pool of worker goroutines that process tasks concurrently.
//
// All methods are safe for concurrent use by multiple goroutines.
type Queue struct {
config Config
tasks chan Task
workers []*worker
wg sync.WaitGroup
mu sync.RWMutex
running bool
}
// New creates a new Queue with the given configuration.
// If config is nil, default values are used.
//
// The queue is created in a stopped state. Call Start to begin processing tasks.
func New(config Config) *Queue {
// Set defaults
if config.Workers <= 0 {
config.Workers = runtime.NumCPU()
}
if config.BufferSize <= 0 {
config.BufferSize = 100
}
if config.DefaultTimeout == 0 {
config.DefaultTimeout = 30 * time.Second
}
if config.ShutdownTimeout == 0 {
config.ShutdownTimeout = 10 * time.Second
}
return &Queue{
config: config,
tasks: make(chan Task, config.BufferSize),
}
}
// Start begins processing tasks.
// It spawns worker goroutines according to the configuration.
//
// This method is idempotent - calling it multiple times has no effect
// if the queue is already running.
//
// Returns an error if the queue is already stopped and cannot be restarted.
func (q *Queue) Start() error {
q.mu.Lock()
defer q.mu.Unlock()
if q.running {
return nil
}
// Start workers
q.workers = make([]*worker, q.config.Workers)
for i := 0; i < q.config.Workers; i++ {
w := &worker{
id: i,
tasks: q.tasks,
}
q.workers[i] = w
q.wg.Add(1)
go w.run(&q.wg)
}
q.running = true
return nil
}
// Submit adds a task to the queue for processing.
// The task will be executed by an available worker goroutine.
//
// If the task buffer is full, this method blocks until space becomes available
// or the context is cancelled.
//
// Returns an error if:
// - The queue is not running
// - The task ID is empty
// - The task handler is nil
// - The context is cancelled
func (q *Queue) Submit(task Task) error {
q.mu.RLock()
defer q.mu.RUnlock()
if !q.running {
return errors.New("queue is not running")
}
if task.ID == "" {
return errors.New("task ID cannot be empty")
}
if task.Handler == nil {
return errors.New("task handler cannot be nil")
}
// Set default timeout if not specified
if task.Timeout == 0 {
task.Timeout = q.config.DefaultTimeout
}
select {
case q.tasks <- task:
return nil
default:
return errors.New("queue is full")
}
}
// Stop gracefully shuts down the queue.
// It waits for all currently executing tasks to complete,
// up to the configured shutdown timeout.
//
// Tasks that are queued but not yet started will not be executed.
// This method blocks until all workers have stopped or the timeout expires.
//
// After calling Stop, the queue cannot be restarted.
func (q *Queue) Stop() error {
q.mu.Lock()
if !q.running {
q.mu.Unlock()
return nil
}
q.running = false
close(q.tasks)
q.mu.Unlock()
// Wait for workers with timeout
done := make(chan struct{})
go func() {
q.wg.Wait()
close(done)
}()
select {
case <-done:
return nil
case <-time.After(q.config.ShutdownTimeout):
return errors.New("shutdown timeout exceeded")
}
}
// Stats returns current queue statistics.
// This method is safe for concurrent use.
func (q *Queue) Stats() Stats {
q.mu.RLock()
defer q.mu.RUnlock()
return Stats{
Workers: q.config.Workers,
QueuedTasks: len(q.tasks),
Running: q.running,
}
}
// Stats contains queue statistics.
type Stats struct {
// Workers is the number of worker goroutines.
Workers int
// QueuedTasks is the number of tasks waiting to be processed.
QueuedTasks int
// Running indicates whether the queue is currently processing tasks.
Running bool
}
ํ ์คํธ์ ์์
// taskqueue_test.go
package taskqueue_test
import (
"context"
"fmt"
"time"
"yourmodule/taskqueue"
)
// ExampleQueue demonstrates basic queue usage.
func ExampleQueue() {
// Create and start queue
queue := taskqueue.New(taskqueue.Config{
Workers: 2,
BufferSize: 10,
})
queue.Start()
defer queue.Stop()
// Submit a simple task
err := queue.Submit(taskqueue.Task{
ID: "example-task",
Priority: taskqueue.PriorityNormal,
Handler: func(ctx context.Context) error {
fmt.Println("Task executed!")
return nil
},
})
if err != nil {
fmt.Println("Error:", err)
return
}
// Wait a bit for task to complete
time.Sleep(100 * time.Millisecond)
// Output: Task executed!
}
// ExampleQueue_priority demonstrates priority-based execution.
func ExampleQueue_priority() {
queue := taskqueue.New(taskqueue.Config{
Workers: 1, // Single worker to see priority effect
BufferSize: 10,
})
queue.Start()
defer queue.Stop()
// Submit tasks with different priorities
tasks := []struct {
id string
priority taskqueue.Priority
}{
{"low", taskqueue.PriorityLow},
{"high", taskqueue.PriorityHigh},
{"normal", taskqueue.PriorityNormal},
{"critical", taskqueue.PriorityCritical},
}
for _, t := range tasks {
queue.Submit(taskqueue.Task{
ID: t.id,
Priority: t.priority,
Handler: func(ctx context.Context) error {
fmt.Printf("Executing: %s\n", t.id)
return nil
},
})
}
time.Sleep(500 * time.Millisecond)
// Output:
// Executing: critical
// Executing: high
// Executing: normal
// Executing: low
}
// ExampleQueue_timeout demonstrates task timeout handling.
func ExampleQueue_timeout() {
queue := taskqueue.New(taskqueue.Config{
Workers: 1,
})
queue.Start()
defer queue.Stop()
// Submit a task with short timeout
queue.Submit(taskqueue.Task{
ID: "timeout-task",
Timeout: 100 * time.Millisecond,
Handler: func(ctx context.Context) error {
select {
case <-time.After(1 * time.Second):
return nil
case <-ctx.Done():
fmt.Println("Task cancelled due to timeout")
return ctx.Err()
}
},
})
time.Sleep(200 * time.Millisecond)
// Output: Task cancelled due to timeout
}
๐ pkg.go.dev์ ๋ฌธ์ ๊ฒ์ํ๊ธฐ
Go ๋ชจ๋์ ๊ณต๊ฐํ๋ค๋ฉด pkg.go.dev์ ์๋์ผ๋ก ๋ฌธ์๊ฐ ๊ฒ์๋ผ. ์ด๊ฑด Go ์ปค๋ฎค๋ํฐ์ ๊ณต์ ํจํค์ง ๋ฌธ์ ์ฌ์ดํธ์ผ! ๐
2๋จ๊ณ: go.mod ํ์ผ์ด ์๋์ง ํ์ธ
3๋จ๊ณ: ํ๊ทธ๋ก ๋ฒ์ ์ง์ (์: v1.0.0)
4๋จ๊ณ: pkg.go.dev๊ฐ ์๋์ผ๋ก ๋ฐ๊ฒฌํ๊ณ ์ธ๋ฑ์ฑ
5๋จ๊ณ: ๋ฌธ์๊ฐ ์๋ ์์ฑ๋์ด ๊ณต๊ฐ๋จ
๋๊ตฐ๊ฐ ๋น์ ์ ํจํค์ง๋ฅผ go getํ๋ฉด pkg.go.dev๊ฐ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ๋ฌธ์๋ฅผ ์์ฑํด. ๋ณ๋๋ก ํ ์ผ์ด ์์ด! ๐
โข README.md ํ์ผ์ ์์ฑํด (ํจํค์ง ๊ฐ์๋ก ์ฌ์ฉ๋จ)
โข LICENSE ํ์ผ์ ํฌํจํด
โข ์์ ์ฝ๋๋ฅผ ์ถฉ๋ถํ ์์ฑํด
โข ๋ชจ๋ ๊ณต๊ฐ API์ ์ฃผ์์ ๋ฌ์
๐ฏ ๋ฌธ์ํ ์ฒดํฌ๋ฆฌ์คํธ
ํ๋ก์ ํธ๋ฅผ ๋ฐฐํฌํ๊ธฐ ์ ์ ์ด ์ฒดํฌ๋ฆฌ์คํธ๋ฅผ ํ์ธํด๋ด! โ
| ํญ๋ชฉ | ์ค๋ช | ์ค์๋ |
|---|---|---|
| ํจํค์ง ์ฃผ์ | ํจํค์ง์ ๋ชฉ์ ๊ณผ ์ฌ์ฉ๋ฒ ์ค๋ช | ํ์ |
| ๊ณต๊ฐ ํจ์ ์ฃผ์ | ๋ชจ๋ exported ํจ์์ ์ฃผ์ | ํ์ |
| ๊ณต๊ฐ ํ์ ์ฃผ์ | ๋ชจ๋ exported ํ์ ์ ์ฃผ์ | ํ์ |
| ์์ ์ฝ๋ | ์ฃผ์ ๊ธฐ๋ฅ์ ๋ํ ์์ | ๊ถ์ฅ |
| ์๋ฌ ๋ฌธ์ํ | ๋ฐ์ ๊ฐ๋ฅํ ์๋ฌ ์ค๋ช | ๊ถ์ฅ |
| README.md | ํ๋ก์ ํธ ๊ฐ์ ๋ฐ ์์ ๊ฐ์ด๋ | ํ์ |
| LICENSE | ๋ผ์ด์ ์ค ์ ๋ณด | ํ์ |
| CHANGELOG | ๋ฒ์ ๋ณ ๋ณ๊ฒฝ ์ฌํญ | ๊ถ์ฅ |
| ์ฑ๋ฅ ์ ๋ณด | ์๊ฐ/๊ณต๊ฐ ๋ณต์ก๋ ๋ฑ | ์ ํ |
| ์ค๋ ๋ ์์ ์ฑ | ๋์์ฑ ๊ด๋ จ ์ ๋ณด | ๊ถ์ฅ |
๐ค ํ ํ๋ก์ ํธ์์์ ๋ฌธ์ํ
ํผ์ ๊ฐ๋ฐํ ๋๋ ๋ฌธ์ํ๊ฐ ์ค์ํ์ง๋ง, ํ ํ๋ก์ ํธ์์๋ ๋๋์ฑ ์ค์ํด! ํนํ **์ฌ๋ฅ๋ท**์์ ํ ํ๋ก์ ํธ๋ฅผ ์งํํ๊ฑฐ๋ ํ์ ํ ๋ ๋ง์ด์ผ. ๐ค
๋ฌธ์ํ ๊ท์น ์ ํ๊ธฐ
ํ์์ ์ผ๊ด๋ ๋ฌธ์๋ฅผ ์์ฑํ๋ ค๋ฉด ๊ท์น์ด ํ์ํด. ๊ฐ๋จํ ๊ฐ์ด๋๋ผ์ธ์ ๋ง๋ค์ด๋ณด์:
# CONTRIBUTING.md
## ๋ฌธ์ํ ๊ฐ์ด๋๋ผ์ธ
### ํ์ ์ฌํญ
- ๋ชจ๋ ๊ณต๊ฐ ํจ์/ํ์
์ ์ฃผ์ ์์ฑ
- ์ฃผ์์ ์ ์ธ ์ด๋ฆ์ผ๋ก ์์
- ์์ ํ ๋ฌธ์ฅ์ผ๋ก ์์ฑ (๋ง์นจํ ํฌํจ)
- ์๋ฌ ์กฐ๊ฑด ๋ช
์
### ๊ถ์ฅ ์ฌํญ
- ๋ณต์กํ ๋ก์ง์๋ ์์ ์ฝ๋ ์ถ๊ฐ
- ์ฑ๋ฅ ํน์ฑ ๋ฌธ์ํ
- ์ค๋ ๋ ์์ ์ฑ ๋ช
์
### ์ฝ๋ ๋ฆฌ๋ทฐ ์ฒดํฌ๋ฆฌ์คํธ
- [ ] ๋ชจ๋ ๊ณต๊ฐ API์ ์ฃผ์์ด ์๋๊ฐ?
- [ ] ์ฃผ์์ด ์ฌ๋ฐ๋ฅธ ํ์์ธ๊ฐ?
- [ ] ์์ ์ฝ๋๊ฐ ๋์ํ๋๊ฐ?
- [ ] golint ๊ฒฝ๊ณ ๊ฐ ์๋๊ฐ?
๋ฌธ์ ๋ฆฌ๋ทฐ ํ๋ก์ธ์ค
์ฝ๋ ๋ฆฌ๋ทฐํ ๋ ๋ฌธ์๋ ํจ๊ป ๋ฆฌ๋ทฐํด์ผ ํด. ๋ค์ ์ฌํญ๋ค์ ์ฒดํฌํด๋ด:
์์ ์ฑ: ํ์ํ ๋ชจ๋ ์ ๋ณด๊ฐ ํฌํจ๋์ด ์๋๊ฐ?
์ ํ์ฑ: ์ฝ๋์ ๋ฌธ์๊ฐ ์ผ์นํ๋๊ฐ?
์์ : ์์ ์ฝ๋๊ฐ ์ค์ ๋ก ๋์ํ๋๊ฐ?
์ผ๊ด์ฑ: ํ๋ก์ ํธ์ ๋ค๋ฅธ ๋ฌธ์์ ์คํ์ผ์ด ์ผ๊ด์ ์ธ๊ฐ?
๐ ๋ฌธ์ ์ ์ง๋ณด์
๋ฌธ์๋ ํ ๋ฒ ์์ฑํ๊ณ ๋์ด ์๋์ผ. ์ฝ๋๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ฌธ์๋ ํจ๊ป ์ ๋ฐ์ดํธํด์ผ ํด! ๐
๋ฌธ์ ์ ๋ฐ์ดํธ ์ ๋ต
๋ฌธ์๋ฅผ ์ต์ ์ํ๋ก ์ ์งํ๋ ํ:
1. ์ฝ๋์ ํจ๊ป ์ปค๋ฐ
์ฝ๋๋ฅผ ์์ ํ ๋ ๊ด๋ จ ๋ฌธ์๋ ํจ๊ป ์์ ํ๊ณ ๊ฐ์ ์ปค๋ฐ์ ํฌํจ์์ผ.
2. PR ์ฒดํฌ๋ฆฌ์คํธ ํ์ฉ
Pull Request ํ
ํ๋ฆฟ์ ๋ฌธ์ ์
๋ฐ์ดํธ ํญ๋ชฉ์ ํฌํจ์์ผ.
## PR ์ฒดํฌ๋ฆฌ์คํธ
- [ ] ์ฝ๋ ๋ณ๊ฒฝ์ฌํญ ๊ตฌํ
- [ ] ํ
์คํธ ์์ฑ/์
๋ฐ์ดํธ
- [ ] ๋ฌธ์ ์
๋ฐ์ดํธ
- [ ] CHANGELOG ์
๋ฐ์ดํธ
- [ ] ์์ ์ฝ๋ ๊ฒ์ฆ
3. ์๋ํ ๋๊ตฌ ํ์ฉ
CI/CD ํ์ดํ๋ผ์ธ์ ๋ฌธ์ ๊ฒ์ฆ ๋จ๊ณ๋ฅผ ์ถ๊ฐํด.
# .github/workflows/docs-check.yml
name: Documentation Check
on: [pull_request]
jobs:
docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Check documentation
run: |
go install golang.org/x/lint/golint@latest
golint -set_exit_status ./...
- name: Verify examples
run: |
go test -run Example
๐ก ์ค์ ์๋๋ฆฌ์ค์ ํด๊ฒฐ์ฑ
์ค์ ๋ก ๊ฐ๋ฐํ๋ค ๋ณด๋ฉด ์ฌ๋ฌ ์ํฉ์ ๋ถ๋ชํ๊ฒ ๋ผ. ๋ช ๊ฐ์ง ํํ ์๋๋ฆฌ์ค์ ํด๊ฒฐ์ฑ ์ ๊ณต์ ํ ๊ฒ! ๐ฏ
์๋๋ฆฌ์ค 1: ๋ ๊ฑฐ์ ์ฝ๋ ๋ฌธ์ํ
๋ฌธ์๊ฐ ์ ํ ์๋ ๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ๋งก์๋ค๋ฉด? ๐ฑ
2๋จ๊ณ: ์์ฃผ ์ฌ์ฉ๋๋ ํจ์ ์ฐ์ ์์๋ก
3๋จ๊ณ: ๋ณต์กํ ๋ก์ง์ ์ง์ค
4๋จ๊ณ: ์ ์ง์ ์ผ๋ก ํ๋
5๋จ๊ณ: ์๋ก์ด ์ฝ๋๋ ๋ฐ๋์ ๋ฌธ์ํ
์๋๋ฆฌ์ค 2: ๋ด๋ถ ๊ตฌํ vs ๊ณต๊ฐ API
๋ด๋ถ ํจ์๋ ๋ฌธ์ํํด์ผ ํ ๊น? ๐ค
๋ต: ๋ณต์กํ ๋ด๋ถ ๋ก์ง์ ๋ฌธ์ํํ๋ ๊ฒ ์ข์. ํ์ง๋ง ๊ณต๊ฐ API๋งํผ ์์ธํ ํ์๋ ์์ด. ํต์ฌ๋ง ๊ฐ๋จํ!
// ๊ณต๊ฐ API - ์์ธํ ๋ฌธ์
// ProcessData processes the input data according to the specified rules.
// It validates the input, applies transformations, and returns the result.
//
// The function supports multiple data formats including JSON and XML.
// Invalid input returns ErrInvalidFormat.
//
// Example:
// result, err := ProcessData(input, rules)
func ProcessData(input []byte, rules Rules) ([]byte, error) {
// ...
}
// ๋ด๋ถ ํจ์ - ๊ฐ๋จํ ์ค๋ช
// validateInput checks if the input data is valid.
// Returns error if validation fails.
func validateInput(input []byte) error {
// ...
}
์๋๋ฆฌ์ค 3: ๋ณต์กํ ์๊ณ ๋ฆฌ์ฆ ์ค๋ช
๋ณต์กํ ์๊ณ ๋ฆฌ์ฆ์ ์ด๋ป๊ฒ ๋ฌธ์ํํ ๊น? ๐
// FindShortestPath finds the shortest path between two nodes using Dijkstra's algorithm.
//
// # Algorithm
//
// The function implements Dijkstra's shortest path algorithm:
// 1. Initialize distances to all nodes as infinite except start node (0)
// 2. Create a priority queue with the start node
// 3. While queue is not empty:
// - Extract node with minimum distance
// - For each neighbor, calculate tentative distance
// - Update distance if shorter path found
// 4. Return the path and total distance
//
// # Complexity
//
// Time: O((V + E) log V) where V is vertices and E is edges
// Space: O(V) for the distance map and priority queue
//
// # Example
//
// graph := NewGraph()
// graph.AddEdge("A", "B", 5)
// graph.AddEdge("B", "C", 3)
//
// path, distance := FindShortestPath(graph, "A", "C")
// // path: ["A", "B", "C"], distance: 8
//
func FindShortestPath(graph *Graph, start, end string) ([]string, int, error) {
// implementation
}
๐ ๋ฌธ์ํ์ ๋ฏธ๋
Go์ ๋ฌธ์ํ ๋๊ตฌ๋ ๊ณ์ ๋ฐ์ ํ๊ณ ์์ด. ์์ผ๋ก ์ด๋ค ๋ณํ๊ฐ ์์์ง ์ด์ง ์ฟ๋ณด์! ๐ฎ
Go 1.19+ ์ ๊ฐ์ ์ฌํญ
์ต๊ทผ Go ๋ฒ์ ์์๋ ๋ฌธ์ํ ๊ด๋ จ ์ฌ๋ฌ ๊ฐ์ ์ด ์์์ด:
โข ๋งํฌ ์ง์ ๊ฐํ: ์๋ ๋งํฌ ์์ฑ ๊ฐ์
โข ๋งํฌ๋ค์ด ์คํ์ผ: ํค๋ฉ, ๋ฆฌ์คํธ ๋ฑ ๋ ๋์ ํฌ๋งทํ
โข ์ฝ๋ ๋ธ๋ก: ๋ ๋์ ์ฝ๋ ํ์ด๋ผ์ดํ
์ปค๋ฎค๋ํฐ ๋๊ตฌ๋ค
godoc ์ธ์๋ ์ ์ฉํ ๋๊ตฌ๋ค์ด ๋ง์:
gomarkdoc: Markdown ํ์์ผ๋ก ๋ฌธ์ ์์ฑ
swag: Swagger/OpenAPI ๋ฌธ์ ์๋ ์์ฑ
pkgsite: pkg.go.dev๋ฅผ ๋ก์ปฌ์์ ์คํ
doctoc: README์ ๋ชฉ์ฐจ ์๋ ์์ฑ
๐ ํ์ต ๋ฆฌ์์ค
๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด ์ด๋ฐ ๋ฆฌ์์ค๋ค์ ์ถ์ฒํด! ๐
โข pkg.go.dev - Go ํจํค์ง ๋ฌธ์
โข Godoc: documenting Go code - ๊ณต์ ๋ธ๋ก๊ทธ
โข ์ ๋ช ์คํ์์ค ํ๋ก์ ํธ (Docker, Kubernetes ๋ฑ)
โข Effective Go - Go ํ๋ก๊ทธ๋๋ฐ ๋ฒ ์คํธ ํ๋ํฐ์ค
๐ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์๋ค๋ ๋๋จํด! ๐ ์ฐ๋ฆฌ๊ฐ ํจ๊ป ๋ฐฐ์ด ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด์:
๋ฌธ์ํ๋ ์ฒ์์ ๊ท์ฐฎ๊ฒ ๋๊ปด์ง ์ ์์ด. ํ์ง๋ง ์ต๊ด์ด ๋๋ฉด ์์ฐ์ค๋ฝ๊ฒ ๋ผ. ๊ทธ๋ฆฌ๊ณ ๋์ค์ ์ ๋ง ํฐ ๋์์ด ๋ผ! ๐
ํนํ ์คํ์์ค ํ๋ก์ ํธ๋ฅผ ํ๊ฑฐ๋, ํ์์ ์ผํ๊ฑฐ๋, ํ๋ฆฌ๋์๋ก ์ผํ ๋ ์ ์์ฑ๋ ๋ฌธ์๋ ๋น์ ์ ์ ๋ฌธ์ฑ์ ๋ณด์ฌ์ฃผ๋ ๊ฐ๋ ฅํ ๋ฌด๊ธฐ๊ฐ ๋ผ. ์ฝ๋๋ง ์ ์ง๋ ๊ฐ๋ฐ์๊ฐ ์๋๋ผ, ์ปค๋ฎค๋์ผ์ด์ ๋ ์ํ๋ ๊ฐ๋ฐ์๋ผ๋ ์ธ์์ ์ฃผ๊ฑฐ๋ ! ๐ช
๊ทธ๋ฆฌ๊ณ ๊ธฐ์ตํด, ์ข์ ๋ฌธ์๋ ๋ฏธ๋์ ๋น์ ๊ณผ ๋๋ฃ๋ค์๊ฒ ์ฃผ๋ ์ ๋ฌผ์ด์ผ. ๐ 6๊ฐ์ ํ์ "์, ๊ทธ๋ ๋ฌธ์ ์ ์จ๋จ๋ค!" ํ๋ ์๊ฐ์ด ๋ฐ๋์ ์ฌ ๊ฑฐ์ผ!
์ด์ ๋น์ ๋ Go ๋ฌธ์ํ ๋ง์คํฐ! ๐ ์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๊ณ , ๋ฉ์ง API ๋ฌธ์๋ฅผ ๋ง๋ค์ด๋ด. ํ์ดํ ! ๐
๐ Happy Documenting! ๐
์ฝ๋๋ ์ค์ํ์ง๋ง, ๋ฌธ์๋ ์ฝ๋๋ฅผ ๋น๋๊ฒ ํด์ฃผ๋ ๋ง๋ฒ์ด์ผ! โจ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.