갈루아의 반서재

Luminus 를 이용한 Clojure 방명록 만들기 (2)


Creating the Database

먼저 어플리케이션을 위한 모델을 만들어야 한다. 마이그레이션 폴더에 위치한 <date>-add-users-table.up.sql 파일을 열어보자. 다음과 같은 내용이 포함되어 있을 것이다. 

1
~/guestbook/resources/migrations$ nano 20180821033932-add-users-table.up.sql
cs


1
2
3
4
5
6
7
8
9
10
11
 
CREATE TABLE users
(id VARCHAR(20PRIMARY KEY,
 first_name VARCHAR(30),
 last_name VARCHAR(30),
 email VARCHAR(30),
 admin BOOLEAN,
 last_login TIMESTAMP,
 is_active BOOLEAN,
 pass VARCHAR(300));
 
cs

user 테이블을 guestbook 어플리케이션에 적합하게 아래의 내용으로 바꿔보자. 

1
2
3
4
5
CREATE TABLE guestbook
(id INTEGER PRIMARY KEY AUTO_INCREMENT,
 name VARCHAR(30),
 message VARCHAR(200),
 timestamp TIMESTAMP);
cs

guestbook 테이블은 코멘트 작성자, 메시지 내용, 그리고 타임스탬프 등 메시지와 관련된 모든 필드에 대한 내용을 저장한다. 

다음으로 <date>-add-users-table.down.sql 파일의 내용도 아래와 같이 수정해보자. 

1
~/guestbook/resources/migrations$ nano 20180821033932-add-users-table.down.sql
cs


1
2
3
 
DROP TABLE guestbook;
 
cs

프로젝트 루트 디렉토리에서 다음의 명령을 통해 마이그레이션을 시켜보자. 

1
2
3
4
5
6
7
8
9
10
11
12
 
~/guestbook$ lein run migrate
2018-08-21 08:09:47,962 [main] DEBUG org.jboss.logging - Logging Provider: org.jboss.logging.Slf4jLoggerProvider
2018-08-21 08:09:49,177 [main] INFO  migratus.core - Starting migrations
2018-08-21 08:09:49,570 [main] INFO  migratus.database - creating migration table 'schema_migrations'
2018-08-21 08:09:49,610 [main] DEBUG migratus.migrations - Looking for migrations in #object[java.io.File 0x2114e9c4 /home/fukaerii/guestbook/resources/migrations]
2018-08-21 08:09:49,619 [main] WARN  migratus.migrations - skipping: '20180821033932-add-users-table.up.sqles' migrations must match pattern: ^(\d+)-([^\.]+)((?:\.[^\.]+)+)$
2018-08-21 08:09:49,637 [main] INFO  migratus.core - Running up for [20180821033932]
2018-08-21 08:09:49,638 [main] INFO  migratus.core - Up 20180821033932-add-users-table
2018-08-21 08:09:49,658 [main] DEBUG migratus.migration.sql - found 1 up migrations
2018-08-21 08:09:49,671 [main] DEBUG migratus.database - marking 20180821033932 complete
2018-08-21 08:09:49,682 [main] INFO  migratus.core - Ending migrations
cs

이로써 데이터베이스 초기화가 끝났다.


Accessing The Database

다음으로 src/clj/guestbook/db/core.clj 파일을 살펴보자. 이미 아래와 같이 데이터베이스 접속에 대한 정의가 포함되어 있다. 

/home/fukaerii/guestbook/src/clj/guestbook/db/core.clj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(ns guestbook.db.core
  (:require
    [clj-time.jdbc]
    [conman.core :as conman]
    [mount.core :refer [defstate]]
    [guestbook.config :refer [env]]))
 
(defstate ^:dynamic *db*
          :start (conman/connect! {:jdbc-url (env :database-url)})
          :stop (conman/disconnect! *db*))
 
(conman/bind-connection *db* "sql/queries.sql")
 
 
cs

데이터베이스 접속은 런타임에 환경 맵으로부터 읽어오게 된다. 기본적으로 :database-url 키는 데이터베이스 접속 URL 문자열을 가리키게 된다. 이 변수는 개발과정에서 dev-config.edn 파일로부터 온 것으로, 생산단계의 환경 변수로 셋팅된다. 예를 들어 다음과 같이 말이다.

export DATABASE_URL="jdbc:h2:./guestbook.db"

앞서 본 예제에서는 임베디드 H2 데이터베이스를 사용하고 있으므로, 데이터는 특정한 파일에 저장되며, 그 위치는 프로젝트가 구동되고 있는 경로에 따라 상대적이다. 

bind-connection 이 호출될 때 데이터베이스 쿼리에 맵핑하는 기능이 생성된다. 살펴볼 수 있듯이 sql/queries.sql 파일을 참조하게 된다. 해당 위치는 resources 폴더 아래에서 찾을 수 있다. 그러면 이 파일을 열어서 살펴보자. 

/home/fukaerii/guestbook/resources/sql/queries.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- :name create-user! :! :n
-- :doc creates a new user record
INSERT INTO users
(id, first_name, last_name, email, pass)
VALUES (:id, :first_name, :last_name, :email, :pass)
 
-- :name update-user! :! :n
-- :doc updates an existing user record
UPDATE users
SET first_name = :first_name, last_name = :last_name, email = :email
WHERE id = :id
 
-- :name get-user :? :1
-- :doc retrieves a user record given the id
SELECT * FROM users
WHERE id = :id
 
-- :name delete-user! :! :n
-- :doc deletes a user record given the id
DELETE FROM users
WHERE id = :id
 
cs

각각의 함수는 :name 그리고 함수이름이 뒤에 붙는 형식이다. 그 밑의 주석은 해당 함수의 기능에 대한 설명이다. 매개변수는 : 표기로 구분된다. 현재의 쿼리를 다음의 것으로 바꿔보자. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
-- :name save-message! :! :n
-- :doc creates a new message
 
INSERT INTO guestbook
(name, message, timestamp)
VALUES (:name, :message, :timestamp)
 
 
 
-- :name get-messages :? :*
-- :doc selects all available messages
 
SELECT * FROM guestbook
 
cs

이제 모델 셋업이 끝났다. 그럼 어플리케이션을 구동시켜보자.