[go: up one dir, main page]

<블로그 원문은 여기에서 확인하실 수 있습니다.>

게시자: James Tamplin, Firebase 제품 관리자

이달 초에 진행된 Google Cloud Next 17은 Firebase와 모바일 개발에 대한 통찰력을 추구하는 모든 개발자를 위한 멋진 이벤트였습니다. 모바일 개발자라면 꼭 봐야할 세션들을 간추려 보았습니다.


모바일 개발 초보라면 'What to consider when developing mobile apps'을 가장 먼저 살펴보시기 바랍니다. 이 세션에서는 Google staff developer advocate인 Laurence Moroney가 연결 불량 및 오프라인 기기, 액세스 제어 및 배터리 수명 등, 모바일 디바이스 개발과 관련하여 몇 가지 짚고 넘어가야 할 사항을 설명합니다.


다음으로는 'Zero to App: Live coding an app with Firebase and GCP' 세션을 살펴보세요. 아주 멋진 라이브 데모가 펼쳐질 것입니다. Mike McDonald, Jen Tong, 그리고 Frank van Puffelen이 iOS와 Android에서 동시에 앱 라이브 코딩을 하는 모습을 보실 수 있습니다. Firebase로 얼마나 쉽게 코딩할 수 있는지, GCP로 얼마나 쉽게 규모를 조정할 수 있는지 확인해 보세요.


뛰어난 UX 에 초점을 맞추고 싶은 모바일 개발자라면 "서버리스" 개발을 통해 큰 근심거리를 덜 수 있습니다. 본인의 얘기처럼 들리신다면, 'Google Cloud Functions and Firebase'를 보시기 바랍니다. 여기서는 Firebase 팀의 Thomas Bouldin과 Brendan Lim이 차세대 모바일 앱과 웹 앱을 만들기 위해 Google 클라우드 기능을 사용하여 Firebase 백엔드 서비스를 확장하는 방법을 소개할 것입니다.


물론, 위의 영상들은 빙산의 일각일 뿐입니다. Google Cloud Next 17 전체 세션을 Google Cloud 유튜브 채널을 통해 확인해 보시기 바랍니다.

<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 조은(우아한형제들)님이 참여해 주셨습니다.>

2015년, 저희는 Android용 Chrome에 트렌디한 유저들이 빠르고 편리한 엑세스를 위해 홈 스크린에 그들의 사이트를 추가할 수 있는 새로운 기능을 추가했습니다. 이 기능은 Android 단축키를 사용합니다. 즉, Android 전체 환경에서 웹 앱이 표시되는 방식이 설치되어 있는 기본 앱과는 다르다는 의미입니다. 일례로, 많은 개발자는 그동안 자신의 웹 앱을 런처의 웹 창 섹션에 표시해줄 것을 요청해왔습니다. 이러한 차이는 사용자에게 혼동을 줄 수 있으며, 사용 환경이 그만큼 통합적이고 일관된다는 느낌을 주지 못할 수 있습니다.

다음 몇 주에 걸쳐 저희는 Chrome 베타 버전에 이 환경의 새로운 버전을 배포할 예정입니다. 이 새 버전에서는 사용자가 Progressive Web App을 자신의 메인 스크린에 추가하면 Chrome이 전보다 더 긴밀하게 웹 앱을 Android에 통합합니다. 이를테면, Progressive Web App은 이제 런처의 앱 창 섹션과 Android Settings에 나타나고 다른 앱에서 들어오는 인텐트를 받을 수 있게 됩니다. 또한, 알림을 길게 누르면 Chrome용 알림 관리 컨트롤이 아니라 일반적인 Android 알림 관리 컨트롤이 나타납니다.




이렇게 새로 제공되는 '메인 스크린에 추가' 기능은 개발자가 사용자를 위한 최상의 환경을 구성할 수 있도록 돕겠다는 저희의 목표에 한 발짝 더 발걸음을 내디딘 것이며, 이와 같은 Progressive Web App 설치 메커니즘을 Android 환경의 모든 브라우저에서 사용할 수 있도록 하기 위해 노력하고 있습니다.

게시자: Yaron Friedman, '메인 스크린에 추가' 기능 전문가

<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 문현경(Web Technologies GDE)님이 참여해 주셨습니다.>





분석에 관한 한, 데이터를 더 일찍 볼수록 더 신속하게 데이터에 대응하여 그 가치를 더욱 높일 수 있습니다. 저희는 Firebase Analytics가 출시된 이후로 이 점을 가장 중요하게 생각해 왔으며 이러한 목표에 더 가까이 다가서실 수 있도록 노력하고 있습니다.

지난 11월에 저희는 실시간 변환 기능을 발표한 바 있습니다. 이 기능 덕분에 최종 사용자 기기에서 가장 중요한 이벤트를 즉시 전달할 수 있습니다. 이와 비슷한 시기에 저희는 BigQuery로의 실시간 내보내기 기능도 발표했습니다. 그에 따라 클라이언트 이벤트가 서버로 전송되는 즉시 BigQuery 내에서 이를 분석할 수 있게 되었습니다.


하지만 BigQuery를 사용하지 않는 개발자가 Firebase Analytics에서 유의미한 결과를 볼 수 있게 되기까지는 종종 몇 시간씩 기다려야 하곤 했습니다. 앱 내에서 분석을 디버그하거나 특정 지역에서 최신 변경 사항에 반응하는 방식을 빠르게 파악하는 것과 같은 작업을 할 때는 특히 이 시간이 정말로 길게 느껴질 수 있습니다.


이러한 요구에 부응하기 위해, Firebase Analytics의 StreamView와 DebugView를 일반 대중에게 공개하게 되었다는 점을 자랑스런 마음으로 알려드리는 바입니다.


StreamView는 이벤트가 Firebase Analytics로 흘러 들어갈 때 이벤트를 시각화하여  사용자가 앱과 상호 작용하는 방식을 대략적으로 보여줍니다. StreamView를 사용하면 최신 기능이 바라던 대로 제대로 제공되는지 확인하거나, 최신 버전의 앱 출시 과정을 지켜보거나, 가장 최근의 사용자 재참여 관련 작업에 대한 반응을 평가할 수 있는데, Firebase Analytics에서 이벤트를 받는 즉시 실시간으로 이 모든 작업을 수행할 수 있습니다.


StreamView에서는 전 세계 어디에서 사람들이 앱을 사용하고 있는지, 도시 수준까지 파악할 수 있습니다.




사용자에게 가장 일반적인 속성이 무엇인지 파악할 수도 있습니다.




또한, 앱에서 트리거되는 이벤트 종류를 개략적으로 파악하고, 이러한 이벤트에 대해 지금까지 기록한 모든 사용자설정 이벤트 매개변수 내역을 확인할 수도 있습니다.




그리고 이 모든 값을 보고서 필터로 사용하여 전 세계에서 특정 이벤트가 발생하거나 특정 제품이 인기를 끄는 지역을 파악하고, 특정 국가나 도시의 사용자를 대상으로 일반적인 사용자 속성이나 이벤트를 파악할 수 있습니다.


StreamView에서 사용할 수 있는 한 가지 고유한 기능으로 User Snapshot이 있습니다. 이 기능은 임의의 사용자에게서 발생하는 이벤트의 라이브 스트림을 제공합니다. 이 뷰를 사용하여 개별 사용자의 앱 이용 방식을 파악할 수 있습니다.




User Snapshot 뷰에서는 설정된 사용자 속성, 사용자 기기와 지역을 볼 수 있으며, 이러한 사용자가 보내는 이벤트의 매개변수를 파악할 수 있습니다. 사용자의 지역과 앱 버전을 기준으로 포함된 사용자를 필터링할 수 있습니다.




완전한 전체 보고서를 얻거나 다른 데이터를 대상으로 특정 세트 데이터를 상호 참조하기 위해 BigQuery를 계속 사용해야 할 수도 있겠지만, StreamView에서 이벤트가 발생할 때 이를 추적하는 것만으로도 앱에 대해 놀라울 정도로 많은 것을 알 수 있습니다. StreamView를 초기에 이용한 고객인 산타 추적기 팀은 제게 이 뷰가 "정말 멋진 기능"이라는 확신을 주었습니다.


저희의 데모 프로젝트에서 StreamView를 지금 바로 확인해보실 수 있습니다!


StreamView가 앱을 실시간으로 파악할 수 있는 훌륭한 도구이기는 하지만, 일반 대중에게 배포된 후에도 기기에서의 이벤트 전달이 여전히 일괄 처리되고 있습니다. 이는 사용자와 배터리 최적화 측면에서는 훌륭한 점이지만, 앱을 개발하고 분석 설정이 올바른지 즉시 확인해야 할 때는 난점이 있습니다.


DebugView를 사용하면 Firebase Analytics에 어떤 이벤트가 보고되고 있는지 즉시 확인할 수 있습니다. DebugView는 Analytics Debugging을 설정한 기기용으로 활성화되며, 앱을 빌드하는 동안 알맞은 매개변수로 알맞은 이벤트를 로깅하는 데 유용합니다.




DebugView에서는 개별 개발 기기에 대한 이벤트, 매개변수 및 사용자 속성을 보여줍니다. 또한, 잘못된 매개변수를 포함하는 이벤트를 강조표시할 수 있으므로 앱을 게시하기 전에 잘못된 매개변수를 수정할 수 있습니다.




직접 시작해보려면 DebugView 관련 문서를 살펴보거나 앞으로 며칠 내에 자기 소유의 프로젝트 콘솔에서 이러한 기능을 찾아 시험해 보시기 바랍니다!

<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 권순선(Google)님이 참여해 주셨습니다.>
 게시자: Shanqing Cai, 도구 및 인프라 부문 소프트웨어 엔지니어.

TensorFlow에서 기계 학습 모델(ML)의 디버깅 작업을 더욱 손쉽게 수행할 수 있게 해주는 도구인 TensorFlow 디버거(tfdbg)를 소개해드리고 싶습니다.
Google의 오픈 소스 ML 라이브러리인 TensorFlow는 데이터 흐름 그래프를 기반으로 합니다. 일반적인 TensorFlow ML 프로그램은 다음 두 가지 개별적인 단계로 구성됩니다.
  1. 라이브러리의 Python API를 사용하여 ML 모델을 데이터 흐름 그래프로 설정
  2. Session.run() 메서드를 사용하여 그래프에 대한 추론 훈련 또는 수행
두 번째 단계(즉, TensorFlow 런타임) 중에 오류와 버그가 발생하는 경우 디버그하기 어렵습니다.

