sqlc 사용기: Go + PostgreSQL, SQL 기능을 하는 함수 생성
1. Intro
sqlc라 최고라는 말을 듣고 적용해보았다!.. 이 도구는 SQL 스키마와 쿼리를 기반으로 타입 안전한 Go 코드를 생성해주어, 안정성과 개발 생산성을 높여준다고 합니다. 이번 글에서는 sqlc를 사용하여 PostgreSQL 데이터베이스와 상호작용하는 방법에 대해 살펴보겠습니다.
2. sqlc 설정
sqlc를 사용하기 위해서는 먼저 sqlc.yaml 설정 파일을 생성해야 합니다. 이 파일에는 데이터베이스 엔진, 쿼리 파일, 스키마 파일, 그리고 생성된 Go 코드의 출력 위치 등을 지정합니다.
version: "2"
cloud:
project: "xxxxxxxxxxx"
sql:
- engine: "postgresql"
queries: "query.sql"
schema: "schema.sql"
database:
uri: "postgres://postgres:xxxxxxxx@xxx-rds.xxxxxxx.ap-xxxxxxxx-2.rds.amazonaws.com:5432/postgres"
gen:
go:
package: "db"
out: "db"
sql_package: "pgx/v5"
3. 스키마 정의
schema.sql 파일에 데이터베이스 테이블 구조를 정의합니다. 예를 들어, authors 테이블을 다음과 같이 정의할 수 있습니다
CREATE TABLE authors (
id BIGSERIAL PRIMARY KEY,
name text NOT NULL,
bio text
);
테이블 정보를 담은 DDL을 실행해 줍니다. 자동으로 마이그레이션을 해주고, 마이그레이션 파일을 저장해주는 라이브러리가 있다고 하는데, 다음에 적용해볼 예정입니다.
4. 쿼리 작성
query.sql 파일에 필요한 데이터베이스 쿼리를 작성합니다. sqlc는 이 쿼리들을 분석하여 Go 함수로 변환합니다.
-- name: GetAuthor :one
SELECT * FROM authors
WHERE id = $1 LIMIT 1;
-- name: ListAuthors :many
SELECT * FROM authors
ORDER BY name;
-- name: CreateAuthor :one
INSERT INTO authors (
name, bio
) VALUES (
$1, $2
)
RETURNING *;
-- name: UpdateAuthor :exec
UPDATE authors
set name = $2,
bio = $3
WHERE id = $1;
-- name: DeleteAuthor :exec
DELETE FROM authors
WHERE id = $1;
5. 코드 생성
sqlc 설정이 완료되면, 다음 명령어를 실행하여 Go 코드를 생성합니다
sqlc generate
이 명령은 db/ 폴더 내에 query.sql.go, models.go, db.go 파일을 생성합니다. 이 파일들에는 데이터베이스 SQL 구문을 실행하기 위한 함수들이 포함되어 있습니다.
6. 생성된 코드 사용
생성된 코드를 사용하여 데이터베이스 작업을 수행할 수 있습니다. 예를 들어
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.26.0
// source: query.sql
package db
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const createAuthor = `-- name: CreateAuthor :one
INSERT INTO authors (
name, bio
) VALUES (
$1, $2
)
RETURNING id, name, bio
`
type CreateAuthorParams struct {
Name string
Bio pgtype.Text
}
func (q *Queries) CreateAuthor(ctx context.Context, arg CreateAuthorParams) (Author, error) {
row := q.db.QueryRow(ctx, createAuthor, arg.Name, arg.Bio)
var i Author
err := row.Scan(&i.ID, &i.Name, &i.Bio)
return i, err
}
const deleteAuthor = `-- name: DeleteAuthor :exec
DELETE FROM authors
WHERE id = $1
`
func (q *Queries) DeleteAuthor(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, deleteAuthor, id)
return err
}
const getAuthor = `-- name: GetAuthor :one
SELECT id, name, bio FROM authors
WHERE id = $1 LIMIT 1
`
func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
row := q.db.QueryRow(ctx, getAuthor, id)
var i Author
err := row.Scan(&i.ID, &i.Name, &i.Bio)
return i, err
}
const listAuthors = `-- name: ListAuthors :many
SELECT id, name, bio FROM authors
ORDER BY name
`
func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) {
rows, err := q.db.Query(ctx, listAuthors)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Author
for rows.Next() {
var i Author
if err := rows.Scan(&i.ID, &i.Name, &i.Bio); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateAuthor = `-- name: UpdateAuthor :exec
UPDATE authors
set name = $2,
bio = $3
WHERE id = $1
`
type UpdateAuthorParams struct {
ID int64
Name string
Bio pgtype.Text
}
func (q *Queries) UpdateAuthor(ctx context.Context, arg UpdateAuthorParams) error {
_, err := q.db.Exec(ctx, updateAuthor, arg.ID, arg.Name, arg.Bio)
return err
}
import (
"context"
"your-project/db"
)
func main() {
// 데이터베이스 연결 설정
conn, err := pgx.Connect(context.Background(), "your-database-url")
if err != nil {
log.Fatal(err)
}
defer conn.Close(context.Background())
queries := db.New(conn)
// 새 저자 생성
author, err := queries.CreateAuthor(context.Background(), db.CreateAuthorParams{
Name: "John Doe",
Bio: pgtype.Text{String: "A prolific writer", Valid: true},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created author: %v\n", author)
// 저자 목록 조회
authors, err := queries.ListAuthors(context.Background())
if err != nil {
log.Fatal(err)
}
for _, a := range authors {
fmt.Printf("Author: %s\n", a.Name)
}
}
sqlc를 사용하면 데이터베이스 스키마, SQL 쿼리와 Go 코드 사이의 불일치를 줄이고, type-safe을 보장하며, 개발 생산성을 향상시킬 수 있습니다. 또한 데이터베이스 스키마 변경 시 자동으로 코드를 업데이트할 수 있어 유지보수가 용이하다고 합니다.
결론적으로, sqlc는 Go와 PostgreSQL을 함께 사용하는 프로젝트에서 강력한 도구가 될 수 있습니다. 타입 안전한 데이터베이스 액세스 코드를 자동으로 생성함으로써 개발자는 비즈니스 로직에 더 집중할 수 있게 되며, 데이터베이스 관련 오류를 크게 줄일 수 있는 장점이 있다고 합니다.
읽어주셔서 감사합니다 ☺️
'개발 > Golang' 카테고리의 다른 글
Golang으로 대량 API 요청 처리: Excel 파일에서 데이터 읽기부터 병렬 처리까지 (1) | 2024.07.14 |
---|---|
Golang으로 백엔드 개발 도전기 (0) | 2024.07.12 |