목차


1.서론

GORM은 Go에서 널리 사용되는 ORM(Object Relational Mapping) 라이브러리로, SQL을 직접 작성하지 않아도 데이터베이스 작업을 손쉽게 처리할 수 있습니다. 하지만 복잡한 쿼리나 예기치 못한 에러가 발생했을 때는 디버깅이 필수적입니다. GORM의 디버깅 기법을 활용하면 실행되는 SQL과 에러 정보를 쉽게 파악할 수 있어 문제 해결이 수월해집니다. 이 글에서는 GORM 디버깅 기법 네 가지를 예제와 함께 소개합니다.


2.GORM 디버깅 기법

2.1 Debug 모드 활성화

GORM에서는 Debug() 메서드를 사용해 SQL 쿼리 로그를 출력할 수 있습니다. 디버깅을 시작할 때 가장 간단하고 강력한 방법입니다. 특정 쿼리에서 db.Debug()를 직접 호출하거나, GORM 객체를 설정할 때 db = db.Debug()로 기본 디버깅 모드를 활성화할 수 있습니다.

//예시 코드
package main

import (
 "gorm.io/driver/sqlite"
 "gorm.io/gorm"
)

type User struct {
 ID   uint
 Name string
 Age  int
}

func main() {
 db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})

 // 특정 쿼리에만 Debug 활성화
 db.Debug().Find(&[]User{})

 // 전체 작업에서 Debug 활성화
 db = db.Debug()
 db.Create(&User{Name: "Alice", Age: 25})
 db.First(&User{})
}
--출력 결과 (콘솔)
SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL
INSERT INTO `users` (`name`,`age`) VALUES ("Alice",25)
SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1

2.2 SQL 로그 직접 확인

Logger 설정을 통해 SQL 로그를 더 정교하게 관리할 수 있습니다. 기본 로그 수준을 조정하면 필요한 정보만 출력할 수 있습니다.

// 예시 코드
package main

import (
 "log"
 "time"

 "gorm.io/driver/sqlite"
 "gorm.io/gorm"
 "gorm.io/gorm/logger"
)

