본문 바로가기
Go

[go/gin/gorm] Basic REST API 만들기 튜토리얼 (4)

by weero 2021. 12. 21.

 

이전 포스트 (1) : https://dev2som.tistory.com/152

이전 포스트 (2) : https://dev2som.tistory.com/153

이전 포스트 (3) : https://dev2som.tistory.com/154

 

저번 편에 이어서 gorm 패키지를 이용해 DB에 데이터를 바인딩할 수 있는 REST API를 만들어보려 한다.

 

 

 

우선 패키지 구조

go project를 만들어주고 다음 구조로 디렉터리와 go 파일들을 생성한다.

  • models 패키지 : DB에 저장될 데이터의 구조체와 DB에 연결하는 코드가 들어있다.
  • controllers 패키지 : HTTP 요청에 따라 호출되는 핸들러 함수들이 들어있다.

 

 

 

main.go

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/___/restapi/controllers"
	"github.com/___/restapi/models"
)

func main() {
	r := gin.Default()
	models.ConnectDatabase()

	r.GET("/info", controllers.ReadInfo)
	r.POST("/info", controllers.CreateInfo)
	r.PUT("/info/:id", controllers.UpdateInfo)
	r.DELETE("/info/:id", controllers.DeleteInfo)

	r.Run("localhost:8080")
}

서버를 설정하는 메인 파일이다. 

main() 함수를 실행할 때 트리거 될 함수들을 선언한다. (이 함수에 대한 내용은 https://dev2som.tistory.com/154 를 참고)

 

 

 

models 패키지의 info.go

package models

type Info struct {
	Id    int	`json:"id" gorm:"primary_key"`
	Name  string	`json:"name"`
	Email string	`json:"email"`
}

DB에 저장될 테이블의 컬럼 구조가 들어있는 구조체이다.

DB에 테이블로 저장될 때 구조체는 테이블명(소문자 복수형)으로 변경되고, 필드명은 컬럼명(소문자, snake case)로 변경된다.

여기에서 필드 Id는 테이블에서 primary key 가 된다.

 

 

 

models 패키지의 setup.go

package models

import (
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	_ "github.com/jinzhu/gorm/dialects/mysql"

	"github.com/jinzhu/gorm"
)

var DB *gorm.DB

func ConnectDatabase() {
	var err error
	DB, err = gorm.Open("mysql", "유저명:비밀번호@tcp(주소:포트)/test?charset=utf8")
	if err != nil {
		panic(err)
	}

	// table 명은 구조체명의 복수형으로 자동 명명된다.
	// column 명은 구조체 필드의 소문자 snake case의 이름을 가진다.
	DB.AutoMigrate(&Info{})
    
	DB.DB().SetMaxIdleConns(10)	// idle connection pool(유휴 연결 풀)의 최대 수 설정
	DB.DB().SetMaxOpenConns(100)	// 데이터베이스에 대한 열린 연결의 최대 수 설정
}

DB와 연결하는 코드이다. gorm.Open()을 통해 DB의 정보를 불러오고, 이것을 전역 변수인 *gorm.DB DB  변수에 저장했다.

전역변수 DB는 첫글자가 대문자이므로 다른 패키지에서도 참조할 수 있다.

 

gorm.DB.AutoMigrate(구조체 주소)는 해당 구조체에 관련된 테이블이 생성되어 있지 않다면 자동으로 테이블을 만들어준다.

 

 

 

controller 패키지의 info.go

package controllers

import (
	"github.com/gin-gonic/gin"
	"github.com/___/restapi/models"
	"net/http"
)

type CreateInput struct {
	Id    int    `json:"id" binding:"required"`
	Name  string `json:"name" binding:"required"`
	Email string `json:"email" binding:"required"`
}

type UpdateInput struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

func ReadInfo(c *gin.Context) {

	// 전체 읽기
	var infoList []models.Info
	models.DB.Find(&infoList)

	// 특정 컬럼 읽기
	//var info models.Info
	//models.DB.First(&info, 3) // primary key 기준으로 info 찾기
	//models.DB.First(&info, "id = ?", 5) // id가 5인 info 찾기


	if infoList == nil {
		c.JSON(http.StatusNoContent, nil)

		return
	}

	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
		"data":   infoList,
	})
}

func CreateInfo(c *gin.Context) {

	input := CreateInput{}
	if err := c.ShouldBind(&input); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": err.Error(),
		})
		return
	}

	// 생성하기
	info := models.Info{Id: input.Id, Name: input.Name, Email: input.Email}
	models.DB.Create(&info)

	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
		"data":   input,
	})
}

func UpdateInfo(c *gin.Context) {
	var info models.Info
	if err := models.DB.Where("id = ?", c.Param("id")).First(&info).Error; err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Record not found!",
		})
		return
	}

	input := UpdateInput{}

	if err := c.ShouldBind(&input); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"status": err.Error(),
		})
		return
	}

	// 수정하기
	models.DB.Model(&info).Updates(input)
	// 수정하기 - 특정 필드 수정하기
	//models.DB.Model(&info).Update("Name", "Lee")
	//models.DB.Model(&info).Update(models.Info{Name: "Lee", Email: "rhdtha01@gmail.com"})
	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
		"data":   info,
	})

}

func DeleteInfo(c *gin.Context) {
	var info models.Info
	if err := models.DB.Where("id = ?", c.Param("id")).First(&info).Error; err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Record not found!",
		})
		return
	}

	// 삭제하기
	models.DB.Delete(&info)
	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
		"data":   info,
	})
}

각 url과 HTTP Method에 따른 핸들러 함수들이 모여있는 파일이다. 각 동작에 대해서는 주석으로 설명을 첨부해두었다.

 

 

 

실행 결과

처음 서버를 동작시키고 테이블을 확인해보면,

아래처럼 구조체 Info에 대한 테이블이 생성되어 있는 것을 볼 수 있다.

 

이후 차례대로 GET, POST, PUT, DELETE 연산을 진행하면서 확인해본 결과 잘 동작되는 것을 볼 수 있다.

 

로그 기록