모바일 앱의 프레임 레이트 — 정의, fps 및 향상 방법

저자: IT Sectr 게시일: 2026-03-31 읽는 시간: 10 분

프레임 레이트는 그래픽 시스템이 초당 표시하는 프레임 수입니다. 모바일 애플리케이션에서 프레임 속도는 애니메이션, 스크롤 및 화면 전환의 부드러움을 직접 결정합니다. Android Developers, 2025에 따르면 표준 디스플레이의 목표 프레임 레이트는 60fps, 높은 주사율을 가진 기기의 경우 120fps입니다. 목표 값에서 벗어나면 시각적 끊김 현상이 발생하고 사용자 경험이 저하됩니다.

핵심 요점

  • 프레임 레이트 — UI의 부드러움을 결정하는 초당 프레임 수(fps)입니다.
  • 표준 목표 프레임 레이트는 60fps이며, 프레임당 16.6ms에 해당합니다.
  • 120Hz 디스플레이를 갖춘 기기에는 120fps(프레임당 8.3ms)가 필요합니다.
  • 놓친 프레임은 Jank(애니메이션의 눈에 띄는 끊김)를 유발합니다.
  • 프레임 레이트 프로파일링은 UI 성능 최적화의 첫 단계입니다.

프레임 레이트란

프레임 레이트(프레임 속도)는 초당 프레임 수(fps)로 측정되는 지표로, 애플리케이션이 초당 화면 이미지를 업데이트하는 횟수를 나타냅니다. 인간의 눈은 24fps(영화)부터 움직임을 부드럽게 인식하지만, 대화형 UI는 터치와 애니메이션이 즉각적으로 느껴지도록 최소 60fps가 필요합니다. 각 프레임은 사용자 입력 처리, 레이아웃 계산, 뷰 계층 구조 렌더링, 화면 출력의 완전한 주기입니다. 이러한 단계 중 하나라도 할당된 시간 예산(60fps에서 16.6ms)을 초과하면 프레임이 건너뛰어지고 사용자는 끊김을 보게 됩니다.

애플리케이션의 프레임 레이트와 디스플레이의 주사율(Refresh Rate)을 구분하는 것이 중요합니다. 주사율은 화면의 하드웨어 특성으로, 디스플레이가 초당 물리적으로 이미지를 업데이트하는 횟수(60, 90, 120 또는 144Hz)입니다. 프레임 레이트는 애플리케이션이 초당 렌더링할 수 있는 프레임 수입니다. 애플리케이션이 120Hz 디스플레이에서 60fps를 출력하면 매 두 번째 프레임이 중복됩니다. 이미지는 부드럽지만 가능한 한 반응성이 높지는 않습니다. Google I/O 2023에 따르면 최신 플래그십은 간단한 UI 시나리오에서 120fps를 유지할 수 있지만, 부하가 높은 경우(게임, 복잡한 목록) 40~60fps로 떨어집니다.

프레임 렌더링 작동 방식

모바일 애플리케이션의 프레임 렌더링은 여러 단계의 파이프라인을 거칩니다. Android에서 파이프라인은 입력 처리(Input), 애니메이션(Animation), 측정 및 배치(Layout), 그리기(Draw), GPU 동기화, 화면 출력(Swap)을 포함합니다. 각 단계는 CPU 또는 GPU에서 실행되며, 모든 단계의 총 시간은 프레임 예산을 초과해서는 안 됩니다. 60fps의 예산은 16.6ms, 120fps의 경우 8.3ms입니다. Choreographer(Android)와 CADisplayLink(iOS)는 디스플레이의 수직 블랭킹 구간(VSync)과 렌더링을 동기화하여 화면이 새로 고쳐지는 순간에만 프레임이 출력되도록 하여 테어링을 방지합니다.

iOS에서 파이프라인은 유사합니다. Run Loop가 이벤트를 처리하고, Core Animation이 레이어를 계산하며, Render Server(별도 프로세스)가 렌더링하여 프레임을 GPU로 보냅니다. iOS의 차이점은 전용 Render Server 프로세스가 메인 애플리케이션에서 렌더링을 분리한다는 점입니다. 애플리케이션이 메인 스레드를 차단해도 Render Server는 마지막으로 알려진 프레임을 그릴 수 있지만 애니메이션은 중지됩니다. Render Server 자체가 따라잡지 못하면 GPU가 유휴 상태가 되고 프레임 레이트가 떨어집니다. Apple WWDC 2022에 따르면 iOS에서 낮은 프레임 레이트의 가장 흔한 원인은 과도한 CALayer 중첩, 무거운 shadowPath 및 오프스크린 렌더링입니다.

