Let’s Go
2015-3-14
송상욱
swsong at fastcatsearch.com
♥ ♥
Why Go?
• 클라우드 시스템 관련 Core들이 Go로 작성되고 있음.
• 특히 Docker는 Go의 성공사례
• 기존 PaaS를 분석하려고 하니 Go를 읽을 줄 알아야한다.
• Github의 Go 프로젝트 59,145개 (2015.3.14기준)
Top Github Projects in Go
• docker/docker
Docker - the open-source application container
engine
• limetext/lime
Open source API-compatible alternative to the
text editor Sublime Text
• syncthing/syncthing
Open Source Continuous File Synchronization
• golang/go
The Go programming language
• GoogleCloudPlatform/kubernetes
Container Cluster Manager from Google
• go-martini/martini
Classy web framework for Go
• joewalnes/websocketd
Turn any program that uses STDIN/STDOUT into a
WebSocket server. Like inetd, but for
WebSockets.
• github/hub
hub helps you win at git.
• coreos/etcd
A highly-available key value store for shared
configuration and service discovery
• astaxie/build-web-application-with-golang
A golang ebook intro how to build a web with
golang
2015.3.14 기준으로 뽑아봤어요
참고문서
• golang-korea
– go 개발언어 관련 자료를 한국어로 번역하는 프로젝트
– https://code.google.com/p/golang-korea/
• 그외의 인터넷 문서들
기초
형아들~♥ 이제 부터 나의 기초를 보여줄게
난 생긴것 처럼 단순한 아이야~ ♥
자 그럼 Let’s Go~♥
Go 는 Object-Oriented 언어인가?
• 맞기도 하고 아니기도 하다.
– http://golang.org/doc/faq#Is_Go_an_object-oriented_language
• 아닌 이유
– 상속관계가 없다. No subclass
• 맞는 이유
– object-oriented 스타일의 프로그래밍을 허용한다. interface제공.
• object-oriented 스타일의 프로그래밍?
– 상속은 제공하지 않으며 interface만 제공.
– implements"라는 선언 필요없음.
– 단순히 해당 인터페이스의 메소드를 구현하기만 하면 인터페이스 사용가능
준비사항
• 홈페이지 : http://golang.org/
• Go SDK 다운로드 : http://golang.org/dl/
• 편집도구 : LiteIDE
– 다운로드 :
http://sourceforge.net/projects/liteide/files/
함수형태
• 리턴값 없음
func f1() { }
• 리턴값 존재
func f1() string {
return “OK”
}
• 리턴값 이름
func f2() (r string) {
r = “OK”
return
}
• struct 내부함수
type Counter struct {
n int
}
func (ctr *Counter) add(int amount) { //리시버 Counter 선언.
ctr.n += amount;
}
func f1() (string, int) {
return “OK”, 1
}
리시버는 나중에 자세하게 나오니까 염려하지마요
Go 가시성
• 최상위 type, 함수, method, 상수, 변수 혹은 구조체의
필드나 method의 이름이 대문자로 시작하면 패키지의
사용자는 접근이 가능
• 반면에 소문자일 경우에는 패키지 내부에서만 접근이
가능
진입점
• main 패키지의 main() 함수
package main
func main() {
//무언가를 수행한다.
}
실행과 빌드
• 실행
$ go run 파일명
• 빌드
$ go build
또는
$ go build 파일명
• 빌드결과삭제
$ go clean
숫자타입
• 정수형
– uint8, uint16, uint32, uint64
– int8, int16, int32, int64
– uint, int, uintptr : 장비의존적
– uint8 == byte
– uint32 == rune
• 부동소숫점
– float32, float64
• 복소수
– complex64, complex128
그외 모든 타입
bool  불린 true, false
numeric  이전 장에서 설명한 숫자타입
string  문자열. Java와 달리 소문자이다.
array  []
slice  [] array를 참조한다. ex) arr[2:5]
struct  ex) type struct Human { }
pointer  *
function  func
interface  interface
map  map
channel  chan
키워드
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
Java와 다른점 몇가지
• 세미콜론은 생략한다. 하지만 같은줄에 여러명령에는 사용.
a= 1
b= 2
c=1; d=2
• 변수선언에는 var를 사용하고 타입은 맨뒤에 선언
var a int cf) int a
var b string cf) String b
• main함수는 파라미터가 없다. 대신 flag사용
var port = flag.String("port", "8080", "Listening port")
func main() {
flag.Parse()
}
• if, for 에 괄호를 사용하지 않으며, 여는 중괄호({)는 반드시 같은줄에 사용한다.
if x > 1 { .. }
for i:=0; i< 100; i++ {
//구현
}
• 선언과 할당을 동시에 할때는 =가 아닌 := 를 사용한다. 이때 변수타입은 값의 타입으로
추정한다.
price := 2000  var price int = 2000 과 동일.
Java와 다른점 몇가지 2
• switch-case 는 break가 필요없다. 기본이 break이며, 필요시 fallthrough 사용.
• try-catch-finally, throw가 없다.
defer  finally
raise  throw
recover  catch
• 포인터존재
var a *Human
a := new(Human)
b := &i
• 사용하지 않는 변수나 import는 컴파일시 에러발생. 제거하거나 주석처리해야한다.
• import 에 alias 를 줄 수 있다.
import “math”
import math2 “my/custom/math”
math2.Average(1,2)
• private , public 이 없다. 대신 함수명이나 변수명이 대문자이면, public이고
소문자이면 private이다.
var Name string  public
var price int  private
func CallMePublic()  public
func callMeInside()  private
Java와 다른점 몇가지 3
• class라는 keyword가 없다. 대신 struct를 만들고 함수에 struct명을
주어(리시버) class와 동일하게 사용할 수 있다.
• Thread가 없다. 대신 light-weight인 고루틴을 사용한다.
go something()  비동기적 something()함수실행
• 여러값을 동시에 리턴가능하다.
return a,b,c
• 여러값을 동시에 할당가능하다.
var a,b,c int = 1,2,3
• 여러변수를 동시에 선언가능하다. import도 동일.
• 자동형변환이 안된다. 명시적 형변환필요.
var c int = 2000
var d float64 = float64(c)  OK. 만약 var d float64 = c 은 에러. var d float64= float32(c))도 에러.
• 배열선업시 괄호는 앞에 쓴다.  []int
var (
a int
b string
)
import (
“fmt”
“io”
)
변수선언/할당
• 사용법1
var x string = "Hello World"
• 사용법2
var x string
x = "Hello World"
• 사용법3
x := "Hello World"
> 일반적으로 많이 사용하는 방식. 타입추정.
변수선언/할당2
• 같은줄에 컴마구분으로 여러변수를 동일 타입으로 정의가능
a int, b int , c int , d int  a, b, c, d int
a int, b int, c float64, d float64  a, b int, c, d float64
func f(a, b int, c, d float64) {
// do something..
}
func main() {
f(1, 2, 3., 4.)
}
다중값 할당
func f() (int, int) {
return 5, 6
}
func main() {
x, y := f()
}
• 오류와 함께 반환하는 기능은 자주 사용됨.
f, err := os.Open(name, os.O_RDONLY, 0)
if err != nil {
return err
}
//파일 f 사용.
다중값 할당2
• 두값의 SWAP이 편하다
• OLD한 방식
a = 10
b = 30
tmp := b
b = a
a = tmp
• Go 방식
a = 10
b= 30
a, b = b, a
• 여러개도 된다.
a, b, c = b, c, a
다중값 할당3
• 동일한 내용 다른 표현
var (
a int = 1
b int = 2
c int = 3
)
var a int, b int, c int = 1, 2, 3
 syntax error: unexpected comma, expecting semicolon or newline or }