그 이유를 이해하기 위해서는, 표준 Python 디버거에 대한 Session.run() 호출이 사실상 단일 명령문이므로 실행 중인 그래프의 내부 구조(노드와 노드의 연결)와 상태(출력 배열 또는 노드의 텐서)를 노출하지 않는다는 점을 유의하세요. gdb와 같은 하위 수준의 디버거는 TensorFlow 그래프 작업과 관련된 방식으로 스택 프레임과 변수 값을 구성할 수 없습니다. 특화된 런타임 디버거는 TensorFlow 사용자가 가장 자주 제기하는 기능 요청 사항 중 하나였습니다.

tfdbg는 이러한 런타임 디버깅 요구에 부응합니다. 기울기 하강을 통해 단순한 선형 방정식에 맞게 단순한 TensorFlow 그래프를 설정하고 실행하는 짤막한 코드 스니펫으로 작동하는 tfdbg를 살펴봅시다.

import numpy as np
import tensorflow as tf
import tensorflow.python.debug as tf_debug
xs = np.linspace(-0.5, 0.49, 100)
x = tf.placeholder(tf.float32, shape=[None], name="x")
y = tf.placeholder(tf.float32, shape=[None], name="y")
k = tf.Variable([0.0], name="k")
y_hat = tf.multiply(k, x, name="y_hat")
sse = tf.reduce_sum((y - y_hat) * (y - y_hat), name="sse")
train_op = tf.train.GradientDescentOptimizer(learning_rate=0.02).minimize(sse)

sess = tf.Session()
sess.run(tf.global_variables_initializer())

sess = tf_debug.LocalCLIDebugWrapperSession(sess)
for _ in range(10):
  sess.run(train_op, feed_dict={x: xs, y: 42 * xs})

이 예제에서 강조표시된 줄에서 볼 수 있듯이, 세션 객체가 디버깅을 위한 클래스로 래핑되어 있으므로(LocalCLIDebugWrapperSession), run() 메서드를 호출하면 tfdbg의 CLI(명령줄 인터페이스)가 실행됩니다. 마우스 클릭이나 명령어를 사용하여 연속적인 run 호출을 진행하고, 그래프의 노드와 노드의 속성을 검사하고, 중간 텐서 목록을 통해 그래프에서 모든 관련된 노드에 대한 전체 실행 기록을 시각화할 수 있습니다. invoke_stepper 명령어를 사용하면 Session.run() 호출이 '단계별 실행 모드'에서 실행되도록 할 수 있습니다. 여기서는 디버그 절차 지향 언어(예: gdb 또는 pdb)와 유사한 방식으로 선택한 노드로 단계별로 진행하여 해당 출력을 검토하고 수정한 후 추가 단계별 실행 작업을 수행할 수 있습니다.

TensorFlow ML 모델 개발에서 자주 발생하는 문제의 한 부류는 오버플로, 0으로 나누기, log0 등으로 인해 잘못된 숫자 값(무한대 및 NaN)이 나타나는 것입니다. 큰 TensorFlow 그래프에서는 이러한 노드의 출처를 찾는 것이 지루하고 시간도 오래 걸릴 수 있습니다. tfdbg CLI와 CLI의 조건부 중단점 지원 기능을 이용하면 이러한 문제가 있는 노드를 신속하게 식별할 수 있습니다. 아래 동영상에서는 tfdbg를 사용하여 신경망에서 무한대/NaN 문제를 디버그하는 방법을 보여줍니다.

TensorFlow 디버거의 작동을 보여주는 스크린캐스트(출처: 가이드)


Print Ops와 같은 대체 디버그 옵션과 비교해볼 때, tfdbg는 코드 줄 변경 필요성이 덜하고 더욱 포괄적인 그래프 지원과 더욱 향상된 대화형 디버그 환경을 제공합니다. 따라서 모델 개발 및 디버그 워크플로도 빨라집니다. 또한, 서버 환경에서 덤프된 텐서에 대한 오프라인 디버그tf.contrib.learn과의 통합과 같은 추가 기능도 제공합니다. 시작하려면 이 문서를 참조하세요. 이 연구 논문에서는 tfdbg의 설계에 대해 더욱 세부적으로 설명합니다.

tfdbg에 필요한 최소 TensorFlow 버전은 0.12.1입니다. 버그를 보고하려면 TensorFlow의 GitHub 문제 페이지에 문제를 공개하세요. 일반적인 사용과 관련하여 도움이 필요하면 StackOverflow에서 tensorflow 태그를 사용하여 질문을 게시하시기 바랍니다.
감사의 말
Google TensorFlow Core/API 팀원들과 Applied Machine Intelligence팀원들의 도움과 의견이 없었다면 이 프로젝트는 불가능했을 것입니다. 고맙습니다.





<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 Justin Hong(Google)님이 참여해 주셨습니다.>
 게시자: Google XLA 팀(TensorFlow 팀 공동 게시)


TensorFlow의 디자인 목표와 핵심 강점 중 하나는 바로 유연성입니다. TensorFlow는 임의 데이터 흐름 그래프를 정의하고 이를 이기종 컴퓨팅 기기(예: CPU 및 GPU)를 사용하여 분산된 방식으로 효율적으로 실행하기 위해 유연성과 확장성이 뛰어난 시스템으로 설계되었습니다.


하지만 유연성은 종종 성능과 양립하기 어렵습니다. TensorFlow는 모든 종류의 데이터 흐름 그래프를 정의할 수 있도록 지원하는 것을 목표로 하는 반면, TensorFlow가 각각의 연산을 개별적으로 최적화하기 때문에 모든 그래프를 효율적으로 실행하기란 어려운 일입니다. 효율적으로 구현된 연산이 존재하거나 각 연산이 상대적으로 무겁게 실행되는 연산의 경우라면 그래도 괜찮습니다. 그렇지 않은 경우 사용자는 여전히 더 낮은 수준의 연산들을 사용해서 연산을 합성해야 하는데, 이러한 합성의 경우 가장 효율적인 방식으로 실행된다는 보장이 없습니다.



이것이 바로 Google에서 TensorFlow용 컴파일러인 XLA(Accelerated Linear Algebra)를 개발한 이유입니다. XLA는 CPU, GPU 및 맞춤형 액셀러레이터(예: Google의 TPU)와 같은 기기에 대해 JIT 컴파일 기법을 사용하여 런타임에 사용자가 생성한 TensorFlow 그래프를 분석하고 실제 런타임 차원과 유형에 맞게 최적화하며, 여러 연산을 함께 합성하고 이에 대한 효율적인 네이티브 기계어 코드를 내보냅니다.


성능 향상을 위해 합성 가능한 연산의 합성

예를 들어 tf.nn.softmax 연산을 살펴보겠습니다. 이 연산에서는 다음과 같이 softmax 매개변수 활성화를 계산합니다.

CodeCogsEqn.gif


Softmax는 원시 TensorFlow 연산(지수, 감소, 요소별 나누기 등)의 합성으로 구현될 수 있습니다.

softmax = exp(logits) / reduce_sum(exp(logits), dim)


이는 연산 외부에서는 필요하지 않은 일시적인 결과의 산출과 추가 데이터 이동으로 인해 느려질 가능성이 있습니다. 게다가, GPU와 같은 코프로세서에서 이러한 분해를 구현하면 여러 차례 “커널 실행”을 일으킬 수 있고 이에 따라 연산 속도가 훨씬 더 느려집니다.


XLA는 TensorFlow가 원시 연산들의 조합을 자동으로 최적화하는데 도움을 주는 컴파일러 기술입니다. XLA로 강화된 TensorFlow는 런타임에 그래프를 분석하고 연산들을 적절히 합성하고 합성된 하위 그래프에 대한 효율적인 기계어 코드를 생성함으로써 런타임 성능 저하 없이 뛰어난 유연성을 유지합니다.


예를 들어, 위에 표시된 것처럼 softmax의 분해 구현은 수동으로 최적화한 복합 연산만큼 빨라지도록 XLA로 최적화됩니다.


나아가, XLA가 TensorFlow 연산의 전체 하위 그래프를 대상으로 효율적인 루프 형태로 합성함으로써 필요한 커널 실행수를 최소화할 수 있습니다. 예를 들면 다음과 같습니다.




이 그래프에서 많은 연산을 단일 요소별 루프로 합성할 수 있습니다. 예를 들어, biases의 단일 요소를 matmul 결과의 단일 요소에 더하는 경우를 살펴보겠습니다. 이 덧셈의 결과는 0(ReLU의 경우)과 비교 가능한 단일 요소입니다. 비교 결과는 거듭제곱된 후 모든 입력 값의 지수 합으로 나누어질 수 있고, 그 결과, softmax의 출력 값이 생성됩니다. 메모리에 matmul, 더하기 및 ReLU에 대한 중간 배열을 생성할 필요가 없습니다.


s[j] = softmax[j](ReLU(bias[j] + matmul_result[j]))


합성된 구현에서는 불필요한 메모리를 할당하지 않고 단일 요소별 루프 내에서 최종 결과를 계산할 수 있습니다. 더욱 복잡한 시나리오에서 이러한 연산은 행렬 곱셈으로 합성될 수도 있습니다.


XLA는 TensorFlow가 성능 저하 문제를 해결하는 동시에 뛰어난 유연성을 유지하는 데 도움이 됩니다.


내부 벤치마크에서 XLA를 사용하는 경우 Nvidia GPU에서 XLA를 사용하지 않는 TensorFlow에 비해 속도가 최대 50% 향상되는 것으로 나타났습니다. 예상한 대로 효율성이 뛰어난 루프로 합성될 수 있는 요소별 연산으로 구성된 긴 시퀀스를 포함하는 모델에서 속도가 가장 크게 개선되었습니다. 하지만 XLA는 여전히 시험용인 것으로 간주해야 하며, 몇몇 벤치마크에서는 속도가 감소할 수도 있습니다.



TensorFlow Developer Summit에서 있었던 이 토론회에서 Chris Leary 씨와 Todd Wang 씨는 TensorFlow가 어떻게 XLA, JIT, AOT 및 기타 컴파일 기법을 활용하여 실행 시간을 최소화하고 컴퓨팅 리소스를 극대화할 수 있는지에 대해 설명했습니다.


극한의 특수화를 통해 실행 파일 크기 감소 실현


성능 향상 외에, TensorFlow 모델은 실행 파일 크기가 줄어든 덕분에 메모리가 한정된 환경(예: 휴대기기)에서 XLA의 이점을 누릴 수 있습니다. tfcompile은 AOT(Ahead-Of-Time) 컴파일에 XLA를 활용하는 도구입니다. 전체 그래프가 XLA로 컴파일된 다음에 그래프에서 연산을 구현하는 세밀한 기계어 코드를 내보냅니다. 이 방식은 최소의 런타임과 결합되어 상당한 크기 감소 효과를 냅니다.


