[go: up one dir, main page]


(1) 앱 품질 관련 가이드라인

1. 앱 출시 이후 품질 개선하기
2. 핵심 앱 품질 가이드라인
3. 태블릿 앱 품질 체크리스트


자신이 개발한 앱이 많은 사람에게 오래도록 사랑받을 수 있도록 만드는 방법이 궁금하신가요? 효과적으로 사용자에게 ‘좋은 인상을 주고, 설치하고, 평가하게’ 하는 순환 고리를 만드는 방법은, 바로  “제품을 개선하는 것"입니다. 이는 오랜 시간에 걸쳐 입증된 확실한 방법입니다. 앱을 출시한 이후 어떻게 품질을 높일 수 있는지 앱 출시 이후 품질 개선하기에서 확인해 보세요!

앱의 품질은 설치 및 사용자 등급이나 평가, 참여, 사용자 보유 등의 측면에서 성공 여부에 장기적으로 직접적인 영향을 줍니다. 안드로이드 사용자는 고품질의 앱을 기대합니다. 핵심 앱 품질 가이드라인에서 다루는 핵심 앱 품질 기준 내용 및 관련 테스트를 통해서 기본적인 품질을 측정하실 수 있습니다. 모든 안드로이드 앱은 반드시 본 기준에 부합해야 합니다.

태블릿 앱은 강력한 기능은 물론, 직관적이고 우수한 UI 디자인을 제공하여 사용자들의 기본적인 기대에 부응하도록 해야합니다. 본인의 앱이 태블릿 사용자를 타겟으로 하고 있다면, 태블릿 앱 품질 체크리스트를 통해서 앱 성공 여부에 큰 영향을 주는 품질이나 기능, UI 등 주요 요소에 초점을 맞추어 품질을 확인할수 있습니다. 사용자에게 최고의 제품을 제공하기 위해서 체크리스트 권장 사항을 가능한 최대한으로 적용하시기 바랍니다.


(2) 안드로이드 디자인 관련 문서

1. 안드로이드 디자인 가이드 (외부 번역)
http://klutzy.github.com/android-design-ko/

*중요 문서 (위의 디자인가이드에 포함)
> Action Bar
> Navigation
> Pure Android
> Multi-pane Layouts


2. 태블릿용 앱을 만들 때 반드시 참고!! 
태블릿용 앱 디자인 도움말

3. 일관성있는 방향 탐색 기능을 제공하세요.
이전(Back) 버튼 및 상위(Up) 버튼 방향 탐색 가이드라인


(3) 안드로이드 앱 홍보 관련 문서

1. Play 스토어에서 자신의 앱을 홍보하기 위해서 반드시 “추천 이미지" 등록!! 
Play 스토어 추천 이미지 가이드라인

2. 전 세계 사용자를 타겟으로~! 국가별로 홍보 그래픽을 현지화하세요! 

Google Play에서 홍보 그래픽을 국가별로 현지화 

(4) 기타 문서


1. 안드로이드 애플리케이션 SD 카드에 설치하기
2. 안드로이드 사용자 데이터 처리


YouTube CodeLab Korea 행사는 YouTube에 관심있는 개발자 여러분들을 모시고 샘플 애플리케이션으로부터 YouTube의 다양한 기능들을 따라서 구현해 보면서 YouTube API를 좀더 깊이 이해하고 적용에 필요한 아이디어를 얻으실 수 있도록 합니다.

이번 YouTube CodeLab Korea 행사에서는 컨텐트를 생성하고, YouTube에 올리고, 올라간 동영상을 앱으로 구현하고 또한 분석까지 해보실 수 있도록 하는 실습 기회를 제공합니다.

일시: 2013년 2월 2일 토요일 오전 10시 ~ 오후 5시
장소: 역삼역 2번출구 강남 파이낸스센터 21층 구글코리아

일정
- 10시 ~ 11시: YouTube API 소개
- 11시 ~ 12시: YouTube 동영상 생성 기능 실습
- 12시 ~ 1시: 점심식사
- 1시 ~ 2시: 동영상 업로드 기능 구현 실습
- 2시 ~ 4시: 동영상 플레이 앱 개발 실습
- 4시 ~ 5시: 동영상 분석 기능 구현 실습

안드로이드 앱 개발 경험이 있는 분들이라면 누구나 쉽게 따라하실 수 있습니다. 개발자 여러분들의 많은 신청 부탁드립니다~! 소개 세션은 영어로 제공되나 최대한 천천히/명료하게 발표할 예정이며, 전체 과정에서 샘플 코드가 제공될 예정이므로 세션 내용을 모두 듣지 않으셔도 안드로이드 개발 경험이 있으시다면 과정을 따라 하시는데 문제가 없습니다. 또한 개발 작업을 도와줄 조교(?)도 준비되어 있으며, 개발에 필요한 자료들도 행사 전에 별도로 보내드릴 예정입니다. :-)

YouTube에 관심있는 개발자 여러분들에게는 YouTube API의 전체적인 내용을 이해하실 수 있는 매우 좋은 기회가 될 것입니다. 부디 호기심 많은 개발자 여러분들의 많은 신청 부탁드립니다~!

- 신청은 1월 31일 오전 10시까지 해 주시기를 부탁드립니다.
- 참가 신청은 선착순이 아니며 선정되신 분들께는 별도로 연락을 드립니다.
- 개발에 필요한 장비(노트북, 안드로이드폰 등)는 직접 가지고 오셔야 합니다.
- 참석자 전원에게는 구글에서 마련한 소정의 기념품을 드립니다.
- 점심 식사가 제공되며 주차는 제공되지 않습니다.

신청하기!!