var a, b, c int = 1, 2, 3
a, b, c := 1, 2, 3
사용하지 않는 변수
• Go 컴파일러는 사용하지 않는 변수존재시
에러발생.
func main() {
i := 0
}
$ go run test.go
# command-line-arguments
.test.go:16: i declared and not used
문자열 비교
• 자바와 다른점
var x string = "hello"
var y string = "hello"
fmt.Println(x == y)
> true
상수
• 변수
var x string = "Hello World”
• 상수
const x string = "Hello World"
상수
• 상수의 Type을 정하지 않으면, 사용될때context에 의해 정해짐.
const huge = 1 << 100
fmt.Println(huge)  constant 1267650600228229401496703205376 overflows
int
fmt.Println(huge >> 40)  4611686018427387904
• iota(Java의 Enum효과)
const (
red = iota // red == 0
blue // blue == 1
green // green == 2
)
다중 정의
• 변수
var (
a = 5
b = 10
)
• 상수
const (
Space = " ”
Newline = "n"
)
다중 정의
• Import
import (
"os"
"fmt"
)
• Type
type(
book struct {
title string
price int
}
coffee struct {
size int
origin string
}
)
루프
• For가 유일한 루프.
• 괄호가 없다.
– 사용법1
for { }
– 사용법2
for i < 10 { }
– 사용법3
for i:=0 ; i < 10; i++ { }
특별 루프
• Range
func main() {
x := [5]int{1,2,3,4}
for i, value := range x {
fmt.Println(value)
}
}
– i : 현재위치(0부터시작)
– value : 현재값. x[i]
• 컴파일시 에러 : i declared and not used
func main() {
x := [5]int{1,2,3,4}
for _, value := range x {
fmt.Println(value)
}
}
– _(언더스코어)는 컴파일러에게 이것이 필요하지 않다고 알려주는 데 사용
Switch-Case
• 괄호가 없다.
• 상수이거나 Integer이 필요가 없다. 문자열도 가능.
• Break가 필요없다. 기본적으로 break이며, 통과를 원하면 fallthrough.
• switch 값이 생략된 경우에는 기본값은 true가 된다.
func main() {
str := "3"
switch str {
case "1":
fmt.Println("@1")
case "2": fmt.Println("@2")
default :
fmt.Println("default")
}
}
----
switch {
case i < 0 || i > 0:
fmt.Println(”Non-Zero")
fallthrough
case i == 0 : fmt.Println(”Zero")
}
Switch-Case
• 컴마로 여러 조건가능.
switch c {
case ' ', '?', '&', '=', '#', '+', '%':
…
}
배열
• 사용법1
var x [5]int
x[0] = 1; x[1] = 2; ;x[2] = 3; x[3] = 4; x[4] = 5
• 사용법2
x := [5]int{1,2,3,4,5}
• 사용법3
x := [5]int{
1,2,3,4,
5,
}
– 한줄씩 사용할 경우 컴마가 반드시 값뒤에 붙어야하고, 마지막 원소도
반드시 컴마존재.
• 사용법4
x := []int{1,2,3,4,5,7,8,9,10} // [1,2,3,4,5,7,8,9,10]
또는 x := []int{} // []
– 배열의 길이를 정하지 않아도 됨.
슬라이스
• 배열을 참조하는 일부를 가리킴.데이터공유
arr := []int{1,2,3,4,5}
x := arr[0:3] // 0<=i<3  [1,2,3]
x := arr[:3] // i<3  [0:3]과 동일
x := arr[3:] // i >= 3  [4,5]
• 선언
var x []float64
• 생성
x := make([]float64, 5)
x := make([]float64, 5, 10)
슬라이스
• 배열을 참조하는 일부를 가리킴.데이터공유
arr := []int{1,2,3,4,5}
x := arr[0:3] // 0<=i<3  [1,2,3]
x := arr[:3] // i<3  [0:3]과 동일
x := arr[3:] // i >= 3  [4,5]
• 선언
var x []float64
• 생성
x := make([]float64, 5)
x := make([]float64, 5, 10)
슬라이스 관련함수
• append
slice1 := []int{1,2,3}
slice2 := append(slice1, 4, 5) // 4,5 를 추가.
// slice1  [1,2,3]
// slice2  [1,2,3,4,5]
• copy
slice1 := []int{1,2,3}
slice2 := make([]int, 2) // 길이가 2인 슬라이스
copy(slice2, slice1)
// slice1  [1,2,3]
// slice2  [1,2]
맵
• Key는 동등연산자(==)이 가능한 integer, float, 복소수, string,
포인터, 그리고 인터페이스와 같이 어떤 타입이든 가능
• struct와 배열,slice는 동등연산을 할 수 없기 때문에 불가.
• 사용법1
var x map[string]int
x = make(map[string]int)
x["a"]=1; x["b"]=2; x["c"]=3
• 사용법2
x := make(map[string]int)
x["a"]=1; x["b"]=2; x["c"]=3
• 사용법3
x := map[string]int{"a":1, "b":2, "c":3}
맵
• 참조
name := x[”aaaa"]
또는
name, ok := x[”aaaa"]
fmt.Println(name, ok)
• 삭제
delete(x, "b")
• 갯수확인
len(x)
다중맵
elements := map[string]map[string]string{
"H": map[string]string{
"name":"Hydrogen",
"state":"gas",
},
"He": map[string]string{
"name":"Helium",
"state":"gas",
},
}
가변함수
func add(args ...int) int {
total := 0
for _, v := range args {
total += v
}
return total
}
• add(1,2,3)  OK
• xs := []int{1,2,3}
add(xs)  Error. 가변인자는 슬라이스와 호환되지 않는다.
• add(xs...)  OK. 가변인자 변수자체를 전달할때는 ...을 붙여야 인식됨.
가변인자는 add(foo...)와 같이 ...를 반드시 붙어야해요
클로저
func main() {
add := func(x, y int) int {
return x + y
}
fmt.Println(add(1,1))
}
---------------------------------------------------------------------------------------------
func makeEvenGenerator() func() uint {
i := uint(0)
return func() (ret uint) {
ret = i
i += 2
return
}
}
func main() {
nextEven := makeEvenGenerator()
fmt.Println(nextEven()) // 0
fmt.Println(nextEven()) // 2
fmt.Println(nextEven()) // 4
}
Defer (지연된 호출)
• 해당함수 종료시 실행됨.
• Java의 try-finally 대체
func main() {
defer second()
first()
}
------------------------------------------------
f, _ := os.Open(filename)
defer f.Close()
• 장점
1. Close 호출을 Open 호출 가까이에 둬서 이해하기가 쉽다.
2. 함수에 return이 여러 개 있더라도 Close가 어떠한 return 시라도 호출
3. 지연된 함수는 런타임 패닉이 일어나더라도 실행
Panic & Recover
• Java의 throw-catch 를 대체
• panic : 런타임오류 강제발생
• recover : 런타임패닉 처리
• 잘못된 사용예. recover가 호출기도 전에 종료.
panic("PANIC")
str := recover()
• 올바른 사용예. defer와 짝을 맞춤. 익명함수 사용.
defer func() {
str := recover()
fmt.Println(str)
}()
panic("PANIC”)
• 사용목적
– 범위를 벗어난 배열 인덱스에 접근시 에러처리
– 맵을 초기화하는 것을 잊어버릴 경우 에러처리
구조체 / 인터페이스
포인터
• Address 연산자로 포인터 사용
func one(xPtr *int) {
*xPtr = 0
}
func main() {
x := 5
one(&x)
fmt.Println(x) // x는 1
}
----------------------------
• new 로 포인터 사용
func main() {
xPtr := new(int)
one(xPtr)
fmt.Println(*xPtr) // x는 1
}
포인터 접근
type Rectangle struct {
length, width int
}
func main() {
var r *Rectangle
r = new(Rectangle) // Rectangle에 대한 포인터 반환.
r.length = 5
(*r).length = 5 //r.length=5와 동일하다.
fmt.Println(r.length) //  5
fmt.Println((*r).length) //  5
}
• c++과 달리 -> 가 없다. 포인터, 인스턴스 모두에 대해 dot(.) 노테이션 사용
Struct
• Go는 클래스가 없다.
• 하지만 클래스와 동일한걸 만들수 있다.
• Java에서 Rectangle 클래스는 area()메소드를 가지고 있음
//JAVA
public class Rectangle {
private float x1, y2, x2, y2;
public float area() {
float l = distance(x1, y1, x1, y2);
float w = distance(x1, y1, x2, y1);
return l * w;
}
private float distance(float x1, float y1, float x2, float y2) {
float a = x2 - x1;
float b = y2 - xy;
return Math.sqrt(a*a + b*b);
}
}
Struct
//C++
class rectangle {
private:
float x1, y2, x2, y2;
public:
float area(void);
}
inline float rectangle::area()
{
float l = distance(x1, y1, x1, y2);
float w = distance(x1, y1, x2, y1);
return l * w;
}
float distance(float x1, float y1, float x2, float y2)
{
float a = x2 - x1;
float b = y2 - xy;
return sqrt(a*a + b*b);
}
Struct
• Receiver를 사용하여 특정 type에 종속적 함수를 생성한다.
//Go
type Rectangle struct {
x1, y1, x2, y2 float64
}
func (r *Rectangle) area() float64 {
l := distance(r.x1, r.y1, r.x1, r.y2)
w := distance(r.x1, r.y1, r.x2, r.y1)
return l * w
}
func distance(x1, y1, x2, y2 float64) float64 {
a := x2 – x1
b := y2 – y1
return math.Sqrt(a*a + b*b)
}
func main() {
r := Rectangle{1, 5, 3, 7}
fmt.Println(r) //  {1 5 3 7}
}
Struct
• 리시버는 포인터 or 객체중 어느걸 써야할까?
type Rectangle struct {
length, width int
}
func (r Rectangle) set(i, j int) {
r.length = i
r.width = j
}
-----
• Receiver는 아래 두가지 모두 사용가능.
func (r Rectangle) set(i, j int) { .. } // OK
func (r *Rectangle) set(i, j int) { .. } // OK
Struct
1. 객체 사용시
func (r Rectangle) set(i, j int) {
r.length = i
r.width = j
}
func main() {
r := Rectangle{1, 5}
r.set(10, 50)
fmt.Println(r) //  [1 5] 안바뀐다. r이 set함수에 복사되어 전달됨. shallow copy.
}
2. 포인터 사용시
func (r *Rectangle) set(i, j int) { ..}
결과 [10 50]
변경됨. call by reference.
Struct
• 생성방식
type Rectangle struct {
length, width int
}
var r Rectangle // Rectangle타입. 내용은 length=0, width=0
var r *Rectangle // *Rectangle타입. 내용은 nil.
r := new(Rectangle) // *Rectangle타입. 내용은 length=0, width=0
r := Rectangle{width:10, length: 50} // Rectangle타입. 이름명시
r := Rectangle{10, 50} // Rectangle타입. 순서대로 할당
Struct – embedded type
type Person struct {
Name string
}
• HAS-A 관계
type Android struct {
Person person
Model string
}
a.person.Name=5 //Person을 통해서 접근
• IS-A 관계
type Android struct {
Person //Android is a person
Model string
}
a := new(Android)
a.Name=5 // Android가 Person인것 처럼 접근가능.
Struct –기본 타입 확장
• 기존 int 타입을 기반으로 string() 함수를 붙인다.
#src/fmt/stringer_test.go 참조
type TI int
func (v TI) String() string {
return fmt.Sprintf("I: %d", int(v))
}
• Java라면
public class IntegerType extends Integer {
public String string() { .. }
}
$ javac IntegerType.java
IntegerType.java:1: error: cannot inherit from final Integer
public class IntegerType extends Integer {
^
1 error
 Integer는 Final class 이므로 확장 불가.
Interface
• Rectangle
type Rectangle struct {
x1, y1, x2, y2 float64
}
func (r *Rectangle) area() float64 { .. }
• Circle
type Circle struct {
x, y, r float64
}
func (c *Circle) area() float64 { .. }
• Shape
type Shape interface {
area() float64
}
• 사용시
func totalArea(shapes ...Shape) float64 { .. }
어떠한 선언적 종속관계도 없다!!
동시성
고루틴(go)
package main
import (
"fmt"
"math/rand"
"time"
)
func f(n int) {
amt := time.Duration(rand.Intn(250))
time.Sleep(time.Millisecond * amt)
fmt.Println(n)
}
func main() {
for i := 0; i < 1000000; i++ {
go f(i)
}
var input string
fmt.Scanln(&input)
}
채널(chan)
• 채널(channel)은 두 고루틴이 서로 통신하고 실행흐름을 동기화하는 수단을
제공
func main() {
c := make(chan string)
c <- "oce”
msg := <-c
fmt.Println(msg)
}
결과
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
• main함수는 첫번째 고루틴이다.
채널(chan)
func main() {
c := make(chan string)
go func() { // 익명 함수의 고루틴
c <- "oce”
}()
msg := <-c
fmt.Println(msg)
}
결과
$ go run main.go
oce
• 비동기 채널설정
기본적으로 채널은 동기적. 버퍼를 주면 비동기적으로 동작가능.
c := make(chan int, 1) //길이가 1인 버퍼설정.
채널(chan) - 방향
• 양방향
func f(c chan string)
• 입력전용
func f(c chan<- string)
• 출력전용
func f(c <-chan string)
Select
c1 := make(chan string)
c2 := make(chan string)
select {
case msg1 := <- c1:
fmt.Println("Message 1", msg1)
case msg2 := <- c2:
fmt.Println("Message 2", msg2)
case <- time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("nothing ready")
}
기타
초기화
• func init() 이 존재하면 main()보다도 먼저실행
• init()은 해당패키지 import시 실행
– import “abc” 가 실행시 abc.go내 init()이 실행된다.
• init() 여러번 중복해서 선언될수 있으며, 코드 순서대로 순차적 실행
package main
import "fmt”
var i = foo()
func foo() int {
fmt.Println("Call foo()")
return 1
}
func init() {
fmt.Println("Call init() 1")
}
func init() {
fmt.Println("Call init() 2")
}
func main() {
fmt.Println("Call main()")
}
func init() {
fmt.Println("Call init() 3")
}
실행결과
Call foo()
Call init() 1
Call init() 2
Call init() 3
Call main()
make()와 new()의 차이
• make()
– 오로지 slice, map, channel을 만드는 용도로만 사용.
– 포인터를 반환하지 않음. 즉 *T가 아닌 T타입을 리턴.
var p *[]int = new([]int) // slice구조를 할당함; *p == nil; 그다지 유용하지 않음
var v []int = make([]int, 100) // slice v는 이제 100개의 int를 가지는 새로운 배열을 참조함
// new : 불필요하게 복잡함:
var p *[]int = new([]int)
*p = make([]int, 100, 100)
// make: 자연스러움:
v := make([]int, 100)
Conversion
var f1 float64 = 6.0
f2 := 12 / f1 // 상수 12는 float64로 자동변환
 2
var i int = 12
var f1 float64 = 6.0
f2 := i / f1 // 변수타입은 자동변환되지 않는다.
 invalid operation: i / f1 (mismatched types int and float64)
var i = 12. //12. 은 float64 형이다.
var f1 float32 = 6.0
f2 := i / f1 //심지어 같은 float형이라도 float32와 float64는 자동형변환
불가.
 invalid operation: i / f1 (mismatched types float64 and float32)
Conversion
var i = 12.
var f1 float32 = 6.0
f2 := i / f1
 ERROR
var i = 12.
var f1 float32 = 6.0
f2 := float32(i) / f1
 2
i := 12
fmt.Println(”Result = ” + i)
 cannot convert "Result=" to type int
invalid operation: "Result=" + i (mismatched types string and int)
import ”strconv”
i := 12
fmt.Println("Result = " + strconv.Itoa(i))
 Result = 3
GOROOT
• GOROOT는 Go SDK의 설치위치를 나타내며, 이 위치하위에서 기본
라이브러리와 문서등을 찾아서 사용한다.
• Go 기본설치위치
– *nix : /usr/local/go
– Windows : C:Go
• 만약 다른 곳에 설치했다면 GOROOT 환경변수를 설정해주어야함.
– ex) /application/go에 설치했다면,
export GOROOT=/application/go
export PATH=$PATH:$GOROOT/bin
– *nix계열은 .bash_profile과 같은 설정파일에 추가한다.
• 기본위치에 설치되어 있다면 GOROOT를 설정할 필요없음
GOPATH
• Java에는 classpath, Go에는 GOPATH
• import 시 GOPATH의 경로를 찾음
• *Nix계열은 콜론, Windows는 세미콜론으로 연결된 경로 리스트
GOPATH=/home/user/gocode
/home/user/gocode/
src/
foo/
bar/ (go code in package bar)
x.go
grep2/ (go code in package main)
y.go
bin/
grep2 (installed command)
pkg/
linux_amd64/
oce/
bar.a (installed package object)
Tip: GOPATH가 여러개일때 특정 src하위에서 install 된 패키지파일은 해당 고패스하위로 들어간다.
패키지
GOPATH=<User Home Directory>/go
$ cd go
$ mkdir -p src/math2
$ cd src/math2
$ vi foo.go # 파일명과 패키지 명이 동일할 필요없음.
package math2 // 패키지명은 폴더와 이름이 일치해야함.
func Average(i, j int) float64 { // average가 아닌 대문자 Average로 선언. import후 호출가능함
return float64(i + j) / 2
}
$ go install
 <User Home Directory>/go/pkg/darwin_amd64/math2.a 생성됨.
-------------------
package main
import “fmt”
import “math2”
func main() {
i := math2.Average(10,30) // 20
fmt.Println(“i=“, i)
}
> 패키지명을 math로 하면 이미 존재하는 go의 기본패키지명과 겹치므로 go install시 에러발생.
패키지
• 최상위가 아닌 중간경로를 주면 math라는 이름으로 패키지 생성가능.
$ mkdir -p src/oce.org/math
$ cd src/oce.org/math
$ vi foo.go
package math
func Average(i, j int) float64 {
return float64(i + j) / 2
}
$ go install
 <User Home Directory>/go/pkg/darwin_amd64/oce.org/math.a 생성됨.
-------------------
package main
import “fmt”
import “oce.org/math”
func main() {
i := math.Average(10,30) // 20
fmt.Println(“i=“, i)
}
패키지
• 패키지 이름의 중복을 방지하기 위하여 import시 Alias가능
package main
import “fmt”
import “math”
import math2 “oce.org/math”
func main() {
i := math.Average(10,30)
j := math2.Average(10,30)
fmt.Println(“i=“, i, “j=“, j)
}
패키지
• Alias를 Dot(.) 으로 하면 해당패키지를 이름없이 바로 접근가능.
package main
import . “fmt”
func main() {
Println(“Called without package name!”)
}
패키지 - remote
• go get
문서화
• Java에는 javadoc, Go에는 godoc
• usage: godoc package [name ...]
godoc -http=:6060
$ godoc oce.org/math
PACKAGE DOCUMENTATION
package math
import "oce.org/math”
FUNCTIONS
func Average(i, j int) float64
$ godoc oce.org/math average  대소문자구분
No match found.
$ godoc oce.org/math Average
func Average(i, j int) float64
$godoc –http=:6060  로컬 웹서버시작
 http://localhost:6060/pkg/
문서화
//Copyright 2015
// oce.org에서 제공하는 수학패키지.
//  빈줄삽입.
// 2015-3-14 업데이트.
package math
// 평균을 구한다.
// 입력값은 i,j 정수를 받아들인다.
// 리턴값은 float64이다.
//
// 입력값:
// i 첫번째정수  Tab을 넣었다.
// j 두번째정수  Tab을 넣었다.
func Average(i, j int) float64 {
return float64(i + j) / 2
}
주석에 Tab이 들어있으면, 회색Box안으로 표시되요
문서화
$ go install; godoc oce.org/math
PACKAGE DOCUMENTATION
package math
import "oce.org/math"
oce.org에서 제공하는 수학패키지.
 빈줄이 나타났다.
2015-3-14 업데이트.
FUNCTIONS
func Average(i, j int) float64
평균을 구한다. 입력값은 i,j 정수를 받아들인다. 리턴값은 float64이다.  주석내 개행은
무시된다.
입력값:
i 첫번째정수
j 두번째정수
문서화
http://localhost:6060/pkg
$ godoc -http=:6060
문서화
http://localhost:6060/pkg/oce.org/math/$ godoc -http=:6060
가장 좋은 문서화예제는 Go
sdk의 소스에 있어요.
<GOROOT>/src/fmt/print.go
<GOROOT>/src/fmt/doc.go
를 참조하세요.
문서화
• Example 도 넣을수 있다.
패키지 os/exec/
문서화
• 함수정의 (src/os/exec/exec.go, src/os/exec/lp_unix.go)
func LookPath(file string) (string, error) { .. }
func (c *Cmd) Start() error { .. }
• 예제정의(src/os/exec/example_test.go)
func ExampleLookPath() {
path, err := exec.LookPath("fortune")
if err != nil {
log.Fatal("installing fortune is in your future")  이 소스가 예제항목으로 삽입된다.
}
fmt.Printf("fortune is available at %sn", path)
}
func ExampleCmd_Start() {
cmd := exec.Command("sleep", "5")
err := cmd.Start()
if err != nil {
log.Fatal(err)  이 소스가 예제항목으로 삽입된다.
}
log.Printf("Waiting for command to finish...")
err = cmd.Wait()
log.Printf("Command finished with error: %v", err)
}
테스트
• 테스트 함수만드는 법
– 파일이름은 _test.go로 끝난다.
– 함수이름이 Test로 시작
– *testing.T를 인자로 받을것 (import “testing”)
• 예제
# foo_test.go
package math
import "testing"
func TestAverage(t *testing.T) {
var v float64
v = Average([]float64{1,2})
if v != 1.5 {
t.Error("Expected 1.5, got ", v)
}
}
테스트
$ go test
PASS
ok oce.org/math 0.003s
• 만약 파일명이 _test.go, test.go, foo_testing.go 등 파일명규칙을 따르지 않을때는
아래와 같이 테스트파일을 못찾는다.
$ go test
? oce.org/math [no test files]
• fmt 패키지내 파일 리스트 참고 (경로 <GOROOT>/src/fmt)
doc.go
export_test.go
fmt_test.go
format.go
print.go
scan.go
scan_test.go
stringer_test.go
테스트
import "testing"
type testpair struct {
values []float64
average float64
}
var tests = []testpair{
{ []float64{1,2}, 1.5 },
{ []float64{1,1,1,1,1,1}, 1 },
{ []float64{-1,1}, 0 },
}
func TestAverage(t *testing.T) {
for _, pair := range tests {
v := Average(pair.values)
if v != pair.average {
t.Error(
"For", pair.values,
"expected", pair.average,
"got", v,
)
}
}
}
struct를 이용하면 여러
테스트값을 편하게
테스트해볼 수 있어요.
핵심패키지
strings
strings.Contains("test", "es") // true
strings.Count("test", "t") // 2
strings.HasPrefix("test", "te") // true
strings.HasSuffix("test", "st") // true
strings.Index("test", "e") // 1
strings.Join([]string{"a","b"}, "-") // "a-b"
strings.Repeat("a", 5) // "aaaaa"
strings.Replace("aaaa", "a", "b", 2) // "bbaa"
strings.Split("a-b-c-d-e", "-") // []string{"a","b","c","d","e"}
strings.ToLower("TEST") // "test"
strings.ToUpper("test") // "TEST”
----
arr := []byte("test")
str := string([]byte{'t','e','s','t'})
더 많은 정보는 여기있어요
https://golang.org/pkg/strings
io
• 함수정의 보다는 대부분이 interface로 구성
• 주요 인터페이스는 Reader와 Writer
• Reader는 Read(), Writer는 Write() 지원
• Reader  Writer 데이터 복사
– func Copy(dst Writer, src Reader) (written int64, err error)
더 많은 정보는 여기있어요
https://golang.org/pkg/io
bytes
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
var b bytes.Buffer // 버퍼는 초기화가 필요없다. 선언후 그대로 사용.
b.Write([]byte("Hello "))
fmt.Fprintf(&b, "world!")
b.WriteTo(os.Stdout)
}
-----
func main() {
// string 이나 []byte 를 io.Reader 로 바꿔준다.
buf := bytes.NewBufferString("R29waGVycyBydWxlIQ==")
dec := base64.NewDecoder(base64.StdEncoding, buf)
io.Copy(os.Stdout, dec)
}
더 많은 정보는 여기있어요
https://golang.org/pkg/bytes
os
• 파일열기 : os.Open()
• 파일닫기 : file.Close()
package main
import (
"fmt"
"os"
)
func main() {
file, err := os.Open("test.txt")
if err != nil {
// 오류를 처리
return
}
defer file.Close()
// 파일의 크기를 구함
stat, err := file.Stat()
if err != nil {
return
}
// 파일을 읽음
bs := make([]byte, stat.Size())
_, err = file.Read(bs)
if err != nil {
return
}
str := string(bs)
fmt.Println(str)
}
error
• 오류에 대한 내장타입
• error.New() 사용시 자체오류 생성가능
package main
import (
"errors"
"fmt"
)
func main() {
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
}
-----
func main() {
const name, id = "bimmler", 17
err := fmt.Errorf("user %q (id %d) not found", name, id)
if err != nil {
fmt.Print(err)
}
}
container
heap Package heap provides heap operations for any type that implements heap.Interface.
list Package list implements a doubly linked list.
ring Package ring implements operations on circular lists.
package main
import (
"container/list"
"fmt"
)
func main() {
// Create a new list and put some numbers in it.
l := list.New()
e4 := l.PushBack(4)
e1 := l.PushFront(1)
l.InsertBefore(3, e4)
l.InsertAfter(2, e1)
// Iterate through list and print its contents.
for e := l.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value)
}
}
container - heap
// This example demonstrates an integer heap built using
the heap interface.
package main
import (
"container/heap"
"fmt"
)
// An IntHeap is a min-heap of ints.
type IntHeap []int
func (h IntHeap) Len() int { return len(h) }
func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *IntHeap) Push(x interface{}) {
// Push and Pop use pointer receivers because
they modify the slice's length,
// not just its contents.
*h = append(*h, x.(int))
}
func (h *IntHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
// This example inserts several ints into an
IntHeap, checks the minimum,
// and removes them in order of priority.
func main() {
h := &IntHeap{2, 1, 5}
heap.Init(h)
heap.Push(h, 3)
fmt.Printf("minimum: %dn", (*h)[0])
for h.Len() > 0 {
fmt.Printf("%d ", heap.Pop(h))
}
}
sort
• Sort 함수는 sort.Interface가 구현된 것만 정렬가능
• sort.Interface는 Len, Less, Swap의 3개 메서드 필요.
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less reports whether the element with
// index i should sort before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
sort
type Person struct {
Name string
Age int
}
type ByName []Person
func (this ByName) Len() int {
return len(this)
}
func (this ByName) Less(i, j int) bool {
return this[i].Name < this[j].Name
}
func (this ByName) Swap(i, j int) {
this[i], this[j] = this[j], this[i]
}
func main() {
kids := []Person{
{"Jill",9},
{"Jack",10},
}
sort.Sort(ByName(kids))
fmt.Println(kids)
}
hash
type Hash interface {
// Write (via the embedded io.Writer interface) adds more data
to the running hash.
// It never returns an error.
io.Writer
// Sum appends the current hash to b and returns the resulting
slice.
// It does not change the underlying hash state.
Sum(b []byte) []byte
// Reset resets the Hash to its initial state.
Reset()
// Size returns the number of bytes Sum will return.
Size() int
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently if all writes
// are a multiple of the block size.
BlockSize() int
}
type Hash32 interface {
Hash
Sum32() uint32
}
type Hash64 interface {
Hash
Sum64() uint64
}
hash - 비암호화
• 종류 : adler32, crc32, crc64, fnv
package main
import (
"fmt"
"hash/crc32"
)
func main() {
h := crc32.NewIEEE()
h.Write([]byte("test"))
v := h.Sum32()
fmt.Println(v)
}
 3632233996
crypto – 암호화해시
• 종류 : aes, cipher, des, dsa, ecdsa, elliptic, hmac, md5, rand, rc4, rsa, sha1,
sha256, sha512, subtle, tls, 509
package main
import (
"crypto/sha1"
"fmt"
"io"
)
func main() {
h := sha1.New()
io.WriteString(h, "His money is twice tainted:")
io.WriteString(h, " 'taint yours and 'taint mine.")
fmt.Printf("% x", h.Sum(nil))
}
net
• Go는 아래에 대해 Portable 인터페이스를 제공
– Network I/O
– TCP/IP
– UDP
– Domain name resolution
– Unix domain socket
type Listener interface {
// Accept waits for and returns the next connection to the listener.
Accept() (c Conn, err error)
// Close closes the listener.
// Any blocked Accept operations will be unblocked and return errors.
Close() error
// Addr returns the listener's network address.
Addr() Addr
}
net
//////////////////// Client 간단버전 //////////////////
conn, err := net.Dial("tcp", "google.com:80")
if err != nil {
// handle error
}
fmt.Fprintf(conn, "GET / HTTP/1.0rnrn")
status, err := bufio.NewReader(conn).ReadString('n')
// ...
////////////////// Server 간단버전 //////////////////
ln, err := net.Listen("tcp", ":8080")
if err != nil {
// handle error
}
for {
conn, err := ln.Accept()
if err != nil {
// handle error
}
go handleConnection(conn)
}
net
////////////// Echo 서버 ////////////
package main
import (
"io”; "log”; "net"
)
func main() {
// Listen on TCP port 2000 on all interfaces.
l, err := net.Listen("tcp", ":2000")
if err != nil {
log.Fatal(err)
}
defer l.Close()
for {
// Wait for a connection.
conn, err := l.Accept()
if err != nil {
log.Fatal(err)
}
// Handle the connection in a new
goroutine.
// The loop then returns to accepting, s
o that
// multiple connections may be served
concurrently.
go func(c net.Conn) {
// Echo all incoming data.
io.Copy(c, c)
// Shut down the connection.
c.Close()
}(conn)
}
}
$ telnet localhost 2000
I love Go  입력
I love Go  출력
net/http
package main
import ("net/http" ; "io")
func hello(res http.ResponseWriter, req
*http.Request) {
res.Header().Set(
"Content-Type",
"text/html",
)
io.WriteString(
res,
`<doctype html>
<html>
<head><title>Hello World</title></head>
<body>Hello World!</body>
</html>`,
)
}
func main() {
http.HandleFunc("/hello", hello)
http.ListenAndServe(":9000", nil)
}
http://localhost:9000/hello
HandleFunc는 URL 라우팅(/hello)
처리를 담당해요
net/rpc
package main
import (
"fmt” ; "net”; "net/rpc"
)
type Server struct {}
func (this *Server) Negate(i int64, reply *int64) error {
*reply = -i
return nil
}
func server() {
rpc.Register(new(Server))
ln, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Println(err)
return
}
for {
c, err := ln.Accept()
if err != nil {
continue
}
go rpc.ServeConn(c)
}
}
func client() {
c, err := rpc.Dial("tcp", "127.0.0.1:9999")
if err != nil {
fmt.Println(err)
return
}
var result int64
err = c.Call("Server.Negate", int64(999), &result)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Server.Negate(999) =", result)
}
}
func main() {
go server()
go client()
var input string
fmt.Scanln(&input)
}
flag (명령줄 파싱)
• 명령줄 플래그(-로 시작)를 flag.String(), Bool(), Int() 등을 사용하여 파싱
– flag.Int(플래그명, 기본값, 설명메시지)
– flag.IntVar(입력변수주소, 플래그명, 기본값, 설명메시지)
– 방법1
import "flag"
var ip = flag.Int("flagname", 1234, "help message for flagname”)
– 방법2
var flagvar int
func init() {
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}
• 플래그가 아닌 일반파라미터는 flag.Args()를 통해 받을 수 있으며
[]string 형으로 리턴된다.
var args []string = flag.Args()
flag (명령줄 파싱)
package main
import (
"flag"
"fmt"
)
var species = flag.String("species", "gopher", "the species we are studying")
var gopherType string
func init() {
const (
defaultGopher = "pocket"
usage = "the variety of gopher"
)
flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage)
flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)")
}
func main() {
flag.Parse()
fmt.Println("species=", *species, "ngopherType=", gopherType)
var args []string = flag.Args()
fmt.Println("Args=", args)
}
flag (명령줄 파싱)
$ ./hello
species= gopher
gopherType= pocket
Args= []
$ ./hello -h
Usage of ./hello:
-g="pocket": the variety of gopher (shorthand)
-gopher_type="pocket": the variety of gopher
-species="gopher": the species we are studying
$ ./hello -species=oce -g=fastcat
species= oce
gopherType= fastcat
Args= []
$ ./hello -species=oce -gopher_type=fastcat  위 결과와 동일
species= oce
gopherType= fastcat
Args= []
$ ./hello -species=”oce fastcat” world war 2
species= oce fastcat
gopherType= pocket
Args= [world war 2]
sync
type Cond
func NewCond(l Locker) *Cond
func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()
type Locker
type Mutex
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
type Once
func (o *Once) Do(f func())
type Pool
func (p *Pool) Get() interface{}
func (p *Pool) Put(x interface{})
type RWMutex
func (rw *RWMutex) Lock()
func (rw *RWMutex) RLock()
func (rw *RWMutex) RLocker() Locker
func (rw *RWMutex) RUnlock()
func (rw *RWMutex) Unlock()
type WaitGroup
func (wg *WaitGroup) Add(delta int)
func (wg *WaitGroup) Done()
func (wg *WaitGroup) Wait()
더 많은 정보는 여기있어요
https://golang.org/pkg/sync/
sync/Mutex
package main
import (
"fmt”; "sync”; "time"
)
func main() {
m := new(sync.Mutex)
for i := 0; i < 10; i++ {
go func(i int) {
m.Lock()
fmt.Println(i, "start")
time.Sleep(time.Second)
fmt.Println(i, "end")
m.Unlock()
}(i)
}
var input string
fmt.Scanln(&input)
}
실행결과
0 start
0 end
1 start
1 end
2 start
2 end
3 start
3 end
4 start
4 end
5 start
5 end
6 start
6 end
7 start
7 end
8 start
8 end
9 start
9 end
sync/Once
package main
import (
"fmt”; "sync"
)
func main() {
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
}
 결과 : Only once
