먼저 mysql
을 설치해야한다. 설치가되어있다면 user와 database를 만들어놓도록 하자.
먼저 mysql
shell에 접속하도록 하자.
mysql
만약, mysql의 user가 무엇이고 database name이 무엇인지 기억이 안난다면 mysql
에 들어가서 다음과 같이 입력하면 된다.
mysql
use mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select user, host from user;
+------------------+-----------+
| user | host |
+------------------+-----------+
| analyzer | % |
| root | 127.0.0.1 |
| root | ::1 |
| debian-sys-maint | localhost |
| root | localhost |
+------------------+-----------+
5 rows in set (0.00 sec)
사용자를 생성하고 싶다면 mysql shell
을 나간다음 root
계정으로 접속하도록 하자.
mysql -u root -p mysql
create user 'test'@'localhost' identified by 'test1234';
Query OK, 0 rows affected (0.00 sec)
유저가 만들어졌다면, 이제 database를 만들어보자.
CREATE DATABASE test default CHARACTER SET UTF8;
Query OK, 1 row affected (0.00 sec)
test
database가 잘 만들어졌는 지 확인해보자.
SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
test
database가 잘 만들어졌다는 것을 확인할 수 있다.
참고로 데이터베이스의 모든 이름(데이터베이스, 테이블), column에는 소문자를 사용하며 공백이나 카멜 대신에 _
를 붙여 사용한다. 또한 데이터 조작어 CREATE, SELECT 등은 대문자로 써서 이들을 구분하는 것이 좋다.
이제 우리가 만든 test
database에 test
user가 접근할 수 있도록 권한을 부여하도록 하자. 권한 부여는 GRANT
를 사용하면 된다.
GRANT ALL PRIVILEGES ON test.* TO test@localhost IDENTIFIED BY 'test1234';
ALL PRIVILEGES
는 해당 데이터 베이스에 대한 모든 권한을 준다는 의미이고, ON test.*
는 test
라는 데이터 베이스에 대한 모든 table의 권한을 준다는 것이다. TO test@localhost
는 test
라는 user
의 도메인으로 localhost
까지만 허용하겠다는 것이고, IDENTIFIED BY
는 user의 비밀번호를 입력하면 된다.
이제 잘 적용되었는 지를 확인하기 위해 table
을 하나 만들어보자.
CREATE TABLE TEMP
(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(32) NOT NULL
) ENGINE=INNODB;
TEMP
라는 테이블을 만들었다. 이제 TEMP
라는 테이블이 잘 만들어졌는 지를 확인해보자.
SHOW tables;
+----------------+
| Tables_in_test |
+----------------+
| TEMP |
+----------------+
잘 만들어진 것을 확인할 수 있다. table
에 대한 자세한 정보를 얻고 싶다면 DESCRIBE
를 사용하면 된다.
DESCRIBE TEMP;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(32) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
아직 table에 데이터가 없으니 일부 데이터를 추가해주도록하자. 데이터 추가는 INSERT
를 해주면 된다.
INSERT INTO TEMP
(name)
VALUES('뉴진스');
데이터가 들어갔다면 select
를 통해서 확인해보자.
SELECT * FROM TEMP;
+----+-----------+
| id | name |
+----+-----------+
| 1 | 뉴진스 |
+----+-----------+
가장 기본적인 데이터베이스 구성이 완료되었다. 이제 golang으로 접근해보도록 하자.
mysql
이 설치되어 있다면, golang에서 sql-driver 패키지로 mysql
을 설치해주어야 한다.
go get -u github.com/go-sql-driver/mysql
설치되었다면, import
문에 다음을 추가해주어야 한다.
import (
_ "github.com/go-sql-driver/mysql"
)
_ "github.com/go-sql-driver/mysql"
는 명시적으로 개발자가 mysql driver
를 사용했다는 것이 아니라, 다른 패키지에서 mysql driver
를 사용하겠다는 것이다.
golang에서는 sql database접속을 위해 다음의 패키지를 제공한다.
import (
"database/sql"
)
"database/sql"
sql서버에 접속할 수 있는 인터페이스로 MYSQL
이든 Postgres
든 내부 서버가 무엇이든 간에 상관없이 접근할 수 있도록 해준다.
때문에 내부 구현체인 driver
가 필요한데, DB로 MYSQL을 쓰고있다면 mysql
드라이버를 사용하고, postgres를 쓰고있다면 postgres
드라이버를 설치해주면 된다.
이후에는 database와 connection을 가져야 하므로 sql.Open
을 사용하여 DB에 연결한다.
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "test:test1234@tcp(127.0.0.1:3306)/test")
if err != nil {
panic(err)
}
defer db.Close()
// See "Important settings" section.
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
}
sql.Open
은 첫번째에 연결한 database이름 두 번째에 DSN
을 넣는다. DSN(data source name)
은 database에 연결하기위해 database의 위치와 그에 대한 정보를 기록하는 값이다. 다음과 같은 형식을 갖는다.
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
dbname
이외에는 모두 optional이다. 보통 DSN
은 외부에 노출되지 않기위해서 .env
와 같은 config파일에 숨겨서 배포하는 것이 원칙이다.
우리는 user이름이 test
이고 비밀번호가 test123
, domain이 127.0.0.1(localhost)
, mysql port number인 3306
, database가 test
이므로 위와 같이 적었다.
연결이 되었다면 3개를 쭉 실행한다.
db.SetConnMaxLifetime(time.Minute * 3)
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(10)
SetConnMaxLifetime
은 MYSQL server, OS, 또는 다른 미들웨어에 의해 connection이 닫히기 이전에, idel connection을 안전하게 닫아주는 것을 보장해준다. 특정 미들웨어들은 idle connection을 5분안에 닫으므로, timeout을 5분 미만으로 잡는 것이 좋다. 이 설정으로 load balancing과 system variable 변경에 도움을 줄 수 있다.
SetMaxOpenConns
은 application에 의해 사용되는 connection의 수를 제한하는데 사용된다. 이는 mysql server와 application의 기능에 의존하기 때문에 추천되는 수가 없다.
SetMaxIdleConns
는 SetMaxOpenConns
와 같은 값으로 설정될 것이 추천된다. SetMaxOpenConns
보다 작으면 개발자가 예상하는 것보다 connection이 열리고 닫히고를 훨씬 자주 반복하게된다. idle connection들은 SetConnMaxLifetime
에 닫히는데, 만약 더 빨리 idle connection을 닫고 싶다면 SetConnMaxIdleTime
을 사용하면 된다.
그럼 이제 우리가 넣은 데이터를 불러오는 query를 만들어보자. go sql은 생각보다 단순하게 query를 사용할 수 있는데
QueryRow
와 Query
를 사용하면 된다.
db.QueryRow
는 말 그대로, 하나의 row를 찾아주는 Query문을 만들겠다는 것이다. 사용법도 매우 간단한데 mysql 서버에 우리가 넣은 '뉴진스' 데이터를 가져와보자.
SELECT * FROM TEMP WHERE name='뉴진스';
+----+-----------+
| id | name |
+----+-----------+
| 1 | 뉴진스 |
+----+-----------+
1 row in set (0.00 sec)
데이터를 얻는데 성공하였다. 그럼 해당 sql문을 golang의 db.QueryRow
에 넣어주면 된다.
var id string
var name string
err = db.QueryRow("SELECT * FROM TEMP WHERE name='뉴진스'").Scan(&id, &name)
if err != nil {
panic(err.Error())
}
fmt.Println(id, " ", name)
사용 방법은 아주 간단하다. db.QueryRow
에 query문을 넣고, .Scan
에 변수를 넣어 결과 데이터를 할당해주면 된다.
결과는 다음과 같다.
1 뉴진스
Scan
에 들어가야할 변수는 SELECT
문으로 들어갈 데이터의 순서대로 넣어주면 된다.
여러 개의 row를 리턴으로 받을 때는 Query
를 사용한다. 물론 0개가 올 수도 있다.
일단 여러 개의 데이터가 TEMP table에 있어야 하므로, 추가해주도록 하자.
INSERT INTO TEMP (name) VALUES('the boys');
INSERT INTO TEMP (name) VALUES('여자아이들');
INSERT INTO TEMP (name) VALUES('차은우');
SELECT * FROM TEMP;
+----+-----------------+
| id | name |
+----+-----------------+
| 1 | 뉴진스 |
| 2 | the boys |
| 3 | 여자아이들 |
| 4 | 차은우 |
+----+-----------------+
4 rows in set (0.00 sec)
데이터 베이스에 정보를 넣었으니 이들을 불러오는 golang 코드를 만들어보자.
rows, err := db.Query("SELECT * FROM TEMP")
if err != nil {
panic(err.Error())
}
defer rows.Close()
for rows.Next() {
var id string
var name string
err := rows.Scan(&id, &name)
if err != nil {
panic(err.Error())
}
fmt.Println(id, " ", name)
}
rows, err := db.Query("SELECT * FROM TEMP")
에서 Query
를 사용하였고, 이를 통해 여러개의 rows를 가져왔다. 이들을 하나씩 순회하여 Scan
하는 것은 위에서 QueryRow
를 사용하는 방법과 같다. 다만, rows
로 for문
을 순회해야하고 이 때 Next
로 다음 포인터를 넘어가는 것이 중요하다.
만약 특정 데이터를 원한다면 어떻게 해야할까?
가령,
SELECT *
FROM TEMP
WHERE name=VARIABLE;
name
부분에 변수(VARIABLE)를 넣어처리하고 싶다면 어떻게해야할까?? 이때 사용하는 것이 바로 Placeholder
이다.
mysql에서의 Placeholder
는 ?
로 써주면 된다. Query, QueryRow
둘 다 사용할 때 query문에는 ?
를 넣고 순서대로 변수들 인자를 넣어주면 된다. 마치 printf
를 쓸 때와 같다.
var id string
var name string
target := "뉴진스"
err = db.QueryRow("SELECT * FROM TEMP WHERE name=?", target).Scan(&id, &name)
if err != nil {
panic(err.Error())
}
fmt.Println(id, " ", name)
여기서 주목할 것인 SELECT * FROM TEMP WHERE name=?
라는 query문안에 placeholder가 들어가 있다는 것이다. 이 placeholder에 값을 넣기위해서 target
이 QueryRow
의 두 번째 인자로 들어가 있다.
결과는 다음과 같다.
1 뉴진스
이는 Query
에서도 마찬가지이다.
maxId := 2
rows, err := db.Query("SELECT * FROM TEMP WHERE id > ?", maxId)
if err != nil {
panic(err.Error())
}
defer rows.Close()
for rows.Next() {
var id string
var name string
err = rows.Scan(&id, &name)
if err != nil {
panic(err.Error())
}
fmt.Println(id, " ", name)
}
이번에는 id
가 특정 값 이상 인 경우의 row들만 불러오도록 하였다. id > ?
의 placeholder에 들어갈 값은 maxId
로 2이다. 그럼 id > 2
인 값들이 rows
로 들어가는 것이다. 결과는 다음과 같다.
3 여자아이들
4 차은우
중요한 것은
mysql
만 placeholder가?
라는 것이다. postgres는$1, $2 ...
이렇게 사용한다는 것을 명심하자.