얼마전, 전 세계 중고등학생을 대상으로 Google Code-in (GCi) 콘테스트가 개최되었습니다.
GCi는 보다 많은 학생들에게 오픈 소스의 세계를 소개하고자 구글에서 개최하는 콘테스트로, 이번에 3회째 접어든 연례 행사입니다. (자세한 내용 링크 참조)


이번 GCi 2012에는 전년 대비 20배에 가까운 한국 학생들이 참여하여, 이를 기념하고자 지난 1월 25일 금요일, 구글 코리아에서는 참가자들을 오피스로 초청하여 점심 식사를 나누고 현직 엔지니어들과 함께 다양한 이야기를 나누는 뜻깊은 시간을 가졌습니다.


이번 행사에는 총 39명의 등록자 중에서, 초청 메일에 회신을 준 19명의 학생들이 참여하였고 오픈 소스 및 개발에 관련한 조언을 위해 5명의 현직 엔지니어분들이 함께 했습니다.

* 초청 엔지니어 소개*
김남형/김준수/김민찬 (LG전자/ 리눅스 커널 커미터)
홍영기 (LG전자/Chromium 커미터)
정재윤 (Google 엔지니어)




자기 소개를 시작으로, 오픈 소스의 장/단점과 현재 개발 관심 분야에 대해서 나누는 질의 응답시간을 가졌습니다. 학생들의 적극적인 질문과 다양한 분야에 대한 관심으로 엔지니어분들과 함께하는 Q&A 세션이 금세 지나가고, 다섯 개의 소그룹으로 나눠서 멘토와 함께 하는 점심 식사 시간을 가졌습니다.

*엔지니어 멘토들이 후배들에게 전해준  팁*
지금의 열정과 관심을 잃지 않고 지속적으로 자신의 관심분야를 찾아갈 것.
오픈 소스 커뮤니티에서 위축되지 말고 적극적으로 참여해 볼 것.
개발자로서의 기본적인 소양을 기르기 위한 노력을 할 것 - 소통 능력, 논리력, 수학 등
보다 깊이 있는 실력을 배양하기 위해서 기본에 충실할 것. 

실제로 등록 이후에 과제 제출을 하기까지가 쉽지 않아 많은 학생이 과제를 완료하지 못했지만, 이번 참가 자체에 큰 의미가 있었다고 봅니다. 2013년에 있을 GCi 2013에서는 많은 한국 학생들이 등록과 함께 과제 제출까지 완료하는 더욱 적극적인 참여를 기대해 봅니다!

참가자 및 초청 엔지니어 단체 사진


작성자: Roman Nurik
작성일: 2012년 11월 26일
원문: http://android-developers.blogspot.hk/2012/11/designing-for-tablets-were-here-to-help.html

Google Play에 아주 괜찮은 안드로이드폰 앱을 출시했고, 사용자 반응이 아주 좋습니다. 이제 뒷짐 지고 치솟는 다운로드수를 감상하고 계시군요. 축하합니다! 하지만 여느 진취적인 개발자처럼, 여러분도 “어떻게 하면 내 앱이 더 큰 성공을 거둘 수 있을까? 라고 고민하실 것입니다. 해결책은 바로, ‘동일하게 훌륭한 사용자 경험을 제공하는 태블릿용 앱을 만들라!’는 것입니다.

사용자들은 태블릿용 앱을 좋아합니다. 예를 들어 Mint.com은 넓은 화면 공간을 제공하는 태블릿이 휴대폰보다 7배 가량 더 많은 예산 데이터를 활용하게  만든다는 사실을 파악했습니다. 그리고 TinyCo는 평균적으로 사용자가 휴대폰보다 태블릿에서 35% 더 많은 지출을 한다는 것을 알아냈습니다. 따라서 지금이야말로 더욱 포괄적이고 일상적인 컴퓨터 사용에 대한 소비자들의 욕구를 만족시키기 위하여, 대형 화면 디바이스로 옮겨가기 위한 전략을 세우기에 적절한 시기입니다.

본 포스팅에서는 사용자에게 훌륭한 태블릿 경험을 제공하는 앱을 만들기 위해 활용할 수 있는 일부 리소스에 대해서 설명 드리겠습니다. 이는 상품 관리자, 디자이너, 개발자, QA 엔지니어에 이르기까지 앱 개발 단계에 있는 모든 사람에게 유용한 정보입니다. 



안드로이드 디자인 가이드라인

안드로이드 앱 디자인이나 개발에 대한 내용을 다루기 위해서는, 먼저 안드로이드 디자인 가이드라인을 참고해야 합니다. 대부분 섹션에서 안드로이드 디바이스 전체와 관련된 사항을 다루지만, 어떤 섹션에서는 특히 태블릿 디자인과 관련된 내용을 주로 다루고 있습니다.


디바이스와 디스플레이(Devices and Displays) 페이지에서는 밀도 독립성에 대한 개념을 소개합니다. 예를 들어 넥서스 4, 넥서스 7, 모토로라 XOOM 모두 비슷한 픽셀 해상도를 가지고 있지만 (각각 1280x768, 1280x800, 1280x800) 화면 크기는 매우 다릅니다. 픽셀 대신, dip(density-independent pixels)로 생각해 봅시다. dip의 경우, 넥서스 4(640x384 dp)와 넥서스 7(960x600dp), 넥서스 10 혹은 모토로라 XOOM(1280x800 dp)의 차이를 이해하기 쉬워집니다.

