[Golang] SMTP를 이용한 메일 보내기

Go는 기본으로 제공하는 net//smtp 패키지를 통해 메일을 보낼 수 있습니다. 아래의 코드는 MicroSoft의 Live를 통해 메일을 보내는 코드입니다.

package main

import (
    "fmt"
    "net/smtp"
)

func main() {
    auth := smtp.PlainAuth("", "hjkim@geoservice.co.kr", "@pw", "smtp.live.com")

    from := "hjkim@geoservice.co.kr"
    to := []string{"korea.gisdeveloper@gmail.com", "geoservice@naver.com"}

    headerSubject := "Subject: 아기공룡 둘리 ㅡ 강민규\r\n"
    headerBlank := "\r\n"

    body := `
        요리 보고 저리 봐도 음음 알 수 없는 둘리~ 둘리~ 
        빙하타고 내려와 음음 친구를 만났지만 

        일억년전 옛날이 너무나 그리워 
        보고픈 엄마찾아 모두함께 나가자 하아 하아 

        기쁠때도 슬플때도 음음 우리 곁엔 둘리~ 둘리~ 
        오랜세월 흘러온~ 둘리와 친구되어 

        고~향은 다르지만 모두가 한마음 
        아득한 엄마나라 우리 함께 떠나자~ 하아 하아 

        <후렴> 
        외로운 둘리는 귀여운 아기공룡 
        호이 호이 둘리는 초능력 내친구(재주꾼) 
        호이 호이 호이 호이  
    `

    msg := []byte(headerSubject + headerBlank + body)

    err := smtp.SendMail("smtp.live.com:587", auth, from, to, msg)
    if err != nil {
        panic(err)
    }

    fmt.Println("done")
}

9번 코드에서 smtp 서버의 주소로 smtp.live.com를 지정했으며, 이 서버의 계정(hjkim@geoservice.co.kr)과 암호(@pw)를 지정합니다. 그리고 12번 코드에서 메일을 받을 주소를 배열을 통해 지정할 수 있습니다. 그리고 14번에 메일의 제목을, 17번에서 메일의 내용을 입력하고 이 제목과 내용을 36번 코드에서처럼 하나의 바이트 배열루 묶습니다. 이렇게 묶은 바이트 메일을 38번 코드를 통해 전송하면 됩니다.

[Golang] JSON 인코딩(Encoding)과 디코딩(Decoding)

Text 기반의 데이터교환 및 전송을 위한 가장 최적화된 표준 포맷으로 JSON을 뽑을 수 있는데요. Go에서는 map이나 struct 타입 객체에 대해 JSON 문자열로 인코딩할 수 있습니다. 여기서 map의 경우에는 그 key의 타입이 string이 아닌 경우 해당 타입을 string으로 변환이 가능할 경우에만 JSON 인코딩이 가능합니다. 다음 코드는 사용자 정의 struct에 대한 map 객체를 인코딩하는 예제입니다.

package main

import (
    "encoding/json"
    "fmt"
)

type Detail struct {
    Age    int
    Active bool
}

func main() {
    mem := map[string]Detail{
        "Alex":    {10, true},
        "Dip2K":   {20, true},
        "Jackass": {15, false},
    }

    jsonBytes, err := json.Marshal(mem)
    if err != nil {
        panic(err)
    }

    jsonString := string(jsonBytes)

    fmt.Println(jsonString)
}

위의 코드를 실행하면 다음과 같은 결과를 볼 수 있습니다.

{"Alex":{"Age":10,"Active":true},"Dip2K":{"Age":20,"Active":true},"Jackass":{"Age":15,"Active":false}}

이제 다시 위에서 얻는 JSON 문자열을 객체로 인코딩해 보도록 하겠습니다. JSON 문자열을 다시 인코딩하기 위해서는 json.Unmarshal 함수를 사용합니다. 예제는 아래와 같습니다.

package main

import (
    "encoding/json"
    "fmt"
)

type Detail struct {
    Age    int
    Active bool
}

func main() {
    jsonString := `
        {
            "Alex":{"Age":10,"Active":true}, 
            "Dip2K":{"Age":20,"Active":true},
            "Jackass":{"Age":15,"Active":false}
        }`

    mem := make(map[string]Detail)
    err := json.Unmarshal([]byte(jsonString), &mem)
    if err != nil {
        panic(err)
    }

    fmt.Println(mem)
    fmt.Println("Dip2K's Active:", mem["Dip2K"].Active)
}

위의 코드에서 14번에 긴 문자열을 jsonString 변수에 담고 있는데요. 긴 문자열을 Go언어에서 입력하기 위한 방식으로 14~19번 코드를 주의해 보시기 바랍니다. 실행 결과는 다음과 같습니다.

map[Dip2K:{20 true} Jackass:{15 false} Alex:{10 true}]
Dip2K's Active: true

[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.