[go: up one dir, main page]


<블로그 원문은 이곳에서 확인하실 수 있으며 블로그 번역 리뷰는 이승민(Android GDE)님이 참여해 주셨습니다>
게시자: Dave Burke, 엔지니어링 부사장




우리는 지난달 Google I/O에서 Android Q의 새로운 기능부터 Kotlin 및 Jetpack의 최신 기능까지, Android 개발자를 위한 새로운 기능에 대해 얘기했습니다.
Android Q와 관련하여 혁신, 보안 및 개인정보 보호, 디지털 웰빙이라는 세 가지 테마를 강조했습니다. 우리는 사용자의 보안, 개인정보 보호 및 웰빙을 항상 최고의 우선순위로 생각하면서, 개발자가 5G, 폴더블폰, 더 넓은 화면, 온 디바이스 머신러닝 등의 최신 기술을 잘 활용하도록 돕고 싶습니다.
우리가 어떻게 점점 Kotlin을 우선하는 방향으로 가고 있고 CameraX, Jetpack SecurityJetpack Compose(Kotlin을 사용하는 Android용 첨단 반응형 스타일의 UI 도구 키트)와 같은 새로운 라이브러리로 계속해서 Jetpack을 확장하고 있는지에 대한 이야기도 했습니다. 기조연설 또는 기술 세션의 라이브스트림을 놓치셨다면, Android 및 Play 세션의 전체 재생목록을 확인해 보세요.
오늘 우리는 최종 Android Q API와 공식 SDK를 포함한 베타 4를 발표할 예정입니다. 바야흐로 올여름에 최종 출시를 앞두고 귀하의 앱을 적절히 준비할 때입니다!
오늘 바로 여기에 등록하여 Pixel 기기에 베타 4를 설치할 수 있습니다. 이미 등록해 Pixel 기기에 베타 3를 받으신 분은 베타 4로 자동으로 업데이트될 것입니다. Android Q 베타 프로그램에 참가 중인 파트너도 앞으로 몇 주에 걸쳐 기기를 베타 4로 업데이트하게 될 것입니다.
Android Q 베타를 시작하려면 developer.android.com/preview를 방문하세요.
베타 4의 내용
베타 4 업데이트는 최종 Android Q 개발자 API(API 레벨 29), 공식 API 29 SDK, Android Studio용으로 업데이트된 빌드 도구와 함께 Pixel 및 Android Emulator용 최신 Android Q 시스템 이미지를 포함합니다. 이와 함께, 이들은 앱과 Android Q의 호환성 여부를 테스트하고 Android Q 기능과 API로 빌드하는 데 필요한 모든 것을 제공합니다.
시작하려면 공식 API 29 SDK 및 도구를 Android Studio 3.4의 안정된 릴리스로 다운로드하거나, 최신 Android Q 지원을 위해서는 Android Studio 3.5 베타로 업데이트하세요. 그런 다음, 안내에 따라 환경을 구성하고 알려진 문제에 대해서는 출시 노트를 참조하세요.
Android Q와 호환되는 앱을 만드세요!
개발자 API가 최종 완성되고 릴리스 후보 빌드가 곧 발표되면, 모든 Android 개발자는 반드시 현재의 앱이 Android Q와 호환되는지 테스트해봐야 합니다. 가능하면 일찍 시작하시는 게 좋습니다.
Google Play에서 현재 앱을 다운로드하여 Android Q 베타 기기 또는 에뮬레이터에 설치한 후 테스트하세요. 앱이 동작하면서 올바로 실행되고, 정상적으로 보이고, 모든 Android Q 동작 변경 사항을 적절히 처리해야 합니다. 개인정보 보호 변경 사항, 제스처 탐색, 바이오닉 라이브러리에 대한 동적 링커 경로 변경 사항, 기타 변경 사항에 따른 영향이 있는지 살펴보세요.
새로운 위치 권한, 백그라운드 액티비티 시작에 대한 제한 사항, 데이터 및 식별자 변경 사항, 기타 주요 개인정보 보호 기능과 같은 Android Q 개인정보 보호 기능을 테스트하세요. 시작하려면 개인정보 보호 체크리스트를 확인하고 테스트할 다른 영역에 대한 정보는 동작 변경 사항 문서를 살펴보시기 바랍니다.
가로 모드의 Android Developers YouTube 채널 UI.업데이트된 Android Emulator를 사용하여 앱의 호환성 여부를 테스트할 수 있습니다.
API 29를 대상으로 하는 플랫폼을 업데이트할 계획이라면 범위 지정 저장소, 무선 스캔을 위한 위치 권한, 전체 화면 인텐트 권한을 테스트해봐야 합니다. 여기에서 앱에 영향을 미칠 수 있는 다른 변경 사항에 대한 내용을 읽어보실 수 있습니다.
제한적인 비 SDK 인터페이스의 사용에 대해 테스트하고 대신에 공개 SDK 또는 NDK에 해당하는 대상으로 이동하는 것도 중요합니다. 이러한 액세스를 강조표시하는 logcat 경고가 있는지 살펴보고 StrictMode 메서드 detectNonSdkApiUsage()를 사용하여 이들 액세스를 프로그래밍 방식으로 캐치하세요.
마지막으로, 앱에서 라이브러리와 SDK를 완전히 테스트하여 이들이 Android Q에서 예상대로 작동하고 개인정보 보호, 성능, UX, 데이터 처리 및 권한에 대한 모범 사례를 준수하는지 확인하세요. 문제를 발견하면 SDK의 최신 버전으로 업데이트하거나 SDK 개발자에게 연락하여 도움을 받으세요. 여기서 SDK 호환성 문제를 신고할 수도 있습니다.
테스트를 마치고 모든 업데이트를 완료했으면 즉시 호환 가능한 앱을 게시하는 것이 좋습니다. 그러면 Android 베타 사용자가 지금 바로 앱을 테스트할 수 있고 사용자가 Android Q로 업데이트할 때 원활하게 전환하도록 하는 데 도움이 됩니다.
우리는 이런 변화를 지원하는 것이 개발자 여러분을 위한 투자이기도 하다는 점을 알고 있으며, 앞으로 몇 개월 동안 최종 릴리스를 완성하기까지 개발자의 앱에 미치는 영향을 최소화하고 개발자의 의견 제시나 정보 제공에 빠르고 적절히 대응하기 위해 노력하겠습니다.
Android Q 기능과 API로 앱 강화
준비되셨으면 Android Q로 전환하고 앱에서 사용할 수 있는 새로운 기능과 API에 대해 알아보세요. Android Q 기능은 사용자 참여를 유도하고 사용자에게 더 많은 제어 능력과 보안 기능을 제공하고 앱의 성능까지도 향상하는 데 도움이 될 수 있습니다.
휴대기기 알림 창
Android Q는 알림에서 시스템 추천 회신 및 작업을 제공합니다.
예를 들어 폴더블폰을 최적화하고 앱에서 제스처 탐색 기능을 지원하여 최신의 혁신적 기기에서 완벽하고 더 넓은 화면 환경을 제공할 수 있습니다. 더 많은 사용자가 참여하도록 하려면 어두운 테마, 알림 내 추천 회신 및 작업, 바로가기 공유, 설정 패널을 지원해 보세요.
Google Maps 앱이 닫히면서 대양 항공 사진 이미지의 홈 화면이 표시됨
제스처 탐색을 통해 앱에서 더 넓은 화면 환경을 제공합니다.