매트릭스와 그리드(Metrics and Grids)에서 논의된 48dp 리듬을 따르면 (특히 태블릿용에서) 크기 요소를 어림 짐작하지 않아도 됩니다. 미심쩍은 경우에는 가로 및 세로 크기 엘리먼트로 48dp (혹은 16dp finer grid) 배수를 사용하세요. 예를 들어, 큰 화면에서 띄엄 띄엄 콘텐츠를 보여줄 때, 96dp 혹은 144dp로 넉넉한 사이드 여백을 사용하세요. 혹은 10인치 태블릿의 마스터/디테일 레이아웃에서 마스터 창의 너비를 결정할 때, 240dp 혹은 288dp 폭으로 마스터 콘텐츠가 어떻게 보이고 어떤 느낌을 주는지 확인하세요.  
멀티창 레이아웃(Multi-pane Layouts) 가이드에서는 앱 방향 탐색을 개선하는 동시에 사용 가능한 화면 공간을 최적화하기 위해  연관된 뷰를 단일 화면으로 결합시키는 용례와 예제를 다룹니다. 또한 모든 방향에서 동일한 기능을 유지하면서 가로 및 세로로 콘텐츠를 배열하기 위한 전략도 다룹니다. 사용자가 가로와 세로 방향을 모두 즐겨 사용하기 때문에 휴대폰용 앱 개발보다 방향 전환에 더욱 적절하게 대응해야 합니다.

다운로드 가능한 스텐실(Downloadable Stencils)에서는 레퍼런스 디바이스 초안 및 정확한 사이즈의 액션바 등을 갖추어 디자이너들에게 최고의 모형을 위한 출발점을 제공합니다. 


개발자를 위한 안드로이드 교육

개발자 사이트의 교육 섹션에서는 과제 기반의 기술 교육 자료를 제공하며 플로우 다이어그램, 예제 코드, 샘플 프로젝트 등을 갖추고 있습니다. 몇몇 강좌에서는 개발자에게 모든 화면 사이즈에 앱 크기를 조정하는 방법을 제공하고 있습니다.



앱 개발 프로세스의 초기 디자인 과정에 더욱 초점을 맞춘 ‘효과적인 방향 탐색 디자인 강좌(Designing Effective Navigation)’는 효과적으로 태블릿에서 화면을 구성하고 그룹화하는 방법을 제공하며, 이러한 방법으로 간단한 뉴스 읽기 애플리케이션을 위한 와이어 프레임 예제도 보여줍니다.

화면 분할로 다이나믹 UI 만들기(Building a Dynamic UI with Fragments)와 다중 화면을 위한 디자인(Designing for Multiple Screens) 강좌에서는 안드로이드 리소스 프레임워크와 결합하여 어떻게 화면 분할을 사용하는지 보여줍니다. 강좌를 통해서 리소스 alias를 활용하여 코드 재활용을 최대화하고 애플리케이션 크기를 최소화하면서 런타임 시 태블릿과 휴대폰 레이아웃 사이에서 쉽게 선택하는 방법을 보여줍니다. 또한 현재 레이아웃에 기반하여 UI 플로우를 조정하는 기술을 보여줍니다.

마지막으로, 엄밀히 말하면 교육 강좌는 아니지만, 태블릿 및 휴대폰 지원하기(Supporting Tablets and Handsets) 문서에서는 주요 우수 사례에 대한 정보를 제공합니다. 텍스트는 건너뛰고 바로 코딩 작업을 시작하고 싶으다면, 이클립스용 안드로이드 개발자 툴을 사용하여 클릭 몇 번으로 여러분의 앱에 휴대폰 및 태블릿 지원을 갖춘 마스터/디테일 플로우를 추가할 수 있습니다.

안드로이드 다지인 인 액션 하이라이트

Developer Relations 팀에서 함께 모여 안드로이드 디자인 우수 사례에 대해 토론하기 위해 매주 Android Design in Action 라이브 쇼를 진행합니다. 또한 안드로이드 앱의 형태나 느낌을 설명하기 위해서 오리지널 ‘재설계’ 모형을 제공합니다. 최근 에피소드에서는 반응형 디자인(responsive design), 즉 어떤 화면 사이즈나 폼팩터에서도 실행될 수 있도록 구성할 수 있는 유연한 앱 디자인에 초점을 맞췄습니다.

해당 에피소드에서는 Google 캘린더에서 캘린더 이벤트를 만드는 것에서부터, PattrnPocket에서 월페이퍼와 스토리를 둘러보고 TED에서 동영상을 보며 오픈 소스 Google I/O 2012 앱에서 컨퍼런스 스케줄을 관리하는 것에 이르기까지 안드로이드에서 실행되는 성공적인 반응형 디자인 예제를 소개하였습니다. 또한 주기적으로 태블릿 디자인 컨셉을 선보이고 있으니, 디자인 아이디어를 얻으시려면 매주 라이브 쇼를 시청해주세요.



태블릿 앱에 대한 더욱 많은 아이디어를 원하시면, 다음의 앱들을 확인해 보세요.
Expedia Hotels & Flights, Pulse News, SeriesGuide, Tasks, Timer


태블릿 품질 체크리스트

developer.android.com에서 “배포(Distribute)” 섹션에서 최근에 공개된 태블릿 앱 품질 체크리스트를 통해서 본인의 앱이 태블릿용으로 적절한지 다양한 기술 측면에서 확인할 수 있습니다. 해당 체크리스트는 Google Play 팀에서 직원이 선정한 태블릿용 컬렉션에 추천 앱으로 선정되는 방법 중에 하나입니다. 따라서 해당 체크리스트에 정의된 기준을 모바일 상품에 관련된 모든 사람이 숙지하도록 해야 합니다.





지금 바로 시작하세요!

