Files
stocksearch/services/session_service.go
2026-03-31 19:32:59 +09:00

76 lines
1.7 KiB
Go

package services
import (
"crypto/rand"
"encoding/hex"
"sync"
"time"
)
// SessionService 인메모리 세션 스토어
type SessionService struct {
sessions sync.Map // sessionID → time.Time(만료시각)
ttl time.Duration // 세션 유효 기간
}
var sessionServiceInstance *SessionService
var sessionServiceOnce sync.Once
// GetSessionService 세션 서비스 싱글톤 반환
func GetSessionService() *SessionService {
sessionServiceOnce.Do(func() {
sessionServiceInstance = NewSessionService()
})
return sessionServiceInstance
}
// NewSessionService 세션 서비스 초기화 (TTL 24시간)
func NewSessionService() *SessionService {
svc := &SessionService{ttl: 24 * time.Hour}
go svc.cleanup()
return svc
}
// Create 새 세션 생성 → sessionID(UUID) 반환
func (s *SessionService) Create() string {
b := make([]byte, 16)
_, _ = rand.Read(b)
id := hex.EncodeToString(b)
s.sessions.Store(id, time.Now().Add(s.ttl))
return id
}
// Validate 세션 유효성 검사 (만료 시 자동 삭제)
func (s *SessionService) Validate(id string) bool {
val, ok := s.sessions.Load(id)
if !ok {
return false
}
expiry, ok := val.(time.Time)
if !ok || time.Now().After(expiry) {
s.sessions.Delete(id)
return false
}
return true
}
// Delete 세션 삭제 (로그아웃)
func (s *SessionService) Delete(id string) {
s.sessions.Delete(id)
}
// cleanup 만료된 세션을 주기적으로 정리 (1시간 주기)
func (s *SessionService) cleanup() {
ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
for range ticker.C {
now := time.Now()
s.sessions.Range(func(key, val any) bool {
if expiry, ok := val.(time.Time); ok && now.After(expiry) {
s.sessions.Delete(key)
}
return true
})
}
}