언어 및 도구들 공부/Go

Go) 패닉이 발생하는 경우, 그리고 recover()

Dr.Mlem 2025. 4. 13. 20:28
Q1.
Go에서 패닉이 일어나는 경우들을 적어줘
 

 

 

A1.

Go에서 **panic(패닉)**이 발생하는 경우들은 몇 가지 주요 카테고리로 나뉩니다. 아래는 대표적인 panic 발생 상황들과 예제를 정리한 표입니다:


✅ Go에서 패닉(panic)이 발생하는 대표적인 경우

상황 예시 설명
1. 배열, 슬라이스, 문자열 인덱스 초과 s := []int{1, 2}; fmt.Println(s[5]) 존재하지 않는 인덱스 접근
2. nil 포인터 참조 var p *int; fmt.Println(*p) nil을 역참조(dereference)
3. map에서 nil 맵에 할당 시 var m map[string]int; m["a"] = 1 선언만 하고 초기화하지 않은 맵
4. 채널에 nil 채널 사용 var ch chan int; ch <- 1 nil 채널에 send/receive
5. close 닫기 오류 ch := make(chan int); close(ch); close(ch) 닫힌 채널을 다시 닫을 때
6. 읽을 수 없는 채널에서 읽기 ch := make(chan int); close(ch); <-ch 가능. 하지만 closed 후의 추가적인 연산 주의
7. type assertion 실패 (비검사) var i interface{} = 10; s := i.(string) 잘못된 타입 변환 시
8. runtime error 관련 (division by zero 등) x := 1 / 0 0으로 나누기
9. goroutine 안에서 recover 하지 않은 panic go func() { panic("error") }() recover 없으면 프로그램 전체가 중단됨

🔁 예외적으로 panic이 안 나는 경우

  • map[key] → 키 없으면 zero value 반환 (panic ❌)
  • nil 슬라이스에 append → 가능 (panic ❌)
  • nil map에서 read → 가능 (panic ❌), 단 write는 panic

✅ recover로 panic 복구

func mayPanic() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("복구됨:", r)
		}
	}()
	panic("문제 발생!")
}
  • recover()를 사용하면 panic을 잡고 정상 흐름으로 복귀 가능
  • 반드시 defer 내에서 호출해야 함

필요하면 각 항목별로 실제 panic 발생 예제 코드도 정리해줄게. 원해?

 

 

 

 

Q2.

recover 사용 방법에 대해서 설명해주고 템플릿과 사용 예시까지 작성해줘

 

 

 

A2.

물론! recover는 Go에서 panic으로부터 프로그램의 전체 크래시를 방지하고 복구하는 기능이에요.

panic이 발생했을 때 recover()를 defer 함수 안에서 호출하면, 해당 panic을 잡고 정상 흐름으로 돌아올 수 있어요.


✅ recover 사용 규칙

항목 설명
recover()는 반드시 defer 안에서 사용해야 한다 panic이 발생한 시점에 이미 defer가 스택에 등록돼 있어야 함
recover()는 interface{} 타입 값을 반환 panic("에러")처럼 발생된 panic 값을 받을 수 있음
recover()가 호출되지 않으면 panic은 상위로 전파되어 프로그램 종료  

✅ 기본 템플릿

func safeWrapper() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("⛑ Panic 복구됨:", r)
		}
	}()

	// 위험한 코드
	doSomething()
}

func doSomething() {
	panic("예상치 못한 에러 발생!")
}
  • doSomething()에서 panic 발생
  • safeWrapper()의 defer 함수에서 recover()가 호출되어 panic 복구
  • 프로그램은 비정상 종료되지 않고 계속 실행됨

✅ 전체 예시: divide 함수

package main

import "fmt"

func safeDivide(a, b int) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("복구 완료! 에러 메시지:", r)
		}
	}()

	result := a / b // b가 0이면 panic 발생
	fmt.Println("결과:", result)
}

func main() {
	safeDivide(10, 2)  // 정상 실행
	safeDivide(5, 0)   // panic 발생 → recover
	fmt.Println("계속 실행됨 ✅")
}

🔄 출력

결과: 5
복구 완료! 에러 메시지: runtime error: integer divide by zero
계속 실행됨 ✅

⛔ 잘못된 예시 (recover가 작동하지 않음)

func unsafe() {
	if r := recover(); r != nil {
		fmt.Println("복구?", r)
	}
	panic("여기서는 recover 작동 안 함") // 이미 recover 이후 실행
}
  • recover()는 반드시 defer 안에서 호출되어야 작동함

필요하면 recover()를 사용한 실제 응용 예제 (예: 서버에서 panic 방지 미들웨어 등)도 더 만들어줄 수 있어!