[TIL] GO특강 Day-2

드림보이즈·2023년 5월 10일

배열

길이 고정되어 있고, 인덱스 통해 접근

  • 불필요한 할당을 피하는데 도움
  • 메모리의 상세한 레이아웃 계획 시 유용
var strArray1 [5]string
strArray1[0] = "a"
...
strArray1[4] = "e"

fmt.Println(strArray1)
//문제1 : 0~4까지 모두 채운후, 전체 코드와 해당 출력 결과는?

var strArray2 xxxx = xxxx {"1", "2", "3", "4", "5"}
fmt.Println(strArray2)
//문제2 : xxxx 자리에 올바른 배열을 선언하고 전체 코드와 해당 출력 결과는?

strArray3 := [5]string{"10", "20", "30"}
fmt.Println(strArray3)
//문제3 : 전체 코드와 해당 출력 결과는?

strArray4 := [...]string{"100", "200", "300"}
fmt.Println(strArray4)
fmt.Println(len(strArray4))
//문제4 : 전체 코드와 두 출력 결과는?

strArray5 := [5]string{2:"codz", 4:"states"}
fmt.Println(strArray5)
fmt.Printf("%#v\n", strArray5)
//문제5 : 전체 코드와 해당 출력 결과는?

슬라이스

길이가 동적으로 늘어나는 배열

실제 자료형의 타입은 레퍼런스 타입

레퍼런스 타입 : 객체의 참조를 저장 (밸류가 아니라)

메모리 효율 좋고, 값 변경 가능

var a1 []int
var a2 [5]int

fmt.Println(reflect.ValueOf(a1).Kind())
fmt.Println(reflect.TypeOf(a1))

fmt.Println(reflect.ValueOf(a2).Kind())
fmt.Println(reflect.TypeOf(a2))
//a : 출력의 결과값은?

var intSlice = []int{0, 1, 2, 3}
fmt.Printf("%#v\n", intSlice)
fmt.Printf("%v\n", intSlice)
fmt.Printf(":2 %v\n", intSlice[:2])
fmt.Printf("1:3 %v\n", intSlice[1:3])
fmt.Printf("2: %v\n", intSlice[2:])
fmt.Printf("0:3 %v\n", intSlice[0:3])
//b : 각 출력 결과값은?

key-value 형으로 데이터 저장

자료형 타입 : 레퍼런스

key의 자료형은 모든 데이터 타입을 사용 가능

선언 : var 맵명 [키 자료형]값 자료형

var map1 map[string]int

map2 := make(map[string]string)
map3 := make(map[int]string, 1000)

var timeZone = map[string]int{
	"UTC": 0,
	"EST": 5,
	"CST": 6,
	"MST": 7,
	"PST": 8,
}

timeSet := timeZone["CST"]
//a : timeZone관련, 루프를 돌면서 key-value 형태로 출력하는 코드 작성

attend := map[string]bool{
	"codz": true,
	"status": false,
	"alice": true,
	...              //이하 생략
}

구조체

다른 언어들과 마찬가지로, 여러 변수 타입을 담아 하나의 형태로 만들 때 사용

type Persons struct {
	Name string
	Age int
	Pnum string
}

var pers Persons
pers.Name = "codz"
pers.Age = 23
pers.Pnum = "01098765432"

// new 사용

p1 := new(Persons)
p1.Name = "codz"
p1.Age = 29
p1.Pnum = "01098765432"
fmt.Println("Person 1 : ", p1)
fmt.Printf("%#v\n", p1)

var p2 = new(Persons)
p2.Name = "states"
p2.Age = 33
p2.Pnum = "01054329876"
fmt.Println("Person 2 : ", p2)
fmt.Printf("%#v\n", p2)

//선언과 동시에 초기화
var p3 = &Persons{"test", 17, "01045673210"}
fmt.Println("Person 3 : ", p3)
fmt.Printf("%#v\n", p3)

//구조체 배열 형식
p4 := []Persons{
		{
			Name: "p1",
			Age:  10,
			Pnum: "9876",
		},
		{
			Name: "p2",
			Age:  9,
			Pnum: "8765",
		},
	}
	fmt.Println(p4)
	fmt.Printf("%#v\n", p4)

인터페이스

메서드의 집합 (구조체는 필드의 집합)

객체 행위를 지정해주는 방법

type Persons  interface {
	Student() int
	Player() string
}

	//예제1 : 인터페이스 데이터 선언
	var x interface{}
	x = 1
	fmt.Println(x)
	
	x = "Hello World"
	fmt.Println(x)
    
    	//예제2 : 인터페이스 데이터 타입 변환과 반환값
	var x interface{}
	x = "10"
	
	sVal := x.(string)
	fmt.Println(sVal)
	x = 10
	nVal, bCnv := x.(int)
	fmt.Println(nVal, bCnv)
    
    	//예제3 : 타입에 따른 핸들링 기법
	var x interface{}
	x = "hello"
	xType := reflect.TypeOf(x)

	if xType.Kind() == reflect.Int {
		fmt.Println("Int ", x)
	} else if xType.Kind() == reflect.String {
		fmt.Println("string ", x)
	}
    
    	//예제4 : 구조체를 선언하고, 구조체별 인터페이스 핸들링
	codz := Persons{Name: "codz", Age: 20}

	var x interface{}
	x = codz

	xPerson := x.(Persons)
	xPrsAge := x.(Persons).Age
	ty := reflect.TypeOf(xPerson)
	fmt.Printf("%v, %d, %s\n", xPerson, xPrsAge, xPerson.Name)
	fmt.Println(ty.String())

메모리 할당 및 초기화

new

