Go Web Programming: [03/03] How Go Works with Web

How Go Works with Web

앞 절에서는 Go 언어를 이용해서 Web 서비스를 작성하는 방법에 대해서 설명했습니다. 바로 net/http 패키지를
사용하면 쉽고 간편하게 작성 할 수 있을을 알게 되었습니다.
Go 언어는 실제 로레벨에서는 어떻게 구현해서 처리하는 것에 대해서 알아보도록 하겠습니다.
모든것의 근원은 동일합니다. Go의 Web 서비스도 1절에서 설명했던 Web의 작동 방식에 근거하고 있습니다.

web 작업 방법의 몇 가지 개념

다음은 서버의 개념 중의 일부를 설명한 것입니다.

  • Request
    사용자가 요구하는 데이터입니다. 사용자의 요청정보를 분석 합니다.
    post, get, cookie, url등의 정보를 포함 합니다.
  • Response
    서버에서 클라이언트에 데이터를 반환하는 데이터 입니다.
  • Conn
    사용자의 연결 정보 입니다.
  • Handler
    요청을 처리하고 데이터를 생성해서 반환하는 처리 로직입니다.

http 패키지가 수행하는 기능을 분석하는

아래의 그림은 Go가 제공하는 Web 서비스의 작업모드 프로세스 그림 입니다.


그림 3.9 http 패키지의 실행 흐름

  1. Listen Socket을 생성하고 지정된 포트를 모니터링 합니다. 클라이언트의 요청을 기다립니다.
  2. Listen Socket은 클라이언트의 요청을 받아들인 후, Client Socket을 통해서 클라이언트와 통신 합니다.
  3. 클라이언트의 요청을 처리 합니다.먼저 Client Socket에서 HTTP 요청의 프로토콜 헤더를 읽고 만약
    POST 메서드인 경우는 클라이언트가 입력한 데이터를 읽을 수 없습니다.그후 해당 handler가 요청을 처리 합니다.
    handler에서 클라이언트에 제공하는 데이터를 준비했으면 Client Socket을 통해서 클라이언트에 전송 합니다.

이 모든 과정은 다음의 3가지 개념만 이해하면 됩니다.
이 개념을 이해한다는 것은 Go 언어에서 어떻게 Web을 처리하는 것인지를 이해한 것입니다.

  • 포트를 감시 하는 방법
  • 클라이언트의 요청 처리 방법
  • handler의 처리 방법

이전 절의 예제에서는 Go 함수 ListenAndServe()를 사용해서 이러한 작업을 처리 했습니다.
여기에서는 다음과 같은 방법으로 처리하고 있습니다.

  1. server 객체를 초기화 합니다.
  2. net.Listen("tcp", addr)함수를 호출 합니다.로레벨의 TCP 프로토콜을 이용해서 해당 서비스를 시작합니다.
  3. 설정한 포트를 모니터링 합니다.

아래의 코드는 Go의 http 패키지의 소스 코드에서 일부를 인용 한 것입니다.
이 코드를 통해서 HTTP 처리 과정에 대하여 전반적으로 처리방식을 알 수 있습니다.

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration 
    for {
        rw e := l.Accept()
        if e != nil {
            if ne, ok := e(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay * = 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
    tempDelay = 0
    c, err := srv.newConn(rw)
    if err != nil {
        continue
    }
    go c.serve()
    }
}

지정 포트를 모니터링한 후 어떤 방식으로 클라이언트의 요청을 처리할까요?
상기의 코드에서는 포트모니터링을 수행 한 후, srv.Serve(net.Listener)함수를 호출하고 있습니다.
이 함수는 클라이언트의 요청 정보를 처리하고 있습니다. 이 함수는 무한루프를 (for {}) 사용해서 사용자의 요청을
Listener를 통해서 요청받은 후, Conn를 연결 객체를 생성 합니다.
마지막으로 단일 goroutine을 생성합니다. 이 요청의 데이터를 인수로해서 conn에 전달 합니다.go c.serve().
이것은 멀티스레드를 구현해서 처리하고 있으므로 사용자의 새로운 정보 요청은 새로운 goroutine에서 이루어 지므로,
이전의 연결 정보와는 상호 영향을주지 않습니다.

좀더 구체적으로 요청을 처리하는 함수에 대해서 자세히 알아보도록 하겠습니다.

  • conn 객체에서 request를 분석 합니다. c.readRequest()
  • 원하는 handler를 가져 옵니다. handler := sh.srv.Handler
    즉, 조금 전에 ListenAndServe() 함수를 호출했을 때, 두 번째 인수 입니다.
    예제에서는 nil 값을 전달했는데, 이것은 비어있는 것과 같습니다.
    기본적으로 handler = DefaultServeMux를 가져와서 사용합니다.
    이 변수의 용도는 바로 라우터 입니다. url에 매핑되는 해당 handler함수로 리디렉션하는 데 사용 됩니다.
    가장먼저 호출했던 코드가 http.HandleFunc("/", sayhelloName) 였습니다.
    이 코드의 의미는 사용자가 / 경로를 요청할 경우 수행할 함수에 대한 매핑입니다.(경로 규칙 등록)
    url에서 “/” 경로를 요청한다면 sayhelloName() 함수로 처리를 리디렉션 합니다.
  • DefaultServeMux는 ServeHTTP 메소드를 호출 합니다.
    이 메소드는 실제로는 sayhelloName() 함수의 본체를 호출하고 있습니다.
  • 마지막으로 response 정보를 입력하여 클라이언트에 피드백을 제공합니다.

전체적인 흐름의 자세한 내용은 아래 그림과 같습니다.


그림 3.10 http 접속의 처리 흐름

지금까지 3가지 문제에 대해 모든 해답을 얻었습니다.
Go 언어에서 Web을 처리하는 방식에 대한 기본적인 지식에 대해서 배웠습니다.