TIL 04 - 제어문, 에러, 패닉, 복구, 테스트, 디버깅(Golang)

프동프동·2023년 2월 10일
0

TIL

목록 보기
4/46
post-thumbnail

제어문(반복문, 조건문 - if, switch)

반복문

  • 특징
    • do-while, while 문이 없다.
    • for문만을 사용한다.
    • 배열의 경우 for 문과 함께 range문을 사용할 수 있다.
  • 사용 방법
    • for 초깃값; 조건식; 변화식 {}
      for i := 0; i < 5; i++ {
      	fmt.Println(i)
      }
    • for 인덱스, 값 := range 배열 {}
      loc := []string{"Seoul", "Busan", "Incheon"}
      for index, name := range loc {
      	fmt.Println("ex3 : ", index, name)
      }
    • 무한 루프
      for {
      	fmt.Println("hi")
      }
  • 주의
    • 괄호 위치 중요
      for i := 0; i < 5; i++
      {
       	fmt.Println(i)
      }
    • 괄호가 없으면 안된다.
      for i := 0; i < 5; i++
      		fmt.Println(i)

조건문 - if

타언어에 있는 것과 마찬가지로 if, else, else if와 switch문이 있다.

  • 사용 방법
    // 예시 1
    if a >= 15 {
    }
    
    // 예시 2
    if a := 10; a >= 5{
    	fmt.Println("a는 5 이상")
    }
    
    // 예시 3
    if a >= 65 {
    	fmt.Println("65 이상")
    } else {
    	fmt.Println("65 미만")
    }
    
    // 예시 4
    if a >= 15 {
    	// ...
    } else if a < 10 && a > 5{
    	// ...
    } else {
    	// ...
    }
  • 주의
    • 괄호는 if문 마지막에 둬야한다.
      if a >= 15
      {
      }
    • 괄호 없이 사용 불가능하다
      if a >= 15
      	fmt.Println()

조건문 - Switch

  • 특징
    • 표현식의 조건으로 모든 타입이 들어올 수 있다.
      • 값이 아닌 변수 Type으로도 분기가 가능하다.
    • case 문 뒤에 표현식을 사용할 수 있다.
    • switch 키워드 뒤에 표현식을 생략할 수 있다.
  • 사용 방법
    // 사용 방법 1 - 표현식 생략
    switch {
    	case a < 0:
    		fmt.Println(a, "는 음수")
    	case a == 0:
    		fmt.Println(a, "는 0")
    	case a > 0:
    		fmt.Println(a, "는 양수")
    }
    
    // 사용 방법 2 - 변수 선언과 함께 바로 초기화하여 사용 가능
    switch b := 27; {
    	case b < 0:
    		fmt.Println(b, "는 음수")
    	case b == 0:
    		fmt.Println(b, "는 0")
    	case b > 0:
    		fmt.Println(b, "는 양수")
    }
    
    // 사용 방법 3 - 일치하는 값 찾기
    switch c := "go"; c {
    	case "go":
    		fmt.Println("GO")
    	case "java":
    		fmt.Println("java")
    	default:
    		fmt.Println("일치하는 값 없음")
    }
    
    // 사용 방법 4 - 초기화 후 연산
    switch c := "go"; c + "lang" {
    	case "golang":
    		fmt.Println("golang!")
    	case "java":
    		fmt.Println("java!")
    	default:
    		fmt.Println("일치하는 값 없음")
    }
    
    // 사용 방법 5 - 2개 이상 변수 선언과 동시에 초기화 하여 사용 가능
    // 예제 5
    switch i, j := 20, 30; {
    	case i < j:
    		fmt.Println("i는 j보다 작다")
    	case i == j:
    		fmt.Println("i와 j는 같다")
    	case i > j:
    		fmt.Println("i는 j보다 크다")
    	}
    }
    

에러(Errors)

에러가 발생하게 되더라도 프로그램이 안정적으로 동작, 종료되도록 Error Handling해야한다.

  • 특징
    • error 타입을 사용하여 에러를 반환하여 코드를 사용하는 쪽에서 에러를 다룰 수 있게 해야한다.
    • error도 인터페이스이다.
      type error interface {
      	Error() string
      }
  • 사용 방법
    • errors 패키지를 이용하는 방법

      func notZero(n int) (string, error) {
      	if n != 0 {
      		s := fmt.Sprint("Hello Golang : ", n)
      		return s, nil
      	}
      	return "", errors.New("0을 입력했습니다. 에러 발생!")
      
      }
      
      // Errorf를 이용한 에러 처리
      func main() {
      	a, err := notZero(1)
      	if err != nil {
      		log.Fatal(err.Error())
      	}
      	fmt.Println("ex : ", a)
      
      	b, err := notZero(0)
      	if err != nil {
      		log.Fatal(err.Error())
      	}
      	//Fatal 이후 프로그램 종료 후 실행 중지
      	fmt.Println("ex : ", b)
      	fmt.Println("End Error Handling!")
      
      }
    • fmt.Errorf()를 이용하는 방법

      func notZero(n int) (string, error) {
      	if n != 0 {
      		s := fmt.Sprint("Hello Golang : ", n)
      		return s, nil
      	}
      	return "", fmt.Errorf("%d를 입력했습니다. 에러 발생!", n)
      
      }
      
      // Errorf를 이용한 에러 처리
      func main() {
      	a, err := notZero(1)
      	if err != nil {
      		log.Fatal(err.Error())
      	}
      	fmt.Println("ex : ", a)
      
      	b, err := notZero(0)
      	if err != nil {
      		
      		log.Fatal(err.Error())
      	}
      	//Fatal 이후 프로그램 종료 후 실행 중지
      	fmt.Println("ex : ", b)
      	fmt.Println("End Error Handling!")
      
      }
    • 사용자 정의 에러를 이용하는 방법

      package main
      
      import (
      	"fmt"
      	"log"
      	"math"
      	"time"
      )
      
      // 예외(에러) 처리 구조체
      type PowError struct {
      	time  time.Time // 에러 발생 시간
      	value float64   // 파라미터
      	message string // 에러 메시지
      }
      
      func (e PowError) Error() string {
      	return fmt.Sprintf("[%v]Error - Input Value(value : %g) - %s", e.time, e.value, e.message)
      }
      
      func Power(f, i float64) (float64, error) {
      	if f == 0 {
      		return 0, PowError{time: time.Now(), value: f, message: "0은 사용할 수 없습니다."}
      	}
      	if math.IsNaN(f) {
      		return 0, PowError{time: time.Now(), value: f, message: "숫자가 아닙니다."}
      	}
      	if math.IsNaN(i) {
      		return 0, PowError{time: time.Now(), value: i, message: "숫자가 아닙니다."}
      	}
      	return math.Pow(f, i), nil
      }
      
      func main() {
      	// 에러 타입이 아닌 경우 에러 처리 방법
      	// 구조체를 사용해서 세부적인 정보 출력 
      
      	// 예제 1
      	v, err := Power(10, 3) // 정상 처리
      	if err != nil {
      		log.Fatal(err.Error())
      	}
      	fmt.Println("ex : ", v)
      
      	// 예제 2
      	t, err := Power(0, 3) // 정상 처리
      	if err != nil {
      		//log.Fatal(err.Error())
      		fmt.Println("PowError.value :", err.(PowError).value)
      		fmt.Println("PowError.message :", err.(PowError).message)
      		fmt.Println("PowError.time :", err.(PowError).time)
      	}
      }