new()는 메모리를 할당하는 내장함수.

type File struct{
	fd int
	name string
	dirinfo string
	nepipe int
}

// 생성자 필요
func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    f := new(File)     //생성자
    f.fd = fd          //초기화
    f.name = name      //
    f.dirinfo = nil    //
    f.nepipe = 0       //
    return f           //포인터 리턴
}

// 복합 레터럴 사용하여 간소화
func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    f := File{fd, name, nil, 0} //복합리터럴 사용
    return &f
}

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    
    return &File{fd, name, nil, 0} //복합리터럴 사용
}

func NewFile(fd int, name string) *File {
    if fd < 0 {
        return nil
    }
    
    return &File{fd: fd, name: name} //복합리터럴 사용
}

make

new()와 달리 특정 데이터 타입에서는 미리 사용할 준비가 될 수 있게 초기화 해줘야 하는 경우 존재

  • 맵, 슬라이스, 채널에서만 적용
  • 반드시 초기화 후 사용
  • 포인터 반환 안함
var p *[]int = new([]int)       // slice 구조체를 할당한다; *p == nil; 유용하지 않음
var v  []int = make([]int, 100) // slice v는 이제 100개의 int를 갖는 배열을 참조

// 불필요하게 복잡한 경우:
var p *[]int = new([]int)
*p = make([]int, 100, 100)

// 추천 형태
v := make([]int, 100)

제어문 ( 반복, 조건)

반복문 for, range

i := 1
for ; i <= 10; i++ {
	fmt.Println(i)
}
 
i = 1
for i <= 10 {
	fmt.Println(i)
	i++
}
 
for i := 1; ; i++ {
	fmt.Println(i)
	if i == 10 {
		break
	}
}

for i := 0; i < 100; i++ {
	fmt.Printf("%d", i)
}

range

var arr [10]int

// 배열이기 때문에 return의 첫번째 값이 index이다.
for idx0, _ := range arr {
	arr[idx0] = len(arr) - idx0
}

for idx1, value1 := range arr {
	fmt.Println("idx: ", idx1, "value: ", value1)
}

조건문 if, switch

switch

func getValue(value int) (int) {
	return value
}

func main() {
	x := getValue(10) // 출력: x == 10
	y := 20 // 출력: y == 20

	switch x {
	case 10:
		fmt.Println("x == 10")
	// golang은 컴파일러가 case문마다 자동으로 break문을 추가해준다.
	case 20:
		fmt.Println("x == 20")
	}

	switch y {
	case 10:
		fmt.Println("y == 10")
	case 20:
		fmt.Println("y == 20")
	}
}

에러

에러 핸들링

error 타입을 사용해서 다룰 수 있게 했다.

func divide(a, b int) (n int, err error) {
	if b == 0 {
			return 0, errors.New("분모가 0이면 안됩니다.")
	}
	return a / b, nil
}

func main() {
	res, err := divide(4, 2) // 코드를 사용하는 쪽에서 에러를 다룸
	if err != nil {
			fmt.Println("ERROR : ", err.Error())
	} else {
			fmt.Println(res)
	}
}

패닉

패닉 : 에러로 인해 프로세스가 강제로 종료되는 상황

직접 panic 함수로 발생시킬 수 있음

if repo, err != model.New(conf) ; err != nil {
	panic(err)
    } else if ctl, err != control.New(conf, repo); err!=nil {
    	panic(err)
        }
package main

import (
	"errors"
	"fmt"
)

var res = 1

func Calc(n int) {
	if n == 0 {
		panic(errors.New("zero calc"))
	} else {
		res *= n
		fmt.Println("Result: ", res)
	}
}

func main() {
	Calc(1)
	Calc(2)
	Calc(0)
	Calc(3)
}

복구

패닉 발생 이후 구문은 실행되지 않음, 패닉이 발생해도 회복 위해 recover 사용

일반적으로 고루틴 스택 해제를 시작하여, 지연된 함수를 실행할 때 이용, 이로써 고루틴 제어를 다시 얻고 정상적인 실행 재개

  • recover는 해제를 중지
  • 패닉에 전달된 인수를 반환
  • 지연함수 내부에서만 유용
func server(workChan *Work) {
	for i=0;i <= 10; i++ {
    	go safeDo(work)
        }
    }
    
func safeDo(work *Work) {
	defer func() { // 종료 및 실패된 work만 리커버
    	if err := recover(); err!= nil { 
        	log.Println("work failed: ", err)
        }
   }()
   do(work) //work 실행
}   

테스트

기본적으로 테스트 프레임워크 내장

  • 파일 이름에 *_test.go 생성
  • go test fh tlfgod
  • 테스트 함수는 "Test*" 사용
  • 사용 패키지 : Go 내장 패키지 "testing"

controller.go

package controller

import ( "fmt" )

func Sum(a, b int) int {
	fmt.Println("Sum : ", a, " + ", b)
	return a + b
}

main.go

package main

import ( "fmt" )

func main() {
	fmt.Println("Hello World")
}

go_test.go

package go_test

import (
	"fmt"
	clt "lecture/go-testing/controller"   // user package import
	"testing"
)

func TestCalc(t *testing.T) {
	fmt.Println("Hello world!")
	ressum := clt.Sum(1, 2)

	t.Errorf("sum = %d", ressum)

	if ressum == 3 {
		t.Error("fail calc")
	}
}

테스트 실행

go mod init
go mod tidy
go test go_test.go

디버깅

vscode 내장 디버그 기능이 편하고 유용함

main.go에서만 디버깅 시작해야

profile
시리즈 클릭하셔서 카테고리 별로 편하게 보세용

0개의 댓글