IT/GO Lang

반응형

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 에 접속하면 다음과 같은 화면이 뜨게됩니다.

 

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

 

 

반응형
반응형

Go의 인기 Web Framework 'Gin' 


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


이번 시간에는 기사 리스트의 뼈대를 만들고 테스트해보도록 하겠습니다.

 

1. 뷰 템플릿 작성

 

메인 페이지에 기사(Article) 리스트가 표시될 예정이므로 새로 템플릿을 만들 필요는 없습니다. 하지만 현재의 내용을 기사 리스트로 대체하려면 index.html 템플릿에 변경이 필요합니다.

 

이 수정사항을 적용하기 위해서 기사 리스트가 payload라는 변수의 템플릿에 전달된다고 해봅시다. 이러한 가정하에서 다음의 snippet은 모든 기사의 리스트들을 표시해야 합니다.

 

  {{range .payload }}
    <!--ID를 기준으로 기사에 대한 링크를 만듭니다-->
    <a href="/article/view/{{.ID}}">
      <!--기사의 제목을 표시-->
      <h2>{{.Title}}</h2>
    </a>
    <!--기사의 컨텐츠를 표시-->
    <p>{{.Content}}</p>
  {{end}}

 

이 snippet은 payload 변수의 모든 항목을 반복하고, 각 기사의 제목과 컨텐츠를 표시합니다. 위의 snippet도 각 기사로 연결됩니다. 아직 개별 기사들을 표시하기 위한 라우트 핸들러를 정의하지 않았기 때문에 이 링크는 예상대로 작동되지 않을 것입니다.

 

업데이트 된 index.html 파일은 다음의 코드를 포함하여야 합니다.

 

<!--index.html-->

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

  <!--Loop over the `payload` variable, which is the list of articles-->
  {{range .payload }}
    <!-- ID를 기준으로 기사에 대한 링크를 만듭니다. -->
    <a href="/article/view/{{.ID}}">
      <!-- 기사의 제목을 표시 -->
      <h2>{{.Title}}</h2>
    </a>
    <!-- 기사의 컨텐츠를 표시 -->
    <p>{{.Content}}</p>
  {{end}}

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

 

2. 유닛 테스트를 포함한 라우터 핸들러 요구사항 지정

 

index 경로에 대한 핸들러를 만들기 전에 이 라우터 핸들러의 예상 동작을 정의하는 테스트를 먼저 만들어보도록 합시다. 이 테스트는 다음의 조건들을 확인해볼 것입니다

 

  1.  핸들러는 HTTP status 200을 응답합니다
  2.  반환된 HTML에는 'Home page' 텍스트가 포함된 제목 태그를 포함합니다. 

테스트를 위한 코드는 handlers.article_test.go 파일의 TestShowIndexPageUnauthentified 함수에 저장됩니다. 우리는 이 함수가 사용하는 헬퍼 기능을 common_test.go 파일에 두도록 할 것입니다.

 

handler.article_test.go의 내용은 다음과 같습니다

 

//------------------- handlers.article_test.go -----------------//

package main

