1. ent
엔티티 작업 소개
이 튜토리얼은 ent
프레임워크에서 엔티티 작업을 숙달하기 위한 포괄적인 가이드를 제공하며, 엔티티의 생성, 조회, 업데이트 및 삭제 과정을 다룹니다. ent
의 핵심 기능에 점진적으로 익숙해지려는 초보자들에게 적합합니다.
3. 엔티티 생성 작업
3.1 단일 엔티티 생성
엔티티를 생성하는 것은 데이터 지속성을 위한 기본적인 작업입니다. 아래는 ent
프레임워크를 사용하여 단일 엔티티 객체를 생성하고 데이터베이스에 저장하는 단계입니다:
- 먼저, 엔티티의 구조와 필드, 즉
스키마
파일에서 엔티티의 모델을 정의합니다. - 해당 엔티티 작업 코드를 생성하기 위해
ent generate
명령을 실행합니다. - 생성된
Create
메서드를 사용하여 새로운 엔티티를 구축하고, 연쇄 호출을 통해 엔티티의 필드 값을 설정합니다. - 마지막으로,
Save
메서드를 호출하여 엔티티를 데이터베이스에 저장합니다.
아래 예제는 사용자 엔티티를 생성하고 저장하는 방법을 보여줍니다:
package main
import (
"context"
"log"
"entdemo/ent"
)
func main() {
// 데이터베이스 상호작용을 위해 클라이언트 인스턴스 생성
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("데이터베이스 연결 실패: %v", err)
}
defer client.Close()
// 컨텍스트 생성
ctx := context.Background()
// 클라이언트를 사용하여 사용자 엔티티 생성
a8m, err := client.User.
Create().
SetName("a8m").
Save(ctx)
if err != nil {
log.Fatalf("사용자 엔티티 생성 실패: %v", err)
}
// 엔티티를 데이터베이스에 성공적으로 저장
log.Printf("사용자 엔티티 저장: %v", a8m)
}
이 예제에서는 먼저 데이터베이스 클라이언트 client
가 생성되었습니다. 그런 다음, User.Create
메서드를 사용하여 새 사용자의 속성을 설정하고, 마지막으로 Save
메서드를 호출하여 사용자를 데이터베이스에 저장합니다.
3.2 일괄 엔티티 생성
특정 시나리오에서는 데이터베이스 초기화 또는 대량 데이터 가져오기 작업 중에 여러 개의 엔티티를 생성해야 할 수 있습니다. ent
프레임워크는 일괄 엔티티 생성 기능을 제공하여 개별적으로 엔티티를 생성하고 저장하는 것보다 더 나은 성능을 제공합니다.
일괄 엔티티 생성의 단계는 다음과 같습니다:
- 개별적인 엔티티를 생성하는 대신
CreateBulk
메서드를 사용하여 여러 엔티티를 한 번에 생성합니다. - 생성할 각 엔티티에 대해
Create
를 호출합니다. - 모든 엔티티가 생성된 후,
Save
메서드를 사용하여 일괄적으로 데이터베이스에 엔티티를 저장합니다.
아래는 일괄 엔티티 생성의 예제입니다:
package main
import (
"context"
"log"
"entdemo/ent"
)
func main() {
client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("데이터베이스 연결 실패: %v", err)
}
defer client.Close()
ctx := context.Background()
// Pet 엔티티 일괄 생성
pets, err := client.Pet.CreateBulk(
client.Pet.Create().SetName("pedro").SetOwner(a8m),
client.Pet.Create().SetName("xabi").SetOwner(a8m),
client.Pet.Create().SetName("layla").SetOwner(a8m),
).Save(ctx)
if err != nil {
log.Fatalf("Pet 엔티티 일괄 생성 실패: %v", err)
}
log.Printf("일괄적으로 %d개의 Pet 엔티티 생성 완료\n", len(pets))
}
이 예제에서는 먼저 client
가 생성되고, 그 후에 CreateBulk
메서드를 사용하여 여러 Pet
엔티티를 구성하고, 그들의 이름과 소유주 필드를 설정합니다. Save
메서드를 호출할 때 모든 엔티티가 한 번에 데이터베이스에 저장되므로 대량의 데이터를 처리할 때 더 나은 성능을 제공합니다.
4. 엔티티 조회 작업
4.1 기본 쿼리
데이터베이스 쿼리는 정보를 검색하는 기본적인 방법입니다. ent
에서는 쿼리를 시작하기 위해 Query
메소드를 사용합니다. 아래는 기본 엔티티 쿼리의 단계와 예제입니다:
- 사용 가능한
Client
인스턴스가 있는지 확인합니다. - 쿼리를 생성하기 위해
Client.Query()
또는Pet.Query()
와 같은 엔티티 도우미 메소드를 사용합니다. - 필요한 경우
Where
와 같은 필터링 조건을 추가합니다. - 쿼리를 실행하고
All
메소드를 호출하여 결과를 검색합니다.
package main
import (
"context"
"log"
"entdemo/ent"
"entdemo/ent/user"
)
func main() {
client, err := ent.Open("sqlite3", "file:ent?cache=shared&_fk=1")
if err != nil {
log.Fatalf("데이터베이스 연결을 열지 못했습니다: %v", err)
}
defer client.Close()
ctx := context.Background()
// 이름이 "a8m"인 모든 사용자 쿼리
users, err := client.User.
Query().
Where(user.NameEQ("a8m")).
All(ctx)
if err != nil {
log.Fatalf("사용자 쿼리에 실패했습니다: %v", err)
}
for _, u := range users {
log.Printf("사용자 찾음: %#v\n", u)
}
}
이 예제는 이름이 "a8m"인 모든 사용자를 찾는 방법을 보여줍니다.
4.2 페이지네이션과 정렬
페이지네이션과 정렬은 쿼리할 때 일반적으로 사용되는 고급 기능으로, 출력 순서와 데이터 양을 제어하는 데 사용됩니다. 아래는 ent
를 사용하여 페이지네이션 및 정렬 쿼리를 수행하는 방법입니다:
-
Limit
메소드를 사용하여 반환할 결과의 최대 수를 설정합니다. - 이전 결과 중 일부를 건너뛰기 위해
Offset
메소드를 사용합니다. - 정렬 필드와 방향을 지정하기 위해
Order
메소드를 사용합니다.
다음은 페이지네이션 및 정렬 쿼리의 예제입니다:
package main
import (
"context"
"log"
"entdemo/ent"
"entdemo/ent/pet"
)
func main() {
client, err := ent.Open("sqlite3", "file:ent?cache=shared&_fk=1")
if err != nil {
log.Fatalf("데이터베이스 연결을 열지 못했습니다: %v", err)
}
defer client.Close()
ctx := context.Background()
// 나이를 기준으로 내림차순으로 펫들 쿼리
pets, err := client.Pet.
Query().
Order(ent.Desc(pet.FieldAge)).
Limit(10).
Offset(0).
All(ctx)
if err != nil {
log.Fatalf("펫 쿼리에 실패했습니다: %v", err)
}
for _, p := range pets {
log.Printf("펫 찾음: %#v\n", p)
}
}
이 예제는 나이를 내림차순으로 정렬된 펫의 첫 페이지, 최대 10개의 레코드를 검색하는 방법을 보여줍니다. Limit
및 Offset
값을 수정하여 전체 데이터셋을 페이징하는 것이 가능합니다.
5. 엔티티 업데이트 작업
5.1 단일 엔티티 업데이트
많은 응용 프로그램에서 엔티티를 업데이트하는 것은 일상적인 작업의 중요한 부분입니다. 이 섹션에서는 Ent 프레임워크를 사용하여 데이터베이스에서 단일 엔티티를 업데이트하는 방법을 보여드리겠습니다.
먼저, 사용자의 나이를 업데이트해야 한다고 가정하면, Ent에서 생성된 Update
메소드를 활용할 수 있습니다.
// 'a8m'이라는 사용자 엔티티와 컨텍스트 'ctx'가 이미 있다고 가정합니다.
a8m, err := a8m.Update(). // 사용자 업데이트 빌더 생성
SetAge(30). // 사용자의 나이를 30세로 설정
Save(ctx) // 저장 작업을 수행하고 결과를 반환
if err != nil {
log.Fatalf("사용자 업데이트에 실패했습니다: %v", err)
}
동시에 여러 필드를 업데이트할 수도 있습니다:
a8m, err := a8m.Update().
SetAge(30). // 나이 업데이트
SetName("Ariel"). // 이름 업데이트
AddRank(10). // 등급을 10 증가
Save(ctx)
if err != nil {
log.Fatalf("사용자 업데이트에 실패했습니다: %v", err)
}
업데이트 작업은 연결될 수 있으며, 매우 유연하고 읽기 쉽습니다. Save
메소드를 호출하면 업데이트를 수행하고 업데이트된 엔티티나 오류 메시지를 반환합니다.
5.2 조건부 업데이트
Ent를 사용하면 조건에 따라 업데이트를 수행할 수 있습니다. 다음은 특정 조건을 충족하는 사용자만 업데이트되는 예시입니다.
// 가정: 사용자의 `id`가 있고 해당 사용자를 현재 버전에 대해 완료로 표시하려고 한다고 가정합니다.
err := client.Todo.
UpdateOneID(id). // 사용자 ID에 따라 업데이트할 빌더 생성
SetStatus(todo.StatusDone).
AddVersion(1).
Where(
todo.Version(currentVersion), // 현재 버전과 일치할 때만 업데이트 작업 실행
).
Exec(ctx)
switch {
case ent.IsNotFound(err):
fmt.Println("할 일을 찾을 수 없습니다")
case err != nil:
fmt.Println("업데이트 오류:", err)
}
조건부 업데이트를 사용할 때는 .Where()
메서드를 사용해야 합니다. 이를 통해 데이터베이스의 현재 값에 따라 업데이트를 수행해야 하는지를 결정할 수 있으며, 이는 데이터 일관성과 무결성을 보장하는 데 중요합니다.
6. 엔터티 삭제 작업
6.1 단일 엔터티 삭제
엔터티 삭제는 데이터베이스 작업에서 또 다른 중요한 기능입니다. Ent 프레임워크는 삭제 작업을 수행하기 위한 간단한 API를 제공합니다.
다음 예시는 특정 사용자 엔터티를 삭제하는 방법을 보여줍니다:
err := client.User.
DeleteOne(a8m). // 사용자 삭제 빌더 생성
Exec(ctx) // 삭제 작업 실행
if err != nil {
log.Fatalf("사용자 삭제 실패: %v", err)
}
6.2 조건부 삭제
업데이트 작업과 유사하게, 특정 조건에 따라 삭제 작업을 수행할 수도 있습니다. 특정 시나리오에서는 특정 조건을 충족하는 엔터티만 삭제하려 할 수 있습니다. .Where()
메서드를 사용하여 이러한 조건을 정의할 수 있습니다:
// 특정 날짜 이전에 업데이트된 모든 파일을 삭제하려고 한다고 가정
affected, err := client.File.
Delete().
Where(file.UpdatedAtLT(date)). // 파일의 업데이트 시간이 지정된 날짜보다 이전일 때만 삭제 실행
Exec(ctx)
if err != nil {
log.Fatalf("파일 삭제 실패: %v", err)
}
// 이 작업은 삭제 작업에 의해 영향받는 레코드 수를 반환합니다
fmt.Printf("%d개의 파일이 삭제되었습니다\n", affected)
조건부 삭제 작업을 사용하면 데이터 작업을 정확히 제어하여 실제로 조건을 충족하는 엔터티만 삭제함으로써 데이터 작업의 보안과 신뢰성을 향상시킬 수 있습니다.