Go 언어로 애플리케이션을 만들기 위한 프로젝트 기본 레이아웃입니다. 디렉토리 구조를 통일된 패턴으로 정의하여 관리하여 각 디렉토리의 역할을 명확히 하고, 유지보수와 코드의 가독성을 높일 수 있습니다.

Architecture

다음과 같은 6가지 개념(Controller, Service, Repository, Storage, Domain, Pkg)을 기본 구조로 활용합니다. 각 개념에 대한 자세한 설명은 아래에 첨부하였습니다.

flowchart LR
    A0(user) --> B0
    A0(user) --> B1
    A0(user) --> B2
    A1(event) --> B3
    B0 --> C
    B1 --> C
    B2 --> C
    B3 --> C
    subgraph domain / pkg
    subgraph controller
    B0(controller/http)
    B1(controller/grpc)
    B2(controller/cmd)
    B3(controller/event)
    end
    C(service) --> |optionnal| D(repository) --> E(storage)
    end

Directories

├── cmd
│   ├── server
│   │   ├── internal
│   │   │   ├── env.go
│   │   │   └── server.go
│   │   └── server.go
│   ├── appname
│   │   ├── internal
│   │   │   ├── env.go
│   │   │   └── appname.go
│   │   └── appname.go
├── docs
├── internal
│   ├── controller
│   │   ├── cmd
│   │   ├── event
│   │   ├── grpc
│   │   └── http
│   ├── domain
│   ├── storage
│   │   ├── ...
│   │   └── ent
│   ├── repository
│   ├── service
│   └── pkg
├── mocks
├── testdata
│   .gitignore
│   .mockery.yaml
│   Dockerfile
│   Makefile
│   go.mod
│   go.sum
└── README.md

/cmd

프로젝트의 메인 애플리케이션(즉, main 패키지)이 위치하는 디렉토리입니다. cmd 디렉토리에는 최소한의 코드만 작성하고, 재사용성이 필요한 코드는 /internal 디렉토리에 작성합니다.

automaxprocs

automaxprocs 패키지는 CPU 코어 수를 자동으로 설정하도록 도와주는 도구입니다.(GOMAXPROCS 값을 자동으로 설정합니다.) container 환경에서 실행하려면 main 패키지에 go.uber.org/automaxprocs 패키지를 항상 import하여 사용합니다.

package main

import (
  "fmt"

  _ "go.uber.org/automaxprocs"
)

func main() {
  fmt.Println("Hello")
}

/cmd/appname/appname.go

애플리케이션의 main 패키지로, 실행 가능한 애플리케이션의 진입점입니다. 디렉토리 이름과 파일 이름이 애플리케이션 이름과 동일하게 사용합니다..

package main

import (
  "github.com/rs/zerolog/log"
  _ "go.uber.org/automaxprocs"

  "github.com/org/repository/cmd/appname/internal"
)

func main() {
  // execute application
  if err := internal.Run(); err != nil {
    log.Fatal().Err(err)
  }
}

/cmd/appname/internal

애플리케이션 실행에 필요한 환경 변수 설정 및 관련 코드를 작성하는 디렉토리입니다. 이 코드는 애플리케이션의 main 패키지에서 임포트하여 사용합니다.

/cmd/appname/internal/env.go

env 패키지를 사용하여 애플리케이션 실행에 필요한 환경 변수를 정의하는 파일입니다. 다른 패키지에서는 환경 변수를 직접 참조하지 않으며, 필요한 환경 변수 값을 주입받아 사용합니다.

package internal

import (
  "github.com/caarlos0/env/v11"
)

// server environment
type environment struct {
  AWSRegion string `env:"AWS_REGION"`
}

func parse() (*environment, error) {
  var e environment
  if err := env.Parse(&e); err != nil {
    return nil, err
  }

  return &e, nil
}

/cmd/appname/internal/appname.go

메인 패키지 내에서 애플리케이션을 실행하는 데 필요한 핵심 코드를 작성합니다.

package internal

import (
  "context"

  "github.com/getsentry/sentry-go"
  dd_tracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
)

func Run() error {
  // Initialize context
  ctx := context.Background()

  // Parse environment variables
  env, err := parse()
  if err != nil {
    return err
  }

  // initialize sentry
  if err := sentry.Init(sentry.ClientOptions{}); err != nil {
    return err
  }
  defer sentry.Flush(2 * time.Second)

  // initialize datadog tracer
  dd_tracer.Start()
  defer dd_tracer.Stop()

  ...
}

/docs

프로젝트와 관련된 문서 파일을 저장하는 디렉토리입니다. API 문서, 아키텍처 다이어그램, README 파일, Swagger 등 다양한 문서화를 포함할 수 있습니다.

/internal

프로젝트 내부에서 사용되는 다양한 공통 라이브러리 및 모듈을 포함하는 최상위 디렉토리입니다. 다음과 같은 하위 디렉토리를 포함합니다. Go 언어에서 internal 패키지는 Go 1.4 이후 도입된 기능으로, 특정 패키지를 외부에서 사용할 수 없도록 제한하는 역할을 합니다. 이를 통해 모듈화와 캡슐화를 강화하며, 패키지 설계 시 의도하지 않은 사용을 방지하는 데 유용합니다.

/internal/pkg

프로젝트 내부에서 재사용 가능한 공통 라이브러리를 작성하는 공간입니다.

/internal/controller/http

HTTP 서버 요청을 처리하는 레이어입니다. HTTP 서버는 Echo 프레임워크를 사용합니다.

/internal/controller/grpc

gRPC 서버 요청을 처리하는 레이어입니다. grpc-go / grpc-ecosystem 참고하여 구현합니다.

/internal/controller/cmd