import (
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

// ----- 홈페이지에 대한 GET 요청이 인증되지 않은 사용자에 대해 HTTP 200 코드가 있는 홈페이지를 반환하는지 테스트합니다 ----- //
func TestShowIndexPageUnauthenticated(t *testing.T) {
	r := getRouter(true)

	r.GET("/", showIndexPage)

	// ---- 위의 라우터로 보낼 요청을 생성합니다 ----- //
	req, _ := http.NewRequest("GET", "/", nil)

	testHTTPResponse(t, r, req, func(w *httptest.ResponseRecorder) bool {
		// ----- HTTP status 코드가 200인지 확인합니다 ----- //
		statusOK := w.Code == http.StatusOK

		// ------ 페이지의 제목이 'Home Page' 인지 테스트합니다 ------- //
		// --- HTML 페이지를 파싱하고 처리할 수 있는 라이브러리를 사용하여 훨씬 디테일한 테스트를 수행합니다 --- //
		p, err := ioutil.ReadAll(w.Body)
		pageOK := err == nil && strings.Index(string(p), "<title>Home Page</title>") > 0

		return statusOK && pageOK
	})
}

 

common_test.go 의 내용은 다음과 같습니다.

 

package main

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

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

var tmpArticleList []article

// ------- 이 함수는 테스트 함수를 실행하기 전, 설정에 사용합니다 ----- //
func TestMain(m *testing.M) {
	// ----- Gin을 테스트 모드로 설정합니다 ----- //
	gin.SetMode(gin.TestMode)

	// ------ 다른 테스트를 실행합니다 ------ //
	os.Exit(m.Run())
}

// ---- 테스트 중 라우터를 만드는 헬퍼 함수 ---- //
func getRouter(withTemplates bool) *gin.Engine {
	r := gin.Default()
	if withTemplates {
		r.LoadHTMLGlob("templates/*")
	}
	return r
}

// ----- 요청을 처리하고 응답을 테스트하는 헬퍼 함수 ------- //
func testHTTPResponse(t *testing.T, r *gin.Engine, req *http.Request, f func(w *httptest.ResponseRecorder) bool) {

	// ---- 응답 레코더 생성 ---- //
	w := httptest.NewRecorder()

	// ------ 서비스를 생성하고 위의 요청을 처리합니다 ----- //
	r.ServeHTTP(w, req)

	if !f(w) {
		t.Fail()
	}
}

// ---- 이 함수는 테스트를 위해 메인 리스트를 임시 리스트에 저장하는데 사용됩니다 ----- //
func saveLists() {
	tmpArticleList = articleList
}

// ------ 이 함수는 임시 리스트에서 메인 리스트를 복구하는데 사용됩니다 ------ //
func restoreLists() {
	articleList = tmpArticleList
}

 

우리는 이 테스트를 구현하기 위해 몇가지의 헬프 함수를 작성했습니다. 이 함수들은 유사한 기능을 테스트하기 위해 추가 테스트를 작성할 때, 상용 코드를 줄이는 데 도움을 줄 것입니다.

 

TestMain 함수는 Gin이 테스트 모드를 사용하도록 설정하고, 나머지 테스트 함수를 호출합니다. getRouter 함수는 메인 어플리케이션과 유사한 방법으로 라우터를 만들고 반환합니다. saveLists() 함수는 원본 기사 목록을 임시 변수에 저장합니다. 이 임시 변수는 restoreLists() 함수에 의해 유닛 테스트가 실행된 후 기사 리스트를 초기 상태로 복원하는데 사용됩니다.

 

최종적으로, testHTTPResponse 함수는 전달된 함수를 실행하여 테스트의 성공 여부를 나타내는 boolean true 값을 반환하는지 확인합니다. 이 함수는 HTTP 요청의 응답을 테스트하는 데 필요한 코드가 중복이 되지 않도록 하는데 도움을 줄 것입니다.

 

HTTP 코드와 반환된 HTML을 확인하려면 다음의 몇가지를 수행합니다.

 

  1.  새 라우터를 생성합니다.
  2. 기본 App이 사용하는 것과 동일한 핸들러를 사용하도록 경로를 정의합니다. (ShowIndexPage)
  3. 이 라우터에 접근할 수 있는 새 요청을 생성합니다.
  4. HTTP 코드와 HTML을 테스트하기 위해 응답을 처리하는 함수를 만듭니다.
  5. testHTTPResponse() 함수를 호출하여 이 테스트를 완료합니다

 

3. 라우터 핸들러 만들기

 

우리는 handler.article.go 파일에 기사와 관련된 기능에 대한 모든 라우터 핸들러를 생성할 것입니다. 인덱스 페이지 핸들러 showIndexPage 는 다음의 작업을 수행하도록 합니다.

 

3-1. 문서 목록을 가져옵니다.

 

이 작업은 이전에 정의된 getAllArticles 함수를 사용하여 수행될 수 있습니다.

 

articles := getAllArticles()

 

 

3-2. 기사 리스트를 전달하는 index.html 템플릿을 렌더링합니다.

 

이 작업은 아래의 코드를 사용합니다.

 

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

 

이전 단계의 버전과의 유일한 차이점은 payload라는 변수가 템플릿에 접근할 기사 리스트를 전달한다는 것입니다.

 

handlers.article.go 는 다음의 코드를 포함하여야 합니다.

 

// handlers.article.go

package main

import (
	"net/http"

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

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

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

}

 

이제 어플리케이션을 실행하고 브라우저에서 https://localhost:8080 에 접속하면 다음과 같은 화면을 볼 수 있습니다.

 

기사 제목과 내용이 보입니다!

 

현재까지의 소스 tree는 다음과 같습니다.

 


다음 시간에는 메인 페이지가 아닌 기사 하나하나의 링크에 맞는 경로 설정을 해보도록 할 예정입니다. 이번 포스팅에서는 많은 내용의 코딩이 필요하므로 실수하지 않도록 꼼꼼하게 체크해주는 것도 필요합니다! 

반응형
반응형

Go 의 인기 Web Framework 'Gin'


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


이번 시간에는 라우터 초기화기사 모델을 디자인하고 테스트하는 과정을 제작해볼 예정입니다.

 

1. 경로 설정

이전 시간까지 main.go 파일에서 라우터를 만들고 설정해주었습니다. 어플리케이션의 커져감에 따라 경로 설정을 하나의 파일에서 작성하여 옮기는 것이 좋습니다. routes.go 파일에 함수 initializeRoutes() 를 만들고, 이 함수를 main() 함수에서 호출하여 모든 경로를 설정하게 될 것입니다. 라우터 핸들러를 별도의 함수로 정의하는 과정입니다.

 

위와 같은 작업을 수행할 routes.go 파일은 다음과 같습니다.

 

▶ routes.go

// routes.go

package main

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

 

인덱스 페이지에 기사 목록들을 표시할 것이기 때문에 코드를 수정하고 나서도 추가 경로를 정의해줄 필요가 없습니다.

 

이제 main.go 파일에 initializeRoutes() 함수를 추가해줍니다.

 

▶ main.go

 

// main.go

package main

import (
	"net/http"

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

var router *gin.Engine

func main() {
	// ---- Gin에서 기본적인 라우터 생성 ----- //
	router = gin.Default()

	// ----- 모든 템플릿 파일 로드 ------- //
	router.LoadHTMLGlob("templates/*")

	router.GET("/", func(c *gin.Context) {
		// Context의 HTML 메소드를 호출하여 템플릿을 렌더링합니다.

		c.HTML(
			// ----- HTTP 상태를 200(OK)에 세팅합니다 ------ //
			http.StatusOK,
			// ------ index.html 템플릿을 사용합니다 ------- //
			"index.html",
			// ----- 페이지에서 사용하는 데이터 전달 ------- //
			gin.H{
				"title": "Home Page",
			},
		)
	})

	// ------ 경로 초기화하기 -------- //
	initializeRoutes()

	// ------ 어플리케이션 서버 구동 ------- //
	router.Run()
}

 

2. 기사 모델 디자인하기

튜토리얼에서는 ID, Title, Content 총 3개의 필드만을 사용하는 단순한 기사 구조를 유지할 것입니다. 이 struct는 다음과 같이 표현할 수 있습니다.

 

type article struct {
  ID      int    `json:"id"`
  Title   string `json:"title"`
  Content string `json:"content"`
}

 

대부분의 어플리케이션은 DB를 사용하여 데이터를 관리합니다. 단순하게 유지하기 위해 다음 두 개의 하드 코딩된 기사로 기사 목록을 초기화하도록 하겠습니다.

 

var articleList = []article{
  article{ID: 1, Title: "1번 기사", Content: "기사 내용 1번"},
  article{ID: 2, Title: "2번 기사", Content: "기사 내용 2번"},
}

 

위의 코드를 models.article.go 라는 새로운 파일에 작성하도록 합니다. 이제 우리는 모든 기사 목록을 반환하는 함수를 만들어줄 필요가 있습니다. 이 함수의 이름을 getAllArticles() 이라고 지정하고, 파일에 저장합니다. 이번에는 이 과정에 대한 테스트를 작성해볼 것입니다. 이 테스트는 이름이 TestGetAllArticles가 되고, models.article_test.go 파일에 저장합니다.

 

getAllArticles() 함수에 대한 테스트(TestGetAllArticles)를 생성해보는 것으로 시작합니다. models.article_test.go 에는 이 Unit 테스트를 포함한 코드가 작성되어야 합니다.

 

▶ models.article_test.go

 

// models.article_test.go

package main

import "testing"

// ------ 모든 기사 목록을 가져오는 기능을 테스트합니다 ------- //
func TestGetAllArticles(t *testing.T) {
	alist := getAllArticles()

	// --- 반환된 기사 목록의 길이가, 리스트가 포함하고 있는 전역변수의 길이와 같은지 확인합니다 --- //
	if len(alist) != len(articleList) {
		t.Fail()
	}

	// ---- 3가지 필드값이 동일한지 확인해봅니다 ----- //
	for i, v := range alist {
		if v.Content != articleList[i].Content ||
			v.ID != articleList[i].ID ||
			v.Title != articleList[i].Title {

			t.Fail()
			break
		}
	}
}

 

이 Unit 테스트는 getAllArticles() 함수를 사용하여 모든 기사 목록을 가져오도록 합니다. 테스트에서는 우선, 이 함수에서 가져온 기사 목록과 전역변수 articleList에 있는 기사 목록과 동일한지를 검증합니다. 그 다음, 각 기사가 동일한지 확인하기 위해 기사 목록을 for반복문을 통해 반복실행합니다. 이 두 테스트 중 하나라도 실패하면 테스트는 실패하게 됩니다.

 

테스트를 작성하고나면 이제 models.article.go 파일에 실제 코드를 작성해보도록 합니다.

 

▶ models.article.go

 

// models.article.go

package main

type article struct {
  ID      int    `json:"id"`
  Title   string `json:"title"`
  Content string `json:"content"`
}

var articleList = []article{
  article{ID: 1, Title: "Article 1", Content: "Article 1 body"},
  article{ID: 2, Title: "Article 2", Content: "Article 2 body"},
}

// ----- 모든 기사 목록을 반환합니다 ----- //
func getAllArticles() []article {
  return articleList
}

 

여기까지 따라오셨다면 프로젝트 폴더 안의 디렉토리 구조는 다음과 같을 것입니다.

 

오류는 내용 업데이트를 통해 수정될거에요!

 


 

다음 시간에는 View 템플릿과 라우터 핸들러를 만들어 각종 기능을 더 추가해보도록 할 예정입니다.

차근차근 제작해나갑시다! 

 

 

반응형
반응형

Go 의 인기 Web Framework 'Gin'


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


이번 시간에는 기본 튜토리얼 템플릿의 연결을 완료하고 검증해보는 과정의 첫 단계를 직접 만들어보고자 합니다.


1. 설치 완성하고 검증하기

템플릿을 모두 만드셨다면 이제 어플리케이션 도입부에 해당하는 파일을 만들 차례입니다. 인덱스 템플릿을 사용할 수 있도록 구성된 가장 단순한 웹 어플리케이션이 포함된 main.go를 만들어 보도록 하겠습니다. 우리는 Gin을 사용해서 네 단계로 이 과정을 진행할 수 있습니다.

 

1-1. 라우터 만들기

Gin에서 라우터를 만드는 기본적인 방법은 다음과 같습니다.

 

router := gin.Default()

 

이를 통해 어플리케이션의 빌드를 정의하는데 사용될 라우터를 만들 수 있습니다.

 

1-2. 템플릿 불러오기

라우터를 제작했다면, 다음의 방법으로 모든 템플릿을 불러올 수 있습니다

 

router.LoadHTMLGlob("templates/*")

 

이를 통해 Templates 폴더의 모든 템플릿 파일들이 불러와지고, 한 번 로딩되면 매번 요청 때마다 다시 읽어올 필요가 없고, 이러한 점이 Gin 웹 어플리케이션을 매우 빠르게 만들어줍니다.

 

1-3. 라우터 핸들러 정의하기

Gin의 가장 중요한 핵심어떻게 어플리케이션의 다양한 경로들을 나누고, 핸들러를 각 경로에 정의하는가에 있습니다. 이번에는 인덱스 페이지에 대한 라우터와 인라인 라우터 핸들러를 만들 것입니다.

 

	router.GET("/", func(c *gin.Context) {
    // ------ Context의 HTML 메소드를 호출하여 템플릿을 렌더링합니다. ----- //
		c.HTML(
			// ----- HTTP 상태를 200(OK)에 세팅합니다 ------ //
			http.StatusOK,
			// ------ index.html 템플릿을 사용합니다 ------- //
			"index.html",
			// ----- 페이지에서 사용하는 데이터 전달 ------- //
			gin.H{
				"title": "Home Page",
			},
		)
	})

 

router.GET 메소드는 GET 요청에 대한 라우터 핸들러를 정의하는데 사용됩니다.

 

라우터 핸들러에는 gin.Context 에 대한 포인터를 매개변수로 가지고 있습니다. 이 컨텍스트는 핸들러가 요청을 처리하는 데 필요할 수 있는 요청에 대한 모든 정보를 포함합니다. 예를 들면, Header, 쿠키와 같은 정보들을 말합니다.

 

컨텍스트에는 HTML, TEXT, JSON, XML 형식으로 응답을 렌더링하는 메소드도 있습니다. 이 경우 HTML 템플릿(index.html)을 렌더링하기 위해 context.html 메소드를 사용합니다. 이 메소드에 대한 호출에는 title값이 Home Page ("title": "Home Page" 부분)로 설정된 추가 데이터가 포함됩니다.  HTML 템플릿에서 사용할 수 있는 값입니다.

 

1-4. 어플리케이션 시작하기

어플리케이션을 시작하기 위해 라우터의 Run 메소드를 사용할 수 있습니다.

 

router.Run()

 

이를 통해 localhost에서 어플리케이션이 시작되고, 기본값으로 8080 포트에서 구동됩니다.

 

완성된 main.go 소스코드는 다음과 같습니다.

 

▶ main.go 

// main.go

package main

import (
  "net/http"

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

var router *gin.Engine

func main() {
	
    // ----- Gin에서 기본 라우터 생성 ----- //
    router = gin.Default()
    
    // ------ 모든 템플릿 파일 로드 ------- //
    router.LoadHTMLGlob("templates/*")
    
	router.GET("/", func(c *gin.Context) {
    // ------ Context의 HTML 메소드를 호출하여 템플릿을 렌더링합니다. ----- //
		c.HTML(
			// ----- HTTP 상태를 200(OK)에 세팅합니다 ------ //
			http.StatusOK,
			// ------ index.html 템플릿을 사용합니다 ------- //
			"index.html",
			// ----- 페이지에서 사용하는 데이터 전달 ------- //
			gin.H{
				"title": "Home Page",
			},
		)
	})

  // ------ 어플리케이션 서버 구동 ------//
  router.Run()

}

 

Mac OS의 경우 터미널, Windows의 경우 CMD 창에서 해당 프로젝트를 생성한 디렉토리로 이동한 후에 다음의 명령어를 실행시켜봅니다.

 

go build -o app

 

어플리케이션이 빌드되고 다음과 같이 실행할 수 있는 실행 파일 이름이 지정된 어플리케이션이 생성됩니다.

 

./app

 

여기까지 모든 진행사항을 충실히 작업하시면서 따라오셨다면, http://localhost:8080에서 우리가 제작한 어플리케이션에 접속할 수 있습니다. 실행시 다음과 같이 실행이 됩니다.

 

localhost:8080으로 접속한 화면

 

디버그 작업을 통해 로드된 HTML 템플릿을 확인!

 

현 시점에서의 어플리케이션 디렉토리 구조는 다음과 같이 구성이 되어있으실 겁니다

 

현재까지 작성된 디렉토리 구조

 


 

다음 시간에는 라우터를 초기화하고 기사(Article)을 작성할 수 있는 템플릿을 생성하고 테스트를 하는 과정을 진행해보도록 하겠습니다. 짧은 내용이지만 직접 제작해보시고 구동해보시면서 Gin을 체험해보세요!

반응형
반응형

Go 의 인기 Web Framework 'Gin'


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


이번 시간에는 Gin Web Framework를 기초로, 기본적인 구조를 직접 만들어보고자 합니다.

 

1. 의존성 설치

튜토리얼에서 사용할 어플리케이션에서는 외부 의존성 프로그램인 Gin 프레임워크만을 사용합니다. 다음 명령어를 입력하여 최신 버전을 설치해주세요.

 

$ go get -u github.com/gin-gonic/gin

 

2. 재활용 가능한 템플릿 만들기

우리가 제작할 어플리케이션은 템플릿을 사용하여 웹 페이지를 표시합니다. 해당 페이지에는 Header, Menu, Sidebar 및 footer와 같은 여러 부분이 필요할 것이며 이 부분들은 모든 페이지에서 공통적으로 사용됩니다. Go Lang을 사용해서 다른 템플릿에서 가져올 수 있는 재사용 가능한 템플릿 스니펫을 만들 수 있습니다.

 

Header와 Footer는 모든 템플릿에서 공통적으로 재사용되는 부분입니다. 따라서 제작 순서는 Header 템플릿에서 사용할 자체 템플릿 파일에 메뉴를 생성하고, Header와 Footer를 가져올 인덱스 페이지용 템플릿을 만드는 식이 될 것입니다. 지금 생성할 모든 템플릿 파일들은 templates라는 이름의 별도의 디렉토리안에 배치하도록 합니다. 먼저 튜토리얼을 시작하고자하는 폴더 안에 templates 폴더를 생성하고 menu.html 파일을 작성하는 것부터 시작해보도록 하겠습니다.

 

▶ templates/menu.html

 

<!--menu.html 모든 페이지에서 공통으로 사용-->

<nav class="navbar navbar-default">
  <div class="container">
    <div class="navbar-header">
      <a class="navbar-brand" href="/">
        홈으로
      </a>
    </div>
  </div>
</nav>

 

우선 현재 메뉴에는 홈 화면으로 돌아갈 수 있는 링크만 포함이 되어 있습니다. 우리는 어플리케이션에 기능을 추가할 때, 여기에 하나씩 추가하게 될 것입니다. 

 

▶ templates/header.html

 

<!-- header.html -->

<!doctype html>
<html>
    <head>
        <!-- 타이틀 페이지를 세팅하기 위해 타이틀을 변수로 사용합니다 -->
        <title>{{ .title }}</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta charset="UTF-8">

        <!-- 부트스트랩을 사용해서 멋진 어플리케이션을 만들어봅시다 -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
        <script async src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
    </head>
    
    <body class="container">
        <!--  이 위치에 menu.html 템플릿 포함시키기 -->
        {{ template "menu.html" . }}

 

▶ templates/footer.html

 

<!--footer.html-->

  </body>
  
</html>

 

 

튜토리얼을 제작하기 위해 우리는 오픈소스 부트스트랩의 프레임워크를 사용합니다. 파일의 대부분은 HTML형식을 가지고 있습니다. 다만 우리가 여기서 주목해야할 부분이 두 줄 있습니다.

 

<title>{{ .title }}</title>

 

위의 코드는 어플리케이션 안에서 설정해야하는 .title 변수를 사용하여 페이지 제목을 동적으로 설정하는데 사용됩니다.

 

{{ template "menu.html" . }}

 

위의 코드는 menu.html파일에서 메뉴 템플릿을 가져오는데 사용됩니다. Go Lang은 이러한 방식을 사용하여 하나의 템플릿을 다른 템플릿으로 가져올 수 있습니다.

 

Footer 템플릿에 정적 HTML이 포함되어 있습니다. 인덱스 페이지의 템플릿은 Header와 Footer를 사용하게 하고, 간단하게 안녕 Gin? 이라는 메시지를 출력할 수 있도록 해봅시다.

 

▶ templates/index.html

 

<!-- index.html -->

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

<h1>안녕 Gin?</h1>

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

 

인덱스 템플릿과 마찬가지로 다른 페이지의 템플릿도 비슷한 방식으로 Header와 Footer 템플릿을 재사용할 것입니다.

 

여기까지 따라오셨다면 어플리케이션의 디렉토리 구조는 다음과 같이 구성되어 있을 것입니다.

 

Templates 폴더 안의 4가지 html 파일이 있습니다

 

 


 

다음 시간에는 본격적으로 어플리케이션 구동을 위한 파일들을 생성해볼 것입니다. 원래 계획대로라면 Gin 체험하기 (3)에서 기본 템플릿 작성과 라우터 생성하고 안녕 Gin 부분을 로컬호스트를 통해 구동하는 부분까지 진행해보려 했습니다만 다 합치다보니 글이 너무 길어지기에 부득이하게 분리하여 업로드할 계획입니다. 포스팅 내용이 과하게 짧은 것은 저도 좋아하지 않지만, 필요한 부분만 찾아서 보시기를 원하시는 분들이 계실 수 있다는 생각을 하고 있기에 이런 결정을 하게 되었습니다.

 

언제나 많은 피드백을 부탁드리고 감사히 받고 있습니다. 감사합니다!

 

 

반응형

+ Recent posts