예를 들어, android-arm에 깊이 3, 폭 60으로 적층된 LSTM 모델이 있다고 가정할 경우 원래 TF 모델 크기는 2.6MB(런타임 1MB + 그래프 1.6MB)입니다. 그런데 XLA로 컴파일하면 크기가 600KB로 줄어듭니다.


이 크기 감소는 정적 컴파일에 기인한 모델의 최적화에 의해 얻어집니다. 모델 실행 시, TensorFlow 런타임의 성능과 유연성을 전부 활용할 필요는 없습니다. 사용자가 관심을 가지는 실제 그래프를 구현하는 연산만 네이티브 코드로 컴파일됩니다. 그렇긴 하지만, XLA의 CPU 백엔드에서 내보내지는 코드의 성능은 최적의 상태와는 거리가 멀기 때문에 이 부분은 작업이 더 필요합니다.

대체 백엔드 및 기기 지원


오늘날 새로운 종류의 컴퓨팅 기기에서 TensorFlow 그래프를 실행하려면 새로운 기기에 대해 모든 TensorFlow 연산(커널)을 다시 구현해야 합니다. 기기에 따라서는 무척 많은 양의 작업이 될 수도 있습니다.


XLA는 설계상 맞춤형 백엔드를 추가하여 새로운 기기를 훨씬 더 쉽게 지원할 수 있도록 되어 있습니다. TensorFlow는 XLA를 대상으로 지정할 수 있으므로, 새로운 기기 백엔드를 XLA에 추가하면 이에 따라 새로운 기기가 TensorFlow 그래프를 실행하도록 할 수 있습니다. XLA 연산은 원시 연산이므로(XLA는 자체적으로 복합 연산 분해를 처리함) XLA는 새로운 기기를 지원해야 할때 훨씬 더 작은 구현만을 필요로 합니다. 이 페이지에 XLA에 맞춤형 백엔드를 추가하는 프로세스에 대해 설명해두었습니다. Google도 이 매커니즘을 사용하여 XLA에서 TPU를 지원하고 있습니다.


결론 및 향후 전망


XLA는 여전히 개발 초기 단계에 있습니다. 일부 사용 사례에서 매우 좋은 결과를 보이고 있으므로, 미래에는 분명히 TensorFlow에 이 기술이 훨씬 더 많이 활용될 수 있을 것입니다. 저희는 XLA를 TensorFlow Github릴리즈함으로써 커뮤니티를 통해 성장하기를 기대하며 이것이 TensorFlow 런타임과 모델들을 새로운 하드웨어에서 보다 쉽게 이식하고 최적화하는데 도움이 되기를 바랍니다.

<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 양찬석(Google)님이 참여해 주셨습니다.>

개발자분들과 만나 Firebase Analytics 관련된 이야기를 나누어보면, 다음과 같이 이야기하시는 경우가 종종 있습니다. "Firebase Analytics를 통해 앱 이벤트를 분석하면 좋겠지요. 하지만 지난 몇 년 동안 Google Analytics를 통해 데이터를 분석하고, 이를 튜닝하는데 많은 시간을 썼기 때문에, 새로운 도구를 사용하여 처음부터 다시 이러한 작업을 시작하고 싶지는 않네요."

그럼에도 Firebase Analytics의 몇몇 기능은 개발자 여러분의 일을 덜고 효과적인 앱 비지니스를 구축하는데 큰 도움이 될 수 있습니다. 예를들어 서버 구현 없이 특정 잠재 고객을 생성한 후 이를 대상으로 알림을 보내거나, Firebase 원격 구성(Remote Config)을 적용하기 위한 사용자 속성을 생성하는 것을 생각해볼 수 있습니다. 이러한 기능을 Google Analytics와 함께 활용할 수 있는 방법은 없을까요?

Google Analytics 기반 앱에 Firebase Analytics를 추가하는 데 관심이 있는 경우에 시도해볼 수 있는 몇 가지 전략을 살펴봅시다.

먼저 이벤트부터 검토

모바일 게임에서 매 라운드가 끝날 때마다 총 점수, 무찌른 적 수, 생존한 라운드 수의 세 가지 이벤트를 Google Analytics로 보낸다고 가정해봅시다. 다음과 같은 방법으로 Google Analytics에 값을 기록할 수 있습니다.

func recordGameOver() {
    let tracker = GAI.sharedInstance().defaultTracker
    let gameOverEvent = GAIDictionaryBuilder.createEvent(withCategory: "gameOver",
       action: "totalScore", 
       label: "", 
       value: myGameStats.totalScore as NSNumber)
    let enemiesBeatenEvent = GAIDictionaryBuilder.createEvent(withCategory: "gameOver",
       action: "enemiesBeaten", 
       label: "", 
       value: myGameStats.enemiesBeaten as NSNumber)
    let roundsSurvivedEvent = GAIDictionaryBuilder.createEvent(withCategory: "gameOver", 
       action: "roundsSurvived", 
       label: "", 
       value: myGameStats.roundsSurvived as NSNumber)
    tracker?.send(gameOverEvent.build() as [NSObject: AnyObject])
    tracker?.send(enemiesBeatenEvent.build() as [NSObject: AnyObject])
    tracker?.send(roundsSurvivedEvent.build() as [NSObject: AnyObject])
}

틀림없이 이런 질문을 하고 싶으실 겁니다. 바로 대답해 드리죠. 네, 맞습니다. 맞춤 측정기준(Custom Dimension)을 구성하고 하나의 이벤트로 이 작업을 수행할 수도 있습니다. 이런 방법으로 Google Analytics에서 플레이어의 최종 점수, 무찌른 적군 수, 생존한 라운드 수를 볼 수 있는 멋진 보고서를 만들 수 있습니다.

이제 이것을 Firebase Analytics 이벤트로 변환하는 방법에 대해 생각해봅시다. Google Analytics는 각각 하나의 연관된 값을 가진 계층적 이벤트를 중심으로 구성되지만, Firebase Analytics는 여기에서 한발 더 나아가 이벤트 매개변수로 함께 전달되는 다수의 연관된 키-값 쌍을 포함한 단일 이벤트를 기록합니다.

따라서 다음과 같이 Google Analytics 이벤트에서 Firebase Analytics 이벤트로 말 그대로 적절히 일대일 변환을 수행할 수 있습니다.

  FIRAnalytics.logEvent(withName: "gameOverTotalScore", 
      parameters: [kFIRParameterValue: myGameStats.totalScore as NSObject])
  FIRAnalytics.logEvent(withName: "gameOverEnemiesBeaten", 
      parameters: [kFIRParameterValue: myGameStats.totalScore as NSObject])
  FIRAnalytics.logEvent(withName: "gameOverTotalScore", 
      parameters: [kFIRParameterValue: myGameStats.totalScore as NSObject])

하지만 Firebase Analytics 환경에서는 이것을 다수의 맞춤 매개변수(Custom Parameter)를 포함한 하나의 이벤트로 기록하는 것이 더 자연스럽습니다.

  let finalStats = ["totalScore": myGameStats.totalScore as NSObject,
                    "enemiesBeaten": myGameStats.enemiesBeaten as NSObject,
                    "roundsSurvived": myGameStats.roundsSurvived as NSObject]
  FIRAnalytics.logEvent(withName: "gameOver", parameters: finalStats)

맞춤 측정기준 방식을 사용하는 개발자에게는 위 방법이 좀 더 익숙할 것 입니다.

두 번째 접근 방식을 선택하면, BigQuery를 사용하여 맞춤 매개변수를 분석해야 하지만, 이는 진지하게 데이타를 분석 하고 원하는 결과를 도출하기 위해서는 꼭 필요한 부분입니다. 이 과정 전체를 단계별로 설명하는 동영상을 참고하시면 도움이 될 듯 합니다.

올바른 방법으로 Firebase 추가하기

Firebase Analytics를 설치하고 실행하는 데 있어 가장 먼저 혼란을 느끼게 되는 사항 중 하나는 Google Analytics와 Firebase Analytics 모두 비슷한 설치 프로세스를 사용한다는 사실입니다.

예를 들어, iOS에서는 많은 개발자가 Google Analytics를 설치하고 실행하기 위해 Xcode 프로젝트에 GoogleService-info.plist 파일을 생성하고 추가했습니다. 하지만 Firebase Analytics를 올바로 구성하려면 또 하나의 GoogleService-info.plist 파일을 생성하고 Xcode 프로젝트에 추가하기도 해야 합니다. 이 두 가지를 모두 어떻게 수행할 수 있을까요?

이에 대한 대답은 콘솔에서 새 Firebase 프로젝트를 생성하는 것이 아니라 기존 Google Analytics 사용 프로젝트를 Firebase로 가져와서 Firebase를 설치하는 것입니다.

Firebase 프로젝트를 생성할 때 "Import Google Project" 버튼을 클릭하여 생성할 것입니다. Google Analytics 프로젝트를 선택한 다음 프로젝트 설정에서 GoogleService-info 파일을 다시 다운로드하세요.

그러면 Firebase Analytics와 Google Analytics 양쪽에서 가져오는 plist 파일의 상위 집합을 얻게 될 것입니다. 내용을 살펴보면 새로운 Firebase 정보 전체와 함께 Google Analytics 추적 ID와 동일한 TRACKING_ID 항목이 보일 것입니다. 이전의 GoogleService-info 파일을 이 새로운 파일로 바꾸고 나머지 Firebase 설치 프로세스를 진행하면 바로 사용할 수 있습니다.

Android에서는 이 프로세스가 기본적으로 동일하지만 .plist 대신 .json 파일을 사용하는 점만 다릅니다. 그리고 아마 Xcode 대신 Android Studio를 사용할 것입니다.

프로젝트를 올바로 설정했다고 가정하고, Google Analytics와 Firebase Analytics 모두에 원하는 데이터를 포함하려면 어떻게 해야 할까요? 다음과 같이 세 가지 옵션을 시도할 수 있습니다.

옵션 1: 2개의 Analytics SDK 사용

가장 간단한 시나리오는 단순히 Google Analytics 서비스를 계속 실행하면서 Firebase Analytics 추적도 앱에 추가하는 것입니다. 클라이언트에서 결과 코드는 다음과 같은 내용일 것입니다.

