1. ent
도구 설치
ent
코드 생성 도구를 설치하려면 다음 단계를 따르세요:
먼저 시스템에 Go 언어 환경이 설치되어 있는지 확인하세요. 그런 다음 다음 명령을 실행하여 ent
도구를 얻을 수 있습니다:
go get -d entgo.io/ent/cmd/ent
이 명령은 ent
도구의 코드를 다운로드하지만 즉시 컴파일하고 설치하지는 않습니다. $GOPATH/bin
디렉토리에 ent
를 설치하여 어디에서나 사용할 수 있도록 하려면 다음 명령도 실행해야 합니다:
go install entgo.io/ent/cmd/ent
설치가 완료되면 ent
도구가 올바르게 설치되었는지 확인하고 ent -h
를 실행하여 사용 가능한 명령 및 지침을 볼 수 있습니다.
2. 스키마 초기화
2.1 ent init
을 사용한 템플릿 초기화
코드 생성을 위해 ent
를 사용하기 시작하려면 새로운 스키마 파일을 만드는 것이 첫 단계입니다. 다음 명령을 실행하여 스키마 템플릿을 초기화할 수 있습니다.
go run -mod=mod entgo.io/ent/cmd/ent new User Pet
이 명령은 user.go
와 pet.go
두 개의 새로운 스키마 파일을 만들고 ent/schema
디렉토리에 배치합니다. ent
디렉토리가 없는 경우 이 명령은 자동으로 생성합니다.
프로젝트의 루트 디렉토리에서 ent init
명령을 실행하는 것이 프로젝트 디렉토리 구조와 명확성을 유지하는 데 도움이 되는 좋은 관행입니다.
2.2 스키마 파일 구조
ent/schema
디렉토리에는 각 스키마가 Go 언어 소스 파일에 해당합니다. 스키마 파일은 데이터베이스 모델을 정의하는 곳으로, 필드 및 엣지(관계)를 포함합니다.
예를 들어, user.go
파일에서는 사용자 모델을 정의하고, 사용자 이름 및 나이와 같은 필드를 정의하고 사용자와 애왁이의 관계를 정의할 수 있습니다. 마찬가지로 pet.go
파일에서는 애왁이 모델 및 애왁이 이름, 유형 등의 관련 필드를 정의하고 애왁이와 사용자 간의 관계를 정의할 수 있습니다.
이러한 파일은 최종적으로 ent
도구에 의해 대응하는 Go 코드를 생성하는 데 사용되며, 이는 데이터베이스 작업 및 CRUD(Create, Read, Update, Delete) 작업을 위한 클라이언트 코드를 포함합니다.
// ent/schema/user.go
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User는 사용자 엔티티의 스키마를 정의합니다.
type User struct {
ent.Schema
}
// Fields 메서드는 엔티티 필드를 정의하는 데 사용됩니다.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").Unique(),
field.Int("age").Positive(),
}
}
// Edges 메서드는 엔티티 연관을 정의하는 데 사용됩니다.
func (User) Edges() []ent.Edge {
// 관계에 대해 더 자세히 설명될 것입니다.
}
ent
의 스키마 파일은 데이터베이스 모델의 구조를 선언하기 위해 Go 언어 유형과 함수를 사용하고, ent
프레임워크에서 제공하는 API를 사용하여 이러한 구조를 정의합니다. 이 접근 방식은 ent
를 매우 직관적이고 확장하기 쉽게 만들어주며, 동시에 Go 언어의 강력한 유형 지정을 활용합니다.
3.1 코드 생성 실행
ent
프레임워크에서 코드 생성을 실행하는 것은 매우 중요한 단계입니다. 이 명령을 사용하면 정의된 스키마에 기반하여 해당하는 Go 코드 파일이 생성되어 후속 개발 작업을 용이하게 합니다. 코드 생성을 실행하는 명령은 간단합니다.
go generate ./ent
위의 명령은 프로젝트의 루트 디렉토리에서 실행해야 합니다. go generate
가 호출될 때 특정 어노테이션을 포함하는 모든 Go 파일을 검색하고 어노테이션에서 지정된 명령을 실행합니다. 예제에서는 ent
의 코드 생성기를 지정한 것입니다.
실행 전에 스키마 초기화와 필요한 필드 추가가 완료되었는지 확인하세요. 그렇지 않으면 생성된 코드에 모든 필요한 부분이 포함되지 않을 수 있습니다.
3.2 생성된 코드 자산 이해하기
생성된 코드 자산에는 각각 다른 기능을 가진 여러 구성 요소가 포함되어 있습니다:
-
클라이언트 및 Tx 객체: 데이터 그래프와 상호 작용하는 데 사용됩니다. 클라이언트는 트랜잭션(Tx)을 생성하거나 데이터베이스 작업을 직접 실행하는 메서드를 제공합니다.
-
CRUD 빌더: 각 스키마 유형에 대해 해당 엔티티의 작업 논리를 간단화하는 생성, 읽기, 업데이트 및 삭제 빌더를 생성합니다.
-
엔티티 객체 (Go 구조체): 스키마의 각 유형에 대한 해당 Go 구조체를 생성하여 데이터베이스의 테이블에 이러한 구조체를 매핑합니다.
-
상수 및 예측 패키지: 빌더와 상호 작용하기 위한 상수 및 예측을 포함합니다.
-
마이그레이션 패키지: SQL 방언에 적합한 데이터베이스 마이그레이션을 위한 패키지입니다.
-
후크 패키지: 변경 미들웨어를 추가하는 기능을 제공하여 엔티티 생성, 업데이트 또는 삭제 전후에 사용자 정의 논리를 실행할 수 있습니다.
생성된 코드를 조사함으로써 ent
프레임워크가 스키마에 대한 데이터 액세스 코드를 자동화하는 방법에 대해 더 깊이 이해할 수 있습니다.
4. 코드 생성 옵션
ent generate
명령은 코드 생성 프로세스를 사용자 정의하는 다양한 옵션을 지원합니다. 다음 명령을 통해 지원되는 모든 생성 옵션을 조회할 수 있습니다:
ent generate -h
다음은 일반적으로 사용되는 명령줄 옵션입니다:
-
--feature strings
: 추가 기능을 제공하여 코드 생성을 확장합니다. -
--header string
: 코드 생성 헤더 파일을 재정의합니다. -
--storage string
: 코드 생성에서 지원되는 저장 드라이버를 지정합니다. 기본값은 "sql"입니다. -
--target string
: 코드 생성을 위한 대상 디렉토리를 지정합니다. -
--template strings
: 추가 Go 템플릿을 실행합니다. 파일, 디렉토리 및 와일드카드 경로를 지원합니다. 예:--template file="path/to/file.tmpl"
.
이러한 옵션을 사용하여 개발자는 다양한 필요에 따라 코드 생성 프로세스를 사용자 정의할 수 있습니다.
5. 저장 옵션 구성
ent
는 기본적으로 SQL 방언을 지원하는 것 외에도 SQL 및 Gremlin 방언에 대한 코드 자산 생성을 지원합니다. 프로젝트가 Gremlin 데이터베이스에 연결해야 하는 경우 해당 데이터베이스 방언을 구성해야 합니다. 다음은 저장 옵션을 지정하는 방법을 보여줍니다:
ent generate --storage gremlin ./ent/schema
위 명령은 코드 생성 시 Gremlin 방언을 사용하도록 ent
에 지시합니다. 이를 통해 생성된 자산이 특정 그래프 데이터베이스와의 호환성을 보장하고 해당 데이터베이스 요구 사항에 맞게 맞추어지도록합니다.
6. 고급 사용: entc
패키지
6.1 프로젝트에서 entc
패키지로의 사용
entc
는 ent
프레임워크의 코드 생성에 사용되는 핵심 패키지입니다. 명령줄 도구 외에도 entc
를 프로젝트에 패키지로 통합하여 코드 생성 프로세스를 코드 자체에서 제어하고 사용자 정의할 수 있습니다.
프로젝트에서 entc
를 패키지로 사용하기 위해서는 entc.go
라는 파일을 생성하고 해당 파일에 다음 내용을 추가해야 합니다:
// +build ignore
package main
import (
"log"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
)
func main() {
if err := entc.Generate("./schema", &gen.Config{}); err != nil {
log.Fatal("running ent codegen:", err)
}
}
이 방법을 사용할 때, main
함수 내에서 gen.Config
구조체를 수정하여 다양한 구성 옵션을 적용할 수 있습니다. 필요에 따라 entc.Generate
함수를 호출하여 코드 생성 프로세스를 유연하게 제어할 수 있습니다.
6.2 entc
의 상세한 구성
entc
는 생성된 코드를 사용자 정의할 수 있도록 다양한 구성 옵션을 제공합니다. 예를 들어 사용자 정의 훅을 구성하여 생성된 코드를 검사하거나 수정하고, 의존성 주입을 통해 외부 의존성을 주입할 수 있습니다.
다음 예시는 entc.Generate
함수에 대한 사용자 정의 훅을 제공하는 방법을 보여줍니다:
func main() {
err := entc.Generate("./schema", &gen.Config{
Hooks: []gen.Hook{
HookFunction,
},
})
if err != nil {
log.Fatalf("ent 코드 생성 중 오류 발생: %v", err)
}
}
// HookFunction은 사용자 정의 훅 함수입니다
func HookFunction(next gen.Generator) gen.Generator {
return gen.GenerateFunc(func(g *gen.Graph) error {
// 여기서 g로 표현된 그래프 모드를 처리할 수 있습니다
// 예를 들어, 필드의 존재 유효성을 검사하거나 구조를 수정할 수 있습니다
return next.Generate(g)
})
}
또한, 외부 의존성을 entc.Dependency
를 사용하여 추가할 수 있습니다:
func main() {
opts := []entc.Option{
entc.Dependency(
entc.DependencyType(&http.Client{}),
),
entc.Dependency(
entc.DependencyName("Writer"),
entc.DependencyTypeInfo(&field.TypeInfo{
Ident: "io.Writer",
PkgPath: "io",
}),
),
}
if err := entc.Generate("./schema", &gen.Config{}, opts...); err != nil {
log.Fatalf("ent 코드 생성 중 오류 발생: %v", err)
}
}
이 예시에서는 http.Client
와 io.Writer
를 생성된 클라이언트 객체에 의존성으로 주입합니다.
7. 스키마 설명의 출력
ent
프레임워크에서 ent describe
명령은 스키마의 설명을 그래픽 형식으로 출력하는 데 사용될 수 있습니다. 이를 통해 개발자들은 기존 엔터티와 관계를 빠르게 이해할 수 있습니다.
다음 명령을 실행하여 스키마의 설명을 얻을 수 있습니다:
go run -mod=mod entgo.io/ent/cmd/ent describe ./ent/schema
위 명령은 각 엔터티의 필드, 유형, 관계 등과 같은 정보를 표시하는 테이블을 출력합니다:
User:
+-------+---------+--------+-----------+ ...
| Field | Type | Unique | Optional | ...
+-------+---------+--------+-----------+ ...
| id | int | false | false | ...
| name | string | true | false | ...
+-------+---------+--------+-----------+ ...
+-------+--------+---------+-----------+ ...
| Edge | Type | Inverse | Relation | ...
+-------+--------+---------+-----------+ ...
| pets | Pet | false | O2M | ...
+-------+--------+---------+-----------+ ...
8. 코드 생성 훅
8.1 훅의 개념
훅은 ent
코드 생성 프로세스에 삽입될 수 있는 미들웨어 함수로, 코드를 생성하기 전과 후에 사용자 정의 논리를 삽입할 수 있습니다. 훅은 생성된 코드의 추상 구문 트리 (AST)를 조작하거나 유효성 검사를 수행하거나 추가적인 코드 스니펫을 추가하는 데 사용할 수 있습니다.
8.2 훅 사용 예시
아래 예시는 모든 필드에 특정 구조 태그(e.g., json
)를 포함해야 함을 보장하기 위해 훅을 사용하는 예시입니다:
func main() {
err := entc.Generate("./schema", &gen.Config{
Hooks: []gen.Hook{
EnsureStructTag("json"),
},
})
if err != nil {
log.Fatalf("ent 코드 생성 중 오류 발생: %v", err)
}
}
// EnsureStructTag는 그래프의 모든 필드가 특정 구조 태그를 포함하도록 보장합니다
func EnsureStructTag(name string) gen.Hook {
return func(next gen.Generator) gen.Generator {
return gen.GenerateFunc(func(g *gen.Graph) error {
for _, node := range g.Nodes {
for _, field := range node.Fields {
tag := reflect.StructTag(field.StructTag)
if _, ok := tag.Lookup(name); !ok {
return fmt.Errorf("%s.%s 필드에 구조 태그 %q가 누락되었습니다", node.Name, field.Name, name)
}
}
}
return next.Generate(g)
})
}
}
이 예시에서는 코드를 생성하기 전에 EnsureStructTag
함수가 각 필드를 json
태그로 확인합니다. 만약 필드에 이 태그가 누락된 경우, 코드 생성이 중단되고 오류가 반환됩니다. 이는 코드 깨끗함과 일관성을 유지하는 효과적인 방법입니다.