2013년은 안드로이드 태블릿을 위한 또 다른 흥미로운 해가 될 것 같습니다. 본 포스팅과 developer.android.com에서 다루었던 우수 사례 및 예제를 참고하여 진화하는 디바이스 환경에서 여러분의 앱이 성공적으로 자리매김할 수 있도록 만들어 보세요! 본인의 앱에 대해서 구체적인 질문이 있으면 구글플러스(+Android Developers)나 트위터(@AndroidDev)로 문의하세요!

작성자: Daniel Russell (Über Tech Lead for Search Quality and User Happiness)
작성일: 2012년 1월 11일
원문: http://googleresearch.blogspot.com/2013/01/advanced-power-searching-with-google.html

어떤 역사적인 카페에서 노벨 수상자가 시상이 떠올랐을까요? 지난 세 번의 바리스타 세계 챔피언십 우승자 중에서, 누가 자신의 국가에서 나오는 커피를 사용하지 않았을까요? “커피 문화에 대한 이모저모”에 관한 블로그 포스팅을 쓰려고 한다면, 이러한 질문에 대한 답을 어떻게 찾을 수 있을까요? 기타 어떤 정보를 찾고 싶으신가요? 구글 고급 파워서치 온라인 강좌에 등록하여 찾아보세요.

일전에 제공되었던 구글 파워서치 강의에 더하여, 구글 고급 파워서치는 심도있는 검색을 할 수 있도록 도와 줍니다. 저의 블로그a Google a Day에 포스팅해 둔 것과 유사한 복잡한 검색 과제들을 해결하고, 첫번째 강좌에서 다루지 않았던 구글 고급 검색툴을 살펴볼 것입니다.

가장 흥미로운 검색은 처음 해답을 뛰어 넘는 곳으로 검색자를 인도합니다. 또한 찾아가는 데까지 한 가지 방법만 있는 것은 아닙니다. 싱글 쿼리로 해결 할 수 없는 질의들을 검색할 때, “검색(search)은 금세 “조사(research)가 됩니다. 구글 서치는 웹 지식의 세계로 깊이 빠져들 수 있게 하는 다양한 툴을 제공합니다.  

www.powersearchingwithgoogle.com에 방문하여 구글 온라인 검색 강좌에 대해서 알아보세요. 또한 구글 퀵 레퍼런스 가이드로 파워 서치하기에서 검색 팁을 확인하세요. 고급 파워 서치는 1월 23일에 시작, 2월 8일에 종료됩니다.


작성일: 2013년 1월 8일
작성자: Tim Bray
원문:  http://android-developers.blogspot.kr/2013/01/verifying-back-end-calls-from-android.html

대부분의 안드로이드 앱은 데이터를 유지하고 공유하기 위해서 일종의 서버 사이드(server side) 백엔드를 가지고 있습니다. 심지어 아주 기본적인 게임도 사용자의 최고 점수를 기억해야 합니다. 하지만 백엔드를 만드려면 어떤 앱과 통신하는지, 그리고 사용자는 누구인지 백엔드 코드가 알 수 있어야 한다는 문제가 있습니다. 클라이언트 앱과 소통하기 위해 HTTP 엔드포인트를 가지고 있기는 하지만, 메세지를 누가 보냈는지 서버측 코드가 어떻게 알 수 있을까요? 실제로 HTTP POST 요청은 누구든지 어디서나 보낼 수 있습니다. 결국, 아무나 사용자 신분을 알 수만 있다면 사용자로 가장할 수 있는 것이 아닐까요?

모바일 디바이스에서 사용자들에게 사용자 이름과 비밀번호를 기입하게 하는 것은 사용자를 불편하게 만드는 일입니다. 특히 앱을 설치하고서 해당 앱에 인터넷을 사용하고 신원을 알 수 있는 권한을 주었다면, 더 이상 사용자를 번거롭게 해서는 안됩니다.

안드로이드 2.2 이상 버전의 모든 디바이스에서 현재 사용이 가능한 Google Play 서비스에서는 Google 계정 사용을 기반으로 해당 문제를 해결할 수 있는 좋은 방법을 제공합니다.


요약

이를 위해서 여러 단계의 과정이 필요합니다. 전체 설명에 앞서, 짧은 버전으로 설명하자면 다음과 같습니다.
> Google Play 서비스를 통해 사용 가능한 GoogleAuthUtil 클래스로 “ID Token”이라는 스트링을 받습니다. 해당 토큰을 벡엔드로 보내고 벡엔드에서는 토큰을 사용하여 빠르고 간단하게 어떤 앱이 보냈으며 사용자가 누구인지를 검증합니다.



이러한 기능은 App Engine의 새로운 Cloud Endpoints 기능과 같은 Google facilities에 내장되어 있는데, 앱/백엔드 아이덴티티를 간단한 프로그래밍 모델로 만든 것입니다. 이제 세부사항을 살펴 봅시다. 



앱 등록

본 과정에서 Google API 콘솔을 꽤 많이 사용하게 될 것입니다. 이를 위해서 새로운 프로젝트를 만들어야 합니다. 하지만 읽기 편한 괜찮은 이름과 이미지로 브랜드화 한다고 해도, 해당 리소스는 본 시나리오 상에서는 사용되지 않습니다.
프로젝트가 다수의 다른 Google API에 접근할 수 있도록 승인할 수 있습니다. 하지만 해당 시나리오에서는 필요하지 않은 절차임을 거듭 말씀드립니다. 그리고 사람들을 프로젝트 멤버로 승인할 때, 관리자 기능을 부여하게 되기 때문에 신중하게 결정해야 합니다.



Client ID 만들기

