Go + GORM으로 백엔드 개발을 하다 보면 다음과 같은 고민, 한 번쯤 해봤을 것이다.
- Struct 안에 JSON 배열을 저장하고 싶은데 어떻게 하지?
- Enum 타입처럼 제한된 값만 저장하고 싶은데?
- time.Time 포맷이 DB랑 안 맞아서 깨져서 나온다?
이럴 때 유용하게 쓰이는 게 GORM의 Custom Type 기능이다. 이 글에서는 JSON 필드, Enum 처리, 커스텀 Time 포맷을 예제로 하나씩 정리해보려 한다.
1. JSON 필드 매핑하기 — Slice나 Map을 JSON으로 저장하기
Go에서는 []string, map[string]string 같은 필드를 DB의 JSON 컬럼에 저장하려면 Scan()과 Value() 메서드를 직접 구현해줘야 한다.
✅ 예제: []string을 JSON으로 저장하는 커스텀 타입
type StringArray []string
func (a *StringArray) Scan(value interface{}) error {
bytes, ok := value.([]byte)
if !ok {
return fmt.Errorf("failed to scan JSON")
}
return json.Unmarshal(bytes, a)
}
func (a StringArray) Value() (driver.Value, error) {
return json.Marshal(a)
}
✅ 모델에 적용
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Hobbies StringArray `gorm:"type:json"`
}
- PostgreSQL이라면 type:jsonb
- MySQL이라면 type:json 을 지정해주면 된다.
2. Enum 타입 처리 — 제한된 값만 허용하기
Go에는 Enum이 없기 때문에 보통 string 타입을 쓰게 되는데, Custom Type으로 구현하면 코드의 의도를 명확히 할 수 있다.
✅ 예제: UserRole Enum 정의
type UserRole string
const (
Admin UserRole = "ADMIN"
Member UserRole = "MEMBER"
)
func (r *UserRole) Scan(value interface{}) error {
*r = UserRole(value.(string))
return nil
}
func (r UserRole) Value() (driver.Value, error) {
return string(r), nil
}
✅ 모델에 적용
type User struct {
ID uint `gorm:"primaryKey"`
Name string
Role UserRole `gorm:"type:varchar(20)"`
}
참고: DB에서 ENUM 타입으로 정의하면 더 안전하게 관리할 수 있다.
3. Custom Time 포맷 처리 — time.Time 포맷 맞추기
기본 time.Time은 RFC3339 형식인데, DB나 프론트에서 yyyy-MM-dd HH:mm:ss 포맷을 요구할 때가 많다. 이럴 때도 Custom Type으로 포맷을 컨트롤할 수 있다.
✅ 예제: CustomTime 타입 정의
type CustomTime struct {
time.Time
}
const layout = "2006-01-02 15:04:05"
func (ct *CustomTime) Scan(value interface{}) error {
switch v := value.(type) {
case time.Time:
ct.Time = v
case []byte:
t, err := time.Parse(layout, string(v))
if err != nil {
return err
}
ct.Time = t
}
return nil
}
func (ct CustomTime) Value() (driver.Value, error) {
return ct.Format(layout), nil
}
func (ct CustomTime) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("\"%s\"", ct.Format(layout))), nil
}
func (ct *CustomTime) UnmarshalJSON(b []byte) error {
str := strings.Trim(string(b), "\"")
t, err := time.Parse(layout, str)
if err != nil {
return err
}
ct.Time = t
return nil
}
✅ 모델에 적용
type Event struct {
ID uint `gorm:"primaryKey"`
Title string
StartTime CustomTime `gorm:"column:start_time"`
}
'개발 > Go' 카테고리의 다른 글
[Gin] GORM 필드 유효성 검사 + Validator 연동하기 (88) | 2025.06.27 |
---|---|
[Go] Go에서 싱글턴 패턴 구현하기 - 실전 예제로 쉽게 이해하기 (58) | 2025.06.20 |
[Gin] GORM에서 CQRS 아키텍처 구현해보기 (85) | 2025.06.18 |
[Gin] GORM + Redis 캐싱 전략 - DB 부하 줄이는 실전 캐싱 (75) | 2025.06.16 |
[Gin] GORM 디버깅 & 에러 핸들링 완전정복 (59) | 2025.06.13 |