๐Ÿคทโ€โ™€๏ธ ๐Ÿคทโ€โ™‚๏ธ Golang์—์„œ jackc/pgx ๋“œ๋ผ์ด๋ฒ„ ์‚ฌ์šฉ ์‹œ PostgreSQL ์˜ค๋ฅ˜: X๋ฅผ Y๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†์Œ

11587 ๋‹จ์–ด showdevbeginnersgopostgres

์†Œ๊ฐœ



์ด๋ด, DEV ์‚ฌ๋žŒ๋“ค! ๐Ÿ‘‹ ์˜ค๋Š˜ ์ €๋Š” "๋‹จ์ˆœ ์˜ค๋ฅ˜"๋ผ๋Š” ์ƒˆ๋กœ์šด ์งง์€ ๊ธฐ์‚ฌ ์‹œ๋ฆฌ์ฆˆ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์ง€๊ธˆ๊นŒ์ง€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์‹ค์Šต์—์„œ ๋งŒ๋‚œ ๋‹ค์–‘ํ•œ ๋ง‰๋‹ค๋ฅธ ๊ณจ๋ชฉ๊ณผ ํ•จ์ •์„ ๋‹ค๋ฃฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ €๋Š” ์ด๋Ÿฌํ•œ ๊ธฐ์‚ฌ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ํ•ญ์ƒ ๋‹ค์Œ ๋„ค ๊ฐ€์ง€ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋ ค๊ณ  ๋…ธ๋ ฅํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ์˜ค๋ฅ˜ ์›์ธ์— ๋Œ€ํ•œ ์„ค๋ช…
  • ๋‚˜์—๊ฒŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์„ค๋ช…;
  • ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ๋‚˜์˜ ํ•ด๊ฒฐ์ฑ…;
  • ๊ฒฐ๋ก  ๋ฐ ๋™๊ธฐ ๋ถ€์—ฌ ๋‹จ์–ด;

  • ๋„ˆ๋ฌด ๊ฐ€ํ˜นํ•˜๊ฒŒ ํŒ๋‹จํ•˜์ง€ ๋ง๊ณ , ๊ทธ๋Ÿฐ ๊ธฐ์‚ฌ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜๋Š”์ง€ ์“ฐ๊ณ , ๋Œ“๊ธ€์—์„œ ๊ตฌ๋ฌธ ๋ถ„์„์„ ์œ„ํ•œ ์ž์‹ ๋งŒ์˜ ์ฃผ์ œ๋ฅผ ์ œ์•ˆํ•˜์„ธ์š”... ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค! ๐Ÿš€

    ๐Ÿ“ ๋ชฉ์ฐจ


  • Explanation of the error
  • Input data when an error occurs
  • Resolving the error
  • Conclusions

  • ์˜ค๋ฅ˜ ์„ค๋ช…



    ๊ณ ์„ฑ๋Šฅ ๋“œ๋ผ์ด๋ฒ„jackc/pgx๋ฅผ ํ†ตํ•ด PostgreSQL ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Golang์—์„œ REST API(๋˜๋Š” ๊ธฐํƒ€ ์ œํ’ˆ)๋ฅผ ๊ฐœ๋ฐœํ•  ๋•Œ ๋•Œ๋•Œ๋กœ ์ •๋ณด ๊ฒ€์ƒ‰์— ํ˜ผ๋ž€์„ ์ค„ ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ์‹ ๊ทœ ์ด๋ฏผ์ž.

    ์ด ๊ฐ„๋‹จํ•œ ์˜ค๋ฅ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. cannot convert [SOMETHING] to [SOMETHING] . ์ œ ๊ฒฝ์šฐ์—๋Š” cannot convert 1 to Text ์ฒ˜๋Ÿผ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.

    ์ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋Š” jackc/pgx ํŒจํ‚ค์ง€์—์„œ ๋ฐœ์ƒํ•˜๋ฉฐ ๋‚ด๋ถ€์ ์œผ๋กœ ์ค€๋น„๋œ ๋ช…๋ น๋ฌธ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด ํŒจํ‚ค์ง€์™€ ์ž๋ฆฌ ํ‘œ์‹œ์ž์˜ ์œ ํ˜•์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์—†๋Š” PostgreSQL์˜ ์กฐํ•ฉ์œผ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

    ๐Ÿ‘ Thanks for this explanation to author of this comment.



    โ†‘ Table of contents

    ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์ž…๋ ฅ ๋ฐ์ดํ„ฐ



    ๋จผ์ € ๋‚ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ํ”„๋กœ์ ํŠธ ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑํ•˜๋Š” ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    -- ./platform/migration/000001_create_init_tables.up.sql
    
    -- Create projects table
    CREATE TABLE "projects" (
      "id" UUID PRIMARY KEY DEFAULT (uuid_generate_v4()),
      "created_at" timestamp DEFAULT (now()),
      "updated_at" timestamp,
      "user_id" UUID NOT NULL,
      "alias" varchar(255) UNIQUE NOT NULL,
      "project_status" int NOT NULL,
      "project_attrs" JSONB NOT NULL
    );
    
    // ...
    


    ํ•„๋“œ์—๋Š” ๋งค์šฐ ๊ตฌ์ฒด์ ์ธ ์œ ํ˜•์ด ์žˆ์œผ๋ฉฐ ์ด ํ…Œ์ด๋ธ”์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑ/์ˆ˜์ •ํ•  ๋•Œ Postgres์—์„œ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.

    ์ด์ œ ์ด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์— ์ ํ•ฉํ•œ Go ๋ชจ๋ธ์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

    // ./app/controllers/project_model.go
    
    // Project struct to describe project object.
    type Project struct {
        ID            uuid.UUID    `db:"id" json:"id"`
        CreatedAt     time.Time    `db:"created_at" json:"created_at"`
        UpdatedAt     time.Time    `db:"updated_at" json:"updated_at"`
        UserID        uuid.UUID    `db:"user_id" json:"user_id"`
        Alias         string       `db:"alias" json:"alias"`
        ProjectStatus int          `db:"project_status" json:"project_status"`
        ProjectAttrs  ProjectAttrs `db:"project_attrs" json:"project_attrs"`
    }
    
    // ProjectAttrs struct to describe project attributes.
    type ProjectAttrs struct {
        Title       string `json:"title"`
        Description string `json:"description"`
        Picture     string `json:"picture"`
        URL         string `json:"url"`
    }
    
    // ...
    


    ์˜ˆ, Go ๋ชจ๋ธ์˜ JSOB ํ•„๋“œ ์œ ํ˜•์ด ์ •๊ทœ ๊ตฌ์กฐ๊ฐ€ ๋˜์–ด project_attrs ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ•„๋“œ์— JSON ํ˜•์‹์œผ๋กœ ์ €์žฅ๋œ๋‹ค๋Š” ๊ฒƒ์ด ๋งž์Šต๋‹ˆ๋‹ค.

    ๐Ÿ‘Œ Other fields are quite normal for any Go project.



    โ†‘ Table of contents



    ์˜ค๋ฅ˜ ํ•ด๊ฒฐ



    ์ด ๊ฐ„๋‹จํ•œ ์˜ค๋ฅ˜์— ๋Œ€ํ•œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ ์›ํ™œํ•˜๊ฒŒ ์ด๋™ํ•ฉ์‹œ๋‹ค.

    ์†”๋ฃจ์…˜์— ๋Œ€ํ•ด ์•Œ์•„์•ผ ํ•  ๊ฒƒ์€ PostgreSQL์—์„œ ์ฟผ๋ฆฌ์—์„œ ์ง์ ‘ ์ž๋ฆฌ ํ‘œ์‹œ์ž์— ๋Œ€ํ•œ ์œ ํ˜•์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ํŒŒ์ผ์—์„œ ์ง€์ •ํ•œ ์œ ํ˜•์„ ์ฟผ๋ฆฌ์— ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    ํ•„๋“œ ์œ ํ˜•์„ ์ง€์ •ํ•˜๋Š” ํ˜•์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: $N::<TYPE> (๋‹ฌ๋Ÿฌ ๊ธฐํ˜ธ + ์ž๋ฆฌ ํ‘œ์‹œ์ž ๋ฒˆํ˜ธ + ๋‘ ๊ฐœ์˜ ์ฝœ๋ก  + DB์˜ ํ•„๋“œ ์œ ํ˜•).

    // ./app/controllers/project_query.go
    
    // CreateProject method for creating project by given Project object.
    func (q *ProjectQueries) CreateProject(p *models.Project) error {
        // Define query string.
        // We define type for each field to solve this simple error.
        query := `
        INSERT INTO projects 
        VALUES ($1::uuid, $2::timestamp, $3::timestamp, $4::uuid, $5::varchar, $6::int, $7::jsonb)
        `
    
        // Send query to database.
        _, err := q.Exec(query, p.ID, p.CreatedAt, p.UpdatedAt, p.UserID, p.Alias, p.ProjectStatus, p.ProjectAttrs)
        if err != nil {
            // Return only error.
            return err
        }
    
        // This query returns nothing.
        return nil
    }
    
    // ...
    


    โ˜๏ธ Once again, please note that we are specifying the PostgreSQL field type from the migration, not the Go structure from the model!



    ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ์ด ๋์ ์„ ์š”์ฒญํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋” ์ด์ƒ ํ‘œ์‹œ๋˜์ง€ ์•Š๊ณ  ์š”์ฒญ์ด ์„ฑ๊ณตํ•ฉ๋‹ˆ๋‹ค! ๐ŸŽ‰

    โ†‘ Table of contents

    ๊ฒฐ๋ก 



    ๊ฐœ์ธ์ ์œผ๋กœ ์ €๋Š” jackc/pgx ํŒจํ‚ค์ง€ ๊ตฌ์„ฑ์—์„œ ์ด ๊ฒ€์‚ฌ๋ฅผ ์™„์ „ํžˆ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ์ด๋Ÿฐ ์ข…๋ฅ˜์˜ ํ•ญ๋ชฉ์„ ๋” ์ข‹์•„ํ•ฉ๋‹ˆ๋‹ค(์ถ”๊ฐ€ ์ •๋ณดhere ).

    ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๊ตฌํ˜„์˜ ์„ธ๋ถ€ ์‚ฌํ•ญ์— ๋›ฐ์–ด๋“ค ํ•„์š” ์—†์ด SQL ์ฟผ๋ฆฌ๋ฅผ ํ•œ๋ˆˆ์— ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•„๋“œ ์œ ํ˜•์„ ์ฆ‰์‹œ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

    ์ž์‹ ์—๊ฒŒ ํŽธ๋ฆฌํ•œ ์˜ต์…˜์„ ์„ ํƒํ•˜๊ณ  ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ๋ฐฉ๊ธˆ ๊ฐœ์ธ์ ์œผ๋กœ ๋„์›€์ด ๋œ ์†”๋ฃจ์…˜์˜ ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ๋“œ๋ ธ์Šต๋‹ˆ๋‹ค. ์„ฑ๊ณต์ ์ธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๊ฐ„๋‹จํ•œ ์˜ค๋ฅ˜๋กœ ์ธํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ˜„ํ•˜๋Š” ๋ฐ ๋ฐฉํ•ด๊ฐ€ ๋˜์ง€ ์•Š๋„๋ก ํ•˜์‹ญ์‹œ์˜ค! ๐Ÿ˜‰

    โ†‘ Table of contents

    ์‚ฌ์ง„ ๋ฐ ๋™์˜์ƒ


  • ์•„๋ฃฌ ํ”„๋ผ์นด์‰ฌ https://unsplash.com/photos/pE9mgLMwee0
  • ์ผ„๋‹ฌ ๋ฃจ์Šค https://unsplash.com/photos/AijuW-HlE30

  • ์ถ”์‹ 



    ์ด ๋ธ”๋กœ๊ทธ์—์„œ ์ด์™€ ๊ฐ™์€ ๊ธฐ์‚ฌ๋ฅผ ๋” ์›ํ•˜์‹œ๋ฉด ์•„๋ž˜์— ๋Œ“๊ธ€์„ ๊ฒŒ์‹œํ•˜๊ณ  ์ €๋ฅผ ๊ตฌ๋…ํ•˜์‹ญ์‹œ์˜ค. ๊ฐ์‚ฌ! ๐Ÿ˜˜

    ๊ทธ๋ฆฌ๊ณ  ๋ฌผ๋ก  LiberaPay ์— ๊ธฐ๋ถ€ํ•˜์‹œ๋ฉด ์ €๋ฅผ ํ›„์›ํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ๊ธฐ๋ถ€๊ธˆ์€ ์ƒˆ๋กœ์šด ๊ธฐ์‚ฌ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ์œ„ํ•œ ๋น„์˜๋ฆฌ ์˜คํ”ˆ ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

    ์ข‹์€ ์›นํŽ˜์ด์ง€ ์ฆ๊ฒจ์ฐพ๊ธฐ