앱에서 Wi-Fi를 통해 IoT 기기를 관리하는 경우 구성, 다운로드 또는 인쇄와 같은 기능을 위한 새로운 네트워크 연결 API를 사용해 보세요. 앱이 Wi-Fi 인터넷 연결을 관리하는 경우 위치 권한을 요청할 필요 없이 기본 설정 Wi-Fi 네트워크를 더 쉽게 표시하기 위한 방법으로서 네트워크 추천 API를 사용해 보세요.
카메라를 사용하신다면 Dynamic Depth 포맷에 대해 알아보세요. 미디어는 동영상 스트리밍에는 AV1, 다이내믹 레인지가 높은 동영상에는 HDR10+를 사용할 수 있습니다. 음성 및 음악 스트리밍의 경우 Opus 인코딩을 사용할 수 있으며, 음악가를 위한 네이티브 MIDI API가 제공됩니다.
Dynamic Depth를 사용하여 앱에 전문적인 번짐 및 보케 옵션을 제공할 수 있습니다.
캡션 또는 게임플레이 레코딩을 지원하려면 자동 재생 캡처를 사용하세요. 더 많은 사용자에게 어필하고 앱의 주목도를 높일 수 있는 훌륭한 방법입니다. 전력을 많이 사용하는 앱이라면 새로운 thermal API를 사용해 기기의 온도를 기준으로 앱 성능을 최적화해 보세요.
BiometricPrompt는 현재 첨단 기기에서 지문 인증을 지원하는 데 널리 쓰이는 방법이므로, 지문이나 다른 생체 인식 인증 방법을 사용하는 모든 개발자는 가능한 한 빨리 이 API를 사용해야 할 것입니다. 이러한 전환을 쉽게 해내려면 우리가 AndroidX 라이브러리에서 제공하는 이전 버전과 호환 가능한 BiometricPrompt API를 사용하세요. Android Q는 표준 및 수동적(안면 인식 및 기타 수동 모드의 경우 확인 없음) 인증 흐름을 둘 다 지원합니다.
이들은 Android Q의 수많은 새로운 기능과 API 중 일부일 뿐이며, 전부 보려면 개발자를 위한 Android Q 베타 사이트를 방문하시기 바랍니다.
Google Play에 앱 업데이트 게시
오늘 우리가 Android Q 베타 4를 발표함으로써, API 29에 대해 컴파일되거나 선택적으로 API 29를 대상으로 하는 앱을 Google Play에 게시할 길도 열릴 것입니다. 즉, 이제는 Google Play를 통해 사용자에게 업데이트를 푸시하여 Android Q 베타 4를 실행하는 기기에서의 테스트를 포함하여, 앱의 호환성을 테스트할 수 있다는 뜻입니다.
베타 4를 구하는 방법
아주 쉽습니다! 여기서 지원되는 Pixel 기기를 등록하시면 무선 다운로드를 통해 업데이트 파일을 받으실 수 있습니다. 이미 등록되어 계신 분은 곧 업데이트를 받아보시게 될 것이므로 따로 조치하실 사항이 없습니다. 다운로드 가능한 시스템 이미지도 여기서 구하실 수 있습니다. Android Q 베타 프로그램에 참여 중인 파트너는 앞으로 몇 주 정도에 걸쳐 기기를 업데이트할 예정입니다. 자세한 내용은 android.com/beta를 참조하세요.
Android GSI 이미지를 받으시면 지원되는 기기에서 훨씬 더 폭넓은 테스트를 수행할 수도 있고, 테스트할 기기가 없다면 Android Emulator에서 테스트할 수 있습니다.
늘 그렇듯이, 여러분의 의견과 정보 제공이 무척 중요하므로 여러분의 다양한 생각과 의견을 계속 알려주시면 고맙겠습니다. 핫리스트를 사용하여 플랫폼 문제(개인정보 보호 및 동작 변경 사항 포함), 앱 호환성 문제, 타사 SDK 문제를 제기하실 수 있습니다. 지금까지 훌륭한 의견을 많이 공유해 주신 점에 다시 한번 감사드리고, 다음 베타 릴리스에 좋은 의견을 최대한 많이 반영하여 통합하도록 노력하겠습니다.

