Timeline — 기초, 애플리케이션에서 애니메이션 시간 관리

저자: IT Sectr 게시일: 2026-03-02 읽는 시간: 8 분
Timeline(타임라인)은 애니메이션이 지속되는 시간, 단계가 실행되는 순서, 시간이 진행에 매핑되는 방식을 결정하는 기본적인 애니메이션 개념입니다. 모바일 앱 개발에서 타임라인은 duration, delay, repeat 및 타임스탬프를 통해 관리됩니다. iOS에서는 CACurrentMediaTime 및 CAMediaTiming을 통해 애니메이션 타임라인이 구현되고, Android에서는 duration, startDelay 및 repeatCount 매개변수와 함께 ValueAnimator 및 Animator가 사용됩니다. Apple Documentation에 따르면, 애니메이션 동기화를 위해서는 정확한 타임라인 제어가 매우 중요합니다. 16ms(60fps에서 1프레임)의 동기화 불일치도 사용자에게 덜컹거림으로 인식됩니다. IT Sectr에서는 Android에서 ValueAnimator를, iOS에서 CABasicAnimation(CAMediaTiming 포함)을 통해 타임라인 사용을 표준화하여 모든 프로젝트에서 균일한 시간 관리를 보장합니다.

핵심 포인트

  • Timeline — 애니메이션 시간 관리 개념: duration, delay, repeat, 시간을 진행에 매핑.
  • CAMediaTiming — 타임라인 관리를 위한 iOS Core Animation 프로토콜: duration, beginTime, repeatCount, speed, timeOffset.
  • ValueAnimator — duration, startDelay, repeatCount를 통해 애니메이션 타임라인 작업을 위한 중앙 Android 클래스.
  • CACurrentMediaTime — 초 단위의 절대 시간을 가져오는 iOS 함수, 동기화에 사용.
  • TimeInterval — iOS의 데이터 타입(Double), 애니메이션 계산을 위한 시간 간격을 나타냄.

Timeline이란?

Timeline은 애니메이션이 시작되는 시기, 지속 시간, 시간 진행이 애니메이션 매개변수의 변화로 변환되는 방식을 정의하는 애니메이션 시간 모델입니다. 각 프레임이 수동으로 설정되는 프레임별 애니메이션과 달리, 타임라인 애니메이션은 매개변수로 설명됩니다: duration — 총 지속 시간, startDelay — 시작 전 지연, repeatCount — 반복 횟수, timingFunction — 속도 곡선.

타임라인은 정규화된 공간 [0, 1] — 로컬 시간 또는 진행률에서 작동합니다. 0 값은 애니메이션 시작에, 1은 완료에 해당합니다. 이징 함수는 선형 로컬 시간을 실제 애니메이션 진행률로 변환합니다. repeatCount > 1이면 타임라인이 순환(RESTART) 또는 반전(REVERSE)됩니다. iOS에서 CAMediaTiming은 speed(재생 속도, >1은 가속) 및 timeOffset(타임라인 이동)을 추가합니다. Android에서 Animator는 setCurrentPlayTime()을 사용하여 타임라인 위치를 직접 제어합니다. Material Design에 따르면, 100ms 미만의 애니메이션은 애니메이션으로 인식되지 않으며, 500ms를 초과하면 사용자를 짜증나게 하기 시작합니다. 인터페이스 애니메이션의 최적 지속 시간은 200~350ms입니다.

iOS의 Timeline: CAMediaTiming 및 CACurrentMediaTime

iOS에서 애니메이션 타임라인 관리는 CAAnimation 및 CALayer에 의해 구현되는 CAMediaTiming 프로토콜을 통해 처리됩니다. 이 프로토콜은 duration(초), beginTime(상위 타임라인 기준 시작 시간), repeatCount(반복 횟수), repeatDuration(총 반복 시간), autoreverses(자동 반전), fillMode(애니메이션 전/후 동작), speed(재생 속도) 및 timeOffset(타임라인 이동) 속성을 제공합니다.

