IT/GoLang

세번째 글 - 패키지 만들기

Aaron's papa 2021. 10. 4. 15:53
반응형

애플리케이션이 해야 할 일이 많아지고 규모가 커지게 되면 코드를 분리해야 하는 경우가 생깁니다. 우리가 지금까지 만들었던 것처럼 main.go라는 파일 안에 모든 로직들을 넣을 순 없게 됩니다. 그래서 패키지화라는 과정을 통해 애플리케이션의 로직을 비슷한 역할을 하는 코드들끼리 모아 둡니다. 파이썬에서는 디렉터리를 만들고 __init__. py라는 파일을 만들어서 패키지화를 할 수 있습니다. 비슷한 방법으로 Go 언어 역시 패키지화를 할 수 있습니다. 하지만 Go 언어에서의 패키지화는 개인적으로는 파이썬 보다 훨씬 편하다고 생각합니다. 그리고 Go 언어가 가지는 여러 가지 특징 중 하나가 손쉬운 패키지화라고도 생각합니다. 이번 글에서는 우리가 앞에서 다뤘던 애플리케이션을 패키지화해서 관리하는 방법에 대해서 살펴보겠습니다.


패키지로 나누기

우리가 지난 시간에 만든 애플리케이션은 /, /hello, /bye를 처리할 수 있도록 구성되어 있습니다. 이 과정에서 /helloDoHello() 함수에서 /byeDoBye() 함수에서 처리하게 해 두었죠. DoHello()DoBye() 함수는 현재 아주 간단한 형태이지만 이 함수들의 규모가 커지게 되면 main.go 파일도 커지게 되고 가독성이 떨어지겠죠. 그래서 이 두 함수를 별도의 패키지로 나눠 보겠습니다. Path 들을 처리할 수 있는 함수들을 한 곳에 모아 보겠습니다. 패키지의 이름은 path로 해보겠습니다.

패키지로 나누기 전에 현재 구조를 살펴보겠습니다.

❯ tree
.
├── bin
│   └── hello-world
├── go.mod
├── go.sum
├── main.go
├── serverless.yaml
└── vendor

우리가 현재 만든 애플리케이션은 위와 같은 구조입니다. main.go 함수에 모든 로직들이 포함되어 있죠. 이제 path라는 디렉터리를 만들어 주고 hello.gobye.go 두 개의 파일을 만들어 주겠습니다. 그리고 두 개의 파일은 모두 아래와 같이 내용을 채워 줍니다.

만약 GoLand로 두 개의 파일을 만들었다면 아래 내용이 자동으로 입력될 겁니다.
package path

이제 애플리케이션 구조는 아래와 같이 됩니다.

❯ tree
.
├── bin
│   └── hello-world
├── go.mod
├── go.sum
├── main.go
├── path
│   ├── bye.go
│   └── hello.go
├── serverless.yaml
└── vendor

그리고 hello.gobye.go 파일에 기존에 만들어 두었던 DoHello() 함수와 DoBye() 함수 두 개를 각각 작성해 줍니다.

// hello.go 파일
package path

func DoHello() string {
    return "Hello From Path Module"
}

// bye.go 파일
package path

func DoBye() string {
    return "Bye From Path Module"
}

DoHello() 함수와 DoBye() 함수는 같은 이름으로 main.go 함수에 있는데 괜찮은 걸까?라는 의문이 생길 텐데요, 결론적으로 말씀드리면 괜찮습니다. main.go 파일에 있는 DoHello() 함수와 hello.go 파일에 있는 DoHello() 함수는 전혀 다른 함수입니다. 왜 그런 걸까요? 이건 패키지의 범위와 관련이 있습니다.


패키지의 범위

