상세 컨텐츠

본문 제목

[Nomad Coin] Explorer - #5.4 Adding A Block

Go/Blockchain

by Gopythor 2022. 7. 18. 02:29

본문

728x90
반응형

#5.4 Adding A Block

 

  • templates에 필요한 variable들을 전혀 보내고 있지 않다.
{{define "home"}}
<!DOCTYPE html>
<html lang="en">
    {{template "head"}}
<body>
    {{template "header" .PageTitle}}
    <main>
        {{range .Blocks}} 
            {{template "block"}}
        {{end}}
    </main>
    {{template "footer"}}
</body>
</html>
{{end}}
  • pages 내부 home.gohtml을 보면, header에 아무 variable도 보내고 있지 않다.
  • 그렇지만 header는 PageTitle이 필요하다.
  • home에 PageTitle이 있으니 header로 보내자.
  • .PageTitle만 추가하면 된다.
{{define "header"}}
 <header>
        <nav>
            <a href="/"><h1>노마드 코인</h1></a>
            <ul>
                <li>
                    <a href="/">Home</a>
                </li>
                <li>
                    <a href="/add">Add</a>
                </li>
            </ul>
        </nav>
    <h1>{{.}}</h1>
</header>
{{end}}
  • header를 수정해보자
  • header는 더이상 PageTitle이 필요없다. header는 struct를 전달받는게 아니기 때문이다.
  • home은 PageTitle이나 Blocks를 사용할 수 있다는 사실을 기억해야 한다. 왜냐하면 home은 homeData struct와 함께 렌더링된다.
  • home이 header에 PageTitle variable을 전달하는 시점에서 header template는 PageTitle이 필요없다.
{{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">
    <title>{{.}} | 노마드 코인</title>
</head>
{{end}}
  • head에도 똑같이 해보자.
  • {{.}}라고 쓰고 띄어쓰기도 한다.
  • PageTitle을 전달 받을 거였다.
{{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}}
  • head도 PageTitle을 전달받을 것이다.
  • footer에도 같은 작업을 하면되는데 필요할 때 하면 된다.
  • footer에는 '노마드 코인'만 있어도 된다.

  • 새로고침을 하면 Home이 나온다.
  • 다른 template들에도 variable을 전달한 것이다. 그러나 아직 Block은 보내지 않았다.
  • range 내부에서 이제 '.'이 struct에 대한 게 아니게 된다.
  • range 내부에 있는 '.'은 Blocks 배열의 각각의 Block을 가리키게 된다.
  • 그래서 우리가 해야할 건, 그 '.'을 전달하는 것 뿐이다.
  • 그럼 block partial이 struct를 받아서 사용할 수 있다.
  • .Hash나 .PrevHash나 뭐든 말이다.

  • variable을 보내고 있다.
  • Golang에서 template를 작업하는 건 간단하다.

 

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 page를 다뤄보자
  • handler function이랑 route를 만들어보자
func add(rw http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(rw, "add", nil)
}
  • add function을 만들자
  • func add(rw http.ResponseWriter, r *http.Request) 라고 쓴다.
  • 보내고 싶은 데이터가 뭔지 생각해보자. 보내고 싶은 데이터는 PageTitle이 전부일 것이다.
  • 나중에 form을 렌더링 할 것이고, POST request를 받아볼 것이다.

 

  • add page에 왔다.
  • 'Data for your block'도 들어가있다.
  • 아직 PageTitle이 없다.
  • head와 header에 Pagetitle이 필요하다.
  • variable를 보내는 대신 그냥 value를 보낸다.
{{define "add"}}
<!DOCTYPE html>
<html lang="en">
    {{template "head" "Add"}}
<body>
    {{template "header" "Add"}}
    <main>
        <form>
            <input type="text" placeholder="Data for your block" required />
        </form>
    </main>
    {{template "footer"}}
</body>
</html>
{{end}}
  • head에 "Add", header에 "Add"나 "Add a block"을 넣으면 된다.

 

  • 새로고침 해보면 value를 직접적으로 바로 보낼 수 있는 것을 확인할 수 있다. Go로부터 무조건 와야하는 게 아니다.
{{define "add"}}
<!DOCTYPE html>
<html lang="en">
    {{template "head" "Add"}}
<body>
    {{template "header" "Add"}}
    <main>
        <form method="POST" action="/add">
            <input type="text" placeholder="Data for your block" required name="blockData" />
            <button>Add Block</button>
        </form>
    </main>
    {{template "footer"}}
</body>
</html>
{{end}}
  • form 작업을 해보자
  • Button을 만들어보자
  • input에 name도 넣어보자. 왜냐하면 이름 없이는 backend에서 이 input에 접근할 수 있는 방법이 없다.
  • method는 POST가 될 것이다. action에 "/add"라고 굳이 명시하지 않아도 된다. 왜냐하면 add에서 렌더링되기 때문이다. 안 써도 default라서 괜찮다.
  • Go에서 POST request를 어떻게 처리하는지 알아보자

  • add page에 input이 있고 button도 있다.
  • controller를 생각해보자. handler function에서 method를 확인하고 있지는 않다.
  • GET을 보내던 POST를 보내던 동작할 것이다.
  • GET 요청이 들어오면 templates.ExecuteTemplate(rw, "add", nil)를 보여줄 것이다.
  • POST 요청이 들어오면 Blockchain에 다른 Block을 추가할 것이다.
  • 이를 위해 switch작업을 해야 한다.
  • request method를 확인할 거고, Go에서 정말 많은 switch를 보게 될 것이다. switch는 가끔 if/else보다도 더 유용할 때가 있다. 더 짧기 때문이다.
  • GET request가 온다면 기존에 작성했던 코드대로 작성할 것이다.
  • 만약에 POST request라면 다른 작업을 할 것이다. 여기서 하고 싶은 건 form에서 보내진 variable을 가져오는 것이다.
  • blockData를 가져오고 싶은 것이다.
  • Form은 URL field와 PUT form을 포함한 모든 parse된 form data를 가지고 있다. 이 field는 오직 ParseForm이 호출된 후에만 사용이 가능하다. 
  • ParseForm을 살펴보자. ParseForm은 r.Form과 r.PostForm을 생성한다.
  • r.PostForm은  application/x-www-form-urlencoded 인코딩을 위한 것이다.
  • Go의 소스코드를 보면 어떻게 코드를 설계해야 하는지 많이 배울 수 있다.
  • else를 쓰는 걸 좋아하지 않고 return을 더 사용한다.
  • (r *Request) Cookies() []*cookie
  • Addcookie는 Sprintf를 사용하고 있다. Hash를 만들때 썼던 것이다.
  • r.ParseForm()이 r.Form을 추가하게 될 것이다. 즉 r.Form내부에 value가 있다는 것이다.

  • r.Form을 보면 그냥 Value이다.

  • Values는 string과 string Array의 map이다.
  • 이 map으로 value 접근이 가능하다.
func add(rw http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templates.ExecuteTemplate(rw, "add", nil)
	case "POST":
		r.ParseForm()
		data := r.Form.Get("blockData")
		blockchain.GetBlockchain().AddBlock(data)
	}

}
  • Form이 function은 아니다. 그저 map이다.
  • map내부를 Get()으로 접근할 수 있고, 가지고 오고 싶었던 "blockData"를 써준다.
  • data가 string이라는 것을 알고 있다.
  • data를 Blockchain에 추가할 준비가 된 것이다.
  • frontend에서 이 function을 호출해보자.
  • 이 data로 Block을 추가한다.
  • redirection으로 응답한다. 그러나 여기에는 redirection할 수 있도록 해준게 아무것도 없다.
  • rw는 데이터를 쓰기 위한 ResponseWriter이다.
  • http package가 필요하다.
  • http package - net/http - Go Packages
  • 이게 Go 소스코드의 전부이다.

  • HTTP func Redirect이다.
func Redirect(w ResponseWriter, r *Request, url string, code int)
  • 공식 문서를 보는게 가장 쉽다.
  • http.Redirect()를 쓰고 ResponseWriter랑 Request, URL은 home으로 갈 거고, response code도 필요하다. response code는 300대여야한다. 그게 redirect되는 response code이다. 302이나 301같이 말이다. 아니면 소스 코드를 쓰는 대신 constant를 사용할 수도 있다.
func add(rw http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		templates.ExecuteTemplate(rw, "add", nil)
	case "POST":
		r.ParseForm()
		data := r.Form.Get("blockData")
		blockchain.GetBlockchain().AddBlock(data)
		http.Redirect(rw, r, "/", http.StatusPermanentRedirect)
	}

}
  • http.Status를 치면 다양한게 뜨는데 permanentlyMoved가 필요하다.

  • 비트코인의 익스플로러는 Block의 Timestamp랑 Block 그 자체를 보여준다.
  • difficulty나 해당 정보 등을 볼 수 있다.
  • JSON API를 만들어보자.
  • 그걸 위해서 function을 만들어볼 거고, 그 걸로 Block을 탐색할 수 있게 할 것이다.
  • Block 보기, 새 Block 만들기, 새 Block의 상세 정보 보기 같은 것.

강의 : 노마드코더 노마드코인

 

728x90
반응형

관련글 더보기

댓글 영역