- 2021/02/24 - [Go] - Golang으로 web application 구현하기 1
- 2021/02/25 - [Go] - Golang으로 web application 구현하기 2
우리가 만든 위키 페이지는 편집기능이 없다.
두 개의 새로운 핸들러를 추가로 생성해보자.
하나는 editHandler 얘는 수정하는 페이지를 처리하고,
하나는 saveHandler 얘는 저장하는 페이지를 처리할 핸들러다.
main 메서드에 아래 두가지를 추가하자.
func main() {
http.HandleFunc("/view/", viewHandler)
http.HandleFunc("/edit/", editHandler)
http.HandleFunc("/save/", saveHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
editHandler 는 페이지를 로드한다. 만약에 존재하지 않는 페이지의 경우 빈 구조체를 생성해서 HTML로 보여준다.
아래 코드를 작성하자.
일단 이 기능은 잘 동작하긴 하지만 하드코딩된 HTML 부분이 보기 불편하다. 편리하게 사용할 수 있도록 바꿔보자.
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
fmt.Fprintf(w, "<h1>Editing %s</h1>"+
"<form action=\"/save/%s\" method=\"POST\">"+
"<textarea name=\"body\">%s</textarea><br>"+
"<input type=\"submit\" value=\"Save\">"+
"</form>",
p.Title, p.Title, p.Body)
}
html/template 패키지는 Go의 표준 라이브러리 중 하나다.
우리는 해당 패키지를 사용해서 HTML을 파일로 분리해서 사용할 수 있다.
기본 Go 코드를 수정하지 않고도 수정페이지의 레이아웃을 변경해서 사용할 수 있다.
그럼 html/template 패키지를 import 해보자.
이제 fmt 패키지는 사용하지 않을 것이므로 해당 부분은 지우도록 한다.
import (
"html/template"
"io/ioutil"
"net/http"
)
edit.html 이라는 파일을 만들어서 아래 코드를 작성한다.
<h1>Editing {{.Title}}</h1>
<form action="/save/{{.Title}}" method="POST">
<div><textarea name="body" rows="20" cols="80">{{printf "%s" .Body}}</textarea></div>
<div><input type="submit" value="Save"></div>
</form>
그리고, 우리가 위에서 작성한 editHandler 을 수정하자.
이제 하드코딩된 부분을 삭제하고 아래처럼 사용할 수 있다.
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
t, _ := template.ParseFiles("edit.html")
t.Execute(w, p)
}
template.ParseFiles 함수는 edit.html의 내용을 읽고 * template.Template을 반환한다.
t.Execute 메소드는 템플릿을 실행하고 생성 된 HTML을 http.ResponseWriter에 기록한다.
.Title .Body 에서 . d은 p.Title , p.Body 를 의미한다.
템플릿 지시문은 이중 중괄호로 묶어서 표현한다.
printf "% s".Body 명령어는 .Body를 바이트 스트림 대신 문자열로 출력하는 역할을 한다.
html / template 패키지는 템플릿 작업에 의해 안전하고 올바른 모양의 HTML 만 생성되도록 한다.
예를 들어 '>' 와 같은 기호는 자동으로 escape 처리하여 >로 전환한다.
사용자의 데이터가 HTML 양식을 변환시키지 않도록 보호해준다.
추가로 viewHandler에도 view.html을 만들어 호출해보자.
view.html
<h1>{{.Title}}</h1>
<p>[<a href="/edit/{{.Title}}">edit</a>]</p>
<div>{{printf "%s" .Body}}</div>
viewHandler를 아래처럼 수정하자.
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadPage(title)
t, _ := template.ParseFiles("view.html")
t.Execute(w, p)
}
두 핸들러에서 거의 똑같은 템플릿 코드를 사용했다.
템플릿 코드를 자체 함수로 이동해서 이 중복 코드를 제거해보자.
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFiles(tmpl + ".html")
t.Execute(w, p)
}
그리고 아래 두 개의 핸들러도 수정하자.
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadPage(title)
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
그럼 결과적으로 아래와 같은 코드가 될 것이다. 이 코드를 빌드하고 테스트해보자.
package main
import (
"html/template"
"io/ioutil"
"log"
"net/http"
)
type Page struct {
Title string
Body []byte
}
func (p *Page) save() error {
filename := p.Title + ".txt"
return ioutil.WriteFile(filename, p.Body, 0600)
}
func loadPage(title string) (*Page, error) {
filename := title + ".txt"
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: title, Body: body}, nil
}
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
t, _ := template.ParseFiles(tmpl + ".html")
t.Execute(w, p)
}
func viewHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/view/"):]
p, _ := loadPage(title)
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
p, err := loadPage(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func main() {
http.HandleFunc("/view/", viewHandler)
http.HandleFunc("/edit/", editHandler)
//http.HandleFunc("/save/", saveHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
'Backend > Go' 카테고리의 다른 글
Golang으로 web application 구현하기 5 (0) | 2021.02.25 |
---|---|
Golang으로 web application 구현하기 4 (0) | 2021.02.25 |
Golang으로 web application 구현하기 2 (0) | 2021.02.25 |
Golang으로 web application 구현하기 1 (0) | 2021.02.24 |
Golang 배우기 실습 : application compile 해서 설치하기 (0) | 2021.02.24 |