Android Q에서 여러분의 앱을 보게 되기를 고대합니다!





<블로그 원문은 이곳에서 확인하실 수 있으며 블로그 번역 리뷰는 박해선(MachineLearning GDE)님이 참여해 주셨습니다>
게시자: Yang Li, Google AI 연구원

탭(tap)은 모바일 인터페이스에서 가장 흔히 사용되는 동작으로, 앱 실행부터 문자 메시지 입력까지 온갖 종류의 작업을 수행하는 데 사용됩니다. 전통적인 데스크톱 그래픽 사용자 인터페이스에서 클릭 가능한 요소(예: 버튼)의 스타일이 종종 관례적으로 정의되지만, 모바일 인터페이스에서는 스타일의 다양성으로 인해 여전히 사람들이 탭 가능한 요소와 그렇지 않은 요소를 구별하기 어려울 수 있습니다. 이런 혼동은 잘못된 어포던스(affrodance)(예: 버튼으로 착각할 수 있는 기능)를 유도하거나 기능을 찾지 못해 사용자의 불만, 불확실성, 실수로 이어질 수 있습니다. 인터페이스 디자이너는 인터페이스에 있는 항목의 탭 가능성을 분명히 나타내는 데 도움이 되는 연구 또는 시각적 어포던스 테스트를 수행하여 이런 혼동을 피할 수 있습니다. 하지만 이러한 연구는 오랜 시간이 소요되고 연구를 통해 얻는 결과가 특정 앱 또는 인터페이스 디자인으로 제한되는 경우가 많습니다.