func recordGameOver() {
    // All the old code you had previously to record Google Analytics
    // … 
    // … So much code … 
    // … 
    // Now add the Firebase stuff
    let finalStats = ["totalScore": myGameStats.totalScore as NSObject,
                      "enemiesBeaten": myGameStats.enemiesBeaten as NSObject,
                      "roundsSurvived": myGameStats.roundsSurvived as NSObject]
    FIRAnalytics.logEvent(withName: "gameOver", parameters: finalStats)
}

개발자 관점에서 볼 때 왜 이런 해결 방법이 매력적인지 쉽게 상상할 수 있습니다. 구현이 매우 간단하고 위험도 낮기 때문이죠. 현재 Google Analytics 보고서의 문제가 생기지 않을까 걱정할 필요 없습니다. 모든 클라이언트 코드가 계속 Google Analytics를 항상 동일한 방식으로 호출하기 때문입니다. 추가로 Firebase Analytics도 호출하고 해당 시스템으로 데이터를 가져오면 됩니다.
그러나 이 해결 방법에는 몇 가지 단점이 있습니다. 첫째로, 두 가지 다른 분석 패키지로 데이터를 전송하게 되는데, 이는 일반적으로 클라이언트가 전보다 많은 네트워크 호출을 하게 됨을 의미합니다. 즉, 앱이 사용자의 모바일 데이터를 더 많이 사용하고 배터리 수명에 부정적인 영향을 미칩니다. 반면에 Firebase Analytics는 네트워크 호출을 수행하는 문제에 관한 한 상당히 보수적인 경향이 있으므로, 아주 큰 문제가 되지는 않을 것 입니다.

아마도 더 큰 단점은 모든 곳에서 분석 코드가 중복된다는 사실일 것입니다. 이미 분석 호출을 구현 세부 정보를 숨기는 별도의 메서드로 추상화했다면 이는 별로 중요한 문제가 아닙니다. 이제 이 하나의 메서드에 Firebase를 그냥 추가할 것입니다. 그러나 추상화하지 않았다면 이 코드를 앱 전체에 추가한다는 아이디어는 개발자에게 그다지 설득력 있게 들리지 않을 것입니다. 그래서 고려할 수 있는 또 다른 옵션이 있습니다.

옵션 2: Google 태그 관리자를 사용하여 어디에서나 이벤트 보고

Google 태그 관리자는 제 개인적으로 '언젠가 따로 시간을 내어서라도 꼭 배우고 싶은' 도구 목록에 항상 올라있는 멋진 만능 도구입니다. 그만한 이유가 있습니다. Google 태그 관리자가 제공하는 정말로 멋진 기능 중 하나는 발신되는 Firebase Analytics 이벤트에서 Google 애널리틱스는 물론 타사 애널리틱스 제공업체에게도 동일한 이벤트를 보고할 수 있다는 점입니다.
이 솔루션의 좋은 점은 클라이언트에서 한 세트의 분석-보고 코드만 필요하다는 사실입니다. 이 예시에서는 Google Analytics 호출을 완전히 제거할 수 있는 훨씬 간단한 구현 방법을 제시합니다.

func recordGameOver() {
    let finalStats = ["totalScore": myGameStats.totalScore as NSObject,
                      "enemiesBeaten": myGameStats.enemiesBeaten as NSObject,
                      "roundsSurvived": myGameStats.roundsSurvived as NSObject]
    FIRAnalytics.logEvent(withName: "gameOver", parameters: finalStats)
    // That's it!
}

Google 태그 관리자를 사용하여 이러한 이벤트를 Google Analytics에 보고하려면 CocoaPods/Gradle을 통해 라이브러리를 설치하고 프로젝트에 .json 파일을 추가해야 합니다. 이 모든 작업은 클라이언트 측에서 진행됩니다. 대부분의 작업은 태그 관리자 콘솔 내에서 수행됩니다.

태그 관리자 콘솔에서 Google Analytics로 보낼 모든 값에 대해 Firebase 이벤트 매개변수를 생성하고 싶을 것입니다. 이 예시에서는 totalScore, enemiesBeaten 및 roundsSurvived에 대한 값이 필요합니다.
다음으로, 대응시키려는 각각의 Firebase Analytics 이벤트에 대한 트리거를 생성할 것입니다. 이 경우에는 "gameOver"라는 이벤트 이름이 나타날 때마다 발생하는 트리거를 생성하려고 합니다.

마지막으로, Google Analytics(Google 태그 관리자 내에서는 Universal Analytics로 지칭함)로 이벤트를 보내 이러한 트리거에 응답하는 태그를 생성할 것입니다. AppsFlyer, Kochava, Tune 등과 같은 서비스로 이벤트를 보낼 수도 있습니다.

솔직히 기회만 된다면 보통은 코드를 적게 작성하는 것이 좋기 때문에 저는 이 접근 방식을 좋아합니다. 이 접근 방식을 취하면 일부 이벤트를 다른 곳은 말고 Google Analytics로만 보고하거나, (일부 이벤트를 Firebase Analytics에만 보고하기 시작하려는 경우) 앱이 출시된 이후라도 Firebase Analytics 이벤트를 Google Analytics로 보고하는 방법을 변경할 수 있는 유연성을 누릴 수도 있습니다.

한 가지 주목할 중요한 점은 현재 Firebase Analytics와 Google 태그 관리자를 함께 사용하여 전자 상거래 데이터를 Google Analytics에 보낼 수는 없다는 사실입니다. 담당 팀에서 이 문제를 해결하기 위해 노력하고 있지만 그동안은 Google Analytics에서 전자 상거래 데이터가 필요한 경우 다른 옵션 중 하나를 고려하거나 전자 상거래 이벤트를 Google 애널리틱스로 바로 보내도록 코드에 그냥 둘 수 있습니다.

위에서 언급했듯이, 클라이언트로부터 중복된 보고 분석 호출을 계속 수행한다는 단점도 있습니다. 또한, 모든 트리거, 이벤트 및 태그를 Google 태그 관리자 패널에 추가하는 데 많은 시간을 써야 합니다. 그러나 이보다 더 나쁜 상황도 생각할 수 있습니다. 클라이언트에서 2개의 분석 SDK를 실행한다는 아이디어에 반대하는 입장이라면 어떻게 해야 할까요? 그렇다면 다른 어떤 옵션을 사용할 수 있을까요? 당연한 질문입니다!

옵션 3: BigQuery를 사용하여 모든 항목 병합

고려할 수 있는 또 다른 옵션은 클라이언트에서 Google Analytics를 Firebase Analytics로 완전히 대체한 다음에 백엔드에서 BigQuery를 사용하여 이전 Google Analytics 데이터를 새 Firebase Analytics 데이터와 병합하는 것입니다.


이 솔루션은 이미 Google Analytics 360 고객으로서 현재 대부분의 맞춤 보고서를 BigQuery를 사용하여 작성하는 경우에만 적합하다는 점은 당연합니다. 이런 경우라면 BigQuery 쿼리를 업데이트하여 두 데이터 소스에서 모두 데이터를 가져오는 것이 합리적인 옵션일 수 있습니다.

앞서 제시한 예로 다시 돌아가서, 다음과 같은 쿼리를 실행하여 BigQuery 내에서 '일일 평균 최종 점수' 보고서를 이미 생성해오고 있다고 가정해봅시다.

SELECT
  AVG(hit.eventInfo.eventValue)
FROM
  `my_awesome_app.ga_sessions_20170123`,
  UNNEST(hits) AS hit
WHERE
  hit.eventInfo.eventCategory = "gameOver"
  AND hit.eventInfo.eventAction = "totalScore"
이제 Firebase Analytics로 전환합시다. 클라이언트에서 Firebase Analytics에 대해서만 호출하는 이전 예시의 간단한 구현을 사용할 수 있습니다.
func recordGameOver() {
    let finalStats = ["totalScore": myGameStats.totalScore as NSObject,
                      "enemiesBeaten": myGameStats.enemiesBeaten as NSObject,
                      "roundsSurvived": myGameStats.roundsSurvived as NSObject]
    FIRAnalytics.logEvent(withName: "gameOver", parameters: finalStats)
    // That's it!
}
그리고 시간에 따라 무찌른 평균 적군 수를 보려면 두 데이터 세트를 병합해야 합니다. 이를 위한 SQL은 다음과 비슷한 내용일 것입니다.

SELECT
  COUNT(*),
  AVG(total_score) AS average
FROM (
  SELECT
    event_param.value.int_value AS total_score
  FROM
    `firebase-project.com_example_awesome_app.app_events_20170123`,
    UNNEST(event_dim) AS event,
    UNNEST(event.params) AS event_param
  WHERE
    event.name = "gameOver"
    AND event_param.key = "totalScore"
  UNION ALL
  SELECT
    hit.eventInfo.eventValue AS total_score
  FROM
    `my_awesome_app.ga_sessions_20170123`,
    UNNEST(hits) AS hit
  WHERE
    hit.eventInfo.eventCategory = "gameOver"
    AND hit.eventInfo.eventAction = "totalScore" )

분명히 이것은 아주 간단한 예시입니다. 수행하려는 작업과 데이터 구조에 따라 상황이 더욱 빠르게 복잡해질 수 있습니다. 그러나 Google 클라우드 빅 데이터 블로그 게시물 작성에 참여 중인 (훨씬 똑똑한) 몇몇 동료가 이처럼 정교한 몇 가지 쿼리에 대한 글을 써서 올릴 것입니다. 그러니 계속 저희 블로그를 지켜봐 주세요!
이미 Google Analytics에 맞게 설정되어 있는 앱에 Firebase Analytics를 추가하는 방법에 대한 몇 가지 지침이 있습니다. 또는 그런 점에서는 다른 분석 플랫폼도 마찬가지입니다. 일단 설정을 마치면 Firebase Analytics 데이터를 사용하는 몇 가지 Firebase 기능을 시험해보는 것이 좋습니다. 특정 사용자 속성을 가진 사용자에게 새로운 원격 구성 설정을 제공하거나 자신이 만든 Firebase Analytics Audience에 알림을 보내보세요. 더 많은 Firebase Analytics 이벤트를 자유롭게 추적해 보세요. 앱 사용자 수에 관계없이 무료이고 무제한이므로 맘껏 이용하세요. 더욱 매력적인 도구와 기능이 계속 추가될 예정임으로 개발하려는 모바일 앱에 지금 바로 Firebase Analytics를 추가하더라도 그리 때이른 일이 아닐 것입니다. 한번 시도해보세요!


