TIL 18 - Golang으로 블록체인 만들기(Template 적용하기) 4

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

TIL

목록 보기
18/46
post-thumbnail

Template를 이용한 웹페이지 출력

  • 소스 코드
    • main.go
      package main
      
      import (
      	"coin/exam07/blockchain"
      	"fmt"
      	"html/template"
      	"log"
      	"net/http"
      )
      
      const port string = ":4000"
      
      type homeData struct {
      	// public/private는 template까지 영향을 준다.
      	PageTitle string
      	Blocks    []*blockchain.Block
      }
      
      func home(rw http.ResponseWriter, r *http.Request) {
      	// template.Must() err가 있다면 처리해준다. 에러가 없으면 Template pointer를 반환한다.
      	tmpl := template.Must(template.ParseFiles("templates/home.html"))
      	// 블록체인을 블러와 저장한다.
      	data := homeData{"Home", blockchain.GetBlockchain().AllBlocks()}
      	tmpl.Execute(rw, data)
      }
      func main() {
      	http.HandleFunc("/", home)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      	// 에러가 있을때만 실행
      	log.Fatal(http.ListenAndServe(port, nil))
      }
    • blockchain/blockchain.go
      package blockchain
      
      import (
      	"crypto/sha256"
      	"fmt"
      	"sync"
      )
      
      // main.go에서 가져다 쓰기위해 임시로 대문자로 변경
      type Block struct {
      	Data     string
      	Hash     string
      	PrevHash string
      }
      type blockchain struct {
      	blocks []*Block
      }
      
      var b *blockchain
      
      var once sync.Once
      
      func (b *Block) calculateHash() {
      	hash := sha256.Sum256([]byte(b.Data + b.PrevHash))
      	b.Hash = fmt.Sprintf("%x", hash)
      }
      
      func getLastHash() string {
      	totalBlocks := len(GetBlockchain().blocks)
      	if totalBlocks == 0 {
      		return ""
      	}
      	return GetBlockchain().blocks[totalBlocks-1].Hash
      }
      
      func createBlock(data string) *Block {
      	newBlock := Block{data, "", getLastHash()}
      	newBlock.calculateHash()
      	return &newBlock
      }
      
      // export 함수
      func (b *blockchain) AddBlock(data string) {
      	b.blocks = append(b.blocks, createBlock(data))
      }
      
      func GetBlockchain() *blockchain {
      	if b == nil {
      		once.Do(func() {
      			b = &blockchain{}
      			b.AddBlock("Genesis Block")
      		})
      	}
      	return b
      }
      
      // 사용자에게 field를 드러내주는 function(singleton의 철학)
      func (b *blockchain) AllBlocks() []*Block {
      	return b.blocks
      	// return GetBlockchain().blocks
      }
  • 실행 결과

Blockchain.Data를 웹페이지에 출력

  • 기존 .html 파일을 .gohtml 파일로 수정
  • css는 해당 mvp.css 사용
  • 소스 코드
    • main.go
      package main
      
      import (
      	"coin/exam08/blockchain"
      	"fmt"
      	"html/template"
      	"log"
      	"net/http"
      )
      
      const port string = ":4000"
      
      type homeData struct {
      	// public/private는 template까지 영향을 준다.
      	PageTitle string
      	Blocks    []*blockchain.Block
      }
      
      func home(rw http.ResponseWriter, r *http.Request) {
      	tmpl := template.Must(template.ParseFiles("templates/home.gohtml"))
      	data := homeData{"Home", blockchain.GetBlockchain().AllBlocks()}
      	tmpl.Execute(rw, data)
      }
      func main() {
      	http.HandleFunc("/", home)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      	// 에러가 있을때만 실행
      	log.Fatal(http.ListenAndServe(port, nil))
      }
    • blockchain/blockchain.go
      package blockchain
      
      import (
      	"crypto/sha256"
      	"fmt"
      	"sync"
      )
      
      // main.go에서 가져다 쓰기위해 임시로 대문자로 변경
      type Block struct {
      	Data     string
      	Hash     string
      	PrevHash string
      }
      type blockchain struct {
      	blocks []*Block
      }
      
      var b *blockchain
      
      var once sync.Once
      
      func (b *Block) calculateHash() {
      	hash := sha256.Sum256([]byte(b.Data + b.PrevHash))
      	b.Hash = fmt.Sprintf("%x", hash)
      }
      
      func getLastHash() string {
      	totalBlocks := len(GetBlockchain().blocks)
      	if totalBlocks == 0 {
      		return ""
      	}
      	return GetBlockchain().blocks[totalBlocks-1].Hash
      }
      
      func createBlock(data string) *Block {
      	newBlock := Block{data, "", getLastHash()}
      	newBlock.calculateHash()
      	return &newBlock
      }
      
      // export 함수
      func (b *blockchain) AddBlock(data string) {
      	b.blocks = append(b.blocks, createBlock(data))
      }
      
      func GetBlockchain() *blockchain {
      	if b == nil {
      		once.Do(func() {
      			b = &blockchain{}
      			b.AddBlock("Genesis Block")
      		})
      	}
      	return b
      }
      
      // 사용자에게 field를 드러내주는 function(singleton의 철학)
      func (b *blockchain) AllBlocks() []*Block {
      	return b.blocks
      	// return GetBlockchain().blocks
      }
    • pages/add.gohtml
      • 다음에 진행
    • pages/home.gohtml
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta http-equiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <link rel="stylesheet" href="https://unpkg.com/mvp.css@1.12/mvp.css"> 
          <title>Coin</title>
        </head>
        <body>
        <header>
          <h1>{{.PageTitle}}</h1>
        </header>
        <main>
          {{range .Blocks}} 
          {{/* .Data는 실제 Blocks 안에 있는 Data를 의미한다. */}}
          {{/* 가져올 값들은 모두 대문자로 시작해야하고 struct에 있는 field명과 같아야한다. */}}
          <section>
            <ul>
              <li>{{.Data}}</li>
              <li>{{.Hash}}</li>
              <li>{{.PrevHash}}</li>
            </ul>
          <section>
          {{end}}
        </main>
        </body>
      </html>
    • partials/footer.html
      • 다음에 진행
    • partials/head.gohtml
      • 다음에 진행
    • partials/header.gohtml
      • 다음에 진행
  • 실행 결과
    • 전체 코드를 실행하면 에러가 생긴다.(home.gohtml 위치 변경하였음)
    • mvp.css를 입혔을 경우

