Go
    • PDF

    Go

    • PDF

    기사 요약

    Go(Go Language) 형식의 액션을 생성하고 다양하게 활용하는 방법과 예제를 소개합니다.

    액션 생성

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

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

    Go로 작성한 코드는 여러 개의 함수를 포함할 수 있지만 main 함수는 프로그램의 시작점으로서 반드시 선언되어야 합니다. 이 점을 고려하여 이름과 장소를 포함하여 "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
    }
    

    위에서 작성한 코드를 사용하여 콘솔에서 'hello'라는 이름의 액션을 생성하는 과정은 다음과 같습니다.

    compute-15-2-701.png

    지원 가능 형태

    실행 환경에서는 지원 가능한 형태는 다음과 같습니다.

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

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

    Go Modules를 사용해서 액션 생성

    go1.19 버전에서 외부 라이브러리를 사용하기 위해서는 Go Modules를 사용합니다. Go Modules를 사용하지 않는 경우, 소스 코드를 직접 포함해야 합니다.
    다음은 go mod를 사용하여 액션에서 아래 패키지를 가져오는 방법에 대한 단계별 가이드입니다. 해당 방법은 인터넷 통신이 가능해야 하며, VPC 환경인 경우 NAT G/W 설정이 필요합니다. NAT G/W 관련 자세한 내용은 NAT Gateway 사용 가이드를 참고해 주시기 바랍니다.

    import "github.com/rs/zerolog"
    
    1. 새 모듈 초기화

    다음 명령을 실행하여 새 모듈을 초기화합니다. 해당 명령을 실행하면, go.mod 파일이 생성됩니다.

    go mod init <module>
    
    1. main.go 작성

    아래 예제 코드를 참고하여 main.go를 작성합니다.

    package main
    
    import (
    	"github.com/rs/zerolog"
    	"github.com/rs/zerolog/log"
    )
    
    func init() {
    	zerolog.TimeFieldFormat = ""
    }
    
    func Main(obj map[string]interface{}) map[string]interface{} {
    	name, ok := obj["name"].(string)
    	if !ok {
    		name = "world"
    	}
    	log.Debug().Str("name", name).Msg("Hello")
    	msg := make(map[string]interface{})
    	msg["module-main"] = "Hello, " + name + "!"
    	return msg
    }
    
    1. 의존성 가져오기

    아래 명령을 실행하여 go.mod 파일을 업데이트하고, go.sum 파일을 생성합니다.

    go get github.com/rs/zerolog@v1.19.0
    
    1. 액션 패키징 및 업로드

    다음 명령을 실행하여 패키지를 압축하고 업로드하여 액션을 생성합니다.

    zip -r action.zip go.mod go.sum main.go
    

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

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

    zip 파일을 이용하여 액션을 생성하는 경우 가능한 세 가지 형태는 다음과 같습니다.

    • 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-party 라이브러리를 사용해야 하는 경우 런타임은 컴파일할 때 인터넷을 통해 해당 라이브러리를 다운로드하지 않고, vendor 폴더 구조를 사용해서 다운로드하고 배치해야 합니다. 여기서는 dep 도구를 사용하는 방법을 설명합니다.
    vendor 폴더는 src 폴더, 패키지 폴더, vendor 폴더를 포함해야 합니다. 최상위 폴더에서 작동하지 않습니다. 만약 main 패키지에 포함된 파일을 사용하기 위해서는 최상위 폴더가 아닌 main으로 명시된 하위 폴더에 배치해야 합니다. 예를 들어 파일 src/hello/hello.go에서 다음과 같은 패키지를 import해야 하는 상황을 가정해 보겠습니다.

    import "github.com/sirupsen/logrus"
    

    이러한 경우 vendor 폴더를 생성하려면 다음과 같은 순서로 진행해 주십시오.

    1. dep 도구를 설치해 주십시오.
    2. src/hello 폴더로 이동해 주십시오.
      cd ./src/hello
      
      • src 폴더가 아니라 src/hello인 점에 주의해 주십시오.
    3. 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 폴더를 다시 생성할 수 있으므로 별도로 저장할 필요가 없습니다. 매니페스트 파일만 저장하면 되지만 컴파일된 상태로 액션을 생성하려면 vendor 폴더를 포함해야 합니다.
    만약 main 함수에서 3rd-party 라이브러리를 사용하려는 경우 최상위에 있는 main 패키지의 파일들을 main 폴더로 이동하여 vendor 폴더를 생성해야 합니다.


    이 문서가 도움이 되었습니까?

    What's Next
    Changing your password will log you out immediately. Use the new password to log back in.
    First name must have atleast 2 characters. Numbers and special characters are not allowed.
    Last name must have atleast 1 characters. Numbers and special characters are not allowed.
    Enter a valid email
    Enter a valid password
    Your profile has been successfully updated.