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