앞서 만든 서버를 Refactoring 하기

  • html/template 패키지를 이용해 .gohtml 파일 경로 설정 및 라우팅
  • 소스 코드
    • main.go
      package main
      
      import (
      	"coin/exam09/blockchain"
      	"fmt"
      	"html/template"
      	"log"
      	"net/http"
      )
      
      const (
      	port        string = ":4000"
      	templateDir string = "templates/"
      )
      
      var templates *template.Template
      
      type homeData struct {
      	// public/private는 template까지 영향을 준다.
      	PageTitle string
      	Blocks    []*blockchain.Block
      }
      
      func home(rw http.ResponseWriter, r *http.Request) {
      	data := homeData{"Home", blockchain.GetBlockchain().AllBlocks()}
      	templates.ExecuteTemplate(rw, "home", data)
      }
      func main() {
      	// tamplates를 업데이트한다.(templates는 pages/*.gohtml을 가지고 있게되고)
      	templates = template.Must(template.ParseGlob(templateDir + "pages/*.gohtml"))
      	// 해당 라인이 실행되면 templates variable은 template Object가 된다.
      	// (templates는 partials/*.gohtml도 함께 가지고 있게 된다.)
      	templates = template.Must(templates.ParseGlob(templateDir + "partials/*.gohtml"))
      	http.HandleFunc("/", home)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      	// 에러가 있을때만 실행
      	log.Fatal(http.ListenAndServe(port, nil))
      }
    • template/pages/add.gohtml
      {{define "add"}}
      <!DOCTYPE html>
      <html lang="en">
      {{template "head"}}
        <body>
        {{template "header"}}
          <main>
            <form>
              <input type="text" placeholder="Data for your block" required />
            </form>
          </main> 
          {{template "footer"}}
        </body>
      </html>
      {{end}}
    • template/pages/home.gohtml
      {{define "home"}}
      <!DOCTYPE html>
      <html lang="en">
      {{template "head"}}
        <body>
        {{template "header"}}
          <main>
            {{range .Blocks}} 
      {{template "Block"}}
            {{end}}
          </main>
          {{template "footer"}}
        </body>
      </html>
      {{end}}
    • template/partials/block.gohtml
      {{define "Block"}}
            <div>
              <ul>
                <li>{{.Data}}</li>
                <li>{{.Hash}}</li>
                {{if .PreHash}}
                  <li>{{.PreHash}}</li>
                {{end}} 
              </ul>
            </div>
          <hr />
      {{end}}
    • template/partials/footer.gohtml
      {{define "footer"}}
      <footer>&copy; 2022</footer>
      {{end}}
    • template/partials/head.gohtml
      {{define "head"}}
        <head>
          <meta charset="UTF-8" />
          <meta http-equiv="X-UA-Compatible" content="IE=edge" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <link rel="stylesheet" href="https://unpkg.com/mvp.css@1.12/mvp.css"> 
          <title>Coin</title>
        </head>
        {{end}}
    • template/partials/header.gohtml
      {{define "header"}}
      
      <header>
          <nav>
            <a href="/"><h1>FDongFDong 코인</h1></a>
            <ul>
              <li>
                <a href="/">Home</a>
              </li>
              <li>
                <a href="/add">Add</a>
              </li>
            </ul>
          </nav>
          <h1>{{.PageTitle}}</h1>
      </header>
      {{end}}
  • 실행 결과
profile
좋은 개발자가 되고싶은

0개의 댓글