Go로 간단한 테스트 페이지를 만들었는데, http.HandleFunc가 두번 호출되는 현상을 발견했습니다. 전부가 그런건 아니었고, 한 개의 핸들러만 두 번씩 호출되었는데, 그 원인은 의외로 간단했습니다.
먼저 코드는 대충 이렇게 생겼습니다.
func main() {
http.HandleFunc("/", RenderMainView)
log.Fatal(http.ListenAndServe(":8000", nil))
}
func RenderTemplate(w http.ResponseWriter, name string, data interface{}) {
tmpl, _ := template.ParseFiles(name)
tmpl.Execute(w, data)
}
func RenderMainView(w http.ResponseWriter, r *http.Request) {
log.Println("root request")
RenderTemplate(w, "src/main/main.html", nil)
}
root url로 요청이 들어오면 "root request"라는 로그를 출력하고, main.html을 화면에 그려주는 앱입니다. main.html은 그냥 간단히 "root view"라는 메시지만 뿌려 줍니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>
<p>root view</p>
</body>
</html>
그런데 호출할 때 마다, 아래 처럼 두번 로그가 찍혔습니다.
사실 이때까지는 왜 두번 찍힐까? 의아하면서도, 별 대수롭게 생각지 않았는데, 이 이후에 다른 시스템으로 요청을 보내고 응답을 받는 로직이 있는데서 문제가 생겼습니다.
토큰을 생성하고 쿠키에 저장한다음 다른 서버로 토큰을 전송하고, 수신된 응답에 전송했던 토큰을 전달 받아, 쿠키에 저장한 토큰과 비교하도록 했는데 두번 호출이 되니 저장된 토큰 값과 일치하지 않는 문제가 생겼습니다.
=> 첫번째 호출 토큰A 생성 -> 토큰A 쿠키저장 -> 토큰A 서버로 전송
=> 두번째 호출 토큰B 생성 -> 토큰B 크키저장 -> 토큰B 서버로 전송.
=> 첫번째 전송에 대한 응답으로 토큰A 수신 -> 응답 쿠키에 저장된 토큰 로드( 토큰B가 로드됨) -> 토큰 비교에서 문제발생.
이렇게 모든 리퀘스트에 대해서 상대 서버의 응답이 비정상이라 판단하여 버리는 상태가 되버렸습니다. 그래서 왜 그럴까 확인하다 개발자 도구의 리퀘스트를 보다가 원인을 찾았습니다.
바로 파비콘 요청에대한 것까지 "/" 요청으로 인식하고, 핸들러에서 처리하고 있었습니다. go에서는 "/" 패턴에 대한 요청은 "/*" 로 간주하여 처리한다고 하네요. 뒤에 특정 문자열이 있는 경우, 그 문자열을 실행하구요. 그래서 main.html에 다음과 같이 파비콘을 지정하여, 저 요청이 없도록 했습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="https://example.com/image.ico">
<title>main</title>
</head>
<body>
<p>root view</p>
</body>
</html>
그런데, 여기서 한가지 궁금증이 더 생겼습니다. 여태 것 파비콘 요청 때문에 문제가 됬던 기억이 없는데, Rails에서는 어떻게 처리를 할까? 궁금했습니다. 그래서 해봤습니다. 그냥 간단히 rails 앱을 하나 만들고 / 로 시작하는 route를 하나 만들었습니다.
Rails.application.routes.draw do
get '/', to: 'application#main'
end
application_controller.rb에는 main 메서드를 만들어 주고
class ApplicationController < ActionController::Base
def main
logger.debug("root requested")
render layout:false
end
end
이때, 혹시 기본 layout이 영향을 줄까봐, layout 없이 화면이 그려지도록 했습니다. 그리고 main.html은 위에서 문제가 되었던 것을 그대로 사용했습니다.
결과는 같았습니다. 역시나 파비콘 요청이 있었어요.
그런데, 로그에는 놀랍게도 한번만 호출된 걸로 나옵니다.
rails 는 기본 생성될 때, favicon.ico이 public 폴더에 생성되고, 파비콘 요청은 여기서 처리하기 때문에 application_controller#main 까지 도착하지도 않았던 것이 하나있고, rails의 라우팅 처리는 일치하는 것만 처리하기에 "/favicon.ico"라는 요청은 파비콘이 없다고 하더라도, 404 Not Found가 발생하게 됩니다. 그랬으니 이제 것 이런 경험을 하지 못했었구나 생각했습니다.
Go를 접한지 이제 두어달이 되갑니다. 장점이 참 많은 언어라고 생각합니다. 특히 도커랑 엮어질 때, 그 장점은 배가 됩니다. 하지만, 간혹 어이없이 오늘 같이 당황하게 만드는 것도 Go 인 것 같습니다. Go는 배우기 쉽지만, 막상 익혀갈 수록 모르는게 많구나 느끼게 만드는 겸손한 언어인 것 같습니다. 결론이 옆으로 샜는데, Go에서는 파비콘 요청때문에 핸들러가 두번 호출될 수 있다를 잘 기억해둬야겠습니다.