[TIL] Go 특강 DAY-5

드림보이즈·2023년 5월 15일

HTTP & Gin-Gonic

1. HTTP 서버 구성

go에서 기본적으로 제공하는 "net/http"를 이용해 구성 가능

package main

import {
	"net/http"
    "fmt"
}

func ping(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "pong\n")
    }
    
func main() {
	http.HandleFunc("/ping", ping)
    
    http.ListenAndServe("0.0.0.0:8080", nil)
    }

2. Gin-Gonic 웹 프레임 워크

  • http를 이용한 웹 프레임 워크
  • 대중적이고 빠르고, 쉽고, 유저 풀 넓음
  • 미들웨어 / 패닉 리버스 / Json 사용 용이 / 라우트 그루핑 / 에러 관리 / 렌더링 장점
go get "github.com/gin-gonic/gin"
import ( 
	"github.com/gin-gonic/gin" 
	"net/http" // 필수는 아니지만, 자주 활용
)

간단한 예제

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
    r.GET("/recv", func(c *gin.Context) {
    	c.JSON(200, gin.H{
        	"result" : "hello~",
            })
       })
   r.Run() // server listen on~
 }  

route

  • 편리한 라우팅 관리 지원
  • 기능, 버전, 사용자별 그룹관리 가능
func main() {
	router := gin.Default()
    
    // group : v1
    v1 := router.Group("/v1")
    {
    	v1.GET("/read", read)
        v1.POST("/submit", submit)
    }

	// group : v2
    v2 := router.Group("/v2")
    {
    	v2.POST("/login", temp4) //호출 Url : http://localhost:8080/v2/login
		v2.DELETE("/submit", temp5) //호출 Url : http://localhost:8080/v2/submit
		v2.GET("/read", temp6) //호출 Url : http://localhost:8080/v2/read
     }
     
    router.Run(":8080")
    }

middleware

여러 종류의 미들웨어 지원

  • cors
  • user 정의 미들웨어
func liteAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
		if c == nil {
			c.Abort() // 미들웨어에서 사용, 이후 요청 중지
			return
		}
		//http 헤더내 "Authorization" 폼의 데이터를 조회
		auth := c.GetHeader("Authorization")
		//실제 인증기능이 올수있다. 단순히 출력기능만 처리 현재는 출력예시
		fmt.Println("Authorization-word ", auth)

		c.Next() // 다음 요청 진행
	}
}
  • 라우팅
func index() *gin.Engine {
	r := gin.New() //gin 선언

	r.Use(gin.Logger())  //gin 내부 log, logger 미들웨어 사용 선언
	r.Use(gin.Recovery())  //gin 내부 recover, recovery 미들웨어 사용 - 패닉복구
	r.Use(CORS()) //crossdomain 미들웨어 사용 등록

	//원하는 만큼 미들웨어 추가가능
	r.GET("/bmart", aMart(), bMart) //User 지정 미들웨어

	
	route := r.Group("/route/v01", liteAuth())
	{
		route.POST("/post", post)
		route.PUT("/put", put)
		route.GET("/get", get)
		// nested group
		auth := route.Group("auth")
		auth.GET("/nest", nest)
	}
	return r
}
  • main-start
func main() {
	mapi := &http.Server{
		Addr:           config.Server.Port,
		Handler:        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)
	}
}

우아한 종료 : graceful stop

  • 모든 어플에는 할당된 작업과 메모리를 해제하고 정상적인 종료하는 과정이 필요한데 이를 ~
  • 종료 인터럽트를 받을 경우, 채널링을 통해 해당 signal에 대한 종료 처리 구현
  • 내장 패키지인 "syscall", "os/signal" 사용
  • os에서 제공하는 기본 시그널은 30가지, os마다 다르고, 확인 방법은 kill -l로 확인 가능

Signal은 운영 체제에서 프로세스에게 발생한 이벤트를 알리는 방법입니다. 이벤트는 주로 프로세스에게 외부에서 발생한 중단 요청을 나타내는 것으로, 일반적으로 사용자 또는 운영 체제 자체에서 발생할 수 있습니다.
운영 체제에서 발생한 신호는 프로세스의 실행을 변경하거나 중단하는 데 사용됩니다. 프로세스는 신호를 수신하고 이에 대응하기 위한 동작을 정의할 수 있습니다. 이를 통해 프로세스는 예기치 않은 상황에 대응하거나 정상적인 종료 절차를 수행할 수 있습니다.
일반적인 운영 체제에서 제공하는 신호 중 일부는 다음과 같습니다.

  • SIGINT: 인터럽트 신호로, 주로 사용자가 터미널에서 Ctrl+C를 누르면 발생합니다. 프로세스는 일반적으로 이 신호를 받아들여 실행을 중단하거나 정리하는 등의 작업을 수행합니다.
  • SIGTERM: 종료 신호로, 프로세스에게 정상적인 종료를 요청하는 신호입니다. 프로세스는 이 신호를 받으면 자원을 정리하고 종료할 수 있습니다.
  • SIGKILL: 강제 종료 신호로, 프로세스에게 강제로 종료하라는 신호입니다. 이 신호는 무시될 수 없으며, 프로세스는 즉시 종료됩니다.
  • SIGHUP: 터미널 연결이 끊겼을 때 발생하는 신호입니다. 주로 데몬 프로세스가 터미널과의 연결을 유지하기 위해 사용됩니다.
  • SIGUSR1, SIGUSR2: 사용자 정의 신호로, 프로세스에게 사용자가 임의로 정의한 동작을 수행하도록 할 수 있습니다.
    이러한 신호는 운영 체제에서 제공하는 Signal API를 통해 프로세스에게 전달될 수 있으며, 프로그램은 해당 신호를 수신하는 핸들러를 등록하여 적절한 동작을 수행할 수 있습니다. 이는 프로세스의 안정성과 유연성을 높이는 데 도움을 줍니다.
mapi := &http.Server{
		Addr:           config.Server.Port,
		Handler:        index(),
		ReadTimeout:    5 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
}

g.Go(func() error {
	return mapi.ListenAndServer()
    })
    
stopSig := make(chan os.Signal) // chan 선언

//해당 chan 핸들링 선언, SIGINT, SIGTERM에 대한 메시지 notify

signal.Notify(stopSig, syscall.SIGINT, syscall.SIGTERM)
<-stopSig // 메시지 등록

// 해당 context 타임아웃 설정, 5초 후 server stop
ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()
if err := mapi.Shutdown(ctx); err != nil {
	log.Fatal("server shutdown", err)
}

// catching ctx.Done(). timeout of 5 sec

select {
	case <- ctx.Done():
    	fmt.Println("timeout 5 secs")
    {
    fmt.Println("server stop")
    
profile
시리즈 클릭하셔서 카테고리 별로 편하게 보세용

0개의 댓글