STUDY/Go Lang

[GoLang] 고급 기법으로 Go 레벨업하기 (1)

Jexists 2023. 10. 22. 19:34

이 글은 골든래빗 《Tucker 의 Go 언어프로그래밍》의 2단계 써머리입니다.

목차

  1. 슬라이스
  2. 메서드
  3. 인터페이스
  4. 함수 고급편
  5. 자료구조

슬라이스 (slice)

→ var 변수명 []타입 선언

→ 변수명 := []타입{} 선언

→ 동적 배열: 자동으로 배열 크기를 증가 시키는 자료구조

→ 배열 가리키는 포인터, 요소개수(Len), 전체 배열 길이(Cap)으로 구성된 구조체

append()

→ 슬라이스만의 기능 (요소 추가) → append(추가하고자 하는 슬라이스, 추가하는 요소) = 새로운 슬라이스 결과 반환

	slice := []int{1, 2, 3, 4, 5}
	slice3 := append([]int{}, slice...)
	fmt.Println(slice3)
	// [1 2 3 4 5]

	slice4 := append([]int{}, slice[0], slice[1], slice[2], slice[3], slice[4])
	fmt.Println(slice4)
	// [1 2 3 4 5]

슬라이싱 Slicing

→ 배열의 일부를 집어내는 기능: 슬라이스 반환 → 배열의 일부 = 배열[시작인덱스:끝인덱스]

- 처음부터 슬라이싱: 시작인덱스 생략가능

	slice4 := []int{1, 2, 3, 4, 5}
	slice5 := slice4[0:3]
	slice6 := slice4[:3]
	// slice4:  [1 2 3 4 5] 5 5
	// slice5:  [1 2 3] 3 3
	// slice6:  [1 2 3] 3 3

- 끝까지 슬라이싱: 끝인덱스 생략가능

	slice7 := []int{1, 2, 3, 4, 5}
	slice8 := slice4[2:len(slice7)]
	slice9 := slice4[2:]
	// slice7:  [1 2 3 4 5] 5 5
	// slice8:  [3 4 5] 3 3
	// slice9:  [3 4 5] 3 3

- 전체 슬라이싱: 시작인덱스, 끝인덱스 생략가능 (배열 전체를 가리키는 슬라이스 생성)
- 배열을 슬라이스로 바꾸고 싶을때 사용

	slice10 := []int{1, 2, 3, 4, 5}
	slice12 := slice10[0:len(slice10)]
	slice11 := slice10[:]
	// slice10:  [1 2 3 4 5] 5 5
	// slice11:  [1 2 3 4 5] 5 5
	// slice12:  [1 2 3 4 5] 5 5

- cap크기 조절
- slice[시작인덱스:끝인덱스:최대인덱스]
- len == 끝인덱스 - 시작인덱스
- cap == 최대인덱스 - 시작인덱스

	slice13 := []int{1, 2, 3, 4, 5}
	slice14 := slice13[1:3]
	slice15 := slice13[1:3:4]
	// slice13:  [1 2 3 4 5] 5 5
	// slice14:  [2 3] 2 4
	// slice15:  [2 3] 2 3
	slice16 := []int{1, 2, 3, 4, 5, 6, 7, 8}
	slice17 := slice16[1:3]
	slice18 := slice16[1:3:4]
	// slice13:  [1 2 3 4 5 6 7 8] 8 8
	// slice14:  [2 3] 2 7 (최대인덱스: 8 - 시작인덱스: 1)
	// slice15:  [2 3] 2 3 (최대인덱스: 4 - 시작인덱스: 1)

- 끝에서 한개전 (끝 인덱스를 정확하게 적어야함:음수X)

	slice19 := []int{1, 2, 3, 4, 5}
	slice20 := slice19[2:len(slice19)-1]
	// slice19:  [1 2 3 4 5] 5 5
	// slice20:  [3 4] 2 3 

메서드

→ 호출시 값 모두 복사

→ 값 중심의 메서드 만들때 사용

→ 호출자 인스턴스 접근 불가

→ 복사되는 양에 따라서 성능상 문제

package main

import "fmt"

type myInt int

func (a myInt) add(b int) int {
	// return a + b // ERROR
	return int(a) + b
}

func main() {
	var a myInt = 10
	fmt.Println(a.add(30))
	var b int = 20
	// fmt.Println(b.add(50))  // ERROR
	// 다른 타입이라서 메서드 사용불가
	fmt.Println(myInt(b).add(50))
}
package main

import (
	fmt
)

type account stuct {
	balance int
}

func withdrawFunc(a *account, amount int) { // 일반 함수 표현
	a.balance -= amount
}

func (a *account) withdrawFunc(amount int) { // 메서드 표현
	a.balance -= amount
}

func main() {
	a := &account{100}

	withdrawFunc(a, 30) // 함수 형태 호출

	a.withdrawFunc(30) // 메서드 형태 호출

	fmt.Printf("%d \\n", a.balance)
}

인터페이스

→ 구현을 포함하지 않는 메서드 집합

→ interface{}

→ 모든 타입을 받고 싶을 때 사용

  var a Interface
  t := a.(ConcreteType)

  // 타입 변환한 결과, 성공여부 (bool) -> 런타임 에러 방지 
  t, ok := a.(ConcreteType)
  if ok {
    ...
  }
  // 한줄로
  if t, ok := a.(ConcreteType); ok {
    ...
  }

함수 고급편

가변 인수 함수

→ 함수 인수 개수가 고정적이지 않은 함수 → 인수 타입 앞에 ... 붙여서 해당 타입 인수 여러개 받는 가변 인수임을 표시

defer 지연 실행

→ 함수가 종료되기 직전에 실행하는 코드

→ 맨 마지막에 defer 쓴게 먼저 실행됨 (stack 스택: FILO / LIFO)

package main

import (
	"fmt"
	"os"
)

func main() {
	// defer 명령문

	f, err := os.Create("test.txt")
	if err != nil {
		fmt.Println("failed to create a file", err)
		return
	}

	defer fmt.Println("반드시 호출")
	defer f.Close()
	defer fmt.Println("파일 닫기")

	fmt.Println("파일에 Hello 적기")
	fmt.Fprintln(f, "Hello")

	// 파일에 Hello 적기
	// 파일 닫기
	// 반드시 호출
}