<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Ethan's Coding Letter</title>
    <link>https://yk-coding-letter.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 7 Jul 2026 01:13:19 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>YK Choi</managingEditor>
    <item>
      <title>Hello, KMP</title>
      <link>https://yk-coding-letter.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;나의 먼 미래에 활용하기 위해 KMP(Kotlin Multiplatform)를 공부해보기 시작했다.&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 가 나의 주요 기술스택이었기 때문에 Kotlin과 Compose 를 최대한 활용하여 앱을 개발해보려 한다.&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;Compose multiplatform wizard(&lt;a href=&quot;https://kmp.jetbrains.com/&quot;&gt;https://kmp.jetbrains.com/&lt;/a&gt;)를 이용하여 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;아래 사진과 같이 폴더를 구성할 수 있었다. Android Studio 에서 직접 new project 생성해서 만들어주는 구성과 조금 다른데, wizard 버전이 더 직관적이라 마음에 들었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btPZJd/btsKbEzu0SI/gInJxSV510bVwlytPQfqR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btPZJd/btsKbEzu0SI/gInJxSV510bVwlytPQfqR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btPZJd/btsKbEzu0SI/gInJxSV510bVwlytPQfqR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtPZJd%2FbtsKbEzu0SI%2FgInJxSV510bVwlytPQfqR0%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;297&quot; height=&quot;357&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;864&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;androidMain 은 android 에만 적용될 코드가, commonMain 은 모든 (android, iOS 등) 플랫폼에서 사용될 코드가, iosMain 에는 iOS에만 적용될 코드가 들어가게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;KMP로 개발하면서 코드 구성 목표는 androidMain, iosMain 은 최소화하고 commonMain을 최대화하는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;3th party library 또한 가급적 최소화하려 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;우선 통신 모듈과 의존성 주입 모듈을 정착시키고 기능을 쉽게 추가할 수 있는 기반부터 만들어보려 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Kotlin 이라는 언어는 JVM 에만 국한되어 있지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin은 Java를 wrapping한 언어라고 잘못 인지하는 경우도 있던데, Java와 100% 호환성을 가지고 있으며 Kotlin 바이트코드를 Java로 변환할 수 있어서 오해가 일어났던 것 같다. 하지만 엄연히 다른 언어이며 Kotlin은 coroutine 처럼 java 에 없는 것들이 있고, 멀티 플랫폼을 지원한다.&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;KMP 를 접한 계기는 건대에서 열린 kotlin conf 2024 를 현장 강연에 놀러갔을 때였는데, 코드도 살짝 맛보게 되었다.&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;jetpack 라이브러리가 android의 성공적인 사례가 되어 KMP 에서 더 확장하게 되었다.&lt;/li&gt;