우리는 'Modeling Mobile Interface Tappability Using Crowdsourcing and Deep Learning'이라는 제목의 CHI'19 논문을 통해 적정 규모로 모바일 인터페이스의 사용성을 모델링하기 위한 한가지 접근 방식을 소개했습니다. 우리는 사용자가 인식한 탭 가능성을 정량적으로 평가하기 위해 다양한 모바일 앱에 걸쳐 UI 요소를 연구하는 과제를 크라우드소싱했습니다. 우리의 모델 예측은 최대 90% 수준에서 사용자 그룹과 일치했는데, 이는 비용과 시간이 많이 드는 사용자 테스트를 수행할 필요 없이 디자인에 포함되는 인터페이스 요소에 대해 사용자가 인식하는 탭 가능성을 추정하는 데 머신러닝 모델을 효과적으로 사용할 수 있음을 입증합니다.


딥 러닝을 이용한 탭 가능성 예측


디자이너는 인터페이스에서 상호작용이 가능하다는 것을 나타내기 위해 요소의 색이나 깊이와 같은 시각적 속성(예: 링크의 파란색과 밑줄)을 종종 사용합니다. 이런 일반적인 기호 표현이 흔히 사용되긴 하지만, 각각의 특정한 디자인 환경에서 이런 기호 표현을 적용할 시점이 항상 분명한 것은 아닙니다. 더욱이, 디자인 트렌드가 계속 변화하고 발전함에 따라 전통적인 기호 표현은 항상 변형되고 도전받고 있어, 사용자 입장에서는 불확실성과 실수를 유발할 가능성이 있습니다.

사용자가 이처럼 변화하는 환경을 어떻게 인식하는지 이해하기 위해, 우리는 실제 모바일 앱에서 탭 가능성에 영향을 미칠 수 있는 기호 표현, 즉 요소의 종류(예: 확인란, 입력란 등), 위치, 크기, , 단어 등을 분석했습니다. 우리는 먼저 최대 3,500가지 앱에서 최대 20,000가지의 고유한 인터페이스 요소에 대해 클릭 가능한 것으로 인식되는 요소에 라벨을 지정해 줄 자발적 지원자를 크라우드소싱으로 모았습니다. 입력란은 제외하고, 종류 기호 표현은 사용자의 탭 가능성 인식에서 불확실성이 낮은 것으로 나타났습니다. 위치 기호 표현은 화면상에 나타나는 어떤 특징의 위치를 지칭하는데, 아래 그림에 나타낸 것처럼 모바일 앱에서 일반적인 레이아웃 디자인에 의해 결정됩니다.


위치를 기준으로 탭 가능한 요소와 그렇지 않은 요소의 정확도를 표시하는 히트맵으로, 색감이 따뜻한 느낌일수록 정확도가 더 높은 영역임을 나타냅니다. 사용자들은 인터페이스의 상부 중앙 쪽으로 갈수록 더 정확하게 탭 불가능한 요소로 라벨을 지정했고, 인터페이스의 하부 중앙 쪽으로 갈수록 더 정확하게 탭 가능한 요소로 라벨을 지정했습니다.
요소의 크기가 미치는 영향은 상대적으로 약했지만, 탭 불가능한 요소가 큰 경우 혼동을 주는 것으로 나타났습니다. 단어의 의미 역시 중요한 역할을 하기는 해도, 사용자들은 밝은 색과 짧은 단어를 보고 탭 가능한 요소로 인식하는 경향성을 보였습니다.