<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 도창욱(Web Technologies GDE)님이 참여해 주셨습니다.>
저희는 Firebase를 사용하여 개발자들이 서버를 관리할 필요 없이 클라이언트 측 코드만 사용하여 웹 앱과 모바일 앱을 빌드할 수 있는 환경을 만들려고 노력해 왔습니다. 하지만 개발자가 자체 서버를 운영해야 하는 경우도 있습니다. 이를 위해, 저희는 지난 11월에 Firebase Admin SDK발표했습니다.
오늘은 두 가지 새로운 Admin SDK 기능을 공유하고자 합니다.
  • Node.js SDK에 FCM(Firebase 클라우드 메시징)을 통해 메시지를 보내기 위한 Admin API가 추가되었습니다.
  • Java SDK는 이제 기본 제공되는 사용자 인증 정보 세트를 통해 초기화가 가능해졌으므로 사용이 더 쉬워졌으며, Google 인프라에서는 특히 그렇습니다.
Admin Node.js FCM API
FCM 기반의 메시지 전송 과정은 새로운 Admin Node.js FCM API를 통해 간소화할 수 있습니다. Node.js SDK 인증을 위한 기존 사용자 인증 정보를 통해 모든 사항이 자동으로 처리되므로 이 새로운 API를 사용하기 위해 추가로 필요한 설정은 없습니다. 새 API는 개별 기기, 기기 그룹, 항목(topic) 및 조건을 통해 대상을 설정하고 메세지를 전송할 수 있습니다.
예를 들어, 곧 있을 슈퍼볼과 관련된 앱을 구축할 때 애틀랜타 팰컨스 관련 항목(/topics/falcons) 또는 뉴잉글랜드 패트리어츠 관련 항목(/topics/patriots)을 구독하는 사람에게 알림을 전송하고 싶을 경우를 가정해봅시다.
var admin = require("firebase-admin");

// Fetch the service account key JSON file contents
var serviceAccount = require("path/to/serviceAccountKey.json");

// Initialize the app with a service account, granting admin privileges
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
});

// Define who to send the message to
var condition = "'falcons' in topics || 'patriots' in topics";

// Define the message payload
var payload = {
  notification: {
    title: "Super Bowl LI: Falcons vs. Patriots",
    body: "Your team is Super Bowl bound! Get the inside scoop on the big game."
  }
};

// Send a message to the condition with the provided payload
admin.messaging.sendToCondition(condition, payload)
  .then(function(response) {
    console.log("Successfully sent message! Server response:", response);
  })
  .catch(function(error) {
    console.log("Error sending message:", error);
  });
추가적으로 FCM 기능에 제3의 옵션을 제공하여 메시지 관련 옵션을 제공할 수 있습니다. 예를 들어, 경기가 일주일도 남지 않았다고 가정하고 해당 메시지를 높은 우선 순위로 설정해 보내고 메시지의 유효 기간을 1주일로 지정할 수 있습니다.
// condition and payload are the same as above

var options = {
  priority: "high",
  timeToLive: 60 * 60 * 24 * 7
};

admin.messaging.sendToCondition(condition, payload, options)
  .then(function(response) {
    console.log("Successfully sent message! Server response:", response);
  })
  .catch(function(error) {
    console.log("Error sending message:", error);
  });
이는 새로운 Admin Node.js FCM API를 통해 할 수 있는 작업의 맛보기일 뿐입니다. 추가 코드 샘플과 상세 설명서는 메시지 전송를 참조하시기 바랍니다.
Admin Java 사용자 인증 정보 인터페이스
지난 11월 Admin SDK가 출시된 이후, Node.js SDK는 여러 초기화 방법을 지원해온 것에 반해 Java SDK로는 서비스 계정 인증서 파일을 통한 초기화만이 가능했습니다. 최신 Admin Java SDK 릴리스부터 두 SDK 모두 몇 가지 유용한 기본 구현을 위시한 완벽한 사용자 인증 정보 인터페이스를 제공합니다.
이 새로운 API로 업그레이드하는 절차는 간단합니다. 이전에는 setServiceAccount() 메서드를 통해 SDK를 초기화했습니다.
FileInputStream serviceAccount = new FileInputStream("path/to/serviceAccountCredentials.json");

FirebaseOptions options = new FirebaseOptions.Builder()
    .setServiceAccount(serviceAccount)
    .setDatabaseUrl("https://<DATABASE_NAME>.firebaseio.com")
    .build();

FirebaseApp.initializeApp(options);
이에 대한 최신 기능은 바로 setCredential() 메서드를 사용하는 것입니다.
FileInputStream serviceAccount = new FileInputStream("path/to/serviceAccountCredentials.json");

FirebaseOptions options = new FirebaseOptions.Builder()
    .setCredential(FirebaseCredentials.fromCertificate(serviceAccount))
    .setDatabaseUrl("https://<DATABASE_NAME>.firebaseio.com")
    .build();

FirebaseApp.initializeApp(options);
Admin Java SDK에는 이제 Google 애플리케이션 기본 사용자 인증 정보를 기반의 사용자 인증 정보 구현이 포함되어 있습니다. 이를 통해 Google App Engine 및 Google Compute Engine과 같은 Google 인프라에서 서비스 계정 사용자 인증 정보에 대한 자동 검색이 지원할 수 있습니다. 즉, 서비스 계정 사용자 인증 정보를 직접 관리할 필요가 없습니다. 대신, 추가 구성 없이 Google 애플리케이션 기본 사용자 인증 정보를 활용하여 완전히 동일한 코드를 로컬 환경, 스테이징 환경 및 프로덕션 환경에서 실행할 수 있습니다.
FirebaseOptions options = new FirebaseOptions.Builder()
    .setCredential(FirebaseCredentials.applicationDefault())
    .setDatabaseUrl("https://<DATABASE_NAME>.firebaseio.com")
    .build();

FirebaseApp.initializeApp(options);
추가 코드 샘플과 상세 설명서는 SDK 초기화를 참조하시기 바랍니다.
Admin SDK의 다음 단계는?
저희는 Firebase 생태계에 최선의 백엔드 개발자 지원을 추가하기 위해 끊임없이 노력하고 있습니다. 향후 Firebase Admin SDK에 추가될 기능을 계속 지켜봐 주세요. 특정 기능이 추가되기를 바란다면 기능 요청 지원 채널을 통해 메모를 보내 알려주시기 바랍니다.

<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 조은(우아한형제들)님이 참여해 주셨습니다.>

 게시자: Alex Fischer, Google 검색 소프트웨어 엔지니어

TL;DR: 오늘, 저희는 Google 검색에서 사용자가 AMP 문서의 캐노니컬 URL에 접속하고 이를 복사 및 공유할 수 있게 해주는 기능을 AMP 에 통합해 추가하려고 합니다. 하지만 이 뉴스를 더 깊이 살펴보기 전에 잠시 뒤로 물러나 AMP 환경의 URL과 이런 URL이 AMP의 속도상 이점과 어떤 식으로 관련이 있는지 살펴봅시다.

URL 안에는 무엇이 있을까요? 웹에서는 많은 URL과 출처가 어느 정도까지는 콘텐츠의 신뢰성과 소유권을 나타냅니다. 예컨대 뉴욕타임즈 기사를 읽고 있다면 URL을 슬쩍 살펴보기만 해도 현재 읽고 있는 기사가 뉴욕타임즈에서 작성한 기사임을 어느 정도 신뢰할 수 있습니다. 특성, 브랜드 및 소유권이 분명하죠.

그런데 다양한 모바일 앱에서 최근에 출시된 제품이나 Google 검색에서 AMP의 최신 버전으로 말미암아 이런 판단의 근거가 조금 불투명해졌습니다. 이 게시물에서는 먼저 기술과 관련하여 저희가 내린 의사 결정의 근거를 설명하고 현존하는 각기 다른 종류의 AMP URL을 살펴보겠습니다. 그런 다음, URL과 관련한 문제를 해결하기 위해 앞으로 수행할 변경 사항에 대해 간략하게 소개하겠습니다.

우선 AMP 문서에 따르면 다음 세 가지 종류의 URL이 있다고 합니다.
  • 원래 URL: 게시자가 AMP 형식으로 작성한 문서입니다. http://www.example.com/amp/doc.html
  • AMP Cache URL: AMP Cache를 통해 제공되는 문서입니다. 예를 들어, Google에서 제공하는 모든 AMP는 Google AMP Cache를 통해 제공됩니다. 대부분의 사용자는 이 URL을 절대로 볼 수 없습니다. https://www-example-com.cdn.ampproject.org/c/www.example.com/amp/doc.html
  • Google AMP 뷰어 URL: AMP 뷰어에 표시되는 문서입니다(예를 들어, 검색 결과 페이지에서 렌더링할 때). https://www.google.com/amp/www.example.com/amp.doc.html


본질적으로 동일한 콘텐츠에 대해 출처가 각기 다른 세 가지 URL이 있다면 혼동될 수 있겠지만, 이렇게 각기 다른 URL이 존재하는 데는 캐싱과 사전 렌더링이라는 두 가지 주된 이유가 있습니다. 이 두 가지 모두 AMP 속도에 크게 기여하지만 새로운 URL이 필요합니다. 그 이유를 자세히 살펴보도록 하겠습니다.

AMP Cache URL

AMP Cache URL부터 살펴보겠습니다. AMP 전문 Google 디벨로퍼 어드보케인 Paul Bakaushas 씨는 why AMP Caches exist(AMP Cache가 존재하는 이유)를 훌륭하게 설명한 글을 게시했습니다. Paul 씨가 게시한 글에서는 AMP Cache의 이점을 자세히 설명하지만, 이를 위해 새로운 URL이 필요한 이유를 묻는 질문에 대해서는 충분한 답을 제시하지 않습니다. 이 질문에 대한 답은 결국 AMP의 디자인 원칙 중 하나인 손쉬운 채택을 위한 빌드에 있습니다. AMP는 모바일 웹의 일부 문제를 대체적으로 해결하는 것이 목적이므로, AMP의 구성 요소 역시 모든 이가 쉽게 사용할 수 있어야 합니다.

유효성 검사, 사용자 근접성 및 AMP Cache에서 제공하는 기타 이점을 이용할 수 있는 다양한 옵션이 있습니다. 하지만 자체 DNS 항목을 관리하지 않거나 복잡한 API를 통해 콘텐츠를 푸시할 엔지니어링 리소스가 없거나 콘텐츠 전송 네트워크 사용 요금을 부담할 수 없는 소규모 사이트의 경우, 이러한 기술 중 많은 부분을 이용할 수 없습니다.

