Go 액션 사용하기

Cloud Functions에서는 Go Language (이하 Go) 코드를 실행시킬 수 있는 액션 컨테이너를 제공합니다. Go 1.1 버전을 기본으로 제공하며 독립된 환경에서 코드를 실행할 수 있습니다.

미리 컴파일하여 Zip 형태로 업로드하거나, Web상에서 코드를 편집하여 실행할 수 있습니다.

액션 생성하기

먼저 아래와 같이 이름과 장소를 포함한, "Hello World"를 출력하는 간단한 GO 액션인 hello를 작성합니다.

package main
import "log"

func Main(obj map[string]interface{}) map[string]interface{} {
  name, ok := obj["name"].(string)
  if !ok {
    name = "world"
  }
  msg := make(map[string]interface{})
  msg["message"] = "Hello, " + name + "!"
  log.Printf("name=%s\n", name)
  return msg
}

Go 코드는 1개 이상의 Go 소스파일을 가질 수 있습니다. Go 소스로 만든 Action의 진입점은 Main Package에 있는 함수입니다. Main 함수의 기본 이름은 Main이지만 사용자의 선택에 따라 다른 이름으로 변경할 수 있습니다. 하지만 이름의 시작점은 항상 대문자로 표기됩니다. 그리고 해당 Main 함수는 다음의 설명과 같이 특정 형태로 만들어져야 합니다.

main 함수의 형태

func Main(event map[string]interface{}) map[string]interface{}

GO 코드는 여러 개의 함수를 포함할 수 있지만 main 함수는 프로그램의 시작점으로서 반드시 선언되어야 합니다.

위에서 작성한 코드로 'hello'라는 이름의 액션을 생성합니다.

img

지원 가능 형태

실행 환경에서는 다음의 형태를 지원합니다.

  • AMD64 아키텍처용으로 컴파일된 Linux ELF 실행 파일의 실행 가능 바이너리
  • AMD64 아키텍처용으로 컴파일된 Linux ELF 실행 파일을 포함하고 최상위에 exec 이름의 실행 파일을 포함하는 zip 파일
  • Go에서 컴파일된 단일 소스 파일
  • 최상위 레벨(폴더)에 실행 바이너리 파일을 포함하지 않은 zip 파일은 차후 컴파일되어 실행됩니다.

GOOS=LinuxGOARCH=amd64로 모든 Go를 지원하는 플랫폼에서 올바른 형식의 바이너리를 크로스컴파일할 수 있습니다. 뒤에 설명하는 것과 같이 사전 컴파일 기능을 사용하여 실행 환경과 동일한 컴파일러를 사용하는 것이 더 안전합니다.

패지키와 vendor를 사용해서 액션 생성하기

코드를 작성하다 보면 하나의 액션 파일 이외의 의존 파일들을 함께 패키징해야 하는 경우가 있습니다. 이런 경우, 관련된 파일들을 하나로 압축하여 패키징하고 압축된 파일을 이용하여 패키징 된 액션을 생성할 수 있습니다.

zip 형태로 Action을 만들 때 아래 3가지 형태로 제작 가능합니다

  • main 패키지 안에 모든 기능을 구현한 경우
  • main 패키지 이외에 일부 패키지를 분리하여 구성한 경우
  • 기능 구현을 위해 외부 종속성을 가진 부분을 포함하는 형태로 구현한 경우(include third party dependencies)

모든 기능이 기본 패키지에 있는 경우 그냥 모든 소스 파일을 zip 파일의 최상위 레벨에 배치하면 됩니다.

패키지 폴더 사용

일부 기능이 메인 함수 실행 부분과 다른 패키지에 속한다면 hello/와 같이 패키지 이름에 신경써서 폴더를 패키징해야 합니다. 아래는 이렇게 패키징된 형태의 예입니다.

golang-main-package/
- src/
   - main.go
   - hello/
       - hello.go
       - hello_test.go

테스트를 실행하고 오류없이 편집하려면 src 폴더를 사용해야 합니다. 기본 패키지의 내용들은 src/ 아래에 위치하게 하고, hello 패키지의 소스코드들은 hello/ 폴더에 위치하게 합니다.

사용을 위해서는 필수적으로 하위 패키지를 import "hello"와 같이 불러와야 합니다. 이는 만약 로컬 개발환경에서 컴파일할 경우 사용자의 GOPATHsrc의 상위 디렉토리를 설정해야 한다는 의미입니다. 만약 사용자가 VSCode와 같은 편집기를 사용한다면 go.inferGopath 옵션을 활성화해야 합니다.

소스를 보낼 때 최상위 디렉토리가 아닌 src 폴더의 내용을 아래와 같이 압축해야 합니다.

cd src
zip -r ../hello.zip *
cd ..

위의 예제 파일은 아래와 같다.

src/main.go

package main

import (
    "fmt"
    "hello"
)

// Main forwading to Hello
func Main(args map[string]interface{}) map[string]interface{} {
    fmt.Println("Main")
    return hello.Hello(args)
}

src/hello/hello.go

package hello

import (
    "fmt"
)