우리는 이러한 라벨을 사용하여 사용자가 탭 가능한 인터페이스 요소와 탭 불가능한 인터페이스 요소로 인식할 가능성을 예측하는 간단한 심층신경망을 훈련했습니다. 인터페이스 요소가 주어지면, 모델은 화면상에 표시되는 요소의 공간 컨텍스트(위치), 요소의 의미 체계와 기능(단어와 종류), 시각적 외관(크기뿐 아니라 원시 픽셀도 포함)을 포함한 다양한 특성을 사용합니다. 이 신경망 모델은 합성곱 신경망(CNN)을 적용하여 원시 픽셀에서 특성을 추출하고 학습된 단어 임베딩(embedding)을 사용하여 텍스트 콘텐츠 및 요소 속성을 표현합니다. 이러한 모든 특성을 연결하여 완전 연결 네트워크 층으로 주입하면되고, 그 출력은 요소의 탭 가능성에 대한 이진 분류 결과를 출력합입니다.

모델 평가


이 모델을 통해 사용자가 인식하는 각 인터페이스 요소의 탭 가능성(모델의 예측)과 개발자나 디자이너가 의도적으로 지정한 요소의 실제 탭 가능 상태 사이의 불일치를 자동으로 진단할 수 있었습니다. 아래 예에서, 우리의 모델은 사용자가 'Followers' 또는 'Following'과 같은 라벨이 탭 가능할 것이라 생각할 확률이 73%라고 예측한 반면, 이들 인터페이스 요소는 실제로는 탭 가능하도록 프로그래밍되지 않았습니다.


특히 사람이 모호하다고 느낄 때 사용자와 비교해 모델의 동작 방식을 이해하고 싶었습니다. 이를 위해 우리는 크라우드 소싱으로 290명의 지원자가 각자 인식한 탭 가능성에 대해 2,000개의 고유한 인터페이스 요소에 각각 라벨을 지정하여 제2의 독립적인 데이터세트를 만들었습니다. 각 요소에 대해 5명의 다른 사용자가 독립적으로 라벨을 지정했습니다. 그 결과, 지원자들은 샘플에 있는 요소 중 40% 이상을 일관되지 않게 라벨을 지정한 것으로 나타났습니다. 아래 그림에 나타낸 것처럼, 우리가 만든 모델은 인간의 인식에서 드러나는 이러한 불확실성과 상당히 잘 맞습니다.


일관성 데이터세트에 있는 각 요소에 대해 모델이 예측한 탭 가능성 확률(Y축)과 사용자가 부여한 라벨의 일관성(X축)을 보여주는 산점도.
어떤 요소의 탭 가능성이 사용자들의 라벨과 맞을 때 우리 모델은 더 명확한 답을 내놓는 경향이 있습니다. 즉, 탭 가능한 경우 1에 가까운 확률을, 탭 불가능한 경우 0에 가까운 확률로 예측합니다. 사용자가 어떤 요소에 대한 일관성이 부족할 때(X축의 중간 쪽으로 치우칠 때), 우리 모델 역시 덜 확실하게 결정합니다. 전체적으로, 우리 모델은 탭 가능한 UI 요소 식별에 있어서 평균 정밀도 90.2%, 재현율 87.0%로 사람이 인식하는 수준의 정확도를 달성했습니다.

탭 가능성의 예측은 우리가 사용자 인터페이스의 다양한 사용성 문제를 해결하기 위해 머신러닝으로 할 수 있는 일의 한 예에 불과합니다. 인터랙션 디자인(interaction design)과 사용자 경험 연구에 있어, 딥 러닝 모델이 크고 다양한 사용자 경험 데이터세트에서 의미 있는 정보를 추출하고 상호작용 동작에 대한 과학적 이해를 진전시키기 위한 도구를 제공할 수 있는 다른 도전 영역이 많이 있습니다.

감사의 말

본 연구는 Google 여름 인턴으로 이번 프로젝트에 참가한 Amanda Swangson과 딥 러닝 및 휴먼 컴퓨터 상호작용 연구원인 Yang Li의 공동 작업으로 진행되었습니다.


Flutter를 처음 시작할 때 가장 먼저 배워야 할 점 중 하나가 바로 Stateless 위젯입니다. 우리는 Stateless 위젯, 상태 저장 위젯, 상속되는 위젯 및 키를 다루는 시리즈 동영상을 녹화했습니다.
필자는 다음 동영상에서 Flutter 위젯이 무엇이고 Flutter 앱에서 StatelessWidget의 사용 방법을 안내합니다.





동영상 시청보다는 글로 읽는 쪽을 선호하는 분들을 위해, 이 게시물을 통해 Flutter 위젯이 무엇이고 어떤 식으로 결합해 인터페이스가 되며 Stateless 위젯으로 UI를 어떻게 작성하는지 설명하겠습니다.

