TIL 09 - HTTP & Gin-Gonic 2(Golang)

프동프동·2023년 2월 10일
0

TIL

목록 보기
9/46
post-thumbnail

Toy Project : Gin을 활용한 CRUD

Tucker Go 언어 프로그래밍 예제를 통해 학습하였습니다.

http 패키지로만 구성되어 있는 예제를 Gin 패키지 이해도를 위해 모두 변경하여 만들어봤습니다.

학습 목표

  • Gin 패키지에 대한 이해도를 높이기 위함
  • 기본적인 CRUD 학습

사용 스킬

  • Back
    • Go
      • gin-gonic
  • Testing Tool
    • Postman

기능

// User Struct
type User struct {
	ID        int       `json:"id"`
	FirstName string    `json:"first_name"`
	LastName  string    `json:"last_name"`
	Email     string    `json:"email"`
	CreatedAt time.Time `json:"create_at"`
}

POST(Create) : User 등록하기

GET(Read) : 등록된 모든 User 정보 가져오기

PUT(Update) : 등록된 User 정보 수정하기

DELETE(Delete) : 등록된 유저 삭제하기

GET(Read) : id 값으로 특정 User 정보 가져오기

  • 삭제된 데이터 가져오기

  • 등록된 데이터 가져오기

실행 코드

디렉터리 구조

CRUD_gin - sub - sub.go
         - main.go

main.go

package main

import (
	"CRUD_gin/sub"
	"fmt"
	"net/http"
	"time"
	"golang.org/x/sync/errgroup"
)

var (
	g errgroup.Group
)

func main() {
	mapi := &http.Server{
		Addr:           ":8080",
		Handler:        sub.Index(),
		ReadTimeout:    5 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	g.Go(func() error {
		return mapi.ListenAndServe()
	})
	if err := g.Wait(); err != nil {
		fmt.Println(err)
	}
}

sub.go

package sub

import (
	"fmt"
	"net/http"
	"strconv"
	"time"
	"github.com/gin-gonic/gin"
)

// in-memory로 관리하기 위함
var userMap map[int]*User

// id 처리를 위한 변수
var lastId int

// User Struct
type User struct {
	ID        int       `json:"id"`
	FirstName string    `json:"first_name"`
	LastName  string    `json:"last_name"`
	Email     string    `json:"email"`
	CreatedAt time.Time `json:"create_at"`
}

// cross domain을 위해 사용
func CORS() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
		c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
		//허용할 header 타입에 대해 열거
		c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, X-Forwarded-For, Authorization, accept, origin, Cache-Control, X-Requested-With")
		//허용할 method에 대해 열거
		c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
		if c.Request.Method == "OPTIONS" {
			c.AbortWithStatus(204)
			return
		}
		c.Next()
	}
}

// GET : User 리스트 조회
func usersHandler(c *gin.Context) {
	// 만약 등록된 유저가 한명도 없을 경우
	if len(userMap) == 0 {
		if err := c.ShouldBindUri(&userMap); err != nil {
			fmt.Printf("error - %+v \n", err)
		}
		fmt.Printf("No User - %+v \n", userMap)
		return
	}
	// 등록된 유저가 있을 경우
	users := []*User{}
	for _, u := range userMap {
		users = append(users, u)
	}

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

// POST
func createUserHandler(c *gin.Context) {
	// 유저 객체를 저장하기 위한 구조체 선언
	user := new(User)
	if err := c.ShouldBindJSON(&user); err != nil {
		fmt.Printf("error - %+v \n", err)
		return
	}
	lastId++
	user.ID = lastId
	user.CreatedAt = time.Now()
	userMap[user.ID] = user

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

// PUT
func updateUserHandler(c *gin.Context) {
	// 클라이언트에서 받은 내용을 updataUser 구조체에 바인딩한다.
	updateUser := new(User)
	if err := c.ShouldBindJSON(&updateUser); err != nil {
		fmt.Printf("error - %+v \n", err)
		return
	}
	// 클라이언트에서 받은 ID값을 가진 유저가 기존 메모리에 등록되어 있는지 확인
	user, ok := userMap[updateUser.ID]
	if !ok {
		c.String(http.StatusOK, "No User ID : %d", updateUser.ID)
		return
	}

	// 업데이트된 항목만 변경
	if updateUser.FirstName != "" {
		user.FirstName = updateUser.FirstName
	}
	if updateUser.LastName != "" {
		user.LastName = updateUser.LastName
	}
	if updateUser.Email != "" {
		user.Email = updateUser.Email
	}

	// c.String(http.StatusOK, "user : %s", user)
	c.JSON(http.StatusCreated, gin.H{
		"status": "ok",
		"data":   user,
	})
}

func getUserHandler(c *gin.Context) {
	tempId := c.Param("id")
	id, err := strconv.Atoi(tempId)
	if err != nil {
		fmt.Printf("error - %+v \n", err)
		c.JSON(http.StatusBadRequest, gin.H{
			"status": "This is not a valid value.",
		})
		return
	}

	user, ok := userMap[id]
	if !ok {
		c.JSON(http.StatusOK, gin.H{
			"status": "No User Id",
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"status": "ok",
		"user":   user,
	})
}

// DELETE
func deleteUserHandler(c *gin.Context) {

	// id, err := strconv.Atoi(tempId)

	tempId := c.Param("id")
	id, err := strconv.Atoi(tempId)
	if err != nil {
		fmt.Printf("error - %+v \n", err)
		c.JSON(http.StatusBadRequest, gin.H{
			"status": "This is not a valid value.",
		})
		return
	}
	_, ok := userMap[id]
	if !ok {
		c.JSON(http.StatusOK, gin.H{
			"status": "No User Id",
		})
		return
	}
	delete(userMap, id)
	c.JSON(http.StatusOK, gin.H{
		"status": "Deleted User ",
		"id":     id,
	})
}

// GET
func Index() *gin.Engine {
	// in memory 사용
	userMap = make(map[int]*User)
	// user 마다 다른 아이디를 생성해주기 위한 변수
	lastId = 0

	// gin 인스턴스 생성
	router := gin.New()

	// logger 미들웨어 사용
	router.Use(gin.Logger())
	// Recovery 미들웨어 사용
	router.Use(gin.Recovery())
	// CORS 사용
	router.Use(CORS())

	router.GET("/users", usersHandler)
	router.POST("/users", createUserHandler)
	router.PUT("/users", updateUserHandler)
	router.DELETE("/users/:id", deleteUserHandler)
	router.GET("/users/:id", getUserHandler)
	return router
}
profile
좋은 개발자가 되고싶은

0개의 댓글