Backend
Enterprise Go 시리즈 #1: 프로젝트 설계와 구조화
Go 서비스에서 유지보수 가능한 프로젝트 구조를 잡는 실전 기준을 정리합니다.
Enterprise Go 시리즈 #1: 프로젝트 설계와 구조화
핵심 원칙
main은 최대한 얇게 유지- 조립(composition)은
internal/app로 집중 - 공개 API가 아니면 기본값은
internal
권장 구조
project/
├── cmd/
│ └── api/
│ └── main.go
├── internal/
│ ├── app/
│ ├── api/
│ ├── service/
│ └── data/
├── pkg/ # 외부 공개가 필요할 때만
└── Makefile
Hollow Main 패턴
package main
func main() {
if err := app.Run(); err != nil {
os.Exit(1)
}
}
main에서 의존성 조립까지 다 하면 테스트 가능한 진입점이 사라집니다.
internal/app의 책임
- 설정 로딩
- 의존성 조립
- 프로세스 라이프사이클(시작/종료)
func Run() error {
cfg := LoadConfig()
db := data.NewDB(cfg)
svc := service.New(db)
server := api.NewHTTPServer(svc)
return server.Run()
}
진화 전략
- 초기 PoC: 단일
main.go - 코드가 커지면:
cmd + internal분리 - 바이너리가 늘면:
cmd/{api,worker,admin}확장
처음부터 과도하게 계층을 만들기보다, 경계가 생길 때 분리하는 방식이 유지보수에 유리합니다.
자주 하는 실수
pkg를 너무 일찍 열어 API 호환성 부담 생성- 도메인/인프라 경계를 파일 경로만으로 구분하고 책임은 섞어둠
main에서 설정, DI, 서버 실행, 시그널 처리까지 모두 처리
요약
좋은 Go 구조의 핵심은 화려한 폴더 트리가 아니라 책임 분리입니다.
main을 얇게 유지하고 internal/app를 조립 루트로 고정하면 규모가 커져도 구조가 무너지지 않습니다.