Go Web Programming: [04/01] Process FORM Inputs

Process form inputs

먼저 사용자가 입력한 양식 전송의 예를 살펴 보겠습니다. 다음과 같은 양식의 내용이 있다고 가정 합니다.
다음의 내용을 login.gtpl파일로 생성합니다.(새로운 디렉토리를 만들고 그 안에 넣어주세요)

<html>
<head>
<title></title>
</head>
<body>
    <form action="/login" method="post">
        이름 : <input type="text" name="username">
        암호 : <input type="password" name="password">
        <input type="submit" value="로그인">
    </form>
</body>
</html>

상기에서 작성한 페이지에서 내용을 입력 후 전송 버튼을 누르면 양식은 서버의 /login에 전달 됩니다.
사용자가 정보를 입력하고 로그인을 클릭 하면, 서버의 login으로 그 입력 데이터를 리디렉션 합니다.
먼저 전송방법POST/GET인지를 판단해야 합니다.

http 패키지를 통하면 아주 쉽게 이것을 처리할 수 있습니다. 이전 장에서 설명한 예제를 기초로해서
login 페이지의 form 데이터를 어​​떻게 처리하는지 살펴 보겠습니다.

package main

import (
    "fmt"
    "html/template"
    "log"
    "net/http"
    "strings"
)

func sayhelloName(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()          // url의 전달 옵션을 분석 합니다. 
               // POST방식일 경우 응답 패킷의 바디를 분석 합니다.(request body)
               // !!! 주의: ParseForm() 메소드가 호출되지 않으면 양식 데이터를 검색 할 수 없습니다.
    fmt.Println(r.Form)       // 분석한 데이터를 출력합니다.
    fmt.Println("path", r.URL.Path)
    fmt.Println("scheme", r.URL.Scheme)
    fmt.Println(r.Form["url_long"])
    for k, v := range r.Form {
        fmt.Println("key :", k)
        fmt.Println("val :", strings.Join(v, ""))
    }
    fmt.Fprintf(w, "Hello Xenostream!") // w에 기록하게되면 클라이언트에 출력 됩니다.
}

func login(w http.ResponseWriter, r *http.Request) {
    fmt.Println("method :", r.Method)       // 요청 처리 방식 
    if r.Method == "GET" {
        t , _ := template.ParseFiles("login.gtpl")
        t.Execute(w, nil)
    } else {
        // 로그인 데이터가 요청되고, 로그인 처리로직이 실행됩니다.
        fmt.Println("username :", r.Form["username"])
        fmt.Println("password :", r.Form["password"])
    }
}

func main() {
    http.HandleFunc("/", sayhelloName)        // 라우팅을 설정합니다
    http.HandleFunc("/login", login)         // 라우팅을 설정합니다
    err := http.ListenAndServe(": 9090", nil) // 감시하는 포트를 설정 합니다
    if err != nil {
        log.Fatal("ListenAndServe :", err)
    }
}

위의 코드에서 사용자의 요청 메소드를 구하려면 단지 r.Method 메소드만 호출함으로 모든
처리가 완료됩니다. 이 메소드의 반환값은 문자열 변수 입니다.
GET, POST, PUT등의 요청 method 정보를 반환 합니다.

login 함수에서 r.Method의 반환값에 따라 로그인 화면표시와 로그인 로직을 처리할지를
결정하게 됩니다. GET 메소드에 의한 요청일 경우 로그인 화면을 표시하고, 다른 방법에 의한 요청은
로그인 로직을 처리 합니다. 예를 들어 데이터베이스를 검색하거나 로그인 정보를 확인하는 등의 일 입니다.

브라우저에서 http:/127.0.0.1:9090/login을 입력하면 다음과 같은 화면이 나타납니다.


그림 4.1 사용자 로그인 화면

사용자 이름과 암호를 입력해도 서버는 아무것도 출력하지 않습니다. 왜 일까요?
기본적으로는 Handler에서는 form의 내용을 자동으로 분석하지 않기 때문 입니다.
반드시 명시적으로 r.ParseForm() 메소드를 호출하지 않는다면, 아무 데이터도 분석하지 않게 됩니다.

이제, 코드를 약간 수정하여 fmt.Println("username :", r.Form["username"]) 앞부분에
r.ParseForm() 코드를 추가하시기 바랍니다.
다시 컴파일한 후 정보를 입력하면 서버가 사용자가 입력 한,사용자 이름암호를 정상적으로 출력하게 됩니다.

r.Form에는 사용자가 입력했던 모든 요청의 데이터가 모두 포함되어 있습니다. 예를 들어 URL에 입력한
query-string, POST 데이터, PUT 데이터등 입니다. URL의 query-string 필드 및 POST 데이터가
충돌하는 경우에는 slice에 저장 됩니다. 여기에는 여러가지 값이 저장되어 있습니다.
Go의 공식 문서에 따르면, 다음 버전에서 POST, GET 등의 데이터를 분리해서 저장한다고 밝힌바 있습니다.

이제는 login.gtpl의 form의 action 값인 http:/127.0.0.1:9090/login
http:/127.0.0.1:9090/login?username=xenostream으로 변경한 후 다시 시도하면, 서버의 출력은
다음과 같습니다.

그림 4.1 서버가 수신 한 데이터를 표시

request.Form은 url.Values 형 입니다.key = value구조로 해당 데이터가 저장되어 있습니다.
form 데이터에 어떤것이 있는지 간단한 몇 가지 예제를 살펴보겠습니다.

v := url.Values{}
v.Set("name", "Ava")
v.Add("friend", "Jess")
v.Add("friend", "Sarah")
v.Add("friend", "Zoe")
// v.Encode () == "name = Ava & friend = Jess & friend = Sarah & friend = Zoe"
fmt.Println(v.Get("name"))
fmt.Println(v.Get("friend"))
fmt.Println(v["friend"])

Tips

Request패키지의 FormValue() 함수에서도 사용자가 전송한 데이터를 얻을 수 있습니다.
예를 들어 r.Form["username"]r.FormValue("username")로도 쓸수 있습니다. r.FormValue
호출할때 자동으로 r.ParseForm이 호출되므로 미리 호출 할 필요는 없습니다.
r.FormValue는 동일한 데이터 중에서 첫번째 데이터를 반환 합니다. 만약 데이터가 존재하지 않는 경우는
빈 문자열을 반환 합니다.