MySQL
1. Define 스키마
섹션 제목: “1. Define 스키마”CREATE TABLE `user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL DEFAULT '', `password` varchar(255) NOT NULL DEFAULT '', `mobile` varchar(20) NOT NULL DEFAULT '', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`), UNIQUE KEY `idx_mobile` (`mobile`)) ENGINE=InnoDB;2. 생성 모델
섹션 제목: “2. 생성 모델”# Without 예시입니다goctl model mysql ddl -src user.sql -dir ./internal/model
# With, Redis 예시입니다goctl model mysql ddl -src user.sql -dir ./internal/model -cacheThis 생성합니다:
internal/model/├── usermodel.go # 예시입니다├── usermodel_gen.go # CRUD 예시입니다└── vars.go # ErrNotFound 예시입니다3. 설정
섹션 제목: “3. 설정”DataSource: "root:password@tcp(127.0.0.1:3306)/dbname?parseTime=true&loc=UTC"CacheRedis: - Host: 127.0.0.1:6379 Type: node Pass: ""4. 서비스 컨텍스트 Wiring
섹션 제목: “4. 서비스 컨텍스트 Wiring”func NewServiceContext(c config.Config) *ServiceContext { conn := sqlx.NewMysql(c.DataSource) return &ServiceContext{ Config: c, UserModel: model.NewUserModel(conn, c.CacheRedis), }}5. CRUD 예제
섹션 제목: “5. CRUD 예제”Insert
섹션 제목: “Insert”result, err := l.svcCtx.UserModel.Insert(l.ctx, &model.User{ Username: req.Username, Password: hashPassword(req.Password), Mobile: req.Mobile,})if err != nil { return nil, err}userId, _ := result.LastInsertId()FindOne
섹션 제목: “FindOne”// 기본 키로 조회합니다(-cache로 생성한 경우 캐시 사용)user, err := l.svcCtx.UserModel.FindOne(l.ctx, userId)if errors.Is(err, model.ErrNotFound) { return nil, errorx.NewCodeError(404, "user not found")}FindOne 통해 Unique Index
섹션 제목: “FindOne 통해 Unique Index”// goctl은 모든 UNIQUE KEY에 대해 FindOneBy<FieldName>을 생성합니다user, err := l.svcCtx.UserModel.FindOneByUsername(l.ctx, req.Username)업데이트
섹션 제목: “업데이트”err = l.svcCtx.UserModel.Update(l.ctx, &model.User{ Id: userId, Username: req.Username, Password: newHash, Mobile: user.Mobile,})Delete
섹션 제목: “Delete”err = l.svcCtx.UserModel.Delete(l.ctx, userId)6. Transactions
섹션 제목: “6. Transactions”Wrap multiple operations 에서 single DB transaction 사용하여 TransactCtx:
err = l.svcCtx.UserModel.TransactCtx(l.ctx, func(ctx context.Context, session sqlx.Session) error { // Insert 예시입니다 result, err := insertUser(ctx, session, user) if err != nil { return err // auto-rollback } userId, _ := result.LastInsertId()
// 연관된 프로필을 삽입합니다 if err := insertProfile(ctx, session, userId, profile); err != nil { return err // auto-rollback }
return nil // commit})7. Custom Queries
섹션 제목: “7. Custom Queries”추가 custom 메서드 로 usermodel.go (아님 usermodel_gen.go, which is overwritten 통해 goctl):
type UserModel interface { userModelInterface // 예시입니다 FindByMobileAndStatus(ctx context.Context, mobile string, status int64) (*User, error) CountActiveUsers(ctx context.Context) (int64, error)}
func (m *defaultUserModel) FindByMobileAndStatus(ctx context.Context, mobile string, status int64) (*User, error) { var user User query := fmt.Sprintf("SELECT %s FROM %s WHERE `mobile` = ? AND `status` = ? LIMIT 1", userRows, m.table) err := m.conn.QueryRowCtx(ctx, &user, query, mobile, status) switch { case err == nil: return &user, nil case errors.Is(err, sqlx.ErrNotFound): return nil, ErrNotFound default: return nil, err }}
func (m *defaultUserModel) CountActiveUsers(ctx context.Context) (int64, error) { var count int64 query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE `status` = 1", m.table) err := m.conn.QueryRowCtx(ctx, &count, query) return count, err}8. Bulk Insert
섹션 제목: “8. Bulk Insert”inserter, err := sqlx.NewBulkInserter(conn, fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?)", tableName, userRowsExpectAutoSet))if err != nil { return err}defer inserter.Flush()
for _, user := range users { if err := inserter.Insert(user.Username, user.Password, user.Mobile); err != nil { return err }}