<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>무근본 개발자</title>
    <link>https://nobase-dev.tistory.com/</link>
    <description>근본이 없어 근본을 만들어 보고자 합니다.</description>
    <language>ko</language>
    <pubDate>Sat, 8 Aug 2026 08:35:08 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>붉은 기타</managingEditor>
    <item>
      <title>Eclipse Copilot</title>
      <link>https://nobase-dev.tistory.com/292</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;github Copilot에서는 IDE 지원이 JetBrains 제품들과 MS 제품들 위주만 공식 지원함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eclipse에서는 Genuitec에서 개발한 Copilot4Eclipse 플러그인을 사용해야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.eclipse.org/content/copilot4eclipse&quot;&gt;https://marketplace.eclipse.org/content/copilot4eclipse&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1729119667569&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;Solution&quot; data-og-title=&quot;Copilot4Eclipse&quot; data-og-description=&quot;Copilot4Eclipse (Copilot for Eclipse) is a&amp;nbsp;free&amp;nbsp;plugin that seamlessly integrates the&amp;nbsp;GitHub Copilot AI developer tools into your Eclipse IDE to create a powerful AI-assisted coding experience.&quot; data-og-host=&quot;marketplace.eclipse.org&quot; data-og-source-url=&quot;https://marketplace.eclipse.org/content/copilot4eclipse&quot; data-og-url=&quot;https://marketplace.eclipse.org/content/copilot4eclipse&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ue9if/hyXlP6xedC/kRv9TXQqlEqVQpZh7DiZgK/img.png?width=79&amp;amp;height=80&amp;amp;face=0_0_79_80,https://scrap.kakaocdn.net/dn/bnYluv/hyXlLC4aVV/b5OY2xobiMl3vUCMYcpMh0/img.png?width=79&amp;amp;height=80&amp;amp;face=0_0_79_80,https://scrap.kakaocdn.net/dn/T7LdC/hyXhRkBOX0/W9wTve3MlvMD7K7JxiVxQK/img.png?width=79&amp;amp;height=80&amp;amp;face=0_0_79_80&quot;&gt;&lt;a href=&quot;https://marketplace.eclipse.org/content/copilot4eclipse&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://marketplace.eclipse.org/content/copilot4eclipse&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ue9if/hyXlP6xedC/kRv9TXQqlEqVQpZh7DiZgK/img.png?width=79&amp;amp;height=80&amp;amp;face=0_0_79_80,https://scrap.kakaocdn.net/dn/bnYluv/hyXlLC4aVV/b5OY2xobiMl3vUCMYcpMh0/img.png?width=79&amp;amp;height=80&amp;amp;face=0_0_79_80,https://scrap.kakaocdn.net/dn/T7LdC/hyXhRkBOX0/W9wTve3MlvMD7K7JxiVxQK/img.png?width=79&amp;amp;height=80&amp;amp;face=0_0_79_80');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Copilot4Eclipse&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Copilot4Eclipse (Copilot for Eclipse) is a&amp;nbsp;free&amp;nbsp;plugin that seamlessly integrates the&amp;nbsp;GitHub Copilot AI developer tools into your Eclipse IDE to create a powerful AI-assisted coding experience.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;marketplace.eclipse.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Copilot4Eclipse는 Eclipse 2023-06(4.28) 및 최신 버전과 호환되며, 이러한 버전을 기반으로 구축된 Eclipse 기반 제품과도 호환됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Eclipse 4.28(2023-06 릴리스) 또는 최신 버전을 기반으로 하는&amp;nbsp;Eclipse IDE 또는 Eclipse 기반 제품입니다.&lt;/li&gt;
