websvrutil

package
v0.0.0-...-14a125a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2025 License: MIT Imports: 29 Imported by: 0

README

websvrutil - Web Server Utilities / 웹 서버 유틸리티

Version / 버전: v1.11.023 Package / 패키지: github.com/arkd0ng/go-utils/websvrutil

Overview / 개요

The websvrutil package provides extreme simplicity web server utilities for Golang. It reduces 50+ lines of typical web server setup code to just 5 lines, prioritizing developer convenience over raw performance.

websvrutil 패키지는 Golang을 위한 극도로 간단한 웹 서버 유틸리티를 제공합니다. 일반적인 웹 서버 설정 코드 50줄 이상을 단 5줄로 줄여주며, 순수 성능보다 개발자 편의성을 우선시합니다.

Design Philosophy / 설계 철학

  • Developer Convenience First / 개발자 편의성 우선: 50+ lines → 5 lines
  • Smart Defaults / 스마트 기본값: Zero configuration for 99% of use cases / 99% 사용 사례에 대한 제로 설정
  • Standard Library Compatible / 표준 라이브러리 호환: Built on net/http, no magic / net/http 기반, 마법 없음
  • Easy Middleware Chaining / 쉬운 미들웨어 체이닝: Simple and intuitive / 간단하고 직관적
  • Auto Template Discovery / 자동 템플릿 발견: Smart template loading and hot reload / 스마트 템플릿 로딩 및 핫 리로드

Installation / 설치

go get github.com/arkd0ng/go-utils/websvrutil

Current Features (v1.11.023) / 현재 기능

App Struct / App 구조체

The main application instance that manages your web server.

웹 서버를 관리하는 주요 애플리케이션 인스턴스입니다.

Methods / 메서드:

  • New(opts ...Option) *App - Create new app instance / 새 앱 인스턴스 생성
  • Use(middleware ...MiddlewareFunc) *App - Add middleware / 미들웨어 추가
  • GET/POST/PUT/PATCH/DELETE/OPTIONS/HEAD(pattern, handler) - Register routes / 라우트 등록
  • NotFound(handler) - Custom 404 handler / 커스텀 404 핸들러
  • Run(addr string) error - Start server / 서버 시작
  • Shutdown(ctx context.Context) error - Graceful shutdown / 정상 종료
  • RunWithGracefulShutdown(addr string, timeout time.Duration) error - Run with signal handling / 시그널 처리와 함께 실행
  • ServeHTTP(w http.ResponseWriter, r *http.Request) - Implement http.Handler / http.Handler 구현
Router / 라우터

Fast HTTP request router with parameter and wildcard support.

매개변수 및 와일드카드 지원을 갖춘 빠른 HTTP 요청 라우터.

Features / 기능:

  • HTTP method routing (GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD) / HTTP 메서드 라우팅
  • Path parameters (:id, :name) / 경로 매개변수
  • Wildcard routes (*) / 와일드카드 라우트
  • Custom 404 handler / 커스텀 404 핸들러
  • Thread-safe / 스레드 안전

