이 Codelab 정보
1. 소개
최종 업데이트: 2021년 3월 11일
조회수 실적을 측정해야 하는 이유는 무엇인가요?
뷰는 사용자 환경에 직접적인 영향을 미치는 Android 애플리케이션의 핵심 부분입니다. 예를 들어 활동 또는 프래그먼트에는 사용자가 상호작용하는 뷰 구성요소를 보유하는 UI가 포함됩니다. UI가 화면에 완전히 그려질 때까지 사용자는 UI의 전체 콘텐츠를 볼 수 없습니다. 느린 화면과 화면 멈춤은 앱과의 사용자 상호작용에 직접적인 영향을 미치고 나쁜 사용자 환경을 만듭니다.
Firebase Performance Monitoring에서 이러한 실적 측정항목을 즉시 제공하지 않나요?
Firebase Performance Monitoring은 앱 시작 시간 (즉, 첫 번째 Activity의 로드 시간만 해당) 및 화면 렌더링 성능 (즉, Activity의 느린 프레임 및 정지된 프레임, Fragment의 경우 해당되지 않음)과 같은 일부 성능 데이터를 자동으로 캡처합니다. 그러나 업계 앱에는 일반적으로 활동이 많지 않고 활동 1개와 프래그먼트가 여러 개 있습니다. 또한 많은 앱은 일반적으로 더 복잡한 사용 사례를 위해 자체 맞춤 뷰를 구현합니다. 따라서 앱에서 커스텀 코드 트레이스를 계측하여 Activity와 Fragment의 로드 시간과 화면 렌더링 성능을 측정하는 방법을 이해하는 것이 종종 유용합니다. 이 Codelab을 쉽게 확장하여 맞춤 뷰 구성요소의 성능을 측정할 수 있습니다.
학습 내용
- Android 앱에 Firebase Performance Monitoring을 추가하는 방법
- 활동 또는 프래그먼트의 로드 이해
- 커스텀 코드 트레이스를 계측하여 Activity 또는 Fragment의 로드 시간을 측정하는 방법
- 화면 렌더링 및 느린/정지된 프레임 이해하기
- 측정항목으로 커스텀 코드 트레이스를 계측하여 느림/화면 멈춤을 기록하는 방법
- Firebase Console에서 수집된 측정항목을 확인하는 방법
필요한 사항
- Android 스튜디오 4.0 이상
- Android 기기/에뮬레이터
- Java 버전 8 이상
2. 설정
코드 가져오기
다음 명령어를 실행하여 이 Codelab의 샘플 코드를 클론합니다. 그러면 컴퓨터에 codelab-measure-android-view-performance
라는 폴더가 생성됩니다.
$ git clone https://github.com/FirebaseExtended/codelab-measure-android-view-performance.git
$ cd codelab-measure-android-view-performance
컴퓨터에 git이 없는 경우 GitHub에서 직접 코드를 다운로드할 수도 있습니다.
measure-view-performance-start
프로젝트를 Android 스튜디오로 가져옵니다. 컴파일 오류가 발생하거나 google-services.json
파일이 누락되었다는 경고가 표시될 수 있습니다. 이 단계의 다음 섹션에서 이 문제를 수정합니다.
이 Codelab에서는 Firebase Assistant 플러그인을 사용하여 Firebase 프로젝트에 Android 앱을 등록하고 필요한 Firebase 구성 파일, 플러그인, 종속 항목을 Android 프로젝트에 추가합니다. 이 모든 작업을 Android 스튜디오 내에서 할 수 있습니다.
Firebase에 앱 연결
- Android 스튜디오/도움말 > 업데이트 확인으로 이동하여 Android 스튜디오 및 Firebase Assistant의 최신 버전을 사용 중인지 확인합니다.
- 도구 > Firebase를 선택하여 Assistant 창을 엽니다.
- 앱에 추가할 Performance Monitoring을 선택한 다음 Performance Monitoring 시작하기를 클릭합니다.
- Firebase에 연결을 클릭하여 Android 프로젝트를 Firebase에 연결합니다(브라우저에서 Firebase Console이 열림).
- Firebase Console에서 프로젝트 추가를 클릭한 후 Firebase 프로젝트 이름을 입력합니다(이미 Firebase 프로젝트가 있는 경우 기존 프로젝트를 선택해도 됩니다). 계속을 클릭하고 약관에 동의하여 Firebase 프로젝트와 새 Firebase 앱을 만듭니다.
- 다음으로 새 Firebase 앱을 Android 스튜디오 프로젝트에 연결하는 대화상자가 표시됩니다.
- Android 스튜디오의 Assistant 창에 앱이 Firebase에 연결되었다는 확인 메시지가 표시됩니다.
앱에 Performance Monitoring 추가
Android 스튜디오의 어시스턴트 창에서 앱에 Performance Monitoring 추가를 클릭합니다.
변경사항 수락 대화상자가 표시되면 Android 스튜디오에서 앱을 동기화하여 필요한 모든 종속 항목이 추가되었는지 확인합니다.
마지막으로 Android 스튜디오의 어시스턴트 창에 모든 종속 항목이 올바르게 설정되었다는 성공 메시지가 표시됩니다.
추가 단계로 '(선택사항) 디버그 로깅 사용 설정' 단계의 안내에 따라 디버그 로깅을 사용 설정합니다. 공개 문서에서도 동일한 안내를 확인할 수 있습니다.
3. 앱 실행
앱을 Performance Monitoring SDK와 성공적으로 통합했다면 이제 프로젝트가 컴파일됩니다. Android 스튜디오에서 Run > Run 'app'을 클릭하여 연결된 Android 기기/에뮬레이터에서 앱을 빌드하고 실행합니다.
앱에는 다음과 같이 해당 활동과 프래그먼트로 이동하는 두 개의 버튼이 있습니다.
이 Codelab의 다음 단계에서는 Activity 또는 Fragment의 로드 시간과 화면 렌더링 성능을 측정하는 방법을 알아봅니다.
4. 활동 또는 프래그먼트 로드 이해
이 단계에서는 활동 또는 프래그먼트가 로드되는 동안 시스템에서 실행하는 작업을 알아봅니다.
활동 로드 이해
활동의 경우 로드 시간은 활동 객체가 생성된 시점부터 첫 번째 프레임이 화면에 완전히 그려질 때까지의 시간으로 정의됩니다 (사용자가 활동의 전체 UI를 처음으로 보게 되는 시점). 앱이 완전히 그려졌는지 측정하려면 reportFullyDrawn()
메서드를 사용하여 애플리케이션이 시작된 후 모든 리소스와 뷰 계층 구조를 완전히 표시할 때까지 걸린 시간을 측정하면 됩니다.
대략적으로 앱에서 startActivity(Intent)
를 호출하면 시스템은 다음 프로세스를 자동으로 실행합니다. 각 프로세스를 완료하는 데 시간이 걸리므로 활동이 생성된 시점과 사용자가 화면에서 활동의 UI를 볼 수 있는 시점 사이에 시간이 더 소요됩니다.
프래그먼트 로드 이해하기
활동과 마찬가지로 프래그먼트의 로드 시간은 프래그먼트가 호스트 활동에 연결된 시점부터 프래그먼트 뷰의 첫 번째 프레임이 화면에 완전히 그려질 때까지의 시간으로 정의됩니다.
5. 활동의 로드 시간 측정
첫 번째 프레임이 지연되면 사용자 환경이 저하될 수 있으므로 사용자가 경험하는 초기 로드 지연 시간을 파악하는 것이 중요합니다. 커스텀 코드 트레이스를 계측하여 이 로드 시간을 측정할 수 있습니다.
- Activity 객체가 생성되는 즉시 Activity 클래스에서 맞춤 코드 트레이스 (이름:
TestActivity-LoadTime
)를 시작합니다.
TestActivity.java
public class TestActivity extends AppCompatActivity {
// TODO (1): Start trace recording as soon as the Activity object is created.
private final Trace viewLoadTrace = FirebasePerformance.startTrace("TestActivity-LoadTime");
// ...
}
onCreate()
콜백을 재정의하고setContentView()
메서드에 의해 추가된 뷰를 가져옵니다.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Current Activity's main View (as defined in the layout xml file) is inflated after this
setContentView(R.layout.activity_test);
// ...
// TODO (2): Get the View added by Activity's setContentView() method.
View mainView = findViewById(android.R.id.content);
// ...
}
onDrawingStart()
및onDrawingFinish()
라는 두 콜백이 있는FistDrawListener
구현이 포함되어 있습니다.FirstDrawListener
및 성능에 영향을 줄 수 있는 요소에 관한 자세한 내용은 다음 섹션을 참고하세요. 활동의onCreate()
콜백 끝에FirstDrawListener
를 등록합니다.onDrawingFinish()
콜백에서viewLoadTrace
를 중지해야 합니다.
TestActivity.java
// TODO (3): Register the callback to listen for first frame rendering (see
// "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when View drawing is
// finished.
FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {
@Override
public void onDrawingStart() {
// In practice you can also record this event separately
}
@Override
public void onDrawingFinish() {
// This is when the Activity UI is completely drawn on the screen
viewLoadTrace.stop();
}
});
- 앱을 다시 실행합니다. 그런 다음 'Logging trace metric'으로 logcat을 필터링합니다.
LOAD ACTIVITY
버튼을 탭하고 아래와 같은 로그를 찾습니다.
I/FirebasePerformance: Logging trace metric: TestActivity-LoadTime (duration: XXXms)
🎉 축하합니다. Activity의 로드 시간을 측정하고 Firebase Performance Monitoring에 데이터를 보고했습니다. 이 Codelab의 뒷부분에서 Firebase Console에서 기록된 측정항목을 확인합니다.
FirstDrawListener의 목적
위 섹션에서 FirstDrawListener
를 등록했습니다. FirstDrawListener
의 목적은 첫 번째 프레임의 그리기가 시작되고 완료된 시점을 측정하는 것입니다.
ViewTreeObserver.OnDrawListener
를 구현하고 뷰 트리가 그려지려고 할 때 호출되는 onDraw()
콜백을 재정의합니다. 그런 다음 결과를 래핑하여 두 가지 유틸리티 콜백 onDrawingStart()
및 onDrawingFinish()
를 제공합니다.
FirstDrawListener
의 전체 코드는 이 Codelab의 소스 코드에서 확인할 수 있습니다.
6. 프래그먼트의 로드 시간 측정
Fragment의 로드 시간을 측정하는 방법은 Activity의 로드 시간을 측정하는 방법과 비슷하지만 약간의 차이가 있습니다. 다시 커스텀 코드 트레이스를 계측해 보겠습니다.
onAttach()
콜백을 재정의하고fragmentLoadTrace
녹음을 시작합니다. 이 트레이스 이름을Test-Fragment-LoadTime
로 지정합니다.
앞 단계에서 설명한 것처럼 Fragment 객체는 언제든지 만들 수 있지만 호스트 활동에 연결된 경우에만 활성화됩니다.
TestFragment.java
public class TestFragment extends Fragment {
// TODO (1): Declare the Trace variable.
private Trace fragmentLoadTrace;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// TODO (2): Start trace recording as soon as the Fragment is attached to its host Activity.
fragmentLoadTrace = FirebasePerformance.startTrace("TestFragment-LoadTime");
}
onViewCreated()
콜백에FirstDrawListener
를 등록합니다. 그런 다음 Activity 예와 마찬가지로onDrawingFinish()
에서 트레이스를 중지합니다.
TestFragment.java
@Override
public void onViewCreated(@NonNull View mainView, Bundle savedInstanceState) {
super.onViewCreated(mainView, savedInstanceState);
// ...
// TODO (3): Register the callback to listen for first frame rendering (see
// "OnFirstDrawCallback" in FirstDrawListener) and stop the trace when view drawing is
// finished.
FirstDrawListener.registerFirstDrawListener(mainView, new FirstDrawListener.OnFirstDrawCallback() {
@Override
public void onDrawingStart() {
// In practice you can also record this event separately
}
@Override
public void onDrawingFinish() {
// This is when the Fragment UI is completely drawn on the screen
fragmentLoadTrace.stop();
}
});
- 앱을 다시 실행합니다. 그런 다음 'Logging trace metric'으로 logcat을 필터링합니다.
LOAD FRAGMENT
버튼을 탭하고 아래와 같은 로그를 찾습니다.
I/FirebasePerformance: Logging trace metric: TestFragment-LoadTime (duration: XXXms)
🎉 축하합니다. 프래그먼트의 로드 시간을 측정하고 Firebase Performance Monitoring에 데이터를 보고했습니다. 이 Codelab의 뒷부분에서 Firebase Console에서 기록된 측정항목을 확인합니다.
7. 화면 렌더링 및 느린/정지된 프레임 이해하기
UI 렌더링은 앱에서 프레임을 생성하여 화면에 표시하는 작업입니다. 사용자와 앱의 상호작용이 원활하게 이루어지도록 하려면 앱이 16ms 미만으로 프레임을 렌더링하여 초당 60프레임 ( 왜 60fps일까요? 참고)을 달성해야 합니다. 앱의 UI 렌더링 속도가 느리면 시스템에서 프레임을 건너뛰게 되고 사용자는 앱에서 끊김을 인식합니다. 이를 버벅거림이라고 합니다.
마찬가지로 정지된 프레임은 렌더링하는 데 700ms 이상 걸리는 UI 프레임입니다. 이 지연은 앱이 멈춘 것처럼 보이고 프레임이 렌더링되는 동안 거의 1초 동안 사용자 입력에 응답하지 않기 때문에 문제가 됩니다.
8. 프래그먼트의 느린/정지된 프레임 측정
Firebase Performance Monitoring은 Activity의 느림/화면 멈춤 프레임을 자동으로 캡처합니다 (단, 하드웨어 가속이 적용된 경우에만). 하지만 현재 프래그먼트에는 이 기능을 사용할 수 없습니다. 프래그먼트의 느린/정지된 프레임은 프래그먼트 수명 주기의 onFragmentAttached()
및 onFragmentDetached()
콜백 사이의 전체 활동의 느린/정지된 프레임으로 정의됩니다.
AppStateMonitor
클래스 (Activity의 화면 트레이스를 기록하는 Performance Monitoring SDK의 일부)에서 아이디어를 얻어 ScreenTrace
클래스 (이 Codelab 소스 코드 저장소의 일부)를 구현했습니다. ScreenTrace
클래스를 활동의 FragmentManager
수명 주기 콜백에 연결하여 느린 프레임/정지된 프레임을 캡처할 수 있습니다. 이 클래스는 다음 두 가지 공개 API를 제공합니다.
recordScreenTrace()
: 화면 트레이스 녹화를 시작합니다.sendScreenTrace()
: 화면 트레이스 녹화를 중지하고 맞춤 측정항목을 로깅하여 총, 느림, 정지된 프레임 수를 기록합니다.
이러한 맞춤 측정항목을 연결하면 Fragment의 화면 트레이스를 Activity의 화면 트레이스와 동일한 방식으로 처리하고 Firebase Console의 성능 대시보드에 다른 화면 렌더링 트레이스와 함께 표시할 수 있습니다.
Fragment의 화면 트레이스를 로깅하는 방법은 다음과 같습니다.
- 프래그먼트를 호스팅하는 활동에서
ScreenTrace
클래스를 초기화합니다.
MainActivity.java
// Declare the Fragment tag
private static final String FRAGMENT_TAG = TestFragment.class.getSimpleName();
// TODO (1): Declare the ScreenTrace variable.
private ScreenTrace screenTrace;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// TODO (2): Initialize the ScreenTrace variable.
screenTrace = new ScreenTrace(this, FRAGMENT_TAG);
// ...
}
- Fragment를 로드할 때
FragmentLifecycleCallbacks
를 등록하고onFragmentAttached()
및onFragmentDetached()
콜백을 재정의합니다. 이 작업은 이미 완료되었습니다.onFragmentAttached()
콜백에서 화면 트레이스 녹화를 시작하고onFragmentDetached()
콜백에서 녹화를 중지해야 합니다.
MainActivity.java
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentAttached(@NonNull FragmentManager fm, @NonNull Fragment f, @NonNull Context context) {
super.onFragmentAttached(fm, f, context);
// TODO (3): Start recording the screen traces as soon as the Fragment is
// attached to its host Activity.
if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
screenTrace.recordScreenTrace();
}
}
@Override
public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f) {
super.onFragmentDetached(fm, f);
// TODO (4): Stop recording the screen traces as soon as the Fragment is
// detached from its host Activity.
if (FRAGMENT_TAG.equals(f.getTag()) && screenTrace.isScreenTraceSupported()) {
screenTrace.sendScreenTrace();
}
// Unregister Fragment lifecycle callbacks after the Fragment is detached
fm.unregisterFragmentLifecycleCallbacks(fragmentLifecycleCallbacks);
}
};
- 앱을 다시 실행합니다. 그런 다음
LOAD FRAGMENT
버튼을 탭합니다. 몇 초 정도 기다린 후 하단 탐색 메뉴에서back button
아이콘을 클릭합니다.
'Logging trace metric'으로 logcat을 필터링한 다음 아래와 같은 로그를 찾습니다.
I/FirebasePerformance: Logging trace metric: _st_MainActivity-TestFragment (duration: XXXms)
'FireperfViews'로 logcat을 필터링한 다음 아래와 같은 로그를 찾습니다.
D/FireperfViews: sendScreenTrace MainActivity-TestFragment, name: _st_MainActivity-TestFragment, total_frames: XX, slow_frames: XX, frozen_frames: XX
🎉 축하합니다. 프래그먼트의 느림/정지 프레임을 측정하고 Firebase Performance Monitoring에 데이터를 보고했습니다. 이 Codelab의 뒷부분에서 Firebase Console에서 기록된 측정항목을 확인합니다.
9. Firebase Console에서 측정항목 확인
- Logcat에서 Firebase Console URL을 클릭하여 트레이스의 세부정보 페이지로 이동합니다.
또는 Firebase Console에서 앱이 있는 프로젝트를 선택합니다. 왼쪽 패널에서 버전 및 모니터링 섹션을 찾아 성능을 클릭합니다.
- 기본 대시보드 탭에서 트레이스 표까지 아래로 스크롤한 다음 맞춤 트레이스 탭을 클릭합니다. 이 표에는 앞에서 추가한 맞춤 코드 트레이스와
_app_start
트레이스와 같은 몇 가지 기본 제공 트레이스가 표시됩니다. - 두 개의 맞춤 코드 트레이스인
TestActivity-LoadTime
및TestFragment-LoadTime
를 찾습니다. 수집된 데이터에 대한 자세한 내용을 보려면 시간을 클릭합니다.
- 커스텀 코드 trace의 세부정보 페이지에는 trace 기간 (측정된 로드 시간)에 관한 정보가 표시됩니다.
- 맞춤 화면 트레이스의 실적 데이터를 볼 수도 있습니다.
- 기본 대시보드 탭으로 돌아가서 trace 표까지 아래로 스크롤한 다음 화면 렌더링 탭을 클릭합니다. 이 표에는 이전에 추가한 맞춤 화면 트레이스와
MainActivity
트레이스와 같은 기본 제공 화면 트레이스가 표시됩니다. - 맞춤 화면 트레이스
MainActivity-TestFragment
를 찾습니다. 트레이스 이름을 클릭하여 느린 렌더링 및 정지된 프레임의 집계된 데이터를 확인합니다.
10. 축하합니다
수고하셨습니다. Firebase Performance Monitoring을 사용하여 Activity 및 Fragment의 로드 시간과 화면 렌더링 성능을 측정했습니다.
달성한 목표
- Firebase Performance Monitoring을 샘플 앱에 통합했습니다.
- 이제 뷰 로드의 수명 주기를 이해합니다.
- 커스텀 코드 트레이스를 추가하여 활동과 프래그먼트의 로드 시간을 모두 측정했습니다.
- 커스텀 측정항목이 있는 맞춤 화면 트레이스를 추가하여 느린/정지된 프레임을 기록했습니다.
다음 단계
Firebase Performance는 맞춤 추적 외에도 앱 성능을 측정하는 더 많은 방법을 제공합니다. 앱 시작 시간, 포그라운드 앱, 백그라운드 앱 성능 데이터를 자동으로 측정합니다. 이제 Firebase Console에서 이러한 측정항목을 확인할 때입니다.
또한 Firebase Performance는 자동 HTTP/S 네트워크 요청 모니터링을 제공합니다. 이를 통해 코드를 한 줄도 작성하지 않고도 네트워크 요청을 쉽게 계측할 수 있습니다. 앱에서 몇 가지 네트워크 요청을 전송하고 Firebase Console에서 측정항목을 확인해 보실 수 있나요?
보너스
이제 커스텀 코드 트레이스를 사용하여 Activity/Fragment의 로드 시간과 화면 렌더링 성능을 측정하는 방법을 알게 되었으므로 오픈소스 코드베이스를 살펴보고 앱의 일부인 Activity/Fragment에 대해 이러한 측정항목을 즉시 캡처할 수 있는지 확인해 보세요. 원하는 경우 PR을 보내주세요. :-)
11. 보너스 학습
Activity가 로드되는 동안 어떤 일이 일어나는지 이해하면 앱의 성능 특성을 더 잘 이해하는 데 도움이 됩니다. 이전 단계에서는 Activity 로드 중에 발생하는 일을 대략적으로 설명했지만 다음 다이어그램에서는 각 단계를 훨씬 더 자세히 설명합니다.