1. ent 소개
Ent는 Go 언어를 위해 Facebook에서 개발한 엔터티 프레임워크입니다. 대규모 데이터 모델 애플리케이션을 구축하고 유지하는 과정을 단순화합니다. ent 프레임워크는 주로 다음 원칙을 따릅니다:
- 데이터베이스 스키마를 그래프 구조로 쉽게 모델링합니다.
- Go 언어 코드 형식으로 스키마를 정의합니다.
- 코드 생성을 기반으로 정적 타입을 구현합니다.
- 데이터베이스 쿼리 및 그래프 탐색 작성이 매우 간단합니다.
- Go 템플릿을 사용하여 쉽게 확장 및 사용자 정의할 수 있습니다.
2. 환경 설정
ent 프레임워크를 사용하려면 개발 환경에 Go 언어가 설치되어 있는지 확인해야 합니다.
프로젝트 디렉토리가 GOPATH
외부에 있는 경우 또는 GOPATH
를 잘 모르는 경우, 다음 명령어를 사용하여 새로운 Go 모듈 프로젝트를 생성할 수 있습니다:
go mod init entdemo
이렇게 하면 새로운 Go 모듈이 초기화되고 entdemo
프로젝트를 위한 새로운 go.mod
파일이 생성됩니다.
3. 첫 번째 스키마 정의
3.1. ent CLI를 사용하여 스키마 생성
먼저 프로젝트의 루트 디렉토리에서 ent CLI 도구를 사용하여 User라는 스키마를 생성하려면 다음 명령어를 실행해야 합니다:
go run -mod=mod entgo.io/ent/cmd/ent new User
위 명령어는 entdemo/ent/schema/
디렉토리에 User 스키마를 생성합니다:
파일 entdemo/ent/schema/user.go
:
package schema
import "entgo.io/ent"
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return nil
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return nil
}
3.2. 필드 추가
다음으로, User 스키마에 필드 정의를 추가해야 합니다. 아래는 User 엔터티에 두 개의 필드를 추가하는 예시입니다.
수정된 파일 entdemo/ent/schema/user.go
:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").
Positive(),
field.String("name").
Default("unknown"),
}
}
이 코드 조각은 User 모델에 두 개의 필드를 정의합니다: age
는 양의 정수이고 name
은 "unknown"이라는 기본값을 갖는 문자열입니다.
3.3. 데이터베이스 엔터티 생성
스키마를 정의한 후에는 이를 기반으로 하위 데이터베이스 접근 로직을 생성하기 위해 go generate
명령어를 실행해야 합니다.
프로젝트의 루트 디렉토리에서 다음 명령어를 실행합니다:
go generate ./ent
이 명령어는 이전에 정의한 스키마를 기반으로 해당하는 Go 코드를 생성하여 다음과 같은 파일 구조를 만들어냅니다:
ent
├── client.go
├── config.go
├── context.go
├── ent.go
├── generate.go
├── mutation.go
... (간결함을 위해 일부 파일 생략)
├── schema
│ └── user.go
├── tx.go
├── user
│ ├── user.go
│ └── where.go
├── user.go
├── user_create.go
├── user_delete.go
├── user_query.go
└── user_update.go
4.1. 데이터베이스 연결 초기화
MySQL 데이터베이스에 연결하기 위해 ent
프레임워크에서 제공하는 Open
함수를 사용할 수 있습니다. 먼저 MySQL 드라이버를 가져와서 올바른 연결 문자열을 제공하여 데이터베이스 연결을 초기화합니다.
package main
import (
"context"
"log"
"entdemo/ent"
_ "github.com/go-sql-driver/mysql" // MySQL 드라이버 가져오기
)
func main() {
// ent.Open을 사용하여 MySQL 데이터베이스와의 연결을 설정합니다.
// 아래의 "your_username", "your_password", "your_database" 자리표시자를 교체하세요.
client, err := ent.Open("mysql", "your_username:your_password@tcp(localhost:3306)/your_database?parseTime=True")
if err != nil {
log.Fatalf("mysql 연결을 열지 못했습니다: %v", err)
}
defer client.Close()
// 자동 마이그레이션 도구 실행
ctx := context.Background()
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("스키마 리소스 생성에 실패했습니다: %v", err)
}
// 여기에 추가적인 비즈니스 로직 작성 가능
}
4.2. 엔터티 생성
사용자 엔터티를 생성하는 것은 새로운 엔터티 객체를 만들고 Save
또는 SaveX
메서드를 사용하여 데이터베이스에 영속화하는 과정을 포함합니다. 다음 코드는 새로운 사용자 엔터티를 만들고 age
와 name
두 개의 필드를 초기화하는 방법을 보여줍니다.
// CreateUser 함수는 새로운 사용자 엔터티를 생성하는 데 사용됩니다.
func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
// client.User.Create()를 사용하여 사용자를 만드는 요청을 작성한 다음, SetAge와 SetName 메서드를 연결하여 엔터티 필드의 값을 설정합니다.
u, err := client.User.
Create().
SetAge(30). // 사용자 나이 설정
SetName("a8m"). // 사용자 이름 설정
Save(ctx) // Save를 호출하여 데이터베이스에 엔터티를 저장합니다
if err != nil {
return nil, fmt.Errorf("사용자 생성에 실패했습니다: %w", err)
}
log.Println("사용자가 생성되었습니다: ", u)
return u, nil
}
main
함수에서 CreateUser
함수를 호출하여 새로운 사용자 엔터티를 생성할 수 있습니다.
func main() {
// ...데이터베이스 연결 설정 코드는 생략
// 사용자 엔터티 생성
u, err := CreateUser(ctx, client)
if err != nil {
log.Fatalf("사용자 생성에 실패했습니다: %v", err)
}
log.Printf("생성된 사용자: %#v\n", u)
}
4.3. 엔터티 조회
엔터티를 조회하기 위해 ent
에서 생성된 쿼리 빌더를 사용할 수 있습니다. 다음 코드는 "a8m"이라는 이름을 가진 사용자를 조회하는 방법을 보여줍니다.
// QueryUser 함수는 지정된 이름을 가진 사용자 엔터티를 조회하는 데 사용됩니다.
func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
// client.User.Query()를 사용하여 사용자에 대한 쿼리를 작성한 후 Where 메서드를 연결하여 사용자 이름으로 조회하는 쿼리 조건을 추가합니다.
u, err := client.User.
Query().
Where(user.NameEQ("a8m")). // 이름이 "a8m"인 경우의 조회 조건 추가
Only(ctx) // Only 메서드는 하나의 결과만을 기대한다는 것을 나타냅니다
if err != nil {
return nil, fmt.Errorf("사용자 조회에 실패했습니다: %w", err)
}
log.Println("사용자 조회 결과: ", u)
return u, nil
}
main
함수에서 QueryUser
함수를 호출하여 사용자 엔터티를 조회할 수 있습니다.
func main() {
// ...데이터베이스 연결 설정 및 사용자 생성 코드는 생략
// 사용자 엔터티 조회
u, err := QueryUser(ctx, client)
if err != nil {
log.Fatalf("사용자 조회에 실패했습니다: %v", err)
}
log.Printf("조회된 사용자: %#v\n", u)
}
5.1. 엣지와 역 엣지 이해
ent
프레임워크에서 데이터 모델은 그래프 구조로 시각화되며, 엔터티는 그래프의 노드를 나타내고 엔터티 간의 관계는 엣지로 표현됩니다. 엣지는 한 엔터티에서 다른 엔터티로의 연결을 나타내며, 예를 들어 사용자
는 여러 대의 자동차
를 소유할 수 있습니다.
역 엣지는 엣지의 반대 참조로, 논리적으로 엔터티 간의 역 관계를 나타내지만 데이터베이스에 새로운 관계를 생성하지는 않습니다. 예를 들어 자동차
의 역 엣지를 통해 이 자동차를 소유한 사용자
를 찾을 수 있습니다.
엣지와 역 엣지의 주요 의미는 연관된 엔터티 간의 탐색을 매우 직관적이고 간단하게 만드는 데 있습니다.
팁:
ent
에서 엣지는 전통적인 데이터베이스 외래 키에 해당하며, 테이블 간의 관계를 정의하는 데 사용됩니다.
5.2. 스키마에서 엣지 정의
먼저, ent
CLI를 사용하여 Car
와 Group
에 대한 초기 스키마를 생성합니다:
go run -mod=mod entgo.io/ent/cmd/ent new Car Group
다음으로, User
스키마에서 Car
와의 엣지를 정의하여 사용자와 자동차 간의 관계를 나타냅니다. 사용자 엔터티에서 Car
유형을 가리키는 cars
엣지를 추가할 수 있으며, 이를 통해 사용자가 여러 대의 자동차를 소유할 수 있음을 나타냅니다:
// entdemo/ent/schema/user.go
// 사용자의 엣지.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("cars", Car.Type),
}
}
엣지를 정의한 후, 해당 코드를 생성하기 위해 go generate ./ent
를 다시 실행해야 합니다.
5.3. 엣지 데이터 조작
사용자와 관련된 자동차를 생성하는 것은 간단한 프로세스입니다. 사용자 엔터티가 주어지면 새로운 자동차 엔터티를 생성하고 사용자와 연결할 수 있습니다:
import (
"context"
"log"
"entdemo/ent"
// 자동차의 스키마 정의를 가져와야 합니다.
_ "entdemo/ent/schema"
)
func 사용자를 위한 자동차 생성(ctx context.Context, client *ent.Client, userID int) error {
user, err := client.User.Get(ctx, userID)
if err != nil {
log.Fatalf("사용자 가져오기 실패: %v", err)
return err
}
// 새로운 자동차 생성 및 사용자와 연결
_, err = client.Car.
Create().
SetModel("Tesla").
SetRegisteredAt(time.Now()).
SetOwner(user).
Save(ctx)
if err != nil {
log.Fatalf("사용자를 위한 자동차 생성 실패: %v", err)
return err
}
log.Println("자동차가 생성되고 사용자와 연결되었습니다.")
return nil
}
마찬가지로, 사용자의 자동차를 조회하는 것도 간단합니다. 사용자가 소유한 모든 자동차의 목록을 조회하려면 다음과 같이 할 수 있습니다:
func 사용자의_자동차_조회(ctx context.Context, client *ent.Client, userID int) error {
user, err := client.User.Get(ctx, userID)
if err != nil {
log.Fatalf("사용자 가져오기 실패: %v", err)
return err
}
// 사용자가 소유한 모든 자동차 조회
cars, err := user.QueryCars().All(ctx)
if err != nil {
log.Fatalf("자동차 조회 실패: %v", err)
return err
}
for _, car := range cars {
log.Printf("자동차: %v, 모델: %v", car.ID, car.Model)
}
return nil
}
위 단계를 통해 스키마에서 엣지를 정의하는 방법과 엣지와 관련된 데이터를 생성하고 조회하는 방법을 배웠습니다.
6. 그래프 탐색 및 조회
6.1. 그래프 구조 이해
ent
에서 그래프 구조는 엔터티와 그들 사이의 엣지로 표현됩니다. 각 엔터티가 그래프의 노드에 해당하고, 엔터티 간의 관계는 엣지에 의해 나타낼 수 있으며, 이는 일대일, 일대다, 다대다 등이 될 수 있습니다. 이러한 그래프 구조는 관계형 데이터베이스에 대한 복잡한 조회 및 작업을 간단하고 직관적으로 만듭니다.
6.2. 그래프 구조 탐색
그래프 탐색 코드 작성은 주로 개체간의 엣지를 통해 데이터를 조회하고 연관시키는 작업을 포함합니다. 아래는 ent
에서 그래프 구조를 탐색하는 간단한 예제입니다:
import (
"context"
"log"
"entdemo/ent"
)
// GraphTraversal은 그래프 구조를 탐색하는 예제입니다
func GraphTraversal(ctx context.Context, client *ent.Client) error {
// "Ariel"이라는 이름을 가진 사용자 조회
a8m, err := client.User.Query().Where(user.NameEQ("Ariel")).Only(ctx)
if err != nil {
log.Fatalf("사용자 조회 실패: %v", err)
return err
}
// Ariel이 소유한 모든 자동차를 탐색
cars, err := a8m.QueryCars().All(ctx)
if err != nil {
log.Fatalf("자동차 조회 실패: %v", err)
return err
}
for _, car := range cars {
log.Printf("Ariel이 소유한 차 모델: %s", car.Model)
}
// Ariel이 속한 모든 그룹을 탐색
groups, err := a8m.QueryGroups().All(ctx)
if err != nil {
log.Fatalf("그룹 조회 실패: %v", err)
return err
}
for _, g := range groups {
log.Printf("Ariel이 속한 그룹: %s", g.Name)
}
return nil
}
위의 코드는 그래프 탐색의 기본적인 예제로, 먼저 사용자를 조회한 후 사용자의 자동차와 그룹을 탐색합니다.
7. 데이터베이스 스키마 시각화
7.1. Atlas 도구 설치
ent
에 의해 생성된 데이터베이스 스키마를 시각화하기 위해 Atlas 도구를 사용할 수 있습니다. Atlas의 설치 단계는 매우 간단합니다. 예를 들어 macOS에서는 다음과 같이 설치할 수 있습니다:
brew install ariga/tap/atlas
참고: Atlas는 다양한 데이터베이스의 테이블 구조 버전 관리를 처리할 수 있는 범용 데이터베이스 마이그레이션 도구입니다. Atlas에 대한 자세한 소개는 이후 장에서 제공될 예정입니다.
7.2. ERD 및 SQL 스키마 생성
Atlas를 사용하여 스키마를 조회하고 내보내는 것은 매우 간단합니다. Atlas를 설치한 후에 다음 명령어를 사용하여 Entity-Relationship Diagram (ERD)을 조회할 수 있습니다:
atlas schema inspect -d [database_dsn] --format dot
또는 직접적으로 SQL 스키마를 생성할 수 있습니다:
atlas schema inspect -d [database_dsn] --format sql
여기서 [database_dsn]
은 데이터 소스 이름(DSN)을 가리킵니다. 예를 들어 SQLite 데이터베이스의 경우 다음과 같을 수 있습니다:
atlas schema inspect -d "sqlite://file:ent.db?mode=memory&cache=shared" --format dot
이러한 명령어로 생성된 출력물은 각각의 도구를 사용하여 뷰 또는 문서로 추가 변환될 수 있습니다.
8. 스키마 마이그레이션
8.1. 자동 마이그레이션 및 버전 관리 마이그레이션
ent는 두 가지 스키마 마이그레이션 전략을 지원합니다: 자동 마이그레이션과 버전 관리 마이그레이션. 자동 마이그레이션은 런타임에서 스키마 변경을 검토하고 적용하는 과정을 의미하며, 개발 및 테스트에 적합합니다. 버전 관리 마이그레이션은 마이그레이션 스크립트를 생성하고, 제품 배포 전에 신중한 검토와 테스트가 필요합니다.
팁: 자동 마이그레이션에 대한 내용은 4.1절의 내용을 참고하세요.
8.2. 버전 관리 마이그레이션 수행
버전 관리 마이그레이션의 과정은 Atlas를 통해 마이그레이션 파일을 생성하는 것을 포함합니다. 아래는 관련 명령어입니다:
마이그레이션 파일 생성:
atlas migrate diff -d ent/schema/path --dir migrations/dir
그 후 이러한 마이그레이션 파일을 데이터베이스에 적용할 수 있습니다:
atlas migrate apply -d migrations/dir --url database_dsn
이 과정을 통해 각 마이그레이션 전에 데이터베이스 마이그레이션의 이력을 유지하고 각 마이그레이션 전에 철저한 검토를 보장할 수 있습니다.
팁: https://github.com/ent/ent/tree/master/examples/start의 샘플 코드를 참고하세요.