파트 1 — Stateless 위젯을 사용하여 도그 앱 만들기
빠르게 진행하기 위해 여기서는 기본적인 앱으로 시작하겠습니다. 이 앱은 Scaffold 위젯, AppBar 위젯, 필자의 반려견인 록키(누렁이 래브라도)에 대한 정보를 표시하는 두 개의 Text 위젯을 포함합니다.




젯은 Flutter 앱의 기본 빌딩 블록입니다. 각 위젯은 사용자 인터페이스의 어느 한 측면의 불변적 선언으로, 많은 작업을 맡을 수 있습니다.
예를 들면 다음과 같은 위젯이 있습니다.

  • 구조적 위젯 — 예: 버튼 또는 메뉴
  • 글꼴이나 색 구성표를 전파하는 스타일 위젯
  • 레이아웃 관련 위젯 — 예: 여백
  • 기타 등등


기존 위젯에서 새 위젯을 작성할 수도 있으므로, 끝없이 조합할 수 있습니다. 그게 어떤 의미인지 보여드리겠습니다.
반려견 이름에 어떤 색을 나타내는 의미를 숨기고 싶다고 해봅시다.

Text 위젯을 DecoratedBox로 래핑하면 됩니다.

import 'package:flutter/material.dart';

void main() {
 runApp(new DogApp());
}

class DogApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'My Dog App',
     home: Scaffold(
       appBar: AppBar(
         title: Text('Yellow Lab'),
       ),
       body: Center(
         child: DecoratedBox( // here is where I added my DecoratedBox
           decoration: BoxDecoration(color: Colors.lightBlueAccent),
           child: Text('Rocky'),
         ),
       ),
     ),
   );
 }
}


그러면 Text 위젯에 배경색이 생깁니다.




텍스트 주변에 여백을 두고 싶을 수도 있을 것입니다.
그러면 여백 위젯을 추가하면 됩니다. 록키(Rocky)라는 이름 주변에 논리 픽셀을 8로 지정하여 여백을 주겠습니다.


import 'package:flutter/material.dart';

void main() {
 runApp(new DogApp());
}

class DogApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'My Dog App',
     home: Scaffold(
       appBar: AppBar(
         title: Text('Yellow Lab'),
       ),
       body: Center(
         child: DecoratedBox(
           decoration: BoxDecoration(color: Colors.lightBlueAccent),
           child: Padding(
             padding: const EdgeInsets.all(8.0)
             child: Text('Rocky'),
           ),
         ),
       ),
     ),
   );
 }


이제 여백이 생겼습니다.




이처럼 위젯을 합치는 프로세스를 '합성'이라고 합니다. 필자는 지금 간단한 위젯을 조합하여 인터페이스를 합성하는 중인데, 각각의 위젯이 한 가지 특정한 작업을 처리합니다. 즉, Padding은 여백을 주고 DecoratedBox는 상자를 꾸미는 등의 방식으로 구성됩니다.

자, 이제 필자가 정말 좋아하는 누렁이 래브라도 한 쌍을 새로 맞이하러 동물보호소로 간다고 해봅시다. Center 위젯 내부에 Column 위젯을 추가하고 새로 만난 반려견의 이름을 추가할 수 있습니다.


import 'package:flutter/material.dart';

void main() {
 runApp(new DogApp());
}

class DogApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'My Dog App',
     home: Scaffold(
       appBar: AppBar(
         title: Text('Yellow Lab'),
       ),
       body: Center(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [
             DecoratedBox(
               decoration: BoxDecoration(color: Colors.lightBlueAccent),
               child: Padding(
                 padding: const EdgeInsets.all(8.0),
                 child: Text('Rocky'),
               ),
             ),
             DecoratedBox(
               decoration: BoxDecoration(color: Colors.lightBlueAccent),
               child: Padding(
                 padding: const EdgeInsets.all(8.0),
                 child: Text('Spot'),
               ),
             ),
             DecoratedBox(
               decoration: BoxDecoration(color: Colors.lightBlueAccent),
               child: Padding(
                 padding: const EdgeInsets.all(8.0),
                 child: Text('Fido'),
               ),
             ),
           ],
         ),
       ),
     ),
   );
 }
}