func main() {
 newLogger := logger.New(
  log.New(log.Writer(), "\r\n", log.LstdFlags), // 로그 출력 설정
  logger.Config{
   SlowThreshold: time.Second,   // 느린 쿼리 기준
   LogLevel:      logger.Warn,   // Warn 레벨 이상의 로그 출력
   Colorful:      true,          // 로그 색상 출력 여부
  },
 )

 db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{
  Logger: newLogger,
 })

 var user User
 db.First(&user)
}
출력 결과 (콘솔)
[2024-11-27 12:34:56] [WARN] Slow SQL >= 1s
[1.000ms] SELECT * FROM `users` WHERE `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1
package logger

type Config struct {
 SlowThreshold time.Duration // 느린 쿼리 기준 시간
 LogLevel      logger.LogLevel // 로그 출력 레벨
 Colorful      bool // 로그 색상 출력 여부
 IgnoreRecordNotFoundError bool // 레코드 없음 에러 무시 여부
 ParameterizedQueries bool // 매개변수화된 쿼리 사용 여부
}

2.3 에러 핸들링

GORM에서는 .Error 필드를 사용해 쿼리 실행 중 발생한 에러를 감지할 수 있습니다. 이를 통해 SQL 실행 실패의 원인을 추적하거나, 데이터베이스의 특정 에러 코드를 기반으로 문제를 세부적으로 처리할 수 있습니다.

일반적인 Error 핸들링

쿼리 실행 결과에서 발생한 에러를 추적하는 기본적인 방법은 아래와 같습니다.

// 예시 코드

package main

import (
 "fmt"
 "gorm.io/driver/sqlite"
 "gorm.io/gorm"
)

func main() {
 db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})

 var user User
 result := db.First(&user, "name = ?", "nonexistent")

 if result.Error != nil {
  fmt.Println("Error occurred:", result.Error)
 } else {
  fmt.Println("User found:", user)
 }
}
출력 결과 (콘솔)
Error occurred: record not found

에러 코드 처리

데이터베이스는 종종 에러와 함께 특정 에러 코드를 반환합니다. 이 코드는 데이터베이스 제약 조건 위반, 연결 문제, SQL 구문 오류 등의 원인을 나타냅니다. GORM에서 이러한 에러 코드를 처리하려면 데이터베이스에서 반환한 에러를 파싱하고 코드를 추출해야 합니다.

// MySQL 에러 코드 처리 예제
package main

import (
 "fmt"

 "github.com/go-sql-driver/mysql"
 "gorm.io/driver/mysql"
 "gorm.io/gorm"
)

func main() {
 dsn := "user:password@tcp(127.0.0.1:3306)/dbname"
 db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})

 newRecord := User{Name: "Alice", Age: 25}
 result := db.Create(&newRecord)

 if result.Error != nil {
  if mysqlErr, ok := result.Error.(*mysql.MySQLError); ok {
   switch mysqlErr.Number {
   case 1062: // 중복 항목 에러 코드
    fmt.Println("Duplicate entry error")
   default:
    fmt.Printf("MySQL Error: %d, Message: %s\n", mysqlErr.Number, mysqlErr.Message)
   }
  } else {
   fmt.Println("Other error:", result.Error)
  }
 }
}

TranslateError를 통해 일반화된 에러로 변환

GORM은 TranslateError 옵션을 활성화하면 데이터베이스 별도로 발생하는 에러를 GORM의 일반화된 에러로 변환해줍니다. 이를 통해 데이터베이스와 상관없이 에러를 일관되게 처리할 수 있습니다.

// PostgreSQL 에러 변환 예제

package main

import (
 "fmt"
 "gorm.io/driver/postgres"
 "gorm.io/gorm"
)

func main() {
 dsn := "host=localhost user=username password=secret dbname=test port=5432 sslmode=disable"
 db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
  TranslateError: true, // 에러 변환 활성화
 })
 if err != nil {
  fmt.Println("Failed to connect to database:", err)
  return
 }

 var user User
 result := db.First(&user, "id = ?", 123)
 if result.Error != nil {
  fmt.Println("Translated error:", result.Error)
 }
}
출력 결과 (콘솔)
존재하지 않는 데이터 조회 시

Translated error: record not found
PostgreSQL 연결 오류 발생 시 (예: 잘못된 비밀번호)

Failed to connect to database: pq: password authentication failed for user "username"

정리

GORM의 에러 핸들링 기능은 단순한 에러 감지에서부터 데이터베이스별 에러 코드 처리, 그리고 Translated Error 로의 변환까지 다양한 수준의 디버깅을 지원합니다. 이를 통해 데이터베이스 작업 중 발생하는 문제를 빠르게 파악하고 해결할 수 있습니다.

이러한 기능들을 조합해 더 안정적이고 효율적인 데이터베이스 처리를 구현할 수 있습니다. 공식문서


2.4 Hooks를 활용한 흐름 분석

GORM은 데이터 생성, 업데이트, 삭제 시 특정 로직을 추가할 수 있는 Hook 기능을 제공합니다. 이를 활용해 데이터 흐름을 추적하고 문제를 파악할 수 있습니다.

//예시 코드
package main

import (
 "log"
 "gorm.io/driver/sqlite"
 "gorm.io/gorm"
)

type User struct {
 ID   uint
 Name string
 Age  int
}

func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
 log.Println("Before creating user:", u.Name)
 return
}

func main() {
 db, _ := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})

 db.Create(&User{Name: "Alice", Age: 25})
}
출력 결과 (콘솔)
Before creating user: Alice

3.결론

GORM 디버깅 기법은 SQL 쿼리와 데이터 흐름을 파악하는 데 필수적인 도구입니다.

이런 디버깅 기법을 적극 활용하면 데이터베이스 작업 중 발생하는 문제를 빠르고 정확히 해결할 수 있습니다.


2024-11-27
카테고리로 돌아가기 ↩