CLI 명령어 구현을 레이어입니다. Cobra 라이브러리를 사용하여 CLI 명령어를 구현합니다.

/internal/controller/event

event 기반 서버 요청을 처리하는 레이어입니다.

/internal/domain

프로젝트 전반에 사용되는 데이터 구조체(Struct)를 관리하는 레이어입니다.

/internal/storage

데이터베이스, Redis, DynamoDB, ORM 등 인프라에 종속적인 코드를 작성하는 레이어입니다. ORM은 ent를 사용합니다.

/internal/repository

storage 패키지를 의존하여 데이터 핸들링을 수행하는 코드를 작성하는 레이어입니다.

/internal/service

비즈니스 로직을 처리하는 레이어입니다. 각종 비즈니스 규칙을 서비스로 구현합니다. 데이터 핸들링이 필요한 경우 repository패키지를 의존하여 작성합니다.

/mocks

테스트 코드에서 사용할 Mocking 관련 코드를 저장하는 디렉토리입니다. 실제 서비스나 데이터베이스 연결 없이도 테스트할 수 있도록 모의 객체(Mock Object)를 정의하여 독립적인 테스트 환경을 제공합니다. Mockery 라이브러리를 사용하여 인터페이스에 대한 Mock 객체를 자동 생성하고, 이를 통해 테스트의 신뢰성을 높입니다.

/testdata

테스트 코드에서 사용할 데이터를 저장하는 디렉토리입니다. 정적 파일, JSON 샘플 데이터, 이미지 파일 등이 포함될 수 있습니다.

Example

controller, service, repository 구조에 대한 예제입니다. interface를 사용하여 service에서 repository를 주입시키고, controller에서 service를 주입합니다.

package domain

import "time"

type User struct {
  ID int64
  Email string
  CreatedAt time.Time
}
package repository

type UserRepository interface {
  FindByID(ctx context.Context, id int64) (*domain.User, error)
}
package service

type UserService interface {
  GetUserEmail(ctx context.Context, id int64) (string), error)
}

// interface를 사용하여 service, controol 사요
type userService struct {
  repository repository.UserRepository
}

func New(repository repository.UserRepository) UserService {
  return &userService{repository: repository}
}
package controller

type userController struct {
  service service.UserService
}

func New(service service.UserService) *userController {
  return &userController{service: service}
}

External Package

유용하게 사용하고 있는 외부 패키지를 정리해두었습니다.

env

환경 변수를 구조체(Struct) 형태로 쉽게 관리할 수 있도록 도와주는 패키지입니다. 설정 관리가 편리해지며, 코드 가독성을 높여줍니다.

zerolog

프로젝트 내부에서 구조화된 로그를 출력하기 위한 패키지입니다. 고성능, Zero Allocation의 효율적인 로깅을 지원하여 프로젝트의 성능을 높입니다.

go-json

Go의 표준 encoding/json 패키지를 대체하는 JSON 처리 라이브러리로, 성능이 뛰어나 JSON 처리 속도와 효율성을 높일 수 있습니다.

validator

유효성 검사를 위한 패키지로, 구조체와 필드에 다양한 검증 규칙을 적용하여 데이터의 무결성을 확보할 수 있습니다.

echo

HTTP 서버를 만들기 위한 웹 프레임워크입니다.

ent

RDBMS와의 상호작용을 위해 사용하는 ORM 라이브러리로, 타입 안전성을 보장하고 스키마 관리 및 데이터베이스 쿼리를 효율적으로 처리할 수 있습니다.

cobra

CLI(Command Line Interface) 도구를 개발할 때 사용하는 패키지로, 명령어 구조, 플래그 및 하위 명령어 등을 쉽게 구현할 수 있도록 지원합니다.

testify

Go 언어에서 테스트를 쉽게 작성할 수 있도록 도와주는 패키지입니다. 다양한 기능을 제공하며, 특히 Assertions 기능으로 코드가 예상대로 작동하는지 간단하게 검증할 수 있습니다. 또한, Mock 객체를 활용하여 테스트의 신뢰성을 높일 수 있습니다. testify는 단위 테스트의 품질을 높이고, 테스트 코드 작성을 간소화하는 데 유용합니다.

mockery

인터페이스에 대한 Mock 객체를 자동으로 생성해주는 패키지입니다. 이를 통해 테스트에서 실제 구현체 대신 Mock 객체를 사용함으로써 테스트의 독립성과 신뢰성을 높일 수 있습니다.

swag

Swagger 2.0 규격에 맞춰 RESTful API 문서를 자동으로 생성하는 패키지입니다. API의 엔드포인트, 요청, 응답 형식을 쉽게 시각화하여 개발자 간의 소통을 원활하게 하고 문서화를 간소화할 수 있습니다.

sentry-go

Sentry 연동을 위한 패키지입니다. 애플리케이션에서 발생하는 오류와 예외를 모니터링하고 추적할 수 있도록 지원하는 패키지입니다.

dd-trace-go

애플리케이션의 성능을 모니터링하고 추적할 수 있도록 해주는 DataDog APM 연동을 위한 패키지입니다

aws-sdk-go-v2

AWS 서비스를 쉽게 사용할 수 있도록 해주는 AWS SDK입니다. SDK를 통해 Amazon S3, DynamoDB, EC2, Lambda 등 다양한 AWS 서비스를 Go 애플리케이션에서 간편하게 호출하고 관리할 수 있습니다. aws-sdk-go-v2는 성능과 효율성을 고려하여 설계되었으며, 비동기 API 지원과 더불어 간결한 코드로 AWS 리소스와 상호작용할 수 있어 클라우드 기반 애플리케이션 개발에 유용합니다.