sync/WaitGroup
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
for _, url := range urls {
// Increment the WaitGroup counter.
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
// Decrement the counter when the goroutine completes.
defer wg.Done()
// Fetch the URL.
http.Get(url)
}(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()
Go 의 동기화 스타일
type Work struct {
x, y, z int
assigned, done bool
}
type WorkSet struct {
mu sync.Mutex
work []*Work
}
type Work struct { x, y, z int }
func worker(in <-chan *Work, out chan<- *Work) {
for w := range in {
w.z = w.x * w.y // do some work...
out <- w
}
}
func main() {
in, out := make(chan *Work), make(chan *Work)
for i := 0; i < 10; i++ { go worker(in, out) }
go sendLotsOfWork(in)
receiveLotsOfResults(out)
}
메모리공유를 통한 통신이나
공유데이터를 보호하기 위한 Lock이 필요없다.
전통적인 접근 Go 스타일
time
정의 : func Sleep(d Duration)
샘플 : time.Sleep(100 * time.Millisecond)
정의 : func (t Time) Sub(u Time) Duration
샘플 :
t0 := time.Now()
expensiveCall()
t1 := time.Now()
mt.Printf("The call took %v to run.n", t1.Sub(t0))
 3m56s
정의 : func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
샘플 : t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
정의 : func (t Time) Format(layout string) string
샘플 :
const layout = "Jan 2, 2006 at 3:04pm (MST)"
t := time.Date(2009, time.November, 10, 15, 0, 0, 0, time.Local)
fmt.Println(t.Format(layout))
정의 : func Parse(layout, value string) (Time, error)
샘플 :
const longForm = "Jan 2, 2006 at 3:04pm (MST)"
t, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)")
Go 에는 Java의 yyyyMMdd
같은 형식이 없어요.
regexp
정의 : func MatchString(pattern string, s string) (matched bool, err error)
샘플 : matched, err := regexp.MatchString("foo.*", "seafood")
정의 : func MustCompile(str string) *Regexp
샘플 : var re = regexp.MustCompile(`^[a-z]+[[0-9]+]$`)
정의 : func (re *Regexp) MatchString(s string) bool
샘플 : fmt.Println(re.MatchString("adam[23]"))
정의 : func (re *Regexp) FindAllString(s string, n int) []string
샘플 : fmt.Println(re.FindAllString("paranormal", -1))
정의 : func (re *Regexp) ReplaceAllString(src, repl string) string
샘플 : fmt.Println(re.ReplaceAllString("-ab-axxb-", "T"))
database
age := 27
rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
log.Fatal(err)
}
fmt.Printf("%s is %dn", name, age)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
https://github.com/golang/go/wiki/SQLDrivers
database - drivers
• MySQL: https://github.com/ziutek/mymysql [*]
• MySQL: https://github.com/go-sql-driver/mysql/ [*]
• Postgres (pure Go): https://github.com/lib/pq [*]
• Oracle: https://github.com/mattn/go-oci8
• MS SQL Server (pure go): https://github.com/denisenkom/go-mssqldb
그 외의 드라이버는 여기를 참조하세요.
https://github.com/golang/go/wiki/SQLDrivers
database - mysql
package main
import (
"os"
"github.com/ziutek/mymysql/mysql"
_ "github.com/ziutek/mymysql/native" // Native
engine
// _ "github.com/ziutek/mymysql/thrsafe" //
Thread safe engine
)
func main() {
db := mysql.New("tcp", "", "127.0.0.1:3306", user,
pass, dbname)
err := db.Connect()
if err != nil {
panic(err)
}
rows, res, err := db.Query("select * from X where
id > %d", 20)
if err != nil {
panic(err)
}
https://github.com/ziutek/mymysql
for _, row := range rows {
for _, col := range row {
if col == nil {
// col has NULL value
} else {
// Do something with text in col (type
[]byte)
}
}
// You can get specific value from a row
val1 := row[1].([]byte)
// You can use it directly if conversion isn't
needed
os.Stdout.Write(val1)
// You can get converted value
number := row.Int(0) // Zero value
str := row.Str(1) // First value
bignum := row.MustUint(2) // Second value
// You may get values by column name
first := res.Map("FirstColumn")
second := res.Map("SecondColumn")
val1, val2 := row.Int(first), row.Str(second)
}
}
log
package main
import (
"bytes"
"fmt"
"log"
)
func main() {
var buf bytes.Buffer
logger := log.New(&buf, "logger: ", log.Lshortfile)
logger.Print("Hello, log file!")
fmt.Print(&buf)
}
 logger: main.go:12: Hello, log file!
