> ## Content Index
> Fetch the complete content index at: https://www.popit.kr/llms.txt
> Use this file to discover other available public pages before exploring further.

# Golang database/sql 패키지 삽질기 - 1편 매개변수 표시자
- URL: https://www.popit.kr/golang-database-sql-1-parameter-placeholders/
- Published: 2019-05-27T22:49:46.000Z
- Updated: 2019-06-10T00:29:01.000Z
- Author: 유 영모
- Tags: #소프트웨어, golang, mysql, SQL, Go, database, sqlite, Parameter Placeholder

필자는 주로 자바로 개발해 왔는데 최근 Golang으로 개발하면서 데이터베이스를 다룰 일이 생겼다. Golang에서는 자바의 [JDBC](https://en.wikipedia.org/wiki/Java%5FDatabase%5FConnectivity?ref=popit.kr)와 같은 [database/sql 패키지](https://golang.org/pkg/database/sql/?ref=popit.kr)를 제공한다.

이 글은 Golang 초심자로서 필자가 database/sql 패키지를 사용하면서 겪었던 삽질기 중 1편으로 매개변수 표시자Parameter Placeholder에 대한 글이다.

---

1. **매개변수 표시자**
2. [SQLite 메모리 데이터베이스](https://www.popit.kr/golang-database-sql-2-sqlite-in-memory/)
3. [커넥션 풀](https://www.popit.kr/golang-database-sql-3-connection-pool/)

---

# Unknown column?

아래는 database/sql 패키지를 사용하여 데이터베이스 parent 테이블에서 id가 '1'인 열의 name 컬럼을 조회하는 코드이다. 데이터베이스로는 [SQLite](https://www.sqlite.org/index.html?ref=popit.kr)를 사용하고 있다.  

```go
package main
 
import (
    "database/sql"
    _ "github.com/mattn/go-sqlite3"
    "log"
)
 
func main() {
    db, err := sql.Open("sqlite3", ":memory:")
 
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    // ...
    var parentName string
    row := db.QueryRow("SELECT name FROM parent WHERE id = $1", 1)
    err = row.Scan(&parentName)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(parentName)
}
```

코드를 실행해 보면 잘 동작하는 것을 확인할 수 있다.

![image2019-5-22_7-44-38](https://www.popit.kr/content/images/wp/2019/05/image2019-5-22_7-44-38-600x234.png)

이제 같은 코드를 데이터베이스만 **MySQL로 변경해서 실행해 보면 Unknown column 오류를 만난다.**

```go
package main
 
import (
   // ...
    _ "github.com/go-sql-driver/mysql"
)
 
func main() {
    db, err := sql.Open("mysql",
        "root:1111@tcp(127.0.0.1:3306)/article")
    // ...
}
```

![image2019-5-22_7-41-10](https://www.popit.kr/content/images/wp/2019/05/image2019-5-22_7-41-10-600x202.png)

왜 같은 코드임에도 불구하고 SQLite는 동작하고 MySQL은 동작하지 않는 걸까? 답은 database/sql 패키지 문서에서 찾을 수 있다.

# 매개변수 표시자Parameter Placeholder 

Golang에서는 Prepared Statement를 사용할 때 SQL문에 '$1'처럼 대체되는 것을 **매개변수 표시자**라고 한다. 아래 코드는 '$1' 매개변수 표시자에 실제 값 1로 대체하여 SQL을 실행한다.  

```go
row := db.QueryRow("SELECT name FROM parent WHERE id = $1", 1)
```

매개변수 표시자는 문법이 있으니... **데이터베이스마다 다르게 써야 한다는 것이다!**

![출처 : http://go-database-sql.org/prepared.html](https://www.popit.kr/content/images/wp/2019/05/image2019-5-22_7-58-25-600x131.png)

출처 : http://go-database-sql.org/prepared.html

# 결론

매개변수 표시자를 사용할 때에는 사용하는 데이터베이스마다 다르게 써야 한다. 예시 코드에서는 MySQL로 데이터베이스를 변경했기 때문에 매개변수 표시자를 '?'로 써야 한다. 아래와 같이 변경하고 실행하면 오류 없이 잘 동작한다.  

```go
row := db.QueryRow("SELECT name FROM parent WHERE id = ?", 1)
```

참고로 매개변수 표시자 문법에는 언급되어 있지 않지만 **SQLite는 '?' 와 '$N'을 모두 수용한다.** 따라서 '?'를 써도 SQLite에서 잘 동작한다.

# 마치며

글을 시작하면서 필자는 주로 자바로 개발을 해왔다고 했다. 자바의 JDBC는 매개변수 표시자를 '?'만 사용한다. 즉 데이터베이스가 달라졌다고 해서 고민할 필요 없다. 드라이버만 변경하면 된다. 이런 경험 때문인지 처음에 오류를 만났을 때 바로 이해하기가 어려웠다.

끝으로 데이터베이스마다 달라지는 매개변수 표시자를 드라이버 수준에서 수용해 주면 좋을 텐데라는 아쉬움이 남는다.