๊ธ€ ์ž‘์„ฑ์ž: ๋˜ฅํด๋ฒ .
๋ฐ˜์‘ํ˜•

2์ค„ ์š”์•ฝ

  • recover ๋Š” deferํ•จ์ˆ˜์— ์˜ํ•ด ์ง์ ‘์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋ฉด ์•ˆ๋œ๋‹ค.
  • panic์„ ์ค‘๋‹จ์‹œํ‚ฌ ํ•จ์ˆ˜์˜ depth๊ฐ€ n์ด๋ผ๋ฉด recover๋Š” n+1 ์—์„œ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•œ๋‹ค.

์ฐฉ๊ฐํ•˜๊ธฐ ์‰ฌ์šด ์ผ€์ด์Šค

๋‹ค๋ฅธ ์–ธ์–ด์˜ try-catch๋ฌธ๊ณผ ๋น„์Šทํ•˜๊ฒŒ go์—์„œ๋Š” defer, panic, recover ํŒจํ„ด์„ ์ด์šฉํ•ด์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ ์ƒํ™ฉ์„ ์ œ์–ด ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

package main

import (
	"fmt"
)

func main() {
	defer a()
	
	panic("panic!")
	
}

func a() {
	r := recover()
	fmt.Println("recoverd: ", r)
	fmt.Println("a called")
}
recoverd:  panic!
a called

Program exited.

๋งŒ์•ฝ ์ง€์—ฐ ํ˜ธ์ถœ ์‹œํ‚ฌ ํ•จ์ˆ˜๊ฐ€ recover์™ธ์— ํ•˜๋‚˜ ๋” ์žˆ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑ ํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

package main

import (
	"fmt"
)

func main() {
	defer b()
	defer a()
	
	panic("panic!")
	
}

func a() {
	r := recover()
	fmt.Println("recoverd: ", r)
	fmt.Println("a called")
}

func b() {
	fmt.Println("b called")
}
recoverd:  panic!
a called
b called

Program exited.

 

์—ฌ๊ธฐ์„œ ๋” ๋‚˜์•„๊ฐ€ ์ฝ”๋“œ๋ฅผ ์ข€ ๋” ๊น”๋”ํ•˜๊ฒŒ ์ •๋ˆํ•˜๊ณ  ์‹ถ์–ด์„œ ์ต๋ช… ํ•จ์ˆ˜๋กœ a()์™€ b()๋ฅผ ๋ฌถ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ

์ด ๊ฒฝ์šฐ๊ฐ€, ์šฐ๋ฆฌ๊ฐ€ ๋ฒ”ํ•˜๊ธฐ ์‰ฌ์šด ์‹ค์ˆ˜๋‹ค.

package main

import (
	"fmt"
)

func main() {
	defer func() {
		a()
		b()
	}()
	
	panic("panic!")
	
}

func a() {
	r := recover()
	fmt.Println("recoverd: ", r)
	fmt.Println("a called")
}

func b() {
	fmt.Println("b called")
}
recoverd:  <nil>
a called
b called
panic: panic!

goroutine 1 [running]:
main.main()
	/tmp/sandbox866461673/main.go:13 +0x60

Program exited: status 2.

์ƒ๊ฐ๋Œ€๋กœ๋ผ๋ฉด ๋‹น์—ฐํžˆ aํ•จ์ˆ˜์—์„œ recover๋ฅผ ํ•˜๊ณ ์žˆ์–ด panic์„ ๋ง‰์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ๋งŒ ๊ฐ™์ง€๋งŒ ์•„๋‹ˆ๋‹ค.

์ต๋ช… ํ•จ์ˆ˜ ํ˜ธ์ถœ -> a ํ˜ธ์ถœ(recover) ๊ณผ ๊ฐ™์ด ํ•จ์ˆ˜๋ฅผ 2๋ฒˆ ํƒ€๊ธฐ ๋•Œ๋ฌธ์— depth๊ฐ€ ๋‹ฌ๋ผ์ ธ panic์„ ๋ง‰์ง€ ๋ชปํ•œ๋‹ค.

 

๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ๋„ panic์„ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•œ๋‹ค.

package main

func main() {
	defer recover()
	panic("panic!")
}
panic: panic!

goroutine 1 [running]:
main.main()
	/tmp/sandbox609508284/main.go:5 +0x60

Program exited: status 2.

 

Go ์˜คํ”ผ์…œ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋งํ•˜๊ณ  ์žˆ๋‹ค.

The return value of recover is nil if any of the following conditions holds:

  • panic's argument was nil;
  • the goroutine is not panicking;
  • recover was not called directly by a deferred function.

recover was not called directly by a deferred function. 

์ฆ‰, recover๋Š” ์ง€์—ฐ๋œ ํ•จ์ˆ˜์— ์˜ํ•ด ์ง์ ‘์ ์œผ๋กœ ํ˜ธ์ถœ๋˜๋ฉด ์•ˆ๋œ๋‹ค๋Š” ๋œป์ด๋‹ค.

 