프로젝트에 두 개의 서로 다른 OAuth 2.0 “Client ID”를 만들어야 합니다. 하나는 “웹 애플리케이션을 위한 Client ID”입니다. 이름 붙이기나 이미지 작업은 무시해도 됩니다. 필요한 것은 Client-ID입니다. Client ID는 9414861317621.apps.googleusercontent.com와 같은 형태를 가지고 있습니다.

그리고 다른 하나는 “안드로이드 앱을 위한 Client ID”입니다. 해당 Client ID를 만들기 위해서는 앱 패키지 이름과 인증 서명이라는 두 가지 정보를 제공해야 합니다. 패키지 이름은 AndroidManifest.xml에서 상위 “package” 속성에서 주어지는 대로 자바 스타일 리버스 DNS입니다. 예를 들면 com.example.identity와 같은 형태입니다.

앱의 인증 서명을 얻으려면, 다음과 같이 Shell 명령어를 사용하세요.
$ keytool -exportcert -alias <your-key-name> -keystore <your-key-store-file> -v -list
“SHA1”라고 표기된 부분을 복사해서, 개발자 콘솔 필드에 복사하고 Client ID를 생성합니다. 다시 말씀드리지만, 필요한 것은 Client ID 스크링 뿐입니다. 



안드로이드 앱에서

Google Play 서비스 GoogleAuthUtil 클래스를 호출하여 ID 토큰을 받아와야 합니다. 해당 절차는 Access Token 토큰 받아오기에 설명되어 있습니다. 추가적으로 좋은 방법이 하나 있습니다. 바로 getToken(email, scope) 메소드에 scope 인자 값입니다. 해당 값은 스트링으로 audience:server:client_id:X 으로 표기되는데, X가 웹 앱을 위한 위에서 설명한 Client ID 값입니다. Client ID가 위에서 주어진 예제 값이라면 scope 인자 값은 audience:server:client_id:9414861317621.apps.googleusercontent.com가 되겠죠. 



문제 해결

보통 OAuth을 요청할 때, 디바이스 사용자는 본인의 신원을 사용하여 어떤 리소스 등을 접근해도 괜찮은지 확인하는 절차를 접하게 됩니다. 하지만 이 경우에 해당 프로젝트를 제어하는 개발자와 관련 사항에 대해 사용자가 이미 동의한 상태이기 때문에 시스템에서는 scope 인자 안에 있는 서버측 Client ID를 보고, 안드로이드 앱과 동일한 프로젝트라는 것을 확인하여 사용자를 번거롭게 할 필요 없이 토큰을 주게 됩니다. 



토큰 보내기

서버와 백엔드와 통신할 준비가 되면 토큰 스트링을 보내야 합니다. 가장 좋은 방법은 POST 메세지 안에 포함시켜서 보내는 것입니다. URL 파라미터로 넣을 수 있지만, 이 경우에 종종 느리게 동작할 수 있습니다. 중간 침입자가 토큰을 가로채지 못하도록 하기 위해서 반드시 HTTP 연결을 사용해야 합니다. 추가적인 통신을 주고 받을 필요가 없습니다. 백엔드에 게임 최고점을 보내는 것이라면, 추가 인자로 ID 토큰 스트링을 넣으면 됩니다.  

토큰 사용하기


서버가 안드로이드 앱으로부터 토큰을 받을 때, 이를 검증하는 것이 매우 중요합니다. 이를 위해서 다음과 같은 두 단계가 필요로 합니다.

1. 실제로 Google에서 서명을 받았는지 검증한다
2. 실제로 의도된 것인지 검증한다.


서명 검증하기

Google의 공개/개인 키 쌍을 사용하여 서명되고, www.googleapis.com/oauth2/v1/certs에서 Google이 공개 키를 공개한다고 (주기적으로 변경) 밝혀졌습니다. 그렇다면 가서 확인해 보겠습니다.

ID 토큰(실제로 JSON 웹 토큰)이 앞서 말한 인증 중 하나로 서명되었는지 확인해야 합니다. 다행히 이를 위한 괜찮은 라이브러리들이 있습니다. 본 포스팅에서 Java, Ruby, PHP를 위한 설명을 해드리겠습니다. 라이브러리들은 Google 인증을 캐쉬하고 필요할 때만 리프레시할 수 있기 때문에 검증이 (거의 항상) 빠른 정적 호출입니다.  



토큰 필드 검증하기

ID 토큰이 JSON 페이로드를 가지고 있으며, 서명을 인증하는 대부분의 라이브러리도 해시나 딕셔너리 등으로 전달해 준다고 밝혀졌습니다. 따라서 aud나 cid, email과 같은 지정된 필드를 검색할 수 있습니다.

첫째로, aud 필드를 보고 안드로이드 앱의 scope 인자에 포함된 스트링인 Client ID와 동일한지 확인해야 합니다. 이 단계를 절대 생략해서는 안됩니다. ID 토큰을 검증하지 않으면, 다른 개발자가 여러분의 서비스로 요청을 도용할 수도 있습니다.

선택적으로, cid라는 필드를 보고 안드로이드 앱 Client ID와 동일한지 확인할 수 있습니다. 참고로, 최상위 프로젝트에서 고유의 Client ID를 가진 여러개의 서로 다른 안드로이드 클라이언트 앱을 가질 수 있습니다. 


세 가지 과정을 모두 완료했다고 가정해 봅시다. 그렇다면 다음과 같은 사항을 알 수 있습니다.
1. 토큰이 Google에서 발행되었다.
2. 페이로드의 이메일 필드에서 확인된 사용자의 디바이스로 토큰이 보내졌다.

그리고 다음 사항에 대해서 "고신뢰(high confidence)"를 가질 수 있습니다.
3. 페이로드의 cid 필드에서 Client ID로 확인된 안드로이드 앱으로 토큰을 얻었다. 