&lt;li&gt;android 라이브러리를 kmp 로 지원할때 XPoet 을 이용해서 한벌의 코드로 java 와 kotlin의 code generate을 한다.&lt;br /&gt;&lt;i&gt;(아마 이것은 OOP이기에 가능했으리라)&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;KMP를 지원하는 버전으로 올리면 기존 android 앱 동작에 영향은 없을까에 대한 의문이 있을수 있지만 X Processing class 인터페이스가 있어서 지원받는다.&lt;/li&gt;
&lt;li&gt;플랫폼마다 같은 함수로 부르기 위해서 class 명과 함수명 앞에 actual과 expect 키워드가 있다.&lt;br /&gt;ex. expect class A, expect fun a()&lt;br /&gt;ex. actual class B, actual fun b()&lt;br /&gt;&lt;i&gt;(어쩔수 없는 플랫폼 특화 기능을 호출하기 위해 필요한것으로 보이며, expect는 부호 클래스, actual은 자식클래스인 android와 iOS 에서 부르는것 같다.)&lt;/i&gt;&lt;/li&gt;
&lt;li&gt;android로 개발할때와 KMP 로 개발할때의 차이는&amp;nbsp;&lt;br /&gt;KMP 에서는 DI 프레임워크로서 아직 hilt 를 지원하지 않고, koin 을 주로 쓰고 있다. 통신은 retrofit 을 쓸 수 없고(기반인 OkHttp가 Jvm-specific이므로) ktor를 주로 쓰고 있다. (&lt;i&gt;Kotlin의 K와 맞춘것 같은건 기분탓일까&lt;/i&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Programming/Kotlin Multiplatform</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/42</guid>
      <comments>https://yk-coding-letter.tistory.com/42#entry42comment</comments>
      <pubDate>Mon, 21 Oct 2024 03:27:08 +0900</pubDate>
    </item>
    <item>
      <title>Android Studio / Intellij 에서 Github Repository 만들기</title>
      <link>https://yk-coding-letter.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Repository는 한번 만들면 계속 쓰기 때문에 새로운 레포 만들일이 사실 많이 없긴하다. 그래서 자주 까먹곤 하는 GitHub연동법을 기록해놓는다.&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;이미 Github에 프로젝트가 있다면 터미널에서 원하는곳에 경로 이동해서 git clone ~~~.git 입력하여 으로 내 기기에 다운로드할 수 있다. Fetch All remotes, Update Selected 를 클릭하여 잘 푸시가 되는지 확인해보자.&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;그런데 내 기기에서만 존재하는 프로젝트를 GitHub에 올릴때, 혹은 GitHub에는 별 거 아닌 파일만(ex. readme) 있는 경우라면 아래와 같이 빠르게 만들 수 있다.&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;1. Android Studio / Intellji 에서 Project 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 상단 Version control &amp;gt; Create Git Repository &amp;gt; 루트 폴더 선택&lt;/p&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;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dFxVf1/btsKcfeBRIN/A2WJ8tItM1hYt7JWvIjsek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dFxVf1/btsKcfeBRIN/A2WJ8tItM1hYt7JWvIjsek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dFxVf1/btsKcfeBRIN/A2WJ8tItM1hYt7JWvIjsek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdFxVf1%2FbtsKcfeBRIN%2FA2WJ8tItM1hYt7JWvIjsek%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;281&quot; height=&quot;266&quot; data-origin-width=&quot;706&quot; data-origin-height=&quot;266&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;3. diff에 잡힌 파일들 commit (커밋은 나중에 수정 가능하니 initial commit으로 빠르게 짓자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. push 하려고 하면 아래처럼 Define remote을 쓰라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG6BEx/btsKdezmB8L/4jPa0di3ueF2VWl2hnYat0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG6BEx/btsKdezmB8L/4jPa0di3ueF2VWl2hnYat0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG6BEx/btsKdezmB8L/4jPa0di3ueF2VWl2hnYat0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG6BEx%2FbtsKdezmB8L%2F4jPa0di3ueF2VWl2hnYat0%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;283&quot; height=&quot;138&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Github 사이트에서 Repository 를 생성하고 이미 만들었다면 해당 링크(~~~.git) 을 입력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 푸시가 됐는지 확인해보자&lt;/p&gt;</description>
      <category>Programming/Github</category>
      <category>Android Studio</category>
      <category>GitHub</category>
      <category>IntelliJ</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/41</guid>
      <comments>https://yk-coding-letter.tistory.com/41#entry41comment</comments>
      <pubDate>Mon, 21 Oct 2024 00:47:59 +0900</pubDate>
    </item>
    <item>
      <title>Charles Proxy 인증 만료 시 재설치안되던 경우</title>
      <link>https://yk-coding-letter.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yk-coding-letter.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yk-coding-letter.tistory.com/37&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1701868212726&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;Charles Proxy 사용법 + 암호화 해제 방법&quot; data-og-description=&quot;개발을 하다보면 클라이언트에서 api 요청을 제대로 보냈는지, 그에 대한 response는 잘 왔는지 테스트하고 싶을 때가 있다. (postman이랑은 다른것은 postman에서 요청하는 request와 동일하게 클라이언&quot; data-og-host=&quot;yk-coding-letter.tistory.com&quot; data-og-source-url=&quot;https://yk-coding-letter.tistory.com/37&quot; data-og-url=&quot;https://yk-coding-letter.tistory.com/37&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/btXILy/hyUIDuSEy4/Uih4pkE4h3HlZG7lJwR5b1/img.png?width=800&amp;amp;height=619&amp;amp;face=0_0_800_619,https://scrap.kakaocdn.net/dn/eczkQ3/hyUIuEH3If/csBii1KTiHbxUT5e55hVW0/img.png?width=800&amp;amp;height=619&amp;amp;face=0_0_800_619,https://scrap.kakaocdn.net/dn/ddGcUL/hyUIwP2yWZ/TY2K48I2UNJSOeOc9rAfVk/img.png?width=1302&amp;amp;height=1792&amp;amp;face=0_0_1302_1792&quot;&gt;&lt;a href=&quot;https://yk-coding-letter.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yk-coding-letter.tistory.com/37&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/btXILy/hyUIDuSEy4/Uih4pkE4h3HlZG7lJwR5b1/img.png?width=800&amp;amp;height=619&amp;amp;face=0_0_800_619,https://scrap.kakaocdn.net/dn/eczkQ3/hyUIuEH3If/csBii1KTiHbxUT5e55hVW0/img.png?width=800&amp;amp;height=619&amp;amp;face=0_0_800_619,https://scrap.kakaocdn.net/dn/ddGcUL/hyUIwP2yWZ/TY2K48I2UNJSOeOc9rAfVk/img.png?width=1302&amp;amp;height=1792&amp;amp;face=0_0_1302_1792');&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;Charles Proxy 사용법 + 암호화 해제 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;개발을 하다보면 클라이언트에서 api 요청을 제대로 보냈는지, 그에 대한 response는 잘 왔는지 테스트하고 싶을 때가 있다. (postman이랑은 다른것은 postman에서 요청하는 request와 동일하게 클라이언&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yk-coding-letter.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에서 charles proxy의 설치 및 최초 사용법에 대해 설명했었다.&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;그러고 나서 1년이 지나 인증서가 만료가 되었다. (1년마다 만료되는건 너무 심한것 같다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mac의 `키체인 접근`에 들어가서 Charles Proxy CA 의 만료 시간을 보면 오늘 날짜보다 이전임을 통해 알수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Charles Proxy CA 를 제거하고&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;Charles &amp;gt; Help &amp;gt; SSL Proxying &amp;gt; Install Charles Root Certificate 을 하면 `키체인 접근`이 다시 열리는데, Charles Proxy CA 를 아무리 찾아봐도 보이지 않았다.&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;a href=&quot;https://charlesdocsy.com/2021/12/29/expired-charles-proxy-root-certificate/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://charlesdocsy.com/2021/12/29/expired-charles-proxy-root-certificate/&lt;/a&gt; 이 문서의 도움을 받아서 방법을 알게됐는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Charles &amp;gt; Help &amp;gt; SSL Proxying&lt;span&gt; &amp;gt; &lt;/span&gt;&lt;/span&gt;reset Charles Root Certificate 을 한번 해줘야 다시 설치가 되었다.&lt;/p&gt;</description>
      <category>Programming</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/39</guid>
      <comments>https://yk-coding-letter.tistory.com/39#entry39comment</comments>
      <pubDate>Wed, 6 Dec 2023 22:43:57 +0900</pubDate>
    </item>
    <item>
      <title>[이슈 기록]  Android 라이브러리 의존성 문제 해결하기</title>
      <link>https://yk-coding-letter.tistory.com/38</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;배경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Room 버전을 2.3.0 에서 2.4.0-alpha03 이상으로 업데이트 해야할 일이 생겼었다.&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;그런데 버전 올리고 빌드를 돌렸더니 갑자기 coroutine 과 rxjava쪽에서 에러가 발생했다고 뜬다.&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;3064&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEvlrJ/btrNDVC8Krt/4NwpfVDBWFU3K9GLkKI3Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEvlrJ/btrNDVC8Krt/4NwpfVDBWFU3K9GLkKI3Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEvlrJ/btrNDVC8Krt/4NwpfVDBWFU3K9GLkKI3Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEvlrJ%2FbtrNDVC8Krt%2F4NwpfVDBWFU3K9GLkKI3Dk%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;3064&quot; height=&quot;566&quot; data-origin-width=&quot;3064&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Room을 업데이트하는데 coroutine/rxjava에서 문제가 생겨서 굉장히 뜬금없다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;원인&lt;/h4&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;kotlinx-coroutines-core의 버전과&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;kotlinx.coroutines-reactive의 버전은 동일해야한다고 한다. &lt;a title=&quot;관련 링크&quot; href=&quot;https://github.com/Kotlin/kotlinx.coroutines/issues/2250#issuecomment-696717233&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;관련 링크&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;내가 dependency에 추가한 paging-rxjava3에는 kotlin-coroutines-reactive 가 1.4.3 버전으로 들어가있다. 하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;room 라이브러리내 kotlin-coroutines-android가 1.5.0 버전으로 dependency가 들어가있고, 이것이 core 버전을 1.5.0으로 올린다. 반면!! reactive는 없다. 따라서 core만 상위 버전으로 올라가 있는 상태이다.&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;해결하기 위해 reactive의 버전을 명시적으로 core와 일치시키면 된다.(1.5.0 이상으로)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1664727387053&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    // coroutine
    def coroutine_version = &quot;1.5.0&quot;
    implementation &quot;org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version&quot;
    implementation &quot;org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version&quot;
    implementation &quot;org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$coroutine_version&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해결 방법&lt;/h4&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;dependency tree&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;./gradlew app:dependencies&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;952&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6Plgo/btrNwDRtesc/xIfGfomI6U3hCvicup5Sy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6Plgo/btrNwDRtesc/xIfGfomI6U3hCvicup5Sy0/img.png&quot; data-alt=&quot;일부 캡쳐 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6Plgo/btrNwDRtesc/xIfGfomI6U3hCvicup5Sy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6Plgo%2FbtrNwDRtesc%2FxIfGfomI6U3hCvicup5Sy0%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;952&quot; height=&quot;192&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일부 캡쳐 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 이해한 바에 의하면 위의 사진을 이렇게 해석하면 된다. &lt;a title=&quot;참고 링크&quot; href=&quot;https://mond-al.github.io/dependency-gradle-setup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고 링크&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;org.mockito:mockito-inline:2.21.0 의존성을 추가되어 있고(build.gradle) 그 내에 다음의 라이브러리 의존성이 추가되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- org.mockito:mockito-core:2.21.0 이 그 내에 추가되어 있다. 하지만 4.0.0으로 자동으로 [버전업]한다. (그 이유는 다른 라이브러리에서 상위 버전의 mockito-core 의 의존성을 추가했기 때문이다) 그리고 org.mockito:mockito-core:4.0.0 내에 포함된 다음의 라이브러리에 의존성을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - net.bytebuddy:byte-buddy:1.11.19&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - net.bytebuddy:byte-buddy-agent:1.11.19&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;dependency tree 읽는 법을 파악하고 나서, 앱의 라이브러리가 어떻게 의존성이 얽혀있는지 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;1480&quot; data-origin-height=&quot;570&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcxGqq/btrNxeKK0nI/m04QhthCjEm5lQOeR3IoZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcxGqq/btrNxeKK0nI/m04QhthCjEm5lQOeR3IoZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcxGqq/btrNxeKK0nI/m04QhthCjEm5lQOeR3IoZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcxGqq%2FbtrNxeKK0nI%2Fm04QhthCjEm5lQOeR3IoZ0%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;1480&quot; height=&quot;570&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급했듯이 kotlinx-coroutines-reactive와 kotlinx-coroutines-core의 버전이 일치해야한다는것이 대전제이다. (그렇게 동작하도록 설계되어 있다고 위에 첨부한 이슈의 jetbrain 개발자가 알려줬기 때문)&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;coroutines-core의 1.4.3 은 1.5.0으로 버전이 올라갔다.(이 이유는 버전이 올라간 room 라이브러리에서 coroutines-core 버전이 올라간걸로 의존성을 걸었기 때문)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 coroutines-reactive는 1.4.3으로 남아있다. 이것이 문제임을 발견하고 명시적으로 버전을 추가하고 나니 이슈없이 잘 빌드되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;추가로, terminal에 출력되는 라인이 매우 길 수 있다. &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 경우엔 아래와 같이 입력하여 text 파일로 저장할 수 있다.(터미널에서 아무 반응없는것 처럼 보이지만 인내하고 기다려보자)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;터미널 현재 경로에 output.txt 파일을 찾을 수 있을 것이다.&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;./gradlew  app:dependencies &amp;gt; output.txt&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;도움된 사이트&lt;/h4&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;a href=&quot;https://github.com/Kotlin/kotlinx.coroutines/issues/2250&quot;&gt;https://github.com/Kotlin/kotlinx.coroutines/issues/2250&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1664726105738&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Caused by: java.lang.NoSuchMethodError kotlinx.coroutines.reactive.FlowSubscription when updating kotlinx-coroutines from 1.3.7 &quot; data-og-description=&quot;Dear Kotlin-team, I m having a rective spring-boot project (spring-boot version 2.3.3.). I updated Kotlin to version 1.4 and kotlinx-coroutines-core and kotlinx-coroutines-reactor to 1.3.9. I am us...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/Kotlin/kotlinx.coroutines/issues/2250&quot; data-og-url=&quot;https://github.com/Kotlin/kotlinx.coroutines/issues/2250&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Jn7nW/hyPZa6cuHb/6GCJsRMMaZ2XJRhtFKKCH1/img.png?width=1200&amp;amp;height=600&amp;amp;face=991_118_1062_196&quot;&gt;&lt;a href=&quot;https://github.com/Kotlin/kotlinx.coroutines/issues/2250&quot; data-source-url=&quot;https://github.com/Kotlin/kotlinx.coroutines/issues/2250&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Jn7nW/hyPZa6cuHb/6GCJsRMMaZ2XJRhtFKKCH1/img.png?width=1200&amp;amp;height=600&amp;amp;face=991_118_1062_196');&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;Caused by: java.lang.NoSuchMethodError kotlinx.coroutines.reactive.FlowSubscription when updating kotlinx-coroutines from 1.3.7&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Dear Kotlin-team, I m having a rective spring-boot project (spring-boot version 2.3.3.). I updated Kotlin to version 1.4 and kotlinx-coroutines-core and kotlinx-coroutines-reactor to 1.3.9. I am us...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;a href=&quot;https://mond-al.github.io/dependency-gradle-setup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://mond-al.github.io/dependency-gradle-setup&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1664728671336&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;앱의 의존성 확인하고 전이 의존성 변경하기&quot; data-og-description=&quot;의존성과 관련하여 문제가 생겼다고 판단되면 의존성 트리를 봐야합니다. 터미널에서 프로젝트가 있는 폴더로 이동하고 아래의 명령어를 실행 하면됩니다.&quot; data-og-host=&quot;mond-al.github.io&quot; data-og-source-url=&quot;https://mond-al.github.io/dependency-gradle-setup&quot; data-og-url=&quot;https://mond-al.github.io/dependency-gradle-setup&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bBZMc3/hyP0FKhwl5/u5uDjpPwtn7Kty0OPzBppK/img.png?width=540&amp;amp;height=400&amp;amp;face=0_0_540_400,https://scrap.kakaocdn.net/dn/cyVqDc/hyPY3stEH6/XbH9NxKJJk4xaeD1ZWoLK0/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/dQpuIg/hyP0K5SG2y/dTlBkxyF8mEYVaWUeHlA1K/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500&quot;&gt;&lt;a href=&quot;https://mond-al.github.io/dependency-gradle-setup&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mond-al.github.io/dependency-gradle-setup&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bBZMc3/hyP0FKhwl5/u5uDjpPwtn7Kty0OPzBppK/img.png?width=540&amp;amp;height=400&amp;amp;face=0_0_540_400,https://scrap.kakaocdn.net/dn/cyVqDc/hyPY3stEH6/XbH9NxKJJk4xaeD1ZWoLK0/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/dQpuIg/hyP0K5SG2y/dTlBkxyF8mEYVaWUeHlA1K/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500');&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;앱의 의존성 확인하고 전이 의존성 변경하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;의존성과 관련하여 문제가 생겼다고 판단되면 의존성 트리를 봐야합니다. 터미널에서 프로젝트가 있는 폴더로 이동하고 아래의 명령어를 실행 하면됩니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mond-al.github.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;</description>
      <category>Programming/Android</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/38</guid>
      <comments>https://yk-coding-letter.tistory.com/38#entry38comment</comments>
      <pubDate>Mon, 3 Oct 2022 01:47:43 +0900</pubDate>
    </item>
    <item>
      <title>Charles Proxy 사용법 + 암호화 해제 방법</title>
      <link>https://yk-coding-letter.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 하다보면 클라이언트에서 api 요청을 제대로 보냈는지, 그에 대한 response는 잘 왔는지 테스트하고 싶을 때가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(postman이랑은 다른것은 postman에서 요청하는 request와 동일하게 클라이언트에서 개발한것이 전송되지 않았을 가능성도 고려할 수 있다. 그리고 관심갖지 못했던 다른 api 들에 대해서도 감지할 수 있다.)&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;Charles 설치, 사용법과 암호화 해제법에 대해 작성하려 한다.&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;1. Charles 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Android 기기를 노트북과 network 연결&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.&amp;nbsp; 노트북에 CA 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. Android 기기에 CA 설치&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. SSL Proxy 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;0. Charles Proxy에 대하여&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Charles 와 같은 프록시 프로그램은 기기에서 발생하는 네트워크를 Proxy(대리자)를 거쳐서 이루어지게 하여, 그 사이에서 Proxy 프로그램이 어떤 데이터들이 오고가는지를 보여주거나 break point를 걸어 디버깅에 용이하게 하는 툴이다. 따라서 Charles 는 컴퓨터(노트북 or 데스크탑)에 설치하는 프로그램이고, 클라이언트 기기는 노트북에 연결되어야한다. 연결하는 방법은 같은 와이파이 Acess Point 에 연결한 후 기기에서 프록시설정을 하면 되는데 자세한건 아래에 적겠다.&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;h4 data-ke-size=&quot;size20&quot;&gt;1. Charles 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.charlesproxy.com/download/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.charlesproxy.com/download/&lt;/a&gt; 사이트에서 OS에 맞게 설치한다.&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;h4 data-ke-size=&quot;size20&quot;&gt;2. 디바이스를 컴퓨터에 연결&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;우선 컴퓨터와 핸드폰이 동일한 와이파이 AP(Access Point)에 연결되어 있어야한다. 왠만하면 공유기가 같으면 될 것이다.&lt;/li&gt;
&lt;li&gt;컴퓨터의 ip 주소를 파악한다. cmd나 터미널로 확인할 수도 있겠지만 charles에서는 help &amp;gt; local IP address 를 보면 친절하게 알려준다.&lt;/li&gt;
&lt;li&gt;디바이스를 (위에서 파악한)컴퓨터의 ip 주소에 연결한다. 프록시 포트 번호는 8888 로 두면 되는데 Charles 프로그램에서 Proxy &amp;gt; Proxy Settings 에 포트번호와 동일하면 된다. (default가 8888임)&lt;/li&gt;
&lt;li&gt;연결이 끝나면 Charles 프로그램에 기기에서 발생한 네트워크들이 감지가 될 것이다. 아마도 http:// ~~ 의 데이터들은 request와 response 값이 보일 수는 있지만, 아직 https 와 같은 암호화된 데이터는 보이지 않을 것이다.(암호화되어 unknown으로 보일 것이다)&lt;/li&gt;
&lt;/ul&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;1700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T9iTi/btrLqWcRVO2/LCWkCDdTeNqNMydgwZ0M11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T9iTi/btrLqWcRVO2/LCWkCDdTeNqNMydgwZ0M11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T9iTi/btrLqWcRVO2/LCWkCDdTeNqNMydgwZ0M11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT9iTi%2FbtrLqWcRVO2%2FLCWkCDdTeNqNMydgwZ0M11%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;215&quot; height=&quot;1700&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;1700&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;이 과정(디바이스를 컴퓨터에 연결 과정)은 Charles가 처음이라면 조금 오래 걸릴 수도 있다.&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이제부터는 Charles 에서 unknown이라고 뜨는 데이터들을 볼 수 있게 하는 방법들이다. (암호화 해제)&lt;/h3&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;h4 data-ke-size=&quot;size20&quot;&gt;3. 컴퓨터에 CA 를 설치한다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Charles에서 help &amp;gt; SSL Proxying &amp;gt; Install Charles Root Certificate 클릭하면 아래와 같은 창이 뜬다.&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0Dom5/btrLirrVxW7/kQhcnLFQKbGKV7NnFVV5A0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0Dom5/btrLirrVxW7/kQhcnLFQKbGKV7NnFVV5A0/img.png&quot; data-alt=&quot; &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0Dom5/btrLirrVxW7/kQhcnLFQKbGKV7NnFVV5A0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0Dom5%2FbtrLirrVxW7%2FkQhcnLFQKbGKV7NnFVV5A0%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;402&quot; height=&quot;232&quot; data-origin-width=&quot;1066&quot; data-origin-height=&quot;614&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;690&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chjPhb/btrLmxkpxKR/DJSpLVBMHZkOUf2hFBAkU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chjPhb/btrLmxkpxKR/DJSpLVBMHZkOUf2hFBAkU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chjPhb/btrLmxkpxKR/DJSpLVBMHZkOUf2hFBAkU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchjPhb%2FbtrLmxkpxKR%2FDJSpLVBMHZkOUf2hFBAkU1%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;470&quot; height=&quot;690&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Charles Proxy CA ... 더블클릭&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xRqqM/btrLhuCY2xD/7Ek6J07iqB328xIVNNGYg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xRqqM/btrLhuCY2xD/7Ek6J07iqB328xIVNNGYg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xRqqM/btrLhuCY2xD/7Ek6J07iqB328xIVNNGYg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxRqqM%2FbtrLhuCY2xD%2F7Ek6J07iqB328xIVNNGYg1%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;475&quot; height=&quot;824&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'이 인증서 사용 시' 를 &quot;항상 신뢰&quot;로 변경&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;a href=&quot;https://www.youtube.com/watch?v=g1IQTuiPWZQ&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;유튜브 영상&lt;/a&gt;을 첨부한다. 노트북에 CA 설치는 1분 30초 정도 까지만 보면 되고, SSL 연결은 아래쪽에 작성하였다.&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;h4 data-ke-size=&quot;size20&quot;&gt;4. Android 기기에 CA 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나에게 가장 많은 시간 소요을 하게 해준 단계이다. 인증서 설치가 잘 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web 디버깅만 한다면 이 과정은 없어도 되겠지만 안드로이드 네트워크를 디버깅하고 싶은 상황이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Charles에서 help &amp;gt; SSL Proxying &amp;gt; Install Charles Root Certificate on a Mobile Device or Remote Browser 를 클릭하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doannt/btrLhY4Gwzk/qKPd5IYBcH4wLAcQsWreg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doannt/btrLhY4Gwzk/qKPd5IYBcH4wLAcQsWreg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doannt/btrLhY4Gwzk/qKPd5IYBcH4wLAcQsWreg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdoannt%2FbtrLhY4Gwzk%2FqKPd5IYBcH4wLAcQsWreg1%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;721&quot; height=&quot;166&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 뜬다. browse to chls.pro/ssl 라고 적혀있듯이 접속하여 다운로드 한다.&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 기기에서 아무 브라우저를 열어(삼성브라우저 등) &lt;a href=&quot;https://chls.pro/ssl&quot;&gt;https://chls.pro/ssl&lt;/a&gt; 로 접속하고, 인증서를 다운로드 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(주의) Charles와 연결되어 있어야 다운로드가 진행된다!!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 열어보면 아래와 같은 화면이 뜰 것이다. 파일은 다운로드 되지만 CA 인증서로는 인정할 수 없다는것 같다.&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;1710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GhZBF/btrLjjG1YOk/eWcOnjtQeuVZE0XsAqyJM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GhZBF/btrLjjG1YOk/eWcOnjtQeuVZE0XsAqyJM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GhZBF/btrLjjG1YOk/eWcOnjtQeuVZE0XsAqyJM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGhZBF%2FbtrLjjG1YOk%2FeWcOnjtQeuVZE0XsAqyJM1%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;226&quot; height=&quot;502&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;1710&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;설정앱 &amp;gt; 생체 인식 및 보안 &amp;gt; 기타 보안 설정 &amp;gt; 기기에 저장된 인증서 설치 &amp;gt; CA 인증서 &amp;gt; 계속 설치 &amp;gt; 다운받았던 파일 선택 &amp;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;span&gt;스크린샷은 내가 찍기 귀찮아서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://gall.dcinside.com/mgallery/board/view/?id=wonyeong&amp;amp;no=368388&quot;&gt;도움받은 링크&lt;/a&gt;&lt;span&gt;의 페이지를 스크린샷했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zGORv/btrLmxSe7sP/kkFNkgWVyz4XZ2fjulaSR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zGORv/btrLmxSe7sP/kkFNkgWVyz4XZ2fjulaSR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zGORv/btrLmxSe7sP/kkFNkgWVyz4XZ2fjulaSR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzGORv%2FbtrLmxSe7sP%2FkkFNkgWVyz4XZ2fjulaSR0%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;619&quot; height=&quot;852&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1792&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;h4 data-ke-size=&quot;size20&quot;&gt;5. SSL 프록시 세팅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Charles 에서 Proxy &amp;gt; SSL Proxy Settings 에 들어가서 원하는 api의 정보를 넣는다. (base URL 등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 넣고 OK를 누른다. 유동적인 부분은 * 를 쓰면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업에서 추가한 url 에 해당하는 api 들은 unknown 이 아니라 암호화가 해제되어 실제 데이터값들이 폴더구조와 함께 보일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에도 첨부했던 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=g1IQTuiPWZQ&quot;&gt;유튜브 영상&lt;/a&gt;의 1분 30초 뒷부분이 해당 내용이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1186&quot; data-origin-height=&quot;958&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V3ysG/btrK95wx9Br/f3rulbbwq0xCcZqXj33BD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V3ysG/btrK95wx9Br/f3rulbbwq0xCcZqXj33BD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V3ysG/btrK95wx9Br/f3rulbbwq0xCcZqXj33BD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV3ysG%2FbtrK95wx9Br%2Ff3rulbbwq0xCcZqXj33BD1%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;361&quot; height=&quot;292&quot; data-origin-width=&quot;1186&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;추가 정보&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.charlesproxy.com/documentation/using-charles/ssl-certificates/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.charlesproxy.com/documentation/using-charles/ssl-certificates/&lt;/a&gt; 에 의하면 Android N 부터 내가 제어할 수 있는 앱만 SSL Proxying을 할 수 있다고 한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TMI&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 아래 사진과 같이 macOS Proxy 을 체크하면 컴퓨터의 네트워크 관찰을 할 수 있다.&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 alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UiA86/btrLqWYgqCO/w5vM27QJ0Awo5AS6TeKtkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UiA86/btrLqWYgqCO/w5vM27QJ0Awo5AS6TeKtkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UiA86/btrLqWYgqCO/w5vM27QJ0Awo5AS6TeKtkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUiA86%2FbtrLqWYgqCO%2Fw5vM27QJ0Awo5AS6TeKtkK%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;442&quot; height=&quot;395&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 유튜브 등의 일부 앱은 proxy로 연결하면 네트워크 연결이 안된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. release 빌드에서도 charles로 관찰하고싶다면 이 글을 참고하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/@jplipata/using-charles-proxy-on-an-android-release-build-2fd7be125674&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/@jplipata/using-charles-proxy-on-an-android-release-build-2fd7be125674&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://github.com/JaeYeopHan/tip-archive/issues/33&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/JaeYeopHan/tip-archive/issues/33&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://devmjun.github.io/archive/Charles&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://devmjun.github.io/archive/Charles&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=g1IQTuiPWZQ&quot;&gt;https://www.youtube.com/watch?v=g1IQTuiPWZQ&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gall.dcinside.com/mgallery/board/view/?id=wonyeong&amp;amp;no=368388&quot;&gt;https://gall.dcinside.com/mgallery/board/view/?id=wonyeong&amp;amp;no=368388&lt;/a&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Programming</category>
      <category>android</category>
      <category>Charles</category>
      <category>client</category>
      <category>Debug</category>
      <category>Mobile</category>
      <category>proxy</category>
      <category>Proxy 프로그램</category>
      <category>response</category>
      <category>네트워크 디버깅</category>
      <category>암호화</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/37</guid>
      <comments>https://yk-coding-letter.tistory.com/37#entry37comment</comments>
      <pubDate>Sun, 4 Sep 2022 21:48:24 +0900</pubDate>
    </item>
    <item>
      <title>[이슈 기록] Android 에서 단일 모듈 -&amp;gt; 멀티 모듈로 변경할 때 만났던 빌드 에러 이슈</title>
      <link>https://yk-coding-letter.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;클린 아키텍쳐를 위하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app - domain - data 의 Layer로 분리한다고 했을 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app은 domain의 의존성을 갖고(usecase or repository interface 등),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;data는 domain의 의존성을 갖는다(repository interface를 상속해야함 등)&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;그런데 app 이 domain 의 의존성만 갖고 있으면 빌드 에러가 났었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(data의 di에서 Repository interface의 구현체인 RepositoryImp 을 주입해주는데, domain의 의존성만으로 repositoryImp을 알 수 없기 때문이라고 생각한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;app 모듈에서 domain과 data 모듈에 모두 의존성을 추가&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;--아무리 구글링해도 안나왔다. 한 4시간 정도 삽질했다--&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;pre id=&quot;code_1656256689414&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;C:\Users\user\Desktop\androidWorkspace\KuringHouse\app\build\generated\source\kapt\debug\com\yeonkyu\kuringhouse\KuringHouseApplication_HiltComponents.java:140: error: [Dagger/MissingBinding] com.yeonkyu.domain.repository.LoginRepository cannot be provided without an @Provides-annotated method.
  public abstract static class SingletonC implements KuringHouseApplication_GeneratedInjector,
                         ^
      com.yeonkyu.domain.repository.LoginRepository is injected at
          com.yeonkyu.domain.usecase.auth.GetUserUseCase(repository)
      com.yeonkyu.domain.usecase.auth.GetUserUseCase is injected at
          com.yeonkyu.kuringhouse.presentation.login.LoginViewModel(getUserUseCase, ��)
      com.yeonkyu.kuringhouse.presentation.login.LoginViewModel is injected at
          com.yeonkyu.kuringhouse.presentation.login.LoginViewModel_HiltModules.BindsModule.binds(vm)
      @dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map&amp;lt;java.lang.String,javax.inject.Provider&amp;lt;androidx.lifecycle.ViewModel&amp;gt;&amp;gt; is requested at
          dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.yeonkyu.kuringhouse.KuringHouseApplication_HiltComponents.SingletonC �� com.yeonkyu.kuringhouse.KuringHouseApplication_HiltComponents.ActivityRetainedC �� com.yeonkyu.kuringhouse.KuringHouseApplication_HiltComponents.ViewModelC]
  It is also requested at:
      com.yeonkyu.domain.usecase.auth.AuthorizeUseCase(repository)
      com.yeonkyu.domain.usecase.auth.UpdateUserUseCase(repository)C:\Users\user\Desktop\androidWorkspace\KuringHouse\app\build\generated\source\kapt\debug\com\yeonkyu\kuringhouse\KuringHouseApplication_HiltComponents.java:140: error: [Dagger/MissingBinding] com.yeonkyu.domain.repository.RoomRepository cannot be provided without an @Provides-annotated method.
  public abstract static class SingletonC implements KuringHouseApplication_GeneratedInjector,&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;그리고 Retrofit은 Data Layer 에만 있으면 될 것이라 생각했는데, app Layer 에서도 같이 의존성을 추가해야했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이거는 다행히 에러로그가 비교적 친절하다 - 대충 Retrofit을 provide받을 수 없다는 에러 문구)&lt;/p&gt;</description>
      <category>Programming/Android</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/36</guid>
      <comments>https://yk-coding-letter.tistory.com/36#entry36comment</comments>
      <pubDate>Mon, 27 Jun 2022 00:21:49 +0900</pubDate>
    </item>
    <item>
      <title>[이슈 기록] TagetSdk 30 -&amp;gt; 31 올리는 과정에서 까다로웠던 이슈 기록 android:exported needs to be explicitly specified for &amp;lt;activity&amp;gt; 이슈</title>
      <link>https://yk-coding-letter.tistory.com/35</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;배경&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TargetSdk 31부터는 Activity나 Service, broadcast receiver에서 인텐트 필터를 사용하지만 명시적으로 선언된 android:exported 값이 없으면 Android 12 이상을 실행하는 기기에 앱을 설치할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( Reference :&amp;nbsp;&lt;a href=&quot;https://developer.android.com/about/versions/12/behavior-changes-12?hl=ko#exported&quot;&gt;https://developer.android.com/about/versions/12/behavior-changes-12?hl=ko#exported &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;size18&quot;&gt;하지만 나는 Manifest 파일을&lt;b&gt; 잘 작성 했음에도&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;android:exported needs to be explicitly specified for &amp;lt;activity&amp;gt;. Apps targeting Android 12 and higher are required to specify an explicit value for `android:exported` when the corresponding component has an intent filter defined.&amp;nbsp;&amp;nbsp;&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;h4 data-ke-size=&quot;size20&quot;&gt;원인&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진과 같이 Manifest 파일에서 (1번 동그라미) Merged Manifest를 클릭하면 자세한 에러 메세지가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 읽어보니 &lt;b&gt;androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity&amp;nbsp;&lt;/b&gt;클래스와,&amp;nbsp;&lt;b&gt;androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity&amp;nbsp;&lt;/b&gt;클래스와,&amp;nbsp;&lt;b&gt;androidx.test.core.app.InstrumentationActivityInvoker$EmptyFloatingActivity &lt;/b&gt;클래스에서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;android:exported 설정을 하지않았기 때문에 발생했다는 것을 알 수 있었다.(2번 동그라미)&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;빌드하면서 dependency에서 추가한 라이브러리들의 Manifest를 통합하는 과정이 있고, targetSdk 31에서 맞지 않는 부분이 있기 때문에 발생하는 것이었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1499&quot; data-origin-height=&quot;611&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uiqDz/btrDqUuLy2C/0c7PJIMAmZKNkUyUrVrrBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uiqDz/btrDqUuLy2C/0c7PJIMAmZKNkUyUrVrrBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uiqDz/btrDqUuLy2C/0c7PJIMAmZKNkUyUrVrrBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuiqDz%2FbtrDqUuLy2C%2F0c7PJIMAmZKNkUyUrVrrBk%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;1499&quot; height=&quot;611&quot; data-origin-width=&quot;1499&quot; data-origin-height=&quot;611&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;h4 data-ke-size=&quot;size20&quot;&gt;해결 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;방법 1)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 되었던 위의 3가지 클래스를 app단의 Manifest에서 overriding 하여 export 값을 설정해주는 것이다. 아래와 같이 말이다.&lt;/p&gt;
