에러는 정말 언제어디서든 발생할수 있는 문제 입니다.
예를들면
1. 작동 상황이 바꾼다던가
- window, macOs
2. 메모리가 부족해 지던가
에러를 처리하는 방법은 두가지가 있습니다.
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)
}
따로 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)
}
errors
패키지와 다른점은
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입니다.