Choreographer를 통한 프레임 추적

Kotlin 코드가 Choreographer.FrameCallback을 구독하고 프레임 간 실제 시간을 기록합니다. 간격이 16.6ms를 초과하면 놓친 프레임이 기록됩니다.

kotlin
class FrameRateMonitor {

    private var lastFrameTime = 0L
    private val frameCallback =
        Choreographer.FrameCallback { frameTimeNanos ->
            if (lastFrameTime != 0L) {
                val deltaMs = (frameTimeNanos - lastFrameTime) / 1_000_000f
                if (deltaMs > 16.6f) {
                    Log.w("FrameRate",
                        "Skipped frame: $deltaMs ms")
                }
            }
            lastFrameTime = frameTimeNanos
            Choreographer.getInstance()
                .postFrameCallback(this)
        }

    fun start() {
        Choreographer.getInstance()
            .postFrameCallback(frameCallback)
    }
}

디스플레이 주사율과 프레임 레이트

주사율(Refresh Rate)은 디스플레이가 초당 물리적으로 이미지를 다시 그리는 횟수를 결정하는 하드웨어 특성입니다. 표준 디스플레이는 60Hz, 최신 플래그십은 90, 120 또는 144Hz입니다. 애플리케이션의 프레임 레이트는 주사율보다 낮거나, 같거나, 높을 수 있습니다(후자의 경우 초과 프레임은 폐기됩니다). 이상적인 시나리오는 프레임 레이트가 주사율과 일치하는 것입니다. 각 하드웨어 주기가 애플리케이션에서 새 프레임을 받아 움직임이 최대한 부드러워집니다. 프레임 레이트가 낮으면 디스플레이가 마지막 프레임을 반복하여 미세 끊김으로 인식됩니다.

Android와 iOS는 동적 주사율 전환을 지원합니다. Android 12+는 Smart Refresh Rate를 사용합니다. 스크롤 중에는 시스템이 주사율을 120Hz로 높이고, 정적 콘텐츠에서는 배터리 절약을 위해 60Hz로 낮춥니다. iOS ProMotion(iPhone 13 Pro 이후)도 유사하게 작동하며 콘텐츠에 따라 주사율이 10~120Hz 사이에서 변합니다. 개발자는 기기가 높은 주사율을 지원하는지 확인하고 프레임당 시간 예산을 조정해야 합니다. 애플리케이션이 8.3ms(120Hz의 경우) 내에 프레임을 렌더링할 수 없는 경우 60Hz를 강제하는 것이 좋습니다. 이렇게 하면 프레임 손실 없이 안정적인 프레임 레이트를 보장할 수 있습니다.

디스플레이 유형주사율프레임당 예산기기
표준60Hz16.6ms대부분의 Android/iOS
고급90Hz11.1msOnePlus, Pixel 6+
플래그십120Hz8.3msiPhone Pro, Galaxy S22+
게이밍144Hz6.9msROG Phone, Nubia RedMagic

프레임 레이트 측정 도구

모바일 애플리케이션에서 프레임 레이트를 측정하기 위해 플랫폼 내장 도구와 타사 프로파일러를 모두 사용할 수 있습니다. Android에서 기본 도구는 GPU Profiling(Developer Options → Profile GPU Rendering)으로, 각 프레임을 단계(Draw, Prepare, Process, Execute)별로 분류한 타임라인을 보여줍니다. 더 자세한 분석은 Android Studio Profiler가 제공합니다. 다시 그리기를 유발하는 특정 뷰를 나타내는 전체 렌더링 프로필을 기록합니다. iOS에서는 Core Animation 템플릿과 함께 Instruments를 사용하여 FPS, 레이어 렌더링 시간 및 오프스크린 렌더링 수를 표시합니다.

프로덕션 프레임 레이트 모니터링을 위해 Firebase Performance(Android)를 사용하여 백그라운드에서 프레임 레이트를 수집하고 기기, OS 버전 및 세션별로 집계합니다. iOS에서는 MetricKitMXAnimatoryMetric을 통해 유사한 데이터를 제공합니다. 게임 및 Flutter 애플리케이션의 경우 FrameTimingCallback(Flutter) 및 Unity Profiler를 사용합니다. 평균 프레임 레이트가 아닌 백분위수(P50, P90, P99)를 측정하는 것이 중요합니다. 애플리케이션의 평균이 55fps라도 P99가 30fps인 경우, 사용자의 1%가 심각한 끊김을 경험하여 부정적인 리뷰로 이어질 수 있습니다.