이 때문에 Google AMP Cache는 단순한 URL "변환"을 기반으로 작동합니다. 웹마스터는 몇몇 URL에서만 콘텐츠를 사용할 수 있게 만들면 됩니다. 그러면 Google AMP Cache가 원래 URL을 미러링하고 변환하는 새로운 URL을 통해 Google의 세계적 인프라를 통해 콘텐츠를 캐시하고 제공할 수 있습니다. 네, 그토록 간단한 문제입니다. 반면에, 원래 URL을 사용하는 AMP Cache를 활용하려면 웹마스터가 DNS 레코드를 수정하거나 이름 서버를 다시 구성해야 합니다. 일부 사이트에서는 이러한 작업을 수행하기도 하지만, URL 기반 접근 방식이 대다수의 사이트에서 활용하기 더욱 쉽습니다.

AMP 뷰어 URL

이전 섹션에서 AMP 문서의 캐시된 버전을 가리키는 Google AMP Cache URL에 대해 살펴보았습니다. 하지만 www.google.com/amp URL은 어떨까요? 이런 URL이 왜 필요할까요? 이런 URL은 "AMP 뷰어" URL이며 사전 렌더링 때문에 존재하는 것입니다.
개인정보 및 리소스를 보호하는 사전 렌더링과 관련하여 AMP에서 기본 제공하는 지원 기능에 대해 거의 언급되지 않으므로 잘못 알고 있는 경우가 많습니다. 다수의 리소스 가져오기를 촉발시키지 않고 사용자의 CPU와 메모리도 고갈시키지 않으며 개인정보에 민감한 분석 코드를 실행하지 않고도 AMP 문서를 미리 렌더링할 수 있습니다. 이는 임베딩 애플리케이션이 모바일 웹 페이지이거나 기본 애플리케이션인지 여부에 관계없이 작동합니다. 하지만 새로운 URL에 대한 필요성은 대부분 모바일 웹 구현에서 비롯됩니다. 따라서 이에 대한 실례로 Google의 모바일 검색 결과 페이지(SERP)를 사용하겠습니다.

사전 렌더링의 작동 방식은 어떻게 됩니까?

사용자가 AMP를 사용한 결과를 반환하는 Google 검색을 수행하면 이러한 결과 중 일부가 자동으로 사전 렌더링됩니다. 사용자가 사전 렌더링된 결과를 클릭하면 AMP 페이지가 즉시 로드됩니다.

사전 렌더링은 AMP 페이지의 콘텐츠 그리고 AMP 문서만 사전 렌더링됨을 나타내는 추가 매개변수와 함께 임베딩 페이지(검색 결과 페이지)에 숨겨진 iframe을 로드하는 방식으로 작동합니다. 이러한 iframe의 수명 주기를 처리하는 자바스크립트 구성 요소를 "AMP 뷰어"라고 합니다.
AMP 뷰어는 숨겨진 iFrame에서 AMP 문서를 사전 렌더링합니다.


사용자 브라우저가 문서를 로드하고 AMP 런타임이 AMP 페이지 렌더링을 시작합니다. 이미지 및 삽입물과 같은 기타 모든 리소스가 AMP 런타임에 의해 관리되므로 이 시점에서 로드되는 다른 항목은 없습니다. AMP 런타임이 일부 리소스를 가져오기로 결정할 수 있지만, 리소스 및 개인정보가 보호되는 방식으로 이러한 작업을 수행합니다.

사용자가 결과를 클릭할 때 AMP 뷰어는 브라우저가 이미 렌더링한 iframe을 표시하고 AMP 런타임에 AMP 문서가 이제 표시된다는 점을 알리기만 하면 됩니다.
보시다시피, 이 작업은 정말로 간단합니다. 어떠한 네트워크 작업도 없고 관련된 새로운 페이지에 대한 하드 탐색도 없습니다. 따라서 결과가 거의 즉각적으로 로드됩니다.

google.com/amp URL은 어디서 온 것입니까?

위에서 언급한 모든 과정은 사용자가 여전히 원래 페이지(여기서는 검색 결과 페이지)에 있는 동안에 이루어집니다. 다시 말해, 사용자가 다른 페이지로 이동하지 않았다는 뜻입니다. 동일한 페이지에서 iframe을 보는 것이므로 브라우저가 URL을 바꾸지 않습니다.

저희는 여전히 브라우저에 표시되는 URL이 화면에 표시되는 실제 페이지를 나타내고 사용자가 이 페이지에 쉽게 연결할 수 있게 되기를 원합니다. 사용자가 브라우저에서 새로고침을 누를 때는 기본 검색 결과 페이지가 아니라 동일한 문서가 나타나기를 기대합니다. 따라서 AMP 뷰어는 이 URL을 수동으로 업데이트해야 합니다. 이 작업은 History API를 사용하여 수행됩니다. 이 API를 통해 AMP 뷰어는 하드 탐색을 수행하지 않고 브라우저의 URL 표시줄을 업데이트할 수 있습니다.

여기서 의문은 브라우저에서 어떤 URL로 업데이트해야 할 것인가 하는 점입니다. 결과 자체의 URL(예: www.example.com/amp/doc.html) 또는 AMP Cache URL(예: www-example-com.cdn.ampproject.org/www.example.com/amp/doc.html)이 되는 것이 이상적입니다. 하지만 안타깝게도 이런 URL 중 어떤 것도 될 수 없습니다. History API의 기본 제한 사항 중 하나는 새로운 URL이 원래 URL과 동일한 출처에 있어야 한다는 것입니다(참조). 이 제한은 브라우저에서 보안상의 이유로 적용하는 것이지만, Google 검색에서는 이 URL이 www.google.com 출처에 있어야 한다는 것을 의미합니다.

헤더 표시줄을 표시하는 이유는 무엇일까요?

이전 섹션에서는 AMP 뷰어가 처리해야 하는 URL에 대한 제한 사항을 설명했습니다. 하지만 이러한 URL은 혼동을 주고 오해를 일으킬 수 있습니다. 이러한 URL은 피싱 공격에 문을 활짝 열어줄 수 있습니다. AMP 페이지가 Google의 로그인 페이지와 같은 모습의 로그인 페이지를 표시하고 URL 표시줄에 www.google.com이 표시되는 경우 이 페이지가 실은 Google 페이지가 아니라는 사실을 사용자가 어떻게 알 수 있겠습니까? 그것이 바로 추가적인 특성이 필요한 이유입니다.

콘텐츠의 적절한 특성을 제공하기 위해, 모든 AMP 뷰어는 사용자에게 현재 보고 있는 콘텐츠의 출처가 어디인지를 분명히 보여줘야 합니다. 이를 위한 한 가지 방법은 페이지의 "진정한" 출처를 표시하는 헤더 표시줄을 추가하는 것입니다.

다음 단계는?

앞서 나온 여러 섹션에서 이처럼 다양한 URL이 존재하는 이유와 모든 AMP 뷰어에 헤더가 있어야 할 이유가 명확히 설명되었기를 바랍니다. 저희는 이런 접근 방식과 URL의 중요성에 대해 여러분은 어떻게 생각하고 있는지 늘 귀를 기울이고 있습니다. 그렇다면 다음 단계는 무엇일까요? 아시다시피, 저희는 스스로 무슨 일을 하고 있는지 깊이 생각해 보고 사용자가 AMP 페이지에서 기대하는 속도와 성능에 대한 기준을 허물지 않고 싶습니다.

2016년 2월, Google 검색에서 AMP를 출시한 이후로 다음과 같은 단계를 밟아왔습니다.
  • 모든 Google URL(예: Google AMP Cache URL 및 Google AMP 뷰어 URL)이 콘텐츠의 원래 소스를 가능한 한 최선의 방식으로 반영합니다(예: www.google.com/amp/www.example.com/amp/doc.html).
  • 사용자가 페이지를 아래로 스크롤하여 문서를 읽을 때 AMP 뷰어 헤더 표시줄이 숨겨져 화면 공간을 더욱 알차게 활용할 수 있도록 합니다.
  • 사용자가 Google AMP 뷰어를 사용할 수 없는 플랫폼에서 AMP 뷰어 URL을 방문할 때 해당 문서에 대한 정규 페이지로 사용자를 리디렉션합니다.
위에서 설명한 사항 외에도, 많은 사용자가 문서의 정규 URL에 액세스하고 이를 복사 및 공유할 방법을 마련해달라고 요청했습니다. 오늘, 저희는 Google 검색의 AMP 뷰어 헤더에 앵커 버튼 형태로 이 기능에 대한 지원을 추가할 것입니다. 이 기능을 통해 사용자는 표시되는 링크를 길게 탭하여 브라우저에서 제공되는 기본 공유 기능을 이용할 수 있습니다.

향후 몇 주 내에 Android Google 앱에서는 사용자가 앱의 공유 버튼을 탭할 때 문서의 원래 URL을 공유할 예정입니다. 이 기능은 이미 iOS Google 앱에서 사용할 수 있습니다.

마지막으로, 저희는 곧 출시될 예정으로 이 기능을 더욱 향상시킬 수 있는 웹 플랫폼 API를 활용할 것입니다. 이러한 API 중 하나가 바로 Web Share API이며, AMP 뷰어는 이 API를 사용하여 AMP 뷰어 URL이 아니라 원래 URL을 통해 플랫폼의 기본 공유 흐름을 호출할 수 있게 됩니다.

Google에서는 사용자와 게시자 모두를 위해 할 수 있는 한 최고의 AMP 환경을 만들기 위해 모든 노력을 기울이고 있습니다. 활발한 활동을 보이는 에코시스템이 저희에게는 매우 중요하며, 특성, 사용자 신뢰도 및 소유권이 이런 에코시스템의 중요한 요소입니다. 저는 이 블로그 게시물을 통해 AMP 문서의 세 가지 URL에 대한 출처와 AMP의 속도를 높이는 데 있어 이러한 URL이 담당하는 역할, 그리고 Google 검색에서 AMP 환경을 더욱 향상시키기 위해 저희가 쏟고 있는 노력이 분명히 전달되기를 바랍니다. 마지막으로, 에코시스템은 여러분의 참여가 있어야만 번창할 수 있습니다. 언제든 피드백을 보내주시고 AMP에 참여해 주시기 바랍니다.

<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 김태호(Android GDE)님이 참여해 주셨습니다.>

게시자: Jamal Eason, Android 제품 관리자

이제 Android Studio 2.3을 다운로드하실 수 있습니다. 이 릴리스에서는 IDE 전체에 걸쳐 품질을 향상시키는 데 중점을 두었습니다. 지금까지 보내주신 모든 피드백에 대해 진심으로 감사드립니다. 저희는 전 세계 곳곳에서 활동하는 수백만 명의 Android 앱 개발자들을 지원할 목적으로 Android Studio를 빠르고 완벽하게 만드는 데 지속적으로 투자할 것을 약속 드립니다.

