package database import ( "fmt" "log" "os" "time" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" "sensor-server/internal/models" ) var DB *gorm.DB // InitDatabase 데이터베이스 초기화 func InitDatabase() error { dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable", getEnv("DB_HOST", "localhost"), getEnv("DB_USER", "postgres"), getEnv("DB_PASSWORD", "password"), getEnv("DB_NAME", "sensor_db"), getEnv("DB_PORT", "5432"), ) var err error DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), }) if err != nil { return fmt.Errorf("failed to connect to database: %v", err) } // 안전한 마이그레이션 수행 err = safeMigrate() if err != nil { return fmt.Errorf("failed to migrate database: %v", err) } // Connection pool 설정 sqlDB, err := DB.DB() if err != nil { return fmt.Errorf("failed to get underlying sql.DB: %v", err) } sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) log.Println("Database initialized successfully") return nil } // safeMigrate 안전한 마이그레이션 수행 func safeMigrate() error { // 기존 제약조건 확인 및 정리 if err := cleanupConstraints(); err != nil { log.Printf("Warning: failed to cleanup constraints: %v", err) } // AutoMigrate 수행 return DB.AutoMigrate(&models.SensorReading{}, &models.Device{}) } // cleanupConstraints 기존 제약조건 정리 func cleanupConstraints() error { // devices 테이블의 device_id unique 제약조건 확인 var count int64 err := DB.Raw(` SELECT COUNT(*) FROM information_schema.table_constraints WHERE table_name = 'devices' AND constraint_name = 'uni_devices_device_id' `).Scan(&count).Error if err != nil { return err } // 제약조건이 존재하면 삭제 if count > 0 { err = DB.Exec(`ALTER TABLE devices DROP CONSTRAINT IF EXISTS uni_devices_device_id`).Error if err != nil { return err } log.Println("Cleaned up existing constraint: uni_devices_device_id") } return nil } // GetDB 데이터베이스 인스턴스 반환 func GetDB() *gorm.DB { return DB } // getEnv 환경변수 조회 (기본값 포함) func getEnv(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue }