[Golang] PostgreSQL 다루기

Golang은 다양한 데이터베이스를 다룰 수 있는데요. Oracle, MySQL, MSSQL Server, DB2 그리고 PostgreSQL 등에 대한 DB 드라이버 라이브러리를 제공합니다. 각 드라이버는 github를 통해 소스코드와 함께 제공되며 드라이버에 대한 라이브러리 소스코드는 go get {URL}처럼 Command Line을 통해 설치하여 사용할 수 있습니다.

이 글은 Local PC에 설치된 PostgreSQL에 연결하여, 이미 존재하는 postgres 데이터베이스의 test 테이블에 데이터를 추가(INSERT)하고 쿼리(SELECT)하는 코드를 설명합니다.

먼저 postgreSQL에 연결하는 코드는 아래와 같습니다.

package main

import (
    "database/sql"
    "fmt"

    _ "github.com/lib/pq"
)

const (
    DB_USER     = "postgres"
    DB_PASSWORD = "ILOVEU"
    DB_NAME     = "postgres"
)

func main() {
    dbinfo := fmt.Sprintf("user=%s password=%s dbname=%s sslmode=disable",
        DB_USER, DB_PASSWORD, DB_NAME)

    db, err := sql.Open("postgres", dbinfo)
    if err != nil {
        panic(err)
    }
    defer db.Close()

    // ... 계속

다음 코드는 연결된 DB에 데이터를 추가(INSERT)하는 코드입니다.

    result, err := db.Exec("INSERT INTO test (name, age) VALUES('Jackass', 19)")
    if err != nil {
        panic(err)
    }

    cntAffected, err := result.RowsAffected()
    if err != nil {
        panic(err)
    }

    fmt.Println("Affected Rows:", cntAffected)

    // ... 계속

위의 코드 중 11번은 INSERT 된 데이터의 개수를 나타내며, 여기서는 1개를 추가했으므로 1이 됩니다. 다음 코드는 저장된 데이터를 조회(SELECT)하는 코드입니다.

    rows, err := db.Query("SELECT name, age FROM test")
    if err != nil {
        panic(err)
    }
    defer rows.Close()

    var name string
    var age int
    for rows.Next() {
        err := rows.Scan(&name, &age)
        if err != nil {
            panic(err)
        }

        fmt.Println(name, age)
    }
}	

실행 결과는 다음과 같습니다.

Affected Rows: 1
Dip2K                            40
Dip2K                            40
Jackass                          19
Jackass                          19
Jackass                          19
Jackass                          19
Jackass                          19
Jackass                          19
Jackass                          19

[Golang] Go의 try .. catch .. finally 구문

Go는 try .. catch .. finally 구문을 제공하지 않습니다. 그래서 아래와 같은 코드의 경우 프로그램은 패닉(Panic)에 빠지고 종료됩니다.

package main

import (
    "fmt"
)

func main() {
    fmt.Println("result:", divide(1, 0))

    fmt.Println("Today is good day.")
}

func divide(a, b int) int {
    return a / b
}

try 구문이 있다면 divide 함수 내부쯤에서 try 구문을 통해 0으로 나뉨에 대한 예외를 처리하고 다시 정상적인 처리로 복구해 최종적으로 “Today is good day.”를 화면에서 볼 수 있습니다. 이에 대한 Go의 처리는 다음과 같습니다.

package main

import (
    "fmt"
)

func main() {
    fmt.Println("result:", divide(1, 0))

    fmt.Println("Today is good day.")
}

func divide(a, b int) int {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println(err)
        }
    }()

    return a / b
}

defer와 recover를 통한 것으로 defer의 인자로 지정된 함수는 마지막에 반드시 호출이 보장됩니다. 바로 이 defer에 지정된 함수 안에서 recover을 호출해 만약 현재 패닉상태라면 에러값을 얻고 정상 상태로 복구를 하게 됩니다. 이제 0으로 나뉘는 경우에도 에러의 내용고 함께 프로그램을 끝까지 정상적으로 실행하여 “Today is good day.”를 화면에 볼 수 있습니다. 아래처럼..

runtime error: integer divide by zero
result: 0
Today is good day.

[Golang] HTTP 웹 서버(Web Sever) 예제