SizedBox라는 위젯을 사용하여 이름과 이름 사이에 빈 공간을 추가하여 다음과 같은 결과를 얻습니다.




하지만 이 세 개의 이름 상자에서 반복 코드( 상용구라고도 함)를 많이 사용했다는 걸 알 수 있습니다. 이름만 하나 고르면 나머지 세부 사항을 알아서 처리해주는 나만의 위젯을 만들 수 있다면 정말 멋지지 않을까요?

네, 그렇게 할 수 있습니다.

StatelessWidget을 만들고 DogName이라 부르도록 하겠습니다. Stateless 위젯은 하위 요소로 구성되고(그래서 build() 메서드를 포함함) 추적할 필요가 있는 가변적 상태는 전혀 포함하지 않는 위젯입니다. 가변적 상태란 시간이 흐르면서 변하는 속성을 의미합니다. 사용자가 업데이트하는 문자열이나 도착/출발 표시를 업데이트하는 데이터 스트림을 포함하는 텍스트 상자를 예로 들 수 있습니다.


class DogName extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
  }
}


이 위젯에는 그런 속성이 전혀 없습니다. 이 위젯에는 변하지 않을 이름을 나타내는 문자열만 필요하므로, StatelessWidget이 제격입니다. 이 문자열을 최종 상태로 만들 수도 있습니다.


class DogName extends StatelessWidget {
 final String name;

 @override
 Widget build(BuildContext context) {
 }
}
생성자를 통해 문자열을 지정할 수 있으며, 그 모든 속성이 최종 속성이므로 이를 상수 생성자로 마크할 수 있습니다.

class DogName extends StatelessWidget {
 final String name;

const DogName(this.name);

 @override
 Widget build(BuildContext context) {
  }
}


아제는 같은 위젯을 사용하여 build 메서드를 정의하기만 하면 되며, 오직 지금 Text 위젯이 위젯의 이름 속성에서 가져온 문자열을 표시할 뿐입니다.

class DogName extends StatelessWidget {
 final String name;

const DogName(this.name);

 @override
 Widget build(BuildContext context) {
   return DecoratedBox(
     decoration: BoxDecoration(color: Colors.lightBlueAccent),
     child: Padding(
       padding: const EdgeInsets.all(8.0),
       child: Text(name),
     ),
   );
 }
}


이 위젯을 사용하여 원래 코드를 단순화하겠습니다.


import 'package:flutter/material.dart';

void main() {
 runApp(new DogApp());
}

class DogApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'My Dog App',
     home: Scaffold(
       appBar: AppBar(
         title: Text('Yellow Lab'),
       ),
       body: Center(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [
             DogName('Rocky'),
             SizedBox(height: 8.0),
             DogName('Spot'),
             SizedBox(height: 8.0),
             DogName('Fido'),
           ],
         ),
       ),
     ),
   );
 }
}


보시다시피, 이렇게 하면 UI는 동일하지만 StatelessWidget과 Flutter의 합성 기능을 사용한 덕분에 코드는 더 간결해집니다.






파트 2 — 위젯 트리와 요소 트리
StatelessWidget을 사용한 UI 작성이 어떤 식으로 이루어지는지 간단한 예를 보셨습니다. 그런데 이때 '이런 build 메서드의 작동 방식은 알겠지만, 이 메서드는 언제 호출되는 걸까?'라는 의문이 드실지 모르겠습니다. 자, 그렇다면 DogName 위젯 단 하나로 시작해봅시다.

우리는 Flutter로 빌드한 앱을 위젯으로 구성된 트리라고 생각하는 경향이 있는데, 그게 나쁜 건 아닙니다. 하지만 서두에서 언급한 바와 같이, 위젯은 앱 UI의 각 부분을 위한 구성일 뿐입니다. 위젯이 바로 UI의 청사진인 셈입니다.

그렇다면 이러한 구성은 무엇을 위한 것일까요? 그건 바로 요소를 위한 것입니다. 요소는 화면상에서 실제로 만들어지고 마운트되는 위젯입니다. 요소 트리는 어떤 특정 순간에 기기에 실제로 표시되는 내용을 나타냅니다.
각각의 위젯 클래스에는 대응하는 요소 클래스와 인스턴스 생성을 위한 메서드가 둘 다 있습니다.




