cjaewon / TIL

🤯 2021, Today I Learend
2 stars 0 forks source link

golang html/template 여러 파일 읽기 #12

Open cjaewon opened 3 years ago

cjaewon commented 3 years ago
<!--  index.html  -->
{{ template "base.html" }}

{{ define "main" }}
  <h1>Hello World</h1>
  <a href="/hello">hello</a>
{{ end }}
<!--  hello.html  -->
{{ template "base.html" }}

{{ define "main" }}
  <a href="/">Travel it!</a>
{{ end }}
<!--  base.html  -->
<main>
  {{ block "main" . }}{{ end }}
</main>

위와 같이 html 템플릿 코드들을 /web/layouts/web/pages 에 정의했다. 그리고 다음과 같은 코드를 통해서 파싱해왔는데 {{ define }} 때문에 index.html의 Tree가 hello.html 와 같은 문제가 발생했다.. 아마 실행하기전에 파싱하는 ParseGlob 에서 발생한 문제인 것 같았다.

package templates

import (
    "errors"
    "html/template"
    "io"

    "github.com/labstack/echo/v4"
)

// Template is
type Template struct {
    templates *template.Template
}

// Render performances html/template rendering
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
    return t.templates.ExecuteTemplate(w, name, data)
}

// New creates an instance of Template
func New() *Template {
    t := Template{
        templates: template.Must(template.ParseGlob("web/*/*.html")),
    }

    return &t
}

이 문제를 해결하기 위해 templates을 templateMap으로 변경시켜준 다음 index.html, hello.html 을 따로따로 base.html 과 합친 뒤 저장해주었다. 다음 코드와 같다.

package templates

import (
    "html/template"
    "io"
    "path/filepath"

    "github.com/labstack/echo/v4"
)

// Template is
type Template struct {
    templateMap map[string]*template.Template
}

// Render performances html/template rendering
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
    return t.templateMap[name].ExecuteTemplate(w, "base.html", data)
}

// New creates an instance of Template
func New() *Template {
    t := Template{
        templateMap: map[string]*template.Template{},
    }

    layouts := template.Must(template.ParseGlob("web/layouts/*.html"))

    pages, err := filepath.Glob("web/pages/*.html")
    if err != nil {
        panic(err)
    }

    for _, file := range pages {
        base := filepath.Base(file)
        tmpl := template.Must(template.ParseFiles(file))
        tmpl.AddParseTree(layouts.Tree.Name, layouts.Tree)

        t.templateMap[base] = tmpl
    }

    return &t
}
cjaewon commented 3 years ago

html/template 의 신기한 점이 가장 최근에 Parse 한 결과를 Tree와, Name을 가지고 있는다.

// html/template, template.go
type Template struct {
    escapeErr error
    text *template.Template
    Tree       *parse.Tree
    *nameSpace
}

// escapeOK is a sentinel value used to indicate valid escaping.
var escapeOK = fmt.Errorf("template escaped correctly")

// nameSpace is the data structure shared by all templates in an association.
type nameSpace struct {
    mu      sync.Mutex
    set     map[string]*Template
    escaped bool
    esc     escaper
}

nameSpace가 다른 template 을 저장하고 있다.

cjaewon commented 3 years ago
func New() *Template {
    t := Template{
        templateMap: map[string]*template.Template{},
    }

    layouts := template.Must(template.ParseGlob("web/layouts/*.html"))

    pages, err := filepath.Glob("web/pages/*.html")
    if err != nil {
        panic(err)
    }

    for _, file := range pages {
        base := filepath.Base(file)
        tmpl := template.Must(template.ParseFiles(file))

        for _, t := range layouts.Templates() {
            tmpl.AddParseTree(t.Tree.Name, t.Tree)
        }

        t.templateMap[base] = tmpl
                // 수정 된 곳 
    }

    return &t
}

맨 위쪽 코드는 한 템플릿 밖에 추가하지 않아서 다음과 같이 반복문으로 써줘야 한다.