🔥 defer와 함수 호출 스택
343자
5분
Go 언어에서 defer
키워드를 사용하면 함수나 메서드의 실행을 지연시킬 수 있어요. 지연된 함수 호출은 스택(stack)에 쌓이게 되죠. 그리고 현재 함수가 리턴할 때, 스택에 쌓인 지연 호출들이 LIFO(Last-In-First-Out) 순서로 실행된답니다.
defer
에 대해 더 자세히 알고 싶다면 이 블로그 포스트를 읽어보세요.
자, 그럼 예제 코드를 통해 defer
가 어떻게 동작하는지 알아볼까요?
package main import "fmt" func main() { fmt.Println("카운팅 시작") // 가장 먼저 출력됩니다. for i := 0; i < 10; i++ { defer fmt.Println(i) // 루프를 돌면서 지연 호출을 스택에 쌓습니다. } fmt.Println("끝!") // 카운팅이 끝난 후 출력됩니다. }
go
위 코드를 실행하면 다음과 같은 출력 결과를 얻을 수 있어요.
카운팅 시작 끝! 9 8 7 6 5 4 3 2 1 0
text
코드의 실행 흐름을 단계별로 살펴보면:
main()
함수가 실행되면서 가장 먼저 "카운팅 시작"이 출력돼요.- 그 다음,
for
루프가 시작되죠. 루프 변수i
는 0부터 9까지 증가하면서 반복합니다. - 루프 내에서
defer fmt.Println(i)
가 호출되는데, 이는 해당 출력문의 실행을 지연시키고 스택에 쌓아둡니다. 즉, 루프를 돌면서defer
로 지정된 출력문들이 차례로 스택에 쌓이게 되는 거예요. - 루프가 끝나면 "끝!"이 출력됩니다. 아직
defer
로 지정된 출력문들은 실행되지 않은 상태죠. main()
함수가 리턴하기 직전, 스택에 쌓여있던 지연 호출들이 LIFO 순서로 실행됩니다. 가장 마지막에 스택에 쌓인defer fmt.Println(9)
가 가장 먼저 실행되고, 그 다음은defer fmt.Println(8)
, ... 이런 식으로 역순으로 실행되어 9부터 0까지 차례로 출력되는 거죠.
이렇게 defer
를 사용하면 현재 함수가 리턴하기 직전에 실행되어야 할 작업들을 깔끔하게 정리할 수 있답니다. 자원 해제, 클린업 작업 등에 유용하게 사용할 수 있어요.
defer
의 또 다른 장점은 에러 처리에 있어요. 에러가 발생했을 때 defer
로 지정된 코드는 항상 실행이 보장되거든요. 이를 이용해 에러 발생 시 자원 해제나 롤백 등의 작업을 안전하게 수행할 수 있습니다.
func doSomething() error { file, err := os.Open("file.txt") // 파일을 엽니다. if err != nil { return err } defer file.Close() // 함수 종료 직전 파일을 닫습니다. 에러 발생해도 실행 보장! // 파일 처리 작업 수행... // ... return nil }
go
이런 식으로 에러가 발생하더라도 defer
로 지정된 file.Close()
는 반드시 실행되므로, 열어둔 파일 핸들이 제대로 닫힘을 보장할 수 있는 거예요.
지금까지 defer
에 대해 알아봤는데요. 함수 리턴 직전에 실행되어야 할 작업이 있다면 defer
를 적극 활용해 보세요.