반응형

Go의 인기 Web Framework 'Gin' 


해당 튜토리얼은 'Building Go Web Applications and Microservices Using Gin'의 내용을 번역하며 개인적으로 정리한 것입니다.


이번 시간에는 각 기사(Article)를 선택할 때 표시될 수 있는 핸들러와 템플릿을 추가하도록 하겠습니다.

 

1. 라우터 설정

이전 라우터와 동일한 방식으로 단일 기사에 대한 요청을 처리하기 위해 새 라우터를 설정하도록 합니다. 모든 기사들의 핸들러는 동일하겠지만, 각 기사별 URL은 다를 수 있다는 점을 고려해야 합니다. Gin을 사용하면 다음과 같이 경로 매개변수를 정의해서 해당 조건을 처리할 수 있습니다.

 

router.GET("/article/view/:article_id", getArticle)

 

이 라우터는 위 경로와 맞는 요청에 대해 라우터를 일치시키고, 라우터의 마지막 부분 값을 article_id 에 저장하도록 합니다. 이 라우터는 getArticle을 정의합니다.

 

이로 인해 업데이트된 routes.go 파일의 코드는 다음과 같습니다.

 

▶ routes.go

 

// routes.go

package main

func initializeRoutes() {

  // 인덱스 라우터 처리(Handle)
  router.GET("/", showIndexPage)

  // /article/view/some_article_id 부분에 대한 GET 요청 처리
  router.GET("/article/view/:article_id", getArticle)

}

 

2. 뷰 템플릿 작성

 

우리는 templates/article.html 이라는 이름으로 새로운 템플릿을 생성할 필요가 생겼습니다. 해당 파일은 index.html 템플릿과 유사한 방법으로 만들 수 있습니다. 다만 이 경우 기사 목록이 포함된 Payload 변수 대신 단일의 기사가 포함될 것입니다.

 

▶ article.html

 

<!--article.html-->

<!-- header.html 템플릿을 이 위치에 포함시킵니다 -->
{{ template "header.html" .}}

<!--기사 제목을 표시합니다-->
<h1>{{.payload.Title}}</h1>

<!--기사 본문 내용을 표시합니다-->
<p>{{.payload.Content}}</p>

<!-- footer.html 템플릿을 이 위치에 포함시킵니다 -->
{{ template "footer.html" .}}

 

3. 라우터 핸들러 생성

기사 페이지 핸들러 getArticle은 다음의 작업을 수행합니다.

 

3-1. 표시할 기사의 ID를 추출합니다

알맞는 기사를 가져와 표시하려면 먼저 Context에서 ID를 추출해야 합니다. 추출하는 방법은 다음과 같습니다.

 

c.Param("article_id")

 

여기서의 c 는 Gin을 사용할 때 라우터 핸들러의 매개변수인 Gin Context를 의미합니다.

 

3-2. 기사 가져오기

이 부분은 models.article.go 파일에 정의된 getArticleByID() 함수를 통해 수행할 수 있습니다.

 

article, err := getArticleByID(articleID)

 

models.article.go에 포함된 getArticleByID 함수는 다음과 같습니다

 

func getArticleByID(id int) (*article, error) {
  for _, a := range articleList {
    if a.ID == id {
      return &a, nil
    }
  }
  return nil, errors.New("기사를 찾을 수 없습니다")
}

 

이 함수는 기사 목록을 반복하고 ID가 전달된 ID와 일치하는 기사를 반환합니다. 일치하는 기사가 없으면 오류를 반환합니다.

 

3-3. 기사를 전달하는 article.html 템플릿을 렌더링합니다

다음과 같은 코드를 사용해 수행할 수 있습니다.

 

c.HTML(
    // ----- HTTP Status를 200(OK)로 설정합니다 ------ //
    http.StatusOK,
    // ----- article.html 템플릿을 사용합니다 ------ //
    "article.html",
    // ---- 페이지에서 사용하는 데이터를 전달합니다 ----- //
    gin.H{
        "title":   article.Title,
        "payload": article,
    },
)

 

위의 코드를 포함하여 업데이트된 handlers.article.go 파일은 다음과 같이 구성될 것입니다.

 

▶ handlers.article.go

 

// handlers.article.go

package main

import (
  "net/http"
  "strconv"

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

func showIndexPage(c *gin.Context) {
  articles := getAllArticles()

  // 템플릿 렌더링을 위해 Context의 HTML 메소드 호출
  c.HTML(
    // ----- HTTP Status를 200(OK)로 설정합니다 ------ //
    http.StatusOK,
    // ----- article.html 템플릿을 사용합니다 ------ //
    "index.html",
    // ---- 페이지에서 사용하는 데이터를 전달합니다 ----- //
    gin.H{
      "title":   "Home Page",
      "payload": articles,
    },
  )
}

func getArticle(c *gin.Context) {
  // ----- 기사 ID가 유효한지 확인합니다 ----- //
  if articleID, err := strconv.Atoi(c.Param("article_id")); err == nil {
    // ----- 기사가 존재하는지 확인합니다 ----- //
    if article, err := getArticleByID(articleID); err == nil {
      // Call the HTML method of the Context to render a template
      c.HTML(
        http.StatusOK,
        "article.html",
        gin.H{
          "title":   article.Title,
          "payload": article,
        },
      )

    } else {
      // ---- 기사를 찾을 수 없는 경우 오류와 함께 중단합니다 ---- //
      c.AbortWithError(http.StatusNotFound, err)
    }

  } else {
    // ---- URL에 잘못된 기사 ID가 지정된 경우 오류와 함께 중단합니다 ---- //
    c.AbortWithStatus(http.StatusNotFound)
  }
}

 

이제 어플리케이션을 Build하고 실행시켜봅니다. http://localhost:8080/article/view/1 에 접속하면 다음과 같은 화면이 뜨게됩니다.

 

제목과 내용이 정상적으로 출력됩니다

 

 

반응형

+ Recent posts