// Hello receive an event in format
// { "name": "Mike"}
// and returns a greeting in format
// { "greetings": "Hello, Mike"}
func Hello(args map[string]interface{}) map[string]interface{} {
    res := make(map[string]interface{})
    greetings := "world"
    name, ok := args["name"].(string)
    if ok {
        greetings = name
    }
    res["golang-main-package"] = "Hello, " + greetings
    fmt.Printf("Hello, %s\n", greetings)
    return res
}

src/hello/hello_test.go

package hello

import (
    "encoding/json"
    "fmt"
)

func ExampleHello() {
    var input = make(map[string]interface{})
    input["name"] = "Mike"
    output := Hello(input)
    json, _ := json.Marshal(output)
    fmt.Printf("%s", json)
    // Output:
    // Hello, Mike
    // {"golang-main-package":"Hello, Mike"}
}

func ExampleHello_noName() {
    var input = make(map[string]interface{})
    output := Hello(input)
    json, _ := json.Marshal(output)
    fmt.Printf("%s", json)
    // Output:
    // Hello, world
    // {"golang-main-package":"Hello, world"}
}

vendor 폴더 사용

다른 3rd 파티 라이브러리를 사용해야 하는 경우 런타임은 컴파일할 때 인터넷을 통해 해당 라이브러리를 다운로드하지 않습니다. vendor 폴더 구조를 사용해서 다운로드하고 배치 해야 합니다. 여기서는 dep 도구를 사용하는 방법을 설명합니다.

vendor 폴더는 src 폴더와 패키지 폴더, 그리고 vendor 폴더를 포함해야 하고 최상위 폴더에서 작동하지 않습니다. 만약 main 패키지에 포함된 파일을 사용하기 위해서는 최상위 폴더가 아닌 main으로 명시된 하위 폴더에 배치해야 합니다.

예를 들어 파일 src/hello/hello.go에서 아래 패키지를 import 한다면,

import "github.com/sirupsen/logrus"

vendor 폴더를 만들기 위해서는 다음의 순서를 진행합니다.

  • dep 도구를 설치하세요.

  • src/hello 폴더에 들어가세요.(src 폴더가 아닙니다.)

    cd ./src/hello
    
  • DEPPROJECTROOT=$(realpath $PWD/../..) dep init를 실행하세요.

이 도구는 사용된 라이브러리를 탐색/감지 하고 2개의 매니페스트 파일 Gopkg.lock, Gopkg.toml을 만듭니다. 이미 매니페스트 파일이 있는 경우 dep ensure를 실행하면 vendor 폴더가 생성되고 종속된 파일이 다운로드됩니다.

구조를 정리해 보면 아래와 같습니다.

golang-hello-vendor
- src/
    - hello.go
    - hello/
      - Gopkg.lock
      - Gopkg.toml
         - hello.go
         - vendor/
            - github.com/...
            - golang.org/...

위의 예제 파일은 아래와 같습니다.

hello.go

package main

import (
    "fmt"
    "hello"
)

// Main forwading to Hello
func Hello(args map[string]interface{}) map[string]interface{} {
    fmt.Println("Entering Hello")
    return hello.Hello(args)
}

hello/hello.go

package hello

import (
    "os"
    "github.com/sirupsen/logrus"
)

var log = logrus.New()

// Hello receive an event in format
// { "name": "Mike"}
// and returns a greeting in format
// { "greetings": "Hello, Mike"}
func Hello(args map[string]interface{}) map[string]interface{} {
    log.Out = os.Stdout
    res := make(map[string]interface{})
    greetings := "world"
    name, ok := args["name"].(string)
    if ok {
        greetings = name
    }
    res["golang-hello-vendor"] = "Hello, " + greetings
    log.WithFields(logrus.Fields{"greetings": greetings}).Info("Hello")
    return res
}

버전 관리 시스템에서 vendor 폴더를 다시 생성할 수 있으므로 따로 저장을 할 필요가 없습니다. 매니페스트 파일만 저장하세요. 하지만 컴파일된 상태로 Action을 생성하기 위해서는 vendor 폴더를 포함해야 합니다.

만약 main 함수에서 서드파티 라이브러리를 사용하고 싶다면 최상위에 있는 main 패키지의 파일들을 main 폴더 내로 이동하여 vendor 폴더를 만들어야 합니다. 최상위 폴더는 인식할 수 없습니다.

기본 파라미터 설정하기

매번 액션을 실행할 때마다 파라미터를 전달하는 대신, 특정 파라미터에 기본값을 지정할 수 있습니다. 위에서 생성한 hello 액션의 place 파라미터에 기본 값을 등록해보도록 하겠습니다.

img

액션 실행 시 파라미터 전달하기

액션 실행 시 입력으로 파라미터를 전달할 수 있습니다. Main 함수에 전달되는 파라미터는 JSON object 형식으로 전달됩니다.

파라미터는 액션 실행 시 직접 입력하거나, JSON 형식의 파일을 작성하여 전달할 수 있습니다. 파일을 통해 파라미터를 전달하는 경우에는 아래와 같은 JSON 형식의 파일을 작성해야 합니다.

img

연관 정보 바로가기

아래 가이드에서 연관 정보를 확인할 수 있습니다.

""에 대한 건이 검색되었습니다.

    ""에 대한 검색 결과가 없습니다.

    처리중...