지난 포스트
웹 프로그래밍 with Golang 1 - Hello, World!
웹 프로그래밍 with Golang 2 - 템플릿 문법
웹 프로그래밍 with Golang 3 - 정적 파일
웹 프로그래밍 with Golang 4 - 라우팅
개요
이번 시간에는 Form과 JSON을 이용하여 간단한 Contact 사이트를 만들어보겠습니다.
<form>
태그는 사용자로부터 데이터를 입력받기 위해 존재하는 문서 구획입니다.
<form>
은 <input>
태그와 함께 사용되는데 submit(제출)이 되면 직렬화(serialize)되어 서버로 전달됩니다.
1 2 3 4 5 6 7 8 9 10 11 <h1 > Contact</h1 > <form method ="POST" > <label > Email:</label > <br /> <input type ="text" name ="email" > <br /> <label > Subject:</label > <br /> <input type ="text" name ="subject" > <br /> <label > Message:</label > <br /> <textarea name ="message" > </textarea > <br /> <hr /> <input type ="submit" > </form >
form에 작성한 내용이 어떻게 객체화되는지 알아보기 위해 아래 스크립트를 추가해봅시다.
(테스트 후에는 제거합니다.)
1 2 3 4 5 6 7 8 9 10 11 12 <script > const formEl = document .querySelector('form' ) formEl.addEventListener('submit' , evt => { evt.preventDefault() const formData = new FormData(evt.target) for (var pair of formData.entries()) { console .log(pair[0 ]+ '=' + pair[1 ]); } }) </script >
HTML FormData의 경우 실제로 서버에 전달될 때에는 다음과 같이 인코딩 됩니다.
email=abc@gamil.com&subject=안녕하세요&message=sadadd
key=value
로 이루어져 있으며 &
로 구분합니다.
GET Method의 경우 URL에 붙으며 (Query String), POST Method의 경우 Body에 붙습니다.
이러한 인코딩 방식을 application/x-www-form-urlencoded
라고 합니다.
1 2 3 4 5 6 7 8 # HTTP POST 예제 POST / HTTP/1.1 Host: foo.com Content-Type: application/x-www-form-urlencoded Content-Length: 48 email=abc@gamil.com&subject=안녕하세요&message=sadadd
HTTP 요청 처리하기
Form을 이용하여 HTTP Request를 보내게 되면 완전히 페이지가 새로고침됩니다. 따라서 각 메서드별로 적절하게 응답을 해줄 필요가 있습니다. 이를 구현하기 위한 한가지 방법으로 템플릿 문법을 사용할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 {{ if .Success }} <h1 > Thanks for your message!</h1 > {{ else }} <h1 > Contact</h1 > <form method ="POST" > <label > Email:</label > <br /> <input type ="text" name ="email" > <br /> <label > Subject:</label > <br /> <input type ="text" name ="subject" > <br /> <label > Message:</label > <br /> <textarea name ="message" > </textarea > <br /> <hr /> <input type ="submit" > </form > {{ end }}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package mainimport ( "net/http" "html/template" "github.com/gorilla/mux" "fmt" ) type ContactDetails struct { Email string Subject string Message string } func main () { r := mux.NewRouter() tmpl := template.Must(template.ParseFiles("form.html" )) r.HandleFunc("/" , func (w http.ResponseWriter, r *http.Request) { tmpl.Execute(w, nil ) }).Methods("GET" ) r.HandleFunc("/" , func (w http.ResponseWriter, r *http.Request) { details := ContactDetails{ Email: r.FormValue("email" ), Subject: r.FormValue("subject" ), Message: r.FormValue("message" ), } fmt.Printf("Email: %s, Subject: %s Message: %s\n" , details.Email, details.Subject, details.Message) tmpl.Execute(w, struct { Success bool }{ true }) }).Methods("POST" ) http.ListenAndServe(":8080" , r) }
브라우저에서 접속하면 기본적으로 GET 메서드를 사용한것과 동일합니다. 따라서 첫번째 핸들러 함수가 실행됩니다. POST 요청을하면 두번째 핸들러가 실행되어 “Thanks for your message!”가 출력되게 됩니다.
Fetch를 이용한 비동기 통신
Fetch API가 웹 표준(Living Standard )으로 지정되면서 Ajax(Asynchronous JavaScript And XML), 즉 비동기 통신이 대세가 되고 있습니다. 원래 비동기 통신은 XMLHttpRequest
를 사용했으나, 편리성이 떨어져 jQuery같은 서브 파티 라이브러리를 주로 사용했었습니다. Fetch
덕분에 더이상 서드파티 라이브러리에 의존할 필요 없이, 편리하게 비동기 통신을 구현할 수 있습니다.
비동기 통신을 하게되면 페이지 새로고침 없이 새로운 데이터를 수신하고 갱신할 수 있습니다. 위에서 언급한 application/x-www-form-urlencoded
방식을 사용해도 되지만, 직관성을 높이기 위하여 비동기 통신에서는 보통 클라이언트와 서버 모두application/json
방식으로 데이터를 인코딩합니다.
자습 컨텐츠 Fetch API에 대해서 (MDN)
비동기 함수에 대해서 (MDN)
Fetch 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <h1 > Contact</h1 > <form method ="POST" > <label > Email:</label > <br /> <input type ="text" name ="email" > <br /> <label > Subject:</label > <br /> <input type ="text" name ="subject" > <br /> <label > Message:</label > <br /> <textarea name ="message" > </textarea > <br /> <hr /> <input type ="submit" > </form > <script > const formEl = document .querySelector('form' ) formEl.addEventListener('submit' , async evt => { evt.preventDefault() const formData = new FormData(evt.target) const obj = {} for (const [key, value] of formData) obj[key] = value const result = await fetch('/' , { method : 'POST' , headers : { 'Content-Type' : 'application/json' , }, body : JSON .stringify(obj), }) const responseData = await result.json() alert(JSON .stringify(responseData)) }) </script >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package mainimport ( "net/http" "html/template" "github.com/gorilla/mux" "encoding/json" ) type ContactDetails struct { Email string Subject string Message string } func main () { r := mux.NewRouter() tmpl := template.Must(template.ParseFiles("fetch.html" )) r.HandleFunc("/" , func (w http.ResponseWriter, r *http.Request) { tmpl.Execute(w, nil ) }).Methods("GET" ) r.HandleFunc("/" , func (w http.ResponseWriter, r *http.Request) { var details ContactDetails json.NewDecoder(r.Body).Decode(&details) json.NewEncoder(w).Encode(details) }).Methods("POST" ) http.ListenAndServe(":8080" , r) }
참고
https://gowebexamples.com/forms/
https://gowebexamples.com/json/
Author:
JeHwanYoo
License:
Copyright (c) 2022 CC-BY-NC-4.0 LICENSE