Flutter에서 프레임 레이트 측정

Dart 예제는 Flutter에서 FrameTimingCallback을 구독하고 놓친 프레임 수를 기록하는 방법을 보여줍니다. 콜백은 각 프레임이 완료된 후에 실행됩니다.

dart
import 'package:flutter/scheduler.dart';

class FrameRateLogger {
    int totalFrames = 0;
    int missedFrames = 0;

    void start() {
        SchedulerBinding.instance
            .addTimingsCallback(_onReportTimings);
    }

    void _onReportTimings(List<FrameTiming> timings) {
        for (final timing in timings) {
            totalFrames++;
            if (timing.totalSpan()
                > Duration(milliseconds: 16)) {
                missedFrames++;
            }
        }
        debugPrint("FPS: \${totalFrames - missedFrames}");
    }
}

프레임 속도 최적화

프레임 레이트 최적화는 렌더링 파이프라인의 병목 지점 식별에서 시작됩니다. 레이아웃 단계의 주요 문제는 뷰 계층 구조의 과도한 중첩, 상대 레이아웃(많은 규칙이 있는 RelativeLayout) 사용, 빈번한 requestLayout 호출입니다. 해결책은 ConstraintLayout 또는 플랫 계층 구조를 사용하고 5~6레벨 이상의 중첩을 피하는 것입니다. 그리기(Draw) 단계에서는 오버드로우(overdraw)가 문제입니다. 하나의 픽셀이 프레임당 여러 번 그려지는 현상입니다. 예를 들어 반투명 프래그먼트 아래의 흰색 Activity 배경, 그 아래에 또 다른 레이어가 있는 경우 각 픽셀은 세 번 그려집니다. Debug GPU Overdraw 도구가 색상 표시로 문제 영역을 보여줍니다. 오버드로우는 2배 이하로 유지하는 것이 좋습니다.

iOS의 주요 문제는 무거운 cornerRadiusmasksToBounds입니다. 이들은 Core Animation이 임시 버퍼를 만들고 그린 다음 결과를 화면에 복사하는 오프스크린 렌더링을 유발합니다. 오프스크린 렌더링은 Instruments Core Animation에서 쉽게 발견할 수 있습니다. Renderer 줄이 빨간색이면 문제가 있는 것입니다. 해결책은 cornerRadius 대신 미리 자른 이미지가 있는 UIImageView를 사용하고, 꼭 필요한 경우가 아니면 groupOpacityshouldRasterize를 피하는 것입니다. 두 플랫폼 모두 invalidate()setNeedsDisplay() 호출 횟수를 최소화하는 것이 중요합니다. 이러한 각 호출은 전체 뷰 다시 그리기 주기를 트리거합니다.

Android에서 계층 구조 최적화

코드는 깊은 RelativeLayout 중첩을 플랫 ConstraintLayout 구조로 대체하는 방법을 보여줍니다. 중첩 수준을 4에서 1로 줄이면 레이아웃 시간이 30~50% 단축됩니다.

kotlin
// 예: ConstraintLayout을 통한 플랫 구조
class OptimizedView(context: Context) :
    ConstraintLayout(context) {

    private val binding =
        ItemProfileBinding.inflate(
            LayoutInflater.from(context)
        )

    fun bind(user: User) {
        binding.avatar.setImageURI(user.avatarUrl)
        binding.nameText.text = user.name
        // 전체 컨테이너를 다시 그리지 않고 데이터 바인딩
    }
}

적응형 주파수와 동적 프레임 레이트

최신 모바일 애플리케이션은 점점 더 적응형 프레임 레이트를 사용하고 있습니다. 이는 현재 시나리오에 따라 목표 주파수를 동적으로 조정하는 시스템입니다. 빠른 스크롤에는 부드러움을 위해 120fps가 필요하지만, 정적 화면에는 60fps 또는 비디오의 경우 30fps만 필요합니다. Android에서는 Choreographer.setFrameInterval(API 33+) 및 Window.setFrameRate를 통해 적응이 구현됩니다. 개발자는 SurfaceView의 setPreferredRefreshRate 또는 Window의 setFrameRate에서 선호 주파수를 지정할 수 있습니다. iOS는 ProMotion을 통해 자동으로 주파수를 관리하지만, 개발자는 CADisplayLink에 대해 preferredFramesPerSecond를 명시적으로 설정할 수 있습니다.