CACurrentMediaTime()은 마지막 장치 재부팅 이후 초 단위의 절대 시간을 반환하는 함수입니다. 여러 애니메이션을 동기화하기 위한 기본 타임스탬프로 사용됩니다: beginTime = CACurrentMediaTime() + offset을 설정하여 정확한 지연으로 애니메이션 그룹을 시작합니다. CAMediaTiming은 타임라인 계층 구조도 지원합니다 — 상위 레이어는 speed 속성을 통해 모든 하위 애니메이션을 가속/감속할 수 있습니다. UIKit에서 UIViewPropertyAnimator(iOS 10+)는 duration, delay 속성 및 startAnimation/pauseAnimation/stopAnimation 콜백을 통해 타임라인을 추상화합니다. SwiftUI에서 Animation 구조체는 애니메이션 사양의 일부로 duration을 제공하지만 직접적인 타임라인 제어는 숨겨져 있습니다 — 사용자 정의 타임라인을 위해 사용자 정의 스케줄러와 함께 TimelineView(iOS 15+)를 사용하세요.

Android의 Timeline: Animator 및 duration

Android에서 타임라인 관리는 Animator 클래스와 그 하위 클래스(ValueAnimator, ObjectAnimator)에 중앙 집중화되어 있습니다. 주요 타임라인 속성은 duration(밀리초), startDelay(시작 전 지연), repeatCount(반복 횟수), repeatMode(RESTART 또는 REVERSE)입니다. setCurrentPlayTime(long) 메서드는 타임라인의 임의 위치로 이동할 수 있게 합니다.

ValueAnimator는 추상화 레이어를 추가합니다: duration을 기반으로 0에서 1(정규화된 시간)까지 값을 생성하고 TimeInterpolator에 전달합니다. AnimatorSet은 순차적(playSequentially) 또는 병렬(playTogether) 실행으로 여러 Animator를 단일 타임라인으로 결합할 수 있습니다. Jetpack Compose에서 타임라인은 AnimationSpec을 통해 관리됩니다: tween(durationMillis, delayMillis, easing), spring(dampingRatio, stiffness) 및 keyframes(각 세그먼트에 대한 사용자 정의 easing이 있는 타임라인 지점). Compose AnimatedContent는 transitionSpec에 타임라인을 사용합니다 — 개발자는 시간 오프셋으로 콘텐츠가 나타나고 사라지는 방식을 설명합니다. Android Performance에 따르면, 60fps의 경우 애니메이션 타임라인은 16ms(vsync 주기)의 배수여야 합니다 — 잘못된 지속 시간은 디스플레이 재생 빈도와의 불일치로 인해 프레임 드롭을 유발할 수 있습니다.

시간 내 애니메이션 구성

여러 애니메이션을 구성할 때 타임라인이 특히 중요해집니다. 세 가지 기본 패턴: 캐스케이드 — 애니메이션이 오프셋과 함께 순차적으로 시작(각 다음은 이전에서 오프셋 후 시작), 병렬 — 모든 애니메이션이 동일한 타임라인으로 동시에 시작, 스태거 — 애니메이션이 겹쳐서 시작(두 번째가 첫 번째 완료 전에 시작).

iOS에서 캐스케이드는 각 CAAnimation의 beginTime = 이전 beginTime + 이전 duration을 통해 구현됩니다. 스태거의 경우 중첩된 애니메이션에서 서로 다른 beginTime 값을 가진 CAAnimationGroup이 사용됩니다. Android에서 AnimatorSet은 playSequentially와 playTogether를 지원하며, 각 Animator에 startDelay를 설정하여 스태거를 만듭니다. Compose에서 AnimatedVisibility는 enter/exitTransition과 함께 staggerChildren 변형에서 스태거를 사용하여 목록 항목의 계단식 표시를 구현합니다. WWDC 및 Google I/O 2024에서 두 플랫폼 모두 타임라인 시각화 도구를 발표했습니다: Xcode Animations Inspector 및 Android Studio Animations Timeline — 실제 타임라인과 드롭된 프레임을 실시간으로 보여줍니다.

코드 예제

iOS: CAMediaTiming을 사용한 CABasicAnimation

사용자 정의 타임라인이 있는 애니메이션 예제: 0.3초 지연, 0.6초 지속 시간, 자동 반전 및 무한 반복. CAMediaTiming이 모든 시간 매개변수를 제어합니다.

swift
import UIKit

