- 대략적인 정리(NomadCoder - Golang 입문)
https://kjh950601.tistory.com/317?category=902684
https://kjh950601.tistory.com/318?category=902684
https://kjh950601.tistory.com/319?category=902684
https://kjh950601.tistory.com/320?category=902684
https://kjh950601.tistory.com/321?category=902684
https://kjh950601.tistory.com/322?category=902684
https://kjh950601.tistory.com/323?category=902684
Maps & Structs
map의 key값과 value값을 string으로 지정을 해보자. package main import "fmt" func main() { jay := map[string]string{"name": "jay", "age":"27"} fmt.Println(jay) } // map[age:27 name:jay] value만 원한..
kjh950601.tistory.com
- 구조체(메서드, 임베딩, 캡슐화)
- 인터페이스와 타입 단언
- Go : 에러와 패닉
- 고루틴과 채널
- 변수 스코프와 블록
구조체
Golang에는 class 개념 대신에 struct개념이 있다.
struct의 public & private
public과 private은 Golang에서 대문자 소문자로 나눠지는데
대문자로 시작한다 그러면 외부에서 가져다 사용할 수 있고,
소문자로 시작한다 그러면 내부에서만 사용이 가능하다.
stack 과 관련된 struct
스택에 구조체를 선언하는 것이다. 스택에 할당되어 {hwan 10}으로 출력이 된다.
package main
import "fmt"
type A struct {
name string
num int
}
func main() {
a := A{}
a.name = "hwan"
a.num = 10
fmt.Println(a)
}
// {hwan 10}
선언과 동시에 초기화도 가능하다.
package main
import "fmt"
type A struct {
name string
num int
}
func main() {
a := A{"hwan", 10}
fmt.Println(a)
}
// {hwan 10}
new 키워드를 통해서 struct 선언(Heap allocation)
new 키워드를 사용해서 구조체를 포인터로 받는다? 그러면 선언과 동시에 초기화가 불가능함.
Golang의 구조체는 일반적인 OOP언어에서 볼 수 있는 생성은 따로 없다.
아래 코드를 보자. {hwan 10}이 출력되는 것을 볼 수 있다.
힙에 할당된 자료가 참조되고 있다는 뜻으로 &이 출력이 된다.
( 그리고 생성자는 사용자가 직접 만들어줘야 한다.)
package main
import "fmt"
type A struct {
name string
num int
}
func main() {
a := new(A)
a.name = "hwan"
a.num = 10
fmt.Println(a)
}
// &{hwan 10}
struct 생성자
Golang은 주소값 전달받으면 주소값의 자료들은 모두 stack에서 heap으로 이동
package main
import "fmt"
type A struct {
name string
num int
}
func newA() *A {
a := A{}
a.name = "hwan"
a.num = 10
return &a
}
func main() {
a := newA()
fmt.Println(a)
}
// &{hwan 10}
함수와 같이 사용
go에서 struct에 멤버함수를 정의하는 기능이 없다.
밖에서 함수 정의한 다음에 struct를 참조하는 식으로 함수를 생성해야 한다.
아래 코드를 보자. print() 함수를 보면 ( a * A)라고 struct구조체를 참조하는 형태를 가진다.
package main
import "fmt"
type A struct {
name string
num int
}
func newA() *A {
a := A{}
a.name = "hwan"
a.num = 10
return &a
}
func (a *A) print() {
fmt.Println(a)
}
func main() {
a := newA()
a.print()
}
//output : &{hwan 10}
Golang의 interface
Interface의 선언도 Struct처럼 Type 선언이 가능.
interface를 제공함으로써 추상화를 돕고, 객체들을 서로 연결시켜주는 역할 제공
선언방법은 아래와 같다.
type Calc interface {...}
interface를 정의하는 방법을 나타내는 아래 코드를 보면
리시버 받는 구간은 plus() 함수를 정의한 부분이고 r Parameter 부분을 볼 수가 있다.
해당 struct는 인터페이스와 연결된 상태가 되면서,
showCalc() 부분을 보면 인자값을 Calculator로 받는걸 볼 수 있다.
package main
import "fmt"
type Calculator interface {
plus() int
minus() int
}
type Parameter struct {
n1, n2 int
}
func (r Parameter) plus() int {
return r.n1 + r.n2
}
func (r Parameter) minus() int {
return r.n1 - r.n2
}
func showCalc(calc Calculator) {
fmt.Println(calc.plus())
fmt.Println(calc.minus())
}
func main() {
r := Parameter{30, 20}
showCalc(r)
}
// 50
// 10
리시버는 포인터로도 받는것이 가능하다.
package main
import "fmt"
type Calculator interface {
plus() int
minus() int
}
type Parameter struct {
n1, n2 int
}
func (r *Parameter) plus() int {
return r.n1 + r.n2
}
func (r *Parameter) minus() int {
return r.n1 - r.n2
}
func showCalc(calc Calculator) {
fmt.Println(calc.plus())
fmt.Println(calc.minus())
}
func main() {
r := &Parameter{30, 20}
showCalc(r)
}
C++이나 C의 void*같은 역할을 하는 타입이 있는데
인터페이스에는 타입이 있다. 변수에도 선언이 가능하다.
package main
import (
"fmt"
"reflect"
)
func typeCheck(x interface{}) {
fmt.Println(reflect.TypeOf(x))
}
func main() {
var x interface{}
x = 1
fmt.Println(x)
typeCheck(x)
x = "Tom"
fmt.Println(x)
typeCheck(x)
}
// 1
// int
// Tom
// string
변수 선언한 후에 값을 입력하면, 해당 값에 대한 타입이 정의되어 아래와 같이 출력
package main
import "fmt"
func main() {
var x interface{} = 1
i := x
j := x.(int)
z := x.(string)
fmt.Println(i)
println(i)
println(j)
}
// 1
// (0x1c44a0,0x1f5e58)
// 1
// panic: interface conversion: interface {} is int, not string
z 변수의 경우 x는 string에 대한 값이 아니라서 error발생. error값을 boolean값으로 return 받게 해보자.
flag에는 bool자료형의 false가 저장된다.
package main
func main() {
var x interface{} = 1
z, flag := x.(string)
println(z, flag)
}
// false
Type Switch문
switch문을 이용해서 type별로 case를 만들어 처리가능하다.
package main
import (
"fmt"
"reflect"
)
func typeSwitch(n interface{}) {
switch n.(type) {
case string:
fmt.Println("string")
case int:
fmt.Println("int")
default:
fmt.Println(reflect.TypeOf(n))
}
}
func main() {
var x interface{} = 1
i := x
j := x.(int)
z, flag := x.(string)
typeSwitch(i)
typeSwitch(j)
typeSwitch(z)
typeSwitch(flag)
}
// int
// int
// string
// bool
Embedding Interfaces
아래코드 처럼 Mixed라는 interface로 묶어서 실행을 해보자.
package main
import "fmt"
type ABC interface {
abc()
}
type ZXC interface {
zxc()
}
type Mixed interface {
ABC
ZXC
}
type Edge struct {
x int
}
func (e Edge) abc() {
fmt.Println(e.x + 100)
}
func (e Edge) zxc() {
fmt.Println(e.x - 100)
}
func main() {
edge := Edge{10}
var mixed Mixed = edge
mixed.abc()
mixed.zxc()
}
// 110
// -90
Type assertion 이해
Type-Switch 즉 interface type이 가지고 value의 Type을 확인하는 것이다.
interface type의 x와 타입 T를 x.(T)로 표현했을때, x가 nil이 아니며, x는 T 타입에 속한다는 것을 확인하는 것을 Type assertion이라고 한다.
아래 코드를 보자. value a를 담은 i를 출력했을 때 값의 주소가 출력이되고 j를 출력했을 때는 값이 출력되는 것을 볼 수 있다.
package main
func main() {
var a interface{} = 1
i := a
j := a.(int)
println(i)
println(j)
}
// (0xf7f120,0xf9c618)
// 1
위와 같은 방법에서 어떤 인터페이스의 값이 특정 타입인지를 확인하다가 에러를 방지하기 위해서
ok값을 받을 수 있다. assert한 타입이 맞으면 ok는 true, 아니면 false가 될 것이고 ok일때는, v에 concrete value가 할당된다.
v, ok := n.(int)
아래코드에서 i는 string type으로 정해놨기 때문에 float64가 아니므로 마지막에 ERROR가 발생
package main
import "fmt"
func main() {
var i interface{} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s, ok)
f, ok := i.(float64)
fmt.Println(f, ok)
f = i.(float64) // panic
fmt.Println(f)
}
// hello
// hello true
// 0 false
// panic: interface conversion: interface {} is string, not float64
에러와 패닉
golang에서 사용자가 error를 정의할려면
error 변수를 생성 => error변수 생성할려면 error 패키지 생성해야한다.
순서를 생각하고 아래 코드를 보자.
숫자가 다를때는 End normally가 출력이 되고 숫자가 같을 때는 error 변수를 생성하면서
입력한 문구가 출력된다.
package main
import (
"errors"
"fmt"
)
func returnError(a, b int) error{
if a == b {
err := errors.New("Error in returnError() functions")
return err
} else {
return nil
}
}
func main() {
err := returnError(1, 2)
if err == nil{
fmt.Println("End normally")
} else {
fmt.Println(err)
}
err = returnError(10, 10)
if err == nil {
fmt.Println("End normally")
} else {
fmt.Println(err)
}
}
// End normally
// Error in returnError() functions
errors.New
errors 패키지의 New 매서드는 파라미터로 문자열을 받아서 대응되는 에러 오브젝트를 만들어 반환한다.
해당 에러 오브젝트에 대해 Error() 메서드를 호출하면 생성할 때 넘긴 문자열이 반환됨.
package main
import (
"errors"
"fmt"
)
func div(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("0으로 나누기 ㄴㄴ")
}
return a / b, nil
}
func main() {
var (
a = 10
b = 0
)
v, e := div(a, b)
if e != nil {
fmt.Println(e.Error())
} else {
fmt.Printf("%v / %v = %v\n", a, b, v)
}
}
// 0으로 나누기 ㄴㄴ
Panic
panic은 겉으로 보이게는 아무런 문제가 없지만 실행하니까 에러가 발생해 프로그램이 종료되는 기능을 뜻한다.
아래 panic이 발생한 후 프로그램이 종료되기 전에 defer 구문이 실행되는 예시 코드를 보자.
- 반복문 안에서 선언한 배열의 개수보다 큰 인덱스 값을 접근해서 panic이 발생하고 프로그램이 종료됨
- “panic done“ 구문이 panic 에러가 발생하는 코드 전에 선언되어서 프로그램이 종료되기 전에
- “Panic done“ 이 출력되고 종료가 된다.
- main() 함수에 있는 fmt.Println(“Hello, world!”)는 실행되지 않음
package main
import "fmt"
func panicTest() {
var a = [4]int{1,2,3,4}
defer fmt.Println("Panic done")
for i := 0; i < 10; i++ {
fmt.Println(a[i])
}
}
func main() {
panicTest()
fmt.Println("Hello, world!")
}
// 1
// 2
// 3
// 4
// Panic done
recover() 함수를 Golang에서 사용하는데 Java에서 try~catch 구문을 사용해 예외 처리를 하는 기능이라고 보면된다. 즉 panic 상황이 생겼을 때 프로그램을 종료하지 않고 예외 처리를 하는 것이라고 보면된다.
index out of range에러를 발생하는 코드를 recover() 함수를 사용해서 예외 처리를 한 예시 코드가 아래에 있다. 코드에서 panic이 발생하는 코드 전에 defer 구문을 사용한 익명 함수로 recover() 함수를 선언해 놓았다.
- for 문에서 index out of range panic이 발생. 프로그램이 종료 되기 전에 지연 처리한 defer 함수가 호출됨
- 함수 내에 recover() 함수가 호출되어 panic을 복구함.
- 즉 main()함수에서 선언한 fmt.Println('Hello, world!”)가 호출됨.
- panicTest() 다음 코드인 fmt.Println(“Hello, world!”)을 실행
package main
import "fmt"
func panicTest() {
defer func() {
r := recover()
fmt.Println(r)
}()
var a = [4]int{1,2,3,4}
for i := 0; i < 10; i++ {
fmt.Println(a[i])
}
}
func main() {
panicTest()
fmt.Println("Hello, world!")
}
// 1
// 2
// 3
// 4
// runtime error: index out of range [4] with length 4
// Hello, world!
recover()함수 정리
- panic이 발생해서 프로그램 종료되는 걸 막음
- 프로그램 종료 전에 실행되어야 해서 defer가 선언된 함수 안에서 쓰임
- 에러 메시지를 반환, 변수에 초기화해서 에러 메시지를 출력할 수 있음.
- 변수에 초기화하지 않으면 따로 에러 메시지를 출력하지 않음.
goroutine
- go routine은 함수 및 메소드를 다른 함수 및 메소드와 동시에 사용할 수 있게 해줌
- 가벼운 thread이고 thread와 비교하면 만들고 사용하는데 적은 비용이 듬.
- 기존 thread는 stack사이즈가 고정되어 있는데 반해 goroutine은 thread에 비해 application의 요청에 따라 stack의 용량을 늘렸다 줄일 수 있음.
- goroutine들 사이에 channel을 통해 의사소통이 가능. channel은 의도적으로 goroutine을 사용해 공유 메모리에 접근할 때 경쟁상태 예방
일반적인 goroutine의 경우 순차적으로 코드가 실행된다.
package main
import (
"fmt"
)
func main() {
human1 := [2]string{"jay", "kevin"}
human2 := [2]string{"thompson", "son"}
humanName(human1)
humanName(human2)
}
func humanName(names [2]string) {
for _, name := range names {
fmt.Println(name)
}
}
// jay
// kevin
// thompson
// son
아래 코드를 보면
- 여기서 time.Sleep()가 있는데 사용한 이유는 go keyword로 지정한 코드가 실행될 충분한 시간을 주는 것
- 추가로 go로 지정된 함수 및 저기서 main이 종료되게 되면 go keyword가 있어도 바로 종료되는데 만약 go만 적힌 함수만 실행되면 아무 출력없이 바로 실행됨.
package main
import (
"fmt"
"time"
)
func main() {
human1 := [2]string{"jay", "son"}
human2 := [2]string{"park", "kevin"}
go humanName(human1)
humanName(human2)
}
func humanName(names [2]string) {
for _, name := range names {
fmt.Println(name)
time.Sleep(time.Second)
}
}
// park
// jay
// son
// kevin
Channel
- channel은 goroutine들 끼리 소통할 수 있게 해주는 매개체
- channel을 통해 값 보내면 받는 쪽에서 값이 올때까지 blocking이 걸린다.
- time.Sleep 같은 것이 없어도 channel을 통해 값을 받을 수 있음
아래 예제코드를 보자.
- c라는 channel을 만들어준 후 c ← 부분을 만나면 c가 값을 받을 때까지
- blocking되는데 일반적인, goroutine과 달리 바로 종료 x
- 값을 넣을 때 c ← true와 같이 넣어줌
package main
import (
"fmt"
"reflect"
)
func isNumbers(numbers int, c chan bool) {
if reflect.TypeOf(numbers).String() == "int" {
c <- true
} else {
c <- false
}
}
func main() {
c := make(chan bool)
numbers := [4]int{1, 1, 3, 2}
for _, number := range numbers {
go isNumbers(number, c)
fmt.Println(<-c)
}
}
// true
// true
// true
// true
send 전용 channel도 만들 수 있는데 하나의 변수를 추가하여 성공적으로 receive했나 확인이 가능하다.
package main
import "fmt"
func sendData(sendch chan<- int) {
sendch <- 10
fmt.Println(<- sendch)
}
// .\goroutine2.go:7:14: invalid operation: <-sendch (receive from send-only type chan<- int)
func main() {
chnl := make(chan int)
go sendData(chnl)
fmt.Println(<-chnl)
}
//10
- 더 이상 받을 값이 없는 경우 close를 receiver에게 알릴 수 있다.
- close를 통해 value의 상태 값들도 받을 수 있음.
close and range
더 이상 받을 값이 없으면 close를 receiver에게 알릴 수 있음,
close를 통해서 value의 상태 값들도 받을 수 있음.
v, ok := <- ch
아래 코드를 보면 ok가 true면 성공적으로 값을 받은 것이고, false면 실패한 것.
range를 사용해 받은 받은 값들을 반복문을 돌려 값을 이용할 수 있다.
package main
import "fmt"
func producer(chnl chan int) {
for i := 0; i <10; i++ {
chnl <- i
}
close(chnl)
}
func main() {
ch := make(chan int)
go producer(ch)
for v := range ch {
fmt.Println("Received", v)
}
}
// result
Received 0
Received 1
Received 2
Received 3
Received 4
Received 5
Received 6
Received 7
Received 8
Received 9
참고: https://velog.io/@kykevin/Go%EC%9D%98-Interface,
2020 TIL no. 8 - Go의 Interface
스터디 자료 출처 https://medium.com/rungo/interfaces-in-go-ab1601159b3a Golang의 Interface란 무엇인가 Golang에서 Struct는 다양한 타입의 필드들로 이루어진 구조체들을 의미하며, Method를 선언하는
velog.io
https://hwan-shell.tistory.com/336
Golang 구조체 설명
Golang에는 class 개념 대신 struct개념이 있습니다. 쉽게말해 C나 C++에 있는 struct입니다. 하지만 Golang에는 다른 OOP와는 다르게, class의 역할중 생성자, 맴버변수 선언을 하지 못합니다. 또한 private, pub
hwan-shell.tistory.com
구름EDU - 모두를 위한 맞춤형 IT교육
구름EDU는 모두를 위한 맞춤형 IT교육 플랫폼입니다. 개인/학교/기업 및 기관 별 최적화된 IT교육 솔루션을 경험해보세요. 기초부터 실무 프로그래밍 교육, 전국 초중고/대학교 온라인 강의, 기업/
edu.goorm.io
https://hoony-gunputer.tistory.com/entry/goroutine-and-channel
goroutine and channel
goroutine이란 go routine은 함수 및 메소드를 다른 함수 및 메소드와 동시에 사용할 수 있게 해준다. go routine은 상당히 가벼운 thread이다. thread와 비교하면 만들고 사용하는데 적은 비용이든다. 그러
hoony-gunputer.tistory.com
'MadApp' 카테고리의 다른 글
(GolangPractice Day2) OJT 2021-12-10 KJH (0) | 2022.03.13 |
---|---|
OJT 2022-03-04 (0) | 2022.03.13 |
OJT 2022-02-21 (0) | 2022.03.13 |
OJT 2021-12-29 (0) | 2022.03.13 |
(GolangPractice Day1) OJT 2021-12-09 KJH (0) | 2022.03.13 |