GORM은 Go에서 가장 널리 쓰이는 ORM이지만, 아무 설정 없이 쓰다 보면 쿼리가 많아지고 성능이 떨어질 수 있다.
이번 글에서는 GORM에서 성능을 높이기 위한 Preload, Select, Index 전략들을 정리해보자.
📌 목차
- N+1 문제와 Preload
- 필요한 컬럼만 조회하기 — Select
- Index 전략
- 정리
1. N+1 문제와 Preload
🤔 N+1 문제란?
예를 들어 게시글(Post)과 작성자(User) 관계가 있을 때, 게시글 10개를 조회하면서 각 게시글의 작성자를 따로 조회하면 1 + N 쿼리가 발생한다.
type Post struct {
ID uint
Title string
UserID uint
User User
}
// 잘못된 예 (N+1 문제 발생)
var posts []Post
db.Find(&posts) // 1번 쿼리
for _, post := range posts {
db.Model(&post).Association("User").Find(&post.User) // N번 쿼리
}
✅ 해결: Preload 사용
// Preload 사용 (1번의 조인 쿼리로 해결)
db.Preload("User").Find(&posts)
- GORM은 내부적으로 LEFT JOIN이 아닌 별도의 SELECT 쿼리로 Preload를 실행함
- 관계 이름이 정확히 일치해야 한다 (User vs user 구분)
⛏️ 조건부 Preload
db.Preload("User", "role = ?", "admin").Find(&posts)
2. 필요한 컬럼만 조회하기 - Select
GORM 기본 설정은 SELECT *로 모든 컬럼을 조회한다.
하지만 실무에서는 정말 필요한 필드만 조회하는 게 훨씬 빠르고 가볍다.
🎯 기본 사용
db.Select("id", "name").Find(&users)
🧩 Struct도 가능
type SlimUser struct {
ID uint
Name string
}
var users []SlimUser
db.Model(&User{}).Select("id", "name").Scan(&users)
🚫 Tip: Preload + Select는 주의
Preload된 모델에는 Select 옵션이 적용되지 않음.
따라서 하위 모델에 대해서도 Select 제어가 필요하면 Joins() 등을 사용해야 함.
3. Index 전략
GORM은 마이그레이션 시 인덱스를 자동 생성할 수 있고, 조회 성능 향상에 매우 중요하다.
🔧 단일 필드 인덱스
type User struct {
ID uint
Email string `gorm:"index"` // index 생성
}
🧱 복합 인덱스
type Order struct {
UserID uint `gorm:"index:idx_user_product"`
ProductID uint `gorm:"index:idx_user_product"`
}
- idx_user_product라는 이름의 복합 인덱스 생성됨
- 조건이 정확히 맞지 않으면 인덱스가 잘 안 쓰이므로 쿼리 구조도 중요
🔍 유니크 인덱스
Username string `gorm:"uniqueIndex"`
⚠️ 주의: 자동 마이그레이션은 인덱스를 변경하지 않음
기존 인덱스가 있으면 자동으로 수정되지 않으므로, 필요시 수동으로 DROP INDEX 후 재생성 필요
🧠 정리
전략 | 핵심 내용 |
Preload | N+1 문제 해결, 연관 모델 미리 로드 |
Select | 필요한 컬럼만 조회해서 속도와 메모리 절약 |
Index | 조회 성능 향상, 조건에 맞는 인덱스 설계 중요 |
'개발 > Go' 카테고리의 다른 글
[Gin] GORM에서 안전한 쿼리 작성법 - SQL 인젝션 방지와 Named Parameter 전략 (69) | 2025.06.09 |
---|---|
[Gin] GORM 마이그레이션 전략 - 자동 vs 수동 관리, 안전하게 스키마 관리하기 (35) | 2025.06.06 |
[Gin] GORM 고급 기능 완전 정복 - Soft Delete, Hook, 트랜잭션까지! (99) | 2025.05.28 |
[Gin] GORM 관계 설정 완전 정복 - 1:N, N:1, N:M 예제로 배우기 (60) | 2025.05.26 |
[Gin] Go + Gin + GORM으로 백엔드 만들기 (기초부터 CRUD까지) (41) | 2025.05.24 |