Go의 타이머와 티커에 대한 실용적인 가이드
Olivia Novak
Dev Intern · Leapcell

서문
일상적인 개발에서 일부 작업의 실행을 지연시키거나 주기적으로 실행해야 하는 상황에 직면할 수 있습니다. 이때 Go에서 타이머를 사용해야 합니다.
Go에는 두 가지 유형의 타이머가 있습니다. time.Timer(일회성 타이머)와 time.Ticker(주기적 타이머)입니다. 이 글에서는 두 가지 유형의 타이머를 모두 소개합니다.
타이머: 일회성 타이머
타이머는 미래의 특정 시간에 한 번 작업을 수행하는 데 사용되는 일회성 타이머입니다.
기본 사용법
타이머를 만드는 방법에는 두 가지가 있습니다.
NewTimer(d Duration) *Timer: 이 함수는 타이머가 만료되기 전에 얼마나 기다려야 하는지를 나타내는time.Duration유형의 매개변수d(시간 간격)를 허용합니다.NewTimer는 내부적으로 채널C를 유지 관리하는 새 타이머를 반환합니다. 타이머가 작동하면 현재 시간이 채널C로 전송됩니다.AfterFunc(d Duration, f func()) *Timer: 지정된 시간 간격d와 콜백 함수f를 허용합니다. 이 함수는 새 타이머를 반환하고 타이머가 만료되면 채널C를 통해 신호를 보내는 대신f를 직접 호출합니다. 타이머의Stop메서드를 호출하면 타이머가 중지되고f의 실행이 취소됩니다.
다음 코드는 NewTimer와 AfterFunc를 사용하여 타이머를 만들고 기본적으로 사용하는 방법을 보여줍니다.
package main import ( "fmt" "time" ) func main() { // NewTimer를 사용하여 타이머를 만듭니다. timer := time.NewTimer(time.Second) go func() { select { case <-timer.C: fmt.Println("timer fired!") } }() // AfterFunc를 사용하여 다른 타이머를 만듭니다. time.AfterFunc(time.Second, func() { fmt.Println("timer2 fired!") }) // 기본 고루틴은 타이머 출력을 볼 수 있도록 2초 동안 기다립니다. time.Sleep(time.Second * 2) }
위 코드의 출력은 다음과 같습니다.
timer fired! timer2 fired!
다음은 코드에 대한 단계별 설명입니다.
NewTimer를 사용하여 타이머를 만든 다음 새 고루틴에서C속성을 수신하여 타이머가 작동할 때까지 기다립니다.AfterFunc를 사용하여 다른 타이머를 만들고 타이머 만료 이벤트를 처리할 콜백 함수를 지정합니다.- 기본 고루틴은 타이머의 작동 정보를 인쇄할 수 있을 만큼 충분히 기다립니다.
메서드 세부 정보
Reset
Reset(d Duration) bool: 이 메서드는 타이머의 만료 시간을 재설정하는 데 사용되며 기본적으로 다시 활성화합니다. 만료되기 전에 타이머가 얼마나 기다려야 하는지를 나타내는 time.Duration 유형의 매개변수 d를 허용합니다.
또한 이 메서드는 bool 값을 반환합니다.
- 타이머가 활성 상태이면
true를 반환합니다. - 타이머가 이미 만료되었거나 중지된 경우
false를 반환합니다(참고:false는 재설정이 실패했음을 의미하지 않으며 타이머의 현재 상태만 나타냅니다).
다음은 코드 예제입니다.
package main import ( "fmt" "time" ) func main() { timer := time.NewTimer(5 * time.Second) // 첫 번째 재설정: 타이머가 활성 상태이므로 true를 반환합니다. b := timer.Reset(1 * time.Second) fmt.Println(b) // true second := time.Now().Second() select { case t := <-timer.C: fmt.Println(t.Second() - second) // 1s } // 두 번째 재설정: 타이머가 이미 만료되었으므로 false를 반환합니다. b = timer.Reset(2 * time.Second) fmt.Println(b) // false second = time.Now().Second() select { case t := <-timer.C: fmt.Println(t.Second() - second) // 2s } }
코드의 출력은 다음과 같습니다.
true 1 false 2
단계별 설명:
- 5초 후에 만료되도록 설정된 타이머를 만듭니다.
Reset메서드를 즉시 호출하여 1초 후에 만료되도록 설정합니다. 타이머가 아직 활성 상태(만료되지 않음)이므로Reset은true를 반환합니다.select문은 타이머가 만료될 때까지 기다리고 실제 경과된 시간(약 1초)을 인쇄합니다.- 타이머가 다시 재설정되어 이번에는 2초 후에 만료되도록 설정됩니다. 타이머가 이미 만료되었으므로
Reset은false를 반환합니다. select문은 타이머가 만료될 때까지 다시 기다리고 경과된 시간(약 2초)을 인쇄합니다.
Stop
Stop() bool: 이 메서드는 타이머를 중지하는 데 사용됩니다. 타이머가 성공적으로 중지되면 true를 반환합니다. 타이머가 이미 만료되었거나 중지된 경우 false를 반환합니다. 참고: Stop 작업은 채널 C를 닫지 않습니다.
다음은 코드 예제입니다.
package main import ( "fmt" "time" ) func main() { timer := time.NewTimer(3 * time.Second) // 타이머가 작동하기 전에 중지하므로 true를 반환합니다. stop := timer.Stop() fmt.Println(stop) // true stop = timer.Stop() // 타이머를 다시 중지하면 이미 중지되었으므로 false를 반환합니다. fmt.Println(stop) // false }
출력은 다음과 같습니다.
true false
단계별 설명:
- 3초 후에 작동하도록 설정된 타이머를 만듭니다.
Stop메서드를 즉시 호출하여 타이머를 중지합니다. 타이머가 아직 작동하지 않았으므로Stop은true를 반환합니다.Stop을 다시 호출하여 동일한 타이머를 중지하려고 시도합니다. 이미 중지되었으므로 이번에는Stop이false를 반환합니다.
Ticker: 주기적 타이머
Ticker는 고정된 간격으로 작업을 반복적으로 실행하는 데 사용되는 주기적 타이머입니다. 매 간격마다 현재 시간을 채널로 보냅니다.
기본 사용법
NewTicker 함수를 사용하여 새 Ticker 객체를 만들 수 있습니다. 이 함수는 time.Duration 매개변수 d(간격)를 허용합니다.
다음은 예제입니다.
package main import ( "context" "fmt" "time" ) func main() { ticker := time.NewTicker(time.Second) defer ticker.Stop() timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*5) defer cancelFunc() go func() { for { select { case <-timeout.Done(): fmt.Println("timeout done") return case <-ticker.C: fmt.Println("ticker fired!") } } }() // 기본 고루틴은 타이머 출력을 볼 수 있도록 7초 동안 기다립니다. time.Sleep(time.Second * 7) }
코드의 출력은 다음과 같습니다.
ticker fired! ticker fired! ticker fired! ticker fired! ticker fired! timeout done
단계별 설명:
- 매초마다 작동하는 타이머를 만듭니다. 함수가 끝날 때 타이머가 정리되도록
defer ticker.Stop()을 사용합니다. - 5초 후에 시간 초과되는 컨텍스트를 만듭니다.
cancelFunc는 종료하기 전에 컨텍스트를 정리하는 데 사용됩니다. - 새 고루틴에서
select문은 타이머의 채널(ticker.C)과 컨텍스트의 완료 채널(timeout.Done())의 두 채널을 수신합니다. 타이머가 매초마다 작동하면 메시지를 인쇄합니다. 컨텍스트가 시간 초과되면(5초 후) 시간 초과 메시지를 인쇄하고 반환하여 고루틴을 종료합니다. - 기본 고루틴은
time.Sleep(time.Second * 7)을 사용하여 7초 동안 기다려 타이머 작동 및 시간 초과 이벤트를 모두 관찰할 수 있도록 합니다.
select로 ticker.C를 수신할 뿐만 아니라 for range 루프를 사용할 수도 있습니다.
for range ticker.C {}
참고: Stop 메서드로 Ticker를 중지하더라도 해당 채널 C는 닫히지 않습니다. 즉, for select 또는 for range를 사용하여 ticker.C를 수신하든 루프를 종료하는 다른 메커니즘(예: 컨텍스트 사용)이 필요합니다.
메서드 세부 정보
Reset
Reset(d Duration) 메서드는 티커를 중지하고 해당 기간을 지정된 기간으로 재설정하는 데 사용됩니다. 다음 틱은 새 기간이 경과한 후에 발생합니다. 새 간격을 나타내는 time.Duration 유형의 매개변수 d를 허용합니다. 이 매개변수는 0보다 커야 합니다. 그렇지 않으면 Reset 메서드가 내부적으로 패닉 상태가 됩니다.
다음은 예제입니다.
package main import ( "time" ) func main() { ticker := time.NewTicker(5 * time.Second) defer ticker.Stop() // 티커를 재설정합니다. ticker.Reset(1 * time.Second) second := time.Now().Second() for t := range ticker.C { // 1s fmt.Printf("Interval: %d seconds", t.Second()-second) break } }
코드의 출력은 다음과 같습니다.
Interval: 1 seconds
단계별 설명:
- 5초마다 작동하는 time.Ticker를 만듭니다.
Reset메서드를 사용하여 간격을 5초에서 1초로 변경합니다.- 단일 루프에서 간격을 인쇄합니다. 예상 결과는 1초입니다.
Stop
Stop() 메서드는 티커를 중지하는 데 사용됩니다. Stop을 호출한 후에는 더 이상 틱이 채널 C로 전송되지 않습니다. 참고: Stop 작업은 채널 C를 닫지 않습니다.
다음은 예제입니다.
package main import ( "fmt" "time" ) func main() { ticker := time.NewTicker(time.Second) quit := make(chan struct{}) // 종료 채널을 만듭니다. go func() { for { select { case <-ticker.C: fmt.Println("Ticker fired!") case <-quit: fmt.Println("Goroutine stopped!") return // 종료 신호를 받으면 루프를 종료합니다. } } }() time.Sleep(time.Second * 3) ticker.Stop() // 티커를 중지합니다. close(quit) // 종료 신호를 보냅니다. fmt.Println("Ticker stopped!") }
출력은 다음과 같습니다.
Ticker fired! Ticker fired! Ticker fired! Goroutine stopped! Ticker stopped!
- 매초마다 작동하는 time.Ticker 객체를 만듭니다. 동시에 실행 중인 고루틴에 중지 신호를 보내는 데 사용되는
chan struct{}유형의 종료 채널이 도입됩니다. - 새 고루틴을 시작합니다. 이 고루틴에서 for-select 루프는 티커 작동(
case <-ticker.C)과 종료 신호(case <-quit)의 두 이벤트를 수신합니다. 티커가 작동할 때마다 메시지를 인쇄합니다. 종료 신호를 받으면 메시지를 인쇄하고 루프를 종료합니다. - 기본 고루틴에서
time.Sleep(time.Second * 3)은 3초의 대기 시간을 시뮬레이션합니다. 이 시간 동안 티커가 몇 번 작동합니다. - 기본 고루틴은
Stop을 호출하여 티커를 중지한 다음 종료 채널을 닫습니다. 고루틴은 종료 신호를 받고 메시지를 인쇄하고 루프를 종료합니다.
Stop 메서드는 채널 C를 닫지 않으므로 다른 수단(예: 종료 신호)을 사용하여 리소스를 정리해야 합니다.
타이머와 티커의 주요 차이점
사용법:
- 타이머는 단일 지연 후에 실행되는 작업에 사용됩니다.
- 티커는 반복적으로 실행해야 하는 작업에 사용됩니다.
동작적 특성:
- 타이머는 지정된 지연 후에 한 번 활성화되어 단일 시간 값을 채널로 보냅니다.
- 티커는 지정된 간격으로 주기적으로 활성화되어 반복된 시간 값을 채널로 보냅니다.
제어 가능성:
- 타이머는 재설정(
Reset메서드)하고 중지(Stop메서드)할 수 있습니다.Reset은 타이머의 작동 시간을 변경하는 데 사용됩니다. - 티커도 재설정(
Reset메서드)하고 중지(Stop메서드)할 수 있습니다.Reset은 티커가 작동하는 간격을 변경하는 데 사용됩니다.
종료 작업:
- 타이머의
Stop메서드는 타이머가 작동하지 않도록 하는 데 사용됩니다. 타이머가 이미 작동한 경우Stop은 이미 채널로 전송된 시간 값을 제거하지 않습니다. - 티커의
Stop메서드는 주기적 활성화를 중지하는 데 사용됩니다. 중지되면 더 이상 새 값이 채널로 전송되지 않습니다.
참고 사항
- 타이머와 티커 모두
Stop메서드를 호출해도 해당C채널은 닫히지 않습니다. 이 채널을 수신하는 다른 고루틴이 있는 경우 잠재적인 메모리 누수를 방지하려면 해당 고루틴을 수동으로 종료해야 합니다. 일반적으로 이러한 리소스 정리는context를 사용하거나 종료 신호(채널로 구현됨)로 처리할 수 있습니다. - 티커가 작업을 완료한 후에는 관련 리소스를 해제하고 메모리 누수를 방지하기 위해
Stop메서드를 호출해야 합니다. 제때에 티커를 중지하지 않으면 지속적인 리소스 점유가 발생할 수 있습니다.
요약
이 글에서는 Go의 타이머와 티커를 심층적으로 살펴보고, 이를 생성하는 방법, 기본 사용법, 관련 메서드를 자세히 소개했습니다. 또한 이 글에서는 이러한 두 가지 유형의 타이머 간의 주요 차이점을 요약하고 이를 사용할 때 염두에 두어야 할 고려 사항을 강조합니다.
Go 코드를 작성할 때는 애플리케이션 시나리오에 따라 적절한 타이머를 선택해야 합니다. 동시에 특히 타이머 작업을 마친 후 즉시 리소스를 해제하는 등 모범 사례를 따르는 것이 중요하며, 이는 잠재적인 메모리 누수를 방지하는 데 매우 중요합니다.
Go 프로젝트 호스팅을 위한 최고의 선택, Leapcell입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무제한 프로젝트를 무료로 배포하세요.
- 사용량에 대해서만 지불하세요. 요청도 없고, 요금도 없습니다.
탁월한 비용 효율성
- 유휴 요금 없이 사용한 만큼만 지불하세요.
- 예: 평균 응답 시간 60ms에서 25달러로 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 지표 및 로깅.
간편한 확장성 및 고성능
- 쉬운 동시성 처리를 위한 자동 확장.
- 운영 오버헤드가 없으므로 구축에만 집중하세요.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ



