Golang - 에러 핸들링

Lumi·2022년 2월 15일
0

Golang

목록 보기
21/38
post-thumbnail

🔥 에러

에러는 정말 언제어디서든 발생할수 있는 문제 입니다.

예를들면

1. 작동 상황이 바꾼다던가
- window, macOs

2. 메모리가 부족해 지던가

에러를 처리하는 방법은 두가지가 있습니다.

    1. 에러가 발생하면 프로그램을 죽이고 처리후에 다시 프로그램을 실행
    1. 에러가 발생을 하면 무시하고 진행
package main

import (
	"bufio"
	"fmt"
	"os"
)

func ReadFile(filename string) (string, error) {
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()

	rd := bufio.NewReader(file)
	// 파일을 읽을수 있을수 있는 변수를 할당하고
	line, _ := rd.ReadString('\n')
	return line, nil
}

func WriteFile(filename string, line string) error {
	file, err := os.Create(filename)

	if err != nil {
		return err
	}
	defer file.Close()

	_, err = fmt.Fprintln(file, line)
	return err
}

const filename string = "data.txt"

func main() {
	line, err := ReadFile(filename)

	if err != nil {
		err = WriteFile(filename, "this is WriteFile")
		if err != nil {
			fmt.Println("파일 생성 실패", err)
			return
		}
		line, err = ReadFile(filename)
		if err != nil {
			fmt.Println("파일 읽기에 실패했습니다.", err)
			return
		}
	}

	fmt.Println("파일 내용은", line)
}

이 코드에서 다루고 있는 에러처리 방법은 거창한 것이 아닙니다.

  • 단순히 에러가 발생을 하면 출력을 하게 됩니다.

에러를 사용자가 직접 발생시킬수도 있습니다.

package main

import (
	"fmt"
	"math"
)

func sqrt(f float64) (float64, error) {
	if f < 0 {
		return 0, fmt.Errorf("제곱근은 양수여야 합니다. f:%g", f)
	}
	return math.Sqrt(f), nil
}

func main() {
	sqrt, err := sqrt(-2)

	if err != nil {
		fmt.Printf("Error, %v\n", err)
		return
	}

	fmt.Printf("결과는 %f 입니다. \n", sqrt)
}
  • main내에 있는 if문은 발생을 하지 않습니다.
  • 왜냐하면 err가 nil이 아니기 떄문입니다.

따로 errors라는 패키지를 활용할수도 있습니다.

package main

import (
	"errors"
	"fmt"
	"math"
)

func sqrt(f float64) (float64, error) {
	if f < 0 {
		return 0, errors.New("제곱근은 양수여야 합니다")
	}
	return math.Sqrt(f), nil
}

func main() {
	sqrt, err := sqrt(-2)

	if err != nil {
		fmt.Printf("Error, %v\n", err)
		return
	}

	fmt.Printf("결과는 %f 입니다. \n", sqrt)
}

특정 타입을 두고 에러를 반환시키는 방법도 있습니다.

package main

import "fmt"

type PasswordError struct {
	length int
	std    int
}

func (err PasswordError) Error() string {
	return "암호 길이가 짧습니다."
}

func RegisterAccount(name, password string) error {
	if len(password) < 8 {
		return PasswordError{len(password), 8}
	}
	return nil
}

func main() {
	err := RegisterAccount("myid", "mypw")

	if err != nil {
		fmt.Println(err.length)
		if errInfo, ok := err.(PasswordError); ok {
			fmt.Println(errInfo.length)
		}
	}
}

에러 랩핑

에러를 감싼다는 의미 입니다.

  • 예제가 좀 깁니다.
package main

import (
	"bufio"
	"fmt"
	"strconv"
	"strings"
)

func MultipleFromString(str string) (int, error) {
	scanner := bufio.NewScanner(strings.NewReader(str))
	// 한글자씩 읽어야 할떄에 사용하면 편한 패키지 입니다.
	// newScanner는 reader타입을 받기 떄문에 인자로 넘겨줍니다.
	scanner.Split(bufio.ScanWords)
	// 읽어올 규칙을 정하는 부분으로 scanwords를 적용하면 한 단어씩 읽어옵니다.

	pos := 0
	a, n, err := readNextInt(scanner)
	fmt.Println(a, n)
	if err != nil {
		return 0, fmt.Errorf("failed to readnextInt(), pos:%d err:%w", pos, err)
	}

	pos += n + 1

	b, n, err := readNextInt(scanner)
	fmt.Println(b, n)
	if err != nil {
		return 0, fmt.Errorf("failed to readnextInt(), pos:%d err:%w", pos, err)
	}
	return a * b, nil
}

// 단어를 읽어서 숫자로 변환하여 반환합니다.
// 변환된 숫자, 읽은 글자수, 에러를 반환
func readNextInt(scanner *bufio.Scanner) (int, int, error) {
	if !scanner.Scan() {
		return 0, 0, fmt.Errorf("faild to scan")
	}
	word := scanner.Text()
	// scanner에서 읽은 값을 text형식 = string형식으로 읽어 준다.
	number, err := strconv.Atoi(word)
	// 문자를 숫자로 바꾸어 주는 역할
	// "24" => 24,
	// 대신 "abc"같은 문자열은 바꿀수 없기 떄문에 에러를 반환
	if err != nil {
		return 0, 0, fmt.Errorf("failed to convert workt to int")
	}

	return number, len(word), nil
}

func readEq(eq string) {
	rst, err := MultipleFromString(eq)
	if err == nil {
		fmt.Println(rst)
	} else {
		fmt.Println("testststs")
	}
}

func main() {
	readEq("123 3")
	readEq("123 abc")
}
  • 이 예제가 너무 길기 떄문에 유튜브에서 다루어 보겠습니다.

🔥 패닉

이전까지는 에러를 표시하는 방법이였지만 패닉이라는 방법은 에러를 만났을떄 프로그램을 빠르게 종료시키는 방법 입니다.

package main

import "fmt"

func divide(a, b int) {
	if b == 0 {
		panic("b는 0일 수 없습니다.")
	}
	fmt.Println("문제 없습니다.")
}

func main() {
	divide(9, 3)
	divide(9, 0)
}
  • 내장함수를 통해 실행이 가능하며 panic코드가 작동을 하면 어디서 에러가 발생을 하였는지 또한 출력이 됩니다.

errors패키지와 다른점은

  • panic는 프로그램을 바로 종료를 시키지만
  • errors 종료를 시키지 않고 에러가 발생을 했다고 출력만하게 됩니다.
package main

import "fmt"

func divide() {
	fmt.Println("f시작!")
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("panic 복구 - ", r)
		}
	}()

	g()
	fmt.Println("문제 없습니다.")
}

func g() {
	a(9, 0)
}

func a(a, b int) int {
	if b == 0 {
		panic("제수는 0일수가 없습니다.")
	}
	return a / b
}
func main() {
	divide()
}

패닉을 복구하는 방법은 recover입니다.

  • 이 부분도 youtube에 정리를 하였습니다.
profile
[기술 블로그가 아닌 하루하루 기록용 블로그]

0개의 댓글