동적 프레임 레이트는 게임 및 애니메이션이 있는 애플리케이션에 특히 중요합니다. Google에 따르면 정적 화면에서 프레임 레이트를 120Hz에서 60Hz로 낮추면 GPU 에너지를 최대 30~40% 절약할 수 있습니다. 부드러움과 전력 소비 간의 최상의 균형을 달성하기 위해 다양한 시나리오에서 실제 프레임 레이트를 측정하고, 장면에 따라 목표 fps를 설정하며(게임: 60, 메뉴: 30, 비디오: 24), Lifecycle-aware 구성 요소를 통해 모드를 전환하여 애플리케이션이 최소화될 때 백그라운드에서 120fps를 렌더링하는 데 리소스를 낭비하지 않도록 하는 것이 좋습니다.

선호 프레임 레이트 설정

Swift 코드는 iOS에서 CADisplayLink의 preferredFramesPerSecond를 설정합니다. 스크롤 중에는 주사율이 120Hz로 증가하고, 정지 시 60Hz로 감소합니다.

swift
class AdaptiveFrameRateManager {

    private var displayLink: CADisplayLink?

    func startWithHighRate() {
        displayLink = CADisplayLink(
            target: self,
            selector: #selector(step)
        )
        if #available(iOS 15.0, *) {
            displayLink?.preferredFrameRateRange =
                CAFrameRateRange(
                    minimum: 60,
                    maximum: 120,
                    preferred: 120
                )
        }
        displayLink?.add(to: .current,
            forMode: .common)
    }

    @objc
    private func step() {
        // 애니메이션 업데이트
    }
}

자주 묻는 질문

모바일 애플리케이션에 적합한 프레임 레이트는 얼마인가요?

모바일 애플리케이션의 목표 프레임 레이트는 60fps(프레임당 16.6ms)입니다. 120Hz 디스플레이를 갖춘 기기의 경우 120fps가 바람직합니다. 30fps 미만의 값은 사용자 경험을 현저히 저하시킵니다.

프레임 레이트와 디스플레이 주사율의 차이는 무엇인가요?

프레임 레이트는 애플리케이션이 초당 렌더링하는 프레임 수입니다. 주사율(Refresh Rate)은 디스플레이가 초당 물리적으로 이미지를 업데이트하는 횟수입니다. 프레임 레이트가 주사율보다 낮으면 디스플레이가 마지막 프레임을 반복합니다.

Android에서 프레임 레이트를 측정하는 방법은?

개발자 옵션의 GPU Profiling, Android Studio Profiler 또는 Firebase Performance를 사용하세요. 프로그래밍 방식 측정의 경우 프레임 간격 계산과 함께 Choreographer.FrameCallback을 사용합니다.

오버드로우란 무엇이며 프레임 레이트에 어떤 영향을 미치나요?

오버드로우(Overdraw)는 하나의 픽셀이 프레임당 여러 번 그려지는 현상입니다. 추가 레이어마다 Draw 단계 시간이 증가하고 프레임 레이트가 감소합니다. 최적의 오버드로우는 2배이며, 심각한 수준은 4배 이상입니다.

동적 프레임 레이트가 배터리를 절약하는 방법은?

정적 콘텐츠에서 동적 프레임 레이트는 주파수를 30~60Hz로 낮추어 GPU 부하를 30~40% 줄입니다. 스크롤 중에는 부드러움을 위해 90~120Hz로 증가합니다.

요약

  • 프레임 레이트는 UI와 애니메이션의 부드러움을 결정하는 초당 프레임 수입니다.
  • 목표 프레임 레이트는 표준 디스플레이에서 60fps(프레임당 16.6ms), 높은 주사율에서 120fps(8.3ms)입니다.
  • 놓친 프레임은 Jank(눈에 띄는 끊김)를 유발하여 사용자 경험을 저하시킵니다.
  • 낮은 프레임 레이트의 주요 원인은 과도한 뷰 중첩, 오버드로우 및 오프스크린 렌더링입니다.
  • Choreographer(Android)와 CADisplayLink(iOS)가 VSync와 렌더링을 동기화합니다.
  • 적응형 프레임 레이트는 부드러움과 전력 소비의 균형을 맞추어 GPU 부하를 최대 40%까지 줄입니다.
  • 프레임 레이트 프로파일링은 모바일 애플리케이션 성능 최적화의 첫 단계입니다.

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

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

프로젝트 논의

더 읽어보기