func breathingAnimation(layer: CALayer) {
    let animation = CABasicAnimation(keyPath: "opacity")
    animation.duration = 0.6
    animation.beginTime = CACurrentMediaTime() + 0.3
    animation.fromValue = 1.0
    animation.toValue = 0.3
    animation.autoreverses = true
    animation.repeatCount = .infinity
    animation.speed = 1.0
    animation.fillMode = .both

    layer.add(animation, forKey: "breathing")
}

beginTime = CACurrentMediaTime() + 0.3은 300ms 지연을 제공합니다. autoreverses = true는 각 사이클 후 애니메이션을 역방향으로 실행합니다. repeatCount = .infinity는 “호흡” 효과의 무한 루프를 만듭니다. speed = 1.0 — 일반 속도, 2.0 값으로 애니메이션을 2배 가속합니다.

iOS: 사용자 정의 타임라인을 위한 TimelineView

SwiftUI TimelineView(iOS 15+)는 사용자 정의 애니메이션을 위한 시스템 타임라인에 대한 액세스를 제공합니다. 뷰는 스케줄러의 각 틱에서 업데이트됩니다.

swift
import SwiftUI

struct TimelineViewExample: View {
    var body: some View {
        TimelineView(.periodic(from: .now, by: 0.1)) { context in
            let seconds = context.date.timeIntervalSinceReferenceDate
            let phase = sin(seconds * 2 * .pi)

            Circle()
                .fill(Color.blue)
                .frame(width: 60 + 30 * phase)
                .animation(.linear(duration: 0), value: phase)
        }
    }
}

TimelineView와 .periodic 스케줄러는 0.1초마다 틱을 생성합니다. 클로저에서 context.date는 현재 시간이며 사인파 위상으로 변환됩니다. 원의 크기는 1Hz 주파수로 맥동합니다. TimelineView는 Animation API 없이 SwiftUI에서 자체 타임라인으로 애니메이션을 만드는 유일한 방법입니다.

Android: 사용자 정의 타임라인을 사용한 ValueAnimator

전체 타임라인 제어가 포함된 ValueAnimator: 지연, 지속 시간, 반복. setCurrentPlayTime 메서드는 타임라인을 따라 이동할 수 있습니다.

kotlin
import android.animation.ValueAnimator
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.View

fun View.timelineAnimation() {
    ValueAnimator.ofFloat(0f, 1f).apply {
        duration = 400
        startDelay = 200
        repeatCount = 2
        repeatMode = ValueAnimator.REVERSE
        interpolator = AccelerateDecelerateInterpolator()

        addUpdateListener { animator ->
            val progress = animator.animatedFraction
            alpha = progress
            translationX = progress * 100f
        }

        start()
    }
}

startDelay = 200ms — 시작 전 지연. repeatMode = REVERSE는 애니메이션을 초기 값으로 되돌립니다. animatedFraction는 repeatMode를 고려하여 0에서 1까지의 진행률을 제공합니다. addUpdateListener는 매 프레임(보통 16ms)마다 실행됩니다. 일시 중지하려면 pause() / resume()을 사용하세요.

Android: 타임라인 구성을 위한 AnimatorSet

AnimatorSet는 여러 애니메이션에서 복잡한 타임라인을 만들 수 있습니다. 예제는 겹침(스태거)이 있는 순차적 실행을 보여줍니다.

kotlin
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.view.View

fun View.staggerAnimation() {
    val fadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f).apply {
        duration = 300
    }
    val slideUp = ObjectAnimator.ofFloat(this, "translationY", 100f, 0f).apply {
        duration = 400
        startDelay = 100
    }
    val scale = ObjectAnimator.ofFloat(this, "scaleX", 0.8f, 1f).apply {
        duration = 250
        startDelay = 200
    }

    AnimatorSet().apply {
        playTogether(fadeIn, slideUp, scale)
        start()
    }
}

playTogether는 모든 애니메이션을 병렬로 시작하지만 각각 고유한 startDelay가 있습니다 — 이는 스태거 효과를 만듭니다: fadeIn이 먼저 시작(0ms), slideUp이 100ms 후, scale이 200ms 후. 결과 타임라인: 0 — 100 — 200 — 600ms. 순차적 실행을 위해서는 playSequentially를 사용하세요.