비호환 및 루팅된 안드로이드 디바이스는 해당 정보를 조작할 수가 있기 때문에 Client ID만이 “고신뢰(high confidence)”를 가지고 있다. 하지만 해당 디바이스들이 가짜 Google 서명이나 디바이스 사용자 인증을 조작할 수 없다.



다음 단계는?

다음은 여러분의 손에 달려 있습니다. 어떤 사용자, 어떤 앱과 통신해야 하는지 알고 있기 때문에 해당 정보로 무엇을 해야할지는 본인에게 달려 있습니다.



코드 예제

다음은 Google 자바 라이브러리를 사용하여 ID 토큰 체커를 구현하는 자바 클래스입니다.

import java.io.IOException;
import java.security.GeneralSecurityException;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
public class Checker {

    private final List mClientIDs;
    private final String mAudience;
    private final GoogleIdTokenVerifier mVerifier;
    private final JsonFactory mJFactory;
    private String mProblem = "Verification failed. (Time-out?)";

    public Checker(String[] clientIDs, String audience) {
        mClientIDs = Arrays.asList(clientIDs);
        mAudience = audience;
        NetHttpTransport transport = new NetHttpTransport();
        mJFactory = new GsonFactory();
        mVerifier = new GoogleIdTokenVerifier(transport, mJFactory);
    }

    public GoogleIdToken.Payload check(String tokenString) {
        GoogleIdToken.Payload payload = null;
        try {
            GoogleIdToken token = GoogleIdToken.parse(mJFactory, tokenString);
            if (mVerifier.verify(token)) {
                GoogleIdToken.Payload tempPayload = token.getPayload();
                if (!tempPayload.getAudience().equals(mAudience))
                    mProblem = "Audience mismatch";
                else if (!mClientIDs.contains(tempPayload.getIssuee()))
                    mProblem = "Client ID mismatch";
                else
                    payload = tempPayload;
            }
        } catch (GeneralSecurityException e) {
            mProblem = "Security issue: " + e.getLocalizedMessage();
        } catch (IOException e) {
            mProblem = "Network problem: " + e.getLocalizedMessage();
        }
        return payload;
    }

    public String problem() {
        return mProblem;
    }
}

Ruby로 잡업하려면 google-id-token Ruby gem을 인스톨해서 아래와 같이 작업하세요.

require 'google-id-token'
validator = GoogleIDToken::Validator.new
jwt = validator.check(token, required_audience, required_client_id)
if jwt
  email = jwt['email']
else
  report "Cannot validate: #{validator.problem}"
end


PHP 프로그래머는 PHP를 위한 Google API 클라이언트 라이브러리를 확인하세요. 특히 apiOAuth2.php에서 verifyIdToken 함수를 확인하세요.


작성자: Daniel Sandler,(안드로이드 시스템 UI팀 소프트웨어 엔지니어)
작성일: 2012년 12월 27일
원문: http://android-developers.blogspot.kr/2012/12/daydream-interactive-screen-savers.html

개인적으로 저는 화면 보호기를 좋아합니다. 원래 화면 보호기를 쓰는 이유는 크고 비싼 모니터를 스프레드시트 고스트로부터 보호하기 위한 실용적인 목적이겠죠. 하지만 저는 컴퓨터들이 주인이 일어나 잠시 나가주기를 남몰래 바라고 있다는 상상을 합니다. 대기 타이머가 끝날 때까지 한동안 화면보호기가 실행되어 재생될 수 있게 말이죠. 화면에 그림을 그린다든지 폭죽을 터뜨리고 주방 기구들을 공중 부양을 시키다가, 언제든 키가 눌러지거나 마우스가 건드려지면 다시 업무로 복귀할 준비가 되는 것이죠.

안드로이드 4.2에서 새롭게 소개된 Daydream은 슬립 모드의 안드로이드 휴대폰과 태블릿에 이처럼 재미있는 경험을 가져다 줍니다. 아직 확인해 보지 않으셨다면, 설정(Settings)에서 Display -> Daydream에서 When to Daydream을 터치하여 충전하는 동안 해당 기능을 활성화시켜보세요.
앱을 위한 시선 끌기모드 (Attract Mode)

Daydream을 지원하는 앱은 어트랙트 모드에서 안드로이드 UI 툴킷 전체를 사용할 수 있습니다. 즉, 레이아웃, 애니메이션, 3D, 사용자 지정 뷰 등을 포함하여 앱이 현재 가지고 있는 컴포넌트를 쉽게 사용할 수 있고, 보다 잔잔한 프리젠테이션을 위해서 해당 컴포넌트들을 쉽게 조합할 수 있습니다. 또한 터치스크린 입력을 사용할 수 있기 때문에 원한다면 여러가지 인터랙티브한 경험을 제공할 수도 있습니다.

Daydream은 본인의 앱을 홍보할 수 있는 기회를 다소 제공합니다. 앱의 복잡한 면을 감추고 시각적으로 흥미있는 부분을 살려서 사용자가 앱 전체에 흥미를 느낄 수 있도록 합니다. 비디오 게임의 시선 끌기 모드 (attract mode) 처럼 말이죠. 

그림 1. Google 세상보기(Google Currents)에서는 부드럽고 지속적으로 움직이는 뉴스 화면에서 화제거리를 스크롤한다.


구글 세상보기(Google Currents)는 이러한 방법으로 접근한 좋은 예입니다. Daydream으로서 시각적으로 흥미있는 화제거리를 선택하여 화면 슬라이딩으로 보여 줍니다. 화제거리를 터치하면 세상보기는 화면 전체로 해당 내용을 보여주며, 다시 터치하면 전체 실행 중인 앱에서 읽을 수 있습니다.  