기본적으로 Go 언어의 패키지는 아래와 같은 규칙을 가집니다.

  1. 같은 패키지의 함수들은 하나의 디렉터리에 모여 있어야 합니다. 우리가 위에서 만든 Path라는 패키지를 구성하는 모든 함수와 변수 등은 서로 다른 파일 일지라도 같은 디렉터리 안에 있어야 합니다. 이때 디렉터리의 이름과 패키지의 이름은 달라고 상관없습니다만, 가독성을 위해 동일하게 유지하는 것이 좋습니다.
  2. 이름을 소문자로 시작하는 함수는 같은 패키지 안에서는 사용할 수 있습니다. 그래서 패키지 내부에서만 사용하는 함수는 이름을 소문자로 시작해야 합니다.
  3. 이름을 대문자로 시작하는 함수는 패키지 외부에서 사용할 수 있습니다. 패키지의 외부에서 호출해야 하는 함수는 이름을 대문자로 시작해야 합니다.

그래서 main.go 파일 안에 있는 DoHello() 함수는 main 패키지에 속하는 함수이고 hello.go 파일 안에 있는 DoHello() 함수는 path 패키지에 속하는 함수이기 때문에 이름이 같지만 완전 다른 함수라고 생각하면 됩니다. 그럼 이제 main.go 에서 path 패키지에 있는 함수를 호출하도록 해보겠습니다.


패키지 호출하기

이제부터가 중요한데요, 앞에서 정의한 패키지를 어떻게 사용할 수 있을까입니다. 바로 Go 언어의 import 구문을 통해서 사용할 수 있습니다. 그래서 아래와 같은 import 구문을 통해서 path 패키지의 함수와 변수들을 사용할 수 있습니다.

import (
    "fmt"
    "net/http"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"

    "hello-world-go/path"
)

hello-world-go는 우리가 첫 번째 글에서 go mod init hellow-world-go라는 명령을 통해 만들었던 바로 그 모듈의 이름입니다. 그리고 그 안에 path라는 패키지를 만들었으니 hello-world-go/path라는 이름으로 새로 만든 path 패키지를 호출할 수 있게 됩니다.

우리가 만든 path 패키지를 가져왔으니 함수를 호출해 보겠습니다.

func handler(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error){
    var responseBody string
    fmt.Println(event)

    switch event.Path {
    case "/hello":
        responseBody = path.DoHello() // DoHello()를 path.DoHello()로 수정
    case "/bye":
        responseBody = path.DoBye() // DoBye()를 path.DoBye()로 수정
    default:
        responseBody = "Default"
    }
    return events.APIGatewayProxyResponse{
        StatusCode: http.StatusOK,
        Body: responseBody,
    }, nil
}

이제 main.go 파일에 만들었던 DoHello() 함수와 DoBye() 함수는 삭제합니다. 그럼 main.go 함수에는 API Gateway로부터 받은 event를 기반으로 각각의 Path에 맞는 함수를 호출하는 기본적인 로직만 남게 되고 나머지 실질적인 처리 로직은 모두 path 패키지에 있는 함수들이 처리하게 됩니다. 이를 통해 파일의 내용을 간소화하고 로직을 분리시켜 가독성을 높일 수 있습니다. 그리고 serverless로 다시 배포하고 테스트를 해보겠습니다.

❯ curl https://o2slv6y2ac.execute-api.ap-northeast-2.amazonaws.com/dev/bye
Bye From Path Module

❯ curl https://o2slv6y2ac.execute-api.ap-northeast-2.amazonaws.com/dev/hello
Hello From Path Module

우리가 의도했던 대로 변경된 함수에서 처리된 스트링으로 잘 동작하는 것을 볼 수 있습니다.


마치며

이번 글에서는 패키지화를 통해 애플리케이션을 구조화하는 방법에 대해서 살펴봤습니다. 이제 우리는 함수를 만들 수 있고, 각각의 함수를 용도에 맞는 모듈로 나눠서 패키지화 하는 방법까지 살펴 봤습니다. 이를 통해 조금 더 큰 규모의 애플리케이션을 만들 수 있고 코드의 가독성을 높일 수 있습니다. 다음 글에서는 AWS의 각 서비스를 호출해서 실제 업무에 도움이 되는 쓸모 있는 애플리케이션을 만들어 보겠습니다.

반응형