728x90

python에 이어서 orm의 맛을 본 저는 go에서도 sqlx을 걷어내고 orm 을 도입하기로 했습니다.

 

github 에서 압도적인 스타수를 자랑하고 엄청난 기능을 지원해줘서 도입하게 되었습니다.

go 에서 구조체와 마샬링 언마샬링과의 궁합도 찰떡이고 에러 핸들링 및 트랜잭션 또한 매우 쉽게 할수 있습니다.

 

Connection

.env 파일 정의하기

SERVER_ADDRESS=0.0.0.0
SERVER_PORT=3070
DB_USER_NAME=
DB_USER_PASSWORD=
DB_NAME=postgres
DB_PORT=5432
DB_HOST=

본인의 정보에 맞게 입력해주시면 됩니다

 

 

db/init.go

package db

import (
	"device-scheduler/logger"
	"fmt"
	"os"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

func GetDbClient() *gorm.DB {
	dbUser := os.Getenv("DB_USER_NAME")
	dbPasswd := os.Getenv("DB_USER_PASSWORD")
	dbHost := os.Getenv("DB_HOST")
	dbPort := os.Getenv("DB_PORT")
	dbName := os.Getenv("DB_NAME")
	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s TimeZone=Asia/Seoul", dbHost, dbUser, dbPasswd, dbName, dbPort)
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("Database not connected error")
	}
	logger.Info(fmt.Sprintf("Dabase connect success || DB -> %s:%s", dbHost, dbPort))
	return db
}

 

db 를 연결하고 연결에 실패하면 panic을 발생하여 앱을 종료합니다

 

cmd/routes.go

package main

import (
	"device-scheduler/db"
	"device-scheduler/domain"
	"device-scheduler/internal/handlers"
	"device-scheduler/service"
	"net/http"

	"github.com/bmizerany/pat"
)

// routes defines the application routes
func routes() http.Handler {
	mux := pat.New()
	dbClient := db.GetDbClient()
	deviceRepositoryDb := domain.NewDeviceRepositoryDb(dbClient)
	dh := handlers.DeviceHandler{service.NewDeviceService(deviceRepositoryDb)}
	mux.Get("/ws", http.HandlerFunc(dh.WsEndpoint))

	return mux
}

 

dbClient를 넘겨받고 저장소를 만들어 서비스에 넘겨줍니다.

이렇게 한이유는 추후에 테스트코드를 작성할때 mock객체를 만들어 테스트 하기 위함입니다.

 

Define Model

package domain

import (
	"device-scheduler/errs"
)

// Device shop_device table
type Device struct {
	ShopDeviceId  int64  `gorm:"column:shop_device_id"`
	ShopId        int    `gorm:"column:shop_id"`
	PanelId       string `gorm:"column:panel_id"`
	SettopId      string `gorm:"column:settop_id"`
	ShopDisplayId int    `gorm:"column:shop_display_id"`
	Status        int    `gorm:"column:status"`
	SortNo        int    `gorm:"column:sort_no"`
	VideoURL      string `gorm:"column:whole_display_path"`
	ThumbnailURL  string `gorm:"column:thumbnail_path"`
}

type DeviceRepository interface {
	FindById(deviceId int) (*Device, *errs.AppError)
	FindAllActive() ([]Device, *errs.AppError)
}

컬럼명이 다른경우 직접 명시해줄수 있습니다. 인덱스 및 pk 값도 설정할 수 있습니다. 더 자세한 내용은 공식문서를 참고해주세요!

 

Example Query & Error handling

 

func (d DeviceRepositoryDb) FindById(deviceId int) (*Device, *errs.AppError) {
	var device Device
	if err := d.Client.Table("device").Where("id = ? AND path IS NOT NULL", deviceId).First(&device).Error; err != nil {
		logger.Error("Error while find by device id " + err.Error())
		return nil, errs.NewUnexpectedError("Unexpected database error")
	}
	return &device, nil
}


func (d DeviceRepositoryDb) FindAllActive() ([]Device, *errs.AppError) {
	devices := make([]Device, 0)
	if err := d.Client.Table("device").Where("path IS NOT NULL AND NOT status = 0").Find(&devices).Error; err != nil {
		logger.Error("Error while find all active device " + err.Error())
		return nil, errs.NewUnexpectedError("Unexpected database error")
	}
	for _, r := range devices {
		fmt.Println(r)
	}

	return devices, nil
}

 

Table 명을 직접 명시할 수 있고 슬라이스를 만들어 여러개의 Row를 할당 할수 있습니다. Find 절이 끝나는 곳에서 .Error를 하게 되었을땐 아무것도 찾지 못하면 Error 를 내뱉게 됩니다.

 

 

https://gorm.io/index.html

 

GORM

The fantastic ORM library for Golang aims to be developer friendly.

gorm.io

 

728x90