GO http 5일차 - Mysql 서버 연결, Query문 작성

0

GO http

목록 보기
5/5

go-mysql setup

먼저 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@localhosttest라는 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&...&paramN=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의 기능에 의존하기 때문에 추천되는 수가 없다.

SetMaxIdleConnsSetMaxOpenConns와 같은 값으로 설정될 것이 추천된다. SetMaxOpenConns보다 작으면 개발자가 예상하는 것보다 connection이 열리고 닫히고를 훨씬 자주 반복하게된다. idle connection들은 SetConnMaxLifetime에 닫히는데, 만약 더 빨리 idle connection을 닫고 싶다면 SetConnMaxIdleTime을 사용하면 된다.

그럼 이제 우리가 넣은 데이터를 불러오는 query를 만들어보자. go sql은 생각보다 단순하게 query를 사용할 수 있는데

QueryRowQuery를 사용하면 된다.

QueryRow

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문으로 들어갈 데이터의 순서대로 넣어주면 된다.

Query

여러 개의 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를 사용하는 방법과 같다. 다만, rowsfor문을 순회해야하고 이 때 Next로 다음 포인터를 넘어가는 것이 중요하다.

Placeholder를 사용하여 Parameterized Query 사용

만약 특정 데이터를 원한다면 어떻게 해야할까?

가령,

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에 값을 넣기위해서 targetQueryRow의 두 번째 인자로 들어가 있다.

결과는 다음과 같다.

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 ... 이렇게 사용한다는 것을 명심하자.

0개의 댓글