&lt;li&gt;GitHub Copilot 구독(&amp;nbsp;개인 수준 또는 비즈니스 수준)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.genuitec.com/products/copilot4eclipse/docs/installation/&quot;&gt;https://www.genuitec.com/products/copilot4eclipse/docs/installation/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;플러그인 사용을 위한 Copilot 로그인&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.genuitec.com/products/copilot4eclipse/docs/basics/signin&quot;&gt;https://www.genuitec.com/products/copilot4eclipse/docs/basics/signin&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 제안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.genuitec.com/products/copilot4eclipse/docs/basics/completions&quot;&gt;https://www.genuitec.com/products/copilot4eclipse/docs/basics/completions&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marketplace.eclipse.org/content/copilot4eclipse#screenshots&quot;&gt;https://marketplace.eclipse.org/content/copilot4eclipse#screenshots&lt;/a&gt;&lt;/p&gt;</description>
      <category>IT/ETC</category>
      <category>Copilot</category>
      <category>Eclipse</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/292</guid>
      <comments>https://nobase-dev.tistory.com/292#entry292comment</comments>
      <pubDate>Thu, 17 Oct 2024 08:01:27 +0900</pubDate>
    </item>
    <item>
      <title>Copilot 리서칭</title>
      <link>https://nobase-dev.tistory.com/291</link>
      <description>&lt;h1&gt;Copilot이란?&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI에서 만든 OpenAI Codex 모델을 사용한 실시간 코드 제안 AI 페어 프로그래머.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(OpenAI Codex란 자연어를 코드로 변환하는 머신러닝 모델로 미세조정된 GPT-3 버전 사용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub 공개 리포지토리에 있는 코드를 포함하여 공개적으로 사용 가능한 소스의 자연어 텍스트 및 소스 코드에 대해 교육 받음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주석과 코드에서 컨텍스트를 가져와 기능 코드를 즉시 제안함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub Copilot이 개발자를 대체할 것으로 기대하지 않으며, 개발자와 협력할 것을 기대함.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;그들의 기능을 강화하고,&lt;/li&gt;
&lt;li&gt;생산성을 높이고,&lt;/li&gt;
&lt;li&gt;수동 작업을 줄이고,&lt;/li&gt;
&lt;li&gt;흥미로운 작업에 집중할 수 있도록 도와줄 것&lt;/li&gt;
&lt;li&gt;진입 장벽을 낮추고 더 많은 사람들이 소프트웨어 개발을 탐색하고 차세대 개발자에 합류할 수 있도록&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Copilot 장점&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상용구나 반복 코드 패턴 생성에 소요되는 시간을 줄여줌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트의 컨텍스트 및 스타일 규칙을 기반으로 권장 사항을 공유함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neovim, JetBrains IDE, Visual Studio, Visual Studio Code 에서 플러그인 형태로 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 새로운 언어나 프레임워크에서 작업하거나 코딩을 배우는 중이라면 도움이 됨.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;낯선 언어로 모든 것을 인터넷 검색하여 코딩하려고 하는 것은 외국어 숙어집만 가지고 외국을 항해하는 것과 같습니다.&amp;nbsp;GitHub Copilot을 사용하는 것은 통역사를 고용하는 것과 같습니다.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;Copilot 주의점&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Copilot이 완변한 코드를 작성하지는 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제안하는 코드를 테스트하지 않으며, 코드가 작동하지 않거나 의미가 없을 수도 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안전하지 않은 코딩 패턴, 버그 또는 오래된 API 또는 관용구에 대한 참조가 포함될 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신중한 테스트 및 코드 및 보안 검토 그리고 자신의 판단과 함께 사용해야 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책임은 항상 개발자에게 있음. (GitHub는 Copilot이 제공한 제안을 소유하지 않음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공개 소스가 주로 영어로 되어 있어 자연어 프롬프트가 영어가 아니거나 문법적으로 잘못된 시나리오에서 제대로 작동하지 않을 수 있음.&lt;/p&gt;
&lt;h1&gt;Copilot 보안&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GitHub의 공개 코드가 포함된 제안을 필터로 막을 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(개인 설정이나 엔터프라이즈 관리자가 관리)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmezlV/btsJ7xmt0Nc/MV7qvuMFg1i3TpXPHXZj9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmezlV/btsJ7xmt0Nc/MV7qvuMFg1i3TpXPHXZj9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmezlV/btsJ7xmt0Nc/MV7qvuMFg1i3TpXPHXZj9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmezlV%2FbtsJ7xmt0Nc%2FMV7qvuMFg1i3TpXPHXZj9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1526&quot; height=&quot;314&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제안되었던 코드가 공유되거나 재사용되지는 않음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경우에 따라 개인 데이터로 보이는 것을 제안하지만 이것은 가상 정보임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Copilot 제안을 사용하여 저작권 침해로 고발되면? 제품별 약관에 제공된 대로 변호해준다고 함.&lt;/p&gt;
&lt;h1&gt;Copilot 관련 데이터&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 초기 관찰 및 인터뷰 결과 (2,000명 이상의 개발 대상)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자 만족도 향상 : 사용자의 60~75% 작업에 만족, 코딩시 좌절감 덜 느끼고 더 만족스러운 작업에 집중할 수 있었음.&lt;/li&gt;
&lt;li&gt;정신 에너지 절약 : 흐름을 유지하고(73%) 반복적인 작업 중에 정신적 노력을 보존하는데 도움이 되었음(87%)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Copilot에 표시된 모든 완료의 평균 26%가 수락을 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Python과 같은 특정 언어에서는 최대 40%까지 생성)&lt;/p&gt;
&lt;h1&gt;Copilot 잘 부려먹는 법&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 함수로 나누고, 매개변수에 의미있는 이름을 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 doc와 주석을 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;익숙하지 않은 라이브러리나 프레임워크 탐색시 사용&lt;/p&gt;
&lt;h1&gt;Copilot 과금&lt;/h1&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;월 10$, 연 100$&lt;/li&gt;
&lt;li&gt;30일 평가판 사용 가능 (⚠️자동으로 유료 구독으로 전환됨)&lt;/li&gt;
&lt;li&gt;학생, 교사나 유명 오픈소스 리포지토리 관리자는 무료 구독 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;비즈니스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인당 월 19$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;Android Studio Plugins&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Plugins에서 GitHub Copilot 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.github.com/ko/copilot/getting-started-with-github-copilot&quot;&gt;https://docs.github.com/ko/copilot/getting-started-with-github-copilot&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yv7Z7/btsJ89EEqED/sKsodZ0wJ7Xoh7K54umPP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yv7Z7/btsJ89EEqED/sKsodZ0wJ7Xoh7K54umPP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yv7Z7/btsJ89EEqED/sKsodZ0wJ7Xoh7K54umPP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyv7Z7%2FbtsJ89EEqED%2FsKsodZ0wJ7Xoh7K54umPP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;374&quot; height=&quot;226&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccp46G/btsJ7NJQFgM/ni8XT0skAPbmZYCjKdEfJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccp46G/btsJ7NJQFgM/ni8XT0skAPbmZYCjKdEfJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccp46G/btsJ7NJQFgM/ni8XT0skAPbmZYCjKdEfJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccp46G%2FbtsJ7NJQFgM%2Fni8XT0skAPbmZYCjKdEfJ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;302&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;1030&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GiQ3g/btsJ9tiyi2e/AYLnIF4VKQhAdqkTNnzR9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GiQ3g/btsJ9tiyi2e/AYLnIF4VKQhAdqkTNnzR9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GiQ3g/btsJ9tiyi2e/AYLnIF4VKQhAdqkTNnzR9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGiQ3g%2FbtsJ9tiyi2e%2FAYLnIF4VKQhAdqkTNnzR9K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;1030&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;1030&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;912&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bymxcx/btsJ7QGiQIF/AzLr6qDq3rcx6VESKmFh40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bymxcx/btsJ7QGiQIF/AzLr6qDq3rcx6VESKmFh40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bymxcx/btsJ7QGiQIF/AzLr6qDq3rcx6VESKmFh40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbymxcx%2FbtsJ7QGiQIF%2FAzLr6qDq3rcx6VESKmFh40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;912&quot; data-origin-width=&quot;1124&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;Android Studio + Copilot&lt;/h1&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주석으로 한국어로 할 일을 적고 Copilot의 제안 받아보기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzk6iq/btsJ79r4Rtm/n310zJkiQ2B0ldd91iURI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzk6iq/btsJ79r4Rtm/n310zJkiQ2B0ldd91iURI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzk6iq/btsJ79r4Rtm/n310zJkiQ2B0ldd91iURI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdzk6iq%2FbtsJ79r4Rtm%2Fn310zJkiQ2B0ldd91iURI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;632&quot; height=&quot;196&quot; data-origin-width=&quot;632&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;번역이 필요한 경우&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Aiut9/btsJ8qUF6MJ/mCCIK2AYoC6pp7BkZOq5Pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Aiut9/btsJ8qUF6MJ/mCCIK2AYoC6pp7BkZOq5Pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Aiut9/btsJ8qUF6MJ/mCCIK2AYoC6pp7BkZOq5Pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAiut9%2FbtsJ8qUF6MJ%2FmCCIK2AYoC6pp7BkZOq5Pk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;410&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 코드 작성시&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tjZKQ/btsJ8wm5DT4/UNHDOAKhFtRZPyXyhtMavK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tjZKQ/btsJ8wm5DT4/UNHDOAKhFtRZPyXyhtMavK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tjZKQ/btsJ8wm5DT4/UNHDOAKhFtRZPyXyhtMavK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtjZKQ%2FbtsJ8wm5DT4%2FUNHDOAKhFtRZPyXyhtMavK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;276&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;210&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAqDhd/btsJ9KEoIY8/3yjPbwfwyPlvXDoFJBb6N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAqDhd/btsJ9KEoIY8/3yjPbwfwyPlvXDoFJBb6N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAqDhd/btsJ9KEoIY8/3yjPbwfwyPlvXDoFJBb6N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAqDhd%2FbtsJ9KEoIY8%2F3yjPbwfwyPlvXDoFJBb6N0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;210&quot; data-origin-width=&quot;720&quot; data-origin-height=&quot;210&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정규표현식 패턴 매칭&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBeBUu/btsJ7wuzDoZ/K1oIgT2vMuej1IrLnjksW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBeBUu/btsJ7wuzDoZ/K1oIgT2vMuej1IrLnjksW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBeBUu/btsJ7wuzDoZ/K1oIgT2vMuej1IrLnjksW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBeBUu%2FbtsJ7wuzDoZ%2FK1oIgT2vMuej1IrLnjksW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;726&quot; height=&quot;176&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;알고리즘 풀이&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKEhUS/btsJ8xl0MOm/tWDOrizNsEvgc9LxnfRGDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKEhUS/btsJ8xl0MOm/tWDOrizNsEvgc9LxnfRGDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKEhUS/btsJ8xl0MOm/tWDOrizNsEvgc9LxnfRGDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKEhUS%2FbtsJ8xl0MOm%2FtWDOrizNsEvgc9LxnfRGDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;344&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Android Room 사용하기&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;152&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cormui/btsJ8xsNq1j/lzjkASMUUAP7s1t97pBGxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cormui/btsJ8xsNq1j/lzjkASMUUAP7s1t97pBGxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cormui/btsJ8xsNq1j/lzjkASMUUAP7s1t97pBGxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcormui%2FbtsJ8xsNq1j%2FlzjkASMUUAP7s1t97pBGxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;152&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;152&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Android Activity xml 생성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;654&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/42kI9/btsJ9ZBvYcE/v6hpDwpWCGfhV7wG4cJZP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/42kI9/btsJ9ZBvYcE/v6hpDwpWCGfhV7wG4cJZP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/42kI9/btsJ9ZBvYcE/v6hpDwpWCGfhV7wG4cJZP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F42kI9%2FbtsJ9ZBvYcE%2Fv6hpDwpWCGfhV7wG4cJZP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;654&quot; data-origin-width=&quot;682&quot; data-origin-height=&quot;654&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h1&gt;참고문서&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/features/copilot/&quot;&gt;https://github.com/features/copilot/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.github.com/ko/copilot/getting-started-with-github-copilot&quot;&gt;https://docs.github.com/ko/copilot/getting-started-with-github-copilot&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dev.to/github/a-beginners-guide-to-prompt-engineering-with-github-copilot-3ibp&quot;&gt;https://dev.to/github/a-beginners-guide-to-prompt-engineering-with-github-copilot-3ibp&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.blog/2022-09-14-8-things-you-didnt-know-you-could-do-with-github-copilot/&quot;&gt;https://github.blog/2022-09-14-8-things-you-didnt-know-you-could-do-with-github-copilot/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://plugins.jetbrains.com/plugin/17718-github-copilot&quot;&gt;https://plugins.jetbrains.com/plugin/17718-github-copilot&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>IT/ETC</category>
      <category>Copilot</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/291</guid>
      <comments>https://nobase-dev.tistory.com/291#entry291comment</comments>
      <pubDate>Thu, 17 Oct 2024 02:01:53 +0900</pubDate>
    </item>
    <item>
      <title>[행사] Droid Knights 2024</title>
      <link>https://nobase-dev.tistory.com/290</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.droidknights.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.droidknights.dev/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1718215215207&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;드로이드나이츠 2024&quot; data-og-description=&quot;대한민국 최대 안드로이드 개발자들만을 위한 컨퍼런스 드로이드나이츠 2024 입니다. 주니어부터 시니어까지 모두가 공감하고 즐길 수 있는 지식의 장으로 만들고자 합니다.&quot; data-og-host=&quot;www.droidknights.dev&quot; data-og-source-url=&quot;https://www.droidknights.dev/&quot; data-og-url=&quot;https://www.droidknights.dev/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cNwRYg/hyWldIbq1i/7wiYFnCUdEPcYBNsxuqNMK/img.png?width=1600&amp;amp;height=900&amp;amp;face=0_0_1600_900,https://scrap.kakaocdn.net/dn/bQ2c8g/hyWlhcJuYs/kc0WCrFPkH1Zt4HAaS8ErK/img.png?width=1600&amp;amp;height=900&amp;amp;face=0_0_1600_900,https://scrap.kakaocdn.net/dn/cWdSyB/hyWllTJXPf/9nK6T6yjMJH3q8fM1w6CB0/img.png?width=2726&amp;amp;height=1280&amp;amp;face=0_0_2726_1280&quot;&gt;&lt;a href=&quot;https://www.droidknights.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.droidknights.dev/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cNwRYg/hyWldIbq1i/7wiYFnCUdEPcYBNsxuqNMK/img.png?width=1600&amp;amp;height=900&amp;amp;face=0_0_1600_900,https://scrap.kakaocdn.net/dn/bQ2c8g/hyWlhcJuYs/kc0WCrFPkH1Zt4HAaS8ErK/img.png?width=1600&amp;amp;height=900&amp;amp;face=0_0_1600_900,https://scrap.kakaocdn.net/dn/cWdSyB/hyWllTJXPf/9nK6T6yjMJH3q8fM1w6CB0/img.png?width=2726&amp;amp;height=1280&amp;amp;face=0_0_2726_1280');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;드로이드나이츠 2024&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;대한민국 최대 안드로이드 개발자들만을 위한 컨퍼런스 드로이드나이츠 2024 입니다. 주니어부터 시니어까지 모두가 공감하고 즐길 수 있는 지식의 장으로 만들고자 합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.droidknights.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난 6월 11일에 드로이드나이츠 2024 행사가 있어 다녀왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 개발자 컨퍼런스를 가다보니 코엑스에서 길도 헤매기도 했고, 겨우 찾은 현장에서는 예전보다 다소 줄어든 부스들을 보며 여러 감정이 들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇년 전까지는 Kotlin, Coroutines, MVVM 등의 주제들이 주를 이루었던 것 같은데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 컨퍼런스에서는 Compose가 관심을 많이 끄는 주제가 되었던 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대충 들었던 세션들의 간략한 정리를 해볼까 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;키노트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 개발자이시자 빅네임이신 박상권님이 나오셔서 드로이드나이츠 소개를 해주셨습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Compose 성능 최적화를 위한 Stability 마스터하기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Compose 동작 구조와 Recomposition의 효율적인 사용을 위한 부분들을 소개해주셨습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Compose 사용이 점차 많아지면서 개발자 문서 정독 후 들으면 좋을 내용이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;유튜브 영상도 업로드 되어 개인적으로 시간 날때 다시 정독할 첫번째 세션입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;시니어와 주니어의 협업 다리: 온라인 및 오프라인 페어코딩의 통찰&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;페어 프로그래밍을 한 학습 및 경험담에 대해 소개해주셨습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;페어코딩을 하기 위한 각자의 룰이나 필요한 자세 등을 알려주었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;페어코딩시 주의할 점이라던지 장단점 등 참고할 좋은 레퍼런스를 만들어 주신 것 같네요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Compose UI 컴포넌트 설계와 테스트&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;물론 개발자 문서에도 잘 정리가 되어 있지만, 이 세션을 본다면 훨씬 이해가 용이해질 듯 싶습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Compose 테스트에 대한 궁금증도 있었는데, 참고가 많이 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Github Actions로 효율적인 배포 환경 만들기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;때마침 업무적으로 하고 싶은 주제 중 하나였던 Github Actions 세션이었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Github Actions를 이용한 다양한 활용에 대해서 소개해주셨습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;조만간 다시 정독하면서 활용 아이디어를 구상하고자 합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;당신의 앱 빌드는 안녕하십니까?&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;평소에 가장 많이 접하는 작업이면서 제대로 알기엔 뭔가 서먹한 빌드 관련 세션입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;세션에 소개된 내용을 안다고 업무 환경에 바로 큰 개선은 어렵겠지만 참고할만한 정보가 참 많았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;Compose로 Animation 만들기 feat.holgraphic Card&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;포켓몬카드로 시작된 애니메이션 개발 연대기 세션입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;목표를 설정하고 문제해결을 위해 노력한 시간들이 엿보이며, 센스 있는 발표도 좋았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;후기&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;역시나 이런 행사를 다녀오면 다시 찾아봐야할 것들, 공부할 것들이 쌓입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;조만간 하나씩 다시 보면서 좋은 시간을 가져봐야 겠습니다.&lt;/span&gt;&lt;/p&gt;</description>
      <category>IT/행사</category>
      <category>2024</category>
      <category>Android</category>
      <category>droid knights</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/290</guid>
      <comments>https://nobase-dev.tistory.com/290#entry290comment</comments>
      <pubDate>Wed, 26 Jun 2024 04:44:48 +0900</pubDate>
    </item>
    <item>
      <title>[DataBinding] 데이터바인딩 설정시 개발 문서 주의점</title>
      <link>https://nobase-dev.tistory.com/289</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오랫만에 데이터바인딩 설정을 잡을 일이 있어서 안드로이드 개발 문서를 살펴보던 중이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/topic/libraries/data-binding/start?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/topic/libraries/data-binding/start?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1705560525410&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;시작하기 &amp;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;시작하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android 스튜디오의 데이터 결합 코드 지원을 비롯하여 개발 환경에서 데이터 결합 라이브러리를 함&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/topic/libraries/data-binding/start?hl=ko&quot; data-og-url=&quot;https://developer.android.com/topic/libraries/data-binding/start?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/npGnn/hyU8Y5Nzl3/vxrqXn3UdGEBvax1hSqSQ0/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676&quot;&gt;&lt;a href=&quot;https://developer.android.com/topic/libraries/data-binding/start?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/topic/libraries/data-binding/start?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/npGnn/hyU8Y5Nzl3/vxrqXn3UdGEBvax1hSqSQ0/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676');&quot;&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;시작하기 &amp;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;시작하기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android 스튜디오의 데이터 결합 코드 지원을 비롯하여 개발 환경에서 데이터 결합 라이브러리를 함&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;806&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ecJmVp/btsDAE0ktxv/3N30zkatwkupen09thgvxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ecJmVp/btsDAE0ktxv/3N30zkatwkupen09thgvxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ecJmVp/btsDAE0ktxv/3N30zkatwkupen09thgvxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FecJmVp%2FbtsDAE0ktxv%2F3N30zkatwkupen09thgvxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;711&quot; height=&quot;328&quot; data-origin-width=&quot;1748&quot; data-origin-height=&quot;806&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1705567126822&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;android {
        ...
        dataBinding {
            enabled = true
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;New Project로 빈 프로젝트를 만듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(최근의 Android Studio가 권장하는 Kotlin DSL (build.gradle.kts)를 선택하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 개발 문서의 가이드대로 데이터바인딩 설정을 해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 build.gradle이 불이 붙은 채로 꺼지지 않습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu3a7q/btsDGVZPY7Y/ULEronvUNL5fOXvcd3vfq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu3a7q/btsDGVZPY7Y/ULEronvUNL5fOXvcd3vfq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu3a7q/btsDGVZPY7Y/ULEronvUNL5fOXvcd3vfq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbu3a7q%2FbtsDGVZPY7Y%2FULEronvUNL5fOXvcd3vfq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;356&quot; height=&quot;121&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;side_eye.webp&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cy8VFE/btsDCWzlHFo/RAcGsovxktkms3BIUKK1B0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy8VFE/btsDCWzlHFo/RAcGsovxktkms3BIUKK1B0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy8VFE/btsDCWzlHFo/RAcGsovxktkms3BIUKK1B0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy8VFE%2FbtsDCWzlHFo%2FRAcGsovxktkms3BIUKK1B0%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;400&quot; data-filename=&quot;side_eye.webp&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엇.. 뭐지? 이런 저런 것들을 건드려 보고, StackOverflow를 뒤져보고요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글께서 시키는 대로 하였는데, 왜 나에게 이런 시련을 주시지? 라는 믿음이 흔들리는 마음도 먹어봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불현듯 생각나서 개발 문서를 다시 확인해봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yykiq/btsDGu9glho/2HhCrzL31ueZkhEMl3Sur1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yykiq/btsDGu9glho/2HhCrzL31ueZkhEMl3Sur1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yykiq/btsDGu9glho/2HhCrzL31ueZkhEMl3Sur1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyykiq%2FbtsDGu9glho%2F2HhCrzL31ueZkhEMl3Sur1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;314&quot; height=&quot;388&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한국어???&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설마???&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;236&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JsxZl/btsDASKBVE4/mAxLtVEavBtkguekyjc3g0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JsxZl/btsDASKBVE4/mAxLtVEavBtkguekyjc3g0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JsxZl/btsDASKBVE4/mAxLtVEavBtkguekyjc3g0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJsxZl%2FbtsDASKBVE4%2FmAxLtVEavBtkguekyjc3g0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;343&quot; height=&quot;145&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;236&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 오잉????&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;big_eye.webp&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qu5ED/btsDGXjb4ML/q91cez7ryR6Je9Knd1t6ZK/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qu5ED/btsDGXjb4ML/q91cez7ryR6Je9Knd1t6ZK/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qu5ED/btsDGXjb4ML/q91cez7ryR6Je9Knd1t6ZK/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqu5ED%2FbtsDGXjb4ML%2Fq91cez7ryR6Je9Knd1t6ZK%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;320&quot; height=&quot;322&quot; data-filename=&quot;big_eye.webp&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 영문 버전으로 바꿔봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/topic/libraries/data-binding/start?authuser=1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/topic/libraries/data-binding/start?authuser=1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/L2Ykg/btsDEjNW8w6/EGKGd5qh8N4x6l25mfI1z0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/L2Ykg/btsDEjNW8w6/EGKGd5qh8N4x6l25mfI1z0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/L2Ykg/btsDEjNW8w6/EGKGd5qh8N4x6l25mfI1z0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FL2Ykg%2FbtsDEjNW8w6%2FEGKGd5qh8N4x6l25mfI1z0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;289&quot; data-origin-width=&quot;1782&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1705567218196&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;android {
    ...
    buildFeatures {
        dataBinding true
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 달라졌습니다......&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잊고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 선후배 동료님들의 개발문서의 한글버전을 믿지 말라는 그 말을요....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;히스토리를 찾아보니 Android Gradle Plugin 4.0 버전부터의 차이가 있었네요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/build/releases/past-releases/agp-4-0-0-release-notes?authuser=1#buildFeatures&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/build/releases/past-releases/agp-4-0-0-release-notes?authuser=1#buildFeatures&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1705561193568&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android 스튜디오 &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;Android Gradle 플러그인 4.0.0 출시 노트&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/build/releases/past-releases/agp-4-0-0-release-notes?authuser=1#buildFeatures&quot; data-og-url=&quot;https://developer.android.com/build/releases/past-releases/agp-4-0-0-release-notes?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bTrCxS/hyU80CxhU1/64uBTFn5DMmDKHf2ALjGO1/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/wqZdP/hyU5KBmDg9/xpwNeyR4aYjyfmUeQuKKqk/img.png?width=1956&amp;amp;height=642&amp;amp;face=0_0_1956_642&quot;&gt;&lt;a href=&quot;https://developer.android.com/build/releases/past-releases/agp-4-0-0-release-notes?authuser=1#buildFeatures&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/build/releases/past-releases/agp-4-0-0-release-notes?authuser=1#buildFeatures&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bTrCxS/hyU80CxhU1/64uBTFn5DMmDKHf2ALjGO1/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/wqZdP/hyU5KBmDg9/xpwNeyR4aYjyfmUeQuKKqk/img.png?width=1956&amp;amp;height=642&amp;amp;face=0_0_1956_642');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Android 스튜디오 &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Android Gradle 플러그인 4.0.0 출시 노트&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;994&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pbjAn/btsDAxmpkQU/B1OOuPMfx7Z2sAdvTgySAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pbjAn/btsDAxmpkQU/B1OOuPMfx7Z2sAdvTgySAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pbjAn/btsDAxmpkQU/B1OOuPMfx7Z2sAdvTgySAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpbjAn%2FbtsDAxmpkQU%2FB1OOuPMfx7Z2sAdvTgySAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;637&quot; height=&quot;369&quot; data-origin-width=&quot;1716&quot; data-origin-height=&quot;994&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방식은 deprecated가 되었고, buildFeatures의 dataBinding으로 설정하는 방법으로요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제 어리석음을 탓하며 시간낭비 삽질을 한 케이스였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에야 많이들 Compose를 쓰시느라 볼 일이 별로 없으시겠지만요...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹여나 저처럼 방황하실 분들을 위해 남겨봅니다. :)&lt;/p&gt;</description>
      <category>Android개발</category>
      <category>AGP</category>
      <category>Android</category>
      <category>build.gradle</category>
      <category>DataBinding</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/289</guid>
      <comments>https://nobase-dev.tistory.com/289#entry289comment</comments>
      <pubDate>Thu, 18 Jan 2024 16:10:19 +0900</pubDate>
    </item>
    <item>
      <title>네이버 지도 사용 중 MissingLibraryException 발생 이슈????</title>
      <link>https://nobase-dev.tistory.com/288</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 회사에서 네이버 지도를 사용하다 발생하였던 이슈에 대해서 정리를 한번 하고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 상에서는 이슈가 전혀 없던 앱이 상용 배포 후 Crashlytics를 통해 MissingLibraryException를 종종 발생한다는 이슈였었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;발단&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버 지도를 붙인 기능이 배포된 후 얼마 후 Crashlytics를 통해서 이상한 오류를 잡아내기 시작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 오류에서 가르키는 부분들은 개발 중에는 본 적이 없는 키워드 들이었습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1701006605742&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Caused by com.getkeepsafe.relinker.MissingLibraryException
Could not find 'libnavermap.so'. Looked for: [arm64-v8a, armeabi-v7a, armeabi], but only found: [].&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;wth.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxnGEe/btsAXhLz57c/blm8wKtax1FZ5xaGywK0lK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxnGEe/btsAXhLz57c/blm8wKtax1FZ5xaGywK0lK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxnGEe/btsAXhLz57c/blm8wKtax1FZ5xaGywK0lK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cxnGEe/btsAXhLz57c/blm8wKtax1FZ5xaGywK0lK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;392&quot; data-filename=&quot;wth.gif&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멀쩡히 잘 넣어둔 라이브러리가 실종이라니 처음에는 황당한 이슈였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;분석&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨋든 발생하고 있는 이슈이니 분석을 해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발견 당시 기준으로 소수의 사용자들이 반복적으로 크래쉬를 경험하고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단말도 넥서스, 픽셀나 에뮬레이터로 보이는 단말명들이었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;첫번째 추정을 하였습니다. 특정 칩셋을 쓰는 단말들에서만 발생하는건가??&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면서 추가 수집된 정보로 arm, x86 단말들이 섞여 있었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사무실에 가지고 있는 단말과 에뮬레이터로 테스트 해보았지만 이상이 없었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애초에 네이버 지도가 arm, x86을 다 지원하고 있으므로 문제가 될 것이 없었죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글플레이에 업로드하였던 aab 파일을 받아서 다시 까보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;amr, x86 so 파일들이 다소곳이 잘 들어가 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫번째 추정은 정답이 아니었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K3h7i/btsAWhdWM9e/vUG0RcM4THowJn022E6liK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K3h7i/btsAWhdWM9e/vUG0RcM4THowJn022E6liK/img.png&quot; data-origin-width=&quot;464&quot; data-origin-height=&quot;310&quot; data-is-animation=&quot;false&quot; width=&quot;265&quot; height=&quot;177&quot; style=&quot;width: 59.2512%; margin-right: 10px;&quot; data-widthpercent=&quot;59.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K3h7i/btsAWhdWM9e/vUG0RcM4THowJn022E6liK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK3h7i%2FbtsAWhdWM9e%2FvUG0RcM4THowJn022E6liK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;464&quot; height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgbqN6/btsASoyP6jo/9hRvk4PscL5SfKBXPGYB6K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgbqN6/btsASoyP6jo/9hRvk4PscL5SfKBXPGYB6K/img.gif&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;400&quot; data-is-animation=&quot;true&quot; data-filename=&quot;sad.gif&quot; style=&quot;width: 39.586%;&quot; data-widthpercent=&quot;40.05&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgbqN6/btsASoyP6jo/9hRvk4PscL5SfKBXPGYB6K/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgbqN6%2FbtsASoyP6jo%2F9hRvk4PscL5SfKBXPGYB6K%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;두번째 추정을 해보았습니다. 누군가 앱 설치 후 APK를 백업하여 재설치를 해본건가??&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저희는 당시 apk가 아닌 aab로 버전을 업로드하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aab는 split apk를 통해 다운받는 단말에 필요한 조각들로 앱이 설치됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/guide/app-bundle/app-bundle-format?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/guide/app-bundle/app-bundle-format&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701008337913&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android App Bundle 형식 &amp;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;Android App Bundle 형식 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android App Bundle은 Google Play에 업로드하는 파일(파일 확장자는 .aab)입니다. App Bundle은 그림 1&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/guide/app-bundle/app-bundle-format?hl=ko&quot; data-og-url=&quot;https://developer.android.com/guide/app-bundle/app-bundle-format?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Cv4gB/hyUFeuuXb7/NMJIQOOuU2vEP1e9PJoeW1/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/pA0Ox/hyUChTXA4R/oOgWhQfiYi1dhfR8WbfA2K/img.png?width=1706&amp;amp;height=1473&amp;amp;face=0_0_1706_1473,https://scrap.kakaocdn.net/dn/f7Nel/hyUE0bWEbL/sYKKuUSjMit4usDwZL54T1/img.png?width=1696&amp;amp;height=1219&amp;amp;face=0_0_1696_1219&quot;&gt;&lt;a href=&quot;https://developer.android.com/guide/app-bundle/app-bundle-format?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/guide/app-bundle/app-bundle-format?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Cv4gB/hyUFeuuXb7/NMJIQOOuU2vEP1e9PJoeW1/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676,https://scrap.kakaocdn.net/dn/pA0Ox/hyUChTXA4R/oOgWhQfiYi1dhfR8WbfA2K/img.png?width=1706&amp;amp;height=1473&amp;amp;face=0_0_1706_1473,https://scrap.kakaocdn.net/dn/f7Nel/hyUE0bWEbL/sYKKuUSjMit4usDwZL54T1/img.png?width=1696&amp;amp;height=1219&amp;amp;face=0_0_1696_1219');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Android App Bundle 형식 &amp;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Android App Bundle 형식 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android App Bundle은 Google Play에 업로드하는 파일(파일 확장자는 .aab)입니다. App Bundle은 그림 1&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 설치된 단말과 백업한 앱을 다시 설치한 단말의 칫셉이 다르다면? 혹은 split apk의 base apk만 설치된다면??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번 설치된 앱의 APK를 추출하여 다른 단말에 설치 후 실행해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두둥. 재현이 되기 시작합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;angry.gif&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8Ygmn/btsATgmLYxW/iqummxg9tnEUswyYzXCJok/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8Ygmn/btsATgmLYxW/iqummxg9tnEUswyYzXCJok/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8Ygmn/btsATgmLYxW/iqummxg9tnEUswyYzXCJok/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/b8Ygmn/btsATgmLYxW/iqummxg9tnEUswyYzXCJok/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;256&quot; height=&quot;293&quot; data-filename=&quot;angry.gif&quot; data-origin-width=&quot;466&quot; data-origin-height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당시의 테스트 방법이었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.&amp;nbsp;일반&lt;br /&gt;&amp;nbsp; &amp;nbsp; 1-1. 구글플레이를 통해 설치한 앱의 APK 를 추출.&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; a. CX파일탐색기 같은 앱 사용하여 APK 추출. (구글플레이 검색)&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; b. Android Studio의 Device File Explorer 등을 사용하여 APK 복사&lt;br /&gt;&amp;nbsp; &amp;nbsp; 1-2. AAB로 업로드된 앱을 설치 후 1번 방법으로 APK 를 추출하면 정상적으로 설치가능한 APK가 나오지 않음.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1-3.&amp;nbsp;추출한&amp;nbsp;APK&amp;nbsp;속의&amp;nbsp;base.apk&amp;nbsp;를&amp;nbsp;추출하여&amp;nbsp;단말에&amp;nbsp;설치.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1-4. 앱 실행. (이때 크래시 발생)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;2.&amp;nbsp;aab로&amp;nbsp;split&amp;nbsp;apks&amp;nbsp;변환&amp;nbsp;후&amp;nbsp;테스트시&lt;br /&gt;&amp;nbsp; &amp;nbsp; 2-1. bundletool을 이용해 aab 파일을 split apks로 변환&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a. bundletool 다운로드 :&amp;nbsp;&lt;a href=&quot;https://github.com/google/bundletool/releases&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/google/bundletool/releases&lt;/a&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; b. 아래 명령어로 변환&lt;/p&gt;
&lt;pre id=&quot;code_1701008685843&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        java -jar bundletool-all-1.14.0.jar build-apks \
          --bundle=app-googlePlayStore-dev-debugWithProguard.aab \
          --output=output.apks \
          --connected-device&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 2-2. output.apks 파일을 zip 파일로 확장자 변경 후 압축 해제&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2-3.&amp;nbsp;splits&amp;nbsp;폴더&amp;nbsp;안의&amp;nbsp;base-master.apk&amp;nbsp;을&amp;nbsp;단말에&amp;nbsp;설치&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2-4. 앱 실행. (이때 크래시 발생)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;3. split apks 설치 방법 (만약에 split apks를 정상적으로 설치해보고 싶다면!!)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1.&amp;nbsp;adb&amp;nbsp;install-multiple&amp;nbsp;*.apk&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은 확인하였지만, 당시에 이슈를 바로 해결할 수가 없었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 이유에서였죠.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확인 결과 MissingLibrary는 네이버 맵 SDK에서 사용하는 라이브러리에서 제공하는 것임.&lt;/li&gt;
&lt;li&gt;네이버 맵 SDK에서 해당 부분을 사용하는 부분은 static 영역인데, static 영역이 초기화 중에 exception이 발생하여 ExceptionInInitializerError가 발생함.&lt;/li&gt;
&lt;li&gt;Exception이 아니라 Error라 핸들링할 수가 없음.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버 맵 SDK 에서 수정되지 않는 이상 Exception 핸들링도 불가하였고, 일반적이지 않고 극소수의 이슈라 우선순위도 낮아 다른 업무에 조금씩 치여가고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 네이버 클라우드 플랫폼에 해당 현상을 문의하였고, 답변에서도 &lt;b&gt;비정상적 활동이고 빈도가 높지 않을 것으로 보이니 무시하는 것을 권장한다&lt;/b&gt;고 하였죠. 나름 뭉갤 수 있는 정당성이 부여되었다라고 죄책감을 조금 내려놓을 때 쯔음....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불현듯이 찾아온 컨퍼런스 영상 속에서 그 해답을 찾게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[드로이드나이츠 2021] 차영호 - AppBundle 괴담 영상 속에서 제가 필요로 하던 답이 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://youtu.be/EVYnTe6aXWQ?t=706&quot;&gt;https://youtu.be/EVYnTe6aXWQ?t=706&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=EVYnTe6aXWQ&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/qOl8u/hyUFe2k4Nf/dpEkzVksTZLZwZquSyK3yK/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=124_464_196_542&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-title=&quot;[드로이드나이츠 2021] 차영호 - AppBundle 괴담&quot; data-original-url=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/EVYnTe6aXWQ&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위 이슈를 SideLoading이라는 키워드로 부르는 것을 알게 되었고, 아래와 같은 포스팅에서도 도움을 얻게 되었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;App Bundle and Sideloading: how to prevent crashes :&amp;nbsp;&lt;a href=&quot;https://objectbox.io/app-bundle-and-sideloading-how-to-prevent-crashes/&quot;&gt;https://objectbox.io/app-bundle-and-sideloading-how-to-prevent-crashes/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1701009668276&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;App Bundle and Sideloading: how to prevent crashes&quot; data-og-description=&quot;Read here, why the new Google Android App Bundle format can cause sideloading crashes when using a native library. Learn how to fix it.&quot; data-og-host=&quot;objectbox.io&quot; data-og-source-url=&quot;https://objectbox.io/app-bundle-and-sideloading-how-to-prevent-crashes/&quot; data-og-url=&quot;https://objectbox.io/app-bundle-and-sideloading-how-to-prevent-crashes/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/6J2qs/hyUE4FpLYy/ZaWiIuyai5ZBHI6JAENVM0/img.jpg?width=1000&amp;amp;height=579&amp;amp;face=0_0_1000_579&quot;&gt;&lt;a href=&quot;https://objectbox.io/app-bundle-and-sideloading-how-to-prevent-crashes/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://objectbox.io/app-bundle-and-sideloading-how-to-prevent-crashes/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/6J2qs/hyUE4FpLYy/ZaWiIuyai5ZBHI6JAENVM0/img.jpg?width=1000&amp;amp;height=579&amp;amp;face=0_0_1000_579');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;App Bundle and Sideloading: how to prevent crashes&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Read here, why the new Google Android App Bundle format can cause sideloading crashes when using a native library. Learn how to fix it.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;objectbox.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덕분에 최종 수정은 아래와 같이 하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱 실행시 split apk 체크로 문제가 인지되면, 토스트 띄우고 앱 종료함.&lt;/li&gt;
&lt;li&gt;split apk 체크로직은 앱이 build 타입이 release일때만 체크하도록 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1701009850620&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun Context.checkSplitApks(): Boolean {
    val splitNames = packageManager.getPackageInfoCompat(packageName, 0).splitNames ?: emptyArray()

    // 네이버 지도 라이브러리는 arm/x86으로 split apk 확인이 가능하네 ㅎㅎ
    val armPattern = &quot;arm&quot;
    val x86Pattern = &quot;x86&quot;

    // split apk 추출 후 base or master apk만 설치시 dpi가 없으니 확인하자!!
    val dpiPattern = &quot;dpi&quot;
    return splitNames.any { it.contains(armPattern) || it.contains(x86Pattern) }
            &amp;amp;&amp;amp; splitNames.any { it.contains(dpiPattern) }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 해당 처리로 앱 종료 처리된 경우 Crashlytics 로그 추가하여 추이를 보기로 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 수정사항으로 더이상 저런 헤괴망측한 오류는 나오지 않았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 간헐적으로 종종 로그는 수집되고 있었답니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘의 괴담은 여기까지입니다. ^^&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;속편안.jpeg&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rgsJ7/btsARiZZ54f/AVmoZ0i1WQSl5kfOs1m9KK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rgsJ7/btsARiZZ54f/AVmoZ0i1WQSl5kfOs1m9KK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rgsJ7/btsARiZZ54f/AVmoZ0i1WQSl5kfOs1m9KK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrgsJ7%2FbtsARiZZ54f%2FAVmoZ0i1WQSl5kfOs1m9KK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;323&quot; height=&quot;404&quot; data-filename=&quot;속편안.jpeg&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Android개발/가져다쓰기</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/288</guid>
      <comments>https://nobase-dev.tistory.com/288#entry288comment</comments>
      <pubDate>Sun, 26 Nov 2023 23:58:08 +0900</pubDate>
    </item>
    <item>
      <title>[Jenkins] APK, AAB Google Play 업로드 설정하기</title>
      <link>https://nobase-dev.tistory.com/286</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins로 Google Play에 APK, AAB를 업로드하기 위해서는 플러그인 설치 및 Google Play 콘솔에서 서비스 계정 설정이 필요함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play]&lt;/b&gt; Google Play 콘솔 로그인 후 &lt;b&gt;설정 - API 액세스&lt;/b&gt; 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2382&quot; data-origin-height=&quot;1672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XHsMB/btrvFfguXlj/Qy7QsuuCwyNRB93yeKqHv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XHsMB/btrvFfguXlj/Qy7QsuuCwyNRB93yeKqHv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XHsMB/btrvFfguXlj/Qy7QsuuCwyNRB93yeKqHv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXHsMB%2FbtrvFfguXlj%2FQy7QsuuCwyNRB93yeKqHv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2382&quot; height=&quot;1672&quot; data-origin-width=&quot;2382&quot; data-origin-height=&quot;1672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play] 새 프로젝트 만들기&lt;/b&gt; 선택 후 저장&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;1682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HowDt/btrvJTxapqC/CmGh7yxM9HZW8klEZt7jF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HowDt/btrvJTxapqC/CmGh7yxM9HZW8klEZt7jF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HowDt/btrvJTxapqC/CmGh7yxM9HZW8klEZt7jF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHowDt%2FbtrvJTxapqC%2FCmGh7yxM9HZW8klEZt7jF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3006&quot; height=&quot;1682&quot; data-origin-width=&quot;3006&quot; data-origin-height=&quot;1682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play]&lt;/b&gt; 프로젝트 보기 선택하여 &lt;b&gt;Google Cloud Platform&lt;/b&gt; 으로 이동&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2528&quot; data-origin-height=&quot;1684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAa3W6/btrvLI9iYiP/ykK9sNmXAOXjE2kah0dPak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAa3W6/btrvLI9iYiP/ykK9sNmXAOXjE2kah0dPak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAa3W6/btrvLI9iYiP/ykK9sNmXAOXjE2kah0dPak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAa3W6%2FbtrvLI9iYiP%2FykK9sNmXAOXjE2kah0dPak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2528&quot; height=&quot;1684&quot; data-origin-width=&quot;2528&quot; data-origin-height=&quot;1684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud ]&lt;/b&gt; 메뉴 - IAM 및 관리자 - 서비스 계정 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;1640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHYTwH/btrvKKfuqGv/oesl5h0yhXBXsejE3kFbz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHYTwH/btrvKKfuqGv/oesl5h0yhXBXsejE3kFbz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHYTwH/btrvKKfuqGv/oesl5h0yhXBXsejE3kFbz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHYTwH%2FbtrvKKfuqGv%2Foesl5h0yhXBXsejE3kFbz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1516&quot; height=&quot;1640&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;1640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud Platform]&lt;/b&gt; + 서비스 계정 만들기 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2684&quot; data-origin-height=&quot;1590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clldtx/btrvJZp9Ck9/EtxxwWU46Hn4qQk8wE4mzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clldtx/btrvJZp9Ck9/EtxxwWU46Hn4qQk8wE4mzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clldtx/btrvJZp9Ck9/EtxxwWU46Hn4qQk8wE4mzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fclldtx%2FbtrvJZp9Ck9%2FEtxxwWU46Hn4qQk8wE4mzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2684&quot; height=&quot;1590&quot; data-origin-width=&quot;2684&quot; data-origin-height=&quot;1590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud Platform] 서비스 계정 세부 정보&lt;/b&gt;의 서비스 계정 이름, 서비스 계정 설명 등 기입 후 만들고 계속하기 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1966&quot; data-origin-height=&quot;1678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nYn2N/btrvJxnaIq1/Ti0jeE7wHDZlKQE2sneXX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nYn2N/btrvJxnaIq1/Ti0jeE7wHDZlKQE2sneXX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nYn2N/btrvJxnaIq1/Ti0jeE7wHDZlKQE2sneXX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnYn2N%2FbtrvJxnaIq1%2FTi0jeE7wHDZlKQE2sneXX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1966&quot; height=&quot;1678&quot; data-origin-width=&quot;1966&quot; data-origin-height=&quot;1678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud Platform] 이 서비스 계정에 프로젝트에 대한 액세스 권한 부여&lt;/b&gt; 에서 &lt;b&gt;역할&lt;/b&gt;에 &lt;b&gt;소유자&lt;/b&gt; 선택 후 계속&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1514&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FmxTW/btrvL5QJSL0/MF2C9H7ATNJISkV6UgU061/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FmxTW/btrvL5QJSL0/MF2C9H7ATNJISkV6UgU061/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FmxTW/btrvL5QJSL0/MF2C9H7ATNJISkV6UgU061/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFmxTW%2FbtrvL5QJSL0%2FMF2C9H7ATNJISkV6UgU061%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1514&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1514&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud Platform] 사용자에게 이 서비스 계정에 대한 액세스 권한 부여&lt;/b&gt; 에서는 필요시 추가 후 완료&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btHWOv/btrvJyzz3Cp/k22L6tUPD5bzDduU2QptXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btHWOv/btrvJyzz3Cp/k22L6tUPD5bzDduU2QptXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btHWOv/btrvJyzz3Cp/k22L6tUPD5bzDduU2QptXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtHWOv%2FbtrvJyzz3Cp%2Fk22L6tUPD5bzDduU2QptXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;890&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud Platform]&lt;/b&gt; 생성된 서비스 계정을 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;871&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ue8Zg/btrvLhxkNhR/m8e4KUlSYDrfB2ekxGF3o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ue8Zg/btrvLhxkNhR/m8e4KUlSYDrfB2ekxGF3o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ue8Zg/btrvLhxkNhR/m8e4KUlSYDrfB2ekxGF3o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUe8Zg%2FbtrvLhxkNhR%2Fm8e4KUlSYDrfB2ekxGF3o1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;871&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;871&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud Platform]&lt;/b&gt; 키 탭을 선택 후 키 추가 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7OQ9A/btrvI3GUI0t/ciRLKk2GnQcwoIF2wtZtv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7OQ9A/btrvI3GUI0t/ciRLKk2GnQcwoIF2wtZtv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7OQ9A/btrvI3GUI0t/ciRLKk2GnQcwoIF2wtZtv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7OQ9A%2FbtrvI3GUI0t%2FciRLKk2GnQcwoIF2wtZtv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1371&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Cloud Platform]&lt;/b&gt; 새 키 만들기 - JSON - 저장 선택하여 json 파일 저장&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TV891/btrvJyM8Yai/zLoUjHNtj10EqyBbxupAs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TV891/btrvJyM8Yai/zLoUjHNtj10EqyBbxupAs1/img.png&quot; data-origin-width=&quot;704&quot; data-origin-height=&quot;496&quot; style=&quot;width: 37.7024%; margin-right: 10px;&quot; data-widthpercent=&quot;38.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TV891/btrvJyM8Yai/zLoUjHNtj10EqyBbxupAs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTV891%2FbtrvJyM8Yai%2FzLoUjHNtj10EqyBbxupAs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;496&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhkoYz/btrvLzK4V5j/MNQR7elj3AjyM1JoBgGpi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhkoYz/btrvLzK4V5j/MNQR7elj3AjyM1JoBgGpi0/img.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;869&quot; style=&quot;width: 61.1348%;&quot; data-widthpercent=&quot;61.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhkoYz/btrvLzK4V5j/MNQR7elj3AjyM1JoBgGpi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhkoYz%2FbtrvLzK4V5j%2FMNQR7elj3AjyM1JoBgGpi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;869&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;983&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KYoZu/btrvJ0o5Oa4/92MvvwvzfYIpmoQuir2cQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KYoZu/btrvJ0o5Oa4/92MvvwvzfYIpmoQuir2cQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KYoZu/btrvJ0o5Oa4/92MvvwvzfYIpmoQuir2cQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKYoZu%2FbtrvJ0o5Oa4%2F92MvvwvzfYIpmoQuir2cQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;983&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;983&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1046&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uwUKP/btrvJ9s2aCb/N50yczjd1UKo6RxkHDRDm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uwUKP/btrvJ9s2aCb/N50yczjd1UKo6RxkHDRDm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uwUKP/btrvJ9s2aCb/N50yczjd1UKo6RxkHDRDm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuwUKP%2FbtrvJ9s2aCb%2FN50yczjd1UKo6RxkHDRDm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1046&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1046&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play]&lt;/b&gt; 다시 &lt;b&gt;Google Play 콘솔&lt;/b&gt;로 넘어와서 설정 - API 액세스 에서 &lt;b&gt;서비스 계정&lt;/b&gt;의 서비스 계정 새로고침 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bj1Pxy/btrvJ0Jn9eQ/wQa3CUTthW9Agz42JoWev0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bj1Pxy/btrvJ0Jn9eQ/wQa3CUTthW9Agz42JoWev0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bj1Pxy/btrvJ0Jn9eQ/wQa3CUTthW9Agz42JoWev0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbj1Pxy%2FbtrvJ0Jn9eQ%2FwQa3CUTthW9Agz42JoWev0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;958&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;958&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play] 서비스 계정&lt;/b&gt;에서 권한 부여 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bct21k/btrvMeNsep2/sa9cT09dccckD23O5O99xK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bct21k/btrvMeNsep2/sa9cT09dccckD23O5O99xK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bct21k/btrvMeNsep2/sa9cT09dccckD23O5O99xK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbct21k%2FbtrvMeNsep2%2Fsa9cT09dccckD23O5O99xK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;344&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play] 사용자 초대&lt;/b&gt;에서 계정 권한 탭에서 &lt;b&gt;출시&lt;/b&gt; 관련 항목들 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnjCZ7/btrvIzsw26S/4FQxfksI7qADPympwAAge1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnjCZ7/btrvIzsw26S/4FQxfksI7qADPympwAAge1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnjCZ7/btrvIzsw26S/4FQxfksI7qADPympwAAge1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnjCZ7%2FbtrvIzsw26S%2F4FQxfksI7qADPympwAAge1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1109&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1109&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;1678&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dawj1m/btrvLJmQf08/ffP5whnzUeIpxHniAoYOC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dawj1m/btrvLJmQf08/ffP5whnzUeIpxHniAoYOC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dawj1m/btrvLJmQf08/ffP5whnzUeIpxHniAoYOC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdawj1m%2FbtrvLJmQf08%2FffP5whnzUeIpxHniAoYOC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1838&quot; height=&quot;1678&quot; data-origin-width=&quot;1838&quot; data-origin-height=&quot;1678&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play]&lt;/b&gt; 앱 권한 탭 선택 후 **애플리케이션 추가**로 앱을 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k3GuY/btrvF285RCU/PSLqJJlW1swteTutCWrE5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k3GuY/btrvF285RCU/PSLqJJlW1swteTutCWrE5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k3GuY/btrvF285RCU/PSLqJJlW1swteTutCWrE5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk3GuY%2FbtrvF285RCU%2FPSLqJJlW1swteTutCWrE5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1110&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1110&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play]&lt;/b&gt; 앱 추가시 권한 확인 후 적용 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q60Hz/btrvK2Nv5ml/GuJtkvHsvfKIkDcbw4MdFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q60Hz/btrvK2Nv5ml/GuJtkvHsvfKIkDcbw4MdFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q60Hz/btrvK2Nv5ml/GuJtkvHsvfKIkDcbw4MdFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq60Hz%2FbtrvK2Nv5ml%2FGuJtkvHsvfKIkDcbw4MdFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1166&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play]&lt;/b&gt; 사용자 초대 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1123&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AMjWF/btrvJxOf0NI/3shVAdt9WgrDjT1aK1MU0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AMjWF/btrvJxOf0NI/3shVAdt9WgrDjT1aK1MU0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AMjWF/btrvJxOf0NI/3shVAdt9WgrDjT1aK1MU0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAMjWF%2FbtrvJxOf0NI%2F3shVAdt9WgrDjT1aK1MU0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;1123&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;1123&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Google Play]&lt;/b&gt; 사용자 초대 완료 후 추가된 사용자 확인&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;915&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KVABE/btrvK2UgUu2/zbZ6wYoBOUB7URzKjezzbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KVABE/btrvK2UgUu2/zbZ6wYoBOUB7URzKjezzbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KVABE/btrvK2UgUu2/zbZ6wYoBOUB7URzKjezzbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKVABE%2FbtrvK2UgUu2%2FzbZ6wYoBOUB7URzKjezzbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;915&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;915&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Jenkins]&lt;/b&gt; Jenkins 플러그인 설정에서 Google Play Android Publisher Plugin 을 설치&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://plugins.jenkins.io/google-play-android-publisher/&quot;&gt;https://plugins.jenkins.io/google-play-android-publisher/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;65&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d3caHL/btrvL4K3SVD/VBuRdANglljggDCHj6pDt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d3caHL/btrvL4K3SVD/VBuRdANglljggDCHj6pDt0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d3caHL/btrvL4K3SVD/VBuRdANglljggDCHj6pDt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd3caHL%2FbtrvL4K3SVD%2FVBuRdANglljggDCHj6pDt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;65&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;65&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Jenkins] Add Credentials&lt;/b&gt; 에서 Google Service Account from private key 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G4qMz/btrvLgFbXE3/7n0HiafkKHa4Mg5pUafGYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G4qMz/btrvLgFbXE3/7n0HiafkKHa4Mg5pUafGYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G4qMz/btrvLgFbXE3/7n0HiafkKHa4Mg5pUafGYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG4qMz%2FbtrvLgFbXE3%2F7n0HiafkKHa4Mg5pUafGYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1903&quot; height=&quot;870&quot; data-origin-width=&quot;1903&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Jenkins] Project Name&lt;/b&gt; 기입 후 &lt;b&gt;GCP&lt;/b&gt;에서 서비스 계정 생성시 저장한 json 파일을 선택한 후 OK 선택&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;603&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMytGg/btrvJSZnCdy/L6FX3kZaSI66wkmpmNIdU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMytGg/btrvJSZnCdy/L6FX3kZaSI66wkmpmNIdU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMytGg/btrvJSZnCdy/L6FX3kZaSI66wkmpmNIdU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMytGg%2FbtrvJSZnCdy%2FL6FX3kZaSI66wkmpmNIdU0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;962&quot; height=&quot;603&quot; data-origin-width=&quot;962&quot; data-origin-height=&quot;603&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[Jenkins]&lt;/b&gt; 업로드할 job 에서 플러그인과 추가한 Credentials로 업로드 설정&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pipeline 사용시 Jenkinsfile에 추가&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;stage('Publish artifacts') {
    steps {
        androidApkUpload googleCredentialsId: 'test',
            apkFilesPattern: '**/outputs/**/*.aab',
            trackName: 'internal',
            rolloutPercentage: '100'
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;job 설정에서 직접 설정시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;빌드 후 조치&lt;/b&gt;에 Upload Android AAB/APKs to Google Play 추가&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Google Play account&lt;/b&gt; - Specific credentials 선택 후 추가하였던 Credentials ****의 Project Name 선택&lt;/li&gt;
&lt;li&gt;APK/AAB 위치 설정&lt;/li&gt;
&lt;li&gt;배포할 트랙 선택, 단계적 배포 수준, In-app Update priority 등을 설정&lt;/li&gt;
&lt;li&gt;업데이트 문구도 설정 가능함&lt;/li&gt;
&lt;li&gt;설정 저장 후 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mDFAf/btrvIyNPljB/qdFouw4dclKkEvk7MmSlcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mDFAf/btrvIyNPljB/qdFouw4dclKkEvk7MmSlcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mDFAf/btrvIyNPljB/qdFouw4dclKkEvk7MmSlcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmDFAf%2FbtrvIyNPljB%2FqdFouw4dclKkEvk7MmSlcK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1678&quot; height=&quot;916&quot; data-origin-width=&quot;1678&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LwK9t/btrvHZLDHCX/Qe7R6Ilk91zcfhz5EMEVR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LwK9t/btrvHZLDHCX/Qe7R6Ilk91zcfhz5EMEVR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LwK9t/btrvHZLDHCX/Qe7R6Ilk91zcfhz5EMEVR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLwK9t%2FbtrvHZLDHCX%2FQe7R6Ilk91zcfhz5EMEVR0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;116&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgsjaq/btrvKI9PdMC/TnydH4K13r4LjLnoXKYINK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgsjaq/btrvKI9PdMC/TnydH4K13r4LjLnoXKYINK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgsjaq/btrvKI9PdMC/TnydH4K13r4LjLnoXKYINK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgsjaq%2FbtrvKI9PdMC%2FTnydH4K13r4LjLnoXKYINK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;327&quot; height=&quot;405&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XYxD4/btrvMJmlSbG/YkTd7x6i1m7uAfOa8E3S3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XYxD4/btrvMJmlSbG/YkTd7x6i1m7uAfOa8E3S3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XYxD4/btrvMJmlSbG/YkTd7x6i1m7uAfOa8E3S3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXYxD4%2FbtrvMJmlSbG%2FYkTd7x6i1m7uAfOa8E3S3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;344&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Android개발</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/286</guid>
      <comments>https://nobase-dev.tistory.com/286#entry286comment</comments>
      <pubDate>Sat, 12 Mar 2022 01:38:40 +0900</pubDate>
    </item>
    <item>
      <title>[Android] View Background 리소스 적용시 주의사항</title>
      <link>https://nobase-dev.tistory.com/285</link>
      <description>&lt;div&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;결론부터 말하자면 View의 Background를 리소스로 설정시에 color 리소스가 아닌 drawable 리소스만을 사용해야 합니다.
(물론 대다수의 경우들은 drawable만 사용합니다....)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Android UI 작업을 하다보면 View의 Background를 설정하는 경우는 매우 흔한 일입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 pressed 효과를 위해 StateListDrawable 형태의 drawable 리소스를 쓰는 경우도 많이 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Android OS 10에서부터는 ColorStateList 형태의 color 리소스도 View의 Background에서 사용이 가능해졌습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(흔히 말하는 selector 리소스 형태들입니다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ColorStateList 리소스 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.android.com/guide/topics/resources/color-list-resource&quot;&gt;https://developer.android.com/guide/topics/resources/color-list-resource&lt;/a&gt;
&lt;div&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;selector xmlns:android=&quot;&amp;lt;http://schemas.android.com/apk/res/android&amp;gt;&quot;&amp;gt;
    &amp;lt;item android:color=&quot;@color/white&quot; android:state_selected=&quot;true&quot; /&amp;gt;
    &amp;lt;item android:color=&quot;@color/white&quot; android:state_pressed=&quot;true&quot; /&amp;gt;
    &amp;lt;item android:color=&quot;@color/black&quot; /&amp;gt;
&amp;lt;/selector&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;b&gt;color/selector_rect_color.xml&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;Drawable 리소스 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://developer.android.com/guide/topics/resources/drawable-resource&quot;&gt;https://developer.android.com/guide/topics/resources/drawable-resource&lt;/a&gt;
&lt;div&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;selector xmlns:android=&quot;&amp;lt;http://schemas.android.com/apk/res/android&amp;gt;&quot;&amp;gt;
    &amp;lt;item android:drawable=&quot;@color/white&quot; android:state_selected=&quot;true&quot; /&amp;gt;
    &amp;lt;item android:drawable=&quot;@color/white&quot; android:state_enabled=&quot;true&quot; /&amp;gt;
    &amp;lt;item android:drawable=&quot;@color/black&quot; /&amp;gt;
&amp;lt;/selector&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;b&gt;drawable/selector_rect.xml&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 최신 Android OS에서 ColorStateList 리소스로 View.setBackgroundResource() 를 사용할 수 있지만, OS 9 이하에서는 Resource NotFoundException을 겪게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-1-문제-재현해보기&quot; href=&quot;https://github.com/NoBaseDev/til/blob/master/00_Android/Tips/View_Background_Resource.md#1-%EB%AC%B8%EC%A0%9C-%EC%9E%AC%ED%98%84%ED%95%B4%EB%B3%B4%EA%B8%B0&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;1. 문제 재현해보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 문제를 재현해 보고, 정확히 문제가 되는 부분을 찾아봅니다.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;try {
    val drawable = context.getDrawable(R.drawable.selector_rect)
    Log.d(TAG, &quot;ResourceTest :: 0. $drawable&quot;)
    val drawable2 = context.getDrawable(R.color.selector_rect_color)
    Log.d(TAG, &quot;ResourceTest :: 1. $drawable2&quot;)
} catch(e: Exception) {
    Log.d(TAG, &quot;ResourceTest :: 2. $e&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱에서 테스트할 부분에 위 코드를 추가 후 OS 10과 OS 9 기기에 올려서 확인해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(기기가 없어도 에뮬레이터로 확인 가능합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각가 아래와 같은 로그를 얻을 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android OS 10&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;ResourceTest :: 0. android.graphics.drawable.StateListDrawable@ba98d70
ResourceTest :: 1. android.graphics.drawable.ColorStateListDrawable@c5c796e&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android OS 9&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ResourceTest :: 0. android.graphics.drawable.StateListDrawable@e71743d
ResourceTest :: 2. android.content.res.Resources$NotFoundException: Drawable com.test.app:color/selector_rect_color with resource ID #0x7f0601c3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-2-문제-범위-확인&quot; href=&quot;https://github.com/NoBaseDev/til/blob/master/00_Android/Tips/View_Background_Resource.md#2-%EB%AC%B8%EC%A0%9C-%EB%B2%94%EC%9C%84-%ED%99%95%EC%9D%B8&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;2. 문제 범위 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제 발생시 CallStack을 확인하여 비교할 코드 범위를 좁혀봅니다.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;android.content.res.Resources$NotFoundException: Drawable com.test.app:color/selector_rect_color with resource ID #0x7f0601c3
Caused by: android.content.res.Resources$NotFoundException: File res/color/selector_rect_color.xml from drawable resource ID #0x7f0601c3
    at android.content.res.ResourcesImpl.loadDrawableForCookie(ResourcesImpl.java:847)
    at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:631)
    at android.content.res.Resources.getDrawableForDensity(Resources.java:888)
    at android.content.res.Resources.getDrawable(Resources.java:827)
    at android.content.Context.getDrawable(Context.java:626)
    ...
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #3: &amp;lt;item&amp;gt; tag requires a 'drawable' attribute or child tag defining a drawable
    at android.graphics.drawable.StateListDrawable.inflateChildElements(StateListDrawable.java:190)
    at android.graphics.drawable.StateListDrawable.inflate(StateListDrawable.java:122)
    at android.graphics.drawable.DrawableInflater.inflateFromXmlForDensity(DrawableInflater.java:142)
    at android.graphics.drawable.Drawable.createFromXmlInnerForDensity(Drawable.java:1332)
    at android.graphics.drawable.Drawable.createFromXmlForDensity(Drawable.java:1291)
    at android.content.res.ResourcesImpl.loadDrawableForCookie(ResourcesImpl.java:833)
	... 20 more&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 로그로 ResourcesImpl.loadDrawableForCookie 에서 Exception을 발생함을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;a id=&quot;user-content-3-aosp를-통한-소스-확인&quot; href=&quot;https://github.com/NoBaseDev/til/blob/master/00_Android/Tips/View_Background_Resource.md#3-aosp%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%86%8C%EC%8A%A4-%ED%99%95%EC%9D%B8&quot; aria-hidden=&quot;true&quot;&gt;&lt;/a&gt;3. AOSP를 통한 소스 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 aosp 소스로 그 차이를 확인할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android OS 9 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/aosp-mirror/platform_frameworks_base/blob/android-9.0.0_r61/core/java/android/content/res/ResourcesImpl.java&quot;&gt;https://github.com/aosp-mirror/platform_frameworks_base/blob/android-9.0.0_r61/core/java/android/content/res/ResourcesImpl.java&lt;/a&gt;&lt;br /&gt;Android OS 9의 경우 loadDrawableForCookie에서 color 리소스를 통해 Drawable 생성 시도를 하다가 Exception이 발생을 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;gradle&quot;&gt;&lt;code&gt;	@Nullable
    private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
    ...
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
        LookupStack stack = mLookupStack.get();
        try {
            // Perform a linear search to check if we have already referenced this resource before.
            if (stack.contains(id)) {
                throw new Exception(&quot;Recursive reference in drawable&quot;);
            }
            stack.push(id);
            try {
                if (file.endsWith(&quot;.xml&quot;)) {
                    final XmlResourceParser rp = loadXmlResourceParser(
                            file, id, value.assetCookie, &quot;drawable&quot;);
                    dr = Drawable.createFromXmlForDensity(wrapper, rp, density, null);
                    rp.close();
                } else {
                    final InputStream is = mAssets.openNonAsset(
                            value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                    AssetInputStream ais = (AssetInputStream) is;
                    dr = decodeImageDrawable(ais, wrapper, value);
                }
            } finally {
                stack.pop();
            }
        } catch (Exception | StackOverflowError e) {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            final NotFoundException rnf = new NotFoundException(
                    &quot;File &quot; + file + &quot; from drawable resource ID #0x&quot; + Integer.toHexString(id));
            rnf.initCause(e);
            throw rnf;
        }
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Android OS 10 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://github.com/aosp-mirror/platform_frameworks_base/blob/android-10.0.0_r47/core/java/android/content/res/ResourcesImpl.java&quot;&gt;https://github.com/aosp-mirror/platform_frameworks_base/blob/android-10.0.0_r47/core/java/android/content/res/ResourcesImpl.java&lt;/a&gt;&lt;br /&gt;Android OS 10에서는 loadDrawableForCookie에서 ColorStateList 리소스 여부를 추가로 확인 후 분기처리가 된 것을 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;@Nullable
    private Drawable loadDrawableForCookie(@NonNull Resources wrapper, @NonNull TypedValue value,
    ...
        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
        LookupStack stack = mLookupStack.get();
        try {
            // Perform a linear search to check if we have already referenced this resource before.
            if (stack.contains(id)) {
                throw new Exception(&quot;Recursive reference in drawable&quot;);
            }
            stack.push(id);
            try {
                if (file.endsWith(&quot;.xml&quot;)) {
                    if (file.startsWith(&quot;res/color/&quot;)) {
                        dr = loadColorOrXmlDrawable(wrapper, value, id, density, file);
                    } else {
                        dr = loadXmlDrawable(wrapper, value, id, density, file);
                    }
                } else {
                    final InputStream is = mAssets.openNonAsset(
                            value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                    AssetInputStream ais = (AssetInputStream) is;
                    dr = decodeImageDrawable(ais, wrapper, value);
                }
            } finally {
                stack.pop();
            }
        } catch (Exception | StackOverflowError e) {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            final NotFoundException rnf = new NotFoundException(
                    &quot;File &quot; + file + &quot; from drawable resource ID #0x&quot; + Integer.toHexString(id));
            rnf.initCause(e);
            throw rnf;
        }
    ...
    private Drawable loadColorOrXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
            int id, int density, String file) {
        try {
            ColorStateList csl = loadColorStateList(wrapper, value, id, null);
            return new ColorStateListDrawable(csl);
        } catch (NotFoundException originalException) {
            // If we fail to load as color, try as normal XML drawable
            try {
                return loadXmlDrawable(wrapper, value, id, density, file);
            } catch (Exception ignored) {
                // If fallback also fails, throw the original exception
                throw originalException;
            }
        }
    }
    ...
    private Drawable loadXmlDrawable(@NonNull Resources wrapper, @NonNull TypedValue value,
            int id, int density, String file)
            throws IOException, XmlPullParserException {
        try (
                XmlResourceParser rp =
                        loadXmlResourceParser(file, id, value.assetCookie, &quot;drawable&quot;)
        ) {
            return Drawable.createFromXmlForDensity(wrapper, rp, density, null);
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 소스 확인으로 OS 10 이후부터는 ColorStateList 리소스도 처리가 가능하지만 그 미만 버전에서 문제가 되는 경우가 발생하는 부분을 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일시 에러가 나거나 버전 분기가 필요한 부분으로 체크 되지도 않기 때문에 생각없이 지나치기 쉬운 부분이 될 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 앱의 minSdkVersion이 29 미만이라면 항상 위 내용을 주의할 필요가 있습니다.&lt;/p&gt;</description>
      <category>Android개발</category>
      <category>Android</category>
      <category>Background</category>
      <category>NotFoundException</category>
      <category>resource</category>
      <category>View</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/285</guid>
      <comments>https://nobase-dev.tistory.com/285#entry285comment</comments>
      <pubDate>Wed, 24 Nov 2021 02:12:20 +0900</pubDate>
    </item>
    <item>
      <title>[Kotlin] 무심코 apply 를 쓰다 생긴 일</title>
      <link>https://nobase-dev.tistory.com/284</link>
      <description>&lt;p&gt;그야말로 별 생각없이 단순작업을 하다 벌어진 일이었습니다.&lt;/p&gt;
&lt;p&gt;Java로 작성된 코드들을 일부 Kotlin으로 변환하고 문법을 조금씩 손을 보는 작업을 진행 중이었죠.&lt;/p&gt;
&lt;p&gt;컴파일 오류도 없고 빨간 줄 하나 없이 잘 빌드되었고 문제가 없는 줄 알았습니다.&lt;/p&gt;
&lt;p&gt;하지만 앱에는 내부적으로 문제가 있었죠. 그 원인은 이런 것이었습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Java 클래스 2개가 있었고 일부 동일한 멤버변수들이 가지고 있는 클래스들이었습니다.&lt;/p&gt;
&lt;p&gt;이 클래스들을 서로 다른 레이어의 Model로 있으면 레이어 간 데이터 전달시 서로 Mapping되는 역할이 있었죠.&lt;/p&gt;
&lt;p&gt;그러다보니 동일한 멤버들을 서로의 멤버에 부어서 변환을 해주는 메서드들이 있었고요.&lt;/p&gt;
&lt;p&gt;여기서 apply로 빌더처럼 데이터를 전달하다가 문제가 발생하였습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;해당코드를 샘플로 다시 작성 해봤습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1616171482038&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Human {
    var sleep: Int = 0
    var eat: Int = 0
    var play: Int = 0
}

class Student {
    var sleep: Int = 0
    var eat: Int = 0
    var study: Int = 0
    
    fun getHuman(): Human {
        return Human().apply {
            sleep = sleep
            eat = eat
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Human 과 Student 클래스가 있고 Student 클래스를 Human으로 맵핑하면서 동일한 데이터를 전달하는 과정입니다.&lt;/p&gt;
&lt;p&gt;아래를 보면 바로 Human 생성 후 return을 하는 것이 보이고 apply로 필요한 데이터를 전달하는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;하지만 이때 sleep과 eat 변수들을 주의깊게 보질 않았던 것이 문제였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1616171601793&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        return Human().apply {
            sleep = sleep
            eat = eat
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위에서 보이는 sleep과 eat는 apply 내에서 this 즉 Human의 멤버들을 뜻하게 됩니다.&lt;/p&gt;
&lt;p&gt;그래서 제가 Human에 넘기려던 Student의 데이터들이 제대로 복사가 안되었던 것입니다.&lt;/p&gt;
&lt;p&gt;this.sleep = sleep 도 결과는 동일하였습니다.&lt;/p&gt;
&lt;p&gt;그래서 아래와 같이 변경하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1616171852665&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        return Human().also {
            it.sleep = sleep
            it.eat = eat
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;apply를 also로 변경하고 Human은 it으로 명시적으로 지정을 해주었습니다.&lt;/p&gt;
&lt;p&gt;Kotlin의 Scope 함수를 잘 이해하고 조금만 신경 썼으면 틀릴 부분은 아니었습니다.&lt;/p&gt;
&lt;p&gt;다만 IDE에서도 문법적으로는 문제가 없어 컴파일 오류로 잡아주질 않아 자칫 잘못하면 헤매기 쉬웠던 문제였죠.&lt;/p&gt;
&lt;p&gt;문법들이 편해지는 만큼 방심하기 쉬워지는 것을 다시 한번 느끼긴 했습니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;// 오랫만에 남기는 포스팅이라 뭐라 끝을 맺을지는 모르겠습니다... 그냥 끝!!!&lt;/p&gt;</description>
      <category>Android개발/Kotlin</category>
      <category>Also</category>
      <category>Android</category>
      <category>apply</category>
      <category>kotlin</category>
      <category>실수</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/284</guid>
      <comments>https://nobase-dev.tistory.com/284#entry284comment</comments>
      <pubDate>Sat, 20 Mar 2021 01:42:11 +0900</pubDate>
    </item>
    <item>
      <title>[Android][WebView] Android 10에서의 변화가 있네요.</title>
      <link>https://nobase-dev.tistory.com/283</link>
      <description>&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;Android WebView는 OS 버전에 따라 몇번의 변화가 있었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;Android 10에서도 또 한번의 변화가 있어 간단히 정리해봤습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 contenteditable=&quot;true&quot;&gt;&lt;span&gt;Android 4.4 (Kitkat) 이전&lt;/span&gt;&lt;/h2&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;커스텀하게 내장된 엔진을 사용하였으며, OS 업데이트시만 업데이트 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 contenteditable=&quot;true&quot;&gt;&lt;span&gt;Android 4.4 (Kitkat)&lt;/span&gt;&lt;/h2&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;Chromium 특정 버전을 사용하며, OS 업데이트시만 업데이트 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 contenteditable=&quot;true&quot;&gt;&lt;span&gt;Android 5 (Lollipop)&lt;/span&gt;&lt;/h2&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;OS 번들이 아니라 System WebView 로 제공되었으며, Play Store를 통해 업데이트가 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 contenteditable=&quot;true&quot;&gt;&lt;span&gt;Android 7 (Nouga)&lt;/span&gt;&lt;/h2&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;Chrome 앱의 엔진이 WebView에 사용되었으며, Chrome 앱 업데이트로 업데이트시 WebView도 업데이트가 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;기존 System WebView와 Chrome WebView 중 선택이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;android_webview_os_7.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;641&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dYvdlg/btqCcBxiYFl/8EdsowMPd779kX0OASEFtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dYvdlg/btqCcBxiYFl/8EdsowMPd779kX0OASEFtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dYvdlg/btqCcBxiYFl/8EdsowMPd779kX0OASEFtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdYvdlg%2FbtqCcBxiYFl%2F8EdsowMPd779kX0OASEFtK%2Fimg.png&quot; data-filename=&quot;android_webview_os_7.png&quot; data-origin-width=&quot;360&quot; data-origin-height=&quot;641&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 contenteditable=&quot;true&quot;&gt;&lt;span&gt;Android 10 (Q)&lt;/span&gt;&lt;/h2&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;다시 System WebView로 제공되었고 Play Store를 통해 업데이트가 되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;System WebView Canary/Dev/Beta 버전들이 제공되며, 선택이 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;Chrome WebView는 제공하지 않습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;android_webview_os_10.png&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQ4OT6/btqCeuDX5Zr/58KTMcehcL6bTRzmU6WHwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQ4OT6/btqCeuDX5Zr/58KTMcehcL6bTRzmU6WHwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQ4OT6/btqCeuDX5Zr/58KTMcehcL6bTRzmU6WHwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQ4OT6%2FbtqCeuDX5Zr%2F58KTMcehcL6bTRzmU6WHwK%2Fimg.png&quot; data-filename=&quot;android_webview_os_10.png&quot; data-origin-width=&quot;362&quot; data-origin-height=&quot;722&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 contenteditable=&quot;true&quot;&gt;&lt;span&gt;관련 링크&lt;/span&gt;&lt;/h3&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/prerelease.md#Android-10-Q&quot;&gt;https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/prerelease.md#Android-10-Q&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/channels.md&quot;&gt;https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/channels.md&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://www.androidpolice.com/2019/10/24/android-10-no-longer-uses-chrome-app-to-render-pages-inside-apps/&quot;&gt;https://www.androidpolice.com/2019/10/24/android-10-no-longer-uses-chrome-app-to-render-pages-inside-apps/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p contenteditable=&quot;true&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://www.xda-developers.com/google-chrome-no-longer-webview-provider-android-10/&quot;&gt;https://www.xda-developers.com/google-chrome-no-longer-webview-provider-android-10/&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Android개발</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/283</guid>
      <comments>https://nobase-dev.tistory.com/283#entry283comment</comments>
      <pubDate>Mon, 24 Feb 2020 14:21:36 +0900</pubDate>
    </item>
    <item>
      <title>[Android] Geofence</title>
      <link>https://nobase-dev.tistory.com/282</link>
      <description>&lt;p&gt;참고링크 : &lt;a href=&quot;https://developer.android.com/training/location/geofencing#kotlin&quot;&gt;https://developer.android.com/training/location/geofencing#kotlin&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;개요&lt;/h2&gt;
&lt;p&gt;Android 에서는 Geofencing 기능을 통해 현재 위치가 특정 위치 반경에 진입/머물기/이탈 등의 이벤트를 처리 가능합니다.&lt;/p&gt;
&lt;p&gt;단말 유저 당, 앱 당 최대 100개의 geofence 를 사용할 수 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can have multiple active geofences, with a limit of 100 per app, per device user.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./images/geofence_2x.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Location Services에 geofence 를 위한 이벤트들을 요청해놓을 수 있습니다.&lt;/p&gt;
&lt;p&gt;요청한 변경들이 감지되면 BroadcastReciever 등을 통해 처리가 가능합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;반경 진입&lt;/li&gt;
&lt;li&gt;반경 이탈&lt;/li&gt;
&lt;li&gt;반경 진입 후 지정한 시간 동안 이탈 안함&lt;/li&gt;
&lt;li&gt;geofence 만료 시간 (만료 시간 후 Location Service는 geofence를 알아서 지웁니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;width: 854px;&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;width: 395px;&quot;&gt;항목&lt;/th&gt;
&lt;th style=&quot;width: 459px;&quot;&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;최대 등록 수&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;100 per app, per device user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;만료시간&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;Geofence 마다 millisecond 로 지정.&lt;br /&gt;NEVER_EXPIRE 옵션 지원.&lt;br /&gt;시간 지정 후 만료시 자동으로 Geofence 제거됨.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;영역 지정&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;Circle 형태로 등록 되며, 위경도와 반경(m) 지정.&lt;br /&gt;&lt;span&gt;최상의 결과는 반경 100~150m 사이로 설정해야 함.&lt;br /&gt;&lt;/span&gt;&lt;span&gt;Wi-FI 사용 가능시 20~40m 까지 가능.&lt;/span&gt;&lt;br /&gt;실내 위치 사용시는 5m 정도로 작을 수도 있음.&lt;br /&gt;Geofence 내부 위치를 알수 없다면 Wi-Fi 정확도는 약 50m라고 가정함.&lt;br /&gt;Wi-Fi 위치 사용할 수 없다면 수백m ~ 수Km 편차 생길수도 있음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;머물기 시간 지정&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;Geofence 진입 후 머물기로 체크할 시간을 millisecond로 지정.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;Geofence 감지할 변경 타입&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;진입 (GEOFENCE_TRANSITION_ENTER)&lt;br /&gt;이탈 (GEOFENCE_TRANSITION_EXIT)&lt;br /&gt;머뭄 (GEOFENCE_TRANSITION_DWELL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;응답시간 지정&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;응답 시간이 큰 경우 전력을 크게 절약하고&lt;br /&gt;응답 시간을 낮춘다고 바로 알림을 받지는 않음.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;Geofence 추가시 초기 알림 설정&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;진입 (INITIAL_TRIGGER_ENTER)&lt;br /&gt;이탈 (INITIAL_TRIGGER_EXIT)&lt;br /&gt;머뭄 (INITIAL_TRIGGER_DWELL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;Geofence 사용 이유 설명&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;앱의 백그라운드 위치에 액세스하므로 사용자에게 권한 설명 필요.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;Geofence 재등록이 필요없는 경우&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;Google Play 서비스 업그레이드나 리소스 제한으로 종료 후 재시작시&lt;br /&gt;Location 프로세스 충돌시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;Geofence 재등록이 필요한 경우&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;단말 재부팅시 부팅 완료 시점을 받아 다시 등록해야 함.&lt;br /&gt;앱이 제거 후 재 설치시&lt;br /&gt;앱 데이터 삭제시&lt;br /&gt;Google Play 서비스 데이터 삭제시&lt;br /&gt;앱에 GEOFENCE_NOT_AVAILABLE 알림 수신시&lt;br /&gt;(일반적으로 Android's Network Location Provider 비활성화시 발생함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 379px;&quot;&gt;Geofence 진입이 잘 동작하지 않는 경우 (GEOFENCE_TRANSITION_ENTER)&lt;/td&gt;
&lt;td style=&quot;width: 443px;&quot;&gt;Geofence 정확한 위치를 사용할 수 없거나 반경이 너무 작은 경우&lt;br /&gt;Wi-Fi가 꺼져 있는 경우 (SettingsClient를 사용해 장치 설정 체크 필요)&lt;br /&gt;- 참고로 Android 10에서는 WifiManager.setEnabled() 호출 불가하므로 설정 패널 사용.&lt;br /&gt;- 시스템 앱이나 Device policy controller 에서는 가능.&lt;br /&gt;Geofence 내 안정적인 네트워크 연결이 없을 경우&lt;br /&gt;단순히 알림이 늦는 경우.&lt;br /&gt;- 지속적으로 위치 쿼리하는 형식이 아니라 대기 시간이 필요함.&lt;br /&gt;- 일반적인 대기 시간은 2 분 미만이며 장치가 움직일 때는 훨씬 적음.&lt;br /&gt;- 백그라운드 위치 제한 적용시 대기 시간은 평균 약 2~3분 정도.&lt;br /&gt;- 단말이 장시간 정지된 경우 대기 시간이 증가할 수 있음. (최대 6분)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2&gt;Android App 샘플 만들기&lt;/h2&gt;
&lt;h3&gt;1. 권한 추가&lt;/h3&gt;
&lt;p&gt;먼저 권한을 아래와 같이 추가 후 앱에서 permission 체크 및 요청 로직을 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ACCESS_BACKGROUND_LOCATION&lt;/code&gt; 권한은 Android 10 부터 필요합니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.android.com/training/permissions/requesting&quot;&gt;App 권한 요청 처리&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.android.com/training/location/receive-location-updates#inform-user-background-location-requirement&quot;&gt;Background 위치 접근 권한 처리&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;AndroidManifest.xml&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;manifest... &amp;gt;
    &amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot;/&amp;gt;

    &amp;lt;!-- Required if your app targets Android 10 (API level 29) or higher --&amp;gt;
    &amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_BACKGROUND_LOCATION&quot;/&amp;gt;
    ...
&amp;lt;/manifest&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;MainActivity.kt&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;    private val MY_PERMISSIONS_REQ_ACCESS_FINE_LOCATION = 100
    private val MY_PERMISSIONS_REQ_ACCESS_BACKGROUND_LOCATION = 101

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        checkPermission()
    }

    override fun onRequestPermissionsResult(requestCode: Int,
                                            permissions: Array&amp;lt;String&amp;gt;, grantResults: IntArray) {
        when (requestCode) {
            MY_PERMISSIONS_REQ_ACCESS_FINE_LOCATION,
            MY_PERMISSIONS_REQ_ACCESS_BACKGROUND_LOCATION -&amp;gt; {
                grantResults.apply {
                    if (this.isNotEmpty()) {
                        this.forEach {
                            if (it != PackageManager.PERMISSION_GRANTED) {
                                checkPermission()
                                return
                            }
                        }
                    } else {
                        checkPermission()
                    }
                }
            }
        }
    }

    private fun checkPermission() {
        val permissionAccessFineLocationApproved = ActivityCompat
            .checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
                PackageManager.PERMISSION_GRANTED

        if (permissionAccessFineLocationApproved) {
            if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.Q) {
                val backgroundLocationPermissionApproved = ActivityCompat
                    .checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_LOCATION) ==
                        PackageManager.PERMISSION_GRANTED

                if (!backgroundLocationPermissionApproved) {
                    ActivityCompat.requestPermissions(
                        this,
                        arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                        MY_PERMISSIONS_REQ_ACCESS_BACKGROUND_LOCATION
                    )
                }
            }
        } else {
            ActivityCompat.requestPermissions(this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                MY_PERMISSIONS_REQ_ACCESS_FINE_LOCATION
            )
        }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. dependency 추가&lt;/h3&gt;
&lt;p&gt;Location Service 및 Geofence 사용을 위해서 location 라이브러리를 추가합니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;app/build.gradle&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;dependencies {
    ...
    implementation 'com.google.android.gms:play-services-location:17.0.0'
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. Geofence List 추가&lt;/h3&gt;
&lt;p&gt;테스트 용으로 추가할 Geofence 데이터를 생성합니다.&lt;/p&gt;
&lt;p&gt;Geofence 객체 생성시 여러가지 설정을 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;ex) 위치 및 반경, 어떤 이벤트를 걸건지, 만료나 머무는 체크 시간을 얼마나 할지 등등&lt;/p&gt;
&lt;p&gt;&lt;b&gt;MainActivity.kt&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;    val geofenceList: MutableList&amp;lt;Geofence&amp;gt; by lazy {
        mutableListOf( 
            getGeofence(&quot;현대백화점&quot;, Pair(37.5085864,127.0601149)),
            getGeofence(&quot;삼성역&quot;, Pair(37.5094518,127.063603))
        )
    }

    private fun getGeofence(reqId: String, geo: Pair&amp;lt;Double, Double&amp;gt;, radius: Float = 100f): Geofence {
        return Geofence.Builder()
            .setRequestId(reqId)    // 이벤트 발생시 BroadcastReceiver에서 구분할 id
            .setCircularRegion(geo.first, geo.second, radius)    // 위치 및 반경(m)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)        // Geofence 만료 시간
            .setLoiteringDelay(10000)                            // 머물기 체크 시간
            .setTransitionTypes(
                Geofence.GEOFENCE_TRANSITION_ENTER                // 진입 감지시
                        or Geofence.GEOFENCE_TRANSITION_EXIT    // 이탈 감지시
                        or Geofence.GEOFENCE_TRANSITION_DWELL)    // 머물기 감지시
            .build()
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. Geofence Client 생성&lt;/h3&gt;
&lt;p&gt;Location API 사용을 위하여 Geofencing Client 인스턴스를 생성해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;MainActivity.kt&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;    private val geofencingClient: GeofencingClient by lazy {
        LocationServices.getGeofencingClient(this)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. Geofencing Request 빌드&lt;/h3&gt;
&lt;p&gt;Geofence 지정 및 관련 이벤트 트리거 방식을 설정하기 위해 GeofencingRequest 를 빌드합니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;MainActivity.kt&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;    private fun getGeofencingRequest(list: List&amp;lt;Geofence&amp;gt;): GeofencingRequest {
        return GeofencingRequest.Builder().apply {
            // Geofence 이벤트는 진입시 부터 처리할 때
            setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)                
            addGeofences(list)    // Geofence 리스트 추가
        }.build()
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6. Broadcast Receiver 추가&lt;/h3&gt;
&lt;p&gt;Geofencing 변경 이벤트를 받을 BroadcastReceiver 를 추가 후 등록해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;GeofenceBroadcastReceiver.kt&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;class GeofenceBroadcastReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        if (geofencingEvent.hasError()) {
            val errorMessage = GeofenceStatusCodes.getStatusCodeString(geofencingEvent.errorCode)
            Log.e(&quot;GeofenceBR&quot;, errorMessage)
            return
        }

        // Get the transition type.
        val geofenceTransition = geofencingEvent.geofenceTransition    // 발생 이벤트 타입

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
        geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            val triggeringGeofences = geofencingEvent.triggeringGeofences

            val transitionMsg = when(geofenceTransition) {
                Geofence.GEOFENCE_TRANSITION_ENTER -&amp;gt; &quot;Enter&quot;
                Geofence.GEOFENCE_TRANSITION_EXIT -&amp;gt; &quot;Exit&quot;
                else -&amp;gt; &quot;-&quot;
            }
            triggeringGeofences.forEach {
                Toast.makeText(context, &quot;${it.requestId} - $transitionMsg&quot;, Toast.LENGTH_LONG).show()
            }

        } else {
            Toast.makeText(context, &quot;Unknown&quot;, Toast.LENGTH_LONG).show()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;AndroidManifest.xml&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;&amp;lt;application... &amp;gt;
    ...
    &amp;lt;receiver android:name=&quot;.GeofenceBroadcastReceiver&quot;/&amp;gt;
&amp;lt;/application&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;MainActivity.kt&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
        PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7. Geofence Request 및 Callback 추가&lt;/h3&gt;
&lt;p&gt;Geofencing Client에 Geofence 정보 및 트리거 방식을 가지고 있는 Geofencing Request과 이벤트 발생시 처리할 Broadcast Receiver를 추가해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;MainActivity.kt&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;private fun addGeofences() {
    geofencingClient.addGeofences(getGeofencingRequest(geofenceList), geofencePendingIntent).run {
        addOnSuccessListener {
            Toast.makeText(this@MainActivity, &quot;add Success&quot;, Toast.LENGTH_LONG).show()
        }
        addOnFailureListener {
            Toast.makeText(this@MainActivity, &quot;add Fail&quot;, Toast.LENGTH_LONG).show()
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;준비는 끝났습니다. 앱을 실행해보아 동작을 확인합니다.&lt;/p&gt;</description>
      <category>Android개발</category>
      <author>붉은 기타</author>
      <guid isPermaLink="true">https://nobase-dev.tistory.com/282</guid>
      <comments>https://nobase-dev.tistory.com/282#entry282comment</comments>
      <pubDate>Thu, 20 Feb 2020 18:43:42 +0900</pubDate>
    </item>
  </channel>
</rss>