Quick Comparison
time.Sleep(500 * time.Millisecond)
timer := time.NewTimer(2 * time.Second)
defer timer.Stop()
ticker := time.NewTicker(time.Second)
defer ticker.Stop()Go의 시간 처리는 한 번 기다림, 한 번 알림, 반복 알림, 작업 수명 제한을 나눠서 고릅니다.
갈리는 기준
| 상황 | 선택 |
|---|---|
| 지금 goroutine을 잠깐 멈춤 | time.Sleep |
| 한 번만 나중에 알림 | time.NewTimer |
| 일정 간격 반복 실행 | time.NewTicker |
| 요청이나 작업 전체 시간 제한 | context.WithTimeout |
| 간단한 select timeout | time.After |
time.Sleep은 현재 goroutine을 멈추기만 합니다. 취소 신호를 받을 필요가 있거나 다른 channel과 같이 기다려야 하면 select와 timer/context를 쓰는 편이 낫습니다.
기본 흐름
한 번 기다릴 때는 timer를 쓴다
timer := time.NewTimer(2 * time.Second)
defer timer.Stop()
select {
case <-timer.C:
return nil
case <-ctx.Done():
return ctx.Err()
}Timer는 channel로 완료 신호를 받기 때문에 select와 잘 맞습니다. 취소 가능한 대기 흐름에서는 Sleep보다 timer가 구조를 더 분명하게 만듭니다.
반복 작업은 ticker를 쓴다
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
refresh()
case <-ctx.Done():
return ctx.Err()
}
}Ticker는 멈출 때까지 주기적으로 신호를 보냅니다. 더 이상 쓰지 않을 때는 Stop()으로 내부 리소스를 정리합니다.
Stop()은 ticker.C를 닫지 않습니다. 따라서 for range ticker.C만으로는 종료되지 않으며, 위 예제처럼 ctx.Done()이나 별도 종료 channel을 같이 선택해야 합니다. receiver가 느리면 ticker는 모든 tick을 쌓아 두지 않고 일부 tick을 건너뛸 수 있으므로, "매 간격마다 정확히 한 번" 실행해야 하는 작업에는 scheduler나 누적 시간 계산을 따로 설계합니다.
타임아웃
작업 수명은 context로 제한한다
ctx, cancel := context.WithTimeout(parent, 2*time.Second)
defer cancel()
if err := callAPI(ctx); err != nil {
return err
}DB, HTTP client, 외부 API 호출처럼 작업 전체에 시간 제한이 필요하면 context.WithTimeout이 기준입니다. timeout은 단순 대기 시간이 아니라 작업의 수명 정책입니다.
time.After는 짧은 select에만 쓴다
select {
case result := <-done:
return result
case <-time.After(2 * time.Second):
return errors.New("timeout")
}time.After는 간단하지만 호출할 때마다 timer를 만듭니다. 최신 Go 런타임은 참조를 잃은 timer를 회수할 수 있지만, 루프에서 timeout을 재설정하거나 취소·중지를 명시해야 하는 흐름에는 Timer를 직접 만들어 Reset과 Stop을 관리하는 편이 의도가 분명합니다.
Timer.Stop()도 channel을 닫지 않습니다. timer가 이미 발화했는지와 다음 대기를 재설정해야 하는지에 따라 처리 순서가 달라지므로, 오래된 Go 버전까지 지원하는 재사용 코드는 해당 버전의 Timer.Reset 문서를 함께 확인합니다.
주의할 점
Ticker를 만들고 멈추지 않으면 불필요한 작업이 계속 살아 있을 수 있습니다. 또한 timeout이 필요한 함수에서 time.Sleep만 쓰면 취소 전파가 막힙니다. 요청, DB, 외부 호출처럼 수명이 있는 작업은 context를 먼저 기준으로 잡는 편이 안전합니다.
참고 링크
2 sources