자주 묻는 질문

Android에서 Animator와 ValueAnimator의 차이점은 무엇인가요?

Animator는 모든 애니메이션의 추상 기본 클래스입니다. ValueAnimator는 객체에 바인딩하지 않고 숫자 값을 애니메이션하는 하위 클래스입니다. ObjectAnimator(ValueAnimator의 하위 클래스)는 객체의 특정 속성(예: alpha 또는 translationY)을 애니메이션합니다. 타임라인의 경우 addUpdateListener를 통한 콜백이 있는 범용 타이머로 ValueAnimator를 사용하세요.

다른 기기에서 애니메이션을 동기화하려면 어떻게 해야 하나요?

프레임 수가 아닌 시스템 시계(iOS의 CACurrentMediaTime, Android의 System.nanoTime)를 사용하여 진행률을 계산하세요. 네트워크 동기화의 경우 오프셋 보정이 있는 NTP 기반 타이머를 사용하세요. iOS는 밀리초 정밀도의 오디오-비디오 타임라인 동기화를 위해 AVAudioSession을 지원합니다. 프레임 기반 동기화는 피하세요 — 프레임 속도는 30~120fps까지 다양합니다.

Animator의 startOffset과 repeatCount는 무엇인가요?

startOffset은 밀리초 단위의 애니메이션 시작 전 지연입니다. 캐스케이드 및 스태거 효과에 사용됩니다. repeatCount는 반복 횟수입니다: 0 = 반복 없음, ValueAnimator.INFINITE = 무한 루프. repeatMode는 방향을 결정합니다: RESTART(처음으로 재설정) 또는 REVERSE(역방향). iOS에서 startOffset은 beginTime이라고 하며(CACurrentMediaTime과 함께), repeatCount는 CABasicAnimation의 동일한 속성입니다.

최적의 애니메이션 지속 시간은 얼마인가요?

Material Design은 인터페이스 애니메이션에 200~350ms를 권장합니다. 100ms 미만의 애니메이션은 애니메이션으로 인식되지 않으며, 500ms 이상은 짜증을 유발합니다. 요소의 진입/퇴장: 200~300ms. 화면 간 전환: 300~400ms. 작은 요소에는 짧은 지속 시간(100~200ms), 큰 요소에는 긴 지속 시간(400~500ms)을 사용하세요.

재생 중 애니메이션이 왜 끊기나요?

끊김(jank)은 다음과 같은 이유로 발생합니다: 메인 스레드의 장기 실행 작업(렌더링 차단), overdraw가 있는 복잡한 View/Layer 계층 구조, 또는 잘못된 타임라인 사용(지속 시간이 vsync의 배수가 아님 — 60fps의 경우 16ms, 120fps의 경우 8ms). 드롭된 프레임을 감지하려면 Xcode Instruments Animations 또는 Android Studio Profiler를 사용하세요. Compose에서는 Modifier.drawWithCache를 사용하여 복잡한 그리기를 캐시하세요.

요약

  • Timeline은 애니메이션 흐름을 정의하는 duration, startDelay, repeatCount, repeatMode 매개변수를 가진 애니메이션 시간 모델입니다.
  • iOS CAMediaTiming 프로토콜은 duration, beginTime, speed, timeOffset, repeatCount, fillMode를 통해 타임라인을 관리합니다.
  • Android Animator 및 ValueAnimator는 직접 제어를 위한 duration, startDelay, repeatCount, repeatMode 및 setCurrentPlayTime을 제공합니다.
  • Android의 AnimatorSet과 iOS의 CAAnimationGroup은 애니메이션을 단일 타임라인으로 결합할 수 있습니다.
  • SwiftUI의 TimelineView(iOS 15+)는 Animation API 없이 사용자 정의 타임라인을 위한 시스템 스케줄러에 대한 액세스를 제공합니다.
  • iOS의 CACurrentMediaTime()과 Android의 System.nanoTime은 애니메이션 동기화를 위한 기본 타임스탬프입니다.
  • Material Design 및 HIG 권장 사항에 따라 인터페이스 애니메이션의 최적 지속 시간은 200-350ms입니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기