&lt;pre id=&quot;code_1653843553831&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        &amp;lt;activity
            android:name=&quot;androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity&quot;
            android:exported=&quot;false&quot; /&amp;gt;
        &amp;lt;activity
            android:name=&quot;androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity&quot;
            android:exported=&quot;false&quot; /&amp;gt;
        &amp;lt;activity
            android:name=&quot;androidx.test.core.app.InstrumentationActivityInvoker$EmptyFloatingActivity&quot;
            android:exported=&quot;false&quot; /&amp;gt;&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;어떻게보면 근본적인 해결책은 아니지만 빠르게 해결할 수 있는 방법인 것 같다.(Merged Manifest에서 문제가 되는 클래스를 명시적으로 알려주니까!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;방법 2)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하는 라이브러리 중 낡은 라이브러리가 있거나 해당 라이브러리에서 targetSdk 31을 고려하지 않았기 때문이다. 나의 경우에는 결론적으로는 fragment를 낡은 버전을 쓰고 있었기 때문이었다.&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;아래와 같이 문제가 되었던 세 Activity들은 아래의 라이브러리 들 중 하나에서 찾을 수 있었는데, 파란색 텍스트를 클릭하면 해당 Manifest로 이동할 수 있었다.&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;1242&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cogUgl/btrDqVHasXf/gyQvdGqTBuTNKWpT5PUM9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cogUgl/btrDqVHasXf/gyQvdGqTBuTNKWpT5PUM9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cogUgl/btrDqVHasXf/gyQvdGqTBuTNKWpT5PUM9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcogUgl%2FbtrDqVHasXf%2FgyQvdGqTBuTNKWpT5PUM9k%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;1242&quot; height=&quot;257&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전부 다 하나하나 클릭해서 들어가보니 test:core:1.2.0 에서 아래의 Manifest가 있었고, 모든 원인은 test:core:1.2.0 때문임을 알 수 있었다. 세 Activity가 intent-filter를 갖고 있지만 android:export 값이 없었다. 이것은 외부 라이브러리이기 때문에 직접 수정할 수는 없었다. 따라서 이 라이브러리를 dependency추가했던 것을 최신버전으로 업데이트하는 것이 솔루션이었다.(다만 최신버전에서 targetSdk 31 대응을 했다는 것을 전제로 하고있다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;838&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdreVw/btrDvRwV7rJ/uF4cvekiBCwxE7ViSu4UQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdreVw/btrDvRwV7rJ/uF4cvekiBCwxE7ViSu4UQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdreVw/btrDvRwV7rJ/uF4cvekiBCwxE7ViSu4UQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdreVw%2FbtrDvRwV7rJ%2FuF4cvekiBCwxE7ViSu4UQk%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;1129&quot; height=&quot;838&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;838&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/a/69650626/18140287&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/a/69650626/18140287&lt;/a&gt; 에 의하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 fragment-testing을 &lt;span style=&quot;background-color: #e3e6e8; color: #232629;&quot;&gt;1.4.0-alpha03&lt;/span&gt; 이상으로 업데이트해야 한다고 한다. 나 또한 1.4.0-alpha03 으로 버전을 올렸다. (기존에는 1.2.0-rc04 이었다.)&lt;/p&gt;
&lt;pre id=&quot;code_1653844269009&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// fragment
def fragment_version = '1.4.0-alpha03'
implementation &quot;androidx.fragment:fragment-ktx:$fragment_version&quot;
debugImplementation &quot;androidx.fragment:fragment-testing:$fragment_version&quot;&lt;/code&gt;&lt;/pre&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;</description>
      <category>Programming/Android</category>
      <category>android</category>
      <category>manifest</category>
      <category>targetSDK</category>
      <category>targetsdk31</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/35</guid>
      <comments>https://yk-coding-letter.tistory.com/35#entry35comment</comments>
      <pubDate>Mon, 30 May 2022 02:19:56 +0900</pubDate>
    </item>
    <item>
      <title>Android + CI/CD 적용하기 - CD편 (feat.Github Actions)</title>
      <link>https://yk-coding-letter.tistory.com/32</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;CI 에서는 yml 파일을 어떻게 작성하는가에 시간을 많이 썼었다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CD 에서는 &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;쿠링 레파지토리의 release 브랜치에 Push 또는 PR이 있을 때 이를 어디에 배포할지에 대한 고민을 첫 번째로 했었고 후보는&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;AWS S3에 apk를 배포&lt;/li&gt;
&lt;li&gt;firebase 등에 apk 배포&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 있었다. 플레이스토어에 바로 배포하는 방법도 존재하긴 하지만 바로 검토를 올리는것은 조심스러워서 일단 apk/aab를 받아보기로 했다. 플레이스토어에 출시노트나 다른 세세한 설정들을 만져야 할 때 직접 손으로 하는게 낫다고도 생각했다.&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;3번째인 firebase에 올리는것이 유력 후보였는데 Github Actions 내에서 또 다시 나를 도왔다. &lt;b&gt;Artifacts라고 해서 github actions으로 만든 산출물을 파일로 저장해준다.&lt;/b&gt; 이로서 정말 A to Z를 깃헙액션에서 다 도움을 받았다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;나의 보안 파일은 무엇이 있나?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;release 브랜치에 업로드 했을 때 필요한 파일 중&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gitignore 로 인하여 깃허브에 보이지 않는 보안 파일은 Secret에 올렸다. 해당파일은&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;google-services.json (배포용)&lt;/li&gt;
&lt;li&gt;signing keyStore (용도 : 플레이 콘솔)&lt;/li&gt;
&lt;li&gt;keyStore 관련 비밀번호들 (용도 : 플레이 콘솔)&lt;br /&gt;[keyStore 비밀번호, alias, alias 비밀번호]&lt;/li&gt;
&lt;/ol&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;1152&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byGsA2/btrvyicHsNG/1IgAUH2EsN0FBi4kEmiyDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byGsA2/btrvyicHsNG/1IgAUH2EsN0FBi4kEmiyDk/img.png&quot; data-alt=&quot;Github &amp;amp;amp;amp;amp;amp;gt; Settings &amp;amp;amp;amp;amp;amp;gt; Security &amp;amp;amp;amp;amp;amp;gt; Secrets &amp;amp;amp;amp;amp;amp;gt; Actions 에 있다.&amp;amp;amp;amp;amp;amp;nbsp;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byGsA2/btrvyicHsNG/1IgAUH2EsN0FBi4kEmiyDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyGsA2%2FbtrvyicHsNG%2F1IgAUH2EsN0FBi4kEmiyDk%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;1152&quot; height=&quot;345&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Github &amp;amp;amp;amp;amp;gt; Settings &amp;amp;amp;amp;amp;gt; Security &amp;amp;amp;amp;amp;gt; Secrets &amp;amp;amp;amp;amp;gt; Actions 에 있다.&amp;amp;amp;amp;amp;nbsp;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;b&gt;step 1. &lt;b&gt;보안 파일 감추기 - &lt;/b&gt;깃헙에서 해야할 일&lt;/b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;1. google-services.json 파일은 json형식으로 그냥 올리면 된다. &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(위 사진에서 GOOGLE_SERVICES_JSON_RELEASE)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2. keyStore.properties 는 로컬에서 gitignore에 의하여 세팅되어 있어 github에서는 보이지 않는다. &lt;/span&gt;이 파일의 형식은&amp;nbsp; 각각의 비밀번호가 XXXX라고 했을때 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;(위 사진에서 KEY_STORE_PROPERTIES)&lt;/p&gt;
&lt;pre id=&quot;code_1646924355264&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;storePassword=XXXX
keyPassword=XXXX
keyAlias=XXXX
storeFile=signing/ku_ring_keystore.jks&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;여기서 storeFile은 keyStore가 있는 파일의 위치이다.&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;위 파일의 각 줄들을 yml 파일에서 읽어 각각의 처리를 한다.&lt;span&gt; (아래에서 다시 다룰 것이다)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. keyStore는 조금 특별하다. 파일용량을 보면 3kb 인데 내용물이 binary 코드로 적혀있다. 따라서 메모장으로 이것을 열어보면 한자같은 이상한 글자들이 보인다. 이것을 그냥 올리면 github 원격 서버에서 빌드하는 과정에서 에러가 발생하고, base64 인코딩한 뒤 저장을 한뒤, 빌드할 때 다시 binary 로 디코딩 변환해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;인코딩 방법:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;&amp;nbsp; &amp;nbsp; openssl base64 -&lt;/span&gt;in&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt; [keystore file path]&amp;nbsp; -&lt;/span&gt;out&lt;span style=&quot;background-color: #f8f8f8;&quot;&gt; [base64 file path]&lt;br /&gt;&lt;/span&gt;&lt;/b&gt;이 명령어를 리눅스명령어로 터미널에서 입력하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(윈도우 컴퓨터라면 리눅스 명령어를 칠 수 있는 bash 를 사용하면 되는데 자세한건 여기서 생략하겠다.)&lt;b&gt;&lt;br /&gt;디코딩 방법:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &lt;span style=&quot;background-color: #f8f8f8;&quot;&gt;echo '${{ secretsYOUER_KEY_NAME }}' | base64 -d &amp;gt; xxxxxx.keystore&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이것은 yml에서 사용될 명령어이다. 아래에서 다시 등장할 것이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;b&gt;step 2. &lt;b&gt;보안 파일 감추기 - &lt;/b&gt;내 로컬 컴퓨터에서 해야할 일&lt;/b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. keystore.properties 와 keyStore 가 있는 경로를 gitignore에서 추가한다. keystore.properties의 생김새는 이렇다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646925642173&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;storePassword=XXXX
keyPassword=XXXX
keyAlias=XXXX
storeFile=signing/ku_ring_keystore.jks&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. build.gradle (module) 에서 keystore.properties 를 가져온다. 경로가 내 프로젝트에 잘 맞는지 잘 확인해야한다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;def keystorePropertiesFile = rootProject.file(&quot;app/signing/keystore.properties&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 keystore.properties가 있는 파일 위치를 정의하고,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;android { } 내에서 signingConfig{ } 내에 release { } 를 만들고, 그 안에 storeFile, keyAlias, keyPassword, storePassword 값들을 각각 대입한다. 그러면 이런식이 될 것이다.&lt;/p&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;def keystorePropertiesFile = rootProject.file(&quot;app/signing/keystore.properties&quot;)

android {
    signingConfigs {
        release {
            def keystoreProperties = new Properties()
            keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

            storeFile file(keystoreProperties['storeFile'])
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storePassword keystoreProperties['storePassword']
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤에 singinConfigs 값을 buildTypes { } 내의 release { } 내에 이런식으로 작성하여 사용할 수 있도록 한다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;signingConfig signingConfigs.release&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;step 3. Github Actions 의 workflow 작성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 준비는 끝났다. CI/CD를 만들어낼 yml 파일을 작성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;release 브랜치를 하나 만들고, CI 편에서 했던 대로 github Actions 에서 work flow를 하나 더 만들어 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;name: Android-release CI/CD

on:
  push:
    branches: [ release ]
  pull_request:
    branches: [ release ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: set up JDK 11
      uses: actions/setup-java@v2
      with:
        java-version: '11'
        distribution: 'temurin'
        cache: gradle

    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
      
    - name: Create google-services
      run: |
        mkdir ./app/src/release
        echo '${{ secrets.GOOGLE_SERVICES_JSON_RELEASE }}' &amp;gt; ./app/src/release/google-services.json

    - name: Create KeyStore File and Properties
      run: |
        mkdir ./app/signing
        echo '${{ secrets.KU_RING_KEY_STORE_JKS }}' | base64 -d &amp;gt; ./app/signing/ku_ring_keystore.jks
        echo '${{ secrets.KEY_STORE_PROPERTIES }}' &amp;gt; ./app/signing/keystore.properties

    # Build APK Release
    - name: Build release Apk
      run: ./gradlew assembleRelease

    # Build AAB Release
    - name: Build release Bundle
      run: ./gradlew bundleRelease

    # Run unit test
    - name: Run unit test
      run: ./gradlew testreleaseUnitTest
      
    # Upload AAB
    - name: Upload a Build AAB Artifact
      uses: actions/upload-artifact@v3.0.0
      with:
        # Artifact name
        name: app-release.aab
        # A file, directory or wildcard pattern that describes what to upload
        path: ./app/build/outputs/bundle/release/app-release.aab
        retention-days: 14

    # Upload APK
    - name: Upload a Build APK Artifact
      uses: actions/upload-artifact@v3.0.0
      with:
        # Artifact name
        name: app-release.apk
        # A file, directory or wildcard pattern that describes what to upload
        path: ./app/build/outputs/apk/release/app-release.apk
        retention-days: 14&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 yml 파일에서 CI편과 다른 점은 'Create KeyStore File and Properties' 부터인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격 서버 위에 app/signing 폴더를 만들고 (폴더가 없는데 접근하려하면 에러가 난다.)&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;echo '${{ secrets.KU_RING_KEY_STORE_JKS }}' | base64 -d &amp;gt; ./app/signing/ku_ring_keystore.jks&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 통해 keyStore를 복호화하여 jks 파일을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷한 맥락으로 keystore.properties가 필요하므로&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;echo '${{ secrets.KEY_STORE_PROPERTIES }}' &amp;gt; ./app/signing/keystore.properties&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 통해 keystore.properties를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;이제 깃헙 원격 서버에서도 나의 로컬 컴퓨터와 같은 배포 환경을 가진 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 아랫쪽을 보면 Upload a Build AAB Artifact가 있는데, 여기서 aab 파일을 생성한다. artifacts를 만드는 것은 uses: actions/upload-artifact@v3.0.0 을 사용하면 되는데, 이것은 다른 사람이 만든 오픈소스이다. 이것을 이용해서 만드는데, 결과물(aab)의 파일명은 무엇일지, 어떤 위치의 파일을 업로드할 것인지, 보존 기간은 얼마로 할것인지 등을 with 구문 내에서 설정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;추가 - Github Actions 에서 추출한 aab가 잘 동작하는지 테스트하려면?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aab 는 플레이콘솔에 업로드할때는 사용될 수 있지만, 스마트폰에 넣었을 때 apk처럼 앱이 되지는 않는다. 따라서 이를 잘 동작하는지 테스트하기 위하여 apk로 변환했었던 기록을 작성한 것이다.&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;i&gt;준비물 : [aab파일, cmd 창에서 java를 실행시킬 수 있는 환경, keyStore파일]&lt;/i&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;1. artifacts를 다운로드 받으면 app-release.aab.zip 파일이 생길 것이다. 이것을 압축을 풀어 app-release.aab로 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &lt;a href=&quot;https://github.com/google/bundletool/releases&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/google/bundletool/releases&lt;/a&gt; 요기 사이트에 들어가서 jar 파일을 다운받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-2. 편의를 위하여 다운받은 jar 파일과 aab 파일과 keyStore파일을 한 폴더에 집어넣는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. cmd 창에서 아래의 명령어를 친다. 보기 편하기 위해 -- 문자열 마다 개행을 넣었는데, 개행 대신 그냥 띄어쓰기하면 된다. (자세한 사용법은 &lt;a href=&quot;https://developer.android.com/studio/command-line/bundletool&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이 링크&lt;/a&gt;를 참고하자)&lt;/p&gt;
&lt;pre id=&quot;code_1646927600901&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.9.0.jar build-apks 
--bundle=app-release.aab 
--output=app-release.apks 
--ks=ku_ring_keystore.jks 
--ks-pass=pass:XXXX
--ks-key-alias=XXXX 
--key-pass=pass:XXXX 
--mode=universal&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. app-release.apks 라는 파일이 생겼을 것이다. 왜 apks 이지? 라는 의문이 들었지만 나중에 다시 apk로 바뀌니 걱정안해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 이 apks 파일의 확장자를 zip으로 바꾼다. (파일명의 확장자만을 바꾸는 것이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 이것을 압축 해제하면 apk 가 생긴다.&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://stefma.medium.com/how-to-store-a-android-keystore-safely-on-github-actions-f0cef9413784&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stefma.medium.com/how-to-store-a-android-keystore-safely-on-github-actions-f0cef9413784&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dnight.tistory.com/entry/Github-Actions-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A1%9C-Keystore-%EC%A0%80%EC%9E%A5%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dnight.tistory.com/entry/Github-Actions-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A1%9C-Keystore-%EC%A0%80%EC%9E%A5%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646926981719&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;Github Actions - 환경변수로 Keystore 저장하여 사용하기&quot; data-og-description=&quot;터미널에서 Keystore 파일을 base64로 변경하여 파일로 저장합니다. openssl base64 -in [keystore file path] -out [base64 file path] 저장된 Base64의 파일내용을 복사하여 Github의 프로젝트 Settings -&amp;gt; Secr..&quot; data-og-host=&quot;dnight.tistory.com&quot; data-og-source-url=&quot;https://dnight.tistory.com/entry/Github-Actions-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A1%9C-Keystore-%EC%A0%80%EC%9E%A5%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://dnight.tistory.com/entry/Github-Actions-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A1%9C-Keystore-%EC%A0%80%EC%9E%A5%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/g1mAN/hyNFH5vwEd/9ZOg0CLBAYkFAtKT8e9NbK/img.png?width=800&amp;amp;height=664&amp;amp;face=0_0_800_664,https://scrap.kakaocdn.net/dn/bkHkar/hyNFxV8qHJ/qYKns7FrqsAmScqDRVtyyk/img.png?width=800&amp;amp;height=664&amp;amp;face=0_0_800_664,https://scrap.kakaocdn.net/dn/brLqs9/hyNFxBRaWt/RJtMBuec6yFxH9cUE4hFuk/img.png?width=1252&amp;amp;height=1040&amp;amp;face=0_0_1252_1040&quot;&gt;&lt;a href=&quot;https://dnight.tistory.com/entry/Github-Actions-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A1%9C-Keystore-%EC%A0%80%EC%9E%A5%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dnight.tistory.com/entry/Github-Actions-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98%EB%A1%9C-Keystore-%EC%A0%80%EC%9E%A5%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/g1mAN/hyNFH5vwEd/9ZOg0CLBAYkFAtKT8e9NbK/img.png?width=800&amp;amp;height=664&amp;amp;face=0_0_800_664,https://scrap.kakaocdn.net/dn/bkHkar/hyNFxV8qHJ/qYKns7FrqsAmScqDRVtyyk/img.png?width=800&amp;amp;height=664&amp;amp;face=0_0_800_664,https://scrap.kakaocdn.net/dn/brLqs9/hyNFxBRaWt/RJtMBuec6yFxH9cUE4hFuk/img.png?width=1252&amp;amp;height=1040&amp;amp;face=0_0_1252_1040');&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;Github Actions - 환경변수로 Keystore 저장하여 사용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;터미널에서 Keystore 파일을 base64로 변경하여 파일로 저장합니다. openssl base64 -in [keystore file path] -out [base64 file path] 저장된 Base64의 파일내용을 복사하여 Github의 프로젝트 Settings -&amp;gt; Secr..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dnight.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/studio/publish/app-signing?hl=ko#groovy&quot;&gt;https://developer.android.com/studio/publish/app-signing?hl=ko#groovy&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1646926993966&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 스튜디오를 사용하여 Google Play에 출시하기 위해 앱에 서명하는 방법과 Play 앱 서명을 선택하는 방법을 알아보세요.&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/studio/publish/app-signing?hl=ko#groovy&quot; data-og-url=&quot;https://developer.android.com/studio/publish/app-signing?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bB5IDE/hyNFMFLxVq/HoMRiRrksKR5So5V5XfFM1/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/studio/publish/app-signing?hl=ko#groovy&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/studio/publish/app-signing?hl=ko#groovy&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bB5IDE/hyNFMFLxVq/HoMRiRrksKR5So5V5XfFM1/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676');&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;앱 서명 &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 스튜디오를 사용하여 Google Play에 출시하기 위해 앱에 서명하는 방법과 Play 앱 서명을 선택하는 방법을 알아보세요.&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;&lt;a href=&quot;https://ajh322.tistory.com/289&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ajh322.tistory.com/289&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646928330416&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;안드로이드 aab 설치하는 방법&quot; data-og-description=&quot;열심히 일을 하다보면 aab 파일에 문제가 없는지 궁금할때가 있다. 유니티 build and run을 사용하지 않고 aab 파일을 핸드폰에 설치하는 방법에 대해서 알아보자 쉬운 방법은 없다 그냥 아래 써있는&quot; data-og-host=&quot;ajh322.tistory.com&quot; data-og-source-url=&quot;https://ajh322.tistory.com/289&quot; data-og-url=&quot;https://ajh322.tistory.com/289&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bJZfTT/hyNFCJTWRN/8skJRcu5qEHkZ4rAKPybdk/img.png?width=800&amp;amp;height=296&amp;amp;face=0_0_800_296,https://scrap.kakaocdn.net/dn/fwCV6/hyNFz7u1XR/a5N2kR9t7kq3JUAf9EpHi1/img.png?width=800&amp;amp;height=296&amp;amp;face=0_0_800_296&quot;&gt;&lt;a href=&quot;https://ajh322.tistory.com/289&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ajh322.tistory.com/289&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bJZfTT/hyNFCJTWRN/8skJRcu5qEHkZ4rAKPybdk/img.png?width=800&amp;amp;height=296&amp;amp;face=0_0_800_296,https://scrap.kakaocdn.net/dn/fwCV6/hyNFz7u1XR/a5N2kR9t7kq3JUAf9EpHi1/img.png?width=800&amp;amp;height=296&amp;amp;face=0_0_800_296');&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;안드로이드 aab 설치하는 방법&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;열심히 일을 하다보면 aab 파일에 문제가 없는지 궁금할때가 있다. 유니티 build and run을 사용하지 않고 aab 파일을 핸드폰에 설치하는 방법에 대해서 알아보자 쉬운 방법은 없다 그냥 아래 써있는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ajh322.tistory.com&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;</description>
      <category>Programming/Github</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/32</guid>
      <comments>https://yk-coding-letter.tistory.com/32#entry32comment</comments>
      <pubDate>Fri, 11 Mar 2022 00:32:34 +0900</pubDate>
    </item>
    <item>
      <title>Android + CI/CD 적용하기 - CI편 (feat.Github Actions)</title>
      <link>https://yk-coding-letter.tistory.com/31</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;CI/CD는 &lt;b&gt;Continuous Integration&lt;/b&gt; 과 &lt;b&gt;Continuous Deploy(or Delivery)&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;CI란&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CI (Continuous Integration)
&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;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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&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;CD란&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CD (Continuous Deployment)
&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;대표적인 프로세스는 무엇이 있는가? (Android에서)
&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 내가 꾸준하게 관리하고 배포하는 프로젝트에서 CI/CD를 적용한다면, 반복되는 작업을 진행하면서 발생하는 실수와 시간낭비를 줄일 수 있어서 예전부터 꼭 해보려고 생각했던 기능이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Github Actions를 선택한 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CI/CD를 도와주는 Tool은 여러가지가 있다. Jenkins, CircleCI, Github Actions 등이 있는데 무료이면서 가장 쉽고 세팅이 적은 Github Actions를 선택했다. Github 레포에서 바로 접근할 수도 있어서 접근성도 높았다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;시작하기&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Github Actions에 들어가면 Android CI 가 바로 뜨지는 않는다. 'browse all category' 버튼을 눌러 더 찾거나 아니면 아래 사진처럼 검색을 하면 나온다.&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;589&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6oS0g/btrvu3eS0nq/bsNb8fpK3whXwGE7KkW7P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6oS0g/btrvu3eS0nq/bsNb8fpK3whXwGE7KkW7P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6oS0g/btrvu3eS0nq/bsNb8fpK3whXwGE7KkW7P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6oS0g%2Fbtrvu3eS0nq%2FbsNb8fpK3whXwGE7KkW7P0%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;1282&quot; height=&quot;589&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;589&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;그러면 레포의 ./github/workflows 폴더에 android.yml (야믈이라고 읽는다고 한다) 폴더가 생긴다. 여기에 어떤 로직을 짜느냐에 따라 특정 브랜치에 Push or PR을 했을 때 수행하는 Work Flow를 만들 수 있다.&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;깃허브에서 원격 서버(Ubuntu)를 제공해주고, 그 공간에서 내가 올린 소스코드로 빌드 등을 해주는 원리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;(public 레포에서는 무료이고, private 레포는 매월 2천 시간까지만 무료이고 이후부터는 유료라고 한다. 기업에서는 private 레포일테니 기업에서 쓰려면 돈을 내는 구조가 되는것 같다.)&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;보안 파일 관리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원격에서 앱을 빌드하려면 한가지 문제가 있었다. 그것은 바로 내가 &lt;b&gt;gitignore&lt;/b&gt;로 보이지 않도록 명시한 보안파일들이다. 이것들이 없으면 빌드 에러가 발생할 것이다. 이를 위해 Github에서 제공하는 Secret 기능도 사용하였다.&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;1437&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwlNYt/btrvymEQL3h/WNJDKQ3O31RRKkPE5DknUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwlNYt/btrvymEQL3h/WNJDKQ3O31RRKkPE5DknUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwlNYt/btrvymEQL3h/WNJDKQ3O31RRKkPE5DknUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwlNYt%2FbtrvymEQL3h%2FWNJDKQ3O31RRKkPE5DknUK%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;1437&quot; height=&quot;724&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사진과 같이 Github사이트에서 Settings -&amp;gt; Secrets -&amp;gt; Actions 에 들어가면 'New Repository secret' 이 우측상단에 보인다. 이걸 클릭해서 파일명과 파일 내용을 작성했다. 나는 우선 파이어베이스를 위한 google-services.json 파일을 GOOGLE_SERVICES_JSON_DEBUG라는 시크릿키 이름으로 추가하였는데, 이것을 가지고 위에서 작성한 yml파일에서 참조하였다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646760304812&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    - name: Create google-services
      run: echo '${{ secrets.GOOGLE_SERVICES_JSON_DEBUG }}' &amp;gt; ./app/google-services.json&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yml파일에서 이렇게 작성하면 내가 Secrets에서 작성했던 문자열을 읽어와서 ./app폴더에 google-services.json 이라는 파일명으로 만드는 것인것 같다.&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Build Variant/Flavor 신경써야하는 이유&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 한 뒤, yml에서 ./gradlew build 를 실행하도록 하면 빌드가 될것이라 생각했다.&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;1366&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQnNQI/btrvwiCUVP6/kkxzf1TWhAt3ysJJbGL291/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQnNQI/btrvwiCUVP6/kkxzf1TWhAt3ysJJbGL291/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQnNQI/btrvwiCUVP6/kkxzf1TWhAt3ysJJbGL291/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQnNQI%2FbtrvwiCUVP6%2Fkkxzf1TWhAt3ysJJbGL291%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;1366&quot; height=&quot;524&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;google-servies.json을 읽지 못해서 발생한 것이 원인이다. 이유가 뭘지 생각해보니, 나는 이 앱에서 Debug용과 Release용 앱을 따로 관리하면서, 다른 서버를 바라보게 하고 다른 google-services.json파일을 갖게 했었다.&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;따라서 원인은 Debug용 google-services.json을 만들었지만 빌드가 release용으로 되고 있었기 때문이었다. 커멘드 라인에서 Build Variant를 변경할 수 있는 방법을 알아보았고, ./gradlew assembleDebug 명령어로 가능하다는 알게 되었다.&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;같은 이슈로 main브랜치에 Push/PR 이 들어오면 유닛테스트를 거치고 싶었는데, 단순히 ./gradlew test 명령어로는 안되고, ./gradlew testdebugUnitTest 명령어로 가능토록 할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결과 화면:&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;860&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UwFyF/btrvuQmT7Gw/Vjg9t7hWc4WIZ0WyyAxKa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UwFyF/btrvuQmT7Gw/Vjg9t7hWc4WIZ0WyyAxKa0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UwFyF/btrvuQmT7Gw/Vjg9t7hWc4WIZ0WyyAxKa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUwFyF%2FbtrvuQmT7Gw%2FVjg9t7hWc4WIZ0WyyAxKa0%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;808&quot; height=&quot;860&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;860&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;yml 파일 내용:&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1646761267035&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;name: Android CI

# Every new pull request to develop branch must fire CI check
on:
  push:
    branches: [ develop ]
  pull_request:
    branches: [ develop ]
    
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called &quot;build&quot;
  build:

    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
    # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
    - uses: actions/checkout@v2
    
    - name: set up JDK 11
      uses: actions/setup-java@v2
      with:
        java-version: '11'
        distribution: 'temurin'
        cache: gradle

    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
      
    - name: Create google-services
      run: echo '${{ secrets.GOOGLE_SERVICES_JSON_DEBUG }}' &amp;gt; ./app/google-services.json
      
    # Build Debug App
    - name: Build with Gradle
      run: ./gradlew assembleDebug
      
    # Run unit test
    - name: Run unit test
      run: ./gradlew testdebugUnitTest&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;&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;span&gt;&lt;/span&gt;&lt;a href=&quot;https://developer.android.com/studio/test/command-line?hl=ko&quot;&gt;https://developer.android.com/studio/test/command-line?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646762208629&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;명령줄에서 테스트를 실행하는 방법입니다.&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/studio/test/command-line?hl=ko&quot; data-og-url=&quot;https://developer.android.com/studio/test/command-line?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ygQ7B/hyNErhCfIQ/k3X3piLi0WvAoqWixnqU6K/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/studio/test/command-line?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/studio/test/command-line?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ygQ7B/hyNErhCfIQ/k3X3piLi0WvAoqWixnqU6K/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676');&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;명령줄에서 테스트 &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;명령줄에서 테스트를 실행하는 방법입니다.&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;&lt;a href=&quot;https://keelim.tistory.com/entry/Github-Actions%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%ED%94%8C%EB%A0%88%EC%9D%B4%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-with-google-servicesjson&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://keelim.tistory.com/entry/Github-Actions%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%ED%94%8C%EB%A0%88%EC%9D%B4%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-with-google-servicesjson&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646761701495&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;[안드로이드] Github Actions으로 구글 플레이스토어 배포 with google-services.json&quot; data-og-description=&quot;---이전 글--- 2021.02.17 - [안드로이드] - [안드로이드] Github Actions으로 구글 플레이스토어 배포 [안드로이드] Github Actions으로 구글 플레이스토어 배포 안녕하세요!! 오늘은 간단하게 CI/CD를 구성하여&quot; data-og-host=&quot;keelim.tistory.com&quot; data-og-source-url=&quot;https://keelim.tistory.com/entry/Github-Actions%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%ED%94%8C%EB%A0%88%EC%9D%B4%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-with-google-servicesjson&quot; data-og-url=&quot;https://keelim.tistory.com/entry/Github-Actions%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%ED%94%8C%EB%A0%88%EC%9D%B4%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-with-google-servicesjson&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dBzNku/hyNEebx61O/woe2Vl9LwGv1XKioSktt81/img.png?width=800&amp;amp;height=125&amp;amp;face=0_0_800_125,https://scrap.kakaocdn.net/dn/cDuOwx/hyNEl2IK7T/40MNLhsiVKkMHQmwo9g231/img.png?width=800&amp;amp;height=125&amp;amp;face=0_0_800_125,https://scrap.kakaocdn.net/dn/lVOZq/hyNElBEstf/GCHQodWjmT4q540UzVGHf1/img.png?width=832&amp;amp;height=290&amp;amp;face=0_0_832_290&quot;&gt;&lt;a href=&quot;https://keelim.tistory.com/entry/Github-Actions%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%ED%94%8C%EB%A0%88%EC%9D%B4%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-with-google-servicesjson&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keelim.tistory.com/entry/Github-Actions%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EA%B8%80-%ED%94%8C%EB%A0%88%EC%9D%B4%EC%8A%A4%ED%86%A0%EC%96%B4-%EB%B0%B0%ED%8F%AC-with-google-servicesjson&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dBzNku/hyNEebx61O/woe2Vl9LwGv1XKioSktt81/img.png?width=800&amp;amp;height=125&amp;amp;face=0_0_800_125,https://scrap.kakaocdn.net/dn/cDuOwx/hyNEl2IK7T/40MNLhsiVKkMHQmwo9g231/img.png?width=800&amp;amp;height=125&amp;amp;face=0_0_800_125,https://scrap.kakaocdn.net/dn/lVOZq/hyNElBEstf/GCHQodWjmT4q540UzVGHf1/img.png?width=832&amp;amp;height=290&amp;amp;face=0_0_832_290');&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;[안드로이드] Github Actions으로 구글 플레이스토어 배포 with google-services.json&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;---이전 글--- 2021.02.17 - [안드로이드] - [안드로이드] Github Actions으로 구글 플레이스토어 배포 [안드로이드] Github Actions으로 구글 플레이스토어 배포 안녕하세요!! 오늘은 간단하게 CI/CD를 구성하여&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keelim.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://zkdlu.tistory.com/19&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://zkdlu.tistory.com/19&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646761723961&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;넌 못지나간다! Test가 실패했으면 배포도 되면 안되지!&quot; data-og-description=&quot;프로젝트를 진행하고 수많은 테스트 코드가 쌓여 프로덕션 코드가 완성되게 됩니다. 그러나 어디까지 테스트는 개발자가 직접 거쳐줘야 하는 단계이고 자동화 되지 않은 절차는 쉽게 잊혀질 수&quot; data-og-host=&quot;zkdlu.tistory.com&quot; data-og-source-url=&quot;https://zkdlu.tistory.com/19&quot; data-og-url=&quot;https://zkdlu.tistory.com/19&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/djxBvk/hyNEf2xqDG/dYGcthaVCRLq3fjruaE7t0/img.jpg?width=277&amp;amp;height=400&amp;amp;face=0_0_277_400,https://scrap.kakaocdn.net/dn/bjKkSZ/hyNEdRftKX/WXF2m0kFFucdFBpZGIirkk/img.jpg?width=277&amp;amp;height=400&amp;amp;face=0_0_277_400,https://scrap.kakaocdn.net/dn/bAEZlH/hyNEpYnZOj/skzGcbLX9EJQ9kl0LxPHuk/img.png?width=2068&amp;amp;height=1486&amp;amp;face=0_0_2068_1486&quot;&gt;&lt;a href=&quot;https://zkdlu.tistory.com/19&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://zkdlu.tistory.com/19&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/djxBvk/hyNEf2xqDG/dYGcthaVCRLq3fjruaE7t0/img.jpg?width=277&amp;amp;height=400&amp;amp;face=0_0_277_400,https://scrap.kakaocdn.net/dn/bjKkSZ/hyNEdRftKX/WXF2m0kFFucdFBpZGIirkk/img.jpg?width=277&amp;amp;height=400&amp;amp;face=0_0_277_400,https://scrap.kakaocdn.net/dn/bAEZlH/hyNEpYnZOj/skzGcbLX9EJQ9kl0LxPHuk/img.png?width=2068&amp;amp;height=1486&amp;amp;face=0_0_2068_1486');&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;넌 못지나간다! Test가 실패했으면 배포도 되면 안되지!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;프로젝트를 진행하고 수많은 테스트 코드가 쌓여 프로덕션 코드가 완성되게 됩니다. 그러나 어디까지 테스트는 개발자가 직접 거쳐줘야 하는 단계이고 자동화 되지 않은 절차는 쉽게 잊혀질 수&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;zkdlu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://thiagolopessilva.medium.com/running-android-unit-testing-on-github-action-with-a-project-that-contains-firebase-dependency-621603363bc1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://thiagolopessilva.medium.com/running-android-unit-testing-on-github-action-with-a-project-that-contains-firebase-dependency-621603363bc1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646761808820&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;Running Android unit testing on GitHub action with a project that contains firebase dependency&quot; data-og-description=&quot;Hello folks!! These days I&amp;rsquo;ve faced the problem to run my unit testing on the Github Actions once while the build process was started it&amp;hellip;&quot; data-og-host=&quot;thiagolopessilva.medium.com&quot; data-og-source-url=&quot;https://thiagolopessilva.medium.com/running-android-unit-testing-on-github-action-with-a-project-that-contains-firebase-dependency-621603363bc1&quot; data-og-url=&quot;https://thiagolopessilva.medium.com/running-android-unit-testing-on-github-action-with-a-project-that-contains-firebase-dependency-621603363bc1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nRqLa/hyNEs8LMAr/UxSexdTprnsYzNmE99zs2k/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900,https://scrap.kakaocdn.net/dn/ciccra/hyNEjjyce2/Gi4gRoBUjtifxFIHS00Clk/img.jpg?width=1265&amp;amp;height=949&amp;amp;face=0_0_1265_949,https://scrap.kakaocdn.net/dn/cZlkgf/hyNEe3DfM1/i1qqm9G9gTPMASp6tE5uWK/img.png?width=1072&amp;amp;height=551&amp;amp;face=0_0_1072_551&quot;&gt;&lt;a href=&quot;https://thiagolopessilva.medium.com/running-android-unit-testing-on-github-action-with-a-project-that-contains-firebase-dependency-621603363bc1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://thiagolopessilva.medium.com/running-android-unit-testing-on-github-action-with-a-project-that-contains-firebase-dependency-621603363bc1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nRqLa/hyNEs8LMAr/UxSexdTprnsYzNmE99zs2k/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900,https://scrap.kakaocdn.net/dn/ciccra/hyNEjjyce2/Gi4gRoBUjtifxFIHS00Clk/img.jpg?width=1265&amp;amp;height=949&amp;amp;face=0_0_1265_949,https://scrap.kakaocdn.net/dn/cZlkgf/hyNEe3DfM1/i1qqm9G9gTPMASp6tE5uWK/img.png?width=1072&amp;amp;height=551&amp;amp;face=0_0_1072_551');&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;Running Android unit testing on GitHub action with a project that contains firebase dependency&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Hello folks!! These days I&amp;rsquo;ve faced the problem to run my unit testing on the Github Actions once while the build process was started it&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;thiagolopessilva.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/thiagoolsilva/ShoppingList&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/thiagoolsilva/ShoppingList&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646761828228&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - thiagoolsilva/ShoppingList: An android application that uses jetpack components and Firebase&quot; data-og-description=&quot;An android application that uses jetpack components and Firebase - GitHub - thiagoolsilva/ShoppingList: An android application that uses jetpack components and Firebase&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/thiagoolsilva/ShoppingList&quot; data-og-url=&quot;https://github.com/thiagoolsilva/ShoppingList&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yfpTh/hyNEgtB1qz/1YkiPMqSxKcJ6GOzq8aEPk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/thiagoolsilva/ShoppingList&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/thiagoolsilva/ShoppingList&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yfpTh/hyNEgtB1qz/1YkiPMqSxKcJ6GOzq8aEPk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - thiagoolsilva/ShoppingList: An android application that uses jetpack components and Firebase&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;An android application that uses jetpack components and Firebase - GitHub - thiagoolsilva/ShoppingList: An android application that uses jetpack components and Firebase&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://keelim.tistory.com/entry/7777?category=1080005&quot;&gt;https://keelim.tistory.com/entry/7777?category=1080005&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1646761914579&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;[안드로이드] Github Actions으로 구글 플레이스토어 배포&quot; data-og-description=&quot;---다음 글--- 2021.08.26 - [안드로이드] - [안드로이드] Github Actions으로 구글 플레이스토어 배포 with google-services.json [안드로이드] Github Actions으로 구글 플레이스토어 배포 with google-services..&quot; data-og-host=&quot;keelim.tistory.com&quot; data-og-source-url=&quot;https://keelim.tistory.com/entry/7777?category=1080005&quot; data-og-url=&quot;https://keelim.tistory.com/entry/7777&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bI2cPW/hyNEhzg9BN/aCFZkcNjGoMfkJAURYsj30/img.png?width=800&amp;amp;height=1268&amp;amp;face=0_0_800_1268,https://scrap.kakaocdn.net/dn/dnBxLl/hyNEeWSZoj/amkBhKLzWYHKZ0SIAb5QZ0/img.png?width=800&amp;amp;height=1268&amp;amp;face=0_0_800_1268&quot;&gt;&lt;a href=&quot;https://keelim.tistory.com/entry/7777?category=1080005&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://keelim.tistory.com/entry/7777?category=1080005&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bI2cPW/hyNEhzg9BN/aCFZkcNjGoMfkJAURYsj30/img.png?width=800&amp;amp;height=1268&amp;amp;face=0_0_800_1268,https://scrap.kakaocdn.net/dn/dnBxLl/hyNEeWSZoj/amkBhKLzWYHKZ0SIAb5QZ0/img.png?width=800&amp;amp;height=1268&amp;amp;face=0_0_800_1268');&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;[안드로이드] Github Actions으로 구글 플레이스토어 배포&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;---다음 글--- 2021.08.26 - [안드로이드] - [안드로이드] Github Actions으로 구글 플레이스토어 배포 with google-services.json [안드로이드] Github Actions으로 구글 플레이스토어 배포 with google-services..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;keelim.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dealicious-inc.github.io/2021/03/30/android-ci-with-actions.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://dealicious-inc.github.io/2021/03/30/android-ci-with-actions.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646762462624&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;GitHub Actions로 안드로이드 CI환경 구축하기 (Goodbye Jenkins)&quot; data-og-description=&quot;Github Actions로 쾌적한 자동화 빌드 구성하기&quot; data-og-host=&quot;dealicious-inc.github.io&quot; data-og-source-url=&quot;https://dealicious-inc.github.io/2021/03/30/android-ci-with-actions.html&quot; data-og-url=&quot;https://dealicious-inc.github.io/2021/03/30/android-ci-with-actions.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxL5Cn/hyNEi51R0p/pOihfvNCk17mq35gufjlf0/img.png?width=1273&amp;amp;height=641&amp;amp;face=0_0_1273_641&quot;&gt;&lt;a href=&quot;https://dealicious-inc.github.io/2021/03/30/android-ci-with-actions.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dealicious-inc.github.io/2021/03/30/android-ci-with-actions.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxL5Cn/hyNEi51R0p/pOihfvNCk17mq35gufjlf0/img.png?width=1273&amp;amp;height=641&amp;amp;face=0_0_1273_641');&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;GitHub Actions로 안드로이드 CI환경 구축하기 (Goodbye Jenkins)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Github Actions로 쾌적한 자동화 빌드 구성하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dealicious-inc.github.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;</description>
      <category>Programming/Github</category>
      <category>CI</category>
      <category>CI/CD</category>
      <category>debug build</category>
      <category>GitHub</category>
      <category>GitHub Actions</category>
      <category>unit test</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/31</guid>
      <comments>https://yk-coding-letter.tistory.com/31#entry31comment</comments>
      <pubDate>Wed, 9 Mar 2022 02:33:24 +0900</pubDate>
    </item>
    <item>
      <title>[번역] RecyclerView를 Wrapping했을 때 퍼포먼스 이슈 - feat.NestedScrollView</title>
      <link>https://yk-coding-letter.tistory.com/30</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Bartek Lipinski의 Medium 게시물&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://medium.com/android-news/android-dev-tip-4-91b7757b1f0a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://medium.com/android-news/android-dev-tip-4-91b7757b1f0a&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646389596513&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;Android Dev Tip #4&quot; data-og-description=&quot;The performance of wrapping content in RecyclerView&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/android-news/android-dev-tip-4-91b7757b1f0a&quot; data-og-url=&quot;https://medium.com/android-news/android-dev-tip-4-91b7757b1f0a&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dLWbUo/hyNBSsntFg/Zc11mQTdiinKb2fbNkMqV0/img.png?width=1200&amp;amp;height=555&amp;amp;face=0_0_1200_555,https://scrap.kakaocdn.net/dn/u3wFU/hyNB4fhz2s/BCcma6r9r4BRFikAZO1yd1/img.png?width=1400&amp;amp;height=819&amp;amp;face=0_0_1400_819,https://scrap.kakaocdn.net/dn/4aMtl/hyNBU4MUVr/BqK3g2kB59cqxqMgvAli1K/img.png?width=1400&amp;amp;height=665&amp;amp;face=0_0_1400_665&quot;&gt;&lt;a href=&quot;https://medium.com/android-news/android-dev-tip-4-91b7757b1f0a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/android-news/android-dev-tip-4-91b7757b1f0a&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dLWbUo/hyNBSsntFg/Zc11mQTdiinKb2fbNkMqV0/img.png?width=1200&amp;amp;height=555&amp;amp;face=0_0_1200_555,https://scrap.kakaocdn.net/dn/u3wFU/hyNB4fhz2s/BCcma6r9r4BRFikAZO1yd1/img.png?width=1400&amp;amp;height=819&amp;amp;face=0_0_1400_819,https://scrap.kakaocdn.net/dn/4aMtl/hyNBU4MUVr/BqK3g2kB59cqxqMgvAli1K/img.png?width=1400&amp;amp;height=665&amp;amp;face=0_0_1400_665');&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 Dev Tip #4&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The performance of wrapping content in RecyclerView&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;[Tip]&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #757575;&quot;&gt;When RecyclerView is wrapping its content, it&amp;rsquo;s not recycling anymore. Every record in the dataset has an item View kept in memory for as long as the RecyclerView is in the layout hierarchy.&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #757575;&quot;&gt;[Tip 번역]&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #757575;&quot;&gt;RecylerView의 내용물을 감쌌을때, RecyclerView는 내용물을 더이상 재활용하지 않습니다. Item View의 모든 데이터들은 RecyclerView가 layout 내부에 있는 한 메모리에 남아있을 것입니다.&lt;/span&gt;&lt;/h4&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;i&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;- 해석 주의 : RecyclerView에서 &lt;span style=&quot;color: #1b711d;&quot;&gt;wrap_content&lt;/span&gt; 를 하는것이 문제가 된다는 뜻이 아닙니다. 해당 게시물의 댓글에서도 다시 언급되는 내용으로, 여기서의 wrapping content는 RecyclerView가 Scroll 내부에 있다는것으로 해석해야합니다.&lt;/span&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;i&gt;&amp;nbsp;따라서 RecyclerView가 `wrapping content`, `in layout hierarchy` 라는 것은 상위에 스크롤 내부에 있다는 것으로 해석하였습니다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;설명:&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #757575;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;RecyclerView의 기본 개념은 간단합니다&lt;/span&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #757575;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;1. 당신은 View들로 이루어진 스크롤 가능한 영역(RecyclerView)을 얻습니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpTVFz/btrvatLcgGh/37SxDah3qIrbKe0MwHrBBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpTVFz/btrvatLcgGh/37SxDah3qIrbKe0MwHrBBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpTVFz/btrvatLcgGh/37SxDah3qIrbKe0MwHrBBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpTVFz%2FbtrvatLcgGh%2F37SxDah3qIrbKe0MwHrBBk%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;700&quot; height=&quot;332&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #757575;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&amp;nbsp;2. Item이 스크린 상의 보이지 않는 영역으로 넘어갔을때, RecyclerView는 그것을 구성요소에서 제외시킵니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QePln/btru9qHXFv5/SDQbwu7pCRJfy7jV97n1ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QePln/btru9qHXFv5/SDQbwu7pCRJfy7jV97n1ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QePln/btru9qHXFv5/SDQbwu7pCRJfy7jV97n1ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQePln%2Fbtru9qHXFv5%2FSDQbwu7pCRJfy7jV97n1ck%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;700&quot; height=&quot;332&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;332&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 그렇게 제외된 Item은 스크롤로 인해 새롭게 보여지는 다른 Item 의 View에서 사용될 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;332&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qGScH/btru3MrI2tV/5CiJ591WH3oLeQy13cjXDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qGScH/btru3MrI2tV/5CiJ591WH3oLeQy13cjXDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qGScH/btru3MrI2tV/5CiJ591WH3oLeQy13cjXDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqGScH%2Fbtru3MrI2tV%2F5CiJ591WH3oLeQy13cjXDK%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;700&quot; height=&quot;332&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;332&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;size18&quot;&gt;이 모든 것은 RecyclerView를 Scroll 내부에 넣었을 때 곤란해집니다. 그렇게 될 경우 RecyclerView는 item의 개수만큼 자식(ItemView) 개수를 갖게 됩니다. 그 결과, RecyclerView는 더이상 자체적으로 스크롤 가능한 존재가 아니게 되고 모든 Item들은 RecyclerView에서 벗어날 수 없게됩니다. RecyclerView는 모든 Item영역들을 갖고 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이러한 모든 것들은 하나의 간단한 사실로 도출합니다 :&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;그 어떤 Item도 스크롤에서 벗어날 수 없다면, 모든 Item들은 재활용될 수 없다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;RecyclerView가 Scroll 계층 내부에 있다면, 모든 item들의 View들은 메모리에 남아있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그것이 당신에게 여전히 문제로 여겨지지 않는다면, 이런 가정을 해보겠습니다 :&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;만약 당신의 RecyclerView에서 DB Table의 모든 row를 보여주어야하는데, Table에 726개의 row가 있다면 당신은 726개의 아이템 View들을 메모리에 전부 올리게 되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;근본적으로, 당신은 Adapter를 사용한 수많은 View를 생성하게 됩니다. 하지만 이때의 성능은 ScrollView 내에 일일이 모든 View들을 추가하는 것과 동일합니다. RecyclerView를 쓰면서 생겼던 성능 이득이 더이상 존재하지 않는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;예시:&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아래의 Layout을 생각해보시죠&lt;/p&gt;
&lt;pre id=&quot;code_1646393329246&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;android.support.v4.widget.NestedScrollView
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;&amp;gt;

    &amp;lt;LinearLayout
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:orientation=&quot;vertical&quot;&amp;gt;

        &amp;lt;ImageView
           android:layout_width=&quot;match_parent&quot;
           android:layout_height=&quot;100dp&quot;
           android:scaleType=&quot;centerCrop&quot;
           android:src=&quot;@drawable/test_image&quot;/&amp;gt;
        
        &amp;lt;TextView
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:background=&quot;#3F51B5&quot;
            android:text=&quot;Example&quot;/&amp;gt;

        &amp;lt;android.support.v7.widget.RecyclerView
            android:id=&quot;@+id/recycler&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;wrap_content&quot;
            android:orientation=&quot;vertical&quot;/&amp;gt;
    &amp;lt;/LinearLayout&amp;gt;
&amp;lt;/android.support.v4.widget.NestedScrollView&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그리고 RecyclerView에서 Item은 아래를 inflate합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1646393365724&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;TextView
    android:id=&quot;@+id/text&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;/&amp;gt;&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;size18&quot;&gt;만약 RecyclerView의 Adapter가 30개의 데이터를 들고 있다면, 해당 화면은 아래와 같을 겁니다:&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;700&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnawsA/btrvaIuSBuy/sMwKiEsKPNSOstkp7DtC3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnawsA/btrvaIuSBuy/sMwKiEsKPNSOstkp7DtC3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnawsA/btrvaIuSBuy/sMwKiEsKPNSOstkp7DtC3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnawsA%2FbtrvaIuSBuy%2FsMwKiEsKPNSOstkp7DtC3K%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;700&quot; height=&quot;409&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;409&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;size18&quot;&gt;처음에는 문제가 없어 보일 것입니다. 하지만 Layout 계층을 보면(Layout Inspector tool을 사용하여) 이러한 것을 볼 수 있습니다.&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;592&quot; data-origin-height=&quot;1110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lUQ68/btru6LM9U0M/e8swuqfTozIhWKMBfGyU5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lUQ68/btru6LM9U0M/e8swuqfTozIhWKMBfGyU5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lUQ68/btru6LM9U0M/e8swuqfTozIhWKMBfGyU5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlUQ68%2Fbtru6LM9U0M%2Fe8swuqfTozIhWKMBfGyU5k%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;592&quot; height=&quot;1110&quot; data-origin-width=&quot;592&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;size18&quot;&gt;RecyclerView의 모든 Item View들은 항상 메모리에 올라가 있습니다. 현재 보이지 않는 Item들까지 포함합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;똑같은 상황에서 NestedScrollView를 사용하지 않았을때의 상황을 비교해 보시죠.(같은 데이터셋)&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;592&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAK1Jp/btrvbgZqV2r/AiQCmEIPO4JmzpQ8HP7ih0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAK1Jp/btrvbgZqV2r/AiQCmEIPO4JmzpQ8HP7ih0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAK1Jp/btrvbgZqV2r/AiQCmEIPO4JmzpQ8HP7ih0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAK1Jp%2FbtrvbgZqV2r%2FAiQCmEIPO4JmzpQ8HP7ih0%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;592&quot; height=&quot;470&quot; data-origin-width=&quot;592&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;어느 시점에서든, 오직 RecyclerView의 스크롤 범위 내의 View 만이 메모리에 올라가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&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;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;번외 - 블로그 내의 댓글)&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Question -&amp;gt; 그렇다면 RecyclerView 내에 ImageView와 TextView를 하나의 셀로 만드는 대신, 다른 대안은 없는 건가요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Anwer -&amp;gt; Adapter 내에 Header를 넣는것이 당신이 할 수 있는 최선일 것입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;nbsp; 참고 - &lt;/b&gt;&lt;b&gt;&lt;a title=&quot;RecyclerViewHeader&quot; href=&quot;https://github.com/blipinsk/RecyclerViewHeader&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;RecyclerViewHeader&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;b&gt;Adapter 를 여러 ViewType으로 생성할 수 있도록 배우는 것을 강력하게 추천합니다. 오랫동안 당신에게 도움이 될것입니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;번역 후 나의 생각&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 블로그 내에서 wrapping content라는 워딩이 계속 걸려서 테스트용 앱에서 wrap_content로 테스트를 해보았는데 Android Studio 내의 Layout Inspector 내에서 Recycling이 잘 되는 것을 확인하였다. 정말로 wrap_content를 말씀하신 게 아니라, RecyclerView 상위 Layout에 스크롤뷰가 있는 경우를 의미하신게 맞는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 대안책으로 제시해 주신 여러 ViewHolder를 바인딩할 수 있게 구성하는 것이 어떻게 보면 상당히 귀찮은 작업이 될 수도 있다. 하지만 Item을 나눌때 sealed class를 잘 사용하면 코드가 조금이나마 깔끔해질 수 있었다.&lt;/p&gt;</description>
      <category>Programming/Android</category>
      <category>android</category>
      <category>nestedScrollView</category>
      <category>performance</category>
      <category>RecyclerView</category>
      <category>scroll</category>
      <author>YK Choi</author>
      <guid isPermaLink="true">https://yk-coding-letter.tistory.com/30</guid>
      <comments>https://yk-coding-letter.tistory.com/30#entry30comment</comments>
      <pubDate>Fri, 4 Mar 2022 19:36:27 +0900</pubDate>
    </item>
  </channel>
</rss>