Android Studio 2.3에서는 품질 향상이 가장 기대되는 점이지만, 이번 릴리스에서는 개발 흐름의 각 단계에 통합되는 몇 가지 새로운 기능을 보게 될 것입니다. 앱을 디자인할 때 앱 이미지에 대해 업데이트된 WebP 지원을 활용하고 Layout Editor에서 업데이트된 ConstraintLayout 라이브러리 지원 및 위젯 팔레트도 확인해 보시기 바랍니다. Android Studio에 App Link Assistant가 새롭게 추가되었습니다. 이를 통해 개발자는 앱에서 지원하는 URI를 빌드하고 통합된 형태로 확인할 수 있습니다. 앱을 빌드하고 배포할 때 업데이트된 실행 버튼을 사용하여 더욱 직관적이고 신뢰할 수 있는 Instant Run 환경을 구현해 보세요. 마지막으로, Android Emulator로 앱을 테스트할 때 이제 텍스트 복사 및 붙여넣기 기능이 제대로 지원됩니다.

Android Studio 2.3의 새로운 기능

Android Studio 2.3에서 품질을 향상한 것 외에 저희가 추가한 기능에 대한 자세한 내용을 보려면 아래에 나와 있는 새로운 기능 목록을 확인해 보세요.
빌드
  • Instant Run 개선 사항 및 UI 변경 사항: 품질 향상에 중점을 둠에 따라, 저희는 Android Studio 2.3에서 Instant Run의 신뢰성을 더욱 향상시키기 위해 이 기능에 대해 몇 가지 중요한 사항을 변경했습니다. 다시 시작해야 할 수도 있는 코드 변경 사항을 반영하도록 이제는 Run 액션을 실행하면 항상 애플리케이션이 다시 시작됩니다. 또한, 새로 추가된 Apply Changes 액션은 앱이 계속 실행되는 상태에서 코드 스왑을 시도합니다. 신뢰성 향상을 위해 기본 구현을 대폭 변경했고, Instant Run 지원 앱에서의 시작 지연 문제도 해결했습니다. 자세히 알아보기.
새로운 Instant Run 버튼 액션
  • 빌드 캐시: 빌드 캐시는 Android Studio 2.2에 도입되었지만 기본적으로 비활성화되어 있으며, Android Studio에서 더욱 빠르게 빌드하기 위한 기본 빌드 최적화 기능입니다. 폭발적으로 증가한 AAR과 사전 덱싱(pre-dexed)된 외부 라이브러리를 캐시함으로써, 새로 캐시된 빌드가 더 빠르게 클린 빌드로 이어집니다. 이는 사용자 차원의 빌드 캐시로, Android Studio 2.3에서는 기본적으로 설정되어 있습니다. 자세히 알아보기.
디자인
  • Constraint Layout에서 체인 및 비율 지원: Android Studio 2.3에는 안정적인 ConstraintLayout 릴리스가 포함되어 있습니다. 이 ConstraintLayout, 릴리스를 사용하면 둘 이상의 Android 뷰를 양방향으로 함께 체인으로 연결하여 한 차원에서 그룹을 형성할 수 있습니다. 이는 두 개의 뷰를 함께 가까이 배치하되 빈 공간에 골고루 분산하려는 경우에 유용합니다. 자세히 알아보기.
Constraint Layout 체인

ConstraintLayout 역시 비율을 지원하는데, 이는 포함하는 레이아웃의 확장과 축소에 따라 위젯의 가로세로 비율을 유지하려는 경우에 유용합니다. 비율에 대해 자세히 알아보세요. 또한, ConstraintLayout의 체인과 비율 모두 ConstraintSet API를 사용하여 직접 코드로 작성할 수 있습니다.

Constraint Layout 비율

  • Layout Editor 팔레트: Layout Editor에서 위젯 팔레트가 업데이트되었으며, 이를 통해 검색, 정렬 및 필터링을 수행하여 레이아웃에 적합한 위젯을 찾을 수 있고 위젯을 디자인 화면에 드래그하기 전에 미리 볼 수도 있습니다. 자세히 알아보기.

Layout Editor 위젯 팔레트

  • 레이아웃 즐겨찾기: 이제 업데이트된 Layout Editor 속성 패널에서 위젯별로 자주 사용하는 속성을 저장할 수 있습니다. 고급 패널에서 속성에 간단하게 별표를 표시하면 됩니다. 그러면 해당 속성이 Favorites 섹션 아래에 표시됩니다. 자세히 알아보기.

Layout Editor 속성 패널의 Favorites Attributes
  • WebP 지원: APK의 공간을 절약하는 데 도움이 되도록 Android Studio가 이제 프로젝트의 PNG 자산에서 WebP 이미지를 생성할 수 있습니다. WebP 무손실 형식은 PNG보다 최대 25% 더 작습니다. Android Studio 2.3에는 PNG를 무손실 WebP로 변환하고 손실 WebP 인코딩을 검사할 수 있는 새로운 마법사가 있습니다. 런처가 아닌 PNG 파일을 마우스 오른쪽 버튼으로 클릭하여 WebP로 변환할 수 있습니다. 이미지를 편집해야 할 경우 프로젝트에서 해당 WebP 파일을 마우스 오른쪽 버튼으로 클릭하여 PNG로 다시 변환할 수도 있습니다. 자세히 알아보기.
WebP 이미지 변환 마법사

  • 머티리얼 아이콘 마법사 업데이트: 업데이트된 벡터 자산 마법사는 검색과 필터링을 지원하며, 각 아이콘 자산에 대한 레이블을 포함합니다. 자세히 알아보기.
벡터 자산 마법사

개발
  • Lint 기준: Android Studio 2.3에서는 프로젝트에서 해결되지 않은 Lint 경고를 기준으로 설정할 수 있습니다. 그러면 Lint가 새로운 문제만 보고하게 됩니다. 이는 앱에 기존의 Lint 문제가 많이 있지만, 새로운 문제를 해결하는 데만 집중하려는 경우 유용합니다. 이 릴리스에 추가된 Lint 기준과 새로운 Lint 검사 및 주석에 대해 자세히 알아보세요.
Lint 기준 지원
  • App Links Assistant: 이제, Android Studio를 사용하여 개발하는 앱에서 Android 앱 링크를 더 쉽게 지원할 수 있게 되었습니다. 새롭게 추가된 App Links Assistant를 사용하면 URL에 대한 새로운 인텐트 필터를 생성하고, 디지털 자산 링크 파일을 통해 앱의 웹사이트 연결을 선언하며, Android 앱 링크 지원을 테스트하는 작업을 손쉽게 수행할 수 있습니다. App Link Assistant는 ToolsApp Link Assistant 메뉴로 이동하여 액세스할 수 있습니다. 자세히 알아보기.
App Links Assistant
  • 템플릿 업데이트: 기본적으로, Android Studio 2.3의 모든 템플릿이 기존에는 RelativeLayout을 포함하던 것이 이제는 ConstraintLayout을 사용합니다. 템플릿Constraint Layout에 대해 자세히 알아보세요. 또한, 하단 탐색 머티리얼 디자인(Bottom Navigation Material Design) 가이드라인을 구현하는 새로운 Bottom Navigation Activity 템플릿도 추가했습니다.

새로운 프로젝트 마법사 템플릿
  • IntelliJ 플랫폼 업데이트: Android Studio 2.3에는 IntelliJ 2016.2 릴리스가 포함되어 있으며, 이 릴리스에는 업데이트된 검사 창과 알림 시스템 같이 향상된 기능이 들어 있습니다. 자세히 알아보기.
테스트
  • Android Emulator의 복사 및 붙여넣기 기능: 많은 사람들의 요구에 힘입어 최신 Emulator(v25.3.1)에 복사 및 붙여넣기 기능을 다시 추가했습니다. Android Emulator와 호스트 운영체제 사이에서 공유하는 클립보드가 있는데, 이를 통해 두 환경 간에 텍스트를 복사할 수 있습니다. 복사 및 붙여넣기 기능은 x86 Google API Emulator 시스템 이미지 API 레벨 19(Android 4.4 - Kitkat) 이상에서 작동합니다.

Android Emulator의 복사 및 붙여넣기 기능 지원

  • Android Emulator 명령줄 도구: Android SDK Tools 25.3부터는 SDK Tools 폴더의 emulator가 개별 에뮬레이터 디렉토리로 이동되었으며 'android avd' 명령이 지원 중단되고 독립 실행형 avdmanager 명령으로 대체되었습니다. emulator 및 'android avd'의 이전 명령줄 매개변수는 업데이트된 도구에서도 계속 작동합니다. 또한, emulator 명령에 대한 위치 리디렉션을 추가했습니다. 하지만, 명령줄을 통해 직접 AVD(Android Virtual Device)를 생성하는 경우 해당하는 스크립트도 업데이트해야 합니다. Android Studio 2.3을 통해 Android Emulator를 사용하는 경우에는 이러한 변경 사항이 워크플로에 영향을 미치지 않습니다. 자세히 알아보기.

요약하자면 Android Studio 2.3에는 다음과 같은 새로운 기능을 비롯한 많은 기능이 포함되어 있습니다.

개발
빌드
디자인
테스트

출시 노트를 살펴보고 Android Studio 2.3에 대해 자세히 알아보세요.

시작하기

다운로드
이전 버전의 Android 스튜디오를 사용하고 계시다면 탐색 메뉴(Help → Check for Update [Windows/Linux], Android Studio → Check for Updates [OS X])에서 공개 버전 채널 업데이트를 확인할 수 있습니다. 공식 다운로드 페이지에서 Android Studio 2.3을 다운로드할 수도 있습니다. Android Studio에 새로 추가된 기능과 향상된 기능을 모두 활용하려면 현재 앱 프로젝트에서 Android Gradle 플러그인 버전도 2.3.0으로 업데이트해야 합니다.
어떤 점이 마음에 드는지, 어떤 문제나 기능에 대해 알고 싶은지 피드백을 보내주시면 감사하겠습니다. Google+ 페이지나 Twitter에서 저희 Android Studio 개발 팀과 계속 소통하시기 바랍니다.

<블로그 원문은 여기에서 확인하실 수 있으며, 블로그 번역 리뷰는 양찬석(Google)님이 참여해 주셨습니다.>