Go 언어는 그 목적 중에 하나로 빠르고 효율적인 서버 어플리케이션 개발에 있는데요. 타 언어와는 다르게 Go는 웹서버 개발을 위한 별도의 외부 라이브러리의 도움 없이 기본 라이브러리만으로도 뛰어난 웹서버를 개발할 수 있습니다. Go 언어를 이용한 간단한 웹서버 예제를 정리해 둡니다.

이 웹서버는 2개의 URL 정의를 통해 1:1로 매칭되는 2가지 기능을 제공합니다. 예를들어서 URL이 /list로 끝나는 것과 /mean으로 구성되는 것인데요. 첫번째는 미리 정의해둔 단어와 그 의미들에 대한 목록을 서비스하고, 두번째는 QueryString으로 word 파라메터에 지정한 단어의 의미를 서비스하는데, 예를들어 http://localhost:8000/mean?word=GIS라고 하면 GIS의 의미를 문자열로 서비스합니다.

먼저 단어와 그 의미를 저정하는 map을 dic라는 타입으로 정의하고 앞서 언급한 2개의 URL 정의에 대한 서비스로 사용할 매서드를 dic 타입에 추가합니다. 아래 코드가 이에 대한 것입니다.

type dic map[string]string

func (msg dic) list(w http.ResponseWriter, req *http.Request) {
    for word, mean := range msg {
        fmt.Fprintf(w, "%s: %s\n", word, mean)
    }
}

func (msg dic) mean(w http.ResponseWriter, req *http.Request) {
    word := req.URL.Query().Get("word")
    mean, ok := msg[word]

    if !ok {
        w.WriteHeader(http.StatusNotFound)
        return
    }

    fmt.Fprintf(w, "%s\n", mean)
}

데이터 저장을 위한 저장소와 이 데이터를 서비스할 매서드 정의가 끝났으니, 이를 활용하는 코드가 필요한데요. 활용하는 코드라 함은 저장소에 몇가지 단어와 그 의미를 담고, 웹서버를 만들어 웹서버가 서비스할 URL을 정의하는 것입니다. 아래의 코드가 이에 대한 것입니다.

func main() {
    msg := dic{
        "GIS":   "지리정보시스템",
        "BIM":   "빌딩정보시스템",
        "CAD":   "컴퓨터를 이용한 디자인",
        "Dip2K": "Deeper Into Purity Since 2000",
    }

    http.HandleFunc("/list", msg.list)
    http.HandleFunc("/mean", msg.mean)

    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

이제 실행하고 웹브라우저에서 http://localhost:8000/list를 호출하면 다음과 같은 결과를 볼 수 있습니다.

GIS: 지리정보시스템
BIM: 빌딩정보시스템
CAD: 컴퓨터를 이용한 디자인
Dip2K: Deeper Into Purity Since 2000

그리고 http://localhost:8000/mean?word=GIS를 호출하면 다음과 같은 결과가 표시됩니다.

지리정보시스템

[Golang] 정렬(Sort)를 위한 sort.Interface 샘플 코드

Go에서 데이터 요소들을 가지는 슬라이스를 정렬하기 위해서는 sort 패키지의 Interface 인터페이스의 Len, Less, Swap 매서드를 구현해야 합니다. 아래의 코드는 문자열 데이터 요소를 가지는 슬라이스에 대한 정렬에 대한 sort.Interface의 구현 코드 및 활용 코드입니다.

package main

import (
    "fmt"
    "sort"
)

type StringSlice []string

func (p StringSlice) Len() int           { return len(p) }
func (p StringSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p StringSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func main() {
   names := []string{"이효숙", "김형준", "김종수", "김졍연", "김도현" }

    fmt.Println(names)

    sort.Sort(StringSlice(names))
    //sort.Strings(names)

    fmt.Println(names)
}

실행해 보면 다음과 같이 정렬되 결과를 볼 수 있습니다.

[이효숙 김형준 김종수 김졍연 김도현]
[김도현 김졍연 김종수 김형준 이효숙]

참고로 19번는 20번 코드로 대체가 가능한데요. 문자열 타입처럼 기본형 타입에 대한 데이터를 구성하는 슬라이스에 대한 정렬은 이미 sort 패키지에서 함수로 제공하고 있습니다.