์—ฌ๊ธฐ์„œ recover๋Š” panic์„ ์ค‘๋‹จ ์‹œํ‚ฌ ํ•จ์ˆ˜์˜ depth๊ฐ€ n์ด๋ผ๋ฉด recover๋Š” n+1์—์„œ ํ˜ธ์ถœํ•ด์•ผ panic์„ ๋ง‰์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์œ ์ถ” ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ข€ ๋” ๊นŠ๊ฒŒ ํŒŒ๋“ค์–ด๊ฐ€ ๋ณด์ž.

 


Panic์ด ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ๋œ๋‹ค๋ฉด?

๋งŒ์•ฝ์— panic 1์ด mainํ•จ์ˆ˜ ๋‚ด์—์„œ ๋ฐœ์ƒํ•˜์˜€๊ณ  defer๋ฌธ์—์„œ panic 2๊ฐ€ ๋˜ ๋ฐœ์ƒ ํ–ˆ๋‹ค๋ฉด recover๋ฅผ ํ•˜๋ฉด panic 1์ด ์žกํž ๊นŒ? panic 2๊ฐ€ ์žกํž๊นŒ?

package main

import "fmt"

func main() {
	defer fmt.Println("program will not crash")

	defer func() {
		fmt.Println( recover() ) // 3
	}()

	defer fmt.Println("now, panic 3 suppresses panic 2")
	defer panic(3)
	defer fmt.Println("now, panic 2 suppresses panic 1")
	defer panic(2)
	panic(1)
}
now, panic 2 suppresses panic 1
now, panic 3 suppresses panic 2
3
program will not crash

defer๋ฌธ์€ ์—ญ๋ฐฉํ–ฅ์œผ๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์žฅ ๋‚˜์ค‘์— ์ง€์—ฐ๋˜์–ด ํ˜ธ์ถœ๋œ panic(3)์ด recover์— ์žกํžˆ๊ฒŒ ๋œ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด, ๋งŒ์•ฝ panic์ด ๋ฐœ์ƒํ•˜๋Š” depth๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅด๋‹ค๋ฉด..?

package main

import "fmt"

func main() { // call depth 0
	defer fmt.Println("to crash, for panic 3 is still active")

	defer func() { // call depth 1
		defer func() { // call depth 2
			fmt.Println( recover() ) // 6
		}()

		// The depth of panic 3 is 0,
		// and the depth of panic 6 is 1.
		defer fmt.Println("now, two active panics: 3 and 6")
		defer panic(6) // will suppress panic 5
		defer panic(5) // will suppress panic 4

		// The following panic will not suppress
		// panic 3,  for they have different depths.
		// The depth of panic 3 is 0.
		// The depth of panic 4 is 1.
		panic(4)
	}()

	defer fmt.Println("now, only panic 3 is active")
	defer panic(3) // will suppress panic 2
	defer panic(2) // will suppress panic 1
	panic(1)
}
now, only panic 3 is active
now, there are two active panics: 3 and 6
6
program will crash, for panic 3 is still active
panic: 1
	panic: 2
	panic: 3

goroutine 1 [running]
...

์œ„์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ถ„์„ํ•˜๋ฉด panic(1) -> panic(2) -> panic(3) -> panic(4) -> panic(5) -> panic(6) ์ˆœ์œผ๋กœ  ํŒจ๋‹‰์ด ๋ฐœ์ƒํ•˜์˜€๊ณ 

1, 2, 3์€ depth 0 ๊ทธ๋ฆฌ๊ณ  4, 5, 6์€ depth 1์—์„œ ๋ฐœ์ƒํ•˜์˜€๋‹ค.

depth 1์—์„œ ๊ฐ€์žฅ ๋‚˜์ค‘์— ๋ฐœ์ƒํ•œ panic(6)์ด 4์™€ 5๋ฅผ ๋ฎ์–ด ์”Œ์› ๊ณ  depth 2์˜ recover์— ์˜ํ•ด ๋ณต๊ตฌ๋˜์—ˆ๋‹ค.

depth 2์˜ recover๋Š” depth 1์˜ panic(6)์„ ๋ณต๊ตฌํ•œ ๊ฒƒ์ด์ง€ depth 0์˜ panic์—๋Š” ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— panic(3)๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๊ณ  ํ”„๋กœ๊ทธ๋žจ์€ ์ข…๋ฃŒ๋œ๋‹ค.


๊ฒฐ๋ก 

recover ํ˜ธ์ถœ์ด ์ ์šฉ๋˜๋Š” ๊ทœ์น™์€ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

ํ•จ์ˆ˜ f์˜ depth๊ฐ€ n์ด๊ณ , f์—์„œ panic์ด ๋ฐœ์ƒํ•˜์—ฌ ๋ณต๊ตฌํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด recover๋Š” ์ง€์—ฐ ํ˜ธ์ถœ๋˜์–ด์•ผ ํ•˜๊ณ  depth๋Š” n+1์ด ๋˜์–ด์•ผ ํ•œ๋‹ค.

 

์ธ์šฉ: https://go101.org/article/panic-and-recover-more.html
 

The Right Places to Call the recover Function - Go 101 (Golang Knowledgebase)

Panic and recover mechanism has been introduced before, and several panic/recover use cases are shown in the last article. We know that a recover call can only take effect if it is invoked in a deferred function call. However, not all recover calls in defe

go101.org

๋ฐ˜์‘ํ˜•