예를 들어 StatelessWidget은 StatelessElement를 생성합니다.

위젯이 트리에 마운트되면 Flutter가 createElement() 메서드를 호출합니다. Flutter는 위젯에 요소를 요구하고, 요소를 생성한 위젯으로 다시 돌아가는 참조 표시와 함께 요소 트리 위에 그 요소를 팝업으로 나타냅니다.
그때 StatefulElement는 '하위 요소가 있는가?'라고 묻고 Widget의 build() 메서드를 호출합니다.






이 앱에는 하위 요소가 여러 개 있습니다. 그러면 이런 위젯이 자체의 대응 요소를 생성합니다.




이렇게 생성된 요소 역시 요소 트리에 마운트됩니다.




그래서 이제는 앱에 두 개의 트리가 있습니다. 하나는 화면에 실제로 표시되는 내용(요소)을 나타내고, 다른 하나는 이러한 요소가 생성된 청사진(위젯)을 보유합니다.

이때 예컨대 요소를 빌드하고 생성하는 프로세스를 시작하는 것은 무엇이고 이 전체 작업을 시작하는 것은 무엇인지 등의 사항이 궁금할 수도 있겠습니다. 그래서 아마 처음에는 미처 알아차리지 못했을 사항을 보여드리겠습니다.

전체 앱을 나타내는 DogApp 클래스는 그 자체가 바로 StatelessWidget입니다.


import 'package:flutter/material.dart';

void main() {
 runApp(new DogApp());
}

class DogApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'My Dog App',
     home: Scaffold(
       appBar: AppBar(
         title: Text('Yellow Lab'),
       ),
       body: Center(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [
             DogName('Rocky'),
             SizedBox(height: 8.0),
             DogName('Spot'),
             SizedBox(height: 8.0),
             DogName('Fido'),
           ],
         ),
       ),
     ),
   );
 }
}
Widget은 거의 모든 일을 할 수 있다고 말했던 것을 기억하십니까? 앱의 진입점인 main()을 살펴보면 main()이 runApp() 메서드를 호출하며 그것이 바로 시작점이라는 사실을 알 수 있습니다. runApp() 메서드는 위젯을 택해 화면 크기와 일치하는 높이와 너비 제약 조건을 가진 앱의 루트 요소로 마운트합니다.

import 'package:flutter/material.dart';

void main() {
 runApp(new DogApp());
}

class DogApp extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'My Dog App',
     home: Scaffold(
       appBar: AppBar(
         title: Text('Yellow Lab'),
       ),
       body: Center(
         child: Column(
           mainAxisAlignment: MainAxisAlignment.center,
           children: [
             DogName('Rocky'),
             SizedBox(height: 8.0),
             DogName('Spot'),
             SizedBox(height: 8.0),
             DogName('Fido'),
           ],
         ),
       ),
     ),
   );
 }
}


그런 다음, Flutter가 위젯 트리에 있는 모든 build() 메서드를 하나씩 진행하면서 모든 것이 빌드되고 화면상에 마운트되고 배치되어 렌더링 준비가 완료될 때까지 위젯을 생성하고 이를 사용해 요소를 만듭니다.




그게 바로 Flutter가 사랑스러운 누렁이 래브라도의 이름이 표시된 작은 텍스트 상자 세 개를 표시하는 방법입니다.





StatelessWidget으로 작성하여 인터페이스를 빌드하는 방법을 소개해 드렸습니다. 필자가 언급하지 않은 점 한 가지는 데이터 변경 시 인터페이스를 업데이트하거나 다시 빌드하는 방법입니다. 그건 StatelessWidget이 데이터를 변경하지 않기 때문입니다. StatelessWidget은 Stateless 위젯이므로 시간의 경과에 따라 데이터를 추적하거나 스스로 다시 빌드하는 작업을 트리거할 수 없습니다.

다행히도, Flutter에는 StatefulWidget도 있으므로 이 시리즈 게시물의 다음 편에서 그에 관한 내용을 다루도록 하겠습니다.

Flutter와 Flutter의 수많은 위젯에 관해 더 자세히 알아보시려면 flutter.io를 살펴보세요.

또한 여기서 시리즈의 다른 편을 시청하거나 Flutter 코드랩을 사용해 보실 수도 있습니다.