Daydream 구조

각 Daydream 구현은 android.service.dreams.DreamService의 서브클래스입니다. DreamService를 확장하면, 간단한 액티비티같은 라이프사이클 API에 접근할 수 있습니다.

서브 클래스에서 오버라이드하기 위한 DreamService 주요 메소드 
(서브클래스 구현 호출을 잊지 말 것)




DreamService 에서 사용하는 주요 메소드
  • setContentView(): Daydream을 위한 배경 설정. XML 리소스 ID나 뷰 인스턴스, 자신이 구현한 사용자 지정 뷰일 수도 있음.
  •  
  • setInteractive(boolean):디폴트로 Daydream은 사용자가 스크린을 터치하면 종료됨. 전형적인 화면 보호기처럼. 사용자가 터치하고 뷰에서 상호 작용하기 원하면 setInteractive(true)를 호출
  • setScreenBright(boolean): 디폴트로 Daydream에서는 가장 밝은 화면으로 설정되기에, 적절치 않은 경우도 있음 (예: 어두운 방). 따라서 false로 설정하면 밝기가 아주 낮은 수준으로 설정됨

마지막으로, 시스템에 Daydream을 홍보하려면, AndroidManifest.xml에 <service>를 생성하세요.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.app">
   <uses-sdk android:targetSdkVersion="17" android:minSdkVersion="17" />

   <application>
       <service
           android:name=".ExampleDaydream"
           android:exported="true"
           android:label="@string/my_daydream_name">
           <intent-filter>
               <action android:name="android.service.dreams.DreamService" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
           <meta-data
               android:name="android.service.dream"
               android:resource="@xml/dream_info" />
       </service>
   </application>
</manifest>


<meta-data> 태그는 선택사항으로, Daydream에 대한 Activity 설정을 명시하는 XML 리소스를 가리킵니다. 사용자는 Settings 앱에서 해당 Daydream 이름 옆에 있는 설정(settings) 아이콘을 눌러서 확인할 수 있습니다. 

<!-- res/xml/dream_info.xml -->
<?xml version="1.0" encoding="utf-8"?>
<dream xmlns:android="http://schemas.android.com/apk/res/android"
   android:settingsActivity="com.example.app/.ExampleDreamSettingsActivity" />


다음은 여러분이 시작해 볼 수 있는 전형적인 화면 보호기 예제로, 움직이는 로고를 보여줍니다. TimeAnimator 를 사용하여 구현한 매끄럽고 부드러운 60Hz 애니메이션을 제공합니다.


그림 2. Will one of them hit the corner?

public class BouncerDaydream extends DreamService {
   @Override
   public void onDreamingStarted() {
       super.onDreamingStarted();

       // Our content view will take care of animating its children.
       final Bouncer bouncer = new Bouncer(this);
       bouncer.setLayoutParams(new
           ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
       bouncer.setSpeed(200); // pixels/sec

       // Add some views that will be bounced around.
       // Here I'm using ImageViews but they could be any kind of
       // View or ViewGroup, constructed in Java or inflated from
       // resources.
       for (int i=0; i<5; i++) {
           final FrameLayout.LayoutParams lp
               = new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
           final ImageView image = new ImageView(this);
           image.setImageResource(R.drawable.android);
           image.setBackgroundColor(0xFF004000);
           bouncer.addView(image, lp);
       }

       setContentView(bouncer);
   }
}

public class Bouncer extends FrameLayout implements TimeAnimator.TimeListener {
   private float mMaxSpeed;
   private final TimeAnimator mAnimator;
   private int mWidth, mHeight;

   public Bouncer(Context context) {
       this(context, null);
   }

   public Bouncer(Context context, AttributeSet attrs) {
       this(context, attrs, 0);
   }

   public Bouncer(Context context, AttributeSet attrs, int flags) {
       super(context, attrs, flags);
       mAnimator = new TimeAnimator();
       mAnimator.setTimeListener(this);
   }

   /**
    * Start the bouncing as soon as we’re on screen.
    */
   @Override
   public void onAttachedToWindow() {
       super.onAttachedToWindow();
       mAnimator.start();
   }

   /**
    * Stop animations when the view hierarchy is torn down.
    */
   @Override
   public void onDetachedFromWindow() {
       mAnimator.cancel();
       super.onDetachedFromWindow();
   }

   /**
    * Whenever a view is added, place it randomly.
    */
   @Override
   public void addView(View v, ViewGroup.LayoutParams lp) {
       super.addView(v, lp);
       setupView(v);
   }

   /**
    * Reposition all children when the container size changes.
    */
   @Override
   protected void onSizeChanged (int w, int h, int oldw, int oldh) {
       super.onSizeChanged(w, h, oldw, oldh);
       mWidth = w;
       mHeight = h;
       for (int i=0; i<getChildCount(); i++) {
           setupView(getChildAt(i));
       }
   }

   /**
    * Bouncing view setup: random placement, random velocity.
    */
   private void setupView(View v) {
       final PointF p = new PointF();
       final float a = (float) (Math.random()*360);
       p.x = mMaxSpeed * (float)(Math.cos(a));
       p.y = mMaxSpeed * (float)(Math.sin(a));
       v.setTag(p);
       v.setX((float) (Math.random() * (mWidth - v.getWidth())));
       v.setY((float) (Math.random() * (mHeight - v.getHeight())));
   }

