TIL 20 - Golang으로 블록체인 만들기(Block 데이터 출력하기) 5

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

TIL

목록 보기
20/46
post-thumbnail

Block 데이터 출력하기

  • /add 페이지 get 호출 시 보여줄 화면
  • /add 페이지에서 블록 데이터 추가 시 /home으로 리다이렉션 진행
  • 추가된 블록 데이터 home template에 렌더링 시켜주기
  • 소스 코드
    • 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 {
      	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 add(rw http.ResponseWriter, r *http.Request) {
      	switch r.Method {
      	case "GET":
      		templates.ExecuteTemplate(rw, "add", nil)
      	case "POST":
      		r.ParseForm()
      		data := r.FormValue("blockData")
      		fmt.Println(data)
      		blockchain.GetBlockchain().AddBlock(data)
      		http.Redirect(rw, r, "/", http.StatusPermanentRedirect)
      
      	}
      }
      func main() {
      	templates = template.Must(template.ParseGlob(templateDir + "pages/*.gohtml"))
      	templates = template.Must(templates.ParseGlob(templateDir + "partials/*.gohtml"))
      	http.HandleFunc("/", home)
      	http.HandleFunc("/add", add)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      	log.Fatal(http.ListenAndServe(port, nil))
      }
    • add.gohtml
      {{define "add"}}
      <!DOCTYPE html>
      <html lang="en">
        {{template "head" "Add"}}
        <body>
          {{template "header" "Add"}}
          <main>
              <form method="POST">
                  <input type="text" placeholder="Data for your block" required name="blockData" />
                  <button>Add Block</button>
              </form>
          </main>
        {{template "footer"}}
        </body>
      </html>
      {{end}}
    • home.gohtml
      {{define "home"}}
      <!DOCTYPE html>
      <html lang="en">
        {{template "head" .PageTitle}}
        <body>
          {{template "header" .PageTitle}}
          <main>
          {{range .Blocks}}
            {{template "block" .}}
          {{end}}
          </main>
        {{template "footer"}}
        </body>
      </html>
      {{end}}
  • 실행 결과
    • 메인 화면
      • Method : GET
      • URL : /home
      • 실행 화면
    • 블록 추가 화면
      • Method : GET
      • URL : /add
      • 실행 화면
      • Method : POST
      • URL : /add
      • 리다이렉션 : /home
      • 실행 화면

앞서 만든 서버 Refactoring 하기

  • main.go 소스코드를 간결하게 하기 위해 explorer/explorer.go 파일로 따로 분리하였다.
  • 또한 추가적으로 /Home에서 블록 데이터 출력 시 Data, Hash 등의 내용을 함께 출력하도록 변경함
  • 소스 코드
    • main.go
      package main
      
      import (
      	"coin/exam11/explorer"
      )
      
      func main() {
      	explorer.Start()
      }
    • explorer.go
      package explorer
      
      import (
      	"coin/exam11/blockchain"
      	"fmt"
      	"log"
      	"net/http"
      	"text/template"
      )
      
      const (
      	port        string = ":4000"
      	templateDir string = "explorer/templates/"
      )
      
      var templates *template.Template
      
      type homeData struct {
      	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 add(rw http.ResponseWriter, r *http.Request) {
      	switch r.Method {
      	case "GET":
      		templates.ExecuteTemplate(rw, "add", nil)
      	case "POST":
      		r.ParseForm()
      		data := r.FormValue("blockData")
      		fmt.Println(data)
      		blockchain.GetBlockchain().AddBlock(data)
      		http.Redirect(rw, r, "/", http.StatusPermanentRedirect)
      
      	}
      }
      func Start() {
      	// Standard 라이브러리를 사용하고
      	templates = template.Must(template.ParseGlob(templateDir + "pages/*.gohtml"))
      	// templates 변수를 사용했다. (템플릿 위에 템플릿을 얹은 형태)
      	templates = template.Must(templates.ParseGlob(templateDir + "partials/*.gohtml"))
      	http.HandleFunc("/", home)
      	http.HandleFunc("/add", add)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      	log.Fatal(http.ListenAndServe(port, nil))
      }
    • block.gohtml
      {{define "block"}}
       <div>
          <ul>
              <li><strong>Data: </strong>{{.Data}}</li>
              <li><strong>Hash: </strong>{{.Hash}}</li>
              {{if .PrevHash}}
              <li><strong>Previous Hash: </strong>{{.PrevHash}}</li>
              {{end}}
          </ul>
          </div>
          <hr />
      {{end}}
  • 실행 결과

Struct 다루기 1

  • JSON으로 변경하기(Marshal)
  • 소스 코드
    • main.go
      package main
      
      import (
      	"coin/exam12/utils"
      	"encoding/json"
      	"fmt"
      	"log"
      	"net/http"
      )
      
      const port string = ":4000"
      
      type URLDescription struct {
      	URL         string
      	Method      string
      	Description string
      }
      
      // Client에게 JSON을 보낸다.
      func documentation(rw http.ResponseWriter, r *http.Request) {
      	data := []URLDescription{
      		{
      			URL:         "/",
      			Method:      "GET",
      			Description: "See Documentation",
      		},
      	}
      	// data를 JSON형태로 인코딩한다.
      	b, err := json.Marshal(data)
      	utils.HandleErr(err)
      	fmt.Printf("%s", b)
      
      }
      func main() {
      	http.HandleFunc("/", documentation)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      	log.Fatal(http.ListenAndServe(port, nil))
      }

Struct 다루기 2

  • JSON으로 변경하기
  • Struct field tag 사용하기
  • 소스 코드
    • main.go
      package main
      
      import (
      	"encoding/json"
      	"fmt"
      	"log"
      	"net/http"
      )
      
      const port string = ":4000"
      
      // struct field tag 사용
      // omitempty : Field가 비어있으면 Field를 숨겨준다.
      // "-" : 해당 필드를 무시한다.
      type URLDescription struct {
      	URL         string `json:"url"`
      	Method      string `json:"method"`
      	Description string `json:"description"`
      	Payload     string `json:"payload,omitempty"`
      	Field       int    `json:"-"`
      }
      
      // Client에게 JSON을 보낸다.
      func documentation(rw http.ResponseWriter, r *http.Request) {
      	data := []URLDescription{
      		{
      			URL:         "/",
      			Method:      "GET",
      			Description: "See Documentation",
      			Field:       1,
      		},
      		{
      			URL:         "/blocks",
      			Method:      "POST",
      			Description: "Add A Block",
      			Payload:     "data:string",
      		},
      	}
      	// Client에게 JSON임을 알려주기 위함
      	rw.Header().Add("Content-Type", "application/json")
      	// // data를 JSON형태로 인코딩한다.
      	// b, err := json.Marshal(data)
      	// utils.HandleErr(err)
      	// fmt.Fprintf(rw, "%s", b)
      
      	// 위와 같은 동작을 한다.
      	json.NewEncoder(rw).Encode(data)
      
      }
      func main() {
      	http.HandleFunc("/", documentation)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      	log.Fatal(http.ListenAndServe(port, nil))
      }
  • 실행 결과

Struct 다루기

  • Interface 이용하여 출력하기
    • MarshalText() 함수 사용하기
      • JSON의 형태를 사용자가 Custom할 수 있다.
  • 소스 코드
    • main.go
      package main
      
      import (
      	"encoding/json"
      	"fmt"
      	"log"
      	"net/http"
      )
      
      const port string = ":4000"
      
      type URL string
      
      // MarshalText()는 인터페이스이다.
      func (u URL) MarshalText() ([]byte, error) {
      	url := fmt.Sprintf("http://localhost%s%s", port, u)
      	return []byte(url), nil
      }
      
      type URLDescription struct {
      	URL         URL    `json:"url"`
      	Method      string `json:"method"`
      	Description string `json:"description"`
      	Payload     string `json:"payload,omitempty"`
      }
      
      func (u URLDescription) String() string {
      	return "Hello I'm the URL Description"
      }
      
      // Client에게 JSON을 보낸다.
      func documentation(rw http.ResponseWriter, r *http.Request) {
      	data := []URLDescription{
      		{
      			URL:         URL("/"),
      			Method:      "GET",
      			Description: "See Documentation",
      		},
      		{
      			URL:         URL("/blocks"),
      			Method:      "POST",
      			Description: "Add A Block",
      			Payload:     "data:string",
      		},
      	}
      	// [사용법 2]
      	// fmt.Println(data)
      	rw.Header().Add("Content-Type", "application/json")
      	json.NewEncoder(rw).Encode(data)
      
      }
      func main() {
      	// [사용법 1]
      	// fmt 패키지는 String()가 구현되어 있으면 호출해준다.
      	// fmt.Println(URLDescription{
      	// 	URL:         "/",
      	// 	Method:      "GET",
      	// 	Description: "See Documentation",
      	// })
      	http.HandleFunc("/", documentation)
      	fmt.Printf("Listening on http://localhost%s\n", port)
      
      	log.Fatal(http.ListenAndServe(port, nil))
      }
  • 실행 결과
profile
좋은 개발자가 되고싶은

0개의 댓글