Firebase 원격 구성(Remote Config)를 사용할 때는 세 가지 주요 단계가 있습니다.
  1. 원격 구성 기본값을 설정합니다. 코드 상에서 직접 값을 지정하거나, plist 또는 xml 파일에서 필요한 값을 가져올 수 있습니다. 이 과정은 서버 작업 없이 클라이언트 측에서 이루어 집니다.
  2. fetch()를 호출하여 클라우드에서 새로운 원격 구성 값을 내려 받습니다.
  3. 마지막으로, activateFetched()를 호출하여, 위에서 내려 받은 새로운 원격 구성 값으로 기존 데이타를 덮어 씁니다. 
이 때, 클라우드 상에서 내려 받기를 요청하면, 기본값과 다른 값만 다운로드합니다. 네트워크 호출은 최소로 유지 되면서도 클라우드를 통해 앱에 적용될 원격 구성 값을 변경 할 수 있습니다.

꽤 수월해 보이지요? 대부분의 경우 실제로 그렇게 어렵지는 않습니다. 다만, 한 가지, 당연히 두 번째 단계를 진행하기 위해서는 네트워크 호출이 필요합니다. 실제 상황에서는 사람들이 터널, 엘리베이터, 사막 또는 네트워크 연결이 불안한 장소에서 앱을 사용할 때도 있습니다. 네트워크 호출 완료에 얼마나 걸릴지 알 수가 없습니다. 다시말해, 사용자가 앱을 사용하기 전에 이 프로세스가 완료될 것이라 보장할 수 없습니다.

이런 점을 염두에 두고, 다음 세 가지 전략을 사용할 수 있습니다.

전략 1: 활성화 및 새로고침

가장 직관적인 방법입니다. 샘플 앱과 구현 가이드에서도 동일한 방법이 사용됩니다. 클라우드에서 새 원격 구성 값을 다운로드한 후 완료 핸들러에서 activateFetched()를 호출하여 즉시 적용한 다음, 새로운 원격 구성 값을 적용해 UI 혹은 그 외 다른 요소를 업데이트 합니다.

이 방식을 적용하면, 사용자가 원격 구성 값이 로드되는 것을 기다리지 않고, 앱을 바로 이용할 수 있다는 장점이 있습니다. 그러나 원격 구성 값 업데이트가 앱 사용 중에 발생하면, 사용자가 앱을 사용하는 중간에 갑자기 버튼 텍스트, UI 배치 또는 기타 중요한 값 등이 변경될 수 있습니다. 이는 사용자를 혼란스럽게 만들 수 있고, 때문에 많은 개발자들은 다음과 같은 솔루션을 선택합니다.

전략 2: 로딩 화면 추가

매우 일반적인 방법으로 사용자가 처음 앱을 시작할 때 로딩 화면을 표시하는 것입니다. 이전과 마찬가지로 완료 핸들러에서 activateFetched를 호출하여 즉시 새로 내려받은 값을 적용합니다. 업데이트가 완료되면, 로딩 화면을 닫고 콘텐츠를 표시합니다. 사용자가 로딩 화면을 지나서 앱으로 진입할 때, '거의 확실히' 새 값을 다운로드하고, 다음 단계로 진행할 수 있다는 점에서 유용합니다.



가장 큰 단점은 로딩 화면이 있다는 것입니다. 사용자가 앱에 진입하는 데 장애가 될 수 있습니다. 그러나 앱 시작 시 꼭 필요한 그 외 다른 작업 혹은 네트워크 호출이 있다면, 큰 문제가 아닐 수 있습니다. 다른 초기화 작업들과 함께 원격 구성 값을 가져오면 됩니다.

하지만 앱에 아직 로딩 화면이 없다면, 다른 방법을 시도해봐도 좋습니다. 바로 다음과 같은 전략 말이죠.

전략 3: 다음 차례를 위한 값 로드

이 방식은 약간은 직관에 반하는 것처럼 보일 수 있습니다. 사용자가 앱을 시작하면, 즉시 activateFetched()를 호출합니다. 그러면 이전에 클라우드에서 가져온 기존 값이 적용됩니다. 그 후, 사용자가 앱을 사용하는 동안, 비동기 fetch() 호출을 시작하여 클라우드에서 새 값을 가져옵니다. 이 때, 호출이 완료되어도 추가적인 작업을 하지 않습니다. 클라우드에서 가져온 값은 사용자가 다음에 앱을 시작해서, activateFetched를 호출할 때까지 기기의 로컬 위치에 저장됩니다.



이 전략은 사용자가 즉시 앱을 사용할 수 있다는 점에서 매우 유용하며, 앱의 인터페이스도 갑자기 변경되지 않습니다. 분명한 단점은 사용자가 업데이트된 원격 구성 값을 보려면 반드시 앱을 다시 시작해야 한다는 점 입니다. 이러한 방식의 여러분의 앱에 적합한지 판단이 필요합니다.

원격 구성을 사용하여 타워 방어 게임의 일부 값을 미세 조정하는 경우라면 아마도 괜찮은 방법일 것입니다. 하지만 요일별 메시지 또는 특정 날짜별 콘텐츠를 전달하려고 원격 구성을 사용할 경우에는 그렇지 않을 수도 있습니다.

전략 3.5: 전략 2와 3 또는 전략 1과 3을 혼합한 전략

원격 구성의 한 가지 장점은 마지막으로 성공한 fetch() 호출이 수행된 시간을 알려줄 수 있다는 것입니다. 따라서 가장 최근에 가져온 원격 구성 데이터가 얼마나 오래되었는지 먼저 확인하는 혼합형 전략을 세울 수 있습니다. 원격 구성 데이터가 비교적 최근 데이터라면(예: 지난 48시간 가량의 데이터) "가장 최근 값 일괄 적용 후 다음 차례를 위해 다른 가져오기 수행" 전략을 실행할 수 있습니다. 그렇지 않으면 이전 두 가지 전략 중 하나를 수행할 수 있습니다.



캐싱에 대한 논의

로딩 전략에 대해 이야기하자면, 관련 주제인 캐싱을 빠뜨릴 수 없습니다. 개발자가 fetch()를 호출할 때마다 원격 구성의 값을 즉시 가져오지 않습니디. 해당 값은 12시간 동안 캐싱됩니다. 이 점이 때로는 개발자를 혼란스럽게 합니다. 이 캐시 시간을 다소 단축할 수는 있지만 네트워크 호출을 너무 자주 시작하면 클라이언트 또는 원격 구성 서비스에 의해 앱이 제한되기 시작할 수 있습니다.

그렇다면 왜 처음부터 이러한 캐싱과 호출 제한이 생긴 걸까요?

부분적으로는 앱이 수백만 명의 사용자로 확장되더라도 서비스를 무료로 유지할 수 있는 방법이기 때문입니다. 제대로 동작하는 캐싱을 추가함으로써 앱의 인기도에 상관없이 서비스를 통해 무료로 앱을 처리하도록 할 수 있습니다.

또한, 이것이 잘못된 코드로부터 서비스를 보호하는 훌륭한 방법이기도 하기 때문입니다. 물론 여러분은 아니겠지만, 실수로 fetch()를 너무 자주 호출하는 개발자들이 있습니다. 우리는 그 중 한 개발자가 실수로 DDoS 공격을 실행하여 전체 서비스가 중단되는 것을 원치 않습니다. 클라이언트 측 라이브러리가 캐시된 값을 제공하고 빈번한 네트워크 호출을 제한하면 서비스를 안전하게 유지하는 데 도움이 됩니다.

그러나 이는 사용자에게도 좋습니다. 훌륭한 캐싱 전략은 앱이 너무 자주 불필요한 네트워크 호출을 수행하여 사용자의 배터리 및 데이터를 낭비하지 않도록 방지할 수 있습니다.

원격 구성 구현을 개발하고 테스트하는 동안 이러한 제한이 불편할 수 있습니다. 이 경우,  개발자 모드를 활성화하여 로컬 제한 동작을 회피할 수 있습니다. 그러나 실제 상황에서는 사용자가 매일 여러분의 앱을 사용하고 있다고 해도 이런 유형의 캐싱 동작은 대개 유용합니다.

하지만 그보다 더 자주 값을 푸시하고 싶다면 어떻게 해야 할까요? 원격 구성을 통해 일종의 "시간의 메시지" 기능을 제공하려는 경우에는 어떻게 해야 할까요? 솔직히 말해서, 이것은 원격 구성 서비스가 이상적인 솔루션이 아니라는 신호일 수 있습니다. 대신 좀 더 적시에 Realtime Database를 사용하는 것을 생각해봐야 합니다.

반면, 긴급한 원격 구성 업데이트를 가끔씩만 적용하려는 경우가 있을 수도 있습니다. 아마도 실수로 최근의 변경 사항을 일괄적으로 적용하는 바람에 인앱 결제 데이터를 망가뜨린 경험이 있을지도 모르겠습니다. 그래서 모든 이가 즉시 새로운 값을 얻기를 바랄 수도 있을 것입니다. 그렇다면 어떻게 하면 캐시를 우회하여 클라이언트가 강제로 가져오기를 수행하도록 할 수 있을까요?

한 가지 해결책은 Firebase 클라우드 메시징을 사용하는 것입니다. FCM을 사용하면 모든 기기에 데이터 전용 알림을 보내 긴급한 업데이트가 보류 중임을 알릴 수 있습니다. 앱은 일종의 플래그를 로컬에 저장하는 방식으로 이러한 수신 알림에 응답할 수 있습니다. 앱은 사용자가 다음에 앱을 시작할 때 이 플래그를 찾을 수 있습니다. true로 설정하면 캐시 시간이 0인 가져오기를 수행하여 즉시 가져오기를 보장할 수 있습니다. 완료되면 이 플래그를 다시 원래대로 설정해야 앱이 평소의 캐싱 동작을 계속할 수 있습니다.

원격 구성 서비스에서 새 값을 즉시 깨우고 가져오는 방식으로 클라이언트가 이 수신 알림에 응답하도록 하고 싶으시겠지만, 앱의 모든 인스턴스가 한꺼번에 이를 수행하면 서버 측 제한에 걸릴 가능성이 매우 높으므로 권장하지 않습니다. 대신 위에서 제시한 전략을 고수하세요. 그러면 모든 사용자가 앱을 여는 데 걸리는 시간에 걸쳐 네트워크 호출이 분산될 것입니다.

원격 구성에서 값을 로드하기 위한 몇 가지 팁과 요령이 있답니다. 혹시 자신만의 요령이 있나요? YouTube 동영상에 이런 팁이나 요령을 댓글로 써서 공유하거나 Firebase Talk 그룹의 이야기를 듣고 원격 구성 구현을 어떻게 진행할지 알려주세요.