backend

Go 언어 defer 정리: 자원 관리와 실행 순서 이해하기

mirabo01 2026. 1. 14. 21:57

에러 처리까지 익혔다면, 이제 Go 코드에서 거의 항상 함께 등장하는
defer 문을 정리할 차례다.

defer는 문법 자체는 단순하지만,
언제 실행되는지, 어떤 순서로 동작하는지를 정확히 이해하지 않으면
의도와 다른 코드가 만들어지기 쉽다.

이 글에서는

  • defer의 기본 동작 방식
  • 자원 관리에서의 사용 패턴
  • 실제로 자주 실수하는 포인트

를 중심으로 정리한다.


defer란 무엇인가

defer는 함수 종료 시점에 실행될 코드를 등록하는 키워드다.

defer fmt.Println("end")
fmt.Println("start")

실행 결과는 다음과 같다.

start
end
  • defer로 등록된 코드는 현재 함수가 return되기 직전에 실행된다
  • 정상 종료든, 에러 반환이든 동일하게 실행된다

이 특성 때문에, defer는 자원 정리에 매우 잘 어울린다.

[이미지: Go defer 실행 시점 흐름]


자원 관리에서의 defer 패턴

가장 대표적인 사용 예는 파일이나 네트워크 자원 정리다.

file, err := os.Open("test.txt")
if err != nil {
    return err
}
defer file.Close()
  • 자원 획득 직후 defer로 정리 로직 등록
  • 중간에 return이 여러 개 있어도 안전하다

실제로 써보면,
“나중에 닫아야지”라는 생각 자체를 코드에서 제거해준다.


defer는 즉시 평가되고, 나중에 실행된다

defer에서 자주 헷갈리는 부분이다.

x := 10
defer fmt.Println(x)
x = 20

출력 결과는 10이다.

  • defer에 전달되는 인자는 defer 선언 시점에 평가
  • 실행만 함수 종료 시점에 된다

이 특성을 이해하지 못하면,
로그나 디버깅 코드에서 의도와 다른 결과를 보게 된다.


defer의 실행 순서 (LIFO)

defer는 스택 구조로 실행된다.

defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")

실행 결과:

3
2
1
  • 마지막에 등록한 defer가 가장 먼저 실행된다
  • 흔히 LIFO(Last In, First Out)라고 설명한다

[이미지: Go defer 스택 실행 순서]

이 특성 덕분에,
복잡한 자원 정리도 자연스럽게 순서를 맞출 수 있다.


반복문 안에서 defer 사용 시 주의점

for _, f := range files {
    defer f.Close()
}

이 코드는 문법적으로는 문제가 없지만,
모든 Close()가 함수 종료 시점에 한꺼번에 실행된다.

⚠️ 주의할 점

  • 파일 개수가 많으면 자원 점유 시간이 길어진다
  • 루프마다 즉시 닫아야 하는 경우에는 defer가 맞지 않을 수 있다

실무에서는 다음과 같이 분리하는 경우도 많다.

for _, name := range files {
    func() {
        f, _ := os.Open(name)
        defer f.Close()
        // 작업 처리
    }()
}

defer와 panic / recover의 관계

panic이 발생해도 defer는 실행된다.

defer fmt.Println("cleanup")
panic("error occurred")
  • panic 발생
  • defer 실행
  • 이후 프로그램 종료 (recover가 없다면)

이 특성 때문에,
defer는 최후의 안전장치 역할도 한다.

[이미지: panic 발생 시 defer 실행 흐름]


defer를 남용하면 안 되는 이유

defer는 편리하지만, 비용이 없는 문법은 아니다.

  • 함수 호출 비용이 발생한다
  • 매우 빈번한 호출 구간에서는 성능에 영향을 줄 수 있다

그래서 실무에서는 보통 이런 기준을 둔다.

  • 파일, 락, 커넥션 정리 → defer 사용
  • 아주 짧고 빈번한 반복 처리 → 직접 호출 고려

대부분의 일반적인 코드에서는
가독성과 안정성을 위해 defer를 사용하는 쪽이 낫다.


정리

defer는
자원 정리 코드를 실수 없이 작성하게 만드는 도구다.

  • 함수 종료 시점에 실행된다
  • 인자는 선언 시점에 평가된다
  • 여러 개면 역순으로 실행된다

이 특성을 정확히 이해하면,
Go 코드에서 자원 관리와 예외 상황 처리가 훨씬 단순해진다.


이런 경우에 특히 유용하다

  • 파일, 네트워크, 락 등 자원 관리
  • 에러가 여러 경로로 반환되는 함수
  • panic 상황에서도 정리가 필요한 코드

반대로,
아주 성능에 민감한 루프 내부에서는
사용 여부를 한 번 더 고민해보는 게 좋다.

다음 글에서는 defer와 함께 자주 쓰이는 주제인
goroutine과 동시성 기초로 넘어가면 흐름이 자연스럽다.