log - level
• 로그레벨에 따라 로깅하는 패키지는 기본 SDK에 없다.
• https://github.com/jcelliott/lumber
• 콘솔로거
log := lumber.NewConsoleLogger(lumber.WARN)
• 파일로거
log := lumber.NewFileLogger("filename.log", lumber.INFO, lumber.ROTATE, 5000, 9, 100)
• 파일로거 (rotate)
log := lumber.NewRotateLogger("filename.log", 5000, 9)
• 사용법
log.Trace("the %s log level", "lowest")
log.Debug("")
log.Info("the default log level")
log.Warn("")
log.Error("")
log.Fatal("the %s log level", "highest")
• 로그앞에 prefix 붙이기
log.Prefix("MYAPP")
끝
수고하셨어요~♥

Let's Go (golang)

  • 1.
  • 2.
    Why Go? • 클라우드시스템 관련 Core들이 Go로 작성되고 있음. • 특히 Docker는 Go의 성공사례 • 기존 PaaS를 분석하려고 하니 Go를 읽을 줄 알아야한다. • Github의 Go 프로젝트 59,145개 (2015.3.14기준)
  • 3.
    Top Github Projectsin Go • docker/docker Docker - the open-source application container engine • limetext/lime Open source API-compatible alternative to the text editor Sublime Text • syncthing/syncthing Open Source Continuous File Synchronization • golang/go The Go programming language • GoogleCloudPlatform/kubernetes Container Cluster Manager from Google • go-martini/martini Classy web framework for Go • joewalnes/websocketd Turn any program that uses STDIN/STDOUT into a WebSocket server. Like inetd, but for WebSockets. • github/hub hub helps you win at git. • coreos/etcd A highly-available key value store for shared configuration and service discovery • astaxie/build-web-application-with-golang A golang ebook intro how to build a web with golang 2015.3.14 기준으로 뽑아봤어요
  • 4.
    참고문서 • golang-korea – go개발언어 관련 자료를 한국어로 번역하는 프로젝트 – https://code.google.com/p/golang-korea/ • 그외의 인터넷 문서들
  • 5.
    기초 형아들~♥ 이제 부터나의 기초를 보여줄게 난 생긴것 처럼 단순한 아이야~ ♥ 자 그럼 Let’s Go~♥
  • 6.
    Go 는 Object-Oriented언어인가? • 맞기도 하고 아니기도 하다. – http://golang.org/doc/faq#Is_Go_an_object-oriented_language • 아닌 이유 – 상속관계가 없다. No subclass • 맞는 이유 – object-oriented 스타일의 프로그래밍을 허용한다. interface제공. • object-oriented 스타일의 프로그래밍? – 상속은 제공하지 않으며 interface만 제공. – implements"라는 선언 필요없음. – 단순히 해당 인터페이스의 메소드를 구현하기만 하면 인터페이스 사용가능
  • 7.
    준비사항 • 홈페이지 :http://golang.org/ • Go SDK 다운로드 : http://golang.org/dl/ • 편집도구 : LiteIDE – 다운로드 : http://sourceforge.net/projects/liteide/files/
  • 8.
    함수형태 • 리턴값 없음 funcf1() { } • 리턴값 존재 func f1() string { return “OK” } • 리턴값 이름 func f2() (r string) { r = “OK” return } • struct 내부함수 type Counter struct { n int } func (ctr *Counter) add(int amount) { //리시버 Counter 선언. ctr.n += amount; } func f1() (string, int) { return “OK”, 1 } 리시버는 나중에 자세하게 나오니까 염려하지마요
  • 9.
    Go 가시성 • 최상위type, 함수, method, 상수, 변수 혹은 구조체의 필드나 method의 이름이 대문자로 시작하면 패키지의 사용자는 접근이 가능 • 반면에 소문자일 경우에는 패키지 내부에서만 접근이 가능
  • 10.
    진입점 • main 패키지의main() 함수 package main func main() { //무언가를 수행한다. }
  • 11.
    실행과 빌드 • 실행 $go run 파일명 • 빌드 $ go build 또는 $ go build 파일명 • 빌드결과삭제 $ go clean
  • 12.
    숫자타입 • 정수형 – uint8,uint16, uint32, uint64 – int8, int16, int32, int64 – uint, int, uintptr : 장비의존적 – uint8 == byte – uint32 == rune • 부동소숫점 – float32, float64 • 복소수 – complex64, complex128
  • 13.
    그외 모든 타입 bool 불린 true, false numeric  이전 장에서 설명한 숫자타입 string  문자열. Java와 달리 소문자이다. array  [] slice  [] array를 참조한다. ex) arr[2:5] struct  ex) type struct Human { } pointer  * function  func interface  interface map  map channel  chan
  • 14.
    키워드 break default funcinterface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var
  • 15.
    Java와 다른점 몇가지 •세미콜론은 생략한다. 하지만 같은줄에 여러명령에는 사용. a= 1 b= 2 c=1; d=2 • 변수선언에는 var를 사용하고 타입은 맨뒤에 선언 var a int cf) int a var b string cf) String b • main함수는 파라미터가 없다. 대신 flag사용 var port = flag.String("port", "8080", "Listening port") func main() { flag.Parse() } • if, for 에 괄호를 사용하지 않으며, 여는 중괄호({)는 반드시 같은줄에 사용한다. if x > 1 { .. } for i:=0; i< 100; i++ { //구현 } • 선언과 할당을 동시에 할때는 =가 아닌 := 를 사용한다. 이때 변수타입은 값의 타입으로 추정한다. price := 2000  var price int = 2000 과 동일.
  • 16.
    Java와 다른점 몇가지2 • switch-case 는 break가 필요없다. 기본이 break이며, 필요시 fallthrough 사용. • try-catch-finally, throw가 없다. defer  finally raise  throw recover  catch • 포인터존재 var a *Human a := new(Human) b := &i • 사용하지 않는 변수나 import는 컴파일시 에러발생. 제거하거나 주석처리해야한다. • import 에 alias 를 줄 수 있다. import “math” import math2 “my/custom/math” math2.Average(1,2) • private , public 이 없다. 대신 함수명이나 변수명이 대문자이면, public이고 소문자이면 private이다. var Name string  public var price int  private func CallMePublic()  public func callMeInside()  private
  • 17.
    Java와 다른점 몇가지3 • class라는 keyword가 없다. 대신 struct를 만들고 함수에 struct명을 주어(리시버) class와 동일하게 사용할 수 있다. • Thread가 없다. 대신 light-weight인 고루틴을 사용한다. go something()  비동기적 something()함수실행 • 여러값을 동시에 리턴가능하다. return a,b,c • 여러값을 동시에 할당가능하다. var a,b,c int = 1,2,3 • 여러변수를 동시에 선언가능하다. import도 동일. • 자동형변환이 안된다. 명시적 형변환필요. var c int = 2000 var d float64 = float64(c)  OK. 만약 var d float64 = c 은 에러. var d float64= float32(c))도 에러. • 배열선업시 괄호는 앞에 쓴다.  []int var ( a int b string ) import ( “fmt” “io” )
  • 18.
    변수선언/할당 • 사용법1 var xstring = "Hello World" • 사용법2 var x string x = "Hello World" • 사용법3 x := "Hello World" > 일반적으로 많이 사용하는 방식. 타입추정.
  • 19.
    변수선언/할당2 • 같은줄에 컴마구분으로여러변수를 동일 타입으로 정의가능 a int, b int , c int , d int  a, b, c, d int a int, b int, c float64, d float64  a, b int, c, d float64 func f(a, b int, c, d float64) { // do something.. } func main() { f(1, 2, 3., 4.) }
  • 20.
    다중값 할당 func f()(int, int) { return 5, 6 } func main() { x, y := f() } • 오류와 함께 반환하는 기능은 자주 사용됨. f, err := os.Open(name, os.O_RDONLY, 0) if err != nil { return err } //파일 f 사용.
  • 21.
    다중값 할당2 • 두값의SWAP이 편하다 • OLD한 방식 a = 10 b = 30 tmp := b b = a a = tmp • Go 방식 a = 10 b= 30 a, b = b, a • 여러개도 된다. a, b, c = b, c, a
  • 22.
    다중값 할당3 • 동일한내용 다른 표현 var ( a int = 1 b int = 2 c int = 3 ) var a int, b int, c int = 1, 2, 3  syntax error: unexpected comma, expecting semicolon or newline or } var a, b, c int = 1, 2, 3 a, b, c := 1, 2, 3
  • 23.
    사용하지 않는 변수 •Go 컴파일러는 사용하지 않는 변수존재시 에러발생. func main() { i := 0 } $ go run test.go # command-line-arguments .test.go:16: i declared and not used
  • 24.
    문자열 비교 • 자바와다른점 var x string = "hello" var y string = "hello" fmt.Println(x == y) > true
  • 25.
    상수 • 변수 var xstring = "Hello World” • 상수 const x string = "Hello World"
  • 26.
    상수 • 상수의 Type을정하지 않으면, 사용될때context에 의해 정해짐. const huge = 1 << 100 fmt.Println(huge)  constant 1267650600228229401496703205376 overflows int fmt.Println(huge >> 40)  4611686018427387904 • iota(Java의 Enum효과) const ( red = iota // red == 0 blue // blue == 1 green // green == 2 )
  • 27.
    다중 정의 • 변수 var( a = 5 b = 10 ) • 상수 const ( Space = " ” Newline = "n" )
  • 28.
    다중 정의 • Import import( "os" "fmt" ) • Type type( book struct { title string price int } coffee struct { size int origin string } )
  • 29.
    루프 • For가 유일한루프. • 괄호가 없다. – 사용법1 for { } – 사용법2 for i < 10 { } – 사용법3 for i:=0 ; i < 10; i++ { }
  • 30.
    특별 루프 • Range funcmain() { x := [5]int{1,2,3,4} for i, value := range x { fmt.Println(value) } } – i : 현재위치(0부터시작) – value : 현재값. x[i] • 컴파일시 에러 : i declared and not used func main() { x := [5]int{1,2,3,4} for _, value := range x { fmt.Println(value) } } – _(언더스코어)는 컴파일러에게 이것이 필요하지 않다고 알려주는 데 사용
  • 31.
    Switch-Case • 괄호가 없다. •상수이거나 Integer이 필요가 없다. 문자열도 가능. • Break가 필요없다. 기본적으로 break이며, 통과를 원하면 fallthrough. • switch 값이 생략된 경우에는 기본값은 true가 된다. func main() { str := "3" switch str { case "1": fmt.Println("@1") case "2": fmt.Println("@2") default : fmt.Println("default") } } ---- switch { case i < 0 || i > 0: fmt.Println(”Non-Zero") fallthrough case i == 0 : fmt.Println(”Zero") }
  • 32.
    Switch-Case • 컴마로 여러조건가능. switch c { case ' ', '?', '&', '=', '#', '+', '%': … }
  • 33.
    배열 • 사용법1 var x[5]int x[0] = 1; x[1] = 2; ;x[2] = 3; x[3] = 4; x[4] = 5 • 사용법2 x := [5]int{1,2,3,4,5} • 사용법3 x := [5]int{ 1,2,3,4, 5, } – 한줄씩 사용할 경우 컴마가 반드시 값뒤에 붙어야하고, 마지막 원소도 반드시 컴마존재. • 사용법4 x := []int{1,2,3,4,5,7,8,9,10} // [1,2,3,4,5,7,8,9,10] 또는 x := []int{} // [] – 배열의 길이를 정하지 않아도 됨.
  • 34.
    슬라이스 • 배열을 참조하는일부를 가리킴.데이터공유 arr := []int{1,2,3,4,5} x := arr[0:3] // 0<=i<3  [1,2,3] x := arr[:3] // i<3  [0:3]과 동일 x := arr[3:] // i >= 3  [4,5] • 선언 var x []float64 • 생성 x := make([]float64, 5) x := make([]float64, 5, 10)
  • 35.
    슬라이스 • 배열을 참조하는일부를 가리킴.데이터공유 arr := []int{1,2,3,4,5} x := arr[0:3] // 0<=i<3  [1,2,3] x := arr[:3] // i<3  [0:3]과 동일 x := arr[3:] // i >= 3  [4,5] • 선언 var x []float64 • 생성 x := make([]float64, 5) x := make([]float64, 5, 10)
  • 36.
    슬라이스 관련함수 • append slice1:= []int{1,2,3} slice2 := append(slice1, 4, 5) // 4,5 를 추가. // slice1  [1,2,3] // slice2  [1,2,3,4,5] • copy slice1 := []int{1,2,3} slice2 := make([]int, 2) // 길이가 2인 슬라이스 copy(slice2, slice1) // slice1  [1,2,3] // slice2  [1,2]
  • 37.
    맵 • Key는 동등연산자(==)이가능한 integer, float, 복소수, string, 포인터, 그리고 인터페이스와 같이 어떤 타입이든 가능 • struct와 배열,slice는 동등연산을 할 수 없기 때문에 불가. • 사용법1 var x map[string]int x = make(map[string]int) x["a"]=1; x["b"]=2; x["c"]=3 • 사용법2 x := make(map[string]int) x["a"]=1; x["b"]=2; x["c"]=3 • 사용법3 x := map[string]int{"a":1, "b":2, "c":3}
  • 38.
    맵 • 참조 name :=x[”aaaa"] 또는 name, ok := x[”aaaa"] fmt.Println(name, ok) • 삭제 delete(x, "b") • 갯수확인 len(x)
  • 39.
    다중맵 elements := map[string]map[string]string{ "H":map[string]string{ "name":"Hydrogen", "state":"gas", }, "He": map[string]string{ "name":"Helium", "state":"gas", }, }
  • 40.
    가변함수 func add(args ...int)int { total := 0 for _, v := range args { total += v } return total } • add(1,2,3)  OK • xs := []int{1,2,3} add(xs)  Error. 가변인자는 슬라이스와 호환되지 않는다. • add(xs...)  OK. 가변인자 변수자체를 전달할때는 ...을 붙여야 인식됨. 가변인자는 add(foo...)와 같이 ...를 반드시 붙어야해요
  • 41.
    클로저 func main() { add:= func(x, y int) int { return x + y } fmt.Println(add(1,1)) } --------------------------------------------------------------------------------------------- func makeEvenGenerator() func() uint { i := uint(0) return func() (ret uint) { ret = i i += 2 return } } func main() { nextEven := makeEvenGenerator() fmt.Println(nextEven()) // 0 fmt.Println(nextEven()) // 2 fmt.Println(nextEven()) // 4 }
  • 42.
    Defer (지연된 호출) •해당함수 종료시 실행됨. • Java의 try-finally 대체 func main() { defer second() first() } ------------------------------------------------ f, _ := os.Open(filename) defer f.Close() • 장점 1. Close 호출을 Open 호출 가까이에 둬서 이해하기가 쉽다. 2. 함수에 return이 여러 개 있더라도 Close가 어떠한 return 시라도 호출 3. 지연된 함수는 런타임 패닉이 일어나더라도 실행
  • 43.
    Panic & Recover •Java의 throw-catch 를 대체 • panic : 런타임오류 강제발생 • recover : 런타임패닉 처리 • 잘못된 사용예. recover가 호출기도 전에 종료. panic("PANIC") str := recover() • 올바른 사용예. defer와 짝을 맞춤. 익명함수 사용. defer func() { str := recover() fmt.Println(str) }() panic("PANIC”) • 사용목적 – 범위를 벗어난 배열 인덱스에 접근시 에러처리 – 맵을 초기화하는 것을 잊어버릴 경우 에러처리
  • 44.
  • 45.
    포인터 • Address 연산자로포인터 사용 func one(xPtr *int) { *xPtr = 0 } func main() { x := 5 one(&x) fmt.Println(x) // x는 1 } ---------------------------- • new 로 포인터 사용 func main() { xPtr := new(int) one(xPtr) fmt.Println(*xPtr) // x는 1 }
  • 46.
    포인터 접근 type Rectanglestruct { length, width int } func main() { var r *Rectangle r = new(Rectangle) // Rectangle에 대한 포인터 반환. r.length = 5 (*r).length = 5 //r.length=5와 동일하다. fmt.Println(r.length) //  5 fmt.Println((*r).length) //  5 } • c++과 달리 -> 가 없다. 포인터, 인스턴스 모두에 대해 dot(.) 노테이션 사용
  • 47.
    Struct • Go는 클래스가없다. • 하지만 클래스와 동일한걸 만들수 있다. • Java에서 Rectangle 클래스는 area()메소드를 가지고 있음 //JAVA public class Rectangle { private float x1, y2, x2, y2; public float area() { float l = distance(x1, y1, x1, y2); float w = distance(x1, y1, x2, y1); return l * w; } private float distance(float x1, float y1, float x2, float y2) { float a = x2 - x1; float b = y2 - xy; return Math.sqrt(a*a + b*b); } }
  • 48.
    Struct //C++ class rectangle { private: floatx1, y2, x2, y2; public: float area(void); } inline float rectangle::area() { float l = distance(x1, y1, x1, y2); float w = distance(x1, y1, x2, y1); return l * w; } float distance(float x1, float y1, float x2, float y2) { float a = x2 - x1; float b = y2 - xy; return sqrt(a*a + b*b); }
  • 49.
    Struct • Receiver를 사용하여특정 type에 종속적 함수를 생성한다. //Go type Rectangle struct { x1, y1, x2, y2 float64 } func (r *Rectangle) area() float64 { l := distance(r.x1, r.y1, r.x1, r.y2) w := distance(r.x1, r.y1, r.x2, r.y1) return l * w } func distance(x1, y1, x2, y2 float64) float64 { a := x2 – x1 b := y2 – y1 return math.Sqrt(a*a + b*b) } func main() { r := Rectangle{1, 5, 3, 7} fmt.Println(r) //  {1 5 3 7} }
  • 50.
    Struct • 리시버는 포인터or 객체중 어느걸 써야할까? type Rectangle struct { length, width int } func (r Rectangle) set(i, j int) { r.length = i r.width = j } ----- • Receiver는 아래 두가지 모두 사용가능. func (r Rectangle) set(i, j int) { .. } // OK func (r *Rectangle) set(i, j int) { .. } // OK
  • 51.
    Struct 1. 객체 사용시 func(r Rectangle) set(i, j int) { r.length = i r.width = j } func main() { r := Rectangle{1, 5} r.set(10, 50) fmt.Println(r) //  [1 5] 안바뀐다. r이 set함수에 복사되어 전달됨. shallow copy. } 2. 포인터 사용시 func (r *Rectangle) set(i, j int) { ..} 결과 [10 50] 변경됨. call by reference.
  • 52.
    Struct • 생성방식 type Rectanglestruct { length, width int } var r Rectangle // Rectangle타입. 내용은 length=0, width=0 var r *Rectangle // *Rectangle타입. 내용은 nil. r := new(Rectangle) // *Rectangle타입. 내용은 length=0, width=0 r := Rectangle{width:10, length: 50} // Rectangle타입. 이름명시 r := Rectangle{10, 50} // Rectangle타입. 순서대로 할당
  • 53.
    Struct – embeddedtype type Person struct { Name string } • HAS-A 관계 type Android struct { Person person Model string } a.person.Name=5 //Person을 통해서 접근 • IS-A 관계 type Android struct { Person //Android is a person Model string } a := new(Android) a.Name=5 // Android가 Person인것 처럼 접근가능.
  • 54.
    Struct –기본 타입확장 • 기존 int 타입을 기반으로 string() 함수를 붙인다. #src/fmt/stringer_test.go 참조 type TI int func (v TI) String() string { return fmt.Sprintf("I: %d", int(v)) } • Java라면 public class IntegerType extends Integer { public String string() { .. } } $ javac IntegerType.java IntegerType.java:1: error: cannot inherit from final Integer public class IntegerType extends Integer { ^ 1 error  Integer는 Final class 이므로 확장 불가.
  • 55.
    Interface • Rectangle type Rectanglestruct { x1, y1, x2, y2 float64 } func (r *Rectangle) area() float64 { .. } • Circle type Circle struct { x, y, r float64 } func (c *Circle) area() float64 { .. } • Shape type Shape interface { area() float64 } • 사용시 func totalArea(shapes ...Shape) float64 { .. } 어떠한 선언적 종속관계도 없다!!
  • 56.
  • 57.
    고루틴(go) package main import ( "fmt" "math/rand" "time" ) funcf(n int) { amt := time.Duration(rand.Intn(250)) time.Sleep(time.Millisecond * amt) fmt.Println(n) } func main() { for i := 0; i < 1000000; i++ { go f(i) } var input string fmt.Scanln(&input) }
  • 58.
    채널(chan) • 채널(channel)은 두고루틴이 서로 통신하고 실행흐름을 동기화하는 수단을 제공 func main() { c := make(chan string) c <- "oce” msg := <-c fmt.Println(msg) } 결과 $ go run main.go fatal error: all goroutines are asleep - deadlock! • main함수는 첫번째 고루틴이다.
  • 59.
    채널(chan) func main() { c:= make(chan string) go func() { // 익명 함수의 고루틴 c <- "oce” }() msg := <-c fmt.Println(msg) } 결과 $ go run main.go oce • 비동기 채널설정 기본적으로 채널은 동기적. 버퍼를 주면 비동기적으로 동작가능. c := make(chan int, 1) //길이가 1인 버퍼설정.
  • 60.
    채널(chan) - 방향 •양방향 func f(c chan string) • 입력전용 func f(c chan<- string) • 출력전용 func f(c <-chan string)
  • 61.
    Select c1 := make(chanstring) c2 := make(chan string) select { case msg1 := <- c1: fmt.Println("Message 1", msg1) case msg2 := <- c2: fmt.Println("Message 2", msg2) case <- time.After(time.Second): fmt.Println("timeout") default: fmt.Println("nothing ready") }
  • 62.
  • 63.
    초기화 • func init()이 존재하면 main()보다도 먼저실행 • init()은 해당패키지 import시 실행 – import “abc” 가 실행시 abc.go내 init()이 실행된다. • init() 여러번 중복해서 선언될수 있으며, 코드 순서대로 순차적 실행 package main import "fmt” var i = foo() func foo() int { fmt.Println("Call foo()") return 1 } func init() { fmt.Println("Call init() 1") } func init() { fmt.Println("Call init() 2") } func main() { fmt.Println("Call main()") } func init() { fmt.Println("Call init() 3") } 실행결과 Call foo() Call init() 1 Call init() 2 Call init() 3 Call main()
  • 64.
    make()와 new()의 차이 •make() – 오로지 slice, map, channel을 만드는 용도로만 사용. – 포인터를 반환하지 않음. 즉 *T가 아닌 T타입을 리턴. var p *[]int = new([]int) // slice구조를 할당함; *p == nil; 그다지 유용하지 않음 var v []int = make([]int, 100) // slice v는 이제 100개의 int를 가지는 새로운 배열을 참조함 // new : 불필요하게 복잡함: var p *[]int = new([]int) *p = make([]int, 100, 100) // make: 자연스러움: v := make([]int, 100)
  • 65.
    Conversion var f1 float64= 6.0 f2 := 12 / f1 // 상수 12는 float64로 자동변환  2 var i int = 12 var f1 float64 = 6.0 f2 := i / f1 // 변수타입은 자동변환되지 않는다.  invalid operation: i / f1 (mismatched types int and float64) var i = 12. //12. 은 float64 형이다. var f1 float32 = 6.0 f2 := i / f1 //심지어 같은 float형이라도 float32와 float64는 자동형변환 불가.  invalid operation: i / f1 (mismatched types float64 and float32)
  • 66.
    Conversion var i =12. var f1 float32 = 6.0 f2 := i / f1  ERROR var i = 12. var f1 float32 = 6.0 f2 := float32(i) / f1  2 i := 12 fmt.Println(”Result = ” + i)  cannot convert "Result=" to type int invalid operation: "Result=" + i (mismatched types string and int) import ”strconv” i := 12 fmt.Println("Result = " + strconv.Itoa(i))  Result = 3
  • 67.
    GOROOT • GOROOT는 GoSDK의 설치위치를 나타내며, 이 위치하위에서 기본 라이브러리와 문서등을 찾아서 사용한다. • Go 기본설치위치 – *nix : /usr/local/go – Windows : C:Go • 만약 다른 곳에 설치했다면 GOROOT 환경변수를 설정해주어야함. – ex) /application/go에 설치했다면, export GOROOT=/application/go export PATH=$PATH:$GOROOT/bin – *nix계열은 .bash_profile과 같은 설정파일에 추가한다. • 기본위치에 설치되어 있다면 GOROOT를 설정할 필요없음
  • 68.
    GOPATH • Java에는 classpath,Go에는 GOPATH • import 시 GOPATH의 경로를 찾음 • *Nix계열은 콜론, Windows는 세미콜론으로 연결된 경로 리스트 GOPATH=/home/user/gocode /home/user/gocode/ src/ foo/ bar/ (go code in package bar) x.go grep2/ (go code in package main) y.go bin/ grep2 (installed command) pkg/ linux_amd64/ oce/ bar.a (installed package object) Tip: GOPATH가 여러개일때 특정 src하위에서 install 된 패키지파일은 해당 고패스하위로 들어간다.
  • 69.
    패키지 GOPATH=<User Home Directory>/go $cd go $ mkdir -p src/math2 $ cd src/math2 $ vi foo.go # 파일명과 패키지 명이 동일할 필요없음. package math2 // 패키지명은 폴더와 이름이 일치해야함. func Average(i, j int) float64 { // average가 아닌 대문자 Average로 선언. import후 호출가능함 return float64(i + j) / 2 } $ go install  <User Home Directory>/go/pkg/darwin_amd64/math2.a 생성됨. ------------------- package main import “fmt” import “math2” func main() { i := math2.Average(10,30) // 20 fmt.Println(“i=“, i) } > 패키지명을 math로 하면 이미 존재하는 go의 기본패키지명과 겹치므로 go install시 에러발생.
  • 70.
    패키지 • 최상위가 아닌중간경로를 주면 math라는 이름으로 패키지 생성가능. $ mkdir -p src/oce.org/math $ cd src/oce.org/math $ vi foo.go package math func Average(i, j int) float64 { return float64(i + j) / 2 } $ go install  <User Home Directory>/go/pkg/darwin_amd64/oce.org/math.a 생성됨. ------------------- package main import “fmt” import “oce.org/math” func main() { i := math.Average(10,30) // 20 fmt.Println(“i=“, i) }
  • 71.
    패키지 • 패키지 이름의중복을 방지하기 위하여 import시 Alias가능 package main import “fmt” import “math” import math2 “oce.org/math” func main() { i := math.Average(10,30) j := math2.Average(10,30) fmt.Println(“i=“, i, “j=“, j) }
  • 72.
    패키지 • Alias를 Dot(.)으로 하면 해당패키지를 이름없이 바로 접근가능. package main import . “fmt” func main() { Println(“Called without package name!”) }
  • 73.
  • 74.
    문서화 • Java에는 javadoc,Go에는 godoc • usage: godoc package [name ...] godoc -http=:6060 $ godoc oce.org/math PACKAGE DOCUMENTATION package math import "oce.org/math” FUNCTIONS func Average(i, j int) float64 $ godoc oce.org/math average  대소문자구분 No match found. $ godoc oce.org/math Average func Average(i, j int) float64 $godoc –http=:6060  로컬 웹서버시작  http://localhost:6060/pkg/
  • 75.
    문서화 //Copyright 2015 // oce.org에서제공하는 수학패키지. //  빈줄삽입. // 2015-3-14 업데이트. package math // 평균을 구한다. // 입력값은 i,j 정수를 받아들인다. // 리턴값은 float64이다. // // 입력값: // i 첫번째정수  Tab을 넣었다. // j 두번째정수  Tab을 넣었다. func Average(i, j int) float64 { return float64(i + j) / 2 } 주석에 Tab이 들어있으면, 회색Box안으로 표시되요
  • 76.
    문서화 $ go install;godoc oce.org/math PACKAGE DOCUMENTATION package math import "oce.org/math" oce.org에서 제공하는 수학패키지.  빈줄이 나타났다. 2015-3-14 업데이트. FUNCTIONS func Average(i, j int) float64 평균을 구한다. 입력값은 i,j 정수를 받아들인다. 리턴값은 float64이다.  주석내 개행은 무시된다. 입력값: i 첫번째정수 j 두번째정수
  • 77.
  • 78.
    문서화 http://localhost:6060/pkg/oce.org/math/$ godoc -http=:6060 가장좋은 문서화예제는 Go sdk의 소스에 있어요. <GOROOT>/src/fmt/print.go <GOROOT>/src/fmt/doc.go 를 참조하세요.
  • 79.
    문서화 • Example 도넣을수 있다. 패키지 os/exec/
  • 80.
    문서화 • 함수정의 (src/os/exec/exec.go,src/os/exec/lp_unix.go) func LookPath(file string) (string, error) { .. } func (c *Cmd) Start() error { .. } • 예제정의(src/os/exec/example_test.go) func ExampleLookPath() { path, err := exec.LookPath("fortune") if err != nil { log.Fatal("installing fortune is in your future")  이 소스가 예제항목으로 삽입된다. } fmt.Printf("fortune is available at %sn", path) } func ExampleCmd_Start() { cmd := exec.Command("sleep", "5") err := cmd.Start() if err != nil { log.Fatal(err)  이 소스가 예제항목으로 삽입된다. } log.Printf("Waiting for command to finish...") err = cmd.Wait() log.Printf("Command finished with error: %v", err) }
  • 81.
    테스트 • 테스트 함수만드는법 – 파일이름은 _test.go로 끝난다. – 함수이름이 Test로 시작 – *testing.T를 인자로 받을것 (import “testing”) • 예제 # foo_test.go package math import "testing" func TestAverage(t *testing.T) { var v float64 v = Average([]float64{1,2}) if v != 1.5 { t.Error("Expected 1.5, got ", v) } }
  • 82.
    테스트 $ go test PASS okoce.org/math 0.003s • 만약 파일명이 _test.go, test.go, foo_testing.go 등 파일명규칙을 따르지 않을때는 아래와 같이 테스트파일을 못찾는다. $ go test ? oce.org/math [no test files] • fmt 패키지내 파일 리스트 참고 (경로 <GOROOT>/src/fmt) doc.go export_test.go fmt_test.go format.go print.go scan.go scan_test.go stringer_test.go
  • 83.
    테스트 import "testing" type testpairstruct { values []float64 average float64 } var tests = []testpair{ { []float64{1,2}, 1.5 }, { []float64{1,1,1,1,1,1}, 1 }, { []float64{-1,1}, 0 }, } func TestAverage(t *testing.T) { for _, pair := range tests { v := Average(pair.values) if v != pair.average { t.Error( "For", pair.values, "expected", pair.average, "got", v, ) } } } struct를 이용하면 여러 테스트값을 편하게 테스트해볼 수 있어요.
  • 84.
  • 85.
    strings strings.Contains("test", "es") //true strings.Count("test", "t") // 2 strings.HasPrefix("test", "te") // true strings.HasSuffix("test", "st") // true strings.Index("test", "e") // 1 strings.Join([]string{"a","b"}, "-") // "a-b" strings.Repeat("a", 5) // "aaaaa" strings.Replace("aaaa", "a", "b", 2) // "bbaa" strings.Split("a-b-c-d-e", "-") // []string{"a","b","c","d","e"} strings.ToLower("TEST") // "test" strings.ToUpper("test") // "TEST” ---- arr := []byte("test") str := string([]byte{'t','e','s','t'}) 더 많은 정보는 여기있어요 https://golang.org/pkg/strings
  • 86.
    io • 함수정의 보다는대부분이 interface로 구성 • 주요 인터페이스는 Reader와 Writer • Reader는 Read(), Writer는 Write() 지원 • Reader  Writer 데이터 복사 – func Copy(dst Writer, src Reader) (written int64, err error) 더 많은 정보는 여기있어요 https://golang.org/pkg/io
  • 87.
    bytes package main import ( "bytes" "fmt" "os" ) funcmain() { var b bytes.Buffer // 버퍼는 초기화가 필요없다. 선언후 그대로 사용. b.Write([]byte("Hello ")) fmt.Fprintf(&b, "world!") b.WriteTo(os.Stdout) } ----- func main() { // string 이나 []byte 를 io.Reader 로 바꿔준다. buf := bytes.NewBufferString("R29waGVycyBydWxlIQ==") dec := base64.NewDecoder(base64.StdEncoding, buf) io.Copy(os.Stdout, dec) } 더 많은 정보는 여기있어요 https://golang.org/pkg/bytes
  • 88.
    os • 파일열기 :os.Open() • 파일닫기 : file.Close() package main import ( "fmt" "os" ) func main() { file, err := os.Open("test.txt") if err != nil { // 오류를 처리 return } defer file.Close() // 파일의 크기를 구함 stat, err := file.Stat() if err != nil { return } // 파일을 읽음 bs := make([]byte, stat.Size()) _, err = file.Read(bs) if err != nil { return } str := string(bs) fmt.Println(str) }
  • 89.
    error • 오류에 대한내장타입 • error.New() 사용시 자체오류 생성가능 package main import ( "errors" "fmt" ) func main() { err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) } } ----- func main() { const name, id = "bimmler", 17 err := fmt.Errorf("user %q (id %d) not found", name, id) if err != nil { fmt.Print(err) } }
  • 90.
    container heap Package heapprovides heap operations for any type that implements heap.Interface. list Package list implements a doubly linked list. ring Package ring implements operations on circular lists. package main import ( "container/list" "fmt" ) func main() { // Create a new list and put some numbers in it. l := list.New() e4 := l.PushBack(4) e1 := l.PushFront(1) l.InsertBefore(3, e4) l.InsertAfter(2, e1) // Iterate through list and print its contents. for e := l.Front(); e != nil; e = e.Next() { fmt.Println(e.Value) } }
  • 91.
    container - heap //This example demonstrates an integer heap built using the heap interface. package main import ( "container/heap" "fmt" ) // An IntHeap is a min-heap of ints. type IntHeap []int func (h IntHeap) Len() int { return len(h) } func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h *IntHeap) Push(x interface{}) { // Push and Pop use pointer receivers because they modify the slice's length, // not just its contents. *h = append(*h, x.(int)) } func (h *IntHeap) Pop() interface{} { old := *h n := len(old) x := old[n-1] *h = old[0 : n-1] return x } // This example inserts several ints into an IntHeap, checks the minimum, // and removes them in order of priority. func main() { h := &IntHeap{2, 1, 5} heap.Init(h) heap.Push(h, 3) fmt.Printf("minimum: %dn", (*h)[0]) for h.Len() > 0 { fmt.Printf("%d ", heap.Pop(h)) } }
  • 92.
    sort • Sort 함수는sort.Interface가 구현된 것만 정렬가능 • sort.Interface는 Len, Less, Swap의 3개 메서드 필요. type Interface interface { // Len is the number of elements in the collection. Len() int // Less reports whether the element with // index i should sort before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) }
  • 93.
    sort type Person struct{ Name string Age int } type ByName []Person func (this ByName) Len() int { return len(this) } func (this ByName) Less(i, j int) bool { return this[i].Name < this[j].Name } func (this ByName) Swap(i, j int) { this[i], this[j] = this[j], this[i] } func main() { kids := []Person{ {"Jill",9}, {"Jack",10}, } sort.Sort(ByName(kids)) fmt.Println(kids) }
  • 94.
    hash type Hash interface{ // Write (via the embedded io.Writer interface) adds more data to the running hash. // It never returns an error. io.Writer // Sum appends the current hash to b and returns the resulting slice. // It does not change the underlying hash state. Sum(b []byte) []byte // Reset resets the Hash to its initial state. Reset() // Size returns the number of bytes Sum will return. Size() int // BlockSize returns the hash's underlying block size. // The Write method must be able to accept any amount // of data, but it may operate more efficiently if all writes // are a multiple of the block size. BlockSize() int } type Hash32 interface { Hash Sum32() uint32 } type Hash64 interface { Hash Sum64() uint64 }
  • 95.
    hash - 비암호화 •종류 : adler32, crc32, crc64, fnv package main import ( "fmt" "hash/crc32" ) func main() { h := crc32.NewIEEE() h.Write([]byte("test")) v := h.Sum32() fmt.Println(v) }  3632233996
  • 96.
    crypto – 암호화해시 •종류 : aes, cipher, des, dsa, ecdsa, elliptic, hmac, md5, rand, rc4, rsa, sha1, sha256, sha512, subtle, tls, 509 package main import ( "crypto/sha1" "fmt" "io" ) func main() { h := sha1.New() io.WriteString(h, "His money is twice tainted:") io.WriteString(h, " 'taint yours and 'taint mine.") fmt.Printf("% x", h.Sum(nil)) }
  • 97.
    net • Go는 아래에대해 Portable 인터페이스를 제공 – Network I/O – TCP/IP – UDP – Domain name resolution – Unix domain socket type Listener interface { // Accept waits for and returns the next connection to the listener. Accept() (c Conn, err error) // Close closes the listener. // Any blocked Accept operations will be unblocked and return errors. Close() error // Addr returns the listener's network address. Addr() Addr }
  • 98.
    net //////////////////// Client 간단버전////////////////// conn, err := net.Dial("tcp", "google.com:80") if err != nil { // handle error } fmt.Fprintf(conn, "GET / HTTP/1.0rnrn") status, err := bufio.NewReader(conn).ReadString('n') // ... ////////////////// Server 간단버전 ////////////////// ln, err := net.Listen("tcp", ":8080") if err != nil { // handle error } for { conn, err := ln.Accept() if err != nil { // handle error } go handleConnection(conn) }
  • 99.
    net ////////////// Echo 서버//////////// package main import ( "io”; "log”; "net" ) func main() { // Listen on TCP port 2000 on all interfaces. l, err := net.Listen("tcp", ":2000") if err != nil { log.Fatal(err) } defer l.Close() for { // Wait for a connection. conn, err := l.Accept() if err != nil { log.Fatal(err) } // Handle the connection in a new goroutine. // The loop then returns to accepting, s o that // multiple connections may be served concurrently. go func(c net.Conn) { // Echo all incoming data. io.Copy(c, c) // Shut down the connection. c.Close() }(conn) } } $ telnet localhost 2000 I love Go  입력 I love Go  출력
  • 100.
    net/http package main import ("net/http"; "io") func hello(res http.ResponseWriter, req *http.Request) { res.Header().Set( "Content-Type", "text/html", ) io.WriteString( res, `<doctype html> <html> <head><title>Hello World</title></head> <body>Hello World!</body> </html>`, ) } func main() { http.HandleFunc("/hello", hello) http.ListenAndServe(":9000", nil) } http://localhost:9000/hello HandleFunc는 URL 라우팅(/hello) 처리를 담당해요
  • 101.
    net/rpc package main import ( "fmt”; "net”; "net/rpc" ) type Server struct {} func (this *Server) Negate(i int64, reply *int64) error { *reply = -i return nil } func server() { rpc.Register(new(Server)) ln, err := net.Listen("tcp", ":9999") if err != nil { fmt.Println(err) return } for { c, err := ln.Accept() if err != nil { continue } go rpc.ServeConn(c) } } func client() { c, err := rpc.Dial("tcp", "127.0.0.1:9999") if err != nil { fmt.Println(err) return } var result int64 err = c.Call("Server.Negate", int64(999), &result) if err != nil { fmt.Println(err) } else { fmt.Println("Server.Negate(999) =", result) } } func main() { go server() go client() var input string fmt.Scanln(&input) }
  • 102.
    flag (명령줄 파싱) •명령줄 플래그(-로 시작)를 flag.String(), Bool(), Int() 등을 사용하여 파싱 – flag.Int(플래그명, 기본값, 설명메시지) – flag.IntVar(입력변수주소, 플래그명, 기본값, 설명메시지) – 방법1 import "flag" var ip = flag.Int("flagname", 1234, "help message for flagname”) – 방법2 var flagvar int func init() { flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") } • 플래그가 아닌 일반파라미터는 flag.Args()를 통해 받을 수 있으며 []string 형으로 리턴된다. var args []string = flag.Args()
  • 103.
    flag (명령줄 파싱) packagemain import ( "flag" "fmt" ) var species = flag.String("species", "gopher", "the species we are studying") var gopherType string func init() { const ( defaultGopher = "pocket" usage = "the variety of gopher" ) flag.StringVar(&gopherType, "gopher_type", defaultGopher, usage) flag.StringVar(&gopherType, "g", defaultGopher, usage+" (shorthand)") } func main() { flag.Parse() fmt.Println("species=", *species, "ngopherType=", gopherType) var args []string = flag.Args() fmt.Println("Args=", args) }
  • 104.
    flag (명령줄 파싱) $./hello species= gopher gopherType= pocket Args= [] $ ./hello -h Usage of ./hello: -g="pocket": the variety of gopher (shorthand) -gopher_type="pocket": the variety of gopher -species="gopher": the species we are studying $ ./hello -species=oce -g=fastcat species= oce gopherType= fastcat Args= [] $ ./hello -species=oce -gopher_type=fastcat  위 결과와 동일 species= oce gopherType= fastcat Args= [] $ ./hello -species=”oce fastcat” world war 2 species= oce fastcat gopherType= pocket Args= [world war 2]
  • 105.
    sync type Cond func NewCond(lLocker) *Cond func (c *Cond) Broadcast() func (c *Cond) Signal() func (c *Cond) Wait() type Locker type Mutex func (m *Mutex) Lock() func (m *Mutex) Unlock() type Once func (o *Once) Do(f func()) type Pool func (p *Pool) Get() interface{} func (p *Pool) Put(x interface{}) type RWMutex func (rw *RWMutex) Lock() func (rw *RWMutex) RLock() func (rw *RWMutex) RLocker() Locker func (rw *RWMutex) RUnlock() func (rw *RWMutex) Unlock() type WaitGroup func (wg *WaitGroup) Add(delta int) func (wg *WaitGroup) Done() func (wg *WaitGroup) Wait() 더 많은 정보는 여기있어요 https://golang.org/pkg/sync/
  • 106.
    sync/Mutex package main import ( "fmt”;"sync”; "time" ) func main() { m := new(sync.Mutex) for i := 0; i < 10; i++ { go func(i int) { m.Lock() fmt.Println(i, "start") time.Sleep(time.Second) fmt.Println(i, "end") m.Unlock() }(i) } var input string fmt.Scanln(&input) } 실행결과 0 start 0 end 1 start 1 end 2 start 2 end 3 start 3 end 4 start 4 end 5 start 5 end 6 start 6 end 7 start 7 end 8 start 8 end 9 start 9 end
  • 107.
    sync/Once package main import ( "fmt”;"sync" ) func main() { var once sync.Once onceBody := func() { fmt.Println("Only once") } done := make(chan bool) for i := 0; i < 10; i++ { go func() { once.Do(onceBody) done <- true }() } for i := 0; i < 10; i++ { <-done } }  결과 : Only once
  • 108.
    sync/WaitGroup var wg sync.WaitGroup varurls = []string{ "http://www.golang.org/", "http://www.google.com/", "http://www.somestupidname.com/", } for _, url := range urls { // Increment the WaitGroup counter. wg.Add(1) // Launch a goroutine to fetch the URL. go func(url string) { // Decrement the counter when the goroutine completes. defer wg.Done() // Fetch the URL. http.Get(url) }(url) } // Wait for all HTTP fetches to complete. wg.Wait()
  • 109.
    Go 의 동기화스타일 type Work struct { x, y, z int assigned, done bool } type WorkSet struct { mu sync.Mutex work []*Work } type Work struct { x, y, z int } func worker(in <-chan *Work, out chan<- *Work) { for w := range in { w.z = w.x * w.y // do some work... out <- w } } func main() { in, out := make(chan *Work), make(chan *Work) for i := 0; i < 10; i++ { go worker(in, out) } go sendLotsOfWork(in) receiveLotsOfResults(out) } 메모리공유를 통한 통신이나 공유데이터를 보호하기 위한 Lock이 필요없다. 전통적인 접근 Go 스타일
  • 110.
    time 정의 : funcSleep(d Duration) 샘플 : time.Sleep(100 * time.Millisecond) 정의 : func (t Time) Sub(u Time) Duration 샘플 : t0 := time.Now() expensiveCall() t1 := time.Now() mt.Printf("The call took %v to run.n", t1.Sub(t0))  3m56s 정의 : func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time 샘플 : t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) 정의 : func (t Time) Format(layout string) string 샘플 : const layout = "Jan 2, 2006 at 3:04pm (MST)" t := time.Date(2009, time.November, 10, 15, 0, 0, 0, time.Local) fmt.Println(t.Format(layout)) 정의 : func Parse(layout, value string) (Time, error) 샘플 : const longForm = "Jan 2, 2006 at 3:04pm (MST)" t, _ := time.Parse(longForm, "Feb 3, 2013 at 7:54pm (PST)") Go 에는 Java의 yyyyMMdd 같은 형식이 없어요.
  • 111.
    regexp 정의 : funcMatchString(pattern string, s string) (matched bool, err error) 샘플 : matched, err := regexp.MatchString("foo.*", "seafood") 정의 : func MustCompile(str string) *Regexp 샘플 : var re = regexp.MustCompile(`^[a-z]+[[0-9]+]$`) 정의 : func (re *Regexp) MatchString(s string) bool 샘플 : fmt.Println(re.MatchString("adam[23]")) 정의 : func (re *Regexp) FindAllString(s string, n int) []string 샘플 : fmt.Println(re.FindAllString("paranormal", -1)) 정의 : func (re *Regexp) ReplaceAllString(src, repl string) string 샘플 : fmt.Println(re.ReplaceAllString("-ab-axxb-", "T"))
  • 112.
    database age := 27 rows,err := db.Query("SELECT name FROM users WHERE age=?", age) if err != nil { log.Fatal(err) } defer rows.Close() for rows.Next() { var name string if err := rows.Scan(&name); err != nil { log.Fatal(err) } fmt.Printf("%s is %dn", name, age) } if err := rows.Err(); err != nil { log.Fatal(err) } https://github.com/golang/go/wiki/SQLDrivers
  • 113.
    database - drivers •MySQL: https://github.com/ziutek/mymysql [*] • MySQL: https://github.com/go-sql-driver/mysql/ [*] • Postgres (pure Go): https://github.com/lib/pq [*] • Oracle: https://github.com/mattn/go-oci8 • MS SQL Server (pure go): https://github.com/denisenkom/go-mssqldb 그 외의 드라이버는 여기를 참조하세요. https://github.com/golang/go/wiki/SQLDrivers
  • 114.
    database - mysql packagemain import ( "os" "github.com/ziutek/mymysql/mysql" _ "github.com/ziutek/mymysql/native" // Native engine // _ "github.com/ziutek/mymysql/thrsafe" // Thread safe engine ) func main() { db := mysql.New("tcp", "", "127.0.0.1:3306", user, pass, dbname) err := db.Connect() if err != nil { panic(err) } rows, res, err := db.Query("select * from X where id > %d", 20) if err != nil { panic(err) } https://github.com/ziutek/mymysql for _, row := range rows { for _, col := range row { if col == nil { // col has NULL value } else { // Do something with text in col (type []byte) } } // You can get specific value from a row val1 := row[1].([]byte) // You can use it directly if conversion isn't needed os.Stdout.Write(val1) // You can get converted value number := row.Int(0) // Zero value str := row.Str(1) // First value bignum := row.MustUint(2) // Second value // You may get values by column name first := res.Map("FirstColumn") second := res.Map("SecondColumn") val1, val2 := row.Int(first), row.Str(second) } }
  • 115.
    log package main import ( "bytes" "fmt" "log" ) funcmain() { var buf bytes.Buffer logger := log.New(&buf, "logger: ", log.Lshortfile) logger.Print("Hello, log file!") fmt.Print(&buf) }  logger: main.go:12: Hello, log file!
  • 116.
    log - level •로그레벨에 따라 로깅하는 패키지는 기본 SDK에 없다. • https://github.com/jcelliott/lumber • 콘솔로거 log := lumber.NewConsoleLogger(lumber.WARN) • 파일로거 log := lumber.NewFileLogger("filename.log", lumber.INFO, lumber.ROTATE, 5000, 9, 100) • 파일로거 (rotate) log := lumber.NewRotateLogger("filename.log", 5000, 9) • 사용법 log.Trace("the %s log level", "lowest") log.Debug("") log.Info("the default log level") log.Warn("") log.Error("") log.Fatal("the %s log level", "highest") • 로그앞에 prefix 붙이기 log.Prefix("MYAPP")
  • 117.