let's go (golang)

Post on 15-Jul-2015

4.942 Views

Category:

Technology

455 Downloads

Preview:

Click to see full reader

TRANSCRIPT

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 mainfunc 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= 1b= 2c=1; d=2

• 변수선언에는 var를 사용하고 타입은 맨뒤에 선언var a int cf) int avar 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 finallyraise throwrecover catch

• 포인터존재var a *Humana := 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 publicvar price int privatefunc CallMePublic() publicfunc 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 intb string)

import (“fmt”“io”)

변수선언/할당

• 사용법1var x string = "Hello World"

• 사용법2var x stringx = "Hello World"

• 사용법3x := "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"

)

• Typetype(

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++ { }

특별 루프

• Rangefunc 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 usedfunc 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]intx[0] = 1; x[1] = 2; ;x[2] = 3; x[3] = 4; x[4] = 5

• 사용법2x := [5]int{1,2,3,4,5}

• 사용법3x := [5]int{

1,2,3,4,5,

}– 한줄씩 사용할 경우 컴마가 반드시 값뒤에 붙어야하고, 마지막 원소도

반드시 컴마존재.

• 사용법4x := []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)

슬라이스 관련함수

• appendslice1 := []int{1,2,3}

slice2 := append(slice1, 4, 5) // 4,5 를 추가.

// slice1 [1,2,3]

// slice2 [1,2,3,4,5]

• copyslice1 := []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는 동등연산을 할 수 없기 때문에 불가.

• 사용법1var x map[string]int

x = make(map[string]int)

x["a"]=1; x["b"]=2; x["c"]=3

• 사용법2x := make(map[string]int)

x["a"]=1; x["b"]=2; x["c"]=3

• 사용법3x := 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 = ii += 2return

}}func main() {

nextEven := makeEvenGenerator()fmt.Println(nextEven()) // 0fmt.Println(nextEven()) // 2fmt.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

Struct1. 객체 사용시

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 typetype 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 stringfmt.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)

Selectc1 := 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() 1Call init() 2Call init() 3Call 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)

Conversionvar 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)

Conversionvar 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.gogrep2/ (go code in package main)

y.gobin/

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 mainimport “fmt”import “math2”func main() {

i := math2.Average(10,30) // 20fmt.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/mathPACKAGE DOCUMENTATION

package mathimport "oce.org/math”

FUNCTIONS

func Average(i, j int) float64

$ godoc oce.org/math average 대소문자구분No match found.

$ godoc oce.org/math Averagefunc 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 %s\n", 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.gopackage math

import "testing"

func TestAverage(t *testing.T) {var v float64v = 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 []float64average 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를 이용하면 여러

테스트값을편하게

테스트해볼수 있어요.

핵심패키지

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

bytespackage 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", 17err := fmt.Errorf("user %q (id %d) not found", name, id)if err != nil {

fmt.Print(err)}

}

containerheap 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 := *hn := 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: %d\n", (*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)

}

hashtype 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 {HashSum32() uint32

}

type Hash64 interface {HashSum64() 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.0\r\n\r\n")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, so 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 2000I love Go 입력I love Go 출력

net/httppackage 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/rpcpackage 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(입력변수주소, 플래그명, 기본값, 설명메시지)

– 방법1import "flag"

var ip = flag.Int("flagname", 1234, "help message for flagname”)

– 방법2var 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]

synctype 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/Mutexpackage 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 start0 end1 start1 end2 start2 end3 start3 end4 start4 end5 start5 end6 start6 end7 start7 end8 start8 end9 start9 end

sync/Oncepackage 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/WaitGroupvar 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"))

databaseage := 27rows, err := db.Query("SELECT name FROM users WHERE age=?", age)if err != nil {

log.Fatal(err)}defer rows.Close()for rows.Next() {

var name stringif err := rows.Scan(&name); err != nil {

log.Fatal(err)}fmt.Printf("%s is %d\n", 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 - mysqlpackage 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 rowval1 := row[1].([]byte)

// You can use it directly if conversion isn't needed

os.Stdout.Write(val1)

// You can get converted valuenumber := row.Int(0) // Zero valuestr := row.Str(1) // First valuebignum := row.MustUint(2) // Second value

// You may get values by column namefirst := res.Map("FirstColumn")second := res.Map("SecondColumn")val1, val2 := row.Int(first), row.Str(second)

}}

logpackage 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")

수고하셨어요~♥

top related