Go에서 팬릭(panic)은 프로그램의 치명적인 오류를 알리기 위해 사용됩니다. 팬릭이 호출되면 실행은 스택을 언와인드하고 모든 deferred 함수가 호출되는 과정부터 시작됩니다. 스택에 recover()를 호출하는 함수가 있다면, 이는 팬릭을 가로채고 처리할 수 있습니다. 그러나 recover는 오직 자신의 goroutine 내부에서만 작동하며 defer로부터 호출될 때만 유효합니다.
안전한 복구를 위한 권장 패턴:
func safe(fn func()) { defer func() { if r := recover(); r != nil { fmt.Println("팬릭에서 복구됨:", r) } }() fn() } go safe(func() { panic("실패!") // 프로그램이 중단되지 않음 })
다음 사항을 기억하는 것이 중요합니다:
panic은 첫 번째 recover까지 현재 goroutine의 실행을 종료합니다.recover가 다른 goroutine에서 발생한 팬릭을 가로챌 수 있나요?
답변: 아닙니다, recover는 팬릭이 발생한 goroutine 내에서만 작동하며, 오직 defer 함수에서 호출된 경우에만 가로챌 수 있습니다. 팬릭이 한 goroutine에서 발생하고 recover가 다른 goroutine에서 호출되면, 가로채기가 이루어지지 않으며 프로그램은 비정상 종료됩니다.
예시:
func main() { go func() { panic("goroutine 내부") }() time.Sleep(time.Second) recover() // 작동하지 않음! Panic이 프로그램을 종료시킴 }
이야기
개발자가 메인 함수에서만 패턴 defer recover()를 사용하여 오류 처리를 구현했습니다. worker 내에서 팬릭이 발생했을 때, 전역 recover가 이를 가로채지 못해 전체 프로그램이 비정상 종료되었습니다.
이야기
프로젝트에서는 웹 서버의 유연한 종료를 위해 defer/recover를 사용하였으며, 이것이 충분하다고 생각했습니다. 그러나 한 goroutine에서 팬릭이 발생하였고, recover의 처리가 적절한 위치에 서지 않아 전체 프로세스가 종료되는 문제를 겪었고, 결과적으로 모든 워커 풀을 리팩토링해야 했습니다.
이야기
메시지 처리 흐름에서 사용자 함수의 잘못된 작동으로 팬릭이 발생했습니다. 개발자는 상위 수준의 recover가 이를 "잡아줄 것"이라고 예상했지만, recover가 오직 defer에서만 작동한다는 것을 인식하지 못했습니다. 이로 인해 서비스는 불규칙적으로 크래시되었고, 큐에 잘못된 메시지가 나타날 때마다 문제가 발생했습니다.