Pattern Syntax / 패턴 구문:

  • /users - Exact match / 정확한 일치
  • /users/:id - Parameter (e.g., /users/123) / 매개변수
  • /files/* - Wildcard (matches everything) / 와일드카드 (모든 것과 일치)
Context / 컨텍스트

Request context for accessing path parameters, query strings, headers, and storing custom values.

경로 매개변수, 쿼리 문자열, 헤더에 액세스하고 커스텀 값을 저장하기 위한 요청 컨텍스트.

Parameter Access / 매개변수 액세스:

  • Param(name string) string - Get path parameter / 경로 매개변수 가져오기
  • Params() map[string]string - Get all parameters / 모든 매개변수 가져오기

Context Storage / 컨텍스트 저장소 (Enhanced in v1.11.017+):

  • Set(key string, value interface{}) - Store value / 값 저장
  • Get(key string) (interface{}, bool) - Retrieve value / 값 검색
  • MustGet(key string) interface{} - Get or panic / 가져오거나 패닉
  • GetString(key string) string - Get string value / 문자열 값 가져오기
  • GetInt(key string) int - Get int value / int 값 가져오기
  • GetInt64(key string) int64 - Get int64 value / int64 값 가져오기
  • GetFloat64(key string) float64 - Get float64 value / float64 값 가져오기
  • GetBool(key string) bool - Get bool value / bool 값 가져오기
  • GetStringSlice(key string) []string - Get string slice / 문자열 슬라이스 가져오기
  • GetStringMap(key string) map[string]interface{} - Get string map / 문자열 맵 가져오기
  • Exists(key string) bool - Check if key exists / 키 존재 확인
  • Delete(key string) - Remove value / 값 제거
  • Keys() []string - Get all keys / 모든 키 가져오기

Request Helpers / 요청 헬퍼:

  • Query(key string) string - Get query parameter / 쿼리 매개변수 가져오기
  • QueryDefault(key, defaultValue string) string - Get with default / 기본값으로 가져오기
  • Header(key string) string - Get request header / 요청 헤더 가져오기
  • Method() string - Get HTTP method / HTTP 메서드 가져오기
  • Path() string - Get URL path / URL 경로 가져오기
  • Context() context.Context - Get request context / 요청 컨텍스트 가져오기
  • WithContext(ctx context.Context) *Context - Replace context / 컨텍스트 교체

Basic Response / 기본 응답:

  • SetHeader(key, value string) - Set response header / 응답 헤더 설정
  • Status(code int) - Set status code / 상태 코드 설정
  • Write(data []byte) (int, error) - Write response / 응답 작성
  • WriteString(s string) (int, error) - Write string / 문자열 작성

JSON Response / JSON 응답:

  • JSON(code int, data interface{}) error - Send JSON response / JSON 응답 전송
  • JSONPretty(code int, data interface{}) error - Send pretty JSON / 보기 좋은 JSON 전송
  • JSONIndent(code int, data, prefix, indent string) error - Custom indent JSON / 커스텀 들여쓰기 JSON
  • Error(code int, message string) error - Send JSON error / JSON 에러 전송

HTML Response / HTML 응답:

  • HTML(code int, html string) error - Send HTML response / HTML 응답 전송
  • HTMLTemplate(code int, tmpl string, data interface{}) error - Render template / 템플릿 렌더링

Text Response / 텍스트 응답:

  • Text(code int, text string) error - Send plain text / 일반 텍스트 전송
  • Textf(code int, format string, args ...interface{}) error - Send formatted text / 형식화된 텍스트 전송

Other Responses / 기타 응답:

  • XML(code int, xml string) error - Send XML response / XML 응답 전송
  • Redirect(code int, url string) - HTTP redirect / HTTP 리다이렉트
  • NoContent() - Send 204 No Content / 204 No Content 전송

Request Binding / 요청 바인딩 (v1.11.013+):

  • Bind(obj interface{}) error - Auto bind based on Content-Type / Content-Type에 따라 자동 바인딩
  • BindJSON(obj interface{}) error - Bind JSON request body / JSON 요청 본문 바인딩
  • BindForm(obj interface{}) error - Bind form data / 폼 데이터 바인딩
  • BindQuery(obj interface{}) error - Bind query parameters / 쿼리 매개변수 바인딩

Cookie Helpers / 쿠키 헬퍼 (v1.11.014+, Enhanced in v1.11.020):

  • Cookie(name string) (*http.Cookie, error) - Get cookie by name / 이름으로 쿠키 가져오기
  • CookieValue(name string) string - Get cookie value (returns empty string if not found) / 쿠키 값 가져오기 (없으면 빈 문자열)
  • SetCookie(cookie *http.Cookie) - Set response cookie / 응답 쿠키 설정
  • SetCookieAdvanced(opts CookieOptions) - Set cookie with advanced options / 고급 옵션으로 쿠키 설정
  • DeleteCookie(name, path string) - Delete cookie / 쿠키 삭제
  • GetCookie(name string) string - Get cookie value / 쿠키 값 가져오기

HTTP Method Helpers / HTTP 메서드 헬퍼 (v1.11.023+):

  • IsGET() bool - Check if request method is GET / GET 요청인지 확인
  • IsPOST() bool - Check if request method is POST / POST 요청인지 확인
  • IsPUT() bool - Check if request method is PUT / PUT 요청인지 확인
  • IsPATCH() bool - Check if request method is PATCH / PATCH 요청인지 확인
  • IsDELETE() bool - Check if request method is DELETE / DELETE 요청인지 확인
  • IsHEAD() bool - Check if request method is HEAD / HEAD 요청인지 확인
  • IsOPTIONS() bool - Check if request method is OPTIONS / OPTIONS 요청인지 확인
  • IsAjax() bool - Check if request is AJAX (XMLHttpRequest) / AJAX 요청인지 확인
  • IsWebSocket() bool - Check if request is WebSocket upgrade / WebSocket 업그레이드 요청인지 확인
  • AcceptsJSON() bool - Check if client accepts JSON responses / JSON 응답 수락 확인
  • AcceptsHTML() bool - Check if client accepts HTML responses / HTML 응답 수락 확인
  • AcceptsXML() bool - Check if client accepts XML responses / XML 응답 수락 확인

Header Helpers / 헤더 헬퍼 (v1.11.014+):

  • AddHeader(key, value string) - Add response header / 응답 헤더 추가
  • GetHeader(key string) string - Get request header / 요청 헤더 가져오기
  • GetHeaders(key string) []string - Get all header values / 모든 헤더 값 가져오기
  • HeaderExists(key string) bool - Check if header exists / 헤더 존재 확인
  • ContentType() string - Get Content-Type header / Content-Type 헤더 가져오기
  • UserAgent() string - Get User-Agent header / User-Agent 헤더 가져오기
  • Referer() string - Get Referer header / Referer 헤더 가져오기
  • ClientIP() string - Get client IP address / 클라이언트 IP 주소 가져오기
Error Response Helpers / 에러 응답 헬퍼

The Context provides convenient methods for sending error responses: Context는 에러 응답 전송을 위한 편리한 메서드를 제공합니다:

// Abort with status code / 상태 코드로 중단
ctx.AbortWithStatus(http.StatusNotFound)
ctx.AbortWithError(http.StatusBadRequest, errors.New("invalid input"))
ctx.AbortWithJSON(http.StatusUnauthorized, map[string]string{"error": "unauthorized"})

// Standardized JSON responses / 표준화된 JSON 응답
ctx.ErrorJSON(400, "Invalid request")
// {"error":"Invalid request","status":400,"success":false}

ctx.SuccessJSON(200, "Operation successful", map[string]interface{}{"id": 123})
// {"message":"Operation successful","data":{"id":123},"status":200,"success":true}

// Common HTTP error shortcuts / 일반적인 HTTP 에러 단축
ctx.NotFound()              // 404
ctx.Unauthorized()          // 401
ctx.Forbidden()             // 403
ctx.BadRequest()            // 400
ctx.InternalServerError()   // 500

File Upload / 파일 업로드 (v1.11.015+):

  • FormFile(name string) (*multipart.FileHeader, error) - Get uploaded file / 업로드된 파일 가져오기
  • MultipartForm() (*multipart.Form, error) - Get parsed multipart form / 파싱된 multipart 폼 가져오기
  • SaveUploadedFile(file *multipart.FileHeader, dst string) error - Save uploaded file / 업로드된 파일 저장

Static File Serving / 정적 파일 서빙 (v1.11.016+):

  • File(filepath string) error - Send file response to client / 클라이언트에게 파일 응답 전송
  • FileAttachment(filepath, filename string) error - Send file as downloadable attachment / 다운로드 가능한 첨부 파일로 전송
  • Static(prefix, dir string) *App (App method) - Serve static files from directory / 디렉토리에서 정적 파일 제공

Helper Function / 헬퍼 함수:

  • GetContext(r *http.Request) *Context - Get Context from request / 요청에서 Context 가져오기

Thread-safe / 스레드 안전: All Context operations are protected by sync.RWMutex / 모든 Context 작업은 sync.RWMutex로 보호됩니다

Options Pattern / 옵션 패턴

Flexible configuration using functional options.

함수형 옵션을 사용한 유연한 설정.

Available Options / 사용 가능한 옵션:

Option / 옵션 Default / 기본값 Description / 설명
WithReadTimeout(d time.Duration) 15s Server read timeout / 서버 읽기 시간 초과
WithWriteTimeout(d time.Duration) 15s Server write timeout / 서버 쓰기 시간 초과
WithIdleTimeout(d time.Duration) 60s Server idle timeout / 서버 유휴 시간 초과
WithMaxHeaderBytes(n int) 1 MB Maximum header size / 최대 헤더 크기
WithTemplateDir(dir string) "templates" Template directory / 템플릿 디렉토리
WithStaticDir(dir string) "static" Static files directory / 정적 파일 디렉토리
WithStaticPrefix(prefix string) "/static" Static files URL prefix / 정적 파일 URL 접두사
WithAutoReload(enable bool) false Auto template reload / 자동 템플릿 재로드
WithLogger(enable bool) true Enable logger middleware / 로거 미들웨어 활성화
WithRecovery(enable bool) true Enable recovery middleware / 복구 미들웨어 활성화
WithMaxUploadSize(size int64) 32 MB Maximum file upload size / 최대 파일 업로드 크기
Session Management / 세션 관리

Cookie-based session management with in-memory storage.

쿠키 기반 세션 관리 및 인메모리 저장소.

SessionStore Methods / SessionStore 메서드:

  • NewSessionStore(opts SessionOptions) *SessionStore - Create session store / 세션 저장소 생성
  • Get(r *http.Request) (*Session, error) - Get or create session / 세션 가져오기 또는 생성
  • New() *Session - Create new session / 새 세션 생성
  • Save(w http.ResponseWriter, session *Session) - Save session and set cookie / 세션 저장 및 쿠키 설정
  • Destroy(w http.ResponseWriter, r *http.Request) error - Destroy session / 세션 파괴
  • Count() int - Get active session count / 활성 세션 수 가져오기

Session Methods / Session 메서드:

  • Set(key string, value interface{}) - Store value / 값 저장
  • Get(key string) (interface{}, bool) - Retrieve value / 값 검색
  • GetString(key string) string - Get string value / 문자열 값 가져오기
  • GetInt(key string) int - Get int value / int 값 가져오기
  • GetBool(key string) bool - Get bool value / bool 값 가져오기
  • Delete(key string) - Remove value / 값 제거
  • Clear() - Clear all values / 모든 값 지우기

SessionOptions / 세션 옵션:

  • CookieName - Session cookie name (default: "sessionid") / 세션 쿠키 이름
  • MaxAge - Session expiration (default: 24h) / 세션 만료 시간
  • Secure - HTTPS only (default: false) / HTTPS만 사용
  • HttpOnly - Prevent JavaScript access (default: true) / JavaScript 액세스 방지
  • SameSite - SameSite attribute (default: Lax) / SameSite 속성
  • Path - Cookie path (default: "/") / 쿠키 경로
  • Domain - Cookie domain / 쿠키 도메인
  • CleanupTime - Cleanup interval (default: 5m) / 정리 간격

Features / 기능:

  • Automatic session expiration / 자동 세션 만료
  • Background cleanup of expired sessions / 만료된 세션의 백그라운드 정리
  • Cryptographically secure session IDs / 암호학적으로 안전한 세션 ID
  • Thread-safe operations / 스레드 안전 작업
  • Type-safe getters / 타입 안전 getter
Middleware / 미들웨어

Built-in middleware for common use cases.

일반적인 사용 사례를 위한 내장 미들웨어.

Recovery Middleware / 복구 미들웨어:

  • Recovery() - Panic recovery with logging / 로깅과 함께 패닉 복구
  • RecoveryWithConfig(config) - Custom recovery configuration / 커스텀 복구 설정
  • Automatically logs panics with stack traces / 스택 트레이스와 함께 패닉 자동 로깅
  • Sends 500 Internal Server Error on panic / 패닉 시 500 Internal Server Error 전송

Logger Middleware / 로거 미들웨어:

  • Logger() - HTTP request logging / HTTP 요청 로깅
  • LoggerWithConfig(config) - Custom logger configuration / 커스텀 로거 설정
  • Logs method, path, status code, duration / 메서드, 경로, 상태 코드, 소요 시간 로깅
  • Customizable log function / 커스터마이즈 가능한 로그 함수

CORS Middleware / CORS 미들웨어:

  • CORS() - Cross-Origin Resource Sharing / Cross-Origin Resource Sharing
  • CORSWithConfig(config) - Custom CORS configuration / 커스텀 CORS 설정
  • Configurable origins, methods, headers / 설정 가능한 오리진, 메서드, 헤더
  • Automatic preflight request handling / 자동 프리플라이트 요청 처리
  • Credentials and max-age support / 자격 증명 및 max-age 지원

RequestID Middleware / 요청 ID 미들웨어:

  • RequestID() - Adds unique request ID to each request / 각 요청에 고유한 요청 ID 추가
  • RequestIDWithConfig(config) - Custom RequestID configuration / 커스텀 요청 ID 설정
  • Stores ID in context and response headers / 컨텍스트 및 응답 헤더에 ID 저장
  • Customizable header name and ID generator / 커스터마이즈 가능한 헤더 이름 및 ID 생성기
  • Preserves existing request ID if present / 기존 요청 ID가 있으면 보존

Timeout Middleware / 타임아웃 미들웨어:

  • Timeout(duration) - Enforces request timeout / 요청 타임아웃 적용
  • TimeoutWithConfig(config) - Custom timeout configuration / 커스텀 타임아웃 설정
  • Sends 503 Service Unavailable on timeout / 타임아웃 시 503 Service Unavailable 전송
  • Configurable timeout duration and message / 설정 가능한 타임아웃 기간 및 메시지
  • Default timeout: 30 seconds / 기본 타임아웃: 30초

BasicAuth Middleware / Basic 인증 미들웨어:

  • BasicAuth(username, password) - HTTP Basic Authentication / HTTP Basic Authentication
  • BasicAuthWithConfig(config) - Custom BasicAuth configuration / 커스텀 Basic 인증 설정
  • Constant-time password comparison / 상수 시간 비밀번호 비교
  • Customizable realm and validator function / 커스터마이즈 가능한 영역 및 검증자 함수
  • Stores username in context for later use / 나중에 사용하기 위해 컨텍스트에 사용자 이름 저장

RateLimiter Middleware / Rate Limiter 미들웨어:

  • RateLimiter(requests, window) - IP-based rate limiting / IP 기반 rate limiting
  • RateLimiterWithConfig(config) - Custom rate limiter configuration / 커스텀 rate limiter 설정
  • Token bucket algorithm with sliding window / 슬라이딩 윈도우와 함께 토큰 버킷 알고리즘
  • Returns 429 Too Many Requests when limit exceeded / 제한 초과 시 429 Too Many Requests 반환
  • X-RateLimit headers (Limit, Remaining, Reset) / X-RateLimit 헤더 (제한, 남은 수, 리셋 시간)
  • Customizable key function (IP, API key, etc.) / 커스터마이즈 가능한 키 함수

Compression Middleware / 압축 미들웨어:

  • Compression() - Gzip compression for responses / 응답에 대한 Gzip 압축
  • CompressionWithConfig(config) - Custom compression configuration / 커스텀 압축 설정
  • Automatic compression when client supports gzip / 클라이언트가 gzip을 지원할 때 자동 압축
  • Configurable compression level (1-9) / 설정 가능한 압축 레벨
  • Default minimum size: 1KB / 기본 최소 크기: 1KB
  • Sets Content-Encoding header / Content-Encoding 헤더 설정

SecureHeaders Middleware / 보안 헤더 미들웨어:

  • SecureHeaders() - Adds security-related HTTP headers / 보안 관련 HTTP 헤더 추가
  • SecureHeadersWithConfig(config) - Custom secure headers configuration / 커스텀 보안 헤더 설정
  • X-Frame-Options (SAMEORIGIN) - Clickjacking protection / 클릭재킹 보호
  • X-Content-Type-Options (nosniff) - MIME sniffing protection / MIME 스니핑 보호
  • X-XSS-Protection (1; mode=block) - XSS protection / XSS 보호
  • Strict-Transport-Security (HSTS) - HTTPS enforcement / HTTPS 강제
  • Content-Security-Policy - CSP support / CSP 지원
  • Referrer-Policy - Referrer control / 리퍼러 제어

BodyLimit Middleware / 본문 제한 미들웨어:

  • BodyLimit(maxBytes) - Limits maximum request body size / 최대 요청 본문 크기 제한
  • BodyLimitWithConfig(config) - Custom body limit configuration / 커스텀 본문 제한 설정
  • Default limit: 10MB / 기본 제한: 10MB
  • Returns error when body exceeds limit / 본문이 제한을 초과하면 에러 반환
  • Prevents memory exhaustion attacks / 메모리 고갈 공격 방지

Static Middleware / 정적 파일 미들웨어:

  • Static(root) - Serves static files from directory / 디렉토리에서 정적 파일 제공
  • StaticWithConfig(config) - Custom static file configuration / 커스텀 정적 파일 설정
  • Automatic index.html serving / 자동 index.html 제공
  • Optional directory browsing / 선택적 디렉토리 탐색
  • Falls through to next handler if file not found / 파일을 찾을 수 없으면 다음 핸들러로 전달

Redirect Middleware / 리디렉션 미들웨어:

  • Redirect(to) - Redirects all requests to URL / 모든 요청을 URL로 리디렉션
  • RedirectWithConfig(config) - Custom redirect configuration / 커스텀 리디렉션 설정
  • HTTPSRedirect() - Redirects HTTP to HTTPS / HTTP를 HTTPS로 리디렉션
  • WWWRedirect(addWWW) - Adds or removes www prefix / www 접두사 추가 또는 제거
  • Default: 301 Moved Permanently / 기본값: 301 Moved Permanently
  • Configurable status code / 설정 가능한 상태 코드
Template System / 템플릿 시스템

Built-in template engine for HTML rendering with auto-discovery and custom functions.

HTML 렌더링을 위한 내장 템플릿 엔진 (자동 발견 및 커스텀 함수 지원).

TemplateEngine / 템플릿 엔진:

  • Automatic template loading from directory / 디렉토리에서 자동 템플릿 로딩
  • Support for nested directories / 중첩 디렉토리 지원
  • Layout system / 레이아웃 시스템 (v1.11.011+)
  • 26+ built-in template functions / 26개 이상의 내장 템플릿 함수 (v1.11.011+)
  • Hot reload in development / 개발 시 핫 리로드 (v1.11.012+)
  • Custom template functions / 커스텀 템플릿 함수
  • Custom delimiters / 커스텀 구분자
  • Thread-safe template caching / 스레드 안전 템플릿 캐싱

Template Methods / 템플릿 메서드:

  • NewTemplateEngine(dir) - Create new template engine / 새 템플릿 엔진 생성
  • Load(name) - Load single template / 단일 템플릿 로드
  • LoadGlob(pattern) - Load templates by pattern / 패턴으로 템플릿 로드
  • LoadAll() - Load all templates recursively / 모든 템플릿 재귀적 로드
  • Render(w, name, data) - Render template to writer / writer에 템플릿 렌더링
  • AddFunc(name, fn) - Add custom template function / 커스텀 템플릿 함수 추가
  • AddFuncs(funcs) - Add multiple functions / 여러 함수 추가
  • SetDelimiters(left, right) - Set custom delimiters / 커스텀 구분자 설정
  • Has(name) - Check if template exists / 템플릿 존재 확인
  • List() - List all loaded templates / 모든 로드된 템플릿 목록
  • Clear() - Remove all templates / 모든 템플릿 제거

App Template Methods / 앱 템플릿 메서드:

  • app.TemplateEngine() - Get template engine instance / 템플릿 엔진 인스턴스 가져오기
  • app.LoadTemplate(name) - Load single template / 단일 템플릿 로드
  • app.LoadTemplates(pattern) - Load templates by pattern / 패턴으로 템플릿 로드
  • app.ReloadTemplates() - Reload all templates / 모든 템플릿 다시 로드
  • app.AddTemplateFunc(name, fn) - Add custom function / 커스텀 함수 추가
  • app.AddTemplateFuncs(funcs) - Add multiple functions / 여러 함수 추가

Context Template Methods / Context 템플릿 메서드:

  • ctx.Render(code, name, data) - Render template file / 템플릿 파일 렌더링
  • ctx.RenderWithLayout(code, layout, template, data) - Render with layout / 레이아웃과 함께 렌더링 (v1.11.011+)
  • ctx.HTMLTemplate(code, tmpl, data) - Render inline template / 인라인 템플릿 렌더링

Layout Methods / 레이아웃 메서드 (v1.11.011+):

  • SetLayoutDir(dir) - Set layout directory / 레이아웃 디렉토리 설정
  • LoadLayout(name) - Load single layout / 단일 레이아웃 로드
  • LoadAllLayouts() - Load all layouts / 모든 레이아웃 로드
  • RenderWithLayout(w, layout, template, data) - Render with layout / 레이아웃과 함께 렌더링
  • HasLayout(name) - Check if layout exists / 레이아웃 존재 확인
  • ListLayouts() - List all loaded layouts / 모든 로드된 레이아웃 목록

Built-in Template Functions (v1.11.011+):

  • String functions: upper, lower, title, trim, trimPrefix, trimSuffix, replace, contains, hasPrefix, hasSuffix, split, join, repeat
  • Date/Time functions: now, formatDate, formatDateSimple, formatDateTime, formatTime
  • URL functions: urlEncode, urlDecode
  • Safe HTML: safeHTML, safeURL, safeJS
  • Utility functions: default, len

Hot Reload Methods (v1.11.012+):

  • EnableAutoReload() - Enable automatic template reloading / 자동 템플릿 재로드 활성화
  • DisableAutoReload() - Disable automatic template reloading / 자동 템플릿 재로드 비활성화
  • IsAutoReloadEnabled() - Check if auto-reload is enabled / 자동 재로드 활성화 확인

Quick Start / 빠른 시작

Basic Server with Routes / 라우트가 있는 기본 서버
package main

import (
    "fmt"
    "log"
    "net/http"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    // Create app with defaults
    // 기본값으로 앱 생성
    app := websvrutil.New()

    // Register routes
    // 라우트 등록
    app.GET("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Welcome!")
    })

    app.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
        // Get Context to access path parameters
        // 경로 매개변수 액세스를 위한 Context 가져오기
        ctx := websvrutil.GetContext(r)
        id := ctx.Param("id")
        fmt.Fprintf(w, "User ID: %s", id)
    })

    app.POST("/users", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusCreated)
        fmt.Fprintf(w, "User created")
    })

    // Start server
    // 서버 시작
    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}
Server with Custom Options / 커스텀 옵션을 사용한 서버
package main

import (
    "log"
    "time"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    // Create app with custom options
    // 커스텀 옵션으로 앱 생성
    app := websvrutil.New(
        websvrutil.WithReadTimeout(30 * time.Second),
        websvrutil.WithWriteTimeout(30 * time.Second),
        websvrutil.WithTemplateDir("views"),
        websvrutil.WithStaticDir("public"),
        websvrutil.WithAutoReload(true), // Enable in development
    )

    // Start server
    // 서버 시작
    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}
Graceful Shutdown / 정상 종료
package main

import (
    "context"
    "log"
    "os"
    "os/signal"
    "syscall"
    "time"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    app := websvrutil.New()

    // Setup signal handling
    // 시그널 처리 설정
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    // Start server in goroutine
    // 고루틴에서 서버 시작
    go func() {
        if err := app.Run(":8080"); err != nil {
            log.Printf("Server error: %v", err)
        }
    }()

    // Wait for interrupt signal
    // 인터럽트 시그널 대기
    <-quit
    log.Println("Shutting down server...")

    // Graceful shutdown with 5 second timeout
    // 5초 타임아웃으로 정상 종료
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    if err := app.Shutdown(ctx); err != nil {
        log.Fatal("Server forced to shutdown:", err)
    }

    log.Println("Server exited")
}
Graceful Shutdown (Simple) / 정상 종료 (간단)
package main

import (
    "fmt"
    "log"
    "time"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    app := websvrutil.New()

    app.GET("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })

    // RunWithGracefulShutdown automatically handles SIGINT and SIGTERM signals
    // RunWithGracefulShutdown은 SIGINT 및 SIGTERM 시그널을 자동으로 처리합니다
    if err := app.RunWithGracefulShutdown(":8080", 5*time.Second); err != nil {
        log.Fatal(err)
    }

    log.Println("Server stopped gracefully")
}
Custom Middleware / 커스텀 미들웨어
package main

import (
    "log"
    "net/http"
    "time"
    "github.com/arkd0ng/go-utils/websvrutil"
)

// Logging middleware example
// 로깅 미들웨어 예제
func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)

        next.ServeHTTP(w, r)

        log.Printf("Completed in %v", time.Since(start))
    })
}

// Authentication middleware example
// 인증 미들웨어 예제
func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")

        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        // Validate token here
        // 여기서 토큰 검증

        next.ServeHTTP(w, r)
    })
}

func main() {
    app := websvrutil.New()

    // Add middleware (executed in order)
    // 미들웨어 추가 (순서대로 실행)
    app.Use(loggingMiddleware)
    app.Use(authMiddleware)

    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}
Context Usage / Context 사용
package main

import (
    "fmt"
    "log"
    "net/http"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    app := websvrutil.New()

    // Path parameters
    // 경로 매개변수
    app.GET("/users/:id/posts/:postId", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)
        userID := ctx.Param("id")
        postID := ctx.Param("postId")
        fmt.Fprintf(w, "User: %s, Post: %s", userID, postID)
    })

    // Query parameters
    // 쿼리 매개변수
    app.GET("/search", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)
        q := ctx.Query("q")
        page := ctx.QueryDefault("page", "1")
        fmt.Fprintf(w, "Query: %s, Page: %s", q, page)
    })

    // Custom values storage
    // 커스텀 값 저장
    app.GET("/user/:id", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)

        // Store custom values
        // 커스텀 값 저장
        ctx.Set("userId", ctx.Param("id"))
        ctx.Set("authenticated", true)

        // Retrieve values
        // 값 검색
        if authenticated := ctx.GetBool("authenticated"); authenticated {
            userID := ctx.GetString("userId")
            fmt.Fprintf(w, "Authenticated user: %s", userID)
        }
    })

    // Request headers
    // 요청 헤더
    app.GET("/api/data", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)
        authToken := ctx.Header("Authorization")
        contentType := ctx.Header("Content-Type")

        // Set response headers
        // 응답 헤더 설정
        ctx.SetHeader("X-API-Version", "1.0")
        ctx.SetHeader("Content-Type", "application/json")

        fmt.Fprintf(w, "Auth: %s, Type: %s", authToken, contentType)
    })

    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}
Wildcard and Custom 404 / 와일드카드 및 커스텀 404
package main

import (
    "fmt"
    "log"
    "net/http"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    app := websvrutil.New()

    // Exact match
    // 정확한 일치
    app.GET("/users", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Users list")
    })

    // Parameter match
    // 매개변수 일치
    app.GET("/users/:id", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)
        id := ctx.Param("id")
        fmt.Fprintf(w, "User ID: %s", id)
    })

    // Wildcard match (catches all paths starting with /files/)
    // 와일드카드 일치 (/files/로 시작하는 모든 경로 포착)
    app.GET("/files/*", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "File: %s", r.URL.Path)
    })

    // Custom 404 handler
    // 커스텀 404 핸들러
    app.NotFound(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusNotFound)
        fmt.Fprintf(w, "Page not found: %s", r.URL.Path)
    })

    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}
File Upload / 파일 업로드
package main

import (
    "fmt"
    "log"
    "net/http"
    "path/filepath"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    // Create app with custom max upload size (10 MB)
    // 커스텀 최대 업로드 크기(10 MB)로 앱 생성
    app := websvrutil.New(
        websvrutil.WithMaxUploadSize(10 << 20), // 10 MB
    )

    // Single file upload / 단일 파일 업로드
    app.POST("/upload", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)

        // Get uploaded file / 업로드된 파일 가져오기
        file, err := ctx.FormFile("file")
        if err != nil {
            http.Error(w, "Failed to get file: "+err.Error(), http.StatusBadRequest)
            return
        }

        // Save file to disk / 파일을 디스크에 저장
        dstPath := filepath.Join("uploads", file.Filename)
        if err := ctx.SaveUploadedFile(file, dstPath); err != nil {
            http.Error(w, "Failed to save file: "+err.Error(), http.StatusInternalServerError)
            return
        }

        fmt.Fprintf(w, "File uploaded successfully: %s (%d bytes)",
            file.Filename, file.Size)
    })

    // Multiple file upload / 여러 파일 업로드
    app.POST("/upload-multiple", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)

        // Get multipart form / multipart 폼 가져오기
        form, err := ctx.MultipartForm()
        if err != nil {
            http.Error(w, "Failed to parse form: "+err.Error(), http.StatusBadRequest)
            return
        }

        // Get all uploaded files / 모든 업로드된 파일 가져오기
        files := form.File["files"]

        for _, file := range files {
            dstPath := filepath.Join("uploads", file.Filename)

            // Save each file / 각 파일 저장
            if err := ctx.SaveUploadedFile(file, dstPath); err != nil {
                fmt.Fprintf(w, "Failed to save %s: %v\n", file.Filename, err)
                continue
            }

            fmt.Fprintf(w, "Saved: %s (%d bytes)\n", file.Filename, file.Size)
        }
    })

    // File upload with form fields / 폼 필드와 함께 파일 업로드
    app.POST("/upload-with-data", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)

        // Get multipart form / multipart 폼 가져오기
        form, err := ctx.MultipartForm()
        if err != nil {
            http.Error(w, "Failed to parse form: "+err.Error(), http.StatusBadRequest)
            return
        }

        // Get form fields / 폼 필드 가져오기
        title := form.Value["title"][0]
        description := form.Value["description"][0]

        // Get uploaded file / 업로드된 파일 가져오기
        file := form.File["file"][0]
        dstPath := filepath.Join("uploads", file.Filename)

        if err := ctx.SaveUploadedFile(file, dstPath); err != nil {
            http.Error(w, "Failed to save file: "+err.Error(), http.StatusInternalServerError)
            return
        }

        fmt.Fprintf(w, "Title: %s\nDescription: %s\nFile: %s (%d bytes)",
            title, description, file.Filename, file.Size)
    })

    log.Println("Server starting on :8080")
    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}
Static File Serving / 정적 파일 서빙
package main

import (
    "fmt"
    "log"
    "net/http"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    app := websvrutil.New()

    // Serve static files from ./public directory at /static/* URLs
    // ./public 디렉토리의 파일을 /static/* URL에서 제공
    app.Static("/static", "./public")

    // Serve static files from multiple directories
    // 여러 디렉토리에서 정적 파일 제공
    app.Static("/css", "./assets/css")
    app.Static("/js", "./assets/js")
    app.Static("/images", "./assets/images")

    // Serve a specific file / 특정 파일 제공
    app.GET("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)
        ctx.File("./public/favicon.ico")
    })

    // Serve file as download / 파일을 다운로드로 제공
    app.GET("/download/report", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)
        ctx.FileAttachment("./reports/monthly.pdf", "monthly-report.pdf")
    })

    // Dynamic file serving based on parameter / 매개변수 기반 동적 파일 제공
    app.GET("/files/:filename", func(w http.ResponseWriter, r *http.Request) {
        ctx := websvrutil.GetContext(r)
        filename := ctx.Param("filename")
        filepath := fmt.Sprintf("./uploads/%s", filename)
        ctx.File(filepath)
    })

    log.Println("Server starting on :8080")
    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}
Session Management / 세션 관리
package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
    "github.com/arkd0ng/go-utils/websvrutil"
)

func main() {
    app := websvrutil.New()

    // Create session store with custom options / 커스텀 옵션으로 세션 저장소 생성
    sessionStore := websvrutil.NewSessionStore(websvrutil.SessionOptions{
        CookieName:  "mysession",
        MaxAge:      24 * time.Hour,
        HttpOnly:    true,
        Secure:      false, // Set to true in production with HTTPS / HTTPS를 사용하는 프로덕션에서는 true로 설정
        SameSite:    http.SameSiteLaxMode,
        Path:        "/",
        CleanupTime: 5 * time.Minute,
    })

    // Login handler - create session / 로그인 핸들러 - 세션 생성
    app.POST("/login", func(w http.ResponseWriter, r *http.Request) {
        // Get or create session / 세션 가져오기 또는 생성
        session, _ := sessionStore.Get(r)

        // Store user data in session / 세션에 사용자 데이터 저장
        session.Set("user_id", 123)
        session.Set("username", "john")
        session.Set("authenticated", true)

        // Save session and set cookie / 세션 저장 및 쿠키 설정
        sessionStore.Save(w, session)

        fmt.Fprintf(w, "Login successful! Session ID: %s", session.ID)
    })

    // Protected route - check session / 보호된 라우트 - 세션 확인
    app.GET("/dashboard", func(w http.ResponseWriter, r *http.Request) {
        session, err := sessionStore.Get(r)
        if err != nil {
            http.Error(w, "Session error", http.StatusInternalServerError)
            return
        }

        // Check if user is authenticated / 사용자 인증 확인
        authenticated := session.GetBool("authenticated")
        if !authenticated {
            http.Error(w, "Not authenticated", http.StatusUnauthorized)
            return
        }

        // Get user data from session / 세션에서 사용자 데이터 가져오기
        username := session.GetString("username")
        userID := session.GetInt("user_id")

        fmt.Fprintf(w, "Welcome %s (ID: %d)!", username, userID)
    })

    // Logout handler - destroy session / 로그아웃 핸들러 - 세션 파괴
    app.POST("/logout", func(w http.ResponseWriter, r *http.Request) {
        if err := sessionStore.Destroy(w, r); err != nil {
            http.Error(w, "Logout failed", http.StatusInternalServerError)
            return
        }
        fmt.Fprintf(w, "Logged out successfully")
    })

    // Session info endpoint / 세션 정보 엔드포인트
    app.GET("/session/info", func(w http.ResponseWriter, r *http.Request) {
        session, _ := sessionStore.Get(r)

        fmt.Fprintf(w, "Session ID: %s\n", session.ID)
        fmt.Fprintf(w, "Created: %s\n", session.CreatedAt)
        fmt.Fprintf(w, "Expires: %s\n", session.ExpiresAt)
        fmt.Fprintf(w, "Active Sessions: %d\n", sessionStore.Count())
    })

    log.Println("Server starting on :8080")
    if err := app.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}

Upcoming Features / 예정된 기능

The following features are planned for future releases:

다음 기능이 향후 릴리스에 계획되어 있습니다:

  • Middleware System (v1.11.006-010): Built-in middleware (recovery, logger, CORS, auth) / 내장 미들웨어
  • Template System (v1.11.011-015): Auto-discovery, layouts, hot reload / 자동 발견, 레이아웃, 핫 리로드
  • Advanced Features (v1.11.016-020): File upload, static serving, cookie helpers / 파일 업로드, 정적 제공

Development Status / 개발 상태

Current Phase / 현재 단계: Phase 2 - Middleware System (v1.11.006-010)

Progress / 진행 상황:

  • ✅ v1.11.001: Project setup and planning / 프로젝트 설정 및 계획
  • ✅ v1.11.002: App & Options / 앱 및 옵션
  • ✅ v1.11.003: Router / 라우터
  • ✅ v1.11.004: Context (Part 1) / 컨텍스트 (1부)
  • ✅ v1.11.005: Response Helpers / 응답 헬퍼
  • ✅ v1.11.006: Middleware (Recovery, Logger, CORS) / 미들웨어 (복구, 로거, CORS)
  • ✅ v1.11.007: Additional Middleware (RequestID, Timeout, BasicAuth) / 추가 미들웨어 (요청 ID, 타임아웃, Basic 인증)
  • ✅ v1.11.008: Advanced Middleware (RateLimiter, Compression, SecureHeaders) / 고급 미들웨어 (Rate Limiter, 압축, 보안 헤더)
  • ✅ v1.11.009: Final Middleware (BodyLimit, Static, Redirect, HTTPSRedirect, WWWRedirect) / 최종 미들웨어 (본문 제한, 정적 파일, 리디렉션)
  • ✅ v1.11.010: Template Engine Core (TemplateEngine, auto-loading, custom functions) / 템플릿 엔진 핵심 (TemplateEngine, 자동 로딩, 커스텀 함수)
  • ✅ v1.11.011: Layout System and Built-in Functions (layouts, 26+ built-in functions) / 레이아웃 시스템 및 내장 함수 (레이아웃, 26개 이상의 내장 함수)
  • ✅ v1.11.012: Hot Reload (automatic template reloading, file watching) / 핫 리로드 (자동 템플릿 재로드, 파일 감시)
  • ✅ v1.11.013: Request Binding (Bind, BindJSON, BindForm, BindQuery) / 요청 바인딩 (Bind, BindJSON, BindForm, BindQuery)
  • ✅ v1.11.014: Cookie & Header Helpers (Cookie, SetCookie, DeleteCookie, GetCookie, AddHeader, GetHeaders, ClientIP) / 쿠키 및 헤더 헬퍼
  • ✅ v1.11.015: File Upload (FormFile, MultipartForm, SaveUploadedFile, MaxUploadSize) / 파일 업로드 (FormFile, MultipartForm, SaveUploadedFile, MaxUploadSize)
  • ✅ v1.11.016: Static File Serving (Static, File, FileAttachment) / 정적 파일 서빙 (Static, File, FileAttachment)
  • ✅ v1.11.017: Context Storage Enhancement (GetInt64, GetFloat64, GetStringSlice, GetStringMap, Exists, Delete, Keys) / 컨텍스트 저장소 향상

Documentation / 문서

Testing / 테스트

# Run all tests
# 모든 테스트 실행
go test ./websvrutil -v

# Run with coverage
# 커버리지와 함께 실행
go test ./websvrutil -cover

# Run benchmarks
# 벤치마크 실행
go test ./websvrutil -bench=.

License / 라이선스

MIT License - see LICENSE for details.

MIT 라이선스 - 자세한 내용은 LICENSE를 참조하세요.

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

Examples

Constants

View Source
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 기본 타임아웃 설정

View Source
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 기본 크기 제한

View Source
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 기본 세션 설정

View Source
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 상수

View Source
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

View Source
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

func GetCSRFToken(ctx *Context) string

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

func New(opts ...Option) *App

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

func (a *App) AddTemplateFunc(name string, fn interface{})

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

func (a *App) AddTemplateFuncs(funcs map[string]interface{})

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

func (a *App) Group(prefix string) *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

func (a *App) LoadTemplate(name string) error

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

func (a *App) LoadTemplates(pattern string) error

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

func (a *App) ReloadTemplates() error

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:

  1. **Clear Phase**: All currently loaded templates are removed from memory

  2. **Load Phase**: Template engine reloads all templates from directory

  3. **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

func (a *App) Run(addr string) error

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

func (a *App) RunWithGracefulShutdown(addr string, timeout time.Duration) error

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:

  1. **Acquire Read Lock**: Locks app.mu.RLock() for thread-safe handler building
  2. **Build Handler Chain**: Calls buildHandler() to construct middleware + router chain
  3. **Release Read Lock**: Unlocks app.mu.RUnlock() after handler construction
  4. **Store App Context**: Adds App instance to request context with key "app"
  5. **Update Request**: Creates new request with enhanced context
  6. **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 오류 응답을 작성하여 처리해야 합니다.

동작 방식:

  1. **읽기 잠금 획득**: 스레드 안전 핸들러 구축을 위해 app.mu.RLock() 잠금
  2. **핸들러 체인 구축**: buildHandler()를 호출하여 미들웨어 + 라우터 체인 구성
  3. **읽기 잠금 해제**: 핸들러 구성 후 app.mu.RUnlock() 잠금 해제
  4. **앱 컨텍스트 저장**: "app" 키로 요청 컨텍스트에 App 인스턴스 추가
  5. **요청 업데이트**: 향상된 컨텍스트로 새 요청 생성
  6. **처리 위임**: 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

func (a *App) Shutdown(ctx context.Context) error

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

func (a *App) Static(prefix, dir string) *App

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

func GetContext(r *http.Request) *Context

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

func (c *Context) AbortWithError(code int, message string)

AbortWithError aborts with status code and error message. AbortWithError는 상태 코드와 에러 메시지로 중단합니다.

Example 예제:

ctx.AbortWithError(http.StatusBadRequest, "Invalid input")

func (*Context) AbortWithJSON

func (c *Context) AbortWithJSON(code int, obj interface{})

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

func (c *Context) AbortWithStatus(code int)

AbortWithStatus aborts the request with the specified status code. AbortWithStatus는 지정된 상태 코드로 요청을 중단합니다.

Example 예제:

ctx.AbortWithStatus(http.StatusUnauthorized)

func (*Context) AcceptsHTML

func (c *Context) AcceptsHTML() bool

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

func (c *Context) AcceptsJSON() bool

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

func (c *Context) AcceptsXML() bool

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

func (c *Context) AddHeader(key, value string)

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

func (c *Context) Bind(obj interface{}) error

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

func (c *Context) BindForm(obj interface{}) error

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

func (c *Context) BindJSON(obj interface{}) error

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

func (c *Context) BindQuery(obj interface{}) error

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

func (c *Context) BindWithValidation(obj interface{}) error

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

func (c *Context) ClientIP() string

ClientIP returns the client IP address. ClientIP는 클라이언트 IP 주소를 반환합니다.

Priority order for IP detection IP 감지 우선순위:

  1. X-Forwarded-For header (first IP in comma-separated list)

  2. X-Real-IP header

  3. RemoteAddr (direct connection)

  4. X-Forwarded-For 헤더 (쉼표로 구분된 목록의 첫 번째 IP)

  5. X-Real-IP 헤더

  6. 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

func (c *Context) ContentType() string

ContentType returns the Content-Type header of the request. ContentType은 요청의 Content-Type 헤더를 반환합니다.

Example 예제:

contentType := ctx.ContentType()

func (*Context) Context

func (c *Context) Context() context.Context

Context returns the request's context.Context. Context는 요청의 context.Context를 반환합니다.

func (*Context) Cookie

func (c *Context) Cookie(name string) (*http.Cookie, error)

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

func (c *Context) CookieValue(name string) string

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) Delete

func (c *Context) Delete(key string)

Delete removes a value from the context. Delete는 컨텍스트에서 값을 제거합니다.

func (*Context) DeleteCookie

func (c *Context) DeleteCookie(name, path string)

DeleteCookie deletes a cookie by setting its MaxAge to -1. DeleteCookie는 MaxAge를 -1로 설정하여 쿠키를 삭제합니다.

Example 예제:

ctx.DeleteCookie("session_id", "/")

func (*Context) Error

func (c *Context) Error(code int, message string) 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

func (c *Context) ErrorJSON(code int, message string)

ErrorJSON sends a standardized JSON error response. ErrorJSON은 표준화된 JSON 에러 응답을 전송합니다.

Example 예제:

ctx.ErrorJSON(http.StatusNotFound, "User not found")

func (*Context) Exists

func (c *Context) Exists(key string) bool

Exists checks if a key exists in the context. Exists는 컨텍스트에 키가 존재하는지 확인합니다.

func (*Context) File

func (c *Context) File(filepath string) error

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

func (c *Context) FileAttachment(filepath, filename string) error

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

func (c *Context) Get(key string) (interface{}, bool)

Get retrieves a value from the context. Get은 컨텍스트에서 값을 검색합니다.

Example 예제:

user, exists := ctx.Get("user")
if exists {
    // Use user
}

func (*Context) GetBool

func (c *Context) GetBool(key string) bool

GetBool retrieves a bool value from the context. GetBool은 컨텍스트에서 bool 값을 검색합니다.

func (*Context) GetCookie

func (c *Context) GetCookie(name string) string

GetCookie is a convenience method to get a cookie value. GetCookie는 쿠키 값을 가져오는 편의 메서드입니다.

Example 예제:

value := ctx.GetCookie("session_id")

func (*Context) GetFloat64

func (c *Context) GetFloat64(key string) float64

GetFloat64 retrieves a float64 value from the context. GetFloat64는 컨텍스트에서 float64 값을 검색합니다.

func (*Context) GetHeader

func (c *Context) GetHeader(key string) string

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

func (c *Context) GetHeaders(key string) []string

GetHeaders returns all values for the given header key. GetHeaders는 주어진 헤더 키의 모든 값을 반환합니다.

Example 예제:

acceptEncodings := ctx.GetHeaders("Accept-Encoding")

func (*Context) GetInt

func (c *Context) GetInt(key string) int

GetInt retrieves an int value from the context. GetInt은 컨텍스트에서 int 값을 검색합니다.

func (*Context) GetInt64

func (c *Context) GetInt64(key string) int64

GetInt64 retrieves an int64 value from the context. GetInt64는 컨텍스트에서 int64 값을 검색합니다.

func (*Context) GetString

func (c *Context) GetString(key string) string

GetString retrieves a string value from the context. GetString은 컨텍스트에서 문자열 값을 검색합니다.

func (*Context) GetStringMap

func (c *Context) GetStringMap(key string) map[string]interface{}

GetStringMap retrieves a map[string]interface{} value from the context. GetStringMap은 컨텍스트에서 map[string]interface{} 값을 검색합니다.

func (*Context) GetStringSlice

func (c *Context) GetStringSlice(key string) []string

GetStringSlice retrieves a []string value from the context. GetStringSlice는 컨텍스트에서 []string 값을 검색합니다.

func (*Context) HTML

func (c *Context) HTML(code int, html string) error

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

func (c *Context) HTMLTemplate(code int, tmpl string, data interface{}) error

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

func (c *Context) Header(key string) string

Header returns the request header with the given key. Header는 주어진 키의 요청 헤더를 반환합니다.

func (*Context) HeaderExists

func (c *Context) HeaderExists(key string) bool

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

func (c *Context) IsAjax() bool

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

func (c *Context) IsDELETE() bool

IsDELETE checks if the request method is DELETE. IsDELETE는 요청 메서드가 DELETE인지 확인합니다.

Example 예제:

if ctx.IsDELETE() {
    // Handle DELETE request
}

func (*Context) IsGET

func (c *Context) IsGET() bool

IsGET checks if the request method is GET. IsGET는 요청 메서드가 GET인지 확인합니다.

Example 예제:

if ctx.IsGET() {
    // Handle GET request
}

func (*Context) IsHEAD

func (c *Context) IsHEAD() bool

IsHEAD checks if the request method is HEAD. IsHEAD는 요청 메서드가 HEAD인지 확인합니다.

Example 예제:

if ctx.IsHEAD() {
    // Handle HEAD request
}

func (*Context) IsOPTIONS

func (c *Context) IsOPTIONS() bool

IsOPTIONS checks if the request method is OPTIONS. IsOPTIONS는 요청 메서드가 OPTIONS인지 확인합니다.

Example 예제:

if ctx.IsOPTIONS() {
    // Handle OPTIONS request
}

func (*Context) IsPATCH

func (c *Context) IsPATCH() bool

IsPATCH checks if the request method is PATCH. IsPATCH는 요청 메서드가 PATCH인지 확인합니다.

Example 예제:

if ctx.IsPATCH() {
    // Handle PATCH request
}

func (*Context) IsPOST

func (c *Context) IsPOST() bool

IsPOST checks if the request method is POST. IsPOST는 요청 메서드가 POST인지 확인합니다.

Example 예제:

if ctx.IsPOST() {
    // Handle POST request
}

func (*Context) IsPUT

func (c *Context) IsPUT() bool

IsPUT checks if the request method is PUT. IsPUT는 요청 메서드가 PUT인지 확인합니다.

Example 예제:

if ctx.IsPUT() {
    // Handle PUT request
}

func (*Context) IsWebSocket

func (c *Context) IsWebSocket() bool

IsWebSocket checks if the request is a WebSocket upgrade request. IsWebSocket는 요청이 WebSocket 업그레이드 요청인지 확인합니다.

Example 예제:

if ctx.IsWebSocket() {
    // Handle WebSocket upgrade
}

func (*Context) JSON

func (c *Context) JSON(code int, data interface{}) error

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

func (c *Context) JSONIndent(code int, data interface{}, prefix, indent string) error

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

func (c *Context) JSONPretty(code int, data interface{}) error

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) Keys

func (c *Context) Keys() []string

Keys returns all keys in the context. Keys는 컨텍스트의 모든 키를 반환합니다.

func (*Context) Method

func (c *Context) Method() string

Method returns the HTTP request method. Method는 HTTP 요청 메서드를 반환합니다.

func (*Context) MultipartForm

func (c *Context) MultipartForm() (*multipart.Form, error)

MultipartForm returns the parsed multipart form, including file uploads. MultipartForm은 파일 업로드를 포함한 파싱된 multipart 폼을 반환합니다.

func (*Context) MustGet

func (c *Context) MustGet(key string) interface{}

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

func (c *Context) Param(name string) string

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

func (c *Context) Params() map[string]string

Params returns all URL parameters as a map. Params는 모든 URL 매개변수를 맵으로 반환합니다.

func (*Context) Path

func (c *Context) Path() string

Path returns the URL path. Path는 URL 경로를 반환합니다.

func (*Context) Query

func (c *Context) Query(key string) string

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

func (c *Context) QueryDefault(key, defaultValue string) string

QueryDefault returns the query string parameter with the given name, or the default value if it doesn't exist. QueryDefault는 주어진 이름의 쿼리 문자열 매개변수를 반환하거나, 존재하지 않으면 기본값을 반환합니다.

func (*Context) Redirect

func (c *Context) Redirect(code int, url string)

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

func (c *Context) Referer() string

Referer returns the Referer header of the request. Referer는 요청의 Referer 헤더를 반환합니다.

Example 예제:

referer := ctx.Referer()

func (*Context) Render

func (c *Context) Render(code int, name string, data interface{}) error

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

func (c *Context) Set(key string, value interface{})

Set stores a value in the context. Set은 컨텍스트에 값을 저장합니다.

Example 예제:

ctx.Set("user", user)
ctx.Set("requestID", "12345")

func (*Context) SetCookie

func (c *Context) SetCookie(cookie *http.Cookie)

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) SetHeader

func (c *Context) SetHeader(key, value string)

SetHeader sets a response header. SetHeader는 응답 헤더를 설정합니다.

func (*Context) Status

func (c *Context) Status(code int)

Status sets the HTTP response status code. Status는 HTTP 응답 상태 코드를 설정합니다.

func (*Context) SuccessJSON

func (c *Context) SuccessJSON(code int, message string, data interface{})

SuccessJSON sends a standardized JSON success response. SuccessJSON은 표준화된 JSON 성공 응답을 전송합니다.

Example 예제:

ctx.SuccessJSON(http.StatusOK, "Operation completed", data)

func (*Context) Text

func (c *Context) Text(code int, text string) error

Text sends a plain text response. Text는 일반 텍스트 응답을 전송합니다.

Example 예제:

ctx.Text(200, "Hello World")

func (*Context) Textf

func (c *Context) Textf(code int, format string, args ...interface{}) error

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

func (c *Context) UserAgent() string

UserAgent returns the User-Agent header of the request. UserAgent는 요청의 User-Agent 헤더를 반환합니다.

Example 예제:

userAgent := ctx.UserAgent()

func (*Context) WithContext

func (c *Context) WithContext(ctx context.Context) *Context

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) Write

func (c *Context) Write(data []byte) (int, error)

Write writes data to the response body. Write는 응답 본문에 데이터를 씁니다.

func (*Context) WriteString

func (c *Context) WriteString(s string) (int, error)

WriteString writes a string to the response body. WriteString은 응답 본문에 문자열을 씁니다.

func (*Context) XML

func (c *Context) XML(code int, xml string) error

XML sends an XML response. XML은 XML 응답을 전송합니다.

Example 예제:

ctx.XML(200, "<root><message>success</message></root>")

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

func (g *Group) Group(prefix string) *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

type MiddlewareFunc func(http.Handler) http.Handler

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

func WithAutoReload(enable bool) Option

WithAutoReload enables or disables automatic template reloading. Default: false. Enable only in development mode for hot reload.

WithAutoReload은 자동 템플릿 재로드를 활성화하거나 비활성화합니다. 기본값: false. 핫 리로드를 위해 개발 모드에서만 활성화하세요.

func WithIdleTimeout

func WithIdleTimeout(d time.Duration) Option

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

func WithLogger(enable bool) Option

WithLogger enables or disables built-in request logging middleware. Default: true. Logs HTTP method, path, status code, and duration.

WithLogger는 내장 요청 로깅 미들웨어를 활성화하거나 비활성화합니다. 기본값: true. HTTP 메서드, 경로, 상태 코드 및 기간을 로깅합니다.

func WithMaxBodySize

func WithMaxBodySize(size int64) Option

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

func WithMaxHeaderBytes(n int) Option

WithMaxHeaderBytes sets the maximum bytes server will read parsing request headers. Default: 1MB. Prevents malicious large headers from consuming memory.

WithMaxHeaderBytes는 서버가 요청 헤더를 파싱할 때 읽을 최대 바이트를 설정합니다. 기본값: 1MB. 악의적인 대형 헤더가 메모리를 소비하는 것을 방지합니다.

func WithMaxUploadSize

func WithMaxUploadSize(size int64) Option

WithMaxUploadSize sets the maximum allowed file upload size in bytes. Default: 32MB. Prevents excessive memory usage from large file uploads.

WithMaxUploadSize는 허용되는 최대 파일 업로드 크기(바이트)를 설정합니다. 기본값: 32MB. 대용량 파일 업로드로 인한 과도한 메모리 사용을 방지합니다.

func WithReadTimeout

func WithReadTimeout(d time.Duration) Option

WithReadTimeout sets the maximum duration for reading entire request including body. Default: 15 seconds. Prevents slow clients from holding connections.

WithReadTimeout은 본문을 포함한 전체 요청 읽기의 최대 기간을 설정합니다. 기본값: 15초. 느린 클라이언트가 연결을 보유하는 것을 방지합니다.

func WithRecovery

func WithRecovery(enable bool) Option

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

func WithStaticDir(dir string) Option

WithStaticDir sets the directory where static files are served from. Default: "static". Used with Static() method for file serving.

WithStaticDir은 정적 파일이 제공되는 디렉토리를 설정합니다. 기본값: "static". 파일 서빙을 위해 Static() 메서드와 함께 사용됩니다.

func WithStaticPrefix

func WithStaticPrefix(prefix string) Option

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

func WithTemplateDir(dir string) Option

WithTemplateDir sets the directory where HTML templates are stored. Default: "templates". Directory is relative to application working directory.

WithTemplateDir은 HTML 템플릿이 저장되는 디렉토리를 설정합니다. 기본값: "templates". 디렉토리는 애플리케이션 작업 디렉토리에 상대적입니다.

func WithWriteTimeout

func WithWriteTimeout(d time.Duration) Option

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:

  1. Acquire read lock (ro.mu.RLock) for thread-safe route lookup
  2. Get list of routes registered for request's HTTP method
  3. Iterate through routes in registration order
  4. 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
  5. If no routes match: Call notFoundHandler (404)
  6. 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 핸들러 호출

프로세스 흐름:

  1. 스레드 안전 라우트 조회를 위해 읽기 잠금(ro.mu.RLock) 획득
  2. 요청의 HTTP 메서드에 등록된 라우트 목록 가져오기
  3. 등록 순서대로 라우트 반복
  4. 각 라우트에 대해 요청 경로와 매칭 시도: - 매칭 성공: 매개변수 추출, Context 생성, 핸들러 호출, 반환 - 매칭 실패: 다음 라우트로 계속
  5. 라우트가 일치하지 않으면: notFoundHandler 호출(404)
  6. 읽기 잠금(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) Delete

func (sess *Session) Delete(key string)

Delete removes a value from the session. Delete는 세션에서 값을 제거합니다.

func (*Session) Get

func (sess *Session) Get(key string) (interface{}, bool)

Get retrieves a value from the session. Get은 세션에서 값을 검색합니다.

func (*Session) GetBool

func (sess *Session) GetBool(key string) bool

GetBool retrieves a bool value from the session. GetBool은 세션에서 bool 값을 검색합니다.

func (*Session) GetInt

func (sess *Session) GetInt(key string) int

GetInt retrieves an int value from the session. GetInt는 세션에서 int 값을 검색합니다.

func (*Session) GetString

func (sess *Session) GetString(key string) string

GetString retrieves a string value from the session. GetString은 세션에서 문자열 값을 검색합니다.

func (*Session) Set

func (sess *Session) Set(key string, value interface{})

Set stores a value in the session. Set은 세션에 값을 저장합니다.

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

type ValidationError struct {
	Field string
	Tag   string
	Value interface{}
}

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는 연결된 에러 메시지를 반환합니다.

type Validator

type Validator interface {
	Validate(interface{}) error
}

Validator defines the interface for custom validators. Validator는 커스텀 검증자를 위한 인터페이스를 정의합니다.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL