Go 1.11과 Go 1.12 이상 간의 차이점

Go 1.12+ 런타임으로 마이그레이션하면 최신 언어 기능을 사용하고 관용적인 코드로 이동성이 향상된 앱을 빌드할 수 있습니다.

App Engine Go 1.12+ 런타임 변경사항

Go 1.12+ 런타임으로의 마이그레이션을 고려하고 있다면 App Engine 표준 환경의 Go 1.12와 이전 런타임 간의 다음과 같은 차이점을 알고 있어야 합니다.

  • 런타임 마이그레이션 수고와 복잡성을 줄이기 위해 App Engine 표준 환경에서는 Go 1.12 이상 런타임에서 Memcache와 같은 여러 기존 번들 서비스와 API에 액세스할 수 있습니다. Go 1.12 이상 앱은 Go용 App Engine SDK를 통해 번들 서비스 API를 호출할 수 있으며 Go 1.11 런타임에서와 같이 동일한 기능 대부분에 액세스할 수 있습니다.

    기존 번들 서비스와 비슷한 기능을 제공하는 Google Cloud 제품을 사용할 수도 있습니다. 이러한 Google Cloud 제품은 관용적인 Go용 Cloud 클라이언트 라이브러리를 제공합니다. Google Cloud에서 별도의 제품으로 제공되지 않는 번들 서비스의 경우(예: 이미지 처리, 검색, 메시지) 타사 제공업체 또는 다른 해결 방법을 사용할 수 있습니다.

    번들되지 않은 서비스로 마이그레이션하는 방법에 대한 자세한 내용은 번들 서비스에서 마이그레이션을 참조하세요.

  • app.yaml 구성 파일의 일부 요소 동작이 수정되었습니다. 자세한 내용은 app.yaml 파일 변경사항을 참조하세요.

  • Go 1.12 이상 런타임의 로깅은 Cloud Logging의 로깅 표준을 따릅니다. Go 1.12 이상 런타임에서 앱 로그는 더 이상 요청 로그와 번들로 묶이지 않고 다른 레코드로 분리됩니다. Go 1.12 이상 런타임에서 로그를 읽고 쓰는 방법에 대한 자세한 내용은 로깅 가이드를 참조하세요.

메모리 사용량 차이

2세대 런타임은 1세대 런타임에 비해 메모리 사용량 기준이 더 높습니다. 이는 다양한 기본 이미지 버전과 같은 여러 요인과 두 세대에서 메모리 사용량을 계산하는 방법의 차이 때문입니다.

2세대 런타임은 애플리케이션 프로세스에서 사용하는 항목과 메모리에 동적으로 캐시된 애플리케이션 파일 수를 합산하여 인스턴스 메모리 사용량을 계산합니다. 메모리 한도 초과로 인해 메모리 집약적인 애플리케이션에서 인스턴스가 종료되는 것을 방지하려면 메모리가 더 많은 더 큰 인스턴스 클래스로 업그레이드하세요.

CPU 사용량 차이

2세대 런타임은 인스턴스 콜드 스타트 시 더 높은 CPU 사용량 기준을 확인할 수 있습니다. 애플리케이션의 확장 구성에 따라 애플리케이션이 CPU 사용률에 따라 확장되도록 구성되었을 때 예상보다 많은 인스턴스 수가 늘어나는 등 의도하지 않은 부작용이 발생할 수 있습니다. 이 문제를 방지하려면 애플리케이션 확장 구성을 검토하고 테스트하여 인스턴스 수가 허용되는지 확인하세요.

요청 헤더 차이점

1세대 런타임을 사용하면 밑줄(예: X-Test-Foo_bar)이 있는 요청 헤더를 애플리케이션에 전달할 수 있습니다. 2세대 런타임에는 Nginx가 호스트 아키텍처에 도입되었습니다. 이러한 변경사항으로 인해 2세대 런타임은 밑줄 (_)이 있는 헤더를 자동으로 삭제하도록 구성됩니다. 애플리케이션 문제를 방지하려면 애플리케이션 요청 헤더에 밑줄을 사용하지 마세요.

app.yaml 파일의 변경사항

app.yaml 구성 파일의 일부 요소 동작이 수정되었습니다.

요소 유형 변경 설명
app_engine_apis Go 1.12 이상에만 적용 가능 Go 1.12 이상의 기존 번들 서비스에 액세스하려면 true로 설정해야 합니다.
login app_engine_apistrue인 경우 지원됨 Go 1.12 이상에 기존 번들 서비스를 사용하지 않는 경우 다른 방법으로 사용자를 인증합니다.
runtime 수정된 대비율 runtime 요소를 변경하여 Go 1.12+를 지정합니다.

자세한 내용은 app.yaml 참조를 확인하세요.

main 패키지 만들기

서비스는 최소 한 개 이상의 소스 파일package main 문을 포함해야 합니다. 또는 서비스에서 google.golang.org/appengine 패키지를 사용하고 있는 경우 appengine.Main()에 호출을 포함시킵니다.

main 패키지 작성

서비스에 아직 main 패키지가 없으면 package main 문을 추가하고 main() 함수를 작성합니다. 최소한 main() 함수가 다음을 수행해야 합니다.

  • PORT 환경 변수를 읽고 http.ListenAndServe() 함수를 호출합니다.

    port := os.Getenv("PORT")
    if port == "" {
    	port = "8080"
    	log.Printf("Defaulting to port %s", port)
    }
    
    log.Printf("Listening on port %s", port)
    if err := http.ListenAndServe(":"+port, nil); err != nil {
    	log.Fatal(err)
    }

HTTP 핸들러 등록

다음 옵션 중 하나를 선택하여 HTTP 핸들러를 등록할 수 있습니다.

  • 모든 http.HandleFunc() 호출을 사용 중인 패키지에서 main 패키지의 main() 함수로 직접 이동하는 것이 좋습니다.
  • 또는 애플리케이션의 패키지를 main 패키지로 가져옵니다. 이때 각 init() 함수(http.HandleFunc() 호출이 포함)가 시작 시 실행되는지 확인합니다.

    다음 bash 스크립트와 함께 http.HandleFunc() 호출을 사용하는 모든 패키지를 찾고 출력을 main 패키지의 import 블록에 복사할 수 있습니다.

    gp=$(go env GOPATH) && p=$(pwd) && pkg=${p#"$gp/src/"} && find . -name "*.go" | xargs grep "http.HandleFunc" --files-with-matches | grep -v vendor/ | grep -v '/main.go' | sed "s#\./\(.*\)/[^/]\+\.go#\t_ \"$pkg/\1\"#" | sort | uniq
    

파일 구조화

Go에서는 각 패키지마다 고유한 디렉터리가 있어야 합니다. 프로젝트의 app.yaml 파일에서 main:을 사용하여 main 패키지의 위치를 App Engine에 알려줄 수 있습니다. 예를 들어 앱의 파일 구조가 다음과 같습니다.

myapp/
├── app.yaml
├── foo.go
├── bar.go
└── web/
    └── main.go

app.yaml 파일에는 다음이 포함됩니다.

main: ./web # Relative filepath to the directory containing your main package.

main 플래그에 대한 자세한 내용은 app.yaml 참조를 확인하세요.

파일을 GOPATH로 이동

다음 명령어를 사용하여 GOPATH를 찾습니다.

go env GOPATH

모든 관련 파일 및 가져오기를 GOPATH로 이동합니다. import ./guestbook 등 관련 가져오기를 사용하는 경우 전체 경로(import github.com/example/myapp/guestbook)를 사용하도록 가져오기를 업데이트합니다.