패닉(Panic)

프로그래밍 런타임 중 치명적인 에러나, 복구할 수 없는 에러로 인해 강제로 종료되는 상황을 패닉(panic)이라고 한다.

  • 필요한 상황
    • 프로세스 강제 종료가 필요한 경우
    • 문법 에러는 아니지만, 로직에 따른 에러르 처리하고 할 때 사용
      • 좀비 프로세스 생성 차단
      • 초기화 및 리소스 할당시 주로 사용
  • 사용 방법
    • panic() 함수를 통해 발생시킬 수 있다.
      • panic() 함수는 호출 즉시 해당 메서드를 즉시 중지시키고 defer 함수를 호출하고 자기자신을 호출한 곳으로 리턴한다.

        func main() {
        	fmt.Println("Start Main")
        	// 방법 1
        	panic("Error occurred : user stopped!")
        	// 방법 2
        	// log.Panic("Error occurred : user stopped!")
        	fmt.Println("End Main") // 실행 불가
        }

복구(Recover)

패닉이 발생하면 해당 시점에서 함수 실행을 즉시 중단한다. 패닉 발생 이후의 구문은 실행되지 않는다. 이러한 패닉이 발생해도 프로그램을 안정적인 상태로 만들기 위해 recover 함수를 사용한다.

  • 필요한 상황
    • 고루틴 스택 해제를 시작하여, 지연된 함수를 실행할 때 recover를 이용한다. 고루틴 제어를 다시 얻고 정상적인 실행을 재개하기 위함
    • 에러 복구
  • 사용 방법
    • 예제 1 - panic() 함수를 사용하여 패닉 발생 시 복구하는 방법
      • panic(”message”)에 설정한 메시지를 받아볼 수 있다.

        func runFunc() {
        	defer func() {
        		s := recover()
        		fmt.Println("Error Message : ", s)
        	}()
        	panic("Error occurred!")
        	fmt.Println("Test") // 호출 안됨
        }
        func main() {
        	runFunc()
        	// <- 이곳으로 온다.
        	fmt.Println("Hello")
        }
    • 예제 2 - 정의하지 않은 panic 발생 시 복구하는 방법
      func runFunc() {
      	defer func() {
      		if s := recover(); s != nil {
      			fmt.Println("Error Message : ", s)
      		}
      	}()
      
      	a := [3]int{1, 2, 3}
      
      	for i := 0; i < 5; i++ {
      		fmt.Println("ex : ", a[i]) // 에러 발생(인덱스 범위)
      	}
      }
      func main() {
      
      	// 예제
      	runFunc()
      	// <- 이곳으로 온다.
      	fmt.Println("Hello")
      }

테스트(Test)

기본적으로 테스트 프레임워크를 내장하고 있다.

  • 사용 방법
    • 파일 이름에 *_test.go 파일 생성, 보통 패키지폴더내 1개의 테스트파일을 기본적으로 구성하여 패키지 테스트를 위해 사용
    • “go test” 명령으로 실행, 폴더내“*_test.go” 파일 자동인식 및 실행
    • 테스트 함수는 접두어 “Test*” 사용
      • 예) TestCalcurator() { } : Test’C’ 대문자 사용을 원칙
    • 사용 패키지 : Go 내장 패키지 “testing”

디버깅(Debugging)

  • 사용 방법
    1. 중단점 설정(F9)
    2. 디버깅 시작 - main.go 파일에서만 시작
      1. 실행 및 디버그 버튼 클릭 및 F5 (or fn+5)
    3. 디버그 진행 및 종료
      1. 계속 : 다음 중지점 까지 진행(F5)
      2. 단위 실행: 다음라인으로 이동(F10)
      3. 단계 정보 : 내부 함수로 이동(F11)
      4. 단계 출력 : 위 단계로 이동(F12)
      5. 다시 시작 : 디버깅 재실행
      6. 중지 : 디버깅 중지(쉬프트 + F5)
profile
좋은 개발자가 되고싶은

0개의 댓글