   /**
    * Every TimeAnimator frame, nudge each bouncing view along.
    */
   public void onTimeUpdate(TimeAnimator animation, long elapsed, long dt_ms) {
       final float dt = dt_ms / 1000f; // seconds
       for (int i=0; i<getChildCount(); i++) {
           final View view = getChildAt(i);
           final PointF v = (PointF) view.getTag();

           // step view for velocity * time
           view.setX(view.getX() + v.x * dt);
           view.setY(view.getY() + v.y * dt);

           // handle reflections
           final float l = view.getX();
           final float t = view.getY();
           final float r = l + view.getWidth();
           final float b = t + view.getHeight();
           boolean flipX = false, flipY = false;
           if (r > mWidth) {
               view.setX(view.getX() - 2 * (r - mWidth));
               flipX = true;
           } else if (l < 0) {
               view.setX(-l);
               flipX = true;
           }
           if (b > mHeight) {
               view.setY(view.getY() - 2 * (b - mHeight));
               flipY = true;
           } else if (t < 0) {
               view.setY(-t);
               flipY = true;
           }
           if (flipX) v.x *= -1;
           if (flipY) v.y *= -1;
       }
   }

   public void setSpeed(float s) {
       mMaxSpeed = s;
   }
}


이 예제 코드로 본인이 원하는 것을 작업해서 화면에 넣어 줄 필요(간단한 그래픽이나 에러 메시지처럼) 없이 사용자에게 손쉽게 보여 줄 수 있습니다. 또한 더욱 복잡한 Daydream 프로젝트를 시작하기에도 좋은 샘플입니다. 

기타 주의 사항
> 우선, 시스템에 피해를 주지 말라 
Daydream은 디바이스가 충전될 때 실행된다. 하지만 Daydream이 지나치게 많은 CPU를 소모하게 되는 경우 충전이 매우 느리거나 전혀 진행되지 않을 수도 있다. 디바이스가 충전되고 있지 않음을 감지하면 시스템은 Daydream을 멈추게 만든다. 따라서 적절한 시간에 충전할수 있도록 코드에서 충분한 전력을 남겨 두도록 한다.

> 화면 잠금을 준수하라
Daydream은 키가드(keyguard) 작동상에서 실행된다. 다시 말해, 민감한 콘텐츠를 보여줄 수 있는 경우라면, 사용자가 해당 콘텐츠를 제어할 수 있는 사용자 툴을 제공할 필요가 있다. 예를 들어, 포토 테이블과 포토 프레임에서는 사용자가 디스플레이될 사진을 앨범에서 선택할 수 있도록 해야한다. (당황스러운 슬라이드쇼를 피함)

> 화면 밝기
Daydream에서 사용되는 장소를 판단하고 이에 따라 setScreenBright()를 사용하여 화면 밝기를 조정하라. 그리고 필요에 따라서 어둡거나 밟은 색상을 사용하라. 침대 머리맡 시계는 탁상 시계보다 어두워야 한다. 두 가지 모두에 쓰일 것이라고 판단되면 사용자에게 선택권을 주어야 한다.

> 상태바 숨김 여부
많은 사용자들이 배터리 충전이나 날짜/시간 정보에 즉각적으로 접근할 수 있기를 원할 것이다. 따라서 setFullscreen() 사용을 피해야 한다. 특히 Daydream이 예술적인 측면보다 정보를 더 많이 가지고 있을 때 더욱 그렇다. Daydream은 요란스럽지 않으면서도 시간 정보와 충전 상태를 보여 줄 수 있게 “lights out”모드로 상태바를 시작할 것이다 (View.SYSTEM_UI_FLAG_LOW_PROFILE).

> 언제 설정을 사용하나
일반적으로 어느정도 재량권을 가지고 Daydream 설정에 제어도구나 다이얼을 추가할 수 있다. 사실 이것은 개인화 기능이기에 사용자 편의대로 수정할 수 있도록 권장되어야 한다. 하지만 때로 심미적인 환경에서 더욱 설득력있는 경험이 나올 수 있기 때문에 소수의 세련되고 아름다운 구성 중에서 선택할 수 있도록 사용자에게 선택권을 주도록 하라 (비행기 조종석의 제어권을 모두 내어주기 보다).

> 하나 이상이 될 수도 있다
설정에서 사용자에게 완전히 상이한 디스플레이 모드 사이에서 선택하도록 한다면, Daydream을 다중 DreamService 구현으로 나누라. 예를 들어 안드로이드 4.2에서 포토 갤러리는 포토 테이블과 포토 프레임 DayDream을 모두 제공한다.

> 개발용 액티비티를 사용하라
대부분 안드로이드 개발 툴은 기존 안드로이드 앱 개발 및 디버깅에 최적화되어 있다. DreamService와 액티비티가 매우 흡사하기 때문에 DreamService과 동일한 콘텐츠 뷰를 호스트하는 테스트용 액티비티를 만드는 것이 유용할 수 있다. 다른 안드로이드 프로젝트처럼 IDE에서 코드를 런치하여 쉽게 테스트 할 수 있다.

자, 이제 충분히 설명 드린 것 같습니다. 여러분의 앱에 Daydream 지원을 개발할 도구가 있습니다. 여러분이 즐겁게 개발하면 사용자들도 즐길 수 있을 것입니다. 그리고 Google Play에 반짝이는 새 APK를 업로드할 때, 앱 설명을 추가하여 Daydream을 찾는 사용자들이 검색할 수 있도록 해주세요!

기타 문서 및 샘플 예제
DreamService API 문서
샘플 코드: BouncerDaydream (본 포스팅 코드 예제를 위해 완성된 프로젝트)
샘플 코드: WebView (HTML 페이지를 보여주는 Daydream)
샘플 코드: Colors (OpenGL ES 2.0 및 TextureView를 보여주는 Daydream)