Documentation
¶
Overview ¶
Package websvrutil provides extreme simplicity web server utilities for building production-ready HTTP servers. It dramatically reduces the complexity of web server setup by providing a high-level, easy-to-use API that handles common server patterns including routing, middleware, context management, sessions, CSRF protection, template rendering, and graceful shutdown.
The package is designed with the following principles:
- Simplicity: Reduce 50+ lines of boilerplate server setup code to just 5 lines
- Safety: Built-in CSRF protection, secure session management, and panic recovery
- Performance: Optimized context pooling, efficient middleware chain, and zero-allocation paths
- Flexibility: Extensible middleware system and customizable options
- Production-ready: Graceful shutdown, health checks, and comprehensive error handling
Main Features:
- App Management: Create and manage HTTP server lifecycle with App struct
- Routing: Flexible route registration with support for path parameters and HTTP methods
- Context: Enhanced request/response handling through Context abstraction
- Middleware: Composable middleware chain for cross-cutting concerns
- Session: Secure session management with multiple storage backends
- CSRF: Built-in Cross-Site Request Forgery protection
- Template: Template engine integration with layout support
- Validation: Request data validation with detailed error messages
- Graceful Shutdown: Safe server shutdown with connection draining
Basic Usage:
app := websvrutil.New()
app.GET("/", func(c *websvrutil.Context) error {
return c.String(200, "Hello, World!")
})
app.Start(":8080")
Advanced Usage with Middleware:
app := websvrutil.New(
websvrutil.WithLogger(logger),
websvrutil.WithCSRF(),
websvrutil.WithSession(sessionStore),
)
// Group routes with common middleware
api := app.Group("/api", authMiddleware)
api.GET("/users", listUsers)
api.POST("/users", createUser)
// Start with graceful shutdown
app.StartWithGracefulShutdown(":8080", 30*time.Second)
Performance Characteristics:
- Context pooling reduces GC pressure by reusing Context objects
- Middleware chain is pre-compiled for zero-overhead execution
- Route matching uses efficient trie-based algorithm
- Template caching minimizes repeated parsing
Thread Safety:
- App struct is safe for concurrent use after initialization
- Context objects are NOT thread-safe and should not be shared between goroutines
- Session operations are thread-safe when using appropriate storage backend
Version information is loaded dynamically from cfg/app.yaml.
websvrutil 패키지는 프로덕션 수준의 HTTP 서버를 구축하기 위한 매우 간단한 웹 서버 유틸리티를 제공합니다. 라우팅, 미들웨어, 컨텍스트 관리, 세션, CSRF 보호, 템플릿 렌더링, 그레이스풀 셧다운을 포함한 일반적인 서버 패턴을 처리하는 높은 수준의 사용하기 쉬운 API를 제공하여 웹 서버 설정의 복잡성을 극적으로 줄입니다.
이 패키지는 다음 원칙으로 설계되었습니다:
- 단순성: 50줄 이상의 보일러플레이트 서버 설정 코드를 단 5줄로 축소
- 안전성: 내장 CSRF 보호, 안전한 세션 관리, 패닉 복구
- 성능: 최적화된 컨텍스트 풀링, 효율적인 미들웨어 체인, 제로 할당 경로
- 유연성: 확장 가능한 미들웨어 시스템과 커스터마이징 가능한 옵션
- 프로덕션 준비: 그레이스풀 셧다운, 헬스 체크, 포괄적인 에러 처리
주요 기능:
- 앱 관리: App 구조체로 HTTP 서버 생명주기 생성 및 관리
- 라우팅: 경로 매개변수와 HTTP 메서드를 지원하는 유연한 라우트 등록
- 컨텍스트: Context 추상화를 통한 향상된 요청/응답 처리
- 미들웨어: 횡단 관심사를 위한 조합 가능한 미들웨어 체인
- 세션: 여러 저장소 백엔드를 지원하는 안전한 세션 관리
- CSRF: 내장 크로스 사이트 요청 위조 보호
- 템플릿: 레이아웃 지원을 갖춘 템플릿 엔진 통합
- 검증: 상세한 에러 메시지를 제공하는 요청 데이터 검증
- 그레이스풀 셧다운: 연결 드레이닝을 통한 안전한 서버 종료
기본 사용법:
app := websvrutil.New()
app.GET("/", func(c *websvrutil.Context) error {
return c.String(200, "Hello, World!")
})
app.Start(":8080")
미들웨어를 사용한 고급 사용법:
app := websvrutil.New(
websvrutil.WithLogger(logger),
websvrutil.WithCSRF(),
websvrutil.WithSession(sessionStore),
)
// 공통 미들웨어로 라우트 그룹화
api := app.Group("/api", authMiddleware)
api.GET("/users", listUsers)
api.POST("/users", createUser)
// 그레이스풀 셧다운으로 시작
app.StartWithGracefulShutdown(":8080", 30*time.Second)
성능 특성:
- 컨텍스트 풀링은 Context 객체를 재사용하여 GC 압력을 줄임
- 미들웨어 체인은 오버헤드 없는 실행을 위해 사전 컴파일됨
- 라우트 매칭은 효율적인 트라이 기반 알고리즘 사용
- 템플릿 캐싱은 반복적인 파싱을 최소화
스레드 안전성:
- App 구조체는 초기화 후 동시 사용이 안전함
- Context 객체는 스레드 안전하지 않으며 고루틴 간 공유하면 안 됨
- 세션 작업은 적절한 저장소 백엔드를 사용할 때 스레드 안전함
버전 정보는 cfg/app.yaml에서 동적으로 로드됩니다.
Example ¶
Example demonstrates a complete application setup with middleware and routes. Example은 미들웨어와 라우트가 포함된 완전한 애플리케이션 설정을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
// Add middleware
// 미들웨어 추가
app.Use(websvrutil.Logger())
app.Use(websvrutil.Recovery())
// Register routes
// 라우트 등록
app.GET("/", func(w http.ResponseWriter, r *http.Request) {
ctx := websvrutil.GetContext(r)
ctx.JSON(200, map[string]string{"message": "Hello World"})
})
app.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
ctx := websvrutil.GetContext(r)
id := ctx.Param("id")
ctx.JSON(200, map[string]string{"user_id": id})
})
fmt.Println("Application configured successfully")
}
Output: Application configured successfully
Index ¶
- Constants
- Variables
- func GetCSRFToken(ctx *Context) string
- type App
- func (a *App) AddTemplateFunc(name string, fn interface{})
- func (a *App) AddTemplateFuncs(funcs map[string]interface{})
- func (a *App) DELETE(pattern string, handler http.HandlerFunc) *App
- func (a *App) GET(pattern string, handler http.HandlerFunc) *App
- func (a *App) Group(prefix string) *Group
- func (a *App) HEAD(pattern string, handler http.HandlerFunc) *App
- func (a *App) LoadTemplate(name string) error
- func (a *App) LoadTemplates(pattern string) error
- func (a *App) NotFound(handler http.HandlerFunc) *App
- func (a *App) OPTIONS(pattern string, handler http.HandlerFunc) *App
- func (a *App) PATCH(pattern string, handler http.HandlerFunc) *App
- func (a *App) POST(pattern string, handler http.HandlerFunc) *App
- func (a *App) PUT(pattern string, handler http.HandlerFunc) *App
- func (a *App) ReloadTemplates() error
- func (a *App) Run(addr string) error
- func (a *App) RunWithGracefulShutdown(addr string, timeout time.Duration) error
- func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (a *App) Shutdown(ctx context.Context) error
- func (a *App) Static(prefix, dir string) *App
- func (a *App) TemplateEngine() *TemplateEngine
- func (a *App) Use(middleware ...MiddlewareFunc) *App
- type BasicAuthConfig
- type BodyLimitConfig
- type CORSConfig
- type CSRFConfig
- type CompressionConfig
- type Context
- func (c *Context) AbortWithError(code int, message string)
- func (c *Context) AbortWithJSON(code int, obj interface{})
- func (c *Context) AbortWithStatus(code int)
- func (c *Context) AcceptsHTML() bool
- func (c *Context) AcceptsJSON() bool
- func (c *Context) AcceptsXML() bool
- func (c *Context) AddHeader(key, value string)
- func (c *Context) BadRequest()
- func (c *Context) Bind(obj interface{}) error
- func (c *Context) BindForm(obj interface{}) error
- func (c *Context) BindJSON(obj interface{}) error
- func (c *Context) BindQuery(obj interface{}) error
- func (c *Context) BindWithValidation(obj interface{}) error
- func (c *Context) ClientIP() string
- func (c *Context) ContentType() string
- func (c *Context) Context() context.Context
- func (c *Context) Cookie(name string) (*http.Cookie, error)
- func (c *Context) CookieValue(name string) string
- func (c *Context) Delete(key string)
- func (c *Context) DeleteCookie(name, path string)
- func (c *Context) Error(code int, message string) error
- func (c *Context) ErrorJSON(code int, message string)
- func (c *Context) Exists(key string) bool
- func (c *Context) File(filepath string) error
- func (c *Context) FileAttachment(filepath, filename string) error
- func (c *Context) Forbidden()
- func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
- func (c *Context) Get(key string) (interface{}, bool)
- func (c *Context) GetBool(key string) bool
- func (c *Context) GetCookie(name string) string
- func (c *Context) GetFloat64(key string) float64
- func (c *Context) GetHeader(key string) string
- func (c *Context) GetHeaders(key string) []string
- func (c *Context) GetInt(key string) int
- func (c *Context) GetInt64(key string) int64
- func (c *Context) GetString(key string) string
- func (c *Context) GetStringMap(key string) map[string]interface{}
- func (c *Context) GetStringSlice(key string) []string
- func (c *Context) HTML(code int, html string) error
- func (c *Context) HTMLTemplate(code int, tmpl string, data interface{}) error
- func (c *Context) Header(key string) string
- func (c *Context) HeaderExists(key string) bool
- func (c *Context) InternalServerError()
- func (c *Context) IsAjax() bool
- func (c *Context) IsDELETE() bool
- func (c *Context) IsGET() bool
- func (c *Context) IsHEAD() bool
- func (c *Context) IsOPTIONS() bool
- func (c *Context) IsPATCH() bool
- func (c *Context) IsPOST() bool
- func (c *Context) IsPUT() bool
- func (c *Context) IsWebSocket() bool
- func (c *Context) JSON(code int, data interface{}) error
- func (c *Context) JSONIndent(code int, data interface{}, prefix, indent string) error
- func (c *Context) JSONPretty(code int, data interface{}) error
- func (c *Context) Keys() []string
- func (c *Context) Method() string
- func (c *Context) MultipartForm() (*multipart.Form, error)
- func (c *Context) MustGet(key string) interface{}
- func (c *Context) NoContent()
- func (c *Context) NotFound()
- func (c *Context) Param(name string) string
- func (c *Context) Params() map[string]string
- func (c *Context) Path() string
- func (c *Context) Query(key string) string
- func (c *Context) QueryDefault(key, defaultValue string) string
- func (c *Context) Redirect(code int, url string)
- func (c *Context) Referer() string
- func (c *Context) Render(code int, name string, data interface{}) error
- func (c *Context) RenderWithLayout(code int, layoutName, templateName string, data interface{}) error
- func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
- func (c *Context) Set(key string, value interface{})
- func (c *Context) SetCookie(cookie *http.Cookie)
- func (c *Context) SetCookieAdvanced(opts CookieOptions)
- func (c *Context) SetHeader(key, value string)
- func (c *Context) Status(code int)
- func (c *Context) SuccessJSON(code int, message string, data interface{})
- func (c *Context) Text(code int, text string) error
- func (c *Context) Textf(code int, format string, args ...interface{}) error
- func (c *Context) Unauthorized()
- func (c *Context) UserAgent() string
- func (c *Context) WithContext(ctx context.Context) *Context
- func (c *Context) Write(data []byte) (int, error)
- func (c *Context) WriteString(s string) (int, error)
- func (c *Context) XML(code int, xml string) error
- type CookieOptions
- type DefaultValidator
- type Group
- func (g *Group) DELETE(pattern string, handler http.HandlerFunc) *Group
- func (g *Group) GET(pattern string, handler http.HandlerFunc) *Group
- func (g *Group) Group(prefix string) *Group
- func (g *Group) HEAD(pattern string, handler http.HandlerFunc) *Group
- func (g *Group) OPTIONS(pattern string, handler http.HandlerFunc) *Group
- func (g *Group) PATCH(pattern string, handler http.HandlerFunc) *Group
- func (g *Group) POST(pattern string, handler http.HandlerFunc) *Group
- func (g *Group) PUT(pattern string, handler http.HandlerFunc) *Group
- func (g *Group) Use(middleware ...MiddlewareFunc) *Group
- type LoggerConfig
- type MiddlewareFunc
- func BasicAuth(username, password string) MiddlewareFunc
- func BasicAuthWithConfig(config BasicAuthConfig) MiddlewareFunc
- func BodyLimit(maxBytes int64) MiddlewareFunc
- func BodyLimitWithConfig(config BodyLimitConfig) MiddlewareFunc
- func CORS() MiddlewareFunc
- func CORSWithConfig(config CORSConfig) MiddlewareFunc
- func CSRF() MiddlewareFunc
- func CSRFWithConfig(config CSRFConfig) MiddlewareFunc
- func Compression() MiddlewareFunc
- func CompressionWithConfig(config CompressionConfig) MiddlewareFunc
- func HTTPSRedirect() MiddlewareFunc
- func Logger() MiddlewareFunc
- func LoggerWithConfig(config LoggerConfig) MiddlewareFunc
- func RateLimiter(requests int, window time.Duration) MiddlewareFunc
- func RateLimiterWithConfig(config RateLimiterConfig) MiddlewareFunc
- func Recovery() MiddlewareFunc
- func RecoveryWithConfig(config RecoveryConfig) MiddlewareFunc
- func Redirect(to string) MiddlewareFunc
- func RedirectWithConfig(config RedirectConfig) MiddlewareFunc
- func RequestID() MiddlewareFunc
- func RequestIDWithConfig(config RequestIDConfig) MiddlewareFunc
- func SecureHeaders() MiddlewareFunc
- func SecureHeadersWithConfig(config SecureHeadersConfig) MiddlewareFunc
- func Static(root string) MiddlewareFunc
- func StaticWithConfig(config StaticConfig) MiddlewareFunc
- func Timeout(timeout time.Duration) MiddlewareFunc
- func TimeoutWithConfig(config TimeoutConfig) MiddlewareFunc
- func WWWRedirect(addWWW bool) MiddlewareFunc
- type Option
- func WithAutoReload(enable bool) Option
- func WithIdleTimeout(d time.Duration) Option
- func WithLogger(enable bool) Option
- func WithMaxBodySize(size int64) Option
- func WithMaxHeaderBytes(n int) Option
- func WithMaxUploadSize(size int64) Option
- func WithReadTimeout(d time.Duration) Option
- func WithRecovery(enable bool) Option
- func WithStaticDir(dir string) Option
- func WithStaticPrefix(prefix string) Option
- func WithTemplateDir(dir string) Option
- func WithWriteTimeout(d time.Duration) Option
- type Options
- type RateLimiterConfig
- type RecoveryConfig
- type RedirectConfig
- type RequestIDConfig
- type Route
- type Router
- func (ro *Router) DELETE(pattern string, handler http.HandlerFunc)
- func (ro *Router) GET(pattern string, handler http.HandlerFunc)
- func (ro *Router) HEAD(pattern string, handler http.HandlerFunc)
- func (ro *Router) Handle(method, pattern string, handler http.HandlerFunc)
- func (ro *Router) NotFound(handler http.HandlerFunc)
- func (ro *Router) OPTIONS(pattern string, handler http.HandlerFunc)
- func (ro *Router) PATCH(pattern string, handler http.HandlerFunc)
- func (ro *Router) POST(pattern string, handler http.HandlerFunc)
- func (ro *Router) PUT(pattern string, handler http.HandlerFunc)
- func (ro *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)
- type SecureHeadersConfig
- type Session
- func (sess *Session) Clear()
- func (sess *Session) Delete(key string)
- func (sess *Session) Get(key string) (interface{}, bool)
- func (sess *Session) GetBool(key string) bool
- func (sess *Session) GetInt(key string) int
- func (sess *Session) GetString(key string) string
- func (sess *Session) Set(key string, value interface{})
- type SessionOptions
- type SessionStore
- type StaticConfig
- type TemplateEngine
- func (e *TemplateEngine) AddFunc(name string, fn interface{})
- func (e *TemplateEngine) AddFuncs(funcs template.FuncMap)
- func (e *TemplateEngine) Clear()
- func (e *TemplateEngine) DisableAutoReload()
- func (e *TemplateEngine) EnableAutoReload() error
- func (e *TemplateEngine) Has(name string) bool
- func (e *TemplateEngine) HasLayout(name string) bool
- func (e *TemplateEngine) IsAutoReloadEnabled() bool
- func (e *TemplateEngine) List() []string
- func (e *TemplateEngine) ListLayouts() []string
- func (e *TemplateEngine) Load(name string) error
- func (e *TemplateEngine) LoadAll() error
- func (e *TemplateEngine) LoadAllLayouts() error
- func (e *TemplateEngine) LoadGlob(pattern string) error
- func (e *TemplateEngine) LoadLayout(name string) error
- func (e *TemplateEngine) Render(w io.Writer, name string, data interface{}) error
- func (e *TemplateEngine) RenderWithLayout(w io.Writer, layoutName, templateName string, data interface{}) error
- func (e *TemplateEngine) SetDelimiters(left, right string)
- func (e *TemplateEngine) SetLayoutDir(dir string)
- type TimeoutConfig
- type ValidationError
- type ValidationErrors
- type Validator
Examples ¶
Constants ¶
const ( // DefaultReadTimeout is the maximum duration for reading the entire request, including the body. // DefaultReadTimeout은 본문을 포함한 전체 요청을 읽기 위한 최대 기간입니다. DefaultReadTimeout = 15 * time.Second // DefaultWriteTimeout is the maximum duration before timing out writes of the response. // DefaultWriteTimeout은 응답 쓰기 전 타임아웃까지의 최대 기간입니다. DefaultWriteTimeout = 15 * time.Second // DefaultIdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled. // DefaultIdleTimeout은 keep-alive가 활성화되어 있을 때 다음 요청을 기다리는 최대 시간입니다. DefaultIdleTimeout = 60 * time.Second )
Default timeout configurations 기본 타임아웃 설정
const ( // DefaultMaxHeaderBytes is the maximum number of bytes the server will read parsing the request header. // DefaultMaxHeaderBytes는 서버가 요청 헤더를 파싱할 때 읽을 최대 바이트 수입니다. DefaultMaxHeaderBytes = 1 << 20 // 1 MB // DefaultMaxBodySize is the maximum size of request body for JSON/form data. // DefaultMaxBodySize는 JSON/폼 데이터에 대한 요청 본문의 최대 크기입니다. // This limit helps prevent DoS attacks via large request bodies. // 이 제한은 큰 요청 본문을 통한 DoS 공격을 방지하는 데 도움이 됩니다. DefaultMaxBodySize = 10 << 20 // 10 MB // DefaultMaxUploadSize is the maximum size for file uploads. // DefaultMaxUploadSize는 파일 업로드의 최대 크기입니다. DefaultMaxUploadSize = 32 << 20 // 32 MB )
Default size limits 기본 크기 제한
const ( // DefaultSessionMaxAge is the maximum age of a session before it expires. // DefaultSessionMaxAge는 세션이 만료되기 전까지의 최대 유효 기간입니다. DefaultSessionMaxAge = 24 * time.Hour // DefaultSessionCookieName is the default name for the session cookie. // DefaultSessionCookieName은 세션 쿠키의 기본 이름입니다. DefaultSessionCookieName = "sessionid" // DefaultSessionCleanup is the interval at which expired sessions are cleaned up. // DefaultSessionCleanup은 만료된 세션이 정리되는 간격입니다. DefaultSessionCleanup = 5 * time.Minute )
Default session configurations 기본 세션 설정
const ( // ContentTypeJSON represents JSON content type with UTF-8 charset. // ContentTypeJSON은 UTF-8 문자셋을 가진 JSON 콘텐츠 타입을 나타냅니다. ContentTypeJSON = "application/json; charset=utf-8" // ContentTypeHTML represents HTML content type with UTF-8 charset. // ContentTypeHTML은 UTF-8 문자셋을 가진 HTML 콘텐츠 타입을 나타냅니다. ContentTypeHTML = "text/html; charset=utf-8" // ContentTypeXML represents XML content type with UTF-8 charset. // ContentTypeXML은 UTF-8 문자셋을 가진 XML 콘텐츠 타입을 나타냅니다. ContentTypeXML = "application/xml; charset=utf-8" // ContentTypeText represents plain text content type with UTF-8 charset. // ContentTypeText는 UTF-8 문자셋을 가진 일반 텍스트 콘텐츠 타입을 나타냅니다. ContentTypeText = "text/plain; charset=utf-8" // ContentTypeForm represents URL-encoded form content type. // ContentTypeForm은 URL 인코딩된 폼 콘텐츠 타입을 나타냅니다. ContentTypeForm = "application/x-www-form-urlencoded" // ContentTypeMultipart represents multipart form data content type. // ContentTypeMultipart는 멀티파트 폼 데이터 콘텐츠 타입을 나타냅니다. ContentTypeMultipart = "multipart/form-data" )
Content-Type constants Content-Type 상수
const ( // HeaderContentType is the Content-Type header name. // HeaderContentType은 Content-Type 헤더 이름입니다. HeaderContentType = "Content-Type" // HeaderAccept is the Accept header name. // HeaderAccept는 Accept 헤더 이름입니다. HeaderAccept = "Accept" // HeaderAuthorization is the Authorization header name. // HeaderAuthorization은 Authorization 헤더 이름입니다. HeaderAuthorization = "Authorization" // HeaderUserAgent is the User-Agent header name. // HeaderUserAgent는 User-Agent 헤더 이름입니다. HeaderUserAgent = "User-Agent" // HeaderXForwardedFor is the X-Forwarded-For header name. // HeaderXForwardedFor는 X-Forwarded-For 헤더 이름입니다. HeaderXForwardedFor = "X-Forwarded-For" // HeaderXRealIP is the X-Real-IP header name. // HeaderXRealIP는 X-Real-IP 헤더 이름입니다. HeaderXRealIP = "X-Real-IP" )
HTTP header names HTTP 헤더 이름
Variables ¶
var Version = version.Get()
Version is the current version of the websvrutil package. It is automatically loaded from the cfg/app.yaml configuration file at package initialization time. If the version cannot be loaded (e.g., file not found or invalid format), it defaults to "v0.0.0-dev".
The version string typically follows semantic versioning (e.g., "1.0.0", "2.1.3"). This version information can be used for:
- Logging and monitoring: Include version in application logs
- Health checks: Expose version in health check endpoints
- Debugging: Identify which version is running in production
- API versioning: Use as part of API version negotiation
Example usage:
log.Printf("Starting websvrutil version: %s", websvrutil.Version)
// In a health check endpoint:
func healthCheck(c *websvrutil.Context) error {
return c.JSON(200, map[string]string{
"status": "ok",
"version": websvrutil.Version,
})
}
Note: The version is loaded once at package initialization and remains constant throughout the application lifecycle. It cannot be modified at runtime.
Version은 websvrutil 패키지의 현재 버전입니다. 패키지 초기화 시점에 cfg/app.yaml 설정 파일에서 자동으로 로드됩니다. 버전을 로드할 수 없는 경우(예: 파일을 찾을 수 없거나 형식이 잘못됨), 기본값 "v0.0.0-dev"로 설정됩니다.
버전 문자열은 일반적으로 시맨틱 버전 관리를 따릅니다(예: "1.0.0", "2.1.3"). 이 버전 정보는 다음 용도로 사용할 수 있습니다:
- 로깅 및 모니터링: 애플리케이션 로그에 버전 포함
- 헬스 체크: 헬스 체크 엔드포인트에서 버전 노출
- 디버깅: 프로덕션에서 실행 중인 버전 식별
- API 버전 관리: API 버전 협상의 일부로 사용
사용 예제:
log.Printf("Starting websvrutil version: %s", websvrutil.Version)
// 헬스 체크 엔드포인트에서:
func healthCheck(c *websvrutil.Context) error {
return c.JSON(200, map[string]string{
"status": "ok",
"version": websvrutil.Version,
})
}
주의: 버전은 패키지 초기화 시 한 번 로드되며 애플리케이션 생명주기 동안 일정하게 유지됩니다. 런타임에 수정할 수 없습니다.
Functions ¶
func GetCSRFToken ¶
GetCSRFToken retrieves the CSRF token from the context. GetCSRFToken은 컨텍스트에서 CSRF 토큰을 검색합니다.
This is useful for rendering the token in HTML forms or JavaScript. HTML 폼이나 JavaScript에서 토큰을 렌더링하는 데 유용합니다.
Example 예제:
token := websvrutil.GetCSRFToken(ctx)
// Use token in HTML form:
// <input type="hidden" name="csrf_token" value="{{.Token}}">
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App orchestrates HTTP routing, middleware execution, template rendering, and graceful shutdown. App는 HTTP 라우팅, 미들웨어 실행, 템플릿 렌더링, 우아한 종료를 총괄하는 핵심 애플리케이션 컨테이너입니다.
func New ¶
New builds a new App, applies functional options, prepares the router, and optionally loads templates. New는 새로운 App을 생성하고 함수형 옵션을 적용하며 라우터를 준비하고 필요 시 템플릿을 로드합니다.
Example:
app := websvrutil.New()
app := websvrutil.New(
websvrutil.WithReadTimeout(30 * time.Second),
websvrutil.WithLogger(true),
)
예제:
app := websvrutil.New()
app := websvrutil.New(
websvrutil.WithReadTimeout(30 * time.Second),
websvrutil.WithLogger(true),
)
Example ¶
ExampleNew demonstrates creating a new web application. ExampleNew는 새 웹 애플리케이션을 생성하는 방법을 보여줍니다.
package main
import (
"fmt"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
fmt.Printf("App created: %T\n", app)
}
Output: App created: *websvrutil.App
Example (WithOptions) ¶
ExampleNew_withOptions demonstrates creating an app with custom options. ExampleNew_withOptions는 커스텀 옵션으로 앱을 생성하는 방법을 보여줍니다.
package main
import (
"fmt"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(
websvrutil.WithTemplateDir(""),
websvrutil.WithStaticDir("custom/static"),
websvrutil.WithStaticPrefix("/assets"),
)
fmt.Printf("App created with options: %T\n", app)
}
Output: App created with options: *websvrutil.App
func (*App) AddTemplateFunc ¶
AddTemplateFunc registers a custom function that can be used in templates. This allows extending template functionality beyond Go's built-in template functions by adding domain-specific helpers, formatters, and utilities.
AddTemplateFunc enables templates to: - Format data (dates, numbers, currency, etc.) - Perform calculations and transformations - Call application logic from templates - Access helper utilities (string manipulation, etc.) - Implement custom template DSL (domain-specific language)
Purpose:
- Registers a single custom function for use in templates
- Extends template capabilities with application-specific logic
- Enables cleaner templates by moving complex logic to Go functions
- Provides reusable template utilities across all templates
- Allows templates to access application context and helpers
Parameters:
name: Function name as it will be called in templates. Must be a valid Go identifier (alphanumeric + underscore, start with letter/underscore). Case-sensitive: "formatDate" and "FormatDate" are different functions. Cannot override built-in template functions (and, or, not, etc.) without special handling.
fn: Function to execute when called from template. Must be a Go function with specific signature requirements:
Returns 1 or 2 values
If 2 values: second must be error (for error handling in templates)
Parameters: Can accept any number of parameters of any type
Parameter types: Template engine will attempt type conversion
Return types: Must be types that can be rendered in templates
Valid function signatures:
func() string
func(string) string
func(int, int) int
func(string) (string, error)
func(interface{}) string
func(...interface{}) string (variadic)
Function Signature Requirements:
The function must follow Go template function rules: - Can have any number of parameters (0+) - Can return 1 value: the result - Can return 2 values: (result, error) - If error is returned and non-nil, template execution fails - Parameter and return types should be serializable/printable
Thread-Safety:
- Thread-safe with read lock protection (app.mu.RLock).
- Safe to call during initialization or runtime.
- Function should be registered BEFORE loading templates.
- Multiple goroutines can safely call AddTemplateFunc concurrently.
- Registered function itself should be thread-safe if called concurrently.
Registration Timing:
- **Critical**: Must be called BEFORE loading templates.
- Functions registered after template loading won't be available in those templates.
- Recommended: Register all functions immediately after app creation.
- If reloading templates, functions persist (don't need re-registration).
Template Engine Requirement:
- Template engine must be initialized (WithTemplateDir option set).
- Silently does nothing if template engine is nil.
- No error returned; check TemplateEngine() != nil if verification needed.
Common Use Cases:
- Date/time formatting: Format timestamps in human-readable formats
- String manipulation: Uppercase, lowercase, truncate, etc.
- Number formatting: Currency, percentages, thousands separators
- URL building: Generate URLs from route names and parameters
- Asset helpers: Generate asset URLs with cache-busting
- Authorization checks: Check user permissions in templates
- i18n/l10n: Translate strings, format locale-specific data
Example - Basic String Formatting:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register uppercase function
app.AddTemplateFunc("upper", strings.ToUpper)
// Register lowercase function
app.AddTemplateFunc("lower", strings.ToLower)
// Load templates (functions now available)
app.LoadTemplates("*.html")
// In template:
// {{ .Name | upper }} → "JOHN"
// {{ .Name | lower }} → "john"
Example - Date Formatting:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register date formatter
app.AddTemplateFunc("formatDate", func(t time.Time) string {
return t.Format("2006-01-02")
})
// Register relative time formatter
app.AddTemplateFunc("timeAgo", func(t time.Time) string {
d := time.Since(t)
if d < time.Hour {
return fmt.Sprintf("%d minutes ago", int(d.Minutes()))
}
return fmt.Sprintf("%d hours ago", int(d.Hours()))
})
app.LoadTemplates("*.html")
// In template:
// {{ .CreatedAt | formatDate }} → "2024-01-15"
// {{ .UpdatedAt | timeAgo }} → "5 minutes ago"
Example - Number Formatting:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register currency formatter
app.AddTemplateFunc("currency", func(amount float64) string {
return fmt.Sprintf("$%.2f", amount)
})
// Register percentage formatter
app.AddTemplateFunc("percent", func(value float64) string {
return fmt.Sprintf("%.1f%%", value*100)
})
app.LoadTemplates("*.html")
// In template:
// {{ .Price | currency }} → "$19.99"
// {{ .Discount | percent }} → "15.0%"
Example - With Error Handling:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register function that can fail
app.AddTemplateFunc("divide", func(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
})
app.LoadTemplates("*.html")
// In template:
// {{ divide 10 2 }} → 5
// {{ divide 10 0 }} → ERROR: template execution fails
Example - URL Building:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register URL builder
app.AddTemplateFunc("url", func(path string) string {
return "/app" + path
})
// Register asset URL builder with version
app.AddTemplateFunc("asset", func(path string) string {
version := "v1.2.3" // Or read from config
return fmt.Sprintf("/static/%s?v=%s", path, version)
})
app.LoadTemplates("*.html")
// In template:
// <a href="{{ url "/users" }}">Users</a>
// <script src="{{ asset "app.js" }}"></script>
Example - Authorization Helper:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register permission checker
app.AddTemplateFunc("can", func(user interface{}, permission string) bool {
// Type assert and check permission
if u, ok := user.(*User); ok {
return u.HasPermission(permission)
}
return false
})
app.LoadTemplates("*.html")
// In template:
// {{ if can .User "edit:posts" }}
// <button>Edit</button>
// {{ end }}
Built-in Template Functions (Go standard library):
- and: Returns boolean AND of arguments
- call: Calls a function
- html: Escapes HTML
- index: Indexes into maps, slices, arrays
- js: Escapes JavaScript
- len: Returns length of string, slice, map
- not: Boolean negation
- or: Returns boolean OR of arguments
- print, printf, println: Formatted output
- urlquery: Escapes for URL query
Function Naming Best Practices:
- Use camelCase or snake_case consistently
- Make names descriptive: "formatDate" better than "fd"
- Avoid single-letter names (except conventional ones)
- Don't shadow built-in functions unless intentional
- Consider prefixing custom functions: "app_formatDate"
Performance Considerations:
- Functions are called during template rendering (request time)
- Keep functions fast (avoid heavy computation, I/O, database queries)
- Cache results if function is called repeatedly
- Consider memoization for expensive functions
- Profile template rendering if performance is critical
Best Practices:
- Register all functions before loading templates
- Keep template functions simple and focused
- Move complex logic to Go code, not templates
- Use error returns for operations that can fail
- Make functions pure (no side effects) when possible
- Document function behavior and parameters
- Test template functions independently
Debugging Tips:
- Test functions in isolation before using in templates
- Check function signature matches template requirements
- Verify template engine is initialized before calling
- Log function calls to debug template execution
- Use error returns to provide better debugging information
Security Considerations:
- Sanitize user input in custom functions
- Don't pass sensitive data to templates unless necessary
- Validate and escape output appropriately
- Be careful with functions that execute arbitrary code
- Don't allow user-controlled function names or parameters
AddTemplateFunc는 템플릿에서 사용할 수 있는 커스텀 함수를 등록합니다. 이를 통해 도메인별 헬퍼, 포매터 및 유틸리티를 추가하여 Go의 내장 템플릿 함수를 넘어 템플릿 기능을 확장할 수 있습니다.
AddTemplateFunc를 사용하면 템플릿이 다음을 수행할 수 있습니다: - 데이터 형식 지정(날짜, 숫자, 통화 등) - 계산 및 변환 수행 - 템플릿에서 애플리케이션 로직 호출 - 헬퍼 유틸리티 액세스(문자열 조작 등) - 커스텀 템플릿 DSL(도메인별 언어) 구현
목적:
- 템플릿에서 사용할 단일 커스텀 함수 등록
- 애플리케이션별 로직으로 템플릿 기능 확장
- 복잡한 로직을 Go 함수로 이동하여 깔끔한 템플릿 활성화
- 모든 템플릿에서 재사용 가능한 템플릿 유틸리티 제공
- 템플릿이 애플리케이션 컨텍스트 및 헬퍼에 액세스할 수 있도록 허용
매개변수:
name: 템플릿에서 호출될 함수 이름. 유효한 Go 식별자여야 합니다(영숫자 + 밑줄, 문자/밑줄로 시작). 대소문자 구분: "formatDate"와 "FormatDate"는 다른 함수입니다. 특별한 처리 없이는 내장 템플릿 함수(and, or, not 등)를 재정의할 수 없습니다.
fn: 템플릿에서 호출될 때 실행할 함수. 특정 시그니처 요구사항이 있는 Go 함수여야 합니다:
1개 또는 2개의 값 반환
2개의 값인 경우: 두 번째는 error여야 함(템플릿에서 오류 처리를 위해)
매개변수: 모든 타입의 매개변수를 모든 수만큼 받을 수 있음
매개변수 타입: 템플릿 엔진이 타입 변환을 시도
반환 타입: 템플릿에서 렌더링할 수 있는 타입이어야 함
등록 타이밍:
- **중요**: 템플릿을 로드하기 전에 호출되어야 합니다.
- 템플릿 로드 후 등록된 함수는 해당 템플릿에서 사용할 수 없습니다.
- 권장사항: 앱 생성 직후 모든 함수를 등록하세요.
- 템플릿을 리로드하는 경우 함수는 유지됩니다(재등록 불필요).
스레드 안전성:
- 읽기 잠금 보호(app.mu.RLock)로 스레드 안전합니다.
- 초기화 또는 런타임 중 호출해도 안전합니다.
- 템플릿을 로드하기 전에 함수를 등록해야 합니다.
- 여러 고루틴이 동시에 AddTemplateFunc를 안전하게 호출할 수 있습니다.
- 등록된 함수 자체는 동시에 호출되는 경우 스레드 안전해야 합니다.
모범 사례:
- 템플릿을 로드하기 전에 모든 함수를 등록하세요
- 템플릿 함수를 간단하고 집중적으로 유지하세요
- 복잡한 로직은 템플릿이 아닌 Go 코드로 이동하세요
- 실패할 수 있는 작업에는 오류 반환을 사용하세요
- 가능하면 함수를 순수하게 만드세요(부작용 없음)
- 함수 동작 및 매개변수를 문서화하세요
- 템플릿 함수를 독립적으로 테스트하세요
보안 고려사항:
- 커스텀 함수에서 사용자 입력을 삭제하세요
- 필요하지 않으면 민감한 데이터를 템플릿에 전달하지 마세요
- 출력을 적절하게 검증하고 이스케이프하세요
- 임의의 코드를 실행하는 함수에 주의하세요
- 사용자가 제어하는 함수 이름이나 매개변수를 허용하지 마세요
func (*App) AddTemplateFuncs ¶
AddTemplateFuncs registers multiple custom functions for use in templates. This is a convenience method for bulk registration of template functions, more efficient and cleaner than calling AddTemplateFunc multiple times.
AddTemplateFuncs is ideal for: - Registering a suite of related helper functions - Bulk registration of formatting utilities - Sharing common template function collections across projects - Organizing template functions by category or domain - One-time registration of all application template helpers
Purpose:
- Registers multiple custom functions in a single call
- Provides cleaner API than repeated AddTemplateFunc calls
- Enables organized template function management
- Supports sharing template function libraries
- Simplifies template function initialization
Parameters:
funcs: Map of function names to function implementations. Key: Function name as it will be called in templates (string) Value: Function implementation (interface{}, must be valid template function)
Map structure: map[string]interface{}{ "functionName": functionImplementation, "otherFunc": otherImplementation, ... }
Each function must follow template function signature requirements:
Returns 1 or 2 values
If 2 values: second must be error
Can accept any number of parameters
Thread-Safety:
- Thread-safe with read lock protection (app.mu.RLock).
- Safe to call during initialization or runtime.
- Should be called BEFORE loading templates.
- Multiple goroutines can safely call AddTemplateFuncs concurrently.
- Registered functions should be thread-safe if called concurrently.
Registration Timing:
- **Critical**: Must be called BEFORE loading templates.
- Functions registered after template loading won't be available.
- Recommended: Register all functions immediately after app creation.
- If reloading templates, functions persist (no re-registration needed).
Template Engine Requirement:
- Template engine must be initialized (WithTemplateDir option set).
- Silently does nothing if template engine is nil.
- No error returned; check TemplateEngine() != nil if verification needed.
Behavior:
- All functions in the map are registered with the template engine
- Existing functions with same names are replaced
- Empty map is valid (no-op, no error)
- Nil map is valid (no-op, no error)
- Functions become available in all templates after loading
Common Use Cases:
- Register suite of string formatters (upper, lower, title, etc.)
- Register date/time formatting functions
- Register number/currency formatters
- Register URL builders and asset helpers
- Register i18n/l10n translation functions
- Share common template utilities across microservices
Example - String Utilities:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register multiple string functions at once
app.AddTemplateFuncs(map[string]interface{}{
"upper": strings.ToUpper,
"lower": strings.ToLower,
"title": strings.Title,
"trim": strings.TrimSpace,
"replace": strings.ReplaceAll,
})
app.LoadTemplates("*.html")
// In template:
// {{ .Name | upper }}
// {{ .Title | title }}
// {{ .Text | trim }}
Example - Date/Time Formatters:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
app.AddTemplateFuncs(map[string]interface{}{
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02")
},
"formatDateTime": func(t time.Time) string {
return t.Format("2006-01-02 15:04:05")
},
"formatTime": func(t time.Time) string {
return t.Format("15:04:05")
},
"timeAgo": func(t time.Time) string {
d := time.Since(t)
switch {
case d < time.Minute:
return "just now"
case d < time.Hour:
return fmt.Sprintf("%d min ago", int(d.Minutes()))
case d < 24*time.Hour:
return fmt.Sprintf("%d hours ago", int(d.Hours()))
default:
return fmt.Sprintf("%d days ago", int(d.Hours()/24))
}
},
})
app.LoadTemplates("*.html")
Example - Number Formatters:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
app.AddTemplateFuncs(map[string]interface{}{
"currency": func(amount float64) string {
return fmt.Sprintf("$%.2f", amount)
},
"percent": func(value float64) string {
return fmt.Sprintf("%.1f%%", value*100)
},
"round": func(value float64, decimals int) float64 {
pow := math.Pow(10, float64(decimals))
return math.Round(value*pow) / pow
},
"thousands": func(n int) string {
return humanize.Comma(int64(n))
},
})
app.LoadTemplates("*.html")
Example - URL and Asset Helpers:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
baseURL := "/app"
version := "v1.2.3"
app.AddTemplateFuncs(map[string]interface{}{
"url": func(path string) string {
return baseURL + path
},
"asset": func(path string) string {
return fmt.Sprintf("/static/%s?v=%s", path, version)
},
"cdn": func(path string) string {
return fmt.Sprintf("https://cdn.example.com/%s", path)
},
"thumbnail": func(imageURL string, size string) string {
return fmt.Sprintf("%s?size=%s", imageURL, size)
},
})
app.LoadTemplates("*.html")
// In template:
// <a href="{{ url "/users" }}">Users</a>
// <script src="{{ asset "app.js" }}"></script>
// <img src="{{ cdn "logo.png" }}">
Example - Comprehensive Helper Suite:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Register complete helper suite
app.AddTemplateFuncs(map[string]interface{}{
// String helpers
"upper": strings.ToUpper,
"lower": strings.ToLower,
"truncate": func(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n] + "..."
},
// Date helpers
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02")
},
// Number helpers
"currency": func(amount float64) string {
return fmt.Sprintf("$%.2f", amount)
},
// Logic helpers
"default": func(value, defaultValue interface{}) interface{} {
if value == nil || value == "" {
return defaultValue
}
return value
},
// Collection helpers
"first": func(slice interface{}) interface{} {
v := reflect.ValueOf(slice)
if v.Kind() == reflect.Slice && v.Len() > 0 {
return v.Index(0).Interface()
}
return nil
},
})
app.LoadTemplates("*.html")
Example - Reusable Function Library:
// helpers/template_funcs.go
package helpers
func StandardTemplateFuncs() map[string]interface{} {
return map[string]interface{}{
"upper": strings.ToUpper,
"lower": strings.ToLower,
"formatDate": func(t time.Time) string {
return t.Format("2006-01-02")
},
// ... more functions ...
}
}
// main.go
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
app.AddTemplateFuncs(helpers.StandardTemplateFuncs())
app.LoadTemplates("*.html")
Example - Domain-Specific Functions:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// E-commerce specific functions
app.AddTemplateFuncs(map[string]interface{}{
"productURL": func(productID string) string {
return fmt.Sprintf("/products/%s", productID)
},
"stockStatus": func(quantity int) string {
if quantity == 0 {
return "Out of Stock"
} else if quantity < 10 {
return "Low Stock"
}
return "In Stock"
},
"priceWithTax": func(price float64, taxRate float64) float64 {
return price * (1 + taxRate)
},
"discountPrice": func(price, discount float64) float64 {
return price * (1 - discount)
},
})
app.LoadTemplates("*.html")
Organizing Template Functions:
- Group by category: strings, dates, numbers, URLs, auth, etc.
- Use namespaced names: "str_upper", "date_format", "url_build"
- Keep related functions together in source code
- Document function behavior and parameters
- Consider separate packages for different function categories
Performance Considerations:
- Registration overhead is negligible (done once at startup)
- Functions are called during template rendering (request time)
- Keep all functions fast (avoid heavy computation, I/O)
- Cache results if function is called repeatedly
- Profile template rendering if performance is critical
Best Practices:
- Register all functions before loading templates
- Group related functions in maps for clarity
- Use consistent naming conventions
- Document function purpose and signature
- Keep functions simple and focused
- Test functions independently before template use
- Consider creating reusable function libraries
- Use type-safe signatures when possible
Comparison with AddTemplateFunc:
- AddTemplateFuncs: Multiple functions, single call, cleaner code
- AddTemplateFunc: Single function, multiple calls, more verbose
- Use AddTemplateFuncs for: Bulk registration, initialization, function suites
- Use AddTemplateFunc for: Individual additions, dynamic registration
Debugging Tips:
- Test each function independently before adding to map
- Verify function signatures match template requirements
- Check template engine is initialized before calling
- Log function registration for debugging
- Use descriptive function names for easier debugging
Security Considerations:
- Sanitize user input in all custom functions
- Don't pass sensitive data to templates unless necessary
- Validate and escape function outputs
- Be careful with functions that execute code or access resources
- Don't allow user-controlled function names or implementations
AddTemplateFuncs는 템플릿에서 사용할 여러 커스텀 함수를 등록합니다. 이는 템플릿 함수의 대량 등록을 위한 편의 메서드로, AddTemplateFunc를 여러 번 호출하는 것보다 효율적이고 깔끔합니다.
AddTemplateFuncs는 다음에 이상적입니다: - 관련된 헬퍼 함수 모음 등록 - 포맷 유틸리티의 대량 등록 - 프로젝트 간 공통 템플릿 함수 컬렉션 공유 - 범주 또는 도메인별 템플릿 함수 구성 - 모든 애플리케이션 템플릿 헬퍼의 일회성 등록
목적:
- 단일 호출로 여러 커스텀 함수 등록
- 반복된 AddTemplateFunc 호출보다 깔끔한 API 제공
- 조직화된 템플릿 함수 관리 활성화
- 템플릿 함수 라이브러리 공유 지원
- 템플릿 함수 초기화 단순화
매개변수:
funcs: 함수 이름에서 함수 구현으로의 맵. 키: 템플릿에서 호출될 함수 이름(string) 값: 함수 구현(interface{}, 유효한 템플릿 함수여야 함)
맵 구조: map[string]interface{}{ "functionName": functionImplementation, "otherFunc": otherImplementation, ... }
각 함수는 템플릿 함수 시그니처 요구사항을 따라야 합니다:
1개 또는 2개의 값 반환
2개의 값인 경우: 두 번째는 error여야 함
모든 수의 매개변수를 받을 수 있음
등록 타이밍:
- **중요**: 템플릿을 로드하기 전에 호출되어야 합니다.
- 템플릿 로드 후 등록된 함수는 사용할 수 없습니다.
- 권장사항: 앱 생성 직후 모든 함수를 등록하세요.
- 템플릿을 리로드하는 경우 함수는 유지됩니다(재등록 불필요).
스레드 안전성:
- 읽기 잠금 보호(app.mu.RLock)로 스레드 안전합니다.
- 초기화 또는 런타임 중 호출해도 안전합니다.
- 템플릿을 로드하기 전에 호출해야 합니다.
- 여러 고루틴이 동시에 AddTemplateFuncs를 안전하게 호출할 수 있습니다.
- 등록된 함수는 동시에 호출되는 경우 스레드 안전해야 합니다.
모범 사례:
- 템플릿을 로드하기 전에 모든 함수를 등록하세요
- 명확성을 위해 맵에서 관련 함수를 그룹화하세요
- 일관된 명명 규칙을 사용하세요
- 함수 목적 및 시그니처를 문서화하세요
- 함수를 간단하고 집중적으로 유지하세요
- 템플릿 사용 전에 함수를 독립적으로 테스트하세요
- 재사용 가능한 함수 라이브러리 생성을 고려하세요
- 가능하면 타입 안전 시그니처를 사용하세요
보안 고려사항:
- 모든 커스텀 함수에서 사용자 입력을 삭제하세요
- 필요하지 않으면 민감한 데이터를 템플릿에 전달하지 마세요
- 함수 출력을 검증하고 이스케이프하세요
- 코드를 실행하거나 리소스에 액세스하는 함수에 주의하세요
- 사용자가 제어하는 함수 이름이나 구현을 허용하지 마세요
func (*App) DELETE ¶
func (a *App) DELETE(pattern string, handler http.HandlerFunc) *App
DELETE registers a DELETE route. DELETE는 DELETE 라우트를 등록합니다.
func (*App) GET ¶
func (a *App) GET(pattern string, handler http.HandlerFunc) *App
GET registers a GET route. GET은 GET 라우트를 등록합니다.
Example 예제:
app.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
// Handler implementation
})
Example ¶
ExampleApp_GET demonstrates registering a GET route. ExampleApp_GET는 GET 라우트를 등록하는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
app.GET("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
})
fmt.Println("GET route registered")
}
Output: GET route registered
func (*App) Group ¶
Group creates a new route group with the given prefix. Group은 주어진 접두사로 새 라우트 그룹을 생성합니다.
The prefix is prepended to all routes registered in the group. 접두사는 그룹에 등록된 모든 라우트 앞에 추가됩니다.
Parameters 매개변수:
- prefix: Path prefix for the group (e.g., "/api/v1")
Returns 반환:
- *Group: New route group for method chaining
Example 예제:
api := app.Group("/api")
api.GET("/users", listUsers) // Route: /api/users
api.POST("/users", createUser) // Route: /api/users
Example ¶
ExampleApp_Group demonstrates creating route groups. ExampleApp_Group는 라우트 그룹을 생성하는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
// Create API group
// API 그룹 생성
api := app.Group("/api")
api.GET("/users", func(w http.ResponseWriter, r *http.Request) {})
api.GET("/posts", func(w http.ResponseWriter, r *http.Request) {})
// Create v1 subgroup
// v1 하위 그룹 생성
v1 := api.Group("/v1")
v1.GET("/products", func(w http.ResponseWriter, r *http.Request) {})
fmt.Println("Route groups created")
}
Output: Route groups created
func (*App) HEAD ¶
func (a *App) HEAD(pattern string, handler http.HandlerFunc) *App
HEAD registers a HEAD route. HEAD는 HEAD 라우트를 등록합니다.
func (*App) LoadTemplate ¶
LoadTemplate loads a single template file by name from the configured template directory. This is a convenience method for loading individual templates dynamically at runtime, useful for lazy loading, selective template loading, or dynamic template management.
LoadTemplate loads the template immediately and makes it available for rendering. The template file must exist in the directory configured via WithTemplateDir option.
Purpose:
- Loads a single named template file from the template directory
- Enables selective template loading (load only what you need)
- Supports dynamic template loading based on runtime conditions
- Useful for lazy loading templates to reduce startup time
- Allows hot-reloading individual templates during development
Parameters:
- name: Template filename relative to the template directory. Must be a simple filename or relative path within the template directory. Examples: "index.html", "layout.html", "partials/header.html" File must have extension matching configured template engine (.html, .tmpl, etc.)
Returns:
- error: Returns error if template loading fails. Error scenarios:
- Template engine not initialized (no TemplateDir option set)
- Template file not found in template directory
- Template syntax errors (invalid Go template syntax)
- File read permission denied
- Invalid template name (path traversal attempts, etc.) Returns nil on successful template load.
Template Engine Requirement:
- Template engine must be initialized via WithTemplateDir option: app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
- Returns error if template engine is nil (not initialized)
- Check error message: "template engine not initialized (set TemplateDir option)"
Thread-Safety:
- Thread-safe with read lock protection (app.mu.RLock).
- Multiple goroutines can call LoadTemplate() concurrently.
- Safe to call during request handling (though not recommended for performance).
- Template engine internally manages template storage with appropriate locking.
Template Resolution:
- Template name is resolved relative to TemplateDir: TemplateDir="/app/views", name="index.html" → loads /app/views/index.html
- Supports subdirectories: name="partials/header.html" → /app/views/partials/header.html
- Does not support absolute paths or parent directory references (../)
- Path traversal attempts are rejected for security
Behavior:
- Loads template file from disk
- Parses template with Go html/template package
- Stores parsed template in engine's template map
- Replaces existing template if name already loaded (reload capability)
- Template becomes immediately available for rendering after successful load
Common Use Cases:
- Lazy loading: Load templates on-demand instead of at startup
- Dynamic loading: Load templates based on user configuration or feature flags
- Hot reload: Reload individual templates during development without server restart
- Selective loading: Load only templates needed for current request pattern
- Template testing: Load specific templates for unit testing
Example - Basic Template Loading:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Load single template
if err := app.LoadTemplate("index.html"); err != nil {
log.Fatalf("Failed to load template: %v", err)
}
// Template is now available for rendering
app.GET("/", func(w http.ResponseWriter, r *http.Request) {
// Render the loaded template
// ...
})
Example - Lazy Loading Strategy:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Don't load all templates at startup
app.GET("/dashboard", func(w http.ResponseWriter, r *http.Request) {
// Load dashboard template on first access
if err := app.LoadTemplate("dashboard.html"); err != nil {
http.Error(w, "Template load error", http.StatusInternalServerError)
return
}
// Render dashboard
})
Example - Development Hot Reload:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// In development mode, reload template on each request
isDev := os.Getenv("ENV") == "development"
app.GET("/", func(w http.ResponseWriter, r *http.Request) {
if isDev {
// Reload template to pick up changes
if err := app.LoadTemplate("index.html"); err != nil {
log.Printf("Template reload failed: %v", err)
}
}
// Render template
})
Example - Error Handling:
if err := app.LoadTemplate("missing.html"); err != nil {
if strings.Contains(err.Error(), "not initialized") {
log.Fatal("Template engine not configured - use WithTemplateDir")
} else if strings.Contains(err.Error(), "not found") {
log.Printf("Template file missing: %v", err)
} else {
log.Printf("Template syntax error: %v", err)
}
}
Example - Load with Subdirectories:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Load template from subdirectory
if err := app.LoadTemplate("partials/header.html"); err != nil {
log.Fatal(err)
}
if err := app.LoadTemplate("layouts/main.html"); err != nil {
log.Fatal(err)
}
Performance Considerations:
- Template loading involves disk I/O (relatively slow)
- Template parsing has CPU overhead (template compilation)
- Prefer loading templates at startup rather than per-request
- Use LoadTemplates() for bulk loading (more efficient than multiple LoadTemplate calls)
- Cache loaded templates; avoid reloading unless necessary
- Hot reload should be development-only (performance impact)
Comparison with LoadTemplates:
- LoadTemplate: Single file, explicit name
- LoadTemplates: Multiple files, glob pattern (e.g., "*.html")
- Use LoadTemplate for: Individual files, dynamic loading, hot reload
- Use LoadTemplates for: Bulk loading, startup initialization, wildcard patterns
Best Practices:
- Load all templates at startup for production (use LoadTemplates("*.html"))
- Use LoadTemplate for dynamic/lazy loading scenarios only
- Handle errors appropriately (log, return HTTP 500, etc.)
- Avoid loading templates during request handling in production
- Use hot reload only in development mode
- Validate template syntax before deployment
Debugging Tips:
- Check error message for specific failure reason
- Verify template file exists in template directory
- Ensure TemplateDir is correctly configured
- Test template syntax with Go template playground
- Enable template engine debug logging if available
Security Considerations:
- Template name should not be user-controlled (path traversal risk)
- Validate template names if accepting from external sources
- Template directory should not be web-accessible
- Ensure template files have appropriate file permissions
LoadTemplate은 구성된 템플릿 디렉토리에서 이름으로 단일 템플릿 파일을 로드합니다. 이는 런타임에 개별 템플릿을 동적으로 로드하기 위한 편의 메서드이며, 지연 로딩, 선택적 템플릿 로딩 또는 동적 템플릿 관리에 유용합니다.
LoadTemplate은 템플릿을 즉시 로드하고 렌더링에 사용할 수 있도록 만듭니다. 템플릿 파일은 WithTemplateDir 옵션을 통해 구성된 디렉토리에 존재해야 합니다.
목적:
- 템플릿 디렉토리에서 이름이 지정된 단일 템플릿 파일 로드
- 선택적 템플릿 로딩 활성화(필요한 것만 로드)
- 런타임 조건에 따른 동적 템플릿 로딩 지원
- 시작 시간을 줄이기 위한 지연 로딩 템플릿에 유용
- 개발 중 개별 템플릿의 핫 리로딩 허용
매개변수:
- name: 템플릿 디렉토리에 상대적인 템플릿 파일 이름. 템플릿 디렉토리 내의 단순 파일 이름 또는 상대 경로여야 합니다. 예제: "index.html", "layout.html", "partials/header.html" 파일은 구성된 템플릿 엔진과 일치하는 확장자를 가져야 합니다(.html, .tmpl 등).
반환값:
- error: 템플릿 로딩이 실패하면 오류를 반환합니다. 오류 시나리오:
- 템플릿 엔진이 초기화되지 않음(TemplateDir 옵션이 설정되지 않음)
- 템플릿 디렉토리에서 템플릿 파일을 찾을 수 없음
- 템플릿 구문 오류(잘못된 Go 템플릿 구문)
- 파일 읽기 권한 거부
- 잘못된 템플릿 이름(경로 순회 시도 등) 템플릿 로드에 성공하면 nil을 반환합니다.
템플릿 엔진 요구사항:
- WithTemplateDir 옵션을 통해 템플릿 엔진이 초기화되어야 합니다: app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
- 템플릿 엔진이 nil인 경우(초기화되지 않음) 오류를 반환합니다
- 오류 메시지 확인: "template engine not initialized (set TemplateDir option)"
스레드 안전성:
- 읽기 잠금 보호(app.mu.RLock)로 스레드 안전합니다.
- 여러 고루틴이 동시에 LoadTemplate()을 호출할 수 있습니다.
- 요청 처리 중 호출해도 안전합니다(성능상 권장하지는 않음).
- 템플릿 엔진은 적절한 잠금으로 템플릿 저장소를 내부적으로 관리합니다.
템플릿 해결:
- 템플릿 이름은 TemplateDir에 상대적으로 해결됩니다: TemplateDir="/app/views", name="index.html" → /app/views/index.html 로드
- 하위 디렉토리 지원: name="partials/header.html" → /app/views/partials/header.html
- 절대 경로 또는 상위 디렉토리 참조(../)를 지원하지 않음
- 경로 순회 시도는 보안을 위해 거부됨
동작 방식:
- 디스크에서 템플릿 파일 로드
- Go html/template 패키지로 템플릿 파싱
- 엔진의 템플릿 맵에 파싱된 템플릿 저장
- 이름이 이미 로드된 경우 기존 템플릿 교체(리로드 기능)
- 성공적으로 로드한 후 템플릿을 즉시 렌더링에 사용할 수 있음
모범 사례:
- 프로덕션에서는 시작 시 모든 템플릿을 로드하세요(LoadTemplates("*.html") 사용)
- 동적/지연 로딩 시나리오에만 LoadTemplate 사용
- 오류를 적절하게 처리하세요(로그, HTTP 500 반환 등)
- 프로덕션에서 요청 처리 중 템플릿 로딩을 피하세요
- 개발 모드에서만 핫 리로드 사용
- 배포 전에 템플릿 구문 검증
보안 고려사항:
- 템플릿 이름은 사용자가 제어해서는 안 됩니다(경로 순회 위험)
- 외부 소스에서 수락하는 경우 템플릿 이름 검증
- 템플릿 디렉토리는 웹에서 액세스할 수 없어야 함
- 템플릿 파일에 적절한 파일 권한이 있는지 확인
func (*App) LoadTemplates ¶
LoadTemplates loads all templates matching the specified glob pattern from the template directory. This is the **recommended method for bulk template loading** at application startup. It efficiently loads multiple template files in a single operation using glob pattern matching.
LoadTemplates is ideal for: - Loading all templates at application startup - Bulk loading templates from specific subdirectories - Loading templates by extension or naming convention - Initializing template cache before request handling begins
Purpose:
- Loads multiple template files matching a glob pattern
- Enables efficient bulk template loading (better than multiple LoadTemplate calls)
- Supports flexible pattern matching for selective loading
- Ideal for startup initialization and batch operations
- Reduces startup time compared to individual file loading
Parameters:
pattern: Glob pattern for matching template files within the template directory. Glob patterns support standard wildcards:
"*" - Matches any sequence of characters (except directory separator)
"?" - Matches any single character
"**" - Matches directories recursively (implementation-dependent)
"[abc]" - Matches any character in the bracket set
"{a,b}" - Matches either alternative (brace expansion)
Common patterns:
"*.html" - All .html files in template directory root
"**/*.html" - All .html files in any subdirectory (recursive)
"layouts/*.tmpl" - All .tmpl files in layouts subdirectory
"partial*.html" - Files starting with "partial" and ending with .html
"*.{html,tmpl}" - Files with .html or .tmpl extension
Returns:
- error: Returns error if template loading fails. Error scenarios:
- Template engine not initialized (no TemplateDir option set)
- Invalid glob pattern syntax
- No files match the pattern (may or may not be an error depending on implementation)
- Template syntax errors in any matched file
- File read permission denied
- Disk I/O errors Returns nil when all matching templates load successfully.
Template Engine Requirement:
- Template engine must be initialized via WithTemplateDir option: app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
- Returns error if template engine is nil: "template engine not initialized (set TemplateDir option)"
Thread-Safety:
- Thread-safe with read lock protection (app.mu.RLock).
- Safe for concurrent calls from multiple goroutines.
- Not recommended during request handling (performance impact).
- Best practice: Load templates during application initialization phase.
Pattern Matching Behavior:
- Pattern is evaluated relative to TemplateDir
- Recursive patterns (like "**/*.html") scan subdirectories
- Pattern matching is case-sensitive on Unix/Linux, case-insensitive on Windows
- Hidden files (starting with .) are typically excluded unless explicitly matched
- Symlinks behavior depends on implementation (may or may not be followed)
Loading Behavior:
- All matching files are loaded and parsed
- Each template is stored with its filename (or relative path) as the key
- Existing templates with same names are replaced (reload capability)
- Loading stops on first error (partial loading may occur)
- Templates become immediately available for rendering after successful load
Common Use Cases:
- Startup initialization: Load all templates before serving requests
- Selective loading: Load only specific subdirectories or file types
- Template organization: Load templates by category (layouts, partials, pages)
- Development: Reload all templates after changes
- Testing: Load test fixture templates
Example - Load All HTML Templates:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Load all .html files at startup
if err := app.LoadTemplates("*.html"); err != nil {
log.Fatalf("Failed to load templates: %v", err)
}
// All templates now available for rendering
app.Run(":8080")
Example - Load Templates Recursively:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Load all .html files from all subdirectories
if err := app.LoadTemplates("**/*.html"); err != nil {
log.Fatalf("Failed to load templates: %v", err)
}
Example - Load Specific Subdirectory:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Load only layout templates
if err := app.LoadTemplates("layouts/*.html"); err != nil {
log.Fatalf("Failed to load layouts: %v", err)
}
// Load only partial templates
if err := app.LoadTemplates("partials/*.html"); err != nil {
log.Fatalf("Failed to load partials: %v", err)
}
Example - Multiple Extensions:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Load both .html and .tmpl files
patterns := []string{"*.html", "*.tmpl"}
for _, pattern := range patterns {
if err := app.LoadTemplates(pattern); err != nil {
log.Fatalf("Failed to load %s: %v", pattern, err)
}
}
Example - Staged Loading:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Load critical templates first
if err := app.LoadTemplates("layouts/*.html"); err != nil {
log.Fatal("Critical layouts missing:", err)
}
// Load optional templates (errors non-fatal)
if err := app.LoadTemplates("optional/*.html"); err != nil {
log.Println("Optional templates not loaded:", err)
}
// Load remaining templates
if err := app.LoadTemplates("*.html"); err != nil {
log.Fatal("Template loading failed:", err)
}
Example - Development Hot Reload:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Initial load
app.LoadTemplates("*.html")
// In development mode, watch for changes and reload
if isDevelopment {
// (Pseudo-code - actual file watching implementation needed)
watchFiles("./views", func() {
log.Println("Templates changed, reloading...")
if err := app.ReloadTemplates(); err != nil {
log.Printf("Reload failed: %v", err)
}
})
}
Performance Considerations:
- Bulk loading is more efficient than multiple LoadTemplate() calls
- Template parsing has CPU overhead (proportional to template complexity)
- Disk I/O is the primary bottleneck (especially for many small files)
- Loading time increases with number of files and template complexity
- Typical startup overhead: 10-100ms for small apps, up to seconds for large apps
- Consider lazy loading for applications with hundreds of templates
Comparison with LoadTemplate:
- LoadTemplates: Multiple files via glob pattern, efficient bulk loading
- LoadTemplate: Single file by exact name, selective loading
- Use LoadTemplates for: Startup initialization, bulk operations, wildcard patterns
- Use LoadTemplate for: Individual files, runtime loading, specific templates
Best Practices:
- Load all templates at startup using LoadTemplates("*.html") or similar
- Organize templates in subdirectories for maintainability
- Use consistent file extensions (.html, .tmpl) for easier glob patterns
- Handle errors gracefully with proper logging
- Validate templates in CI/CD pipeline before deployment
- Use recursive patterns carefully (can be slow with deep directory trees)
- Reload templates only in development mode (not production)
Debugging Tips:
- Log the pattern used to verify it matches intended files
- Check template directory structure matches expected pattern
- Test glob patterns with shell commands (ls views/*.html)
- Enable verbose logging to see which files are loaded
- Verify file permissions allow reading template files
Security Considerations:
- Pattern should not be user-controlled (glob injection risk)
- Validate patterns if accepting from external configuration
- Template directory should not be web-accessible
- Ensure loaded templates don't execute untrusted code
LoadTemplates는 템플릿 디렉토리에서 지정된 글로브 패턴과 일치하는 모든 템플릿을 로드합니다. 이는 애플리케이션 시작 시 **대량 템플릿 로딩을 위한 권장 메서드**입니다. 글로브 패턴 매칭을 사용하여 단일 작업으로 여러 템플릿 파일을 효율적으로 로드합니다.
LoadTemplates는 다음에 이상적입니다: - 애플리케이션 시작 시 모든 템플릿 로드 - 특정 하위 디렉토리에서 대량 템플릿 로드 - 확장자 또는 명명 규칙별 템플릿 로드 - 요청 처리가 시작되기 전에 템플릿 캐시 초기화
목적:
- 글로브 패턴과 일치하는 여러 템플릿 파일 로드
- 효율적인 대량 템플릿 로딩 활성화(여러 LoadTemplate 호출보다 우수)
- 선택적 로딩을 위한 유연한 패턴 매칭 지원
- 시작 초기화 및 배치 작업에 이상적
- 개별 파일 로딩에 비해 시작 시간 단축
매개변수:
pattern: 템플릿 디렉토리 내에서 템플릿 파일을 매칭하기 위한 글로브 패턴. 글로브 패턴은 표준 와일드카드를 지원합니다:
"*" - 모든 문자 시퀀스와 일치(디렉토리 구분자 제외)
"?" - 모든 단일 문자와 일치
"**" - 디렉토리를 재귀적으로 일치(구현에 따라 다름)
"[abc]" - 브래킷 세트의 모든 문자와 일치
"{a,b}" - 대안 중 하나와 일치(브레이스 확장)
일반적인 패턴:
"*.html" - 템플릿 디렉토리 루트의 모든 .html 파일
"**/*.html" - 모든 하위 디렉토리의 모든 .html 파일(재귀)
"layouts/*.tmpl" - layouts 하위 디렉토리의 모든 .tmpl 파일
"partial*.html" - "partial"로 시작하고 .html로 끝나는 파일
"*.{html,tmpl}" - .html 또는 .tmpl 확장자를 가진 파일
반환값:
- error: 템플릿 로딩이 실패하면 오류를 반환합니다. 오류 시나리오:
- 템플릿 엔진이 초기화되지 않음(TemplateDir 옵션이 설정되지 않음)
- 잘못된 글로브 패턴 구문
- 패턴과 일치하는 파일이 없음(구현에 따라 오류일 수도 아닐 수도 있음)
- 일치하는 파일의 템플릿 구문 오류
- 파일 읽기 권한 거부
- 디스크 I/O 오류 모든 일치하는 템플릿이 성공적으로 로드되면 nil을 반환합니다.
스레드 안전성:
- 읽기 잠금 보호(app.mu.RLock)로 스레드 안전합니다.
- 여러 고루틴에서 동시 호출에 안전합니다.
- 요청 처리 중에는 권장하지 않습니다(성능 영향).
- 모범 사례: 애플리케이션 초기화 단계에서 템플릿을 로드하세요.
모범 사례:
- 시작 시 LoadTemplates("*.html") 등을 사용하여 모든 템플릿을 로드하세요
- 유지 관리성을 위해 하위 디렉토리에 템플릿을 구성하세요
- 쉬운 글로브 패턴을 위해 일관된 파일 확장자(.html, .tmpl)를 사용하세요
- 적절한 로깅으로 오류를 우아하게 처리하세요
- 배포 전에 CI/CD 파이프라인에서 템플릿을 검증하세요
- 재귀 패턴은 신중하게 사용하세요(깊은 디렉토리 트리에서 느릴 수 있음)
- 개발 모드에서만 템플릿을 리로드하세요(프로덕션이 아님)
보안 고려사항:
- 패턴은 사용자가 제어해서는 안 됩니다(글로브 주입 위험)
- 외부 구성에서 수락하는 경우 패턴을 검증하세요
- 템플릿 디렉토리는 웹에서 액세스할 수 없어야 합니다
- 로드된 템플릿이 신뢰할 수 없는 코드를 실행하지 않도록 하세요
func (*App) NotFound ¶
func (a *App) NotFound(handler http.HandlerFunc) *App
NotFound sets the handler for 404 Not Found responses. NotFound는 404 Not Found 응답에 대한 핸들러를 설정합니다.
Example 예제:
app.NotFound(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "Custom 404 page")
})
func (*App) OPTIONS ¶
func (a *App) OPTIONS(pattern string, handler http.HandlerFunc) *App
OPTIONS registers an OPTIONS route. OPTIONS는 OPTIONS 라우트를 등록합니다.
func (*App) PATCH ¶
func (a *App) PATCH(pattern string, handler http.HandlerFunc) *App
PATCH registers a PATCH route. PATCH는 PATCH 라우트를 등록합니다.
func (*App) POST ¶
func (a *App) POST(pattern string, handler http.HandlerFunc) *App
POST registers a POST route. POST는 POST 라우트를 등록합니다.
Example ¶
ExampleApp_POST demonstrates registering a POST route. ExampleApp_POST는 POST 라우트를 등록하는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
app.POST("/users", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(201)
w.Write([]byte("User created"))
})
fmt.Println("POST route registered")
}
Output: POST route registered
func (*App) PUT ¶
func (a *App) PUT(pattern string, handler http.HandlerFunc) *App
PUT registers a PUT route. PUT은 PUT 라우트를 등록합니다.
func (*App) ReloadTemplates ¶
ReloadTemplates clears all currently loaded templates and reloads them from the template directory. This is a **development and debugging tool** for refreshing templates without server restart. Not recommended for production use due to performance impact and potential race conditions.
ReloadTemplates is useful for: - Development: See template changes immediately without restarting server - Debugging: Test template modifications in running application - Hot reload: Implement file watching with automatic template refresh - Testing: Reset template state between test cases
Purpose:
- Clears all currently loaded templates from memory
- Reloads all templates from template directory (same as initial load)
- Enables template hot-reloading during development
- Provides clean slate for template testing and debugging
- Allows dynamic template updates without application restart
Returns:
- error: Returns error if template reloading fails. Error scenarios:
- Template engine not initialized (no TemplateDir option set)
- Template directory not accessible (permissions, missing directory)
- Template syntax errors in any template file
- Disk I/O errors during file reading
- Partial reload failure (some templates cleared, reload incomplete) Returns nil when all templates successfully reloaded.
Template Engine Requirement:
- Template engine must be initialized via WithTemplateDir option: app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
- Returns error if template engine is nil: "template engine not initialized (set TemplateDir option)"
Thread-Safety:
- Thread-safe with read lock protection (app.mu.RLock).
- Safe for concurrent calls from multiple goroutines.
- **WARNING**: Race condition possible during reload:
- Templates are cleared first (all templates temporarily unavailable)
- Templates are then reloaded (new templates become available)
- Requests during this window may fail if templates are missing
- Duration: Typically 10-100ms depending on template count and complexity
- Not recommended for production due to race condition risk.
Reload Behavior:
**Clear Phase**: All currently loaded templates are removed from memory
**Load Phase**: Template engine reloads all templates from directory
**Result**: Fresh template state matching current filesystem contents
Template discovery: - Reloads all template files in TemplateDir and subdirectories - Uses same loading logic as initial startup - Discovers new files added since startup - Removes deleted files from template cache - Updates modified files with latest content
Performance Impact:
- Template clearing: Very fast (memory deallocation)
- Template reloading: Disk I/O + parsing overhead
- Total time: Typically 10-100ms for small apps, up to seconds for large apps
- Blocks template rendering during reload window
- Not suitable for high-traffic production environments
Common Use Cases:
- Development workflow: Reload templates after editing without restart
- Live preview: Watch template files and reload on changes
- Template debugging: Test template modifications iteratively
- A/B testing: Switch template implementations dynamically
- Testing: Reset templates between test cases
Example - Manual Reload in Development:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
app.LoadTemplates("*.html")
// Add reload endpoint for development
if os.Getenv("ENV") == "development" {
app.GET("/admin/reload-templates", func(w http.ResponseWriter, r *http.Request) {
if err := app.ReloadTemplates(); err != nil {
http.Error(w, "Reload failed: "+err.Error(), 500)
return
}
w.Write([]byte("Templates reloaded successfully"))
})
}
Example - Automatic Reload with File Watching:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
app.LoadTemplates("*.html")
// Watch template directory for changes (pseudo-code)
if os.Getenv("ENV") == "development" {
go func() {
watcher := newFileWatcher("./views")
for change := range watcher.Events() {
log.Printf("Template changed: %s, reloading...", change.Path)
if err := app.ReloadTemplates(); err != nil {
log.Printf("Reload failed: %v", err)
} else {
log.Println("Templates reloaded successfully")
}
}
}()
}
app.Run(":8080")
Example - Reload on HTTP Request (Dev Mode):
isDev := os.Getenv("ENV") == "development"
app.GET("/", func(w http.ResponseWriter, r *http.Request) {
// Reload templates on each request in development
if isDev {
if err := app.ReloadTemplates(); err != nil {
log.Printf("Template reload error: %v", err)
}
}
// Render template (now using latest version)
// ...
})
Example - Conditional Reload (URL Parameter):
app.GET("/page", func(w http.ResponseWriter, r *http.Request) {
// Allow reload via query parameter in development
if os.Getenv("ENV") == "development" && r.URL.Query().Get("reload") == "1" {
log.Println("Manual reload requested")
if err := app.ReloadTemplates(); err != nil {
log.Printf("Reload failed: %v", err)
}
}
// Render page
// ...
})
// Usage: http://localhost:8080/page?reload=1
Example - Testing with Template Reload:
func TestTemplateRendering(t *testing.T) {
app := websvrutil.New(websvrutil.WithTemplateDir("./test-templates"))
// Load initial templates
app.LoadTemplates("*.html")
// Test with original templates
// ... run tests ...
// Modify template files
// ... write new template content ...
// Reload to pick up changes
if err := app.ReloadTemplates(); err != nil {
t.Fatalf("Reload failed: %v", err)
}
// Test with modified templates
// ... run more tests ...
}
File Watching Integration Example (using fsnotify):
import "github.com/fsnotify/fsnotify"
func setupTemplateWatcher(app *websvrutil.App, dir string) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}
go func() {
defer watcher.Close()
for {
select {
case event := <-watcher.Events:
if event.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Remove) != 0 {
log.Printf("Template change detected: %s", event.Name)
time.Sleep(100 * time.Millisecond) // Debounce
if err := app.ReloadTemplates(); err != nil {
log.Printf("Reload error: %v", err)
} else {
log.Println("Templates reloaded")
}
}
case err := <-watcher.Errors:
log.Printf("Watcher error: %v", err)
}
}
}()
return watcher.Add(dir)
}
Race Condition Scenario:
- Time 0ms: Request A starts, needs template "index.html"
- Time 10ms: ReloadTemplates() called, clears all templates
- Time 15ms: Request A tries to render "index.html" → ERROR (template missing)
- Time 50ms: ReloadTemplates() completes, templates available again
- Time 60ms: Request B renders "index.html" → SUCCESS
Mitigation Strategies:
- Use only in development mode (ENV check)
- Avoid reload during high traffic periods
- Implement reload cooldown (don't reload more than once per second)
- Add reload mutex to serialize reload operations
- Consider blue-green deployment for production template updates
Best Practices:
- **Development only**: Never use in production environments
- Protect reload endpoints with authentication (if exposed)
- Log reload operations for debugging
- Implement reload cooldown to prevent excessive disk I/O
- Handle reload errors gracefully (don't crash server)
- Test thoroughly before relying on hot-reload in development
- Consider using a dedicated file watcher library (fsnotify, etc.)
Debugging Tips:
- Log template count before and after reload
- Monitor reload duration to detect performance issues
- Check for template syntax errors in logs
- Verify file permissions on template directory
- Test reload with simple template first
Production Alternatives:
- Blue-green deployment: Deploy new version with updated templates
- Rolling restart: Gradually restart application instances
- Immutable deployments: Build container images with templates included
- Configuration management: Use config management tools for updates
Security Considerations:
- Protect reload endpoints with authentication/authorization
- Don't expose reload functionality in production
- Validate template directory permissions
- Log reload operations for security auditing
- Rate-limit reload operations to prevent abuse
ReloadTemplates는 현재 로드된 모든 템플릿을 지우고 템플릿 디렉토리에서 다시 로드합니다. 이는 서버 재시작 없이 템플릿을 새로 고치기 위한 **개발 및 디버깅 도구**입니다. 성능 영향과 잠재적인 경쟁 조건으로 인해 프로덕션 사용은 권장되지 않습니다.
ReloadTemplates는 다음에 유용합니다: - 개발: 서버를 재시작하지 않고 템플릿 변경 사항을 즉시 확인 - 디버깅: 실행 중인 애플리케이션에서 템플릿 수정 테스트 - 핫 리로드: 자동 템플릿 새로 고침과 함께 파일 감시 구현 - 테스팅: 테스트 케이스 간 템플릿 상태 재설정
목적:
- 메모리에서 현재 로드된 모든 템플릿 지우기
- 템플릿 디렉토리에서 모든 템플릿 다시 로드(초기 로드와 동일)
- 개발 중 템플릿 핫 리로딩 활성화
- 템플릿 테스트 및 디버깅을 위한 깨끗한 상태 제공
- 애플리케이션 재시작 없이 동적 템플릿 업데이트 허용
반환값:
- error: 템플릿 리로딩이 실패하면 오류를 반환합니다. 오류 시나리오:
- 템플릿 엔진이 초기화되지 않음(TemplateDir 옵션이 설정되지 않음)
- 템플릿 디렉토리에 액세스할 수 없음(권한, 누락된 디렉토리)
- 템플릿 파일의 템플릿 구문 오류
- 파일 읽기 중 디스크 I/O 오류
- 부분 리로드 실패(일부 템플릿 지워짐, 리로드 불완전) 모든 템플릿이 성공적으로 리로드되면 nil을 반환합니다.
스레드 안전성:
- 읽기 잠금 보호(app.mu.RLock)로 스레드 안전합니다.
- 여러 고루틴에서 동시 호출에 안전합니다.
- **경고**: 리로드 중 경쟁 조건 가능:
- 템플릿이 먼저 지워짐(모든 템플릿이 일시적으로 사용 불가)
- 그런 다음 템플릿이 리로드됨(새 템플릿 사용 가능)
- 이 기간 동안의 요청은 템플릿이 누락되면 실패할 수 있음
- 기간: 템플릿 수와 복잡성에 따라 일반적으로 10-100ms
- 경쟁 조건 위험으로 인해 프로덕션에는 권장되지 않습니다.
모범 사례:
- **개발 전용**: 프로덕션 환경에서는 절대 사용하지 마세요
- 인증으로 리로드 엔드포인트를 보호하세요(노출된 경우)
- 디버깅을 위해 리로드 작업을 로깅하세요
- 과도한 디스크 I/O를 방지하기 위해 리로드 쿨다운을 구현하세요
- 리로드 오류를 우아하게 처리하세요(서버를 충돌시키지 마세요)
- 개발에서 핫 리로드에 의존하기 전에 철저히 테스트하세요
- 전용 파일 감시 라이브러리(fsnotify 등) 사용을 고려하세요
보안 고려사항:
- 인증/권한으로 리로드 엔드포인트를 보호하세요
- 프로덕션에서 리로드 기능을 노출하지 마세요
- 템플릿 디렉토리 권한을 검증하세요
- 보안 감사를 위해 리로드 작업을 로깅하세요
- 남용을 방지하기 위해 리로드 작업의 속도를 제한하세요
func (*App) Run ¶
Run starts the HTTP server and begins listening for incoming requests on the specified network address. This method is the primary way to start your web application in production and development. Run blocks the calling goroutine until the server is stopped via Shutdown() or encounters a fatal error. It configures the http.Server with all registered routes, middleware, templates, and options, then starts listening and serving HTTP traffic.
Run is suitable for simple deployments where graceful shutdown isn't critical. For production applications that need to handle SIGINT/SIGTERM signals and drain connections gracefully, use RunWithGracefulShutdown() instead.
Parameters:
- addr: Network address to listen on, in "host:port" format. Common patterns:
- ":8080" - Listen on all interfaces (0.0.0.0), port 8080 (most common)
- "localhost:8080" - Listen only on localhost (127.0.0.1), port 8080 (development)
- "0.0.0.0:8080" - Explicitly listen on all interfaces
- "192.168.1.10:8080" - Listen on specific IP address
- ":80" or ":443" - Standard HTTP/HTTPS ports (requires elevated privileges) Empty host ("") defaults to all interfaces (same as "0.0.0.0"). Port must be provided; addr like "localhost" without port will cause error.
Returns:
- error: Returns error if server fails to start or encounters fatal error during operation. Common errors:
- "address already in use" - Port is occupied by another process
- "permission denied" - Insufficient privileges for port (e.g., port 80 without sudo)
- "server is already running" - Run() called twice on same App instance
- Network-related errors during operation Returns nil when server shuts down gracefully via Shutdown().
Behavior:
Blocking Call: Run() blocks until server stops. Execute in main goroutine or use goroutine if you need concurrent operations: go app.Run(":8080") // Non-blocking in separate goroutine
Initialization Sequence: 1. Acquires mutex lock and checks if server is already running 2. Builds final handler chain by applying all middleware to router 3. Creates http.Server with configured timeouts and limits 4. Sets running flag to true 5. Releases mutex and starts ListenAndServe() 6. Blocks until server stops 7. Sets running flag to false on shutdown
Handler Chain: Middleware is applied in LIFO order (last added = outermost). Example: app.Use(A).Use(B).Use(C) results in execution order: C → B → A → handler
Server Configuration: Uses options provided to New():
ReadTimeout: Maximum duration for reading entire request
WriteTimeout: Maximum duration for writing response
IdleTimeout: Keep-alive idle connection timeout
MaxHeaderBytes: Maximum size of request headers
Running State: Sets a.running = true during operation. This prevents:
Adding new routes after server starts (would cause panic)
Adding new middleware after server starts (would cause panic)
Starting server twice on same App instance
Graceful Shutdown: When Shutdown() is called from another goroutine:
ListenAndServe() returns http.ErrServerClosed
Run() returns nil (normal shutdown, not an error)
Active connections drain according to shutdown context timeout
Thread-Safety:
- Safe to call from single goroutine (typical usage in main()).
- Uses mutex to protect running state check and modification.
- Cannot call Run() concurrently on same App instance (returns error).
- Can call Shutdown() from different goroutine while Run() is blocking.
Port Requirements:
Privileged Ports (1-1023): Require root/administrator privileges on Unix systems. Ports 80 (HTTP) and 443 (HTTPS) need sudo or setcap capabilities.
Ephemeral Ports (1024-65535): Can be used by any user. Development typically uses 3000, 8000, 8080, 8888, etc.
Port Conflicts: If port is already in use, Run() returns error immediately. Use different port or stop conflicting process.
Performance Considerations:
Timeout Configuration: Set appropriate ReadTimeout and WriteTimeout to prevent slowloris attacks and resource exhaustion from slow clients.
MaxHeaderBytes: Limit header size to prevent large header attacks. Default is 1 MB, adjust based on your application needs.
Keep-Alive: IdleTimeout controls how long idle connections are kept open. Shorter timeouts free resources faster but require more connection handshakes.
Middleware Overhead: Each middleware adds processing time per request. Minimize middleware and optimize hot paths.
Common Use Cases:
- Simple deployment: app.Run(":8080")
- Development server: app.Run("localhost:3000")
- Production with error handling: if err := app.Run(":80"); err != nil { log.Fatal(err) }
- Docker deployment: app.Run(":8080") (expose port 8080)
- Multiple servers: Run different App instances on different ports
Example - Basic Server Start:
app := websvrutil.New()
app.GET("/", homeHandler)
app.GET("/api/users", usersHandler)
fmt.Println("Starting server on :8080")
if err := app.Run(":8080"); err != nil {
log.Fatalf("Server failed: %v", err)
}
Example - Development Server:
app := websvrutil.New(websvrutil.WithTemplateDir("./templates"))
app.Static("/static", "./static")
app.GET("/", indexHandler)
log.Println("Dev server running on http://localhost:3000")
if err := app.Run("localhost:3000"); err != nil {
log.Fatal(err)
}
Example - Production with Environment Variable:
port := os.Getenv("PORT")
if port == "" {
port = "8080" // Default port
}
app := websvrutil.New(
websvrutil.WithReadTimeout(10 * time.Second),
websvrutil.WithWriteTimeout(10 * time.Second),
)
// ... configure routes ...
log.Printf("Server starting on port %s", port)
if err := app.Run(":" + port); err != nil {
log.Fatalf("Server error: %v", err)
}
Example - Non-Blocking with Goroutine:
app := websvrutil.New()
// ... configure routes ...
// Start server in background goroutine
go func() {
if err := app.Run(":8080"); err != nil {
log.Printf("Server stopped: %v", err)
}
}()
// Main goroutine can do other work
time.Sleep(1 * time.Second)
fmt.Println("Server is running in background")
// Keep main goroutine alive
select {}
Example - Multiple Servers:
// API server
apiApp := websvrutil.New()
apiApp.GET("/api/health", healthHandler)
go apiApp.Run(":8080")
// Admin server
adminApp := websvrutil.New()
adminApp.GET("/admin/dashboard", dashboardHandler)
go adminApp.Run(":9090")
// Block main goroutine
select {}
Error Handling:
- Always check returned error to detect startup failures.
- Log errors appropriately for debugging and monitoring.
- Use log.Fatal() or os.Exit(1) for fatal startup errors.
- Distinguish between startup errors (immediate) and runtime errors (during operation).
Alternative Startup Methods:
- RunWithGracefulShutdown(): Handles OS signals and drains connections gracefully.
- http.Server.ListenAndServe(): Direct http.Server usage if you need custom setup.
- http.Server.ListenAndServeTLS(): For HTTPS with TLS certificates.
Best Practices:
- Use RunWithGracefulShutdown() for production (handles SIGINT/SIGTERM).
- Configure appropriate timeouts to prevent resource exhaustion.
- Use environment variables for port configuration (12-factor app).
- Log server start/stop events for monitoring.
- Use health check endpoints for load balancer monitoring.
- Test port availability before deployment (netstat, lsof).
- Document required ports in deployment guides.
Deployment Considerations:
- Docker: Use EXPOSE directive and map ports with -p flag.
- Kubernetes: Define containerPort in Pod spec and Service port.
- Cloud: Configure security groups/firewall rules for ports.
- Reverse Proxy: Run on high port (8080), proxy from nginx/Apache on port 80.
- Load Balancer: Run multiple instances on different servers/ports.
Run은 HTTP 서버를 시작하고 지정된 네트워크 주소에서 들어오는 요청을 수신합니다. 이 메서드는 프로덕션 및 개발 환경에서 웹 애플리케이션을 시작하는 주요 방법입니다. Run은 Shutdown()을 통해 서버가 중지되거나 치명적인 오류가 발생할 때까지 호출하는 고루틴을 차단합니다. 등록된 모든 라우트, 미들웨어, 템플릿 및 옵션으로 http.Server를 구성한 다음 HTTP 트래픽을 수신하고 제공하기 시작합니다.
Run은 우아한 종료가 중요하지 않은 간단한 배포에 적합합니다. SIGINT/SIGTERM 신호를 처리하고 연결을 우아하게 드레인해야 하는 프로덕션 애플리케이션의 경우 대신 RunWithGracefulShutdown()을 사용하세요.
매개변수:
- addr: "host:port" 형식으로 수신할 네트워크 주소. 일반적인 패턴:
- ":8080" - 모든 인터페이스(0.0.0.0), 포트 8080에서 수신(가장 일반적)
- "localhost:8080" - localhost(127.0.0.1)에서만 수신, 포트 8080(개발)
- "0.0.0.0:8080" - 모든 인터페이스에서 명시적으로 수신
- "192.168.1.10:8080" - 특정 IP 주소에서 수신
- ":80" 또는 ":443" - 표준 HTTP/HTTPS 포트(높은 권한 필요) 빈 호스트("")는 모든 인터페이스로 기본 설정("0.0.0.0"과 동일). 포트를 제공해야 합니다. 포트 없이 "localhost"와 같은 addr은 오류를 발생시킵니다.
반환값:
- error: 서버가 시작에 실패하거나 작동 중 치명적인 오류가 발생하면 오류를 반환합니다. 일반적인 오류:
- "address already in use" - 다른 프로세스가 포트를 점유 중
- "permission denied" - 포트에 대한 권한 부족(예: sudo 없이 포트 80)
- "server is already running" - 동일한 App 인스턴스에서 Run()을 두 번 호출
- 작동 중 네트워크 관련 오류 Shutdown()을 통해 서버가 우아하게 종료되면 nil을 반환합니다.
동작 방식:
차단 호출: Run()은 서버가 중지될 때까지 차단됩니다. main 고루틴에서 실행하거나 동시 작업이 필요한 경우 고루틴을 사용하세요: go app.Run(":8080") // 별도의 고루틴에서 비차단
초기화 순서: 1. 뮤텍스 잠금을 획득하고 서버가 이미 실행 중인지 확인 2. 라우터에 모든 미들웨어를 적용하여 최종 핸들러 체인 구축 3. 구성된 타임아웃 및 제한으로 http.Server 생성 4. running 플래그를 true로 설정 5. 뮤텍스를 해제하고 ListenAndServe() 시작 6. 서버가 중지될 때까지 차단 7. 종료 시 running 플래그를 false로 설정
핸들러 체인: 미들웨어는 LIFO 순서로 적용됩니다(마지막 추가 = 가장 바깥쪽). 예: app.Use(A).Use(B).Use(C)는 실행 순서 C → B → A → handler가 됩니다.
서버 구성: New()에 제공된 옵션 사용:
ReadTimeout: 전체 요청을 읽는 최대 시간
WriteTimeout: 응답을 작성하는 최대 시간
IdleTimeout: Keep-alive 유휴 연결 타임아웃
MaxHeaderBytes: 요청 헤더의 최대 크기
실행 상태: 작동 중 a.running = true를 설정합니다. 이는 다음을 방지합니다:
서버 시작 후 새 라우트 추가(패닉 발생)
서버 시작 후 새 미들웨어 추가(패닉 발생)
동일한 App 인스턴스에서 서버를 두 번 시작
우아한 종료: 다른 고루틴에서 Shutdown()이 호출되면:
ListenAndServe()가 http.ErrServerClosed를 반환
Run()은 nil을 반환(정상 종료, 오류 아님)
활성 연결은 종료 컨텍스트 타임아웃에 따라 드레인됨
스레드 안전성:
- 단일 고루틴에서 호출하기에 안전합니다(main()의 일반적인 사용).
- running 상태 검사 및 수정을 보호하기 위해 뮤텍스를 사용합니다.
- 동일한 App 인스턴스에서 Run()을 동시에 호출할 수 없습니다(오류 반환).
- Run()이 차단되는 동안 다른 고루틴에서 Shutdown()을 호출할 수 있습니다.
포트 요구사항:
특권 포트(1-1023): Unix 시스템에서 root/관리자 권한이 필요합니다. 포트 80(HTTP)과 443(HTTPS)은 sudo 또는 setcap 기능이 필요합니다.
임시 포트(1024-65535): 모든 사용자가 사용할 수 있습니다. 개발은 일반적으로 3000, 8000, 8080, 8888 등을 사용합니다.
포트 충돌: 포트가 이미 사용 중이면 Run()이 즉시 오류를 반환합니다. 다른 포트를 사용하거나 충돌하는 프로세스를 중지하세요.
성능 고려사항:
타임아웃 구성: 느린 클라이언트로 인한 slowloris 공격 및 리소스 고갈을 방지하기 위해 적절한 ReadTimeout 및 WriteTimeout을 설정하세요.
MaxHeaderBytes: 대용량 헤더 공격을 방지하기 위해 헤더 크기를 제한하세요. 기본값은 1MB이며 애플리케이션 요구사항에 따라 조정하세요.
Keep-Alive: IdleTimeout은 유휴 연결을 얼마나 오래 열어 둘지 제어합니다. 짧은 타임아웃은 리소스를 더 빨리 해제하지만 더 많은 연결 핸드셰이크가 필요합니다.
미들웨어 오버헤드: 각 미들웨어는 요청당 처리 시간을 추가합니다. 미들웨어를 최소화하고 핫 패스를 최적화하세요.
일반적인 사용 사례:
- 간단한 배포: app.Run(":8080")
- 개발 서버: app.Run("localhost:3000")
- 오류 처리가 있는 프로덕션: if err := app.Run(":80"); err != nil { log.Fatal(err) }
- Docker 배포: app.Run(":8080") (포트 8080 노출)
- 여러 서버: 다른 포트에서 다른 App 인스턴스 실행
예제 - 기본 서버 시작:
app := websvrutil.New()
app.GET("/", homeHandler)
app.GET("/api/users", usersHandler)
fmt.Println("Starting server on :8080")
if err := app.Run(":8080"); err != nil {
log.Fatalf("Server failed: %v", err)
}
예제 - 개발 서버:
app := websvrutil.New(websvrutil.WithTemplateDir("./templates"))
app.Static("/static", "./static")
app.GET("/", indexHandler)
log.Println("Dev server running on http://localhost:3000")
if err := app.Run("localhost:3000"); err != nil {
log.Fatal(err)
}
예제 - 환경 변수가 있는 프로덕션:
port := os.Getenv("PORT")
if port == "" {
port = "8080" // 기본 포트
}
app := websvrutil.New(
websvrutil.WithReadTimeout(10 * time.Second),
websvrutil.WithWriteTimeout(10 * time.Second),
)
// ... 라우트 구성 ...
log.Printf("Server starting on port %s", port)
if err := app.Run(":" + port); err != nil {
log.Fatalf("Server error: %v", err)
}
예제 - 고루틴으로 비차단:
app := websvrutil.New()
// ... 라우트 구성 ...
// 백그라운드 고루틴에서 서버 시작
go func() {
if err := app.Run(":8080"); err != nil {
log.Printf("Server stopped: %v", err)
}
}()
// main 고루틴은 다른 작업을 수행할 수 있음
time.Sleep(1 * time.Second)
fmt.Println("Server is running in background")
// main 고루틴을 살아있게 유지
select {}
예제 - 여러 서버:
// API 서버
apiApp := websvrutil.New()
apiApp.GET("/api/health", healthHandler)
go apiApp.Run(":8080")
// 관리 서버
adminApp := websvrutil.New()
adminApp.GET("/admin/dashboard", dashboardHandler)
go adminApp.Run(":9090")
// main 고루틴 차단
select {}
오류 처리:
- 시작 실패를 감지하기 위해 항상 반환된 오류를 확인하세요.
- 디버깅 및 모니터링을 위해 오류를 적절히 로깅하세요.
- 치명적인 시작 오류에 대해 log.Fatal() 또는 os.Exit(1)을 사용하세요.
- 시작 오류(즉시)와 런타임 오류(작동 중)를 구별하세요.
대체 시작 메서드:
- RunWithGracefulShutdown(): OS 신호를 처리하고 연결을 우아하게 드레인합니다.
- http.Server.ListenAndServe(): 커스텀 설정이 필요한 경우 직접 http.Server 사용.
- http.Server.ListenAndServeTLS(): TLS 인증서로 HTTPS 사용.
모범 사례:
- 프로덕션에는 RunWithGracefulShutdown() 사용(SIGINT/SIGTERM 처리).
- 리소스 고갈을 방지하기 위해 적절한 타임아웃 구성.
- 포트 구성에 환경 변수 사용(12-factor 앱).
- 모니터링을 위해 서버 시작/중지 이벤트 로깅.
- 로드 밸런서 모니터링을 위해 헬스 체크 엔드포인트 사용.
- 배포 전 포트 가용성 테스트(netstat, lsof).
- 배포 가이드에 필요한 포트 문서화.
배포 고려사항:
- Docker: EXPOSE 지시문 사용 및 -p 플래그로 포트 매핑.
- Kubernetes: Pod spec에서 containerPort 정의 및 Service 포트.
- 클라우드: 포트에 대한 보안 그룹/방화벽 규칙 구성.
- 리버스 프록시: 높은 포트(8080)에서 실행, 포트 80의 nginx/Apache에서 프록시.
- 로드 밸런서: 다른 서버/포트에서 여러 인스턴스 실행.
func (*App) RunWithGracefulShutdown ¶
RunWithGracefulShutdown starts the HTTP server with automatic graceful shutdown on OS signals. This is the **recommended production method** for starting your web application. It combines server startup with intelligent signal handling, automatically draining connections when receiving SIGINT (Ctrl+C) or SIGTERM (common deployment signals) without additional code.
This method simplifies production deployments by: - Handling Ctrl+C gracefully during development and testing - Responding to Docker/Kubernetes termination signals (SIGTERM) - Draining connections before process exit - Preventing dropped requests during deployments - Eliminating boilerplate signal handling code
RunWithGracefulShutdown is ideal for: - Production applications (Docker, Kubernetes, systemd) - Development servers (clean Ctrl+C shutdown) - CI/CD pipelines (proper test cleanup) - Any deployment requiring zero-downtime updates
Parameters:
addr: Network address to listen on, in "host:port" format. Same format as Run() method:
":8080" - Listen on all interfaces, port 8080 (most common)
"localhost:8080" - Listen only on localhost (development)
"0.0.0.0:8080" - Explicitly listen on all interfaces
":80" or ":443" - Standard HTTP/HTTPS ports (requires elevated privileges)
timeout: Maximum duration to wait for active connections to close during shutdown. This timeout applies from signal reception to forceful connection termination. Common timeout values:
5 seconds: Fast shutdown, suitable for APIs with quick responses
15 seconds: Balanced for typical web applications
30 seconds: Recommended for most production applications (default recommendation)
60 seconds: Patient shutdown for applications with long-running requests
120+ seconds: Very patient, for file uploads, video processing, batch operations If timeout expires, remaining connections are forcibly closed.
Returns:
- error: Returns error if server fails to start or shutdown encounters errors. Possible error scenarios:
- Server startup failure (port in use, permission denied, invalid address)
- Shutdown timeout exceeded (context.DeadlineExceeded wrapped in error)
- Network errors during connection cleanup Returns nil when server shuts down cleanly after receiving signal.
Behavior:
Startup Phase: 1. Starts server in background goroutine using Run() 2. Registers signal handlers for SIGINT and SIGTERM 3. Blocks waiting for either server error or OS signal
Signal Handling: Automatically responds to:
SIGINT: Sent by Ctrl+C, shell interrupts, keyboard interrupt
SIGTERM: Sent by kill command, Docker stop, Kubernetes pod termination, systemd service stop, process managers, deployment scripts
Shutdown Sequence: 1. Receives SIGINT or SIGTERM signal 2. Prints received signal name to console 3. Creates context with specified timeout 4. Calls Shutdown() to drain connections gracefully 5. Waits up to timeout duration for connections to close 6. Returns nil on success or error on failure
Two Exit Paths:
Server startup error: Returns immediately with error (port conflict, etc.)
Signal received: Initiates graceful shutdown, returns after completion
Blocking Call: This method blocks until server stops (via signal or error). Must be called from main goroutine or dedicated server goroutine.
Signal Details:
SIGINT (Signal Interrupt):
Triggered by: Ctrl+C in terminal, kill -INT <pid>
Use case: Development, manual interruption, user-initiated shutdown
Exit code: Typically 130 (128 + 2)
SIGTERM (Signal Terminate):
Triggered by: kill <pid>, Docker stop, Kubernetes pod deletion, systemd stop
Use case: Automated deployments, orchestrator-managed lifecycle
Exit code: Typically 143 (128 + 15)
Most common in production environments
Thread-Safety:
- Call from single goroutine (typically main()).
- Spawns internal goroutines for server and signal handling.
- Safe concurrent signal delivery (channel buffered, signal.Notify thread-safe).
Deployment Integration:
- Docker: Responds to `docker stop` (sends SIGTERM, waits, then SIGKILL)
- Kubernetes: Handles pod termination (SIGTERM from kubelet)
- systemd: Responds to `systemctl stop` (sends SIGTERM)
- Heroku/PaaS: Responds to dyno/instance termination signals
- Process Managers (PM2, Supervisor): Handles managed process restarts
Best Practices:
- Use this method for production instead of plain Run().
- Set timeout longer than longest expected request duration.
- Add timeout buffer for cleanup operations (DB commits, cache writes).
- Log startup and shutdown events for monitoring.
- Test shutdown behavior under load before production deployment.
- Consider health check degradation before signal shutdown.
- Clean up resources (DB connections, files) after method returns.
Performance Considerations:
- Signal handling overhead is negligible (channel + goroutine).
- Shutdown performance depends on active connection count and timeout.
- Timeout too short: Risk of forcibly closed connections
- Timeout too long: Slower deployments, longer pod termination
- Balance timeout based on application request patterns.
Common Use Cases:
- Production server: app.RunWithGracefulShutdown(":8080", 30*time.Second)
- Development server: app.RunWithGracefulShutdown("localhost:3000", 5*time.Second)
- Docker container: app.RunWithGracefulShutdown(":8080", 25*time.Second)
- Kubernetes pod: app.RunWithGracefulShutdown(":8080", 30*time.Second)
- Quick testing: app.RunWithGracefulShutdown(":9000", 1*time.Second)
Example - Basic Production Server:
func main() {
app := websvrutil.New()
app.GET("/", homeHandler)
app.GET("/api/users", usersHandler)
log.Println("Starting server on :8080")
if err := app.RunWithGracefulShutdown(":8080", 30*time.Second); err != nil {
log.Fatalf("Server error: %v", err)
}
log.Println("Server shutdown complete")
}
Example - With Resource Cleanup:
func main() {
// Initialize dependencies
db, err := sql.Open("postgres", dbURL)
if err != nil {
log.Fatal(err)
}
defer db.Close() // Cleanup after server stops
cache := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
defer cache.Close()
// Configure application
app := websvrutil.New()
app.GET("/api/data", func(w http.ResponseWriter, r *http.Request) {
// Use db and cache
})
// Start with graceful shutdown
log.Println("Server starting...")
if err := app.RunWithGracefulShutdown(":8080", 30*time.Second); err != nil {
log.Printf("Server error: %v", err)
os.Exit(1)
}
log.Println("Cleanup complete, exiting")
}
Example - Environment-Based Configuration:
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
shutdownTimeout := 30 * time.Second
if timeoutStr := os.Getenv("SHUTDOWN_TIMEOUT"); timeoutStr != "" {
if d, err := time.ParseDuration(timeoutStr); err == nil {
shutdownTimeout = d
}
}
app := websvrutil.New()
// ... configure routes ...
log.Printf("Starting server on port %s with %v shutdown timeout", port, shutdownTimeout)
if err := app.RunWithGracefulShutdown(":"+port, shutdownTimeout); err != nil {
log.Fatalf("Server failed: %v", err)
}
}
Example - Docker Container:
# Dockerfile
FROM golang:1.21-alpine
WORKDIR /app
COPY . .
RUN go build -o server .
EXPOSE 8080
CMD ["./server"]
# Go application
func main() {
app := websvrutil.New()
// ... configure routes ...
// Docker sends SIGTERM on `docker stop`
// Default Docker stop timeout is 10 seconds before SIGKILL
// Use shorter timeout to ensure graceful completion
if err := app.RunWithGracefulShutdown(":8080", 8*time.Second); err != nil {
log.Fatal(err)
}
}
Example - Kubernetes Deployment:
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
terminationGracePeriodSeconds: 35 # Allow 35s for graceful shutdown
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"] # Small delay for load balancer
# Go application
func main() {
app := websvrutil.New()
// ... configure routes ...
// Kubernetes sends SIGTERM on pod deletion
// Use timeout less than terminationGracePeriodSeconds
if err := app.RunWithGracefulShutdown(":8080", 30*time.Second); err != nil {
log.Fatal(err)
}
}
Example - With Health Check:
var shutdownRequested atomic.Bool
func main() {
app := websvrutil.New()
// Health check endpoint (used by load balancer)
app.GET("/health", func(w http.ResponseWriter, r *http.Request) {
if shutdownRequested.Load() {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
w.WriteHeader(http.StatusOK)
})
app.GET("/api/data", dataHandler)
// Run with graceful shutdown
// Could enhance to mark unhealthy before actual shutdown
if err := app.RunWithGracefulShutdown(":8080", 30*time.Second); err != nil {
log.Fatal(err)
}
}
Monitoring and Debugging:
- Log signal reception with timestamp for debugging deployments.
- Track shutdown duration metrics to optimize timeout setting.
- Monitor forced connection closures (indicates timeout too short).
- Alert on frequent restarts or shutdown failures.
- Include deployment context in logs (version, environment, reason).
Alternative Patterns:
- Custom Signal Handling: Use Run() + manual signal.Notify() for custom logic
- Multiple Signals: Add SIGHUP, SIGUSR1 for reload/reconfiguration
- Health Check Integration: Mark unhealthy before shutdown
- Graceful Restart: Fork new process before shutting down old one
Limitations:
- Only handles SIGINT and SIGTERM (not SIGHUP, SIGUSR1, etc.)
- Single timeout for all connections (can't prioritize critical connections)
- No built-in health check degradation (implement separately if needed)
- Cannot cancel shutdown once initiated (signal reception commits to shutdown)
RunWithGracefulShutdown은 OS 신호 시 자동 우아한 종료를 지원하는 HTTP 서버를 시작합니다. 이는 웹 애플리케이션을 시작하는 **권장 프로덕션 메서드**입니다. 서버 시작과 지능적인 신호 처리를 결합하여 SIGINT(Ctrl+C) 또는 SIGTERM(일반적인 배포 신호)을 받을 때 추가 코드 없이 자동으로 연결을 드레인합니다.
이 메서드는 다음을 통해 프로덕션 배포를 단순화합니다: - 개발 및 테스트 중 Ctrl+C를 우아하게 처리 - Docker/Kubernetes 종료 신호(SIGTERM)에 응답 - 프로세스 종료 전 연결 드레인 - 배포 중 중단된 요청 방지 - 보일러플레이트 신호 처리 코드 제거
RunWithGracefulShutdown은 다음에 이상적입니다: - 프로덕션 애플리케이션(Docker, Kubernetes, systemd) - 개발 서버(깨끗한 Ctrl+C 종료) - CI/CD 파이프라인(적절한 테스트 정리) - 무중단 업데이트가 필요한 모든 배포
매개변수:
addr: "host:port" 형식으로 수신할 네트워크 주소. Run() 메서드와 동일한 형식:
":8080" - 모든 인터페이스에서 수신, 포트 8080(가장 일반적)
"localhost:8080" - localhost에서만 수신(개발)
"0.0.0.0:8080" - 모든 인터페이스에서 명시적으로 수신
":80" 또는 ":443" - 표준 HTTP/HTTPS 포트(높은 권한 필요)
timeout: 종료 중 활성 연결이 닫힐 때까지 기다릴 최대 시간. 이 타임아웃은 신호 수신부터 강제 연결 종료까지 적용됩니다. 일반적인 타임아웃 값:
5초: 빠른 종료, 빠른 응답을 가진 API에 적합
15초: 일반적인 웹 애플리케이션에 균형 잡힌 값
30초: 대부분의 프로덕션 애플리케이션에 권장(기본 권장사항)
60초: 장기 실행 요청이 있는 애플리케이션을 위한 참을성 있는 종료
120초 이상: 파일 업로드, 비디오 처리, 배치 작업을 위한 매우 참을성 있는 종료 타임아웃이 만료되면 남은 연결이 강제로 닫힙니다.
반환값:
- error: 서버가 시작에 실패하거나 종료 중 오류가 발생하면 오류를 반환합니다. 가능한 오류 시나리오:
- 서버 시작 실패(포트 사용 중, 권한 거부, 잘못된 주소)
- 종료 타임아웃 초과(오류로 래핑된 context.DeadlineExceeded)
- 연결 정리 중 네트워크 오류 신호를 받은 후 서버가 깨끗하게 종료되면 nil을 반환합니다.
동작 방식:
시작 단계: 1. Run()을 사용하여 백그라운드 고루틴에서 서버 시작 2. SIGINT 및 SIGTERM에 대한 신호 핸들러 등록 3. 서버 오류 또는 OS 신호를 기다리며 차단
신호 처리: 다음에 자동으로 응답:
SIGINT: Ctrl+C, 쉘 인터럽트, 키보드 인터럽트에 의해 전송
SIGTERM: kill 명령, Docker stop, Kubernetes 포드 종료, systemd 서비스 중지, 프로세스 관리자, 배포 스크립트에 의해 전송
종료 순서: 1. SIGINT 또는 SIGTERM 신호 수신 2. 수신된 신호 이름을 콘솔에 출력 3. 지정된 타임아웃으로 컨텍스트 생성 4. Shutdown()을 호출하여 연결을 우아하게 드레인 5. 연결이 닫힐 때까지 타임아웃 기간만큼 대기 6. 성공 시 nil 또는 실패 시 오류 반환
두 가지 종료 경로:
서버 시작 오류: 오류와 함께 즉시 반환(포트 충돌 등)
신호 수신: 우아한 종료 시작, 완료 후 반환
차단 호출: 이 메서드는 서버가 중지될 때까지(신호 또는 오류를 통해) 차단됩니다. main 고루틴 또는 전용 서버 고루틴에서 호출되어야 합니다.
신호 상세:
SIGINT(신호 인터럽트):
트리거: 터미널에서 Ctrl+C, kill -INT <pid>
사용 사례: 개발, 수동 중단, 사용자 시작 종료
종료 코드: 일반적으로 130(128 + 2)
SIGTERM(신호 종료):
트리거: kill <pid>, Docker stop, Kubernetes 포드 삭제, systemd stop
사용 사례: 자동화된 배포, 오케스트레이터 관리 생명주기
종료 코드: 일반적으로 143(128 + 15)
프로덕션 환경에서 가장 일반적
스레드 안전성:
- 단일 고루틴에서 호출(일반적으로 main()).
- 서버 및 신호 처리를 위한 내부 고루틴 생성.
- 안전한 동시 신호 전달(채널 버퍼링, signal.Notify 스레드 안전).
배포 통합:
- Docker: `docker stop`에 응답(SIGTERM 전송, 대기, 그 다음 SIGKILL)
- Kubernetes: 포드 종료 처리(kubelet의 SIGTERM)
- systemd: `systemctl stop`에 응답(SIGTERM 전송)
- Heroku/PaaS: dyno/인스턴스 종료 신호에 응답
- 프로세스 관리자(PM2, Supervisor): 관리되는 프로세스 재시작 처리
모범 사례:
- 프로덕션에서는 일반 Run() 대신 이 메서드를 사용하세요.
- 가장 긴 예상 요청 기간보다 타임아웃을 길게 설정하세요.
- 정리 작업(DB 커밋, 캐시 쓰기)을 위한 타임아웃 버퍼를 추가하세요.
- 모니터링을 위해 시작 및 종료 이벤트를 로깅하세요.
- 프로덕션 배포 전에 부하 상태에서 종료 동작을 테스트하세요.
- 신호 종료 전에 헬스 체크 저하를 고려하세요.
- 메서드 반환 후 리소스(DB 연결, 파일)를 정리하세요.
성능 고려사항:
- 신호 처리 오버헤드는 무시할 수 있습니다(채널 + 고루틴).
- 종료 성능은 활성 연결 수와 타임아웃에 따라 다릅니다.
- 타임아웃이 너무 짧으면: 강제로 닫힌 연결의 위험
- 타임아웃이 너무 길면: 느린 배포, 긴 포드 종료
- 애플리케이션 요청 패턴을 기반으로 타임아웃의 균형을 맞추세요.
일반적인 사용 사례:
- 프로덕션 서버: app.RunWithGracefulShutdown(":8080", 30*time.Second)
- 개발 서버: app.RunWithGracefulShutdown("localhost:3000", 5*time.Second)
- Docker 컨테이너: app.RunWithGracefulShutdown(":8080", 25*time.Second)
- Kubernetes 포드: app.RunWithGracefulShutdown(":8080", 30*time.Second)
- 빠른 테스트: app.RunWithGracefulShutdown(":9000", 1*time.Second)
예제 - 기본 프로덕션 서버:
func main() {
app := websvrutil.New()
app.GET("/", homeHandler)
app.GET("/api/users", usersHandler)
log.Println("Starting server on :8080")
if err := app.RunWithGracefulShutdown(":8080", 30*time.Second); err != nil {
log.Fatalf("Server error: %v", err)
}
log.Println("Server shutdown complete")
}
예제 - 리소스 정리와 함께:
func main() {
// 종속성 초기화
db, err := sql.Open("postgres", dbURL)
if err != nil {
log.Fatal(err)
}
defer db.Close() // 서버 중지 후 정리
cache := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
defer cache.Close()
// 애플리케이션 구성
app := websvrutil.New()
app.GET("/api/data", func(w http.ResponseWriter, r *http.Request) {
// db 및 cache 사용
})
// 우아한 종료와 함께 시작
log.Println("Server starting...")
if err := app.RunWithGracefulShutdown(":8080", 30*time.Second); err != nil {
log.Printf("Server error: %v", err)
os.Exit(1)
}
log.Println("Cleanup complete, exiting")
}
모니터링 및 디버깅:
- 배포 디버깅을 위해 타임스탬프와 함께 신호 수신을 로깅하세요.
- 타임아웃 설정을 최적화하기 위해 종료 기간 메트릭을 추적하세요.
- 강제 연결 종료를 모니터링하세요(타임아웃이 너무 짧음을 나타냄).
- 빈번한 재시작 또는 종료 실패 시 경고하세요.
- 로그에 배포 컨텍스트(버전, 환경, 이유)를 포함하세요.
대안 패턴:
- 커스텀 신호 처리: 커스텀 로직을 위해 Run() + 수동 signal.Notify() 사용
- 여러 신호: 리로드/재구성을 위해 SIGHUP, SIGUSR1 추가
- 헬스 체크 통합: 종료 전에 비정상 표시
- 우아한 재시작: 이전 프로세스를 종료하기 전에 새 프로세스 포크
제한사항:
- SIGINT 및 SIGTERM만 처리(SIGHUP, SIGUSR1 등은 아님)
- 모든 연결에 단일 타임아웃(중요한 연결의 우선순위를 지정할 수 없음)
- 내장 헬스 체크 저하 없음(필요한 경우 별도로 구현)
- 시작된 종료를 취소할 수 없음(신호 수신이 종료를 커밋)
func (*App) ServeHTTP ¶
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements the http.Handler interface, making App compatible with standard Go HTTP servers. This is the **entry point for every HTTP request** processed by the application.
ServeHTTP is automatically called by Go's http.Server for each incoming request. You typically don't call this method directly; instead, it's invoked by the server infrastructure when handling connections.
Purpose:
- Implements http.Handler interface for compatibility with net/http ecosystem
- Serves as the single entry point for all HTTP requests
- Builds the complete handler chain (router + middleware) on each request
- Stores application context in request context for downstream handlers
- Provides thread-safe access to App state during request processing
Interface Compliance:
- Satisfies http.Handler interface: ServeHTTP(ResponseWriter, *Request)
- Enables App to be used anywhere http.Handler is accepted:
- http.Server.Handler field
- http.ListenAndServe(addr, app)
- Middleware wrapping: middleware(app)
- Testing: httptest.NewServer(app)
- Reverse proxies, API gateways, etc.
Parameters:
w http.ResponseWriter: Response writer for sending HTTP response to client. Used by handlers to write status codes, headers, and body content. Passed through middleware chain to final route handler.
r *http.Request: Incoming HTTP request containing method, URL, headers, body. Enhanced with application context before passing to handler chain. Contains all request metadata needed for routing and processing.
Returns:
- (none): ServeHTTP returns nothing per http.Handler interface contract. Response is written directly to ResponseWriter. Errors should be handled by writing appropriate HTTP error responses.
Behavior:
- **Acquire Read Lock**: Locks app.mu.RLock() for thread-safe handler building
- **Build Handler Chain**: Calls buildHandler() to construct middleware + router chain
- **Release Read Lock**: Unlocks app.mu.RUnlock() after handler construction
- **Store App Context**: Adds App instance to request context with key "app"
- **Update Request**: Creates new request with enhanced context
- **Delegate Handling**: Calls handler.ServeHTTP(w, r) to process request
Context Storage:
- Stores App instance in request context: context.WithValue(r.Context(), "app", a)
- Purpose: Enable template rendering and app access in handlers
- Retrieval in handlers: app := r.Context().Value("app").(*websvrutil.App)
- Available throughout request lifecycle (middleware, handlers, helpers)
- Useful for: template rendering, app configuration access, shared resources
Thread-Safety:
- Fully thread-safe for concurrent request handling.
- Read lock (RLock) allows multiple simultaneous requests.
- Handler building is isolated per-request (no shared state mutation).
- Safe for high-concurrency environments (thousands of concurrent requests).
- Write operations (Use, GET, POST, etc.) require full write lock, blocking ServeHTTP.
Performance Characteristics:
- Lock Overhead: RLock/RUnlock is very fast (uncontended: ~20ns)
- Handler Building: O(n) where n = middleware count (typically 3-10)
- Context Creation: Negligible overhead (~50ns)
- Overall Overhead: < 1μs for typical configurations
- Scalability: Handles millions of requests/second on modern hardware
Usage with http.Server:
server := &http.Server{
Addr: ":8080",
Handler: app, // App implements http.Handler via ServeHTTP
}
server.ListenAndServe()
Usage with http.ListenAndServe:
http.ListenAndServe(":8080", app) // App used as http.Handler
Usage in Testing:
func TestHandler(t *testing.T) {
app := websvrutil.New()
app.GET("/test", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
// ServeHTTP called automatically by test infrastructure
req := httptest.NewRequest("GET", "/test", nil)
rec := httptest.NewRecorder()
app.ServeHTTP(rec, req) // Direct invocation in tests
assert.Equal(t, 200, rec.Code)
assert.Equal(t, "OK", rec.Body.String())
}
Middleware Execution Flow:
// With registered middleware
app.Use(LoggingMiddleware)
app.Use(AuthMiddleware)
app.GET("/api/data", dataHandler)
// Request arrives → ServeHTTP called
// 1. Acquire read lock
// 2. Build: AuthMiddleware(LoggingMiddleware(router))
// 3. Release read lock
// 4. Store app in context
// 5. Execute: Auth → Logging → Router → dataHandler
// 6. Response returns through same chain
Context Access in Handlers:
func myHandler(w http.ResponseWriter, r *http.Request) {
// Retrieve app from context (stored by ServeHTTP)
app := r.Context().Value("app").(*websvrutil.App)
// Access template engine
templates := app.TemplateEngine()
// Use for rendering
// ...
}
Common Use Cases:
- Standard HTTP Server: server.Handler = app
- Middleware Wrapping: wrappedApp := someMiddleware(app)
- Reverse Proxy Backend: proxy target pointing to app
- Testing: httptest.NewServer(app) or direct ServeHTTP calls
- Embedded Servers: Run app alongside other services
Integration Examples:
// Example 1: Standard Server
app := websvrutil.New()
app.GET("/", homeHandler)
http.ListenAndServe(":8080", app) // ServeHTTP called per request
// Example 2: Custom Server
server := &http.Server{
Addr: ":8080",
Handler: app,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
server.ListenAndServe()
// Example 3: Wrapped with External Middleware
app := websvrutil.New()
wrapped := http.TimeoutHandler(app, 30*time.Second, "Timeout")
http.ListenAndServe(":8080", wrapped)
// Example 4: Multiple Apps (different ports)
publicApp := websvrutil.New()
publicApp.GET("/", publicHandler)
adminApp := websvrutil.New()
adminApp.GET("/admin", adminHandler)
go http.ListenAndServe(":8080", publicApp) // Public on 8080
http.ListenAndServe(":9090", adminApp) // Admin on 9090
Performance Optimization Notes:
- Handler building per-request ensures middleware changes take effect immediately
- Alternative: Cache handler and rebuild on middleware changes (more complex)
- Current approach: Simple, correct, sufficient for 99% of applications
- For extreme performance (1M+ req/s): Consider handler caching with invalidation
Compatibility Notes:
- Works with all Go HTTP infrastructure (http.Server, http.Client, httptest, etc.)
- Compatible with third-party middleware (gorilla, negroni, etc.)
- Can be embedded in larger applications or frameworks
- Safe to use with http2, HTTP/3 (QUIC) when available
Error Handling:
- ServeHTTP itself does not return errors (interface contract)
- Errors must be handled by writing HTTP error responses
- Panics in handlers should be caught by recovery middleware
- Network errors handled by http.Server automatically
Best Practices:
- Don't call ServeHTTP directly except in tests
- Let http.Server manage ServeHTTP invocation
- Use recovery middleware to catch panics
- Log request/response in middleware, not in ServeHTTP
- Keep handler building fast (minimize middleware count)
Debugging Tips:
- Add logging before/after handler.ServeHTTP() to trace requests
- Use request ID middleware for request correlation
- Monitor lock contention (should be near zero)
- Profile ServeHTTP execution time for performance tuning
ServeHTTP는 http.Handler 인터페이스를 구현하여 App을 표준 Go HTTP 서버와 호환되게 합니다. 이는 애플리케이션에서 처리하는 **모든 HTTP 요청의 진입점**입니다.
ServeHTTP는 각 들어오는 요청에 대해 Go의 http.Server에 의해 자동으로 호출됩니다. 일반적으로 이 메서드를 직접 호출하지 않으며, 대신 연결을 처리할 때 서버 인프라에 의해 호출됩니다.
목적:
- net/http 생태계와의 호환성을 위해 http.Handler 인터페이스 구현
- 모든 HTTP 요청의 단일 진입점 역할
- 각 요청에서 완전한 핸들러 체인(라우터 + 미들웨어) 구축
- 다운스트림 핸들러를 위해 요청 컨텍스트에 애플리케이션 컨텍스트 저장
- 요청 처리 중 App 상태에 대한 스레드 안전 액세스 제공
인터페이스 준수:
- http.Handler 인터페이스를 만족: ServeHTTP(ResponseWriter, *Request)
- http.Handler가 허용되는 모든 곳에서 App을 사용할 수 있게 합니다:
- http.Server.Handler 필드
- http.ListenAndServe(addr, app)
- 미들웨어 래핑: middleware(app)
- 테스팅: httptest.NewServer(app)
- 리버스 프록시, API 게이트웨이 등
매개변수:
w http.ResponseWriter: 클라이언트에 HTTP 응답을 보내기 위한 응답 작성기. 핸들러가 상태 코드, 헤더 및 본문 내용을 작성하는 데 사용됩니다. 미들웨어 체인을 통해 최종 경로 핸들러로 전달됩니다.
r *http.Request: 메서드, URL, 헤더, 본문을 포함하는 들어오는 HTTP 요청. 핸들러 체인으로 전달하기 전에 애플리케이션 컨텍스트로 향상됩니다. 라우팅 및 처리에 필요한 모든 요청 메타데이터를 포함합니다.
반환값:
- (없음): ServeHTTP는 http.Handler 인터페이스 계약에 따라 아무것도 반환하지 않습니다. 응답은 ResponseWriter에 직접 작성됩니다. 오류는 적절한 HTTP 오류 응답을 작성하여 처리해야 합니다.
동작 방식:
- **읽기 잠금 획득**: 스레드 안전 핸들러 구축을 위해 app.mu.RLock() 잠금
- **핸들러 체인 구축**: buildHandler()를 호출하여 미들웨어 + 라우터 체인 구성
- **읽기 잠금 해제**: 핸들러 구성 후 app.mu.RUnlock() 잠금 해제
- **앱 컨텍스트 저장**: "app" 키로 요청 컨텍스트에 App 인스턴스 추가
- **요청 업데이트**: 향상된 컨텍스트로 새 요청 생성
- **처리 위임**: handler.ServeHTTP(w, r)를 호출하여 요청 처리
컨텍스트 저장:
- 요청 컨텍스트에 App 인스턴스 저장: context.WithValue(r.Context(), "app", a)
- 목적: 핸들러에서 템플릿 렌더링 및 앱 액세스 활성화
- 핸들러에서 검색: app := r.Context().Value("app").(*websvrutil.App)
- 요청 생명주기 전체에서 사용 가능(미들웨어, 핸들러, 헬퍼)
- 유용한 용도: 템플릿 렌더링, 앱 구성 액세스, 공유 리소스
스레드 안전성:
- 동시 요청 처리를 위해 완전히 스레드 안전합니다.
- 읽기 잠금(RLock)은 여러 동시 요청을 허용합니다.
- 핸들러 구축은 요청별로 격리됩니다(공유 상태 변경 없음).
- 높은 동시성 환경에 안전합니다(수천 개의 동시 요청).
- 쓰기 작업(Use, GET, POST 등)은 전체 쓰기 잠금이 필요하며 ServeHTTP를 차단합니다.
성능 특성:
- 잠금 오버헤드: RLock/RUnlock은 매우 빠름(경합 없음: ~20ns)
- 핸들러 구축: O(n), 여기서 n = 미들웨어 수(일반적으로 3-10)
- 컨텍스트 생성: 무시할 수 있는 오버헤드(~50ns)
- 전체 오버헤드: 일반적인 구성의 경우 < 1μs
- 확장성: 현대 하드웨어에서 초당 수백만 요청 처리
모범 사례:
- 테스트를 제외하고 ServeHTTP를 직접 호출하지 마세요.
- http.Server가 ServeHTTP 호출을 관리하도록 하세요.
- 패닉을 잡기 위해 복구 미들웨어를 사용하세요.
- ServeHTTP가 아닌 미들웨어에서 요청/응답을 로깅하세요.
- 핸들러 구축을 빠르게 유지하세요(미들웨어 수 최소화).
디버깅 팁:
- handler.ServeHTTP() 전후에 로깅을 추가하여 요청을 추적하세요.
- 요청 상관관계를 위해 요청 ID 미들웨어를 사용하세요.
- 잠금 경합을 모니터링하세요(거의 0에 가까워야 함).
- 성능 튜닝을 위해 ServeHTTP 실행 시간을 프로파일링하세요.
func (*App) Shutdown ¶
Shutdown initiates graceful server shutdown, waiting for active connections to complete or timeout. This method is the recommended way to stop a running HTTP server without abruptly dropping active requests. It stops accepting new connections immediately while allowing in-flight requests to complete within the context timeout period. This ensures data integrity, prevents client errors, and maintains service reliability during deployments, scaling, or maintenance.
Graceful shutdown is essential for production systems to avoid: - Dropped client requests (broken downloads, incomplete API calls) - Corrupted database transactions (half-written data) - Lost websocket messages or streaming data - Error monitoring alerts from terminated connections - Poor user experience during deployments
Parameters:
- ctx: Context controlling shutdown timeout and cancellation. Typically created with context.WithTimeout() or context.WithDeadline(). Common timeout values:
- 5 seconds: Fast shutdown, risk of terminating slow requests
- 30 seconds: Balanced approach for most applications (recommended)
- 60 seconds: Patient shutdown for long-running requests
- No timeout (context.Background()): Wait indefinitely (not recommended) If context expires before all connections close, Shutdown returns context.DeadlineExceeded. Connections remaining after timeout are forcibly closed.
Returns:
- error: Returns error if shutdown fails or server isn't running. Common errors:
- "server is not running" - Shutdown() called when server isn't started
- "server is not initialized" - Internal state inconsistency
- context.DeadlineExceeded - Timeout reached before all connections closed
- Network errors during connection cleanup Returns nil on successful graceful shutdown (all connections drained within timeout).
Behavior:
Immediate Effect: Stops accepting new connections instantly. New client connection attempts receive "connection refused" errors.
In-Flight Requests: Allows active HTTP requests to complete normally. Handlers continue executing, writing responses, closing connections.
Idle Connections: Keep-alive idle connections are closed immediately. No disruption to clients since no active request was in progress.
WebSocket/Streaming: Long-lived connections get full timeout period to close. Application should monitor context and cleanly close streams.
Timeout Behavior: If context deadline expires:
Remaining connections are forcibly terminated
Shutdown returns context.DeadlineExceeded error
Clients may see "connection reset" or incomplete responses
Run() Completion: After Shutdown() completes, Run() unblocks and returns nil. This allows main goroutine or deployment scripts to proceed.
State Cleanup: Running flag is set to false, allowing app reuse (though not recommended).
Concurrency:
Thread-Safe: Safe to call from different goroutine while Run() is blocking. Common pattern: Run() in main goroutine, Shutdown() in signal handler goroutine.
Single Shutdown: Calling Shutdown() multiple times is safe but redundant. Subsequent calls return immediately (idempotent operation).
Locks: Uses read lock to check state, doesn't block Run() operation. Delegates to http.Server.Shutdown() which handles synchronization internally.
Usage Patterns:
- Manual Shutdown: Application-triggered shutdown based on internal logic
- Signal Handler: OS signal (SIGINT/SIGTERM) triggers shutdown
- Health Check: Gracefully remove server from load balancer before shutdown
- Rolling Deployment: Drain connections before replacing server instance
- Testing: Clean teardown of test servers
Best Practices:
- Always use timeout context to prevent indefinite waits.
- Choose timeout based on longest expected request duration.
- Log shutdown start/completion for monitoring and debugging.
- Notify load balancer to stop sending traffic before shutdown.
- Close database connections and external resources after shutdown.
- In handlers, check context cancellation for early exit during shutdown.
- Test shutdown behavior under load to validate timeout settings.
Common Use Cases:
- Signal-based shutdown: syscall.SIGINT/SIGTERM handler calls Shutdown()
- Kubernetes pod termination: PreStop hook calls shutdown endpoint
- Health check degradation: Mark unhealthy, wait, then shutdown
- Deployment automation: Blue-green or rolling update workflows
- Development: Ctrl+C triggers graceful shutdown
Example - Basic Shutdown with Timeout:
// In separate goroutine or signal handler
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := app.Shutdown(ctx); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Println("Shutdown timeout: some connections were forcibly closed")
} else {
log.Printf("Shutdown error: %v", err)
}
} else {
log.Println("Shutdown completed successfully")
}
Example - Signal Handler Pattern:
go func() {
if err := app.Run(":8080"); err != nil {
log.Fatalf("Server error: %v", err)
}
}()
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := app.Shutdown(ctx); err != nil {
log.Fatalf("Shutdown failed: %v", err)
}
log.Println("Server exited")
Example - Health Check Integration:
var healthy atomic.Bool
healthy.Store(true)
app.GET("/health", func(w http.ResponseWriter, r *http.Request) {
if healthy.Load() {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusServiceUnavailable)
}
})
// On shutdown signal
healthy.Store(false) // Mark unhealthy
time.Sleep(10 * time.Second) // Wait for load balancer to notice
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
app.Shutdown(ctx) // Drain connections
Example - Handler Context Check:
app.GET("/long-task", func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 100; i++ {
// Check if shutdown is in progress
select {
case <-r.Context().Done():
log.Println("Handler cancelled during shutdown")
http.Error(w, "Service shutting down", http.StatusServiceUnavailable)
return
default:
}
// Do work
time.Sleep(100 * time.Millisecond)
}
w.Write([]byte("Completed"))
})
Kubernetes Example:
# In Kubernetes Pod spec
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15 && kill -SIGTERM 1"]
# Application handles SIGTERM
signal.Notify(quit, syscall.SIGTERM)
<-quit
app.Shutdown(context.WithTimeout(context.Background(), 30*time.Second))
Monitoring and Observability:
- Log shutdown initiation with timestamp
- Track shutdown duration metrics
- Alert on shutdown timeouts
- Monitor forcibly closed connection count
- Include shutdown reason in logs (signal, health check, manual)
Shutdown은 우아한 서버 종료를 시작하여 활성 연결이 완료되거나 타임아웃될 때까지 기다립니다. 이 메서드는 활성 요청을 갑자기 중단하지 않고 실행 중인 HTTP 서버를 중지하는 권장 방법입니다. 새 연결 수락을 즉시 중지하면서 진행 중인 요청이 컨텍스트 타임아웃 기간 내에 완료되도록 합니다. 이는 데이터 무결성을 보장하고, 클라이언트 오류를 방지하며, 배포, 확장 또는 유지 관리 중 서비스 안정성을 유지합니다.
우아한 종료는 다음을 방지하기 위해 프로덕션 시스템에 필수적입니다: - 중단된 클라이언트 요청(깨진 다운로드, 불완전한 API 호출) - 손상된 데이터베이스 트랜잭션(절반만 작성된 데이터) - 손실된 웹소켓 메시지 또는 스트리밍 데이터 - 종료된 연결로 인한 오류 모니터링 경고 - 배포 중 나쁜 사용자 경험
매개변수:
- ctx: 종료 타임아웃 및 취소를 제어하는 컨텍스트. 일반적으로 context.WithTimeout() 또는 context.WithDeadline()로 생성됩니다. 일반적인 타임아웃 값:
- 5초: 빠른 종료, 느린 요청 종료 위험
- 30초: 대부분의 애플리케이션에 균형 잡힌 접근 방식(권장)
- 60초: 장기 실행 요청에 대한 참을성 있는 종료
- 타임아웃 없음(context.Background()): 무기한 대기(권장하지 않음) 모든 연결이 닫히기 전에 컨텍스트가 만료되면 Shutdown은 context.DeadlineExceeded를 반환합니다. 타임아웃 후 남은 연결은 강제로 닫힙니다.
반환값:
- error: 종료가 실패하거나 서버가 실행 중이지 않으면 오류를 반환합니다. 일반적인 오류:
- "server is not running" - 서버가 시작되지 않았을 때 Shutdown() 호출
- "server is not initialized" - 내부 상태 불일치
- context.DeadlineExceeded - 모든 연결이 닫히기 전에 타임아웃 도달
- 연결 정리 중 네트워크 오류 성공적인 우아한 종료(타임아웃 내 모든 연결 드레인) 시 nil 반환.
동작 방식:
즉각적인 효과: 새 연결 수락을 즉시 중지합니다. 새 클라이언트 연결 시도는 "connection refused" 오류를 받습니다.
진행 중인 요청: 활성 HTTP 요청이 정상적으로 완료되도록 합니다. 핸들러는 계속 실행되고, 응답을 작성하고, 연결을 닫습니다.
유휴 연결: Keep-alive 유휴 연결은 즉시 닫힙니다. 진행 중인 활성 요청이 없었으므로 클라이언트에 중단이 없습니다.
WebSocket/스트리밍: 장기 연결은 닫기 위한 전체 타임아웃 기간을 받습니다. 애플리케이션은 컨텍스트를 모니터링하고 스트림을 깔끔하게 닫아야 합니다.
타임아웃 동작: 컨텍스트 데드라인이 만료되면:
남은 연결이 강제로 종료됨
Shutdown이 context.DeadlineExceeded 오류를 반환
클라이언트는 "connection reset" 또는 불완전한 응답을 볼 수 있음
Run() 완료: Shutdown()이 완료된 후 Run()이 차단 해제되고 nil을 반환합니다. 이를 통해 main 고루틴 또는 배포 스크립트가 진행할 수 있습니다.
상태 정리: running 플래그가 false로 설정되어 앱 재사용이 가능합니다(권장하지 않음).
동시성:
스레드 안전: Run()이 차단되는 동안 다른 고루틴에서 호출하기에 안전합니다. 일반적인 패턴: main 고루틴에서 Run(), 신호 핸들러 고루틴에서 Shutdown().
단일 종료: Shutdown()을 여러 번 호출해도 안전하지만 중복됩니다. 후속 호출은 즉시 반환됩니다(멱등 작업).
잠금: 읽기 잠금을 사용하여 상태를 확인하고 Run() 작업을 차단하지 않습니다. 내부적으로 동기화를 처리하는 http.Server.Shutdown()에 위임합니다.
사용 패턴:
- 수동 종료: 내부 로직을 기반으로 애플리케이션이 트리거하는 종료
- 신호 핸들러: OS 신호(SIGINT/SIGTERM)가 종료를 트리거
- 헬스 체크: 종료 전에 로드 밸런서에서 서버를 우아하게 제거
- 롤링 배포: 서버 인스턴스를 교체하기 전에 연결 드레인
- 테스트: 테스트 서버의 깔끔한 해체
모범 사례:
- 무기한 대기를 방지하기 위해 항상 타임아웃 컨텍스트를 사용하세요.
- 예상되는 가장 긴 요청 기간을 기반으로 타임아웃을 선택하세요.
- 모니터링 및 디버깅을 위해 종료 시작/완료를 로깅하세요.
- 종료 전에 로드 밸런서에 트래픽 전송 중지를 알리세요.
- 종료 후 데이터베이스 연결 및 외부 리소스를 닫으세요.
- 핸들러에서 종료 중 조기 종료를 위해 컨텍스트 취소를 확인하세요.
- 타임아웃 설정을 검증하기 위해 부하 상태에서 종료 동작을 테스트하세요.
일반적인 사용 사례:
- 신호 기반 종료: syscall.SIGINT/SIGTERM 핸들러가 Shutdown() 호출
- Kubernetes 포드 종료: PreStop 후크가 종료 엔드포인트 호출
- 헬스 체크 저하: 비정상 표시, 대기, 그 다음 종료
- 배포 자동화: Blue-green 또는 롤링 업데이트 워크플로
- 개발: Ctrl+C가 우아한 종료 트리거
예제 - 타임아웃이 있는 기본 종료:
// 별도의 고루틴 또는 신호 핸들러에서
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := app.Shutdown(ctx); err != nil {
if errors.Is(err, context.DeadlineExceeded) {
log.Println("Shutdown timeout: some connections were forcibly closed")
} else {
log.Printf("Shutdown error: %v", err)
}
} else {
log.Println("Shutdown completed successfully")
}
예제 - 신호 핸들러 패턴:
go func() {
if err := app.Run(":8080"); err != nil {
log.Fatalf("Server error: %v", err)
}
}()
// 인터럽트 신호 대기
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := app.Shutdown(ctx); err != nil {
log.Fatalf("Shutdown failed: %v", err)
}
log.Println("Server exited")
예제 - 헬스 체크 통합:
var healthy atomic.Bool
healthy.Store(true)
app.GET("/health", func(w http.ResponseWriter, r *http.Request) {
if healthy.Load() {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusServiceUnavailable)
}
})
// 종료 신호 시
healthy.Store(false) // 비정상 표시
time.Sleep(10 * time.Second) // 로드 밸런서가 감지할 때까지 대기
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
app.Shutdown(ctx) // 연결 드레인
예제 - 핸들러 컨텍스트 확인:
app.GET("/long-task", func(w http.ResponseWriter, r *http.Request) {
for i := 0; i < 100; i++ {
// 종료가 진행 중인지 확인
select {
case <-r.Context().Done():
log.Println("Handler cancelled during shutdown")
http.Error(w, "Service shutting down", http.StatusServiceUnavailable)
return
default:
}
// 작업 수행
time.Sleep(100 * time.Millisecond)
}
w.Write([]byte("Completed"))
})
Kubernetes 예제:
# Kubernetes Pod spec에서
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15 && kill -SIGTERM 1"]
# 애플리케이션이 SIGTERM 처리
signal.Notify(quit, syscall.SIGTERM)
<-quit
app.Shutdown(context.WithTimeout(context.Background(), 30*time.Second))
모니터링 및 관찰 가능성:
- 타임스탬프와 함께 종료 시작 로깅
- 종료 기간 메트릭 추적
- 종료 타임아웃 시 경고
- 강제 닫힌 연결 수 모니터링
- 로그에 종료 이유 포함(신호, 헬스 체크, 수동)
func (*App) Static ¶
Static registers a route to serve static files from a filesystem directory. This method provides a convenient way to serve CSS, JavaScript, images, fonts, and other static assets directly from the filesystem without writing custom handlers. It internally uses Go's http.FileServer with http.StripPrefix to handle file serving, directory browsing (if enabled), range requests, ETags, and proper MIME type detection.
Static routes are ideal for serving assets in development and simple deployments. For production, consider using a CDN or dedicated static file server (nginx, CloudFront) for better performance, caching, and reduced application server load.
Parameters:
prefix: URL path prefix where static files will be served (must start with "/"). This prefix is stripped before looking up files in the directory. Common patterns:
"/static" - serves all files under /static/* URLs
"/assets" - serves all files under /assets/* URLs
"/public" - serves all files under /public/* URLs The prefix should NOT include a trailing slash (use "/static", not "/static/").
dir: Filesystem directory path containing static files to serve. Can be absolute path ("/var/www/static") or relative to working directory ("./public"). The directory must exist and be readable, or file requests will return 404. Relative paths are resolved from the current working directory (where app starts). Consider using absolute paths or path.Join for portability.
Returns:
- *App: Returns the App instance for method chaining, enabling fluent API: app.Static("/static", "./public").Static("/assets", "./assets")
Behavior:
Registration Timing: Must be called before Run() or RunWithGracefulShutdown(). Attempting to register static routes after server start causes panic.
Path Mapping: URL path "/static/css/style.css" maps to "{dir}/css/style.css". The prefix is stripped, remaining path is used to locate file.
Directory Listings: Go's http.FileServer shows directory listings if no index.html exists. To disable directory browsing, ensure directories have index files or use custom handler.
File Lookup: Files are served directly from filesystem without caching. File changes are reflected immediately (useful in development). For production, consider pre-compiling assets or using embedded filesystems.
MIME Types: Content-Type header is automatically set based on file extension using mime.TypeByExtension (e.g., .css → text/css, .js → application/javascript).
Range Requests: http.FileServer supports HTTP range requests (byte-range downloads), enabling resume/pause functionality and efficient video streaming.
ETags: Automatically generated based on file modification time and size, enabling browser cache validation and conditional requests (If-None-Match).
Error Handling: Returns 404 Not Found if file doesn't exist, 403 Forbidden if file isn't readable, 500 Internal Server Error on filesystem errors.
Thread-Safety:
- Safe for concurrent calls during app configuration phase (before server start).
- Acquires mutex lock to prevent concurrent modifications to routing table.
- Panics if called after server is running.
Security Considerations:
Path Traversal: Go's http.FileServer automatically sanitizes paths to prevent "../" attacks. Requests for "/static/../../etc/passwd" are safely blocked.
Hidden Files: Files starting with "." (e.g., .env, .git) are served by default. Consider using custom middleware to block sensitive files if needed.
Directory Traversal: If directory listings are disabled, attackers can't enumerate files. Add index.html or use custom 403 handler for directories.
Sensitive Files: Never serve directories containing source code, configuration, or sensitive data. Use dedicated directories for public assets only.
Performance Considerations:
No Built-in Caching: Files are read from disk on each request. For high-traffic sites, use CDN, nginx, or implement caching middleware.
Development vs Production: Serving static files from application is convenient for development but inefficient for production. Use reverse proxy or CDN in production.
Compression: http.FileServer doesn't compress responses. Add gzip middleware or use reverse proxy with compression for better performance.
File System I/O: Each request performs filesystem I/O. Consider embedding static files in binary using embed.FS for better performance.
Common Use Cases:
- Development assets: app.Static("/static", "./static")
- Multiple asset directories: app.Static("/css", "./css").Static("/js", "./js")
- Public uploads: app.Static("/uploads", "./uploads")
- Documentation: app.Static("/docs", "./docs/html")
- Single-page app: app.Static("/", "./dist") (for React, Vue, Angular builds)
Example - Basic Static File Serving:
// Serve files from ./public directory at /static/* URLs
app.Static("/static", "./public")
// URL: /static/css/style.css → File: ./public/css/style.css
// URL: /static/js/app.js → File: ./public/js/app.js
// URL: /static/images/logo.png → File: ./public/images/logo.png
Example - Multiple Static Directories:
// Serve CSS, JavaScript, and images from separate directories
app.Static("/css", "./assets/css")
app.Static("/js", "./assets/js")
app.Static("/images", "./assets/images")
Example - Absolute Path:
// Use absolute path for production deployment
app.Static("/static", "/var/www/myapp/static")
Example - Embedded Filesystem (Go 1.16+):
//go:embed static/*
var staticFiles embed.FS
// Serve embedded files (better performance, single binary)
app.GET("/static/*", func(w http.ResponseWriter, r *http.Request) {
fs := http.FileServer(http.FS(staticFiles))
fs.ServeHTTP(w, r)
})
Example - Custom Static Handler with Cache Headers:
app.GET("/static/*", func(w http.ResponseWriter, r *http.Request) {
// Add aggressive caching for production
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
// Serve file
fs := http.StripPrefix("/static/", http.FileServer(http.Dir("./public")))
fs.ServeHTTP(w, r)
})
Best Practices:
- Use dedicated directories for static assets (don't mix with source code).
- Add Cache-Control headers for better performance (via middleware).
- Consider using content-addressable URLs (e.g., /static/app.abc123.js).
- Block sensitive files (.git, .env, .config) using middleware.
- Use CDN or reverse proxy for production static file serving.
- Enable compression (gzip) for text assets (CSS, JS, HTML).
- Set appropriate CORS headers if assets are accessed from different domains.
Alternative Approaches:
- Embedded Files: Use //go:embed for single-binary deployment with better performance.
- CDN: Upload assets to CDN (CloudFront, CloudFlare) for global distribution.
- Reverse Proxy: nginx/Apache serve static files, proxy API requests to Go app.
- Custom Handler: Write custom http.Handler for advanced caching, transformation.
Static은 파일시스템 디렉토리에서 정적 파일을 제공하는 라우트를 등록합니다. 이 메서드는 CSS, JavaScript, 이미지, 폰트 및 기타 정적 자산을 커스텀 핸들러를 작성하지 않고 파일시스템에서 직접 제공하는 편리한 방법을 제공합니다. 내부적으로 Go의 http.FileServer와 http.StripPrefix를 사용하여 파일 제공, 디렉토리 탐색(활성화된 경우), 범위 요청, ETag 및 적절한 MIME 타입 감지를 처리합니다.
Static 라우트는 개발 및 간단한 배포에서 자산을 제공하는 데 이상적입니다. 프로덕션의 경우 더 나은 성능, 캐싱 및 애플리케이션 서버 부하 감소를 위해 CDN 또는 전용 정적 파일 서버(nginx, CloudFront) 사용을 고려하세요.
매개변수:
prefix: 정적 파일이 제공될 URL 경로 접두사("/"로 시작해야 함). 이 접두사는 디렉토리에서 파일을 찾기 전에 제거됩니다. 일반적인 패턴:
"/static" - /static/* URL 하위의 모든 파일 제공
"/assets" - /assets/* URL 하위의 모든 파일 제공
"/public" - /public/* URL 하위의 모든 파일 제공 접두사는 후행 슬래시를 포함하지 않아야 합니다("/static/" 대신 "/static" 사용).
dir: 제공할 정적 파일이 포함된 파일시스템 디렉토리 경로. 절대 경로("/var/www/static") 또는 작업 디렉토리 기준 상대 경로("./public")일 수 있습니다. 디렉토리가 존재하고 읽을 수 있어야 하며, 그렇지 않으면 파일 요청이 404를 반환합니다. 상대 경로는 현재 작업 디렉토리(앱 시작 위치)에서 해석됩니다. 이식성을 위해 절대 경로 또는 path.Join 사용을 고려하세요.
반환값:
- *App: 메서드 체이닝을 위해 App 인스턴스를 반환하여 유창한 API를 가능하게 합니다: app.Static("/static", "./public").Static("/assets", "./assets")
동작 방식:
등록 타이밍: Run() 또는 RunWithGracefulShutdown() 전에 호출되어야 합니다. 서버 시작 후 정적 라우트를 등록하려고 하면 패닉이 발생합니다.
경로 매핑: URL 경로 "/static/css/style.css"는 "{dir}/css/style.css"에 매핑됩니다. 접두사가 제거되고 나머지 경로가 파일을 찾는 데 사용됩니다.
디렉토리 목록: index.html이 없으면 Go의 http.FileServer가 디렉토리 목록을 표시합니다. 디렉토리 탐색을 비활성화하려면 디렉토리에 인덱스 파일이 있는지 확인하거나 커스텀 핸들러를 사용하세요.
파일 조회: 파일은 캐싱 없이 파일시스템에서 직접 제공됩니다. 파일 변경사항은 즉시 반영됩니다(개발에 유용). 프로덕션의 경우 자산을 미리 컴파일하거나 임베디드 파일시스템 사용을 고려하세요.
MIME 타입: Content-Type 헤더는 파일 확장자를 기반으로 mime.TypeByExtension을 사용하여 자동으로 설정됩니다 (예: .css → text/css, .js → application/javascript).
범위 요청: http.FileServer는 HTTP 범위 요청(바이트 범위 다운로드)을 지원하여 재개/일시중지 기능 및 효율적인 비디오 스트리밍을 가능하게 합니다.
ETag: 파일 수정 시간과 크기를 기반으로 자동으로 생성되어 브라우저 캐시 유효성 검사 및 조건부 요청(If-None-Match)을 가능하게 합니다.
오류 처리: 파일이 없으면 404 Not Found 반환, 파일을 읽을 수 없으면 403 Forbidden, 파일시스템 오류 시 500 Internal Server Error 반환.
스레드 안전성:
- 앱 구성 단계(서버 시작 전) 동안 동시 호출에 안전합니다.
- 라우팅 테이블에 대한 동시 수정을 방지하기 위해 뮤텍스 잠금을 획득합니다.
- 서버 실행 후 호출되면 패닉이 발생합니다.
보안 고려사항:
경로 순회: Go의 http.FileServer는 "../" 공격을 방지하기 위해 경로를 자동으로 정리합니다. "/static/../../etc/passwd" 요청은 안전하게 차단됩니다.
숨김 파일: "."로 시작하는 파일(예: .env, .git)은 기본적으로 제공됩니다. 필요한 경우 민감한 파일을 차단하기 위해 커스텀 미들웨어 사용을 고려하세요.
디렉토리 순회: 디렉토리 목록이 비활성화된 경우 공격자가 파일을 열거할 수 없습니다. 디렉토리에 index.html을 추가하거나 커스텀 403 핸들러를 사용하세요.
민감한 파일: 소스 코드, 구성 또는 민감한 데이터가 포함된 디렉토리를 제공하지 마세요. 공개 자산만을 위한 전용 디렉토리를 사용하세요.
성능 고려사항:
내장 캐싱 없음: 파일은 각 요청마다 디스크에서 읽습니다. 트래픽이 많은 사이트의 경우 CDN, nginx 또는 캐싱 미들웨어를 사용하세요.
개발 vs 프로덕션: 애플리케이션에서 정적 파일을 제공하는 것은 개발에는 편리하지만 프로덕션에는 비효율적입니다. 프로덕션에서는 리버스 프록시 또는 CDN을 사용하세요.
압축: http.FileServer는 응답을 압축하지 않습니다. gzip 미들웨어를 추가하거나 압축 기능이 있는 리버스 프록시를 사용하여 더 나은 성능을 얻으세요.
파일 시스템 I/O: 각 요청은 파일시스템 I/O를 수행합니다. 더 나은 성능을 위해 embed.FS를 사용하여 바이너리에 정적 파일을 임베드하는 것을 고려하세요.
일반적인 사용 사례:
- 개발 자산: app.Static("/static", "./static")
- 여러 자산 디렉토리: app.Static("/css", "./css").Static("/js", "./js")
- 공개 업로드: app.Static("/uploads", "./uploads")
- 문서: app.Static("/docs", "./docs/html")
- 단일 페이지 앱: app.Static("/", "./dist") (React, Vue, Angular 빌드용)
예제 - 기본 정적 파일 제공:
// ./public 디렉토리의 파일을 /static/* URL에서 제공
app.Static("/static", "./public")
// URL: /static/css/style.css → 파일: ./public/css/style.css
// URL: /static/js/app.js → 파일: ./public/js/app.js
// URL: /static/images/logo.png → 파일: ./public/images/logo.png
예제 - 여러 정적 디렉토리:
// CSS, JavaScript 및 이미지를 별도의 디렉토리에서 제공
app.Static("/css", "./assets/css")
app.Static("/js", "./assets/js")
app.Static("/images", "./assets/images")
예제 - 절대 경로:
// 프로덕션 배포를 위한 절대 경로 사용
app.Static("/static", "/var/www/myapp/static")
예제 - 임베디드 파일시스템(Go 1.16+):
//go:embed static/*
var staticFiles embed.FS
// 임베디드 파일 제공(더 나은 성능, 단일 바이너리)
app.GET("/static/*", func(w http.ResponseWriter, r *http.Request) {
fs := http.FileServer(http.FS(staticFiles))
fs.ServeHTTP(w, r)
})
예제 - 캐시 헤더가 있는 커스텀 정적 핸들러:
app.GET("/static/*", func(w http.ResponseWriter, r *http.Request) {
// 프로덕션을 위한 공격적인 캐싱 추가
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
// 파일 제공
fs := http.StripPrefix("/static/", http.FileServer(http.Dir("./public")))
fs.ServeHTTP(w, r)
})
모범 사례:
- 정적 자산을 위한 전용 디렉토리를 사용하세요(소스 코드와 섞지 마세요).
- 더 나은 성능을 위해 Cache-Control 헤더를 추가하세요(미들웨어를 통해).
- 콘텐츠 주소 지정 가능 URL 사용을 고려하세요(예: /static/app.abc123.js).
- 미들웨어를 사용하여 민감한 파일(.git, .env, .config)을 차단하세요.
- 프로덕션 정적 파일 제공에 CDN 또는 리버스 프록시를 사용하세요.
- 텍스트 자산(CSS, JS, HTML)에 대해 압축(gzip)을 활성화하세요.
- 자산이 다른 도메인에서 액세스되는 경우 적절한 CORS 헤더를 설정하세요.
대안 접근 방식:
- 임베디드 파일: 더 나은 성능으로 단일 바이너리 배포를 위해 //go:embed 사용.
- CDN: 전역 배포를 위해 자산을 CDN(CloudFront, CloudFlare)에 업로드.
- 리버스 프록시: nginx/Apache가 정적 파일을 제공하고 API 요청을 Go 앱에 프록시.
- 커스텀 핸들러: 고급 캐싱, 변환을 위한 커스텀 http.Handler 작성.
func (*App) TemplateEngine ¶
func (a *App) TemplateEngine() *TemplateEngine
TemplateEngine returns the template engine instance for direct template manipulation. This method provides access to the underlying TemplateEngine for advanced template operations beyond the convenience methods (LoadTemplate, LoadTemplates, etc.).
TemplateEngine is useful when you need: - Direct access to template parsing and execution - Custom template functions before loading templates - Advanced template configuration not exposed by convenience methods - Integration with custom template loaders or pipelines
Purpose:
- Provides read-only access to the TemplateEngine instance
- Enables advanced template manipulation and configuration
- Allows custom template loading strategies
- Supports template introspection and debugging
Returns:
- *TemplateEngine: The template engine instance configured for this App. Returns nil if template engine was not initialized (no TemplateDir option set). Safe to call even when templates are not configured.
Thread-Safety:
- Fully thread-safe with read lock protection.
- Acquires app.mu.RLock() before accessing templates field.
- Multiple goroutines can safely call TemplateEngine() concurrently.
- Returned TemplateEngine instance has its own internal thread-safety.
Template Engine Initialization:
- Template engine is initialized when TemplateDir option is set during New(): app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
- Returns nil if TemplateDir option was not provided
- Check for nil before using returned value: if engine := app.TemplateEngine(); engine != nil { ... }
Common Use Cases:
- Access template engine for custom function registration before loading
- Introspect loaded templates for debugging
- Implement custom template caching strategies
- Integrate with hot-reload or file watching systems
- Template testing and validation
Example - Check Template Engine Availability:
engine := app.TemplateEngine()
if engine == nil {
log.Fatal("Template engine not initialized - set TemplateDir option")
}
Example - Access for Custom Configuration:
app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
// Get engine for advanced operations
engine := app.TemplateEngine()
if engine != nil {
// Perform custom template operations
// ...
}
Example - Template Introspection:
engine := app.TemplateEngine()
if engine != nil {
// Check if specific template is loaded
// Inspect template metadata
// Debug template parsing issues
}
Relationship to Convenience Methods:
- LoadTemplate() calls engine.Load() internally
- LoadTemplates() calls engine.LoadGlob() internally
- ReloadTemplates() calls engine.Clear() + engine.LoadAll() internally
- AddTemplateFunc() calls engine.AddFunc() internally
- AddTemplateFuncs() calls engine.AddFuncs() internally
Best Practices:
- Always check for nil before using returned engine
- Use convenience methods (LoadTemplate, etc.) for common operations
- Use TemplateEngine() for advanced scenarios requiring direct access
- Don't cache the engine reference; call TemplateEngine() when needed
Performance Notes:
- Very lightweight operation (RLock + field access)
- No template loading or parsing occurs
- Safe to call frequently without performance concerns
TemplateEngine은 직접 템플릿 조작을 위한 템플릿 엔진 인스턴스를 반환합니다. 이 메서드는 편의 메서드(LoadTemplate, LoadTemplates 등)를 넘어서는 고급 템플릿 작업을 위해 기본 TemplateEngine에 대한 액세스를 제공합니다.
TemplateEngine은 다음이 필요할 때 유용합니다: - 템플릿 파싱 및 실행에 대한 직접 액세스 - 템플릿 로드 전 커스텀 템플릿 함수 - 편의 메서드로 노출되지 않은 고급 템플릿 구성 - 커스텀 템플릿 로더 또는 파이프라인과의 통합
목적:
- TemplateEngine 인스턴스에 대한 읽기 전용 액세스 제공
- 고급 템플릿 조작 및 구성 활성화
- 커스텀 템플릿 로딩 전략 허용
- 템플릿 검사 및 디버깅 지원
반환값:
- *TemplateEngine: 이 App에 구성된 템플릿 엔진 인스턴스. 템플릿 엔진이 초기화되지 않은 경우(TemplateDir 옵션이 설정되지 않음) nil을 반환합니다. 템플릿이 구성되지 않았을 때도 안전하게 호출할 수 있습니다.
스레드 안전성:
- 읽기 잠금 보호로 완전히 스레드 안전합니다.
- templates 필드에 액세스하기 전에 app.mu.RLock()을 획득합니다.
- 여러 고루틴이 동시에 TemplateEngine()을 안전하게 호출할 수 있습니다.
- 반환된 TemplateEngine 인스턴스는 자체 내부 스레드 안전성을 가지고 있습니다.
템플릿 엔진 초기화:
- New() 중 TemplateDir 옵션이 설정될 때 템플릿 엔진이 초기화됩니다: app := websvrutil.New(websvrutil.WithTemplateDir("./views"))
- TemplateDir 옵션이 제공되지 않은 경우 nil을 반환합니다
- 반환된 값을 사용하기 전에 nil을 확인하세요: if engine := app.TemplateEngine(); engine != nil { ... }
일반적인 사용 사례:
- 로드 전 커스텀 함수 등록을 위한 템플릿 엔진 액세스
- 디버깅을 위한 로드된 템플릿 검사
- 커스텀 템플릿 캐싱 전략 구현
- 핫 리로드 또는 파일 감시 시스템과의 통합
- 템플릿 테스트 및 검증
모범 사례:
- 반환된 엔진을 사용하기 전에 항상 nil을 확인하세요
- 일반적인 작업에는 편의 메서드(LoadTemplate 등)를 사용하세요
- 직접 액세스가 필요한 고급 시나리오에만 TemplateEngine()을 사용하세요
- 엔진 참조를 캐시하지 마세요; 필요할 때 TemplateEngine()을 호출하세요
성능 참고사항:
- 매우 가벼운 작업(RLock + 필드 액세스)
- 템플릿 로딩 또는 파싱이 발생하지 않음
- 성능 문제 없이 자주 호출해도 안전
func (*App) Use ¶
func (a *App) Use(middleware ...MiddlewareFunc) *App
Use adds middleware to the application's middleware chain. Use는 애플리케이션의 미들웨어 체인에 미들웨어를 추가합니다.
Middleware functions are executed in the order they are added. 미들웨어 함수는 추가된 순서대로 실행됩니다.
Example 예제:
app.Use(loggingMiddleware) app.Use(authMiddleware)
type BasicAuthConfig ¶
type BasicAuthConfig struct {
// Username is the required username.
// Username은 필수 사용자 이름입니다.
Username string
// Password is the required password.
// Password는 필수 비밀번호입니다.
Password string
// Realm is the authentication realm.
// Realm은 인증 영역입니다.
// Default: "Restricted"
Realm string
// Validator is a custom validation function.
// Validator는 커스텀 검증 함수입니다.
// If provided, Username and Password are ignored.
// 제공되면 Username과 Password는 무시됩니다.
Validator func(username, password string) bool
}
BasicAuthConfig defines the configuration for BasicAuth middleware. BasicAuthConfig는 BasicAuth 미들웨어의 설정을 정의합니다.
type BodyLimitConfig ¶
type BodyLimitConfig struct {
// MaxBytes is the maximum allowed request body size in bytes
// MaxBytes는 허용되는 최대 요청 본문 크기(바이트)입니다
MaxBytes int64
}
BodyLimitConfig defines the configuration for BodyLimit middleware. BodyLimitConfig는 BodyLimit 미들웨어의 설정을 정의합니다.
type CORSConfig ¶
type CORSConfig struct {
// AllowOrigins defines allowed origins
// AllowOrigins는 허용된 오리진을 정의합니다
AllowOrigins []string
// AllowMethods defines allowed HTTP methods
// AllowMethods는 허용된 HTTP 메서드를 정의합니다
AllowMethods []string
// AllowHeaders defines allowed headers
// AllowHeaders는 허용된 헤더를 정의합니다
AllowHeaders []string
// AllowCredentials indicates whether credentials are allowed
// AllowCredentials는 자격 증명 허용 여부를 나타냅니다
AllowCredentials bool
// MaxAge indicates how long preflight results can be cached
// MaxAge는 프리플라이트 결과를 캐시할 수 있는 시간을 나타냅니다
MaxAge time.Duration
}
CORSConfig defines configuration for CORS middleware. CORSConfig는 CORS 미들웨어의 설정을 정의합니다.
type CSRFConfig ¶
type CSRFConfig struct {
// TokenLength is the length of the CSRF token in bytes (default: 32)
// TokenLength는 CSRF 토큰의 바이트 길이입니다 (기본값: 32)
TokenLength int
// TokenLookup defines where to find the CSRF token
// TokenLookup은 CSRF 토큰을 찾을 위치를 정의합니다
// Format: "<source>:<name>"
// Possible values:
// - "header:<name>" (e.g., "header:X-CSRF-Token")
// - "form:<name>" (e.g., "form:csrf_token")
// - "query:<name>" (e.g., "query:csrf_token")
// Default: "header:X-CSRF-Token"
TokenLookup string
// CookieName is the name of the CSRF cookie (default: "_csrf")
// CookieName은 CSRF 쿠키의 이름입니다 (기본값: "_csrf")
CookieName string
// CookiePath is the path of the CSRF cookie (default: "/")
// CookiePath는 CSRF 쿠키의 경로입니다 (기본값: "/")
CookiePath string
// CookieDomain is the domain of the CSRF cookie
// CookieDomain은 CSRF 쿠키의 도메인입니다
CookieDomain string
// CookieSecure indicates if the cookie should only be sent over HTTPS (default: false)
// CookieSecure는 쿠키가 HTTPS를 통해서만 전송되어야 하는지를 나타냅니다 (기본값: false)
CookieSecure bool
// CookieHTTPOnly indicates if the cookie should be HTTP only (default: true)
// CookieHTTPOnly는 쿠키가 HTTP 전용이어야 하는지를 나타냅니다 (기본값: true)
CookieHTTPOnly bool
// CookieSameSite defines the SameSite cookie attribute (default: SameSiteStrictMode)
// CookieSameSite는 SameSite 쿠키 속성을 정의합니다 (기본값: SameSiteStrictMode)
CookieSameSite http.SameSite
// CookieMaxAge is the max age of the CSRF cookie in seconds (default: 86400 = 24 hours)
// CookieMaxAge는 CSRF 쿠키의 최대 수명(초)입니다 (기본값: 86400 = 24시간)
CookieMaxAge int
// ContextKey is the key used to store the CSRF token in context (default: "csrf_token")
// ContextKey는 컨텍스트에 CSRF 토큰을 저장하는 데 사용되는 키입니다 (기본값: "csrf_token")
ContextKey string
// ErrorHandler is called when CSRF validation fails
// ErrorHandler는 CSRF 검증이 실패할 때 호출됩니다
// If not set, a default error handler will be used
// 설정되지 않으면 기본 에러 핸들러가 사용됩니다
ErrorHandler func(http.ResponseWriter, *http.Request, error)
// Skipper defines a function to skip CSRF validation
// Skipper는 CSRF 검증을 건너뛸 함수를 정의합니다
// Return true to skip validation for the given request
// 주어진 요청에 대한 검증을 건너뛰려면 true 반환
Skipper func(*http.Request) bool
}
CSRFConfig represents the configuration for CSRF protection. CSRFConfig는 CSRF 보호를 위한 설정을 나타냅니다.
func DefaultCSRFConfig ¶
func DefaultCSRFConfig() CSRFConfig
DefaultCSRFConfig returns default CSRF configuration. DefaultCSRFConfig는 기본 CSRF 설정을 반환합니다.
type CompressionConfig ¶
type CompressionConfig struct {
// Level is the gzip compression level.
// Level은 gzip 압축 레벨입니다.
// Valid values: -1 (default), 0 (no compression), 1 (best speed) to 9 (best compression)
// 유효한 값: -1 (기본), 0 (압축 없음), 1 (최고 속도) ~ 9 (최고 압축)
Level int
// MinLength is the minimum response size to compress (in bytes).
// MinLength는 압축할 최소 응답 크기입니다 (바이트).
// Default: 1024 (1KB)
MinLength int
}
CompressionConfig defines the configuration for Compression middleware. CompressionConfig는 Compression 미들웨어의 설정을 정의합니다.
type Context ¶
type Context struct {
// Request is the HTTP request
// Request는 HTTP 요청입니다
Request *http.Request
// ResponseWriter is the HTTP response writer
// ResponseWriter는 HTTP 응답 작성기입니다
ResponseWriter http.ResponseWriter
// contains filtered or unexported fields
}
Context represents the context of a single HTTP request/response cycle. It provides a convenient API for accessing request data, setting response properties, and storing temporary values during request processing.
Context is created automatically by the router for each matched route and is passed to handlers through the request's context.Context. Handlers retrieve Context via GetContext(r) or similar helper functions.
Context provides: - URL path parameters from dynamic routes (e.g., :id, :name) - Query string parameters - Request headers and cookies - Request body parsing (JSON, form data, files) - Response helpers (JSON, HTML, status codes, redirects) - Custom value storage for middleware communication - Reference to parent App instance
Thread-Safety: - Context instances should NOT be shared across goroutines - Each request gets its own Context instance - values map is protected by mutex for custom value storage - params map is read-only after initialization (no mutex needed)
Lifecycle: 1. Router creates Context: NewContext(w, r) 2. Router sets params: ctx.setParams(params) 3. Router stores in request context: r.WithContext(contextWithValue(...)) 4. Handler retrieves Context: ctx := GetContext(r) 5. Handler uses Context methods 6. Context is garbage collected after request completes
Context는 단일 HTTP 요청/응답 사이클의 컨텍스트를 나타냅니다. 요청 데이터 액세스, 응답 속성 설정 및 요청 처리 중 임시 값 저장을 위한 편리한 API를 제공합니다.
Context는 각 일치하는 라우트에 대해 라우터에 의해 자동으로 생성되며 요청의 context.Context를 통해 핸들러에 전달됩니다. 핸들러는 GetContext(r) 또는 유사한 헬퍼 함수를 통해 Context를 검색합니다.
Context는 다음을 제공합니다: - 동적 라우트의 URL 경로 매개변수(예: :id, :name) - 쿼리 문자열 매개변수 - 요청 헤더 및 쿠키 - 요청 본문 파싱(JSON, 폼 데이터, 파일) - 응답 헬퍼(JSON, HTML, 상태 코드, 리디렉션) - 미들웨어 통신을 위한 커스텀 값 저장 - 부모 App 인스턴스에 대한 참조
스레드 안전성: - Context 인스턴스는 고루틴 간에 공유해서는 안 됩니다 - 각 요청은 자체 Context 인스턴스를 받습니다 - values 맵은 커스텀 값 저장을 위해 뮤텍스로 보호됩니다 - params 맵은 초기화 후 읽기 전용입니다(뮤텍스 불필요)
func GetContext ¶
GetContext retrieves the Context from the request's context.Context. GetContext는 요청의 context.Context에서 Context를 검색합니다.
Example 예제:
func handler(w http.ResponseWriter, r *http.Request) {
ctx := websvrutil.GetContext(r)
id := ctx.Param("id")
}
func NewContext ¶
func NewContext(w http.ResponseWriter, r *http.Request) *Context
NewContext creates a new Context instance wrapping an HTTP request and response. This is an internal function called by the router for each matched route. Applications should retrieve Context via GetContext(r), not create manually.
Performance Optimizations: - values map is lazily allocated (nil by default, created on first Set() call) - params map is pre-allocated (empty, filled by router after creation) - No allocations for requests without custom values
Thread-Safety: - Each request gets unique Context instance - Not safe for concurrent access (single request processing only) - values map protected by mutex when accessed
NewContext는 HTTP 요청 및 응답을 래핑하는 새 Context 인스턴스를 생성합니다. 이것은 각 일치하는 라우트에 대해 라우터가 호출하는 내부 함수입니다. 애플리케이션은 수동으로 생성하지 말고 GetContext(r)를 통해 Context를 검색해야 합니다.
성능 최적화: - values 맵은 지연 할당됩니다(기본적으로 nil, 첫 번째 Set() 호출 시 생성) - params 맵은 사전 할당됩니다(비어 있음, 생성 후 라우터에 의해 채워짐) - 커스텀 값이 없는 요청에 대한 할당 없음
func (*Context) AbortWithError ¶
AbortWithError aborts with status code and error message. AbortWithError는 상태 코드와 에러 메시지로 중단합니다.
Example 예제:
ctx.AbortWithError(http.StatusBadRequest, "Invalid input")
func (*Context) AbortWithJSON ¶
AbortWithJSON aborts with status code and JSON error response. AbortWithJSON은 상태 코드와 JSON 에러 응답으로 중단합니다.
Example 예제:
ctx.AbortWithJSON(http.StatusBadRequest, map[string]string{
"error": "Invalid input",
})
func (*Context) AbortWithStatus ¶
AbortWithStatus aborts the request with the specified status code. AbortWithStatus는 지정된 상태 코드로 요청을 중단합니다.
Example 예제:
ctx.AbortWithStatus(http.StatusUnauthorized)
func (*Context) AcceptsHTML ¶
AcceptsHTML checks if the client accepts HTML responses. AcceptsHTML은 클라이언트가 HTML 응답을 수락하는지 확인합니다.
It checks the Accept header for "text/html". Accept 헤더에서 "text/html"을 확인합니다.
Example 예제:
if ctx.AcceptsHTML() {
ctx.HTML(http.StatusOK, "index", data)
}
func (*Context) AcceptsJSON ¶
AcceptsJSON checks if the client accepts JSON responses. AcceptsJSON은 클라이언트가 JSON 응답을 수락하는지 확인합니다.
It checks the Accept header for "application/json". Accept 헤더에서 "application/json"을 확인합니다.
Example 예제:
if ctx.AcceptsJSON() {
ctx.JSON(http.StatusOK, data)
}
func (*Context) AcceptsXML ¶
AcceptsXML checks if the client accepts XML responses. AcceptsXML은 클라이언트가 XML 응답을 수락하는지 확인합니다.
It checks the Accept header for "application/xml" or "text/xml". Accept 헤더에서 "application/xml" 또는 "text/xml"을 확인합니다.
Example 예제:
if ctx.AcceptsXML() {
ctx.XML(http.StatusOK, data)
}
func (*Context) AddHeader ¶
AddHeader adds a header value to the response. AddHeader는 응답에 헤더 값을 추가합니다.
Unlike SetHeader, this appends the value if the header already exists. SetHeader와 달리 헤더가 이미 존재하는 경우 값을 추가합니다.
Example 예제:
ctx.AddHeader("Set-Cookie", "cookie1=value1")
ctx.AddHeader("Set-Cookie", "cookie2=value2")
func (*Context) BadRequest ¶
func (c *Context) BadRequest()
BadRequest sends a 400 Bad Request response. BadRequest는 400 Bad Request 응답을 전송합니다.
Example 예제:
ctx.BadRequest()
func (*Context) Bind ¶
Bind automatically binds the request data based on Content-Type. Bind는 Content-Type에 따라 요청 데이터를 자동으로 바인딩합니다.
It supports JSON (application/json) and form data (application/x-www-form-urlencoded, multipart/form-data). JSON (application/json) 및 폼 데이터 (application/x-www-form-urlencoded, multipart/form-data)를 지원합니다.
Example 예제:
var data RequestData
if err := ctx.Bind(&data); err != nil {
return ctx.Error(400, "Invalid request data")
}
func (*Context) BindForm ¶
BindForm binds the form data to the provided struct. BindForm은 폼 데이터를 제공된 구조체에 바인딩합니다.
The struct should use `form` tags to specify form field names. 구조체는 `form` 태그를 사용하여 폼 필드 이름을 지정해야 합니다.
Example 예제:
type LoginForm struct {
Username string `form:"username"`
Password string `form:"password"`
}
var form LoginForm
if err := ctx.BindForm(&form); err != nil {
return ctx.Error(400, "Invalid form data")
}
func (*Context) BindJSON ¶
BindJSON binds the request body as JSON to the provided struct. BindJSON은 요청 본문을 JSON으로 제공된 구조체에 바인딩합니다.
Body size limit 본문 크기 제한: - Enforces maximum body size from App.options.MaxBodySize - App.options.MaxBodySize에서 최대 본문 크기 강제 적용 - Default: 10 MB (configurable with WithMaxBodySize option) - 기본값: 10 MB (WithMaxBodySize 옵션으로 설정 가능) - Returns error if request body exceeds limit - 요청 본문이 제한을 초과하면 에러 반환
Security considerations 보안 고려사항: - Prevents denial-of-service attacks with large payloads - 대용량 페이로드를 사용한 서비스 거부 공격 방지 - Uses io.LimitReader to enforce limit at read level - io.LimitReader를 사용하여 읽기 수준에서 제한 강제 적용
Example 예제:
var user User
if err := ctx.BindJSON(&user); err != nil {
return ctx.Error(400, "Invalid JSON")
}
Custom limit 커스텀 제한:
app := websvrutil.New(
websvrutil.WithMaxBodySize(5 * 1024 * 1024), // 5 MB
)
Example ¶
ExampleContext_BindJSON demonstrates binding JSON request bodies. ExampleContext_BindJSON은 JSON 요청 본문을 바인딩하는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
app := websvrutil.New(websvrutil.WithTemplateDir(""))
app.POST("/users", func(w http.ResponseWriter, r *http.Request) {
ctx := websvrutil.GetContext(r)
var user User
if err := ctx.BindJSON(&user); err != nil {
ctx.ErrorJSON(400, "Invalid JSON")
return
}
ctx.JSON(201, user)
})
fmt.Println("JSON binding route configured")
}
Output: JSON binding route configured
func (*Context) BindQuery ¶
BindQuery binds the query parameters to the provided struct. BindQuery는 쿼리 매개변수를 제공된 구조체에 바인딩합니다.
The struct should use `form` tags to specify query parameter names. 구조체는 `form` 태그를 사용하여 쿼리 매개변수 이름을 지정해야 합니다.
Example 예제:
type SearchQuery struct {
Q string `form:"q"`
Page int `form:"page"`
}
var query SearchQuery
if err := ctx.BindQuery(&query); err != nil {
return ctx.Error(400, "Invalid query parameters")
}
func (*Context) BindWithValidation ¶
BindWithValidation binds and validates the request data. BindWithValidation은 요청 데이터를 바인딩하고 검증합니다.
Example 예제:
type User struct {
Name string `json:"name" validate:"required,min=3"`
Email string `json:"email" validate:"required,email"`
}
var user User
if err := ctx.BindWithValidation(&user); err != nil {
return ctx.Error(400, err.Error())
}
func (*Context) ClientIP ¶
ClientIP returns the client IP address. ClientIP는 클라이언트 IP 주소를 반환합니다.
Priority order for IP detection IP 감지 우선순위:
X-Forwarded-For header (first IP in comma-separated list)
X-Real-IP header
RemoteAddr (direct connection)
X-Forwarded-For 헤더 (쉼표로 구분된 목록의 첫 번째 IP)
X-Real-IP 헤더
RemoteAddr (직접 연결)
Header details 헤더 세부정보:
X-Forwarded-For: - Standard header set by proxies (nginx, HAProxy, CloudFlare, etc.) - 프록시(nginx, HAProxy, CloudFlare 등)가 설정하는 표준 헤더 - Format: "client, proxy1, proxy2" (comma-separated chain) - 형식: "클라이언트, 프록시1, 프록시2" (쉼표로 구분된 체인) - Returns ONLY the first IP (original client) for security - 보안을 위해 첫 번째 IP(원본 클라이언트)만 반환 - Example: "203.0.113.195, 70.41.3.18" → returns "203.0.113.195" - 예제: "203.0.113.195, 70.41.3.18" → "203.0.113.195" 반환
X-Real-IP: - Non-standard but widely used by nginx reverse proxies - 비표준이지만 nginx 리버스 프록시에서 널리 사용 - Contains single IP address (no chain) - 단일 IP 주소 포함 (체인 없음) - More reliable than X-Forwarded-For when available - 사용 가능한 경우 X-Forwarded-For보다 신뢰성 높음
RemoteAddr: - Direct TCP connection source address - 직접 TCP 연결 소스 주소 - Format: "IP:Port" (e.g., "192.168.1.100:54321") - 형식: "IP:포트" (예: "192.168.1.100:54321") - Returns IP only (strips port number) - IP만 반환 (포트 번호 제거) - Most reliable when no proxies involved - 프록시가 없을 때 가장 신뢰할 수 있음
Security considerations 보안 고려사항: - X-Forwarded-For can be spoofed by malicious clients - X-Forwarded-For는 악의적인 클라이언트가 위조할 수 있음 - Only use first IP to prevent proxy chain manipulation - 프록시 체인 조작을 방지하기 위해 첫 번째 IP만 사용 - For critical security decisions, validate IP against trusted proxy list - 중요한 보안 결정의 경우 신뢰할 수 있는 프록시 목록에 대해 IP 검증 - Consider implementing IP whitelist/blacklist if needed - 필요한 경우 IP 화이트리스트/블랙리스트 구현 고려
Performance 성능: - Time complexity: O(n) where n = length of X-Forwarded-For or RemoteAddr - 시간 복잡도: O(n), n = X-Forwarded-For 또는 RemoteAddr 길이 - Optimized with byte-by-byte comparison (faster than strings.Split) - 바이트 단위 비교로 최적화 (strings.Split보다 빠름) - No memory allocation for string operations - 문자열 작업에 메모리 할당 없음
Example scenarios 시나리오 예제:
Direct connection (no proxy):
RemoteAddr: "203.0.113.195:54321" Returns: "203.0.113.195"
Behind nginx reverse proxy:
X-Real-IP: "203.0.113.195" RemoteAddr: "127.0.0.1:8080" (nginx internal) Returns: "203.0.113.195" (from X-Real-IP)
Behind CloudFlare CDN:
X-Forwarded-For: "203.0.113.195, 104.16.133.229" RemoteAddr: "104.16.133.229:443" (CloudFlare IP) Returns: "203.0.113.195" (first IP from X-Forwarded-For)
Example usage 사용 예제:
ip := ctx.ClientIP()
if ip == "127.0.0.1" {
// Local request
} else {
// Remote request, log for rate limiting
rateLimiter.Check(ip)
}
func (*Context) ContentType ¶
ContentType returns the Content-Type header of the request. ContentType은 요청의 Content-Type 헤더를 반환합니다.
Example 예제:
contentType := ctx.ContentType()
func (*Context) Context ¶
Context returns the request's context.Context. Context는 요청의 context.Context를 반환합니다.
func (*Context) Cookie ¶
Cookie returns the named cookie provided in the request. Cookie는 요청에서 제공된 이름이 지정된 쿠키를 반환합니다.
Example 예제:
cookie, err := ctx.Cookie("session_id")
if err != nil {
// Cookie not found
}
func (*Context) CookieValue ¶
CookieValue retrieves a cookie value by name. CookieValue는 이름으로 쿠키 값을 검색합니다.
It returns the cookie value as a string. If the cookie is not found, it returns an empty string.
쿠키 값을 문자열로 반환합니다. 쿠키를 찾을 수 없으면 빈 문자열을 반환합니다.
Example 예제:
sessionID := ctx.CookieValue("session_id")
func (*Context) DeleteCookie ¶
DeleteCookie deletes a cookie by setting its MaxAge to -1. DeleteCookie는 MaxAge를 -1로 설정하여 쿠키를 삭제합니다.
Example 예제:
ctx.DeleteCookie("session_id", "/")
func (*Context) Error ¶
Error sends an error response with the given status code and message. Error는 주어진 상태 코드와 메시지로 에러 응답을 전송합니다.
This is a convenience method for sending JSON error responses. JSON 에러 응답 전송을 위한 편의 메서드입니다.
Example 예제:
ctx.Error(400, "Invalid input")
func (*Context) ErrorJSON ¶
ErrorJSON sends a standardized JSON error response. ErrorJSON은 표준화된 JSON 에러 응답을 전송합니다.
Example 예제:
ctx.ErrorJSON(http.StatusNotFound, "User not found")
func (*Context) Exists ¶
Exists checks if a key exists in the context. Exists는 컨텍스트에 키가 존재하는지 확인합니다.
func (*Context) File ¶
File sends a file response to the client. File은 클라이언트에게 파일 응답을 전송합니다.
The filepath should be the absolute or relative path to the file. filepath는 파일의 절대 경로 또는 상대 경로여야 합니다.
Example 예제:
ctx.File("./public/index.html")
func (*Context) FileAttachment ¶
FileAttachment sends a file as a downloadable attachment. FileAttachment는 파일을 다운로드 가능한 첨부 파일로 전송합니다.
The filename parameter sets the name shown in the download dialog. filename 매개변수는 다운로드 대화상자에 표시되는 이름을 설정합니다.
Example 예제:
ctx.FileAttachment("./reports/report.pdf", "monthly-report.pdf")
func (*Context) Forbidden ¶
func (c *Context) Forbidden()
Forbidden sends a 403 Forbidden response. Forbidden은 403 Forbidden 응답을 전송합니다.
Example 예제:
ctx.Forbidden()
func (*Context) FormFile ¶
func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
FormFile retrieves the first file for the provided form key. FormFile은 제공된 폼 키에 대한 첫 번째 파일을 가져옵니다.
func (*Context) Get ¶
Get retrieves a value from the context. Get은 컨텍스트에서 값을 검색합니다.
Example 예제:
user, exists := ctx.Get("user")
if exists {
// Use user
}
func (*Context) GetBool ¶
GetBool retrieves a bool value from the context. GetBool은 컨텍스트에서 bool 값을 검색합니다.
func (*Context) GetCookie ¶
GetCookie is a convenience method to get a cookie value. GetCookie는 쿠키 값을 가져오는 편의 메서드입니다.
Example 예제:
value := ctx.GetCookie("session_id")
func (*Context) GetFloat64 ¶
GetFloat64 retrieves a float64 value from the context. GetFloat64는 컨텍스트에서 float64 값을 검색합니다.
func (*Context) GetHeader ¶
GetHeader returns the request header with the given key. GetHeader는 주어진 키의 요청 헤더를 반환합니다.
This is an alias for Header() for consistency. 일관성을 위한 Header()의 별칭입니다.
Example 예제:
userAgent := ctx.GetHeader("User-Agent")
func (*Context) GetHeaders ¶
GetHeaders returns all values for the given header key. GetHeaders는 주어진 헤더 키의 모든 값을 반환합니다.
Example 예제:
acceptEncodings := ctx.GetHeaders("Accept-Encoding")
func (*Context) GetInt ¶
GetInt retrieves an int value from the context. GetInt은 컨텍스트에서 int 값을 검색합니다.
func (*Context) GetInt64 ¶
GetInt64 retrieves an int64 value from the context. GetInt64는 컨텍스트에서 int64 값을 검색합니다.
func (*Context) GetString ¶
GetString retrieves a string value from the context. GetString은 컨텍스트에서 문자열 값을 검색합니다.
func (*Context) GetStringMap ¶
GetStringMap retrieves a map[string]interface{} value from the context. GetStringMap은 컨텍스트에서 map[string]interface{} 값을 검색합니다.
func (*Context) GetStringSlice ¶
GetStringSlice retrieves a []string value from the context. GetStringSlice는 컨텍스트에서 []string 값을 검색합니다.
func (*Context) HTML ¶
HTML sends an HTML response with the given status code and HTML content. HTML은 주어진 상태 코드와 HTML 콘텐츠로 HTML 응답을 전송합니다.
Example 예제:
ctx.HTML(200, "<h1>Hello World</h1>")
func (*Context) HTMLTemplate ¶
HTMLTemplate renders an HTML template with the given data. HTMLTemplate은 주어진 데이터로 HTML 템플릿을 렌더링합니다.
The template is parsed and executed with the provided data. 템플릿은 제공된 데이터로 파싱되고 실행됩니다.
Example 예제:
tmpl := "<h1>Hello {{.Name}}</h1>"
ctx.HTMLTemplate(200, tmpl, map[string]string{"Name": "World"})
func (*Context) Header ¶
Header returns the request header with the given key. Header는 주어진 키의 요청 헤더를 반환합니다.
func (*Context) HeaderExists ¶
HeaderExists checks if a request header exists. HeaderExists는 요청 헤더가 존재하는지 확인합니다.
Example 예제:
if ctx.HeaderExists("Authorization") {
// Process authentication
}
func (*Context) InternalServerError ¶
func (c *Context) InternalServerError()
InternalServerError sends a 500 Internal Server Error response. InternalServerError는 500 Internal Server Error 응답을 전송합니다.
Example 예제:
ctx.InternalServerError()
func (*Context) IsAjax ¶
IsAjax checks if the request is an AJAX request (XMLHttpRequest). IsAjax는 요청이 AJAX 요청(XMLHttpRequest)인지 확인합니다.
It checks for the X-Requested-With header set to "XMLHttpRequest". X-Requested-With 헤더가 "XMLHttpRequest"로 설정되었는지 확인합니다.
Example 예제:
if ctx.IsAjax() {
// Handle AJAX request
}
func (*Context) IsDELETE ¶
IsDELETE checks if the request method is DELETE. IsDELETE는 요청 메서드가 DELETE인지 확인합니다.
Example 예제:
if ctx.IsDELETE() {
// Handle DELETE request
}
func (*Context) IsGET ¶
IsGET checks if the request method is GET. IsGET는 요청 메서드가 GET인지 확인합니다.
Example 예제:
if ctx.IsGET() {
// Handle GET request
}
func (*Context) IsHEAD ¶
IsHEAD checks if the request method is HEAD. IsHEAD는 요청 메서드가 HEAD인지 확인합니다.
Example 예제:
if ctx.IsHEAD() {
// Handle HEAD request
}
func (*Context) IsOPTIONS ¶
IsOPTIONS checks if the request method is OPTIONS. IsOPTIONS는 요청 메서드가 OPTIONS인지 확인합니다.
Example 예제:
if ctx.IsOPTIONS() {
// Handle OPTIONS request
}
func (*Context) IsPATCH ¶
IsPATCH checks if the request method is PATCH. IsPATCH는 요청 메서드가 PATCH인지 확인합니다.
Example 예제:
if ctx.IsPATCH() {
// Handle PATCH request
}
func (*Context) IsPOST ¶
IsPOST checks if the request method is POST. IsPOST는 요청 메서드가 POST인지 확인합니다.
Example 예제:
if ctx.IsPOST() {
// Handle POST request
}
func (*Context) IsPUT ¶
IsPUT checks if the request method is PUT. IsPUT는 요청 메서드가 PUT인지 확인합니다.
Example 예제:
if ctx.IsPUT() {
// Handle PUT request
}
func (*Context) IsWebSocket ¶
IsWebSocket checks if the request is a WebSocket upgrade request. IsWebSocket는 요청이 WebSocket 업그레이드 요청인지 확인합니다.
Example 예제:
if ctx.IsWebSocket() {
// Handle WebSocket upgrade
}
func (*Context) JSON ¶
JSON sends a JSON response with the given status code and data. JSON은 주어진 상태 코드와 데이터로 JSON 응답을 전송합니다.
The data will be marshaled to JSON and sent with Content-Type: application/json. 데이터는 JSON으로 마샬링되어 Content-Type: application/json으로 전송됩니다.
Example 예제:
ctx.JSON(200, map[string]string{"message": "success"})
Example ¶
ExampleContext_JSON demonstrates sending JSON responses. ExampleContext_JSON은 JSON 응답을 전송하는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http/httptest"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
req := httptest.NewRequest("GET", "/", nil)
rec := httptest.NewRecorder()
ctx := websvrutil.NewContext(rec, req)
data := map[string]interface{}{
"message": "success",
"code": 200,
}
ctx.JSON(200, data)
fmt.Println("Content-Type:", rec.Header().Get("Content-Type"))
}
Output: Content-Type: application/json; charset=utf-8
func (*Context) JSONIndent ¶
JSONIndent sends a JSON response with indentation for readability. JSONIndent는 가독성을 위해 들여쓰기가 있는 JSON 응답을 전송합니다.
This is useful for debugging or development. For production, use JSON() instead. 디버깅이나 개발에 유용합니다. 프로덕션에서는 JSON()을 사용하세요.
Example 예제:
ctx.JSONIndent(200, data, "", " ")
func (*Context) JSONPretty ¶
JSONPretty sends a JSON response with pretty-printing (2-space indentation). JSONPretty는 보기 좋게 출력된 JSON 응답을 전송합니다 (2칸 들여쓰기).
This is a convenience wrapper around JSONIndent with default indentation. 기본 들여쓰기가 있는 JSONIndent의 편의 래퍼입니다.
Example 예제:
ctx.JSONPretty(200, data)
func (*Context) MultipartForm ¶
MultipartForm returns the parsed multipart form, including file uploads. MultipartForm은 파일 업로드를 포함한 파싱된 multipart 폼을 반환합니다.
func (*Context) MustGet ¶
MustGet retrieves a value from the context and panics if it doesn't exist. MustGet은 컨텍스트에서 값을 검색하고 존재하지 않으면 패닉합니다.
Example 예제:
user := ctx.MustGet("user").(User)
func (*Context) NoContent ¶
func (c *Context) NoContent()
NoContent sends a 204 No Content response. NoContent는 204 No Content 응답을 전송합니다.
This is commonly used for successful DELETE requests or when no response body is needed. DELETE 요청이 성공했거나 응답 본문이 필요없을 때 일반적으로 사용됩니다.
Example 예제:
ctx.NoContent()
func (*Context) NotFound ¶
func (c *Context) NotFound()
NotFound sends a 404 Not Found response. NotFound는 404 Not Found 응답을 전송합니다.
Example 예제:
ctx.NotFound()
func (*Context) Param ¶
Param returns the value of the URL parameter with the given name. Param은 주어진 이름의 URL 매개변수 값을 반환합니다.
Example 예제:
// Route: /users/:id
// URL: /users/123 id := ctx.Param("id") // Returns "123" "123" 반환
Example ¶
ExampleContext_Param demonstrates retrieving URL parameters. ExampleContext_Param은 URL 매개변수를 가져오는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
app.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
ctx := websvrutil.GetContext(r)
id := ctx.Param("id")
fmt.Printf("User ID: %s\n", id)
})
fmt.Println("Route with parameter configured")
}
Output: Route with parameter configured
func (*Context) Params ¶
Params returns all URL parameters as a map. Params는 모든 URL 매개변수를 맵으로 반환합니다.
func (*Context) Query ¶
Query returns the query string parameter with the given name. Query는 주어진 이름의 쿼리 문자열 매개변수를 반환합니다.
Example 예제:
// URL: /search?q=golang&page=2 q := ctx.Query("q") // Returns "golang" "golang" 반환
page := ctx.Query("page") // Returns "2" "2" 반환
Example ¶
ExampleContext_Query demonstrates retrieving query parameters. ExampleContext_Query는 쿼리 매개변수를 가져오는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http/httptest"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
req := httptest.NewRequest("GET", "/?page=1&limit=10", nil)
rec := httptest.NewRecorder()
ctx := websvrutil.NewContext(rec, req)
page := ctx.Query("page")
limit := ctx.Query("limit")
fmt.Printf("Page: %s, Limit: %s\n", page, limit)
}
Output: Page: 1, Limit: 10
func (*Context) QueryDefault ¶
QueryDefault returns the query string parameter with the given name, or the default value if it doesn't exist. QueryDefault는 주어진 이름의 쿼리 문자열 매개변수를 반환하거나, 존재하지 않으면 기본값을 반환합니다.
func (*Context) Redirect ¶
Redirect sends an HTTP redirect response. Redirect는 HTTP 리다이렉트 응답을 전송합니다.
Common status codes: - 301: Moved Permanently - 302: Found (temporary redirect) - 303: See Other - 307: Temporary Redirect - 308: Permanent Redirect
일반적인 상태 코드: - 301: 영구 이동 - 302: 발견 (임시 리다이렉트) - 303: 다른 것 보기 - 307: 임시 리다이렉트 - 308: 영구 리다이렉트
Example 예제:
ctx.Redirect(302, "/new-url")
func (*Context) Referer ¶
Referer returns the Referer header of the request. Referer는 요청의 Referer 헤더를 반환합니다.
Example 예제:
referer := ctx.Referer()
func (*Context) Render ¶
Render renders a template file with the given data. Render는 주어진 데이터로 템플릿 파일을 렌더링합니다.
The template file is loaded from the template engine. 템플릿 파일은 템플릿 엔진에서 로드됩니다.
Example 예제:
ctx.Render(200, "index.html", map[string]string{"Title": "Home"})
func (*Context) RenderWithLayout ¶
func (c *Context) RenderWithLayout(code int, layoutName, templateName string, data interface{}) error
RenderWithLayout renders a template with a layout. RenderWithLayout는 레이아웃과 함께 템플릿을 렌더링합니다.
Example 예제:
ctx.RenderWithLayout(200, "base.html", "index.html", map[string]string{"Title": "Home"})
func (*Context) SaveUploadedFile ¶
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
SaveUploadedFile saves the uploaded file to the destination path. SaveUploadedFile은 업로드된 파일을 대상 경로에 저장합니다.
func (*Context) Set ¶
Set stores a value in the context. Set은 컨텍스트에 값을 저장합니다.
Example 예제:
ctx.Set("user", user)
ctx.Set("requestID", "12345")
func (*Context) SetCookie ¶
SetCookie adds a Set-Cookie header to the response. SetCookie는 응답에 Set-Cookie 헤더를 추가합니다.
Example 예제:
cookie := &http.Cookie{
Name: "session_id",
Value: "abc123",
Path: "/",
MaxAge: 3600,
HttpOnly: true,
Secure: true,
}
ctx.SetCookie(cookie)
Example ¶
ExampleContext_SetCookie demonstrates setting cookies. ExampleContext_SetCookie는 쿠키를 설정하는 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
req := httptest.NewRequest("GET", "/", nil)
rec := httptest.NewRecorder()
ctx := websvrutil.NewContext(rec, req)
cookie := &http.Cookie{
Name: "session",
Value: "abc123",
Path: "/",
}
ctx.SetCookie(cookie)
fmt.Println("Cookie set successfully")
}
Output: Cookie set successfully
func (*Context) SetCookieAdvanced ¶
func (c *Context) SetCookieAdvanced(opts CookieOptions)
SetCookieAdvanced sets a cookie with advanced options. SetCookieAdvanced는 고급 옵션으로 쿠키를 설정합니다.
This method accepts a CookieOptions struct for full control over cookie attributes. 쿠키 속성을 완전히 제어하기 위해 CookieOptions 구조체를 받습니다.
Example 예제:
ctx.SetCookieAdvanced(CookieOptions{
Name: "session",
Value: "abc123",
MaxAge: 3600,
Path: "/",
HttpOnly: true,
Secure: true,
})
func (*Context) SuccessJSON ¶
SuccessJSON sends a standardized JSON success response. SuccessJSON은 표준화된 JSON 성공 응답을 전송합니다.
Example 예제:
ctx.SuccessJSON(http.StatusOK, "Operation completed", data)
func (*Context) Text ¶
Text sends a plain text response. Text는 일반 텍스트 응답을 전송합니다.
Example 예제:
ctx.Text(200, "Hello World")
func (*Context) Textf ¶
Textf sends a formatted plain text response. Textf는 형식화된 일반 텍스트 응답을 전송합니다.
This uses fmt.Sprintf for formatting. fmt.Sprintf를 사용하여 형식화합니다.
Example 예제:
ctx.Textf(200, "Hello %s", "World")
func (*Context) Unauthorized ¶
func (c *Context) Unauthorized()
Unauthorized sends a 401 Unauthorized response. Unauthorized는 401 Unauthorized 응답을 전송합니다.
Example 예제:
ctx.Unauthorized()
func (*Context) UserAgent ¶
UserAgent returns the User-Agent header of the request. UserAgent는 요청의 User-Agent 헤더를 반환합니다.
Example 예제:
userAgent := ctx.UserAgent()
func (*Context) WithContext ¶
WithContext returns a shallow copy of Context with a new context.Context. The returned Context shares the same mutex and values with the original.
WithContext는 새 context.Context를 가진 Context의 얕은 복사본을 반환합니다. 반환된 Context는 원본과 동일한 뮤텍스와 값을 공유합니다.
func (*Context) WriteString ¶
WriteString writes a string to the response body. WriteString은 응답 본문에 문자열을 씁니다.
type CookieOptions ¶
type CookieOptions struct {
// Name is the cookie name
// Name은 쿠키 이름입니다
Name string
// Value is the cookie value
// Value는 쿠키 값입니다
Value string
// Path is the cookie path (default: "/")
// Path는 쿠키 경로입니다 (기본값: "/")
Path string
// Domain is the cookie domain
// Domain은 쿠키 도메인입니다
Domain string
// MaxAge is the cookie max age in seconds
// MaxAge는 쿠키 최대 수명(초)입니다
// Use 0 for session cookies
// 세션 쿠키의 경우 0 사용
// Use -1 to delete the cookie
// 쿠키를 삭제하려면 -1 사용
MaxAge int
// Secure indicates if cookie should only be sent over HTTPS
// Secure는 쿠키가 HTTPS를 통해서만 전송되어야 하는지를 나타냅니다
Secure bool
// HttpOnly prevents JavaScript access to the cookie
// HttpOnly는 JavaScript의 쿠키 액세스를 방지합니다
HttpOnly bool
// SameSite controls cross-site cookie behavior
// SameSite는 크로스 사이트 쿠키 동작을 제어합니다
SameSite http.SameSite
}
CookieOptions represents options for setting a cookie. CookieOptions는 쿠키 설정을 위한 옵션을 나타냅니다.
type DefaultValidator ¶
type DefaultValidator struct{}
DefaultValidator is the built-in validator. DefaultValidator는 내장 검증자입니다.
func (*DefaultValidator) Validate ¶
func (v *DefaultValidator) Validate(obj interface{}) error
Validate validates a struct using validation tags. Validate는 검증 태그를 사용하여 구조체를 검증합니다.
Supported tags 지원되는 태그: - required: field must not be empty 필드가 비어 있지 않아야 함 - email: field must be a valid email address 유효한 이메일 주소여야 함 - min=<value>: minimum length/value 최소 길이/값 - max=<value>: maximum length/value 최대 길이/값 - len=<value>: exact length 정확한 길이 - eq=<value>: equal to value 값과 같아야 함 - ne=<value>: not equal to value 값과 같지 않아야 함 - gt=<value>: greater than value 값보다 커야 함 - gte=<value>: greater than or equal to value 값보다 크거나 같아야 함 - lt=<value>: less than value 값보다 작아야 함 - lte=<value>: less than or equal to value 값보다 작거나 같아야 함 - oneof=<values>: one of the values (comma-separated) 값 중 하나 (쉼표로 구분) - alpha: alphabetic characters only 알파벳 문자만 - alphanum: alphanumeric characters only 영숫자 문자만 - numeric: numeric characters only 숫자 문자만
Example 예제:
type User struct {
Name string `validate:"required,min=3,max=50"`
Email string `validate:"required,email"`
Age int `validate:"required,gte=18,lte=100"`
Role string `validate:"required,oneof=admin,user,guest"`
}
type Group ¶
type Group struct {
// contains filtered or unexported fields
}
Group represents a route group with a common prefix and middleware. Group은 공통 접두사와 미들웨어를 가진 라우트 그룹을 나타냅니다.
Route groups allow organizing related routes under a common path prefix and applying middleware to all routes in the group. 라우트 그룹을 사용하면 관련 라우트를 공통 경로 접두사 아래로 구성하고 그룹의 모든 라우트에 미들웨어를 적용할 수 있습니다.
Features 기능: - Prefix: All routes in the group share a common path prefix - 접두사: 그룹의 모든 라우트가 공통 경로 접두사 공유 - Middleware: Group-specific middleware applied to all routes - 미들웨어: 모든 라우트에 적용되는 그룹별 미들웨어 - Nesting: Groups can be nested to create hierarchical route structures - 중첩: 그룹을 중첩하여 계층적 라우트 구조 생성 가능
Example 예제:
// Create API v1 group API v1 그룹 생성
v1 := app.Group("/api/v1")
v1.Use(AuthMiddleware())
v1.GET("/users", listUsers)
v1.POST("/users", createUser)
// Create nested admin group 중첩된 admin 그룹 생성
admin := v1.Group("/admin")
admin.Use(AdminMiddleware())
admin.GET("/stats", getStats)
// Results in route: /api/v1/admin/stats
func (*Group) DELETE ¶
func (g *Group) DELETE(pattern string, handler http.HandlerFunc) *Group
DELETE registers a DELETE route in the group. DELETE는 그룹에 DELETE 라우트를 등록합니다.
func (*Group) GET ¶
func (g *Group) GET(pattern string, handler http.HandlerFunc) *Group
GET registers a GET route in the group. GET은 그룹에 GET 라우트를 등록합니다.
func (*Group) Group ¶
Group creates a nested route group with the given prefix. Group은 주어진 접두사로 중첩된 라우트 그룹을 생성합니다.
The new group inherits the parent group's prefix and middleware. 새 그룹은 부모 그룹의 접두사와 미들웨어를 상속합니다.
Parameters 매개변수:
- prefix: Additional path prefix for the nested group
Returns 반환:
- *Group: New nested route group
Example 예제:
api := app.Group("/api")
v1 := api.Group("/v1") // Prefix: /api/v1
admin := v1.Group("/admin") // Prefix: /api/v1/admin
admin.GET("/users", listUsers) // Route: /api/v1/admin/users
func (*Group) HEAD ¶
func (g *Group) HEAD(pattern string, handler http.HandlerFunc) *Group
HEAD registers a HEAD route in the group. HEAD는 그룹에 HEAD 라우트를 등록합니다.
func (*Group) OPTIONS ¶
func (g *Group) OPTIONS(pattern string, handler http.HandlerFunc) *Group
OPTIONS registers an OPTIONS route in the group. OPTIONS는 그룹에 OPTIONS 라우트를 등록합니다.
func (*Group) PATCH ¶
func (g *Group) PATCH(pattern string, handler http.HandlerFunc) *Group
PATCH registers a PATCH route in the group. PATCH는 그룹에 PATCH 라우트를 등록합니다.
func (*Group) POST ¶
func (g *Group) POST(pattern string, handler http.HandlerFunc) *Group
POST registers a POST route in the group. POST는 그룹에 POST 라우트를 등록합니다.
func (*Group) PUT ¶
func (g *Group) PUT(pattern string, handler http.HandlerFunc) *Group
PUT registers a PUT route in the group. PUT은 그룹에 PUT 라우트를 등록합니다.
func (*Group) Use ¶
func (g *Group) Use(middleware ...MiddlewareFunc) *Group
Use adds middleware to the group. Use는 그룹에 미들웨어를 추가합니다.
Middleware is applied to all routes in the group in the order they are added. 미들웨어는 추가된 순서대로 그룹의 모든 라우트에 적용됩니다.
Parameters 매개변수:
- middleware: One or more middleware functions
Returns 반환:
- *Group: The group for method chaining
Example 예제:
api := app.Group("/api")
api.Use(AuthMiddleware(), LoggingMiddleware())
api.GET("/users", listUsers) // Both middleware applied
type LoggerConfig ¶
type LoggerConfig struct {
// LogFunc is called to log each request
// LogFunc은 각 요청을 로깅하기 위해 호출됩니다
LogFunc func(method, path string, status int, duration time.Duration)
}
LoggerConfig defines configuration for Logger middleware. LoggerConfig는 Logger 미들웨어의 설정을 정의합니다.
type MiddlewareFunc ¶
MiddlewareFunc decorates an http.Handler with additional behavior (logging, auth, etc.) while preserving the signature. MiddlewareFunc는 로깅, 인증 같은 부가 동작을 추가하면서 http.Handler 시그니처를 유지하는 래퍼 함수입니다.
func BasicAuth ¶
func BasicAuth(username, password string) MiddlewareFunc
BasicAuth returns a middleware that enforces HTTP Basic Authentication. BasicAuth는 HTTP Basic Authentication을 적용하는 미들웨어를 반환합니다.
Example 예제:
app := websvrutil.New()
app.Use(websvrutil.BasicAuth("admin", "password"))
func BasicAuthWithConfig ¶
func BasicAuthWithConfig(config BasicAuthConfig) MiddlewareFunc
BasicAuthWithConfig returns a BasicAuth middleware with custom configuration. BasicAuthWithConfig는 커스텀 설정으로 BasicAuth 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.BasicAuthWithConfig(websvrutil.BasicAuthConfig{
Validator: func(username, password string) bool {
return username == "admin" && password == "secret"
},
Realm: "Admin Area",
}))
func BodyLimit ¶
func BodyLimit(maxBytes int64) MiddlewareFunc
BodyLimit returns a middleware that limits the maximum request body size. BodyLimit는 최대 요청 본문 크기를 제한하는 미들웨어를 반환합니다.
Default limit is 10MB. 기본 제한은 10MB입니다.
Example 예제:
// Limit request body to 5MB 요청 본문을 5MB로 제한
server.Use(BodyLimit(5 * 1024 * 1024))
func BodyLimitWithConfig ¶
func BodyLimitWithConfig(config BodyLimitConfig) MiddlewareFunc
BodyLimitWithConfig returns a middleware with custom configuration. BodyLimitWithConfig는 사용자 정의 설정으로 미들웨어를 반환합니다.
func CORS ¶
func CORS() MiddlewareFunc
CORS returns a middleware that handles Cross-Origin Resource Sharing. CORS는 Cross-Origin Resource Sharing을 처리하는 미들웨어를 반환합니다.
Example 예제:
app := websvrutil.New() app.Use(websvrutil.CORS())
Example ¶
ExampleCORS demonstrates using the CORS middleware. ExampleCORS는 CORS 미들웨어 사용 방법을 보여줍니다.
package main
import (
"fmt"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
// Add CORS middleware with custom config
// 커스텀 설정으로 CORS 미들웨어 추가
app.Use(websvrutil.CORSWithConfig(websvrutil.CORSConfig{
AllowOrigins: []string{"https://example.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{"Content-Type", "Authorization"},
}))
fmt.Println("CORS middleware configured")
}
Output: CORS middleware configured
func CORSWithConfig ¶
func CORSWithConfig(config CORSConfig) MiddlewareFunc
CORSWithConfig returns a CORS middleware with custom configuration. CORSWithConfig는 커스텀 설정으로 CORS 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.CORSWithConfig(websvrutil.CORSConfig{
AllowOrigins: []string{"https://example.com"},
AllowMethods: []string{"GET", "POST"},
AllowHeaders: []string{"Content-Type"},
AllowCredentials: true,
}))
func CSRF ¶
func CSRF() MiddlewareFunc
CSRF returns a CSRF middleware with default configuration. CSRF는 기본 설정으로 CSRF 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.CSRF())
func CSRFWithConfig ¶
func CSRFWithConfig(config CSRFConfig) MiddlewareFunc
CSRFWithConfig returns a CSRF middleware with custom configuration. CSRFWithConfig는 커스텀 설정으로 CSRF 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.CSRFWithConfig(websvrutil.CSRFConfig{
TokenLength: 32,
CookieName: "_csrf",
CookieSecure: true,
}))
func Compression ¶
func Compression() MiddlewareFunc
Compression returns a middleware that compresses HTTP responses using gzip. Compression은 gzip을 사용하여 HTTP 응답을 압축하는 미들웨어를 반환합니다.
Automatically compresses responses when client supports gzip (Accept-Encoding: gzip). 클라이언트가 gzip을 지원할 때 자동으로 응답을 압축합니다 (Accept-Encoding: gzip).
Example 예제:
app := websvrutil.New() app.Use(websvrutil.Compression())
func CompressionWithConfig ¶
func CompressionWithConfig(config CompressionConfig) MiddlewareFunc
CompressionWithConfig returns a Compression middleware with custom configuration. CompressionWithConfig는 커스텀 설정으로 Compression 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.CompressionWithConfig(websvrutil.CompressionConfig{
Level: gzip.BestCompression,
MinLength: 2048, // 2KB
}))
func HTTPSRedirect ¶
func HTTPSRedirect() MiddlewareFunc
HTTPSRedirect returns a middleware that redirects HTTP requests to HTTPS. HTTPSRedirect는 HTTP 요청을 HTTPS로 리디렉션하는 미들웨어를 반환합니다.
Example 예제:
// Redirect all HTTP to HTTPS 모든 HTTP를 HTTPS로 리디렉션
httpServer.Use(HTTPSRedirect())
func Logger ¶
func Logger() MiddlewareFunc
Logger returns middleware that records method, path, status code, and latency for each HTTP request. Logger는 각 HTTP 요청의 메서드, 경로, 상태 코드, 지연 시간을 기록하는 미들웨어를 제공합니다.
Example / 예제:
app := websvrutil.New() app.Use(websvrutil.Logger())
Example ¶
ExampleLogger demonstrates using the Logger middleware. ExampleLogger는 Logger 미들웨어 사용 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
// Add logger middleware
// 로거 미들웨어 추가
app.Use(websvrutil.Logger())
app.GET("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
fmt.Println("Logger middleware configured")
}
Output: Logger middleware configured
func LoggerWithConfig ¶
func LoggerWithConfig(config LoggerConfig) MiddlewareFunc
LoggerWithConfig returns middleware that applies a user-defined logging function per request. LoggerWithConfig는 요청마다 사용자 정의 로깅 함수를 실행하는 미들웨어를 제공합니다.
Example / 예제:
app.Use(websvrutil.LoggerWithConfig(websvrutil.LoggerConfig{
LogFunc: func(method, path string, status int, duration time.Duration) {
log.Printf("[ACCESS] %s %s %d %v", method, path, status, duration)
},
}))
func RateLimiter ¶
func RateLimiter(requests int, window time.Duration) MiddlewareFunc
RateLimiter returns a middleware that limits the number of requests per client. RateLimiter는 클라이언트당 요청 수를 제한하는 미들웨어를 반환합니다.
Uses a simple token bucket algorithm with IP-based rate limiting. IP 기반 rate limiting과 함께 간단한 토큰 버킷 알고리즘을 사용합니다.
Example 예제:
app := websvrutil.New() app.Use(websvrutil.RateLimiter(100, time.Minute)) // 100 requests per minute
func RateLimiterWithConfig ¶
func RateLimiterWithConfig(config RateLimiterConfig) MiddlewareFunc
RateLimiterWithConfig returns a RateLimiter middleware with custom configuration. RateLimiterWithConfig는 커스텀 설정으로 RateLimiter 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.RateLimiterWithConfig(websvrutil.RateLimiterConfig{
Requests: 50,
Window: 30 * time.Second,
KeyFunc: func(r *http.Request) string {
return r.Header.Get("X-API-Key")
},
}))
func Recovery ¶
func Recovery() MiddlewareFunc
Recovery returns middleware that wraps handlers with panic recovery and sends a safe HTTP 500 response when a panic occurs. Recovery는 핸들러를 패닉 복구 로직으로 감싸 패닉이 발생하면 안전한 HTTP 500 응답을 보냅니다.
Key behaviors: - Wraps handler execution with defer/recover to prevent server crashes. - Logs the panic value together with the captured stack trace for diagnostics. - Sends a generic 500 response so internal details never leak to clients.
주요 동작: - defer/recover로 핸들러 실행을 감싸 서버 전체가 중단되는 것을 방지합니다. - 패닉 값과 캡처된 스택 트레이스를 함께 기록해 진단에 활용합니다. - 내부 정보가 노출되지 않도록 클라이언트에는 일반적인 500 응답만 전달합니다.
Example:
app := websvrutil.New()
app.Use(websvrutil.Recovery())
app.GET("/panic", func(w http.ResponseWriter, r *http.Request) {
var user *User
fmt.Fprintf(w, "Name: %s", user.Name) // triggers panic for demonstration
})
예제:
app := websvrutil.New()
app.Use(websvrutil.Recovery())
app.GET("/panic", func(w http.ResponseWriter, r *http.Request) {
var user *User
fmt.Fprintf(w, "Name: %s", user.Name) // 데모를 위해 패닉을 유도합니다
})
Example ¶
ExampleRecovery demonstrates using the Recovery middleware. ExampleRecovery는 Recovery 미들웨어 사용 방법을 보여줍니다.
package main
import (
"fmt"
"net/http"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
app := websvrutil.New(websvrutil.WithTemplateDir(""))
// Add recovery middleware
// 복구 미들웨어 추가
app.Use(websvrutil.Recovery())
app.GET("/panic", func(w http.ResponseWriter, r *http.Request) {
panic("something went wrong")
})
fmt.Println("Recovery middleware configured")
}
Output: Recovery middleware configured
func RecoveryWithConfig ¶
func RecoveryWithConfig(config RecoveryConfig) MiddlewareFunc
RecoveryWithConfig returns a Recovery middleware with custom configuration. RecoveryWithConfig는 커스텀 설정으로 Recovery 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.RecoveryWithConfig(websvrutil.RecoveryConfig{
PrintStack: true,
LogFunc: func(err interface{}, stack []byte) {
log.Printf("Panic: %v\n%s", err, stack)
},
}))
func Redirect ¶
func Redirect(to string) MiddlewareFunc
Redirect returns a middleware that redirects all requests to the specified URL. Redirect는 모든 요청을 지정된 URL로 리디렉션하는 미들웨어를 반환합니다.
Uses 301 Moved Permanently by default. 기본적으로 301 Moved Permanently를 사용합니다.
Example 예제:
// Redirect all HTTP to HTTPS 모든 HTTP를 HTTPS로 리디렉션
httpServer.Use(Redirect("https://example.com"))
func RedirectWithConfig ¶
func RedirectWithConfig(config RedirectConfig) MiddlewareFunc
RedirectWithConfig returns a middleware with custom configuration. RedirectWithConfig는 사용자 정의 설정으로 미들웨어를 반환합니다.
func RequestID ¶
func RequestID() MiddlewareFunc
RequestID returns a middleware that adds a unique request ID to each request. RequestID는 각 요청에 고유한 요청 ID를 추가하는 미들웨어를 반환합니다.
The request ID is added to the request context and response headers. 요청 ID는 요청 컨텍스트와 응답 헤더에 추가됩니다.
Example 예제:
app := websvrutil.New()
app.Use(websvrutil.RequestID())
app.GET("/", func(w http.ResponseWriter, r *http.Request) {
requestID := r.Context().Value("request_id").(string)
fmt.Fprintf(w, "Request ID: %s", requestID)
})
func RequestIDWithConfig ¶
func RequestIDWithConfig(config RequestIDConfig) MiddlewareFunc
RequestIDWithConfig returns a RequestID middleware with custom configuration. RequestIDWithConfig는 커스텀 설정으로 RequestID 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.RequestIDWithConfig(websvrutil.RequestIDConfig{
Header: "X-Custom-Request-ID",
Generator: func() string {
return uuid.New().String()
},
}))
func SecureHeaders ¶
func SecureHeaders() MiddlewareFunc
SecureHeaders returns a middleware that adds security-related HTTP headers. SecureHeaders는 보안 관련 HTTP 헤더를 추가하는 미들웨어를 반환합니다.
Adds headers like X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, etc. X-Frame-Options, X-Content-Type-Options, X-XSS-Protection 등의 헤더를 추가합니다.
Example 예제:
app := websvrutil.New() app.Use(websvrutil.SecureHeaders())
func SecureHeadersWithConfig ¶
func SecureHeadersWithConfig(config SecureHeadersConfig) MiddlewareFunc
SecureHeadersWithConfig returns a SecureHeaders middleware with custom configuration. SecureHeadersWithConfig는 커스텀 설정으로 SecureHeaders 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.SecureHeadersWithConfig(websvrutil.SecureHeadersConfig{
XFrameOptions: "DENY",
ContentSecurityPolicy: "default-src 'self'",
}))
func Static ¶
func Static(root string) MiddlewareFunc
Static returns a middleware that serves static files from the specified directory. Static는 지정된 디렉토리에서 정적 파일을 제공하는 미들웨어를 반환합니다.
Example 예제:
// Serve static files from "./public" directory "./public" 디렉토리에서 정적 파일 제공
server.Use(Static("./public"))
func StaticWithConfig ¶
func StaticWithConfig(config StaticConfig) MiddlewareFunc
StaticWithConfig returns a middleware with custom configuration. StaticWithConfig는 사용자 정의 설정으로 미들웨어를 반환합니다.
func Timeout ¶
func Timeout(timeout time.Duration) MiddlewareFunc
Timeout returns a middleware that enforces a request timeout. Timeout은 요청 타임아웃을 적용하는 미들웨어를 반환합니다.
If the handler does not complete within the timeout, a 503 Service Unavailable response is sent. 핸들러가 타임아웃 내에 완료되지 않으면 503 Service Unavailable 응답이 전송됩니다.
Example 예제:
app := websvrutil.New() app.Use(websvrutil.Timeout(5 * time.Second))
func TimeoutWithConfig ¶
func TimeoutWithConfig(config TimeoutConfig) MiddlewareFunc
TimeoutWithConfig returns a Timeout middleware with custom configuration. TimeoutWithConfig는 커스텀 설정으로 Timeout 미들웨어를 반환합니다.
Example 예제:
app.Use(websvrutil.TimeoutWithConfig(websvrutil.TimeoutConfig{
Timeout: 10 * time.Second,
Message: "Request timed out",
}))
func WWWRedirect ¶
func WWWRedirect(addWWW bool) MiddlewareFunc
WWWRedirect returns a middleware that redirects non-www to www or vice versa. WWWRedirect는 non-www를 www로 또는 그 반대로 리디렉션하는 미들웨어를 반환합니다.
Example 예제:
// Redirect to www version www 버전으로 리디렉션
server.Use(WWWRedirect(true))
type Option ¶
type Option func(*Options)
Option is a functional option type for configuring App instances. Follows the functional options pattern for clean, extensible API design.
Pattern Benefits: - Backward compatible (new options don't break existing code) - Self-documenting (With* function names describe purpose) - Optional parameters (use only what you need) - Chainable configuration
Usage:
app := websvrutil.New(
WithReadTimeout(5*time.Second),
WithLogger(false),
)
Option은 App 인스턴스 구성을 위한 함수형 옵션 타입입니다. 깔끔하고 확장 가능한 API 디자인을 위한 함수형 옵션 패턴을 따릅니다.
func WithAutoReload ¶
WithAutoReload enables or disables automatic template reloading. Default: false. Enable only in development mode for hot reload.
WithAutoReload은 자동 템플릿 재로드를 활성화하거나 비활성화합니다. 기본값: false. 핫 리로드를 위해 개발 모드에서만 활성화하세요.
func WithIdleTimeout ¶
WithIdleTimeout sets the maximum time to wait for next request when keep-alive is enabled. Default: 60 seconds. Balances connection reuse and resource consumption.
WithIdleTimeout은 keep-alive가 활성화된 경우 다음 요청을 기다리는 최대 시간을 설정합니다. 기본값: 60초. 연결 재사용과 리소스 소비의 균형을 맞춥니다.
func WithLogger ¶
WithLogger enables or disables built-in request logging middleware. Default: true. Logs HTTP method, path, status code, and duration.
WithLogger는 내장 요청 로깅 미들웨어를 활성화하거나 비활성화합니다. 기본값: true. HTTP 메서드, 경로, 상태 코드 및 기간을 로깅합니다.
func WithMaxBodySize ¶
WithMaxBodySize sets the maximum allowed request body size in bytes. This limit applies to JSON, form data, and other request bodies (not file uploads). Default: 10MB. Prevents memory exhaustion from large payloads.
Example:
app := websvrutil.New(
websvrutil.WithMaxBodySize(5 * 1024 * 1024), // 5 MB
)
WithMaxBodySize는 허용되는 최대 요청 본문 크기(바이트)를 설정합니다. 이 제한은 JSON, 폼 데이터 및 기타 요청 본문에 적용됩니다(파일 업로드 제외). 기본값: 10MB. 대용량 페이로드로 인한 메모리 고갈을 방지합니다.
func WithMaxHeaderBytes ¶
WithMaxHeaderBytes sets the maximum bytes server will read parsing request headers. Default: 1MB. Prevents malicious large headers from consuming memory.
WithMaxHeaderBytes는 서버가 요청 헤더를 파싱할 때 읽을 최대 바이트를 설정합니다. 기본값: 1MB. 악의적인 대형 헤더가 메모리를 소비하는 것을 방지합니다.
func WithMaxUploadSize ¶
WithMaxUploadSize sets the maximum allowed file upload size in bytes. Default: 32MB. Prevents excessive memory usage from large file uploads.
WithMaxUploadSize는 허용되는 최대 파일 업로드 크기(바이트)를 설정합니다. 기본값: 32MB. 대용량 파일 업로드로 인한 과도한 메모리 사용을 방지합니다.
func WithReadTimeout ¶
WithReadTimeout sets the maximum duration for reading entire request including body. Default: 15 seconds. Prevents slow clients from holding connections.
WithReadTimeout은 본문을 포함한 전체 요청 읽기의 최대 기간을 설정합니다. 기본값: 15초. 느린 클라이언트가 연결을 보유하는 것을 방지합니다.
func WithRecovery ¶
WithRecovery enables or disables built-in panic recovery middleware. Default: true. Catches panics and returns 500 Internal Server Error.
WithRecovery는 내장 패닉 복구 미들웨어를 활성화하거나 비활성화합니다. 기본값: true. 패닉을 캐치하고 500 Internal Server Error를 반환합니다.
func WithStaticDir ¶
WithStaticDir sets the directory where static files are served from. Default: "static". Used with Static() method for file serving.
WithStaticDir은 정적 파일이 제공되는 디렉토리를 설정합니다. 기본값: "static". 파일 서빙을 위해 Static() 메서드와 함께 사용됩니다.
func WithStaticPrefix ¶
WithStaticPrefix sets the URL prefix for static file routes. Default: "/static". Example: "/static" maps to StaticDir on filesystem.
WithStaticPrefix는 정적 파일 라우트의 URL 접두사를 설정합니다. 기본값: "/static". 예: "/static"은 파일 시스템의 StaticDir에 매핑됩니다.
func WithTemplateDir ¶
WithTemplateDir sets the directory where HTML templates are stored. Default: "templates". Directory is relative to application working directory.
WithTemplateDir은 HTML 템플릿이 저장되는 디렉토리를 설정합니다. 기본값: "templates". 디렉토리는 애플리케이션 작업 디렉토리에 상대적입니다.
func WithWriteTimeout ¶
WithWriteTimeout sets the maximum duration for writing response before timeout. Default: 15 seconds. Prevents slow clients from holding connections.
WithWriteTimeout은 시간 초과 전 응답 쓰기의 최대 기간을 설정합니다. 기본값: 15초. 느린 클라이언트가 연결을 보유하는 것을 방지합니다.
type Options ¶
type Options struct {
// ReadTimeout is the maximum duration for reading the entire request, including the body.
// ReadTimeout은 본문을 포함하여 전체 요청을 읽는 최대 기간입니다.
ReadTimeout time.Duration
// WriteTimeout is the maximum duration before timing out writes of the response.
// WriteTimeout은 응답 쓰기 시간 초과 전 최대 기간입니다.
WriteTimeout time.Duration
// IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled.
// IdleTimeout은 keep-alive가 활성화된 경우 다음 요청을 기다리는 최대 시간입니다.
IdleTimeout time.Duration
// MaxHeaderBytes controls the maximum number of bytes the server will read parsing the request header.
// MaxHeaderBytes는 서버가 요청 헤더를 파싱할 때 읽을 최대 바이트 수를 제어합니다.
MaxHeaderBytes int
// TemplateDir is the directory where HTML templates are stored.
// TemplateDir은 HTML 템플릿이 저장된 디렉토리입니다.
TemplateDir string
// StaticDir is the directory where static files are served from.
// StaticDir은 정적 파일이 제공되는 디렉토리입니다.
StaticDir string
// StaticPrefix is the URL prefix for static files.
// StaticPrefix는 정적 파일의 URL 접두사입니다.
StaticPrefix string
// EnableAutoReload enables automatic template reloading in development mode.
// EnableAutoReload은 개발 모드에서 자동 템플릿 재로드를 활성화합니다.
EnableAutoReload bool
// EnableLogger enables built-in request logging middleware.
// EnableLogger는 내장 요청 로깅 미들웨어를 활성화합니다.
EnableLogger bool
// EnableRecovery enables built-in panic recovery middleware.
// EnableRecovery는 내장 패닉 복구 미들웨어를 활성화합니다.
EnableRecovery bool
// MaxUploadSize is the maximum allowed file upload size in bytes.
// MaxUploadSize는 허용되는 최대 파일 업로드 크기(바이트)입니다.
MaxUploadSize int64
// MaxBodySize is the maximum allowed request body size in bytes (for JSON, etc).
// MaxBodySize는 허용되는 최대 요청 본문 크기(바이트)입니다 (JSON 등).
MaxBodySize int64
}
Options holds all configuration settings for an App instance. This structure uses the functional options pattern for flexible, extensible configuration without breaking API compatibility when adding new options.
Options provides: - Server timeouts (read, write, idle) - HTTP header size limits - Template and static file serving configuration - Development features (auto-reload) - Built-in middleware controls (logging, recovery) - Request size limits (uploads, body)
Usage:
Use With* functions to configure:
app := websvrutil.New(
websvrutil.WithReadTimeout(10*time.Second),
websvrutil.WithTemplateDir("./views"),
websvrutil.WithLogger(true),
)
Default Values:
See defaultOptions() for complete default configuration. All options have sensible production-ready defaults.
Options는 App 인스턴스에 대한 모든 구성 설정을 보유합니다. 이 구조는 새 옵션을 추가할 때 API 호환성을 깨지 않고 유연하고 확장 가능한 구성을 위해 함수형 옵션 패턴을 사용합니다.
Options는 다음을 제공합니다: - 서버 타임아웃(읽기, 쓰기, 유휴) - HTTP 헤더 크기 제한 - 템플릿 및 정적 파일 서빙 구성 - 개발 기능(자동 리로드) - 내장 미들웨어 제어(로깅, 복구) - 요청 크기 제한(업로드, 본문)
type RateLimiterConfig ¶
type RateLimiterConfig struct {
// Requests is the maximum number of requests allowed per window.
// Requests는 윈도우당 허용되는 최대 요청 수입니다.
// Default: 100
Requests int
// Window is the time window for rate limiting.
// Window는 rate limiting을 위한 시간 윈도우입니다.
// Default: 1 minute
Window time.Duration
// KeyFunc is the function to extract the rate limit key from the request.
// KeyFunc는 요청에서 rate limit 키를 추출하는 함수입니다.
// Default: uses client IP address
KeyFunc func(r *http.Request) string
}
RateLimiterConfig defines the configuration for RateLimiter middleware. RateLimiterConfig는 RateLimiter 미들웨어의 설정을 정의합니다.
type RecoveryConfig ¶
type RecoveryConfig struct {
// PrintStack determines whether to print the stack trace
// PrintStack은 스택 트레이스를 출력할지 결정합니다
PrintStack bool
// LogFunc is called when a panic is recovered
// LogFunc은 패닉이 복구될 때 호출됩니다
LogFunc func(err interface{}, stack []byte)
}
RecoveryConfig defines configuration for Recovery middleware. RecoveryConfig는 Recovery 미들웨어의 설정을 정의합니다.
type RedirectConfig ¶
type RedirectConfig struct {
// Code is the HTTP status code for redirect (default: 301 Moved Permanently)
// Code는 리디렉션을 위한 HTTP 상태 코드입니다 (기본값: 301 Moved Permanently)
Code int
// To is the destination URL
// To는 대상 URL입니다
To string
}
RedirectConfig defines the configuration for Redirect middleware. RedirectConfig는 Redirect 미들웨어의 설정을 정의합니다.
type RequestIDConfig ¶
type RequestIDConfig struct {
// Header is the name of the header to store the request ID.
// Header는 요청 ID를 저장할 헤더의 이름입니다.
// Default: "X-Request-ID"
Header string
// Generator is the function to generate request IDs.
// Generator는 요청 ID를 생성하는 함수입니다.
// Default: generateRequestID (random 16-byte hex string)
Generator func() string
}
RequestIDConfig defines the configuration for RequestID middleware. RequestIDConfig는 RequestID 미들웨어의 설정을 정의합니다.
type Route ¶
type Route struct {
// Method is the HTTP method (GET, POST, etc.)
// Method는 HTTP 메서드입니다 (GET, POST 등)
Method string
// Pattern is the URL pattern (e.g., "/users/:id")
// Pattern은 URL 패턴입니다 (예: "/users/:id")
Pattern string
// Handler is the function to call when the route matches
// Handler는 라우트가 일치할 때 호출할 함수입니다
Handler http.HandlerFunc
// contains filtered or unexported fields
}
Route represents a registered HTTP route with pattern matching capabilities. Each Route instance stores the HTTP method, URL pattern, handler function, and pre-parsed pattern segments for efficient request matching.
Route contains: - HTTP method specification (GET, POST, PUT, etc.) - URL pattern with support for literals, parameters, and wildcards - Handler function to execute when route matches - Pre-parsed segments for O(1) pattern compilation and O(n) matching
Pattern Syntax: - Literal segments: "/users/profile" - Must match exactly - Parameter segments: "/users/:id" - Captures value into named parameter - Wildcard segments: "/files/*" - Matches all remaining path components - Mixed patterns: "/api/:version/users/:id" - Multiple parameters allowed
Matching Behavior: - Case-sensitive path matching - Parameter values are extracted and stored in Context - Wildcard captures everything after its position as single parameter "*" - First matching route wins (routes checked in registration order) - Failed matches return nil params and false status
Performance: - Pattern parsing done once at registration (O(m) where m = segments) - Match checking is O(n) where n = number of segments in pattern - No regular expressions used (simple string comparison) - Minimal memory overhead per route
Thread-Safety: - Route instances are read-only after creation - Safe for concurrent request matching - Handler function should be thread-safe
Route는 패턴 매칭 기능을 가진 등록된 HTTP 라우트를 나타냅니다. 각 Route 인스턴스는 HTTP 메서드, URL 패턴, 핸들러 함수, 그리고 효율적인 요청 매칭을 위해 사전 파싱된 패턴 세그먼트를 저장합니다.
Route는 다음을 포함합니다: - HTTP 메서드 사양(GET, POST, PUT 등) - 리터럴, 매개변수, 와일드카드를 지원하는 URL 패턴 - 라우트가 일치할 때 실행할 핸들러 함수 - O(1) 패턴 컴파일 및 O(n) 매칭을 위한 사전 파싱된 세그먼트
패턴 구문: - 리터럴 세그먼트: "/users/profile" - 정확히 일치해야 함 - 매개변수 세그먼트: "/users/:id" - 명명된 매개변수로 값 캡처 - 와일드카드 세그먼트: "/files/*" - 나머지 모든 경로 구성 요소 일치 - 혼합 패턴: "/api/:version/users/:id" - 여러 매개변수 허용
매칭 동작: - 대소문자 구분 경로 매칭 - 매개변수 값은 추출되어 Context에 저장됨 - 와일드카드는 그 위치 이후의 모든 것을 단일 매개변수 "*"로 캡처 - 처음 일치하는 라우트가 승리(등록 순서대로 라우트 확인) - 실패한 매칭은 nil params 및 false 상태 반환
성능: - 등록 시 패턴 파싱이 한 번 수행됨(O(m), m = 세그먼트 수) - 매칭 확인은 O(n), n = 패턴의 세그먼트 수 - 정규 표현식 미사용(단순 문자열 비교) - 라우트당 최소 메모리 오버헤드
스레드 안전성: - Route 인스턴스는 생성 후 읽기 전용 - 동시 요청 매칭에 안전 - 핸들러 함수는 스레드 안전해야 함
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is the HTTP request router that matches incoming requests to registered route handlers. This is the core routing engine used by App to dispatch HTTP requests to appropriate handlers based on HTTP method and URL path patterns with support for parameters and wildcards.
Router provides: - Method-based routing (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD) - URL pattern matching with parameters (:id) and wildcards (*) - Efficient route lookup with O(n) complexity where n = number of routes for method - Thread-safe concurrent route registration and request handling - Custom 404 Not Found handler support - Zero external dependencies (uses only Go standard library)
Routing Features: - Static routes: Exact path matching (e.g., "/users", "/api/v1/status") - Named parameters: Capture path segments (e.g., "/users/:id" → id=123) - Wildcard routes: Match remaining path (e.g., "/files/*" → */docs/file.pdf) - Case-sensitive matching: "/Users" and "/users" are different routes - Automatic parameter extraction and storage in request context - Support for all standard HTTP methods
Thread-Safety: - Fully thread-safe for concurrent use - Read-write mutex protects route registration and lookup - Multiple requests can be handled concurrently (read locks) - Route registration blocks request handling briefly (write locks)
Performance Characteristics: - Route lookup: O(n) where n = number of routes for HTTP method - Pattern parsing: O(m) where m = number of path segments (done once at registration) - Memory: Minimal overhead, stores only registered routes - No route compilation or preprocessing beyond initial pattern parsing - Suitable for applications with hundreds of routes
Router는 등록된 라우트 핸들러에 들어오는 요청을 매칭하는 HTTP 요청 라우터입니다. 이것은 App이 HTTP 메서드와 URL 경로 패턴을 기반으로 HTTP 요청을 적절한 핸들러로 디스패치하는 데 사용하는 핵심 라우팅 엔진이며, 매개변수와 와일드카드를 지원합니다.
Router는 다음을 제공합니다: - 메서드 기반 라우팅(GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD) - 매개변수(:id) 및 와일드카드(*)가 있는 URL 패턴 매칭 - O(n) 복잡도의 효율적인 라우트 조회(n = 메서드의 라우트 수) - 스레드 안전 동시 라우트 등록 및 요청 처리 - 커스텀 404 Not Found 핸들러 지원 - 외부 종속성 없음(Go 표준 라이브러리만 사용)
라우팅 기능: - 정적 라우트: 정확한 경로 매칭(예: "/users", "/api/v1/status") - 명명된 매개변수: 경로 세그먼트 캡처(예: "/users/:id" → id=123) - 와일드카드 라우트: 나머지 경로 일치(예: "/files/*" → */docs/file.pdf) - 대소문자 구분 매칭: "/Users"와 "/users"는 다른 라우트 - 자동 매개변수 추출 및 요청 컨텍스트에 저장 - 모든 표준 HTTP 메서드 지원
스레드 안전성: - 동시 사용에 완전히 스레드 안전 - 읽기-쓰기 뮤텍스가 라우트 등록 및 조회 보호 - 여러 요청을 동시에 처리할 수 있음(읽기 잠금) - 라우트 등록은 요청 처리를 잠깐 차단(쓰기 잠금)
성능 특성: - 라우트 조회: O(n), n = HTTP 메서드의 라우트 수 - 패턴 파싱: O(m), m = 경로 세그먼트 수(등록 시 한 번만 수행) - 메모리: 최소 오버헤드, 등록된 라우트만 저장 - 초기 패턴 파싱 이외에는 라우트 컴파일이나 전처리 없음 - 수백 개의 라우트를 가진 애플리케이션에 적합
func (*Router) DELETE ¶
func (ro *Router) DELETE(pattern string, handler http.HandlerFunc)
DELETE registers a route for HTTP DELETE requests. Convenience method that calls Handle("DELETE", pattern, handler).
DELETE는 HTTP DELETE 요청에 대한 라우트를 등록합니다. Handle("DELETE", pattern, handler)를 호출하는 편의 메서드입니다.
func (*Router) GET ¶
func (ro *Router) GET(pattern string, handler http.HandlerFunc)
GET registers a route for HTTP GET requests. Convenience method that calls Handle("GET", pattern, handler).
GET은 HTTP GET 요청에 대한 라우트를 등록합니다. Handle("GET", pattern, handler)를 호출하는 편의 메서드입니다.
func (*Router) HEAD ¶
func (ro *Router) HEAD(pattern string, handler http.HandlerFunc)
HEAD registers a route for HTTP HEAD requests. Convenience method that calls Handle("HEAD", pattern, handler).
HEAD는 HTTP HEAD 요청에 대한 라우트를 등록합니다. Handle("HEAD", pattern, handler)를 호출하는 편의 메서드입니다.
func (*Router) Handle ¶
func (ro *Router) Handle(method, pattern string, handler http.HandlerFunc)
Handle registers a new HTTP route with the specified method, pattern, and handler function. This is the core route registration method used by all HTTP method shortcuts (GET, POST, etc.). Routes are matched in registration order; first match wins.
Handle is the foundation of the routing system, providing: - Method-specific route registration (GET, POST, PUT, etc.) - Pattern parsing with parameters and wildcards - Handler association for matched routes - Thread-safe route registration for concurrent use
Purpose:
- Registers a route that maps HTTP method + URL pattern to handler
- Parses pattern into segments for efficient matching
- Stores route in method-specific list for fast lookup
- Enables RESTful API design with method-based routing
Parameters:
method: HTTP method string (case-insensitive, converted to uppercase). Standard methods: "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD" Custom methods: Any string is accepted (e.g., "PROPFIND", "LOCK", "UNLOCK") Empty string: Valid but not recommended (matches empty method requests)
pattern: URL pattern string with optional parameters and wildcards. Pattern Syntax:
Literal paths: "/users", "/api/v1/status"
Must match exactly (case-sensitive)
Leading/trailing slashes are trimmed during parsing
Named parameters: "/users/:id", "/posts/:postId/comments/:commentId"
Starts with colon (:)
Matches any single path segment
Captured value accessible via Context.Param(name)
Parameter name follows colon (alphanumeric + underscore recommended)
Wildcards: "/files/*", "/static/*"
Single asterisk (*)
Matches all remaining path segments
Must be last segment (anything after is ignored)
Captured value accessible via Context.Param("*")
Mixed patterns: "/api/:version/users/:id", "/docs/:lang/*"
Combine literals, parameters, and wildcards
Parameters can appear multiple times
Wildcard (if present) must be last
Pattern Examples:
"/users" → matches: "/users" only
"/users/:id" → matches: "/users/123", "/users/abc"
"/users/:id/posts" → matches: "/users/123/posts"
"/files/*" → matches: "/files/a", "/files/a/b", "/files/a/b/c"
"/:lang/docs/*" → matches: "/en/docs/guide", "/fr/docs/api/v1"
handler: http.HandlerFunc to execute when route matches. Handler receives http.ResponseWriter and *http.Request Should write response status, headers, and body Has access to route parameters via Context (extract from request.Context())
Thread-Safety:
- Fully thread-safe with write lock (mu.Lock)
- Blocks concurrent Handle() calls during registration
- Blocks ServeHTTP() route lookup briefly during registration
- Safe to register routes while serving requests (though not recommended)
Registration Behavior:
- Routes are appended to method-specific list
- Registration order determines matching priority
- No duplicate detection; same pattern can be registered multiple times
- Later registrations shadow earlier ones (first match wins)
- Pattern parsing happens once at registration (compiled for fast matching)
Pattern Parsing:
- Pattern is parsed into segments immediately
- Parsing happens once (O(m) where m = number of segments)
- Segments stored in Route for O(n) matching during requests
- No regular expressions used (simple string operations)
Performance:
- Registration: O(m) where m = pattern segment count
- Memory: One Route struct + segments slice per registration
- Lookup: O(n) where n = number of routes for method
- No route compilation or code generation
Common Use Cases:
- RESTful API endpoints: Handle("GET", "/api/users", listUsers)
- Resource-specific actions: Handle("POST", "/users/:id/activate", activate)
- File serving: Handle("GET", "/static/*", serveFiles)
- Custom HTTP methods: Handle("PROPFIND", "/dav/*", webdavPropfind)
- Health checks: Handle("GET", "/health", healthCheck)
Example - Basic Route Registration:
router := newRouter()
router.Handle("GET", "/users", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("User list"))
})
Example - Route with Parameters:
router.Handle("GET", "/users/:id", func(w http.ResponseWriter, r *http.Request) {
ctx := GetContext(r)
id := ctx.Param("id")
fmt.Fprintf(w, "User ID: %s", id)
})
Example - Route with Wildcard:
router.Handle("GET", "/files/*", func(w http.ResponseWriter, r *http.Request) {
ctx := GetContext(r)
filepath := ctx.Param("*")
fmt.Fprintf(w, "File path: %s", filepath)
})
Example - Multiple Parameters:
router.Handle("GET", "/api/:version/users/:id", func(w http.ResponseWriter, r *http.Request) {
ctx := GetContext(r)
version := ctx.Param("version")
id := ctx.Param("id")
fmt.Fprintf(w, "API %s, User %s", version, id)
})
Example - Custom HTTP Method:
router.Handle("PROPFIND", "/dav/*", func(w http.ResponseWriter, r *http.Request) {
// WebDAV PROPFIND implementation
w.WriteHeader(207) // Multi-Status
})
Method Shortcuts:
Instead of calling Handle directly, use method shortcuts:
- router.GET(pattern, handler) → Handle("GET", pattern, handler)
- router.POST(pattern, handler) → Handle("POST", pattern, handler)
- router.PUT(pattern, handler) → Handle("PUT", pattern, handler)
- router.PATCH(pattern, handler) → Handle("PATCH", pattern, handler)
- router.DELETE(pattern, handler) → Handle("DELETE", pattern, handler)
- router.OPTIONS(pattern, handler) → Handle("OPTIONS", pattern, handler)
- router.HEAD(pattern, handler) → Handle("HEAD", pattern, handler)
Best Practices:
- Register routes during application initialization, not per-request
- Use method shortcuts (GET, POST) for standard methods
- Keep patterns simple and RESTful
- Document route parameters in handler comments
- Validate parameter values in handlers
- Use consistent naming for parameters across routes
- Test route matching thoroughly
Limitations:
- No route groups or nested routers
- No automatic parameter validation
- No route naming or URL generation
- No regex pattern support
- Wildcard must be last segment
- No per-route middleware (use App-level middleware)
Handle은 지정된 메서드, 패턴 및 핸들러 함수로 새 HTTP 라우트를 등록합니다. 이것은 모든 HTTP 메서드 단축키(GET, POST 등)에서 사용하는 핵심 라우트 등록 메서드입니다. 라우트는 등록 순서대로 매칭되며, 첫 번째 일치가 승리합니다.
Handle은 라우팅 시스템의 기반이며 다음을 제공합니다: - 메서드별 라우트 등록(GET, POST, PUT 등) - 매개변수 및 와일드카드가 있는 패턴 파싱 - 매칭된 라우트에 대한 핸들러 연결 - 동시 사용을 위한 스레드 안전 라우트 등록
목적:
- HTTP 메서드 + URL 패턴을 핸들러에 매핑하는 라우트 등록
- 효율적인 매칭을 위해 패턴을 세그먼트로 파싱
- 빠른 조회를 위해 메서드별 목록에 라우트 저장
- 메서드 기반 라우팅으로 RESTful API 디자인 활성화
매개변수:
method: HTTP 메서드 문자열(대소문자 구분 없음, 대문자로 변환됨). 표준 메서드: "GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD" 커스텀 메서드: 모든 문자열 허용(예: "PROPFIND", "LOCK", "UNLOCK") 빈 문자열: 유효하지만 권장하지 않음(빈 메서드 요청과 일치)
pattern: 선택적 매개변수 및 와일드카드가 있는 URL 패턴 문자열. 패턴 구문:
리터럴 경로: "/users", "/api/v1/status"
정확히 일치해야 함(대소문자 구분)
파싱 중 앞뒤 슬래시 제거
명명된 매개변수: "/users/:id", "/posts/:postId/comments/:commentId"
콜론(:)으로 시작
모든 단일 경로 세그먼트와 일치
캡처된 값은 Context.Param(name)으로 액세스 가능
매개변수 이름은 콜론 다음에 위치(영숫자 + 밑줄 권장)
와일드카드: "/files/*", "/static/*"
단일 별표(*)
나머지 모든 경로 세그먼트와 일치
마지막 세그먼트여야 함(이후의 모든 것은 무시됨)
캡처된 값은 Context.Param("*")로 액세스 가능
혼합 패턴: "/api/:version/users/:id", "/docs/:lang/*"
리터럴, 매개변수, 와일드카드 결합
매개변수는 여러 번 나타날 수 있음
와일드카드(있는 경우)는 마지막이어야 함
handler: 라우트가 일치할 때 실행할 http.HandlerFunc. 핸들러는 http.ResponseWriter 및 *http.Request를 받음 응답 상태, 헤더 및 본문을 작성해야 함 Context를 통해 라우트 매개변수에 액세스(request.Context()에서 추출)
스레드 안전성:
- 쓰기 잠금(mu.Lock)으로 완전히 스레드 안전
- 등록 중 동시 Handle() 호출 차단
- 등록 중 ServeHTTP() 라우트 조회를 잠깐 차단
- 요청을 서빙하는 동안 라우트를 등록하는 것은 안전하지만 권장하지 않음
등록 동작:
- 라우트는 메서드별 목록에 추가됨
- 등록 순서가 매칭 우선순위 결정
- 중복 감지 없음; 동일한 패턴을 여러 번 등록할 수 있음
- 나중 등록이 이전 등록을 가림(첫 번째 일치가 승리)
- 패턴 파싱은 등록 시 한 번 발생(빠른 매칭을 위해 컴파일)
모범 사례:
- 요청당이 아닌 애플리케이션 초기화 중에 라우트 등록
- 표준 메서드에는 메서드 단축키(GET, POST) 사용
- 패턴을 간단하고 RESTful하게 유지
- 핸들러 주석에 라우트 매개변수 문서화
- 핸들러에서 매개변수 값 검증
- 라우트 전체에서 매개변수에 일관된 이름 사용
- 라우트 매칭을 철저히 테스트
제한사항:
- 라우트 그룹 또는 중첩 라우터 없음
- 자동 매개변수 검증 없음
- 라우트 이름 지정 또는 URL 생성 없음
- 정규식 패턴 지원 없음
- 와일드카드는 마지막 세그먼트여야 함
- 라우트별 미들웨어 없음(App 레벨 미들웨어 사용)
func (*Router) NotFound ¶
func (ro *Router) NotFound(handler http.HandlerFunc)
NotFound sets a custom handler for 404 Not Found responses. This handler is called when no registered route matches the request.
Purpose:
- Customize 404 error response (custom HTML, JSON, logging, etc.)
- Implement branded error pages
- Add telemetry/metrics for 404s
- Provide helpful error messages or suggestions
Default Behavior:
- If NotFound is not called, uses http.NotFound() from standard library
- Default writes "404 page not found\n" with text/plain content type
Thread-Safety:
- Thread-safe with write lock (mu.Lock)
- Safe to call while serving requests
Example - Custom 404 Handler:
router.NotFound(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Custom 404 - Page not found"))
})
Example - JSON 404 Response:
router.NotFound(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{
"error": "not_found",
"message": "The requested resource was not found",
})
})
NotFound는 404 Not Found 응답에 대한 커스텀 핸들러를 설정합니다. 이 핸들러는 등록된 라우트가 요청과 일치하지 않을 때 호출됩니다.
목적:
- 404 오류 응답 커스터마이즈(커스텀 HTML, JSON, 로깅 등)
- 브랜드화된 오류 페이지 구현
- 404에 대한 원격 측정/메트릭 추가
- 유용한 오류 메시지 또는 제안 제공
기본 동작:
- NotFound가 호출되지 않으면 표준 라이브러리의 http.NotFound() 사용
- 기본적으로 text/plain 콘텐츠 타입으로 "404 page not found\n" 작성
스레드 안전성:
- 쓰기 잠금(mu.Lock)으로 스레드 안전
- 요청을 서빙하는 동안 호출해도 안전
func (*Router) OPTIONS ¶
func (ro *Router) OPTIONS(pattern string, handler http.HandlerFunc)
OPTIONS registers a route for HTTP OPTIONS requests. Convenience method that calls Handle("OPTIONS", pattern, handler).
OPTIONS는 HTTP OPTIONS 요청에 대한 라우트를 등록합니다. Handle("OPTIONS", pattern, handler)를 호출하는 편의 메서드입니다.
func (*Router) PATCH ¶
func (ro *Router) PATCH(pattern string, handler http.HandlerFunc)
PATCH registers a route for HTTP PATCH requests. Convenience method that calls Handle("PATCH", pattern, handler).
PATCH는 HTTP PATCH 요청에 대한 라우트를 등록합니다. Handle("PATCH", pattern, handler)를 호출하는 편의 메서드입니다.
func (*Router) POST ¶
func (ro *Router) POST(pattern string, handler http.HandlerFunc)
POST registers a route for HTTP POST requests. Convenience method that calls Handle("POST", pattern, handler).
POST는 HTTP POST 요청에 대한 라우트를 등록합니다. Handle("POST", pattern, handler)를 호출하는 편의 메서드입니다.
func (*Router) PUT ¶
func (ro *Router) PUT(pattern string, handler http.HandlerFunc)
PUT registers a route for HTTP PUT requests. Convenience method that calls Handle("PUT", pattern, handler).
PUT은 HTTP PUT 요청에 대한 라우트를 등록합니다. Handle("PUT", pattern, handler)를 호출하는 편의 메서드입니다.
func (*Router) ServeHTTP ¶
func (ro *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements the http.Handler interface for Router. This is the request processing entry point that matches requests to routes and executes handlers.
ServeHTTP is automatically called by Go's http.Server for each incoming request. It performs route matching, parameter extraction, context creation, and handler execution.
Purpose:
- Implements http.Handler interface (Router can be used as http.Handler)
- Matches incoming requests to registered routes
- Extracts URL parameters from matched routes
- Creates and populates Context with request data and parameters
- Invokes matched route handler or 404 handler
Process Flow:
- Acquire read lock (ro.mu.RLock) for thread-safe route lookup
- Get list of routes registered for request's HTTP method
- Iterate through routes in registration order
- For each route, attempt to match request path: - If match succeeds: Extract parameters, create Context, call handler, return - If match fails: Continue to next route
- If no routes match: Call notFoundHandler (404)
- Release read lock (ro.mu.RUnlock)
Route Matching:
- Routes are checked in registration order (first match wins)
- Matching is case-sensitive
- Parameters and wildcards are extracted into params map
- Match function returns (params, true) on success or (nil, false) on failure
Context Creation:
- NewContext(w, r) creates Context wrapper around request/response
- Parameters from route matching are stored via ctx.setParams(params)
- Context is stored in request's context.Context for handler access
- Handlers retrieve Context via GetContext(r) or similar function
Thread-Safety:
- Uses read lock for concurrent request handling
- Multiple requests can be processed simultaneously
- Route lookup is thread-safe with RWMutex
- Route registration (Handle) briefly blocks ServeHTTP with write lock
Performance:
- Route lookup: O(n) where n = number of routes for HTTP method
- Path matching: O(m) where m = number of segments in pattern
- Typical latency: < 10μs for small route sets
- Scales well up to hundreds of routes per method
Parameters:
- w: http.ResponseWriter for writing response
- r: *http.Request containing request details
Example - Router as http.Handler:
router := newRouter()
router.GET("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
http.ListenAndServe(":8080", router) // Router implements http.Handler
ServeHTTP는 Router에 대한 http.Handler 인터페이스를 구현합니다. 이것은 요청을 라우트와 매칭하고 핸들러를 실행하는 요청 처리 진입점입니다.
ServeHTTP는 각 들어오는 요청에 대해 Go의 http.Server에 의해 자동으로 호출됩니다. 라우트 매칭, 매개변수 추출, 컨텍스트 생성 및 핸들러 실행을 수행합니다.
목적:
- http.Handler 인터페이스 구현(Router를 http.Handler로 사용 가능)
- 들어오는 요청을 등록된 라우트와 매칭
- 매칭된 라우트에서 URL 매개변수 추출
- 요청 데이터 및 매개변수로 Context 생성 및 채우기
- 매칭된 라우트 핸들러 또는 404 핸들러 호출
프로세스 흐름:
- 스레드 안전 라우트 조회를 위해 읽기 잠금(ro.mu.RLock) 획득
- 요청의 HTTP 메서드에 등록된 라우트 목록 가져오기
- 등록 순서대로 라우트 반복
- 각 라우트에 대해 요청 경로와 매칭 시도: - 매칭 성공: 매개변수 추출, Context 생성, 핸들러 호출, 반환 - 매칭 실패: 다음 라우트로 계속
- 라우트가 일치하지 않으면: notFoundHandler 호출(404)
- 읽기 잠금(ro.mu.RUnlock) 해제
라우트 매칭:
- 라우트는 등록 순서대로 확인됨(첫 번째 일치가 승리)
- 매칭은 대소문자 구분
- 매개변수 및 와일드카드는 params 맵으로 추출됨
- 매칭 함수는 성공 시 (params, true) 또는 실패 시 (nil, false) 반환
스레드 안전성:
- 동시 요청 처리를 위해 읽기 잠금 사용
- 여러 요청을 동시에 처리할 수 있음
- RWMutex로 라우트 조회가 스레드 안전
- 라우트 등록(Handle)은 쓰기 잠금으로 ServeHTTP를 잠깐 차단
성능:
- 라우트 조회: O(n), n = HTTP 메서드의 라우트 수
- 경로 매칭: O(m), m = 패턴의 세그먼트 수
- 일반적인 지연 시간: 작은 라우트 세트의 경우 < 10μs
- 메서드당 수백 개의 라우트까지 잘 확장됨
type SecureHeadersConfig ¶
type SecureHeadersConfig struct {
// XFrameOptions provides clickjacking protection.
// XFrameOptions는 클릭재킹 보호를 제공합니다.
// Default: "SAMEORIGIN"
XFrameOptions string
// XContentTypeOptions prevents MIME type sniffing.
// XContentTypeOptions는 MIME 타입 스니핑을 방지합니다.
// Default: "nosniff"
XContentTypeOptions string
// XXSSProtection enables XSS filter.
// XXSSProtection은 XSS 필터를 활성화합니다.
// Default: "1; mode=block"
XXSSProtection string
// ContentSecurityPolicy defines CSP policy.
// ContentSecurityPolicy는 CSP 정책을 정의합니다.
// Default: "" (not set)
ContentSecurityPolicy string
// StrictTransportSecurity enforces HTTPS.
// StrictTransportSecurity는 HTTPS를 강제합니다.
// Default: "max-age=31536000; includeSubDomains"
StrictTransportSecurity string
// ReferrerPolicy controls referrer information.
// ReferrerPolicy는 리퍼러 정보를 제어합니다.
// Default: "strict-origin-when-cross-origin"
ReferrerPolicy string
}
SecureHeadersConfig defines the configuration for SecureHeaders middleware. SecureHeadersConfig는 SecureHeaders 미들웨어의 설정을 정의합니다.
type Session ¶
type Session struct {
ID string
Data map[string]interface{}
CreatedAt time.Time
ExpiresAt time.Time
// contains filtered or unexported fields
}
Session represents a user session with key-value storage. Session은 키-값 저장소가 있는 사용자 세션을 나타냅니다.
func (*Session) Clear ¶
func (sess *Session) Clear()
Clear removes all values from the session. Clear는 세션에서 모든 값을 제거합니다.
func (*Session) GetBool ¶
GetBool retrieves a bool value from the session. GetBool은 세션에서 bool 값을 검색합니다.
type SessionOptions ¶
type SessionOptions struct {
// Name of the session cookie
// 세션 쿠키 이름
CookieName string
// Session expiration time
// 세션 만료 시간
MaxAge time.Duration
// Use secure cookies (HTTPS only)
// 보안 쿠키 사용 (HTTPS만)
Secure bool
// Prevent JavaScript access
// JavaScript 액세스 방지
HttpOnly bool
// SameSite cookie attribute
// SameSite 쿠키 속성
SameSite http.SameSite
// Cleanup interval
// 정리 간격
CleanupTime time.Duration
// Cookie path
// 쿠키 경로
Path string
// Cookie domain
// 쿠키 도메인
Domain string
}
SessionOptions configures the session store. SessionOptions는 세션 저장소를 설정합니다.
func DefaultSessionOptions ¶
func DefaultSessionOptions() SessionOptions
DefaultSessionOptions returns default session options. DefaultSessionOptions는 기본 세션 옵션을 반환합니다.
type SessionStore ¶
type SessionStore struct {
// contains filtered or unexported fields
}
SessionStore manages HTTP sessions with in-memory storage. SessionStore는 인메모리 저장소로 HTTP 세션을 관리합니다.
func NewSessionStore ¶
func NewSessionStore(opts SessionOptions) *SessionStore
NewSessionStore creates a new session store with the given options. NewSessionStore는 주어진 옵션으로 새 세션 저장소를 생성합니다.
Example ¶
ExampleNewSessionStore demonstrates creating a session store. ExampleNewSessionStore는 세션 저장소 생성 방법을 보여줍니다.
package main
import (
"fmt"
"github.com/arkd0ng/go-utils/websvrutil"
)
func main() {
store := websvrutil.NewSessionStore(websvrutil.DefaultSessionOptions())
fmt.Printf("Session store created: %T\n", store)
}
Output: Session store created: *websvrutil.SessionStore
func (*SessionStore) Count ¶
func (s *SessionStore) Count() int
Count returns the number of active sessions. Count는 활성 세션 수를 반환합니다.
func (*SessionStore) Destroy ¶
func (s *SessionStore) Destroy(w http.ResponseWriter, r *http.Request) error
Destroy removes the session and clears the cookie. Destroy는 세션을 제거하고 쿠키를 지웁니다.
func (*SessionStore) Get ¶
func (s *SessionStore) Get(r *http.Request) (*Session, error)
Get retrieves or creates a session for the request. Get은 요청에 대한 세션을 검색하거나 생성합니다.
func (*SessionStore) New ¶
func (s *SessionStore) New() *Session
New creates a new session with a unique ID. New는 고유 ID로 새 세션을 생성합니다.
func (*SessionStore) Save ¶
func (s *SessionStore) Save(w http.ResponseWriter, session *Session)
Save saves the session and sets the cookie. Save는 세션을 저장하고 쿠키를 설정합니다.
type StaticConfig ¶
type StaticConfig struct {
// Root is the root directory to serve files from
// Root는 파일을 제공할 루트 디렉토리입니다
Root string
// Index is the index file to serve (default: "index.html")
// Index는 제공할 인덱스 파일입니다 (기본값: "index.html")
Index string
// Browse enables directory browsing
// Browse는 디렉토리 탐색을 활성화합니다
Browse bool
}
StaticConfig defines the configuration for Static middleware. StaticConfig는 Static 미들웨어의 설정을 정의합니다.
type TemplateEngine ¶
type TemplateEngine struct {
// contains filtered or unexported fields
}
TemplateEngine manages HTML template rendering. TemplateEngine는 HTML 템플릿 렌더링을 관리합니다.
func NewTemplateEngine ¶
func NewTemplateEngine(dir string) *TemplateEngine
NewTemplateEngine creates a new template engine. NewTemplateEngine은 새로운 템플릿 엔진을 생성합니다.
Example 예제:
engine := NewTemplateEngine("views")
func (*TemplateEngine) AddFunc ¶
func (e *TemplateEngine) AddFunc(name string, fn interface{})
AddFunc adds a custom template function. AddFunc는 커스텀 템플릿 함수를 추가합니다.
Example 예제:
engine.AddFunc("upper", strings.ToUpper)
func (*TemplateEngine) AddFuncs ¶
func (e *TemplateEngine) AddFuncs(funcs template.FuncMap)
AddFuncs adds multiple custom template functions. AddFuncs는 여러 커스텀 템플릿 함수를 추가합니다.
Example 예제:
engine.AddFuncs(template.FuncMap{
"upper": strings.ToUpper,
"lower": strings.ToLower,
})
func (*TemplateEngine) Clear ¶
func (e *TemplateEngine) Clear()
Clear removes all loaded templates. Clear는 모든 로드된 템플릿을 제거합니다.
func (*TemplateEngine) DisableAutoReload ¶
func (e *TemplateEngine) DisableAutoReload()
DisableAutoReload disables automatic template reloading. DisableAutoReload은 자동 템플릿 재로드를 비활성화합니다.
func (*TemplateEngine) EnableAutoReload ¶
func (e *TemplateEngine) EnableAutoReload() error
EnableAutoReload enables automatic template reloading when files change. EnableAutoReload은 파일이 변경될 때 자동 템플릿 재로드를 활성화합니다.
This feature is useful during development. It watches the template directory and automatically reloads templates when they are modified. 이 기능은 개발 중에 유용합니다. 템플릿 디렉토리를 감시하고 수정되면 자동으로 템플릿을 다시 로드합니다.
Example 예제:
engine.EnableAutoReload()
func (*TemplateEngine) Has ¶
func (e *TemplateEngine) Has(name string) bool
Has checks if a template exists. Has는 템플릿이 존재하는지 확인합니다.
func (*TemplateEngine) HasLayout ¶
func (e *TemplateEngine) HasLayout(name string) bool
HasLayout checks if a layout exists. HasLayout는 레이아웃이 존재하는지 확인합니다.
func (*TemplateEngine) IsAutoReloadEnabled ¶
func (e *TemplateEngine) IsAutoReloadEnabled() bool
IsAutoReloadEnabled returns whether auto-reload is enabled. IsAutoReloadEnabled는 자동 재로드가 활성화되어 있는지 반환합니다.
func (*TemplateEngine) List ¶
func (e *TemplateEngine) List() []string
List returns all loaded template names. List는 모든 로드된 템플릿 이름을 반환합니다.
func (*TemplateEngine) ListLayouts ¶
func (e *TemplateEngine) ListLayouts() []string
ListLayouts returns all loaded layout names. ListLayouts는 모든 로드된 레이아웃 이름을 반환합니다.
func (*TemplateEngine) Load ¶
func (e *TemplateEngine) Load(name string) error
Load loads a single template file. Load는 단일 템플릿 파일을 로드합니다.
Example 예제:
err := engine.Load("index.html")
func (*TemplateEngine) LoadAll ¶
func (e *TemplateEngine) LoadAll() error
LoadAll loads all templates from the directory recursively. LoadAll은 디렉토리에서 모든 템플릿을 재귀적으로 로드합니다.
Example 예제:
err := engine.LoadAll()
func (*TemplateEngine) LoadAllLayouts ¶
func (e *TemplateEngine) LoadAllLayouts() error
LoadAllLayouts loads all layout templates from the layout directory. LoadAllLayouts는 레이아웃 디렉토리에서 모든 레이아웃 템플릿을 로드합니다.
Example 예제:
err := engine.LoadAllLayouts()
func (*TemplateEngine) LoadGlob ¶
func (e *TemplateEngine) LoadGlob(pattern string) error
LoadGlob loads all templates matching the pattern. LoadGlob는 패턴과 일치하는 모든 템플릿을 로드합니다.
Example 예제:
err := engine.LoadGlob("*.html")
func (*TemplateEngine) LoadLayout ¶
func (e *TemplateEngine) LoadLayout(name string) error
LoadLayout loads a layout template. LoadLayout는 레이아웃 템플릿을 로드합니다.
Example 예제:
err := engine.LoadLayout("base.html")
func (*TemplateEngine) Render ¶
func (e *TemplateEngine) Render(w io.Writer, name string, data interface{}) error
Render renders a template with data to the writer. Render는 템플릿을 데이터와 함께 writer에 렌더링합니다.
Example 예제:
err := engine.Render(w, "index.html", data)
func (*TemplateEngine) RenderWithLayout ¶
func (e *TemplateEngine) RenderWithLayout(w io.Writer, layoutName, templateName string, data interface{}) error
RenderWithLayout renders a template with a layout. RenderWithLayout는 레이아웃과 함께 템플릿을 렌더링합니다.
Example 예제:
err := engine.RenderWithLayout(w, "base.html", "index.html", data)
func (*TemplateEngine) SetDelimiters ¶
func (e *TemplateEngine) SetDelimiters(left, right string)
SetDelimiters sets custom template delimiters. SetDelimiters는 커스텀 템플릿 구분자를 설정합니다.
Example 예제:
engine.SetDelimiters("[[", "]]")
func (*TemplateEngine) SetLayoutDir ¶
func (e *TemplateEngine) SetLayoutDir(dir string)
SetLayoutDir sets the layout templates directory. SetLayoutDir는 레이아웃 템플릿 디렉토리를 설정합니다.
Example 예제:
engine.SetLayoutDir("views/layouts")
type TimeoutConfig ¶
type TimeoutConfig struct {
// Timeout is the maximum duration for the request.
// Timeout은 요청의 최대 지속 시간입니다.
// Default: 30 seconds
Timeout time.Duration
// Message is the error message sent on timeout.
// Message는 타임아웃 시 전송되는 에러 메시지입니다.
// Default: "Service Unavailable"
Message string
}
TimeoutConfig defines the configuration for Timeout middleware. TimeoutConfig는 Timeout 미들웨어의 설정을 정의합니다.
type ValidationError ¶
ValidationError represents a validation error. ValidationError는 검증 에러를 나타냅니다.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
Error returns the error message. Error는 에러 메시지를 반환합니다.
type ValidationErrors ¶
type ValidationErrors []*ValidationError
ValidationErrors represents multiple validation errors. ValidationErrors는 여러 검증 에러를 나타냅니다.
func (ValidationErrors) Error ¶
func (e ValidationErrors) Error() string
Error returns the concatenated error messages. Error는 연결된 에러 메시지를 반환합니다.