<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>이상한 나라의 개발자</title>
    <link>https://rien-atelier.tistory.com/</link>
    <description>안드로이드 / 코틀린 독학으로 취업하자!</description>
    <language>ko</language>
    <pubDate>Tue, 14 Apr 2026 17:20:27 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>RIEN </managingEditor>
    <image>
      <title>이상한 나라의 개발자</title>
      <url>https://tistory1.daumcdn.net/tistory/5316766/attach/f11cb352f343462dbf95412f2cca1828</url>
      <link>https://rien-atelier.tistory.com</link>
    </image>
    <item>
      <title>[Test] Hamcrest를 위한 Junit의 assertThat이 Deprecated된 건에 대해</title>
      <link>https://rien-atelier.tistory.com/206</link>
      <description>&lt;figure id=&quot;og_1731993490015&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;Replace JUnit assertThat with Hamcrest | jSparrow Documentation&quot; data-og-description=&quot;Replace JUnit assertThat with Hamcrest Description The JUnit Assert.assertThat (opens new window) method is deprecated. Its sole purpose is to forward the call to the MatcherAssert.assertThat (opens new window) method defined in Hamcrest 1.3. Therefore, it&quot; data-og-host=&quot;jsparrow.github.io&quot; data-og-source-url=&quot;https://jsparrow.github.io/rules/replace-j-unit-assert-that-with-hamcrest.html#description&quot; data-og-url=&quot;https://jsparrow.github.io/rules/replace-j-unit-assert-that-with-hamcrest.html#description&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://jsparrow.github.io/rules/replace-j-unit-assert-that-with-hamcrest.html#description&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jsparrow.github.io/rules/replace-j-unit-assert-that-with-hamcrest.html#description&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Replace JUnit assertThat with Hamcrest | jSparrow Documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Replace JUnit assertThat with Hamcrest Description The JUnit Assert.assertThat (opens new window) method is deprecated. Its sole purpose is to forward the call to the MatcherAssert.assertThat (opens new window) method defined in Hamcrest 1.3. Therefore, it&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jsparrow.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Hamcrest를 다시 사용해보려고 하던 중&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;assertThat이 Deprecated 되었다는 것을 알게 되었습니다.  &lt;/p&gt;
&lt;pre id=&quot;code_1731993613400&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이전 코드
import org.junit.Assert.*

// 사용 코드
assertThat(result, `is`(true))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;그래서 공식 문서 등을 찾아보니 Deprecated된 JUnit의 assertThat을 대신하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Hamcrest 1.3에서 제공해준다고 하네요~.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731994486965&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 새로운 코드
import org.hamcrest.MatcherAssert.assertThat

// 사용 코드
assertThat(result, `is`(true))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Android/Android</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/206</guid>
      <comments>https://rien-atelier.tistory.com/206#entry206comment</comments>
      <pubDate>Tue, 19 Nov 2024 14:34:58 +0900</pubDate>
    </item>
    <item>
      <title>data object!</title>
      <link>https://rien-atelier.tistory.com/205</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;얼마전 부터 sealed 내부에서 object를 사용하게 된다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;data object 사용을 권장하고 있는 점을 알게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;data object가 무엇인지 조사하고 학습한 내용을 정리한 글입니다.  &lt;/p&gt;
&lt;figure id=&quot;og_1731148874516&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;Object declarations and expressions | Kotlin&quot; data-og-description=&quot; &quot; data-og-host=&quot;kotlinlang.org&quot; data-og-source-url=&quot;https://kotlinlang.org/docs/object-declarations.html#data-objects&quot; data-og-url=&quot;https://kotlinlang.org/docs/object-declarations.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnN4HH/hyXwvm0Qoh/IeRQRU6r8Mg5TEEKlFfNsk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400&quot;&gt;&lt;a href=&quot;https://kotlinlang.org/docs/object-declarations.html#data-objects&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kotlinlang.org/docs/object-declarations.html#data-objects&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnN4HH/hyXwvm0Qoh/IeRQRU6r8Mg5TEEKlFfNsk/img.png?width=800&amp;amp;height=400&amp;amp;face=0_0_800_400');&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;Object declarations and expressions | Kotlin&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kotlinlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. data object에서 제공해주는 기능!&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- toString() : data object의 이름을 반환합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- equals() / hashCode() 를 이용해 동등성 검사를 할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;단! custom equals() 또는 hashCode() 메서드를 구현할 수는 없습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;또한 equals() 메서드는 동일한 data object에서는 동일한 값을 반환하는 것이 보장됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1731150076095&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun main() {
	println(Test) // com.seomseom.test.MainActivity$Test@366f00
	println(Test1) // Test1
    println(Test2) // Test2
    println(Test1 == Test1) // true
}

object Test
data object Test1
data object Test2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. data class와의 차이점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- data object는 singleton이기 때문에 copy 메서드를 지원해주지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- componentN() 메서드를 지원해주지 않기 때문에 구조분해 선언을 할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731149329035&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sealed interface ReadResult

data class Number(val number: Int): ReadResult
data class Text(val text: String): ReadResult
data object EndOfFile: ReadResult

fun main() {
	println(Number(7)) // Number(number = 7)
    println(EndOfFile) // EndOfFile
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 결론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인적으로 크게 object와 data object의 큰 차이점은 없는것 같지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;디버깅이나 로그를 찍을 때 좀 더 간편하게 상태를 확인할 수 있는 장점이 있다고 생각합니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;</description>
      <category>Kotlin</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/205</guid>
      <comments>https://rien-atelier.tistory.com/205#entry205comment</comments>
      <pubDate>Sat, 9 Nov 2024 20:07:46 +0900</pubDate>
    </item>
    <item>
      <title>[Compose Internal] changed 예시 코드 분석</title>
      <link>https://rien-atelier.tistory.com/203</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;Compose Internal을 읽어보면서 궁금했던 점에 자료를 찾아보면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이해한 내용을 정리한 글입니다. ☺️&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;혹시 잘못된 점이 있다면 댓글 부탁드립니다!!  &amp;zwj;♀️&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730982194120&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun Header (
	text: String,
    $composer: Composer&amp;lt;*&amp;gt;,
    $changed: Int
) {
	var $dirty = $changed
    if ($changed and 0b0110 === 0) {
    	$dirty = $dirty or if ($composer.changed(text)) 0b0010 else 0b0100
    }
    
    if ($dirty and 0b1011 xor 0b1010 !== 0 || !$composer.skipping) {
    	f(text)
    } else {
    	$composer.skipToGroupEnd()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. $changed and 0b0110 === 0&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;parameter로 전달된 text의 변경사항이 아직 확인되지 않았으므로 확인이 필요하다는 의미&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;만약 text의 변경사항이 확인되지 않았다면, $dirty에 text의 변경사항을 적용해주는 다음 코드를 실행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이 때, text의 변경사항은 $composer.changed(text)에서 확인!&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- text에 변경사항이 있다면 2번째 bit를 1로 toggle -&amp;gt; 0b0010&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- text에 변경사항이 없다면 3번째 bit를 1로 toggle -&amp;gt; 0b0100&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;위 작업을 통해 $dirth에 변경사항 여부 Flag가 적용되어 집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. $dirty and 0b1011&lt;/h3&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;위에서 3번째 bit는 text에 변경사항이 없는 경우에 1로 toggle하는 점을 확인하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;0b1011은 $dirty에서 3번째 bit가 무엇이든 상관 없이 0으로 clear 시키므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;즉 위 연산은 text가 변경되었을 경우에 해당하는 ( 또는 recomposition이 필요한 경우에 해당하는 ) bit를 제외하고 clear하는 연산이라고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. xor 0b1010 !== 0&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;xor는 동일한 bit인 경우에는 0을 반환하는 비트 연산자 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;즉, 3번째 bit는 (2)에서 0으로 clear 되었기 때문에 text의 값이 변경되었거나(2번째 bit가 1) compose 내부적으로 recomposition이 변경된 상태일 때(1번째와 4번째 bit가 1)인 경우 0과 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 결론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Compose는 changed parameter를 통해 smart recomposition을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;parameter의 변경 사항을 확인이 필요한 경우(1) parameter의 변경 여부에 따라 알맞은 bit를&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;toggling 해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이후 $dirty 값을 이용하여 값이 변경되었거나, skippable 하지 않은 Composable에 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;f 메서드를 호출하여 recomposition을 실행하고 아닐 경우 skip 하는 방향으로 진행하게 됩니다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Android/Android</category>
      <category>오블완</category>
      <category>티스토리챌린지</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/203</guid>
      <comments>https://rien-atelier.tistory.com/203#entry203comment</comments>
      <pubDate>Thu, 7 Nov 2024 23:21:44 +0900</pubDate>
    </item>
    <item>
      <title>프로젝트 중 발생한 이슈 및 해결방법 리스트 업</title>
      <link>https://rien-atelier.tistory.com/202</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Hilt - 2024.03.21&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Unsupported metadata version. Check that your Kotlin version is &amp;gt;= 1.0: java.lang.IllegalStateException&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  해결방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;- Hilt 버전과 Kotlin 버전이 맞지 않아 발생하는 이슈입니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kotlin: 1.9.0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Hilt: 2.48&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Android/Error</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/202</guid>
      <comments>https://rien-atelier.tistory.com/202#entry202comment</comments>
      <pubDate>Thu, 21 Mar 2024 11:35:33 +0900</pubDate>
    </item>
    <item>
      <title>[하루네컷] ContentResolver에서 최신순 정렬을 위해 삽질한 결과</title>
      <link>https://rien-atelier.tistory.com/199</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;하루네컷 프로젝트를 진행하면서 기기의 이미지들을 최신순으로 받아와&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;자체 갤러리 화면을 제작하는 Featrue를 구현하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;간단한 사용 방법은 구글링을 통해 알 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Database와 마찬가지로 Query문을 작성하고, 그 결과로 받은 Cursor를 이용해 데이터를 읽어오는 방식입니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;처음에 짠 Query는  아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-27 오후 3.29.29.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7aRnh/btscRqf32iV/EkBXWRjez5PtIKdmPJeB6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7aRnh/btscRqf32iV/EkBXWRjez5PtIKdmPJeB6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7aRnh/btscRqf32iV/EkBXWRjez5PtIKdmPJeB6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7aRnh%2FbtscRqf32iV%2FEkBXWRjez5PtIKdmPJeB6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;637&quot; height=&quot;466&quot; data-filename=&quot;스크린샷 2023-04-27 오후 3.29.29.png&quot; data-origin-width=&quot;1170&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;DATE_TAKEN를 기준으로 내림차순 정렬하고&lt;/li&gt;
&lt;li&gt;Paging 기능을 위한 LIMIT과 OFFSET을 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;하였지만...&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends2&quot; data-emoticon-name=&quot;083&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/083.png&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends2/large/083.png&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;결과는 정렬이 적용되지 않았습니다.&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;디버깅으로 결과를 찍어보니, LIMIT과 OFFSET은 적용이 되었지만, sorting만 적용되지 않은 것을 확인하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;여기서 먼저 문제 상황을 확실히 정의할 필요가 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  정렬만 적용이 되지 않는다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이제 문제 상황과 관련해서 해결방법을 구글을 통해 찾아볼 차례입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;ContentResolver와 Query, Sort를 키워드로 검색해보니, 비슷한 이슈와 관련된 정보가 나오지 않아, 생각보다 시간이 오래 걸리게 되었어요. &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;하지만, 이슈를 확인하고도 그냥 둘 수 없으니,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;좀 더 검색 키워드를 넓혀보기로 하였습니다. Sort 자체가 아니라 Bundle로!&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;그리고 검색 결과로 나온 예시 코드들을 하나씩 제 코드와 비교해가며 어떤 부분이 다른지 세세히 확인해보던 중&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&quot;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;sort의 조건을 지정할 때 putString이 아닌, putStringArray를 사용해야 한다.&lt;/b&gt;&lt;/span&gt;&quot;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;는 점을 알게 되었습니다.ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  수정된 코드&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-27 오후 3.36.54.png&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;858&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UWPsj/btscXXqroOX/KHasxqinshDoL6DHRxzTNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UWPsj/btscXXqroOX/KHasxqinshDoL6DHRxzTNK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UWPsj/btscXXqroOX/KHasxqinshDoL6DHRxzTNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUWPsj%2FbtscXXqroOX%2FKHasxqinshDoL6DHRxzTNK%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;654&quot; height=&quot;461&quot; data-filename=&quot;스크린샷 2023-04-27 오후 3.36.54.png&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;858&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;요렇게 수정해주니 기획에 맞게 최신순으로 이미지가 정렬되어 나오는 것을 확인하였습니다.  &lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;006&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/006.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/006.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;</description>
      <category>기록/TIL</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/199</guid>
      <comments>https://rien-atelier.tistory.com/199#entry199comment</comments>
      <pubDate>Thu, 27 Apr 2023 15:39:10 +0900</pubDate>
    </item>
    <item>
      <title>[Android] 실행시점에 Permission 요청하기</title>
      <link>https://rien-atelier.tistory.com/198</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;토이 프로젝트로 진행하고 있는 하루네컷에서 사용자에게 권한을 요청해야 하는 UseCase가 있어,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Android Permission Request 부분에 대해 복습도 해볼겸 이렇게 포스트를 정리해보게 되었습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;혼자 독학하고 있는 안드로이드 초보자입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;size14&quot;&gt;  참고&lt;/p&gt;
&lt;figure id=&quot;og_1682227567565&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;런타임 권한 요청 &amp;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;런타임 권한 요청 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 모든 Android 앱은 액세스가 제한된 샌드박스에서 실행됩니다. 앱이 자체 샌드박스 밖에 있&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/training/permissions/requesting&quot; data-og-url=&quot;https://developer.android.com/training/permissions/requesting?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cLTI5K/hySmWRDDQG/1Th5gknmdV8p5khVEUvJQ1/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/training/permissions/requesting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/training/permissions/requesting&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cLTI5K/hySmWRDDQG/1Th5gknmdV8p5khVEUvJQ1/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 앱은 액세스가 제한된 샌드박스에서 실행됩니다. 앱이 자체 샌드박스 밖에 있&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;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Permission 요청 flow&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;앱을 만들다보면 가끔(?) Device 내 제한된 데이터에 접근하거나 제한된 동작을 수행하애 하는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이럴 때는 적절한 권한(Permission)을 요청해야 하며, Permission에 종류에 따라 정해진 flow에 따라 개발을 진행해야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwAPnL/btsbXIm4PzD/fkJrURpnkvEh4c5jvaVvsk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwAPnL/btsbXIm4PzD/fkJrURpnkvEh4c5jvaVvsk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwAPnL/btsbXIm4PzD/fkJrURpnkvEh4c5jvaVvsk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwAPnL%2FbtsbXIm4PzD%2FfkJrURpnkvEh4c5jvaVvsk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;554&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;예를 들어, install-time permission들은 위 이미지에 보이는 것과 같이 추가적인 요청 작업(4) 없이 권한을 얻을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;그럼 여기서 말하는 Permission에 종류에는 어떤것이 있을까요?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Permission의 종류&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Permission은 크게 제한 범위에 따라&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;install-time permission&lt;/li&gt;
&lt;li&gt;runtime-permission&lt;/li&gt;
&lt;li&gt;special permission&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;으로 나누어지게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 Install-time permission&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Install-time permission은 가장 좁은 범위의 Permission으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;시스템 또는 다른 앱에 영향을 주지 않는 데이터 / action과 관련된 permission입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;선언된 Install-time permission은 App store의 App 상세 정보 페이지에서 확인할 수 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자가 앱을 설치할 때 자동적으로 시스템에 의해 동의(grant)되게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 Runtime permission&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Runtime permission은 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;dangerous permissions&lt;/b&gt;&lt;/span&gt;이라고도 하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;시스템 또는 다른 앱에 영향을 줄 수 있는 제한된 데이터 또는 action과 관련된 permission입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Install-time permission과 다르게 사용자에게 권한을 요청하는 추가적인 작업이 필요하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;권한 요청 시,  아래와 같은 창을 보신 적이 있을거예요.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-23-20-19-54.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;599&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4c7MU/btsb6Z9AMJR/KaVnelmKqKvKBj3fredtJ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4c7MU/btsb6Z9AMJR/KaVnelmKqKvKBj3fredtJ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4c7MU/btsb6Z9AMJR/KaVnelmKqKvKBj3fredtJ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4c7MU%2Fbtsb6Z9AMJR%2FKaVnelmKqKvKBj3fredtJ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;276&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-23-20-19-54.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;599&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이러한 Runtime permission으로 접근 가능한 데이터들은 위치, 연락처 정보 등과 같이 민감한 정보가 해당됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3 Special permissions&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;오직 platform과 OEM들에 의해서만 정의될 수 있는 특별한 권한으로, 특정 데이터 또는 action에 대한 접근을 막고자 정의됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  아래는 Device의 설정에서 확인할 수 있는 Special app access 화면이며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;여기에 속하는 대부분의 동작이 Special permission으로 제공됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-23-20-29-05.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;990&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cUpNrA/btsbSFZyMr9/ABdEXcrGAi9vJaasAxRjHk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cUpNrA/btsbSFZyMr9/ABdEXcrGAi9vJaasAxRjHk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cUpNrA/btsbSFZyMr9/ABdEXcrGAi9vJaasAxRjHk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcUpNrA%2FbtsbSFZyMr9%2FABdEXcrGAi9vJaasAxRjHk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;461&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-23-20-29-05.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;990&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.4 Permission groups&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이러한 Permission들은 종류가 다양하지만 하나의 그룹으로 묶여 사용될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;예를 들어, 가장 자주 사용된다고 생각되는 Camera Permission Group에는 사진 요청과 비디오 촬영과 관련된 Permission이 속해있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Permission을 그룹화하는 이유는 System이 사용자에게 권한을 요청하는 Dialog를 최대한 적게 띄우기 위해서입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;요청하는 권한별로 Dialog를 하나씩 띄우게 된다면 사용자로 하여금 앱에 대한 불신감을 키울 수 있기 때문입니다. &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;뭐야.. 왜 이렇게 사용하는 데이터가 많은거지?  이런 느낌이지 않을까요?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  적절한 Permission 요청은 사용자와 앱 간의 신뢰성을 높이고 안정적인 서비스 제공을 보장하는 조건 중 하나라고 생각해요.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이번에는 위에서 보았던 여러 Permission 타입 중 Runtime permission 적용방법에 대해 학습하고 정리해보고자 합니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. Runtime permission 요청하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Runtime permission은 앞에서도 말했듯, dangerous permission(이름만 들어도 위험해 보이는..)이라 하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;권한을 요청하기 위해서는 추가적인 작업이 필요하게 됩니다.  &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  정확히는 Android 6.0(API 23) 이상의 기기에서만 필요하지만, 제가 알기로 이제는 API 23 이상만 지원한다고 알고 있어 앞으로는 무조건 추가 작업이 필요할 듯 해요.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1 Runtime permission 권장사항&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Runtimer Permission은 사용자에게 권한을 요청하기 때문에, 아래와 같은 사항들을 지키는 것을 권장합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;그 권한이 필요한 &lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;기능이 실행될 때&lt;/b&gt;&lt;/span&gt; 권한 요청해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;권한을 수락하거나 거절할 수 있는 방법&lt;/u&gt;을 반드시 제공해주어야 합니다.&lt;/li&gt;
&lt;li&gt;만약 사용자가 권한 요청을 거부하였어도, 해당 기능을 제외한 &lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;축소된 서비스를 제공&lt;/b&gt;&lt;/span&gt;하여 사용자가 계속 앱을 이용할 수 있도록 해야 합니다.&lt;/li&gt;
&lt;li&gt;권한이 필요한 특정 기능을 사용할 때는 해당 권한이 허가되었는지 &lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;매번 검사하는 로직을 추가&lt;/b&gt;&lt;/span&gt;해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2 Permission 요청 순서&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;672&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfO8St/btsbV6uT4cg/Pgl3pHYilIe6LlApAIb25k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfO8St/btsbV6uT4cg/Pgl3pHYilIe6LlApAIb25k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfO8St/btsbV6uT4cg/Pgl3pHYilIe6LlApAIb25k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfO8St%2FbtsbV6uT4cg%2FPgl3pHYilIe6LlApAIb25k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;672&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;가장 먼저 선행되어야 하는 작업은&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.1 진짜 그 permission이 필요한가?&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;최근 Android 버전이 올라가면서, 여러 permission이 생겨나고 사라지고 있는 것으로 알고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;또한 특정 라이브러리를 통해 권한을 별도로 요청하지 않아도 내부적으로 처리해주는 기능 또한 존재하죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;따라서, 해당 권한이 실제 필요한지 먼저 검증하는 단계가 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.2 permission 추가&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;permission 요청이 필요한 것으로 결정되었다면, 해당 permission을 manifest 파일에 추가해줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  요렇게!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-23 오후 9.18.19.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKVy2V/btsbSD1HDM3/E839qCDY2V2RSbaIWAjn1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKVy2V/btsbSD1HDM3/E839qCDY2V2RSbaIWAjn1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKVy2V/btsbSD1HDM3/E839qCDY2V2RSbaIWAjn1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKVy2V%2FbtsbSD1HDM3%2FE839qCDY2V2RSbaIWAjn1k%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;1400&quot; height=&quot;214&quot; data-filename=&quot;스크린샷 2023-04-23 오후 9.18.19.png&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.3 UX 설계&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;어떠한 기능을 제공받기 위해 이러한 권한을 요청하는 것인지 사용자가 인식할 수 있도록 적절한 UX 설계해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.4 사용자가 특정 권한이 필요한 기능을 사용하기 전까지 대기&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;앱을 시작하는 순간부터 사용자에게 권한을 요청하는 것이 아닌, 해당 권한이 실질적으로 필요한 기능을&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자가 실행했을 때 권한이 요청되도록 기다려야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.5 권한 승인 여부 확인&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;권한 요청이 실행되었을 때 가장 먼저 해당 권한이 이미 승인되어 있는지 확인하는 작업이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이미 승인이 되어있다면 바로 해당 데이터에 접근하거나 Action을 수행할 수 있고, 승인이 되어있지 않다면 권한 승인을 위한 다음 작업을 수행해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  여기서 주의해야 할 점! 한번 권한이 승인되었다고 해서 계속 유지되는 것은 아닙니다. 때문에 권한이 필요한 특정 기능을 수행할 때마다 매번 권한 승인 여부를 확인하는 작업이 이루어져야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  요렇게!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.14.12.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;254&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzIAYg/btsbSEsS2Fm/fwsjERGUCePmKz9LkDTpe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzIAYg/btsbSEsS2Fm/fwsjERGUCePmKz9LkDTpe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzIAYg/btsbSEsS2Fm/fwsjERGUCePmKz9LkDTpe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzIAYg%2FbtsbSEsS2Fm%2FfwsjERGUCePmKz9LkDTpe1%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;1228&quot; height=&quot;254&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.14.12.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;254&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.6 사용자에게 권한이 필요한 이유에 대해서 설명을 해야 하는지 확인&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;시스템에서 특정 권한이 필요한 이유를 사용자에게 설명해줘야 하는 권한이라고 지정한 경우에는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;설명이 포함된 UI&lt;/b&gt;&lt;/span&gt;를 사용자에게 제공해주어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  요렇게!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.30.20.png&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhoFhb/btscjkMeuPW/rGWdwOh7DiiKDcLnO3DKcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhoFhb/btscjkMeuPW/rGWdwOh7DiiKDcLnO3DKcK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhoFhb/btscjkMeuPW/rGWdwOh7DiiKDcLnO3DKcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhoFhb%2FbtscjkMeuPW%2FrGWdwOh7DiiKDcLnO3DKcK%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;1378&quot; height=&quot;274&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.30.20.png&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이 UI에는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;접근하려는 데이터는 무엇인가?&lt;/li&gt;
&lt;li&gt;해당 권한을 승인하였을 때의 이점은 무엇인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;에 대한 세부적인 설명을 제시해주어야 합니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 만약 설명해줄 필요가 없는 권한인 경우에는 선택사항입니다. )&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  사용자에게 요청하는 권한과 요청 이유에 대해 설명해줌으로서 앱의 신뢰성을 높일 수 있어, 저는 설명 UI를 추가하는 것을 선호해요.  &lt;/blockquote&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;추가적으로 Android 12(API 31)부터는 privacy indicator라고 하여, 앱이 마이크나 카메라에 접근하여 사용중이라면 화면에 사용중이라는 것을 표시해주는 기능이 제공되고 있습니다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-23-22-22-41.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;227&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bou5V1/btsbT1noDVV/hdQ0a8TCVXp7dcWKDyhrzk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bou5V1/btsbT1noDVV/hdQ0a8TCVXp7dcWKDyhrzk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bou5V1/btsbT1noDVV/hdQ0a8TCVXp7dcWKDyhrzk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbou5V1%2FbtsbT1noDVV%2FhdQ0a8TCVXp7dcWKDyhrzk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;520&quot; height=&quot;109&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-23-22-22-41.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;227&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size14&quot;&gt;이렇듯 특정 상황에서는 실시간으로 사용자의 정보에 접근하고 있다는 것을 Notification 등으로 보여주는 것도 유용합니다.  &lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;따라서, 앱에서 특정 권한이 필요한데, 그 이유가 사용자에게 있어서 분명하지 않을 경우에는 요청 근거에 대해서 알려줄 수 있는 방법을 고민해보아야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.7 permission 요청&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자에게 권한에 대한 설명을 보여준 후 사용자가 권한을 승인하거나, 권한에 대한 설명을 보여줄 필요가 없는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;시스템에 권한을 요청하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이를 위해 AndroidX에서 제공해주는 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;RequestPremission&lt;/b&gt;&lt;/span&gt;을 사용할 수 있습니다.  &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  해당 방법은 시스템에서 request code를 관리하며, 좀 더 간편하게 로직을 구성할 수 있도록 해줍니다. 때문에 해당 포스트에서는 RequestPermission을 사용한 방법만 정리하도록 하겠습니다.  &lt;/blockquote&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;하지만 만약 직접 result code를 지정하는 방법이 궁금하시다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;a href=&quot;https://developer.android.com/training/permissions/requesting#manage-request-code-yourself&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;  여기를 클릭해주세요. :-)&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;RequestPermission을 사용하기 위해서는 아래 dependency가 필요합니다. &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;androidx.activity 1.2.0 이상&lt;/li&gt;
&lt;li&gt;androidx.fragment 1.3.0 이상&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  아래와 같이 ActivityResultLauncher를 만들어줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자가 시스템이 띄워준 prompt를 클릭하면 정의한 callback method가 호출되게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.43.41.png&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/87ZTA/btscmMaHU2G/5se5VzN5YVw6FlQATUU1V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/87ZTA/btscmMaHU2G/5se5VzN5YVw6FlQATUU1V1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/87ZTA/btscmMaHU2G/5se5VzN5YVw6FlQATUU1V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F87ZTA%2FbtscmMaHU2G%2F5se5VzN5YVw6FlQATUU1V1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1526&quot; height=&quot;370&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.43.41.png&quot; data-origin-width=&quot;1526&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  여러 Permission을 요청할 때는 RequestPermission 대신, RequestMultiplePermissions를 사용해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이후, 정의한 requestPermissionLauncher를 호출해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.49.50.png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1l5dx/btsb1Qk3eNY/bEm9RntEG4XtJRwaVEL2iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1l5dx/btsb1Qk3eNY/bEm9RntEG4XtJRwaVEL2iK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1l5dx/btsb1Qk3eNY/bEm9RntEG4XtJRwaVEL2iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1l5dx%2Fbtsb1Qk3eNY%2FbEm9RntEG4XtJRwaVEL2iK%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;1308&quot; height=&quot;240&quot; data-filename=&quot;스크린샷 2023-04-23 오후 10.49.50.png&quot; data-origin-width=&quot;1308&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;  3.2.8 사용자가&lt;/blockquote&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;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;여기까지 코드를 작성하면 기본적인 권한 요청은 완료되었습니다.  &lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이후에는 공식문서에 포함되어 있는 추가적인 내용에 대해서 정리해보도록 하겠습니다.  &lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. location 권한 요청&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;앱 내에서 가장 민감한 정보를 하나 뽑아보라 하면 location을 들 수 있을거 같은데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;때문에 Location과 관련해서는 UseCase에 맞게 사용할 수 있는 다양한 권한들이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1 Foreground location&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;아래 상황에서 현재 기기의 위치 정보를 얻고자 할 때 foreground locaion 권한이 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면에 보이고 있는 Activity에서&lt;/li&gt;
&lt;li&gt;Foreground Service에서&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  Android 10(API 29) 이상에서는 location 정보에 접근하는 foreground service는 manifest에 등록할 때 service type을 location으로 지정해주어야 합니다.&lt;br /&gt;( * 이전 버전에서는 선택사항이지만 권장하고 있습니다! :-) )&lt;/blockquote&gt;
&lt;pre id=&quot;code_1682419522726&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- Recommended for Android 9 (API level 28) and lower. --&amp;gt;
&amp;lt;!-- Required for Android 10 (API level 29) and higher. --&amp;gt;
&amp;lt;service
    android:name=&quot;MyNavigationService&quot;
    android:foregroundServiceType=&quot;location&quot; ... &amp;gt;
    &amp;lt;!-- Any inner elements go here. --&amp;gt;
&amp;lt;/service&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이 때 필요한 권한은&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;4.1.1 대략적인 위치 정보가 필요한 경우&lt;/blockquote&gt;
&lt;pre id=&quot;code_1682414603658&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- Include this permission any time your app needs location information. --&amp;gt;
&amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_COARSE_LOCATION&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;4.1.2 정확한 위치 정보가 필요한 경우&lt;/blockquote&gt;
&lt;pre id=&quot;code_1682414631319&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- Include only if your app benefits from precise location access. --&amp;gt;
&amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_FINE_LOCATION&quot; /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2 Background location&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;앱 서비스 상 앱이 종료되어도 Background에서 계속해서 기기의 위치를 수집해야 하는 경우가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;가족들간에 위치 공유 서비스&lt;/li&gt;
&lt;li&gt;사용자 위치 기반의 IOT 서비스 앱&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Background Location의 정확도는 Foreground Location과 동일하게&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;어떠한 Location Permission을 사용하고 있는지에 따라 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;4.2.1 Android 10 (API 29) 이상부터는&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Background Location을 사용하기 위해 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;ACCESS_BACKGROUND_LOCATION&lt;/b&gt; &lt;/span&gt;권한이 manifest에 필수로 추가되어야 합니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 이전 버전에서는 foreground location 접근 권한이 있다면 background location 또한 접근 가능합니다. )&lt;/p&gt;
&lt;pre id=&quot;code_1682419433679&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;manifest ... &amp;gt;
  &amp;lt;!-- Required only when requesting background location access on
       Android 10 (API level 29) and higher. --&amp;gt;
  &amp;lt;uses-permission android:name=&quot;android.permission.ACCESS_BACKGROUND_LOCATION&quot; /&amp;gt;
&amp;lt;/manifest&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 사용자가 Permission을 거부했을 때는..&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;개인 정보가 중요해지고 있는 지금.. 사용자들은 자신들의 정보를 보호하기 위해 민감한 정보와 관련된 Permission을 거부할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;때문에 사용자에게 해당 권한이 필요한 근거와 해당 권한을 거부했을 때의 상황에 대한 설명을 통해,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자가 계속해서 서비스를 이용할 수 있도록 해야합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5.1 권장사항&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;5.1.1 UI를 이용해 사용자에게 알려주기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;권한 거부로 인해 사용이 불가능한 버튼 또는 기능에 대해 사용자가 인식할 수 있도록 다른 UI로 표현할 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;5.1.2 권한을 거부하더라도 계속해서 앱을 사용할 수 있도록 하기&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;권한을 거부한 이후 사용자가 더이상 앱을 사용할 수 없도록 경고 문자(또는 화면)을 전체화면 가득 채우거나 하지 않기&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Android 11 (API 30)부터, 기기에 앱이 설치된 이후 사용자가 한번이라도 권한을 거부하였다면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이후에 다시 권한요청을 요청하더라도 관련된 System Prompt를 띄우지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자가 한번 이상 &quot;거부&quot;에 의사를 밝혔다면, 영구적으로 권한을 승인하지 않겠다는 의미로 해석하고 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;때문에 해당 권한이 실질적으로 필요한 경우에 권한을 요청하여 사용자가 이러한 권한이 왜 필요한지 확실히 인식하여 최대한 권한을 승인할 수 있는 방안을 모색해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  실제 토이 프로젝트 중에도 사용자가 한번 시스템 Prompt에서 권한을 거부하게 되면 다음부터는 Prompt를 띄우지 않아 기횡을 수정하는 일이 있었습니다.  &lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;또한, 권한은 특정 상황에 따라 자동적으로 승인되거나 거부될 수 있기 때문에 앱 내에서는 항상 권한이 필요한 기능을 실행하기 전에&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;권한을 검사하고 필요한 경우에는 요청하는 로직을 추가해야 합니다. &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. One-time permissions&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Android 11 (API 30)부터 사용자에게 권한을 요청하는 Prompt에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  요렇게 &quot;&lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;이번만 허용(Only this time)&lt;/b&gt;&lt;/span&gt;&quot;이라는 선택지가 포함되어 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-25-19-14-42.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKKwE2/btscJmbOry1/QOSVqka24nTBpA4xQtKc70/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKKwE2/btscJmbOry1/QOSVqka24nTBpA4xQtKc70/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKKwE2/btscJmbOry1/QOSVqka24nTBpA4xQtKc70/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKKwE2%2FbtscJmbOry1%2FQOSVqka24nTBpA4xQtKc70%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;373&quot; height=&quot;440&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-25-19-14-42.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자가 &quot;이번만 허용&quot;을 선택하게 되면 &lt;u&gt;&lt;b&gt;일시적으로만&lt;/b&gt;&lt;/u&gt; 권한이 승인되게 되며, 아래 상황 중 하나일 때까지 그 권한이 유지됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;앱의 Activity가 보이는 중에는 데이터에 접근 가능합니다.&lt;/li&gt;
&lt;li&gt;사용자가 앱을 background로 보낸다면, 짧은 기간동안은 데이터에 접근 가능합니다.&lt;/li&gt;
&lt;li&gt;Activity가 보이는 중에 Foreground service를 실행되고 그 후에 사용자가 앱을 background로 보냈다면, foreground service가 살아있을 때까지는 데이터에 접근 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;단, 만약 사용자가 시스템 설정 등에서 이러한 One-time permission까지 취소시킨다면 foreground service가 살아있어도,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;권한과 관련된 데이터에 접근할 수 없으며, 앱 자체가 종료되게 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  이후 사용자가 다시 앱을 실행하거나 해당 기능을 요청하게 되면, 위 이미지와 같은 권한을 요청하는 Prompt가 다시 뜨게 됩니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 사용하지 않는 permission Deny 시키기&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6.1 App에서 Deny 시키기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Android 13(API 33) 이상부터는 더이상 필요로 하지 않는 runtime 권한을 제거할 수 있는 기능을 제공해주고 있습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이렇게 사용하지 않는 권한들을 제거함으로써 사용자로 하여금 앱에 대한 신뢰도를 높이는 것이 목적입니다. :-)&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;runtime 권한을 제거하기 위해서는&lt;/p&gt;
&lt;pre id=&quot;code_1682416506186&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;revokeSelfPermissionOnKill(Mainfest.permission.EXTERNAL_STORAGE)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;와 같이 permission의 이름을 revokeSelfpermissionOnKill 메서드에 전달하여 Deny 시킬 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 여러 permission을 한번에 Deny 시킬 때는 revokeSelfPermissionsOnKill()을 사용할 수 있습니다. )&lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  이러한 권한 Deny 작업은 비동기적으로 수행되며 app의 UID와 연관된 모든 프로세스를 종료합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;  추가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;시스템이 앱의 권한들을 Deny 시키려면 앱과 관련된 모든 프로세스들이 종료되기에&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;가장 안전하게 프로세스를 종료될 수 있는 시점에 해당 작업을 수행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;보통은 앱이 foreground에서 background로 이동할 때까지 기다린 후에 작업을 진행하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;사용자게에 특정 권한이 더이상 사용되지 않는다는 것을 알려주는 것은 해당 작업이 이루어진 이후에 Dialog로 보여지게 됩니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6.2 사용하지 않는 permission 자동으로 Deny&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;만약 앱이 Android 11(API 30) 이상을 타겟팅하고 있고 사용자가 앱을 몇달 동안 사용하지 않은 경우에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;시스템이 자동적으로 사용자의 민감한 정보들을 보호하기 위해 runtime permission들을 Deny 상태로 설정해줍니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;현재 개인적으로 진행하고 있는 토이 프로젝트에서 권한 요청 기능 구현이 필요해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;관련 내용을 정리할 겸 포스트를 작성하게 되었습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;감사합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  제 프로젝트에 사용자에게 권한에 대해 설명해주는 화면이예요. :-)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-25-13-56-55.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;970&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnRnpe/btscJmbRmhn/cuzMA463BmvhzdQDKJ5kOK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnRnpe/btscJmbRmhn/cuzMA463BmvhzdQDKJ5kOK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnRnpe/btscJmbRmhn/cuzMA463BmvhzdQDKJ5kOK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnRnpe%2FbtscJmbRmhn%2FcuzMA463BmvhzdQDKJ5kOK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;408&quot; height=&quot;366&quot; data-filename=&quot;KakaoTalk_Photo_2023-04-25-13-56-55.jpeg&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;970&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;</description>
      <category>Android/Android</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/198</guid>
      <comments>https://rien-atelier.tistory.com/198#entry198comment</comments>
      <pubDate>Sun, 23 Apr 2023 20:44:13 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스 &amp;gt; LV2] 요격 시스템(Kotlin)</title>
      <link>https://rien-atelier.tistory.com/197</link>
      <description>&lt;figure id=&quot;og_1682223517679&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;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bYqD0s/hySm6NpwFC/jPTlWVVRUThSODCojhxKdk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bfmbtq/hySmTUQvi0/7od9F0ylz196mo3qxmXSCk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/181188&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bYqD0s/hySm6NpwFC/jPTlWVVRUThSODCojhxKdk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/bfmbtq/hySmTUQvi0/7od9F0ylz196mo3qxmXSCk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[입력]&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;각 폭격 미사일의 x좌표 범위 목록 targets&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  targets는 (s,e) 형태이며, 해당 폭격 미사일은 s와 e에서 발사하는 요격 미사일로는 요격할 수 없습니다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[결과]&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;모든 폭격 미사일을 요격하기 위해 필요한 요격 미사일 수의 최솟값을 return&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;먼저 미사일들을 구간을 기준으로 오름차순 정렬할 필요가 있습니다.  &lt;/p&gt;
&lt;pre id=&quot;code_1682223981995&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;targets.sortWith(compareBy({ it[0] }, { it[1] }))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;그럼 필요한 요격 미사일의 최소 개수를 구할 수 있을까요?&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;중복되는 구간을 적절히 이용할 필요가 있습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-23 오후 1.25.34.png&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Woih0/btsbSK7fRq6/IWgk3OAvmep21CxW25VvsK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Woih0/btsbSK7fRq6/IWgk3OAvmep21CxW25VvsK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Woih0/btsbSK7fRq6/IWgk3OAvmep21CxW25VvsK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWoih0%2FbtsbSK7fRq6%2FIWgk3OAvmep21CxW25VvsK%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;531&quot; height=&quot;248&quot; data-filename=&quot;스크린샷 2023-04-23 오후 1.25.34.png&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 구간은 (s1, e1)입니다.&lt;/li&gt;
&lt;li&gt;target 2의 s가 구간 e1보다 작으니, 아직까지는 요격 미사일을 쏘지 않습니다.&lt;/li&gt;
&lt;li&gt;이 때의 구간은 target 1과 target 2가 겹치는 (s2, e1) 입니다.&lt;/li&gt;
&lt;li&gt;target 3의 s가 구간 e1보다 작으니, 아직까지는 요격 미사일을 쏘지 않습니다.&lt;/li&gt;
&lt;li&gt;이 때의 구간은 (s2, e1)과 (s3, e3)가 겹치는 (s3, e3) 입니다.&lt;/li&gt;
&lt;li&gt;target 4의 s가 구간 e3보다 크기, 구간이 겹치지 않습니다. 그렇다면 이전 구간에 속해있는 미사일을 요격해 제거해줄 필요가 있습니다. 요격 미사일 1개가 필요하게 됩니다.&lt;/li&gt;
&lt;li&gt;마지막 target4 미사일이 남아있으므로 이를 요격해줄 필요가 있습니다. 결과 요격 미사일은 총 2개가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;위에 서술한 프로세스를 코드로 구현하면 아래오 같습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Kotlin&lt;/blockquote&gt;
&lt;pre id=&quot;code_1682224382839&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlin.math.min
class Solution {
    fun solution(targets: Array&amp;lt;IntArray&amp;gt;): Int {
        /**
         *   현재 구간을 (s, e)라 할 때, i번째 미사일의 si가 구간 내에 있을 때는 구간을 (si, min(ei, e))로 변경
         *   그러지 않을 경우 answer + 1, 구간을 (si, ei)로 갱신
         **/
        var answer: Int = 0
        targets.sortWith(compareBy({ it[0] }, { it[1] }))
        var (s, e) = targets.first() // 첫 구간
        (1 until targets.size).forEach {
            val (si, ei) = targets[it]
            e = if (si &amp;lt; e) { // si가 구간 내에 있을 때
                min(ei, e)
            } else {
                answer++
                ei
            }
            s = si
        }
        // 마지막에 요격하지 못한 미사일들을 한번에 처리하도록 + 1
        return answer + 1
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm/프로그래머스</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/197</guid>
      <comments>https://rien-atelier.tistory.com/197#entry197comment</comments>
      <pubDate>Sun, 23 Apr 2023 13:33:09 +0900</pubDate>
    </item>
    <item>
      <title>[하루네컷] UiEvent에 StateFlow 대신 SharedFlow를 사용해보다.</title>
      <link>https://rien-atelier.tistory.com/195</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;해당 글은 직접 토이 프로젝트를 진행하면서 적용한 내용 중, 기억해두면 좋을거 같은 내용을 따로 정리해둔 게시글입니다.&lt;br /&gt;안드로이드를 독학하고 있는 초보자입니다.  &lt;br /&gt;혹시 틀린 점이 있다면 꼭 댓글로 말씀 부탁드립니다.  &lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;현재 진행중인 하루네컷을 개발하던 도중, ViewModel에서 발생한 Event를 UI에 알려주는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;b&gt;UiEvent&lt;/b&gt;&lt;/span&gt;를 사용하게 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  UiEvent를 요렇게 만들어두고,&lt;/p&gt;
&lt;pre id=&quot;code_1681982261377&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sealed interface PostUiEvent {
    object Initialized : PostUiEvent
    object Loading: PostUiEvent

    sealed interface Fail : PostUiEvent {
        data class DuplicateTagName(
            val messageId: Int = R.string.duplicate_tag_name
        ) : Fail
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.18.07.png&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kzQWk/btsbCgRE3Ri/eMvpAHqkaSuBd2KG1TZWS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kzQWk/btsbCgRE3Ri/eMvpAHqkaSuBd2KG1TZWS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kzQWk/btsbCgRE3Ri/eMvpAHqkaSuBd2KG1TZWS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkzQWk%2FbtsbCgRE3Ri%2FeMvpAHqkaSuBd2KG1TZWS1%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;1556&quot; height=&quot;132&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.18.07.png&quot; data-origin-width=&quot;1556&quot; data-origin-height=&quot;132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;실제 Event가 발생하는 곳은 아래와 같이 StateFlow가 emit할 수 있도록,&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  Loading -&amp;gt; DuplicateTagName -&amp;gt; Loading -&amp;gt; DuplicateTagName&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;계속 변경이 이루어질 수 있도록 하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.21.07.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;520&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIRlqJ/btsbBbDj1jX/5NPjio2V06TQCxzlEok73k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIRlqJ/btsbBbDj1jX/5NPjio2V06TQCxzlEok73k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIRlqJ/btsbBbDj1jX/5NPjio2V06TQCxzlEok73k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIRlqJ%2FbtsbBbDj1jX%2F5NPjio2V06TQCxzlEok73k%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;685&quot; height=&quot;290&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.21.07.png&quot; data-origin-width=&quot;1230&quot; data-origin-height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;하지만 왜인걸!!&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;007&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/007.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/007.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DuplicateTagName이 한번 발생한 이후, 다시 Tag를 추가하려고 하면, DuplicateTagName을 emit하지 않고 있습니다.&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( )&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원인을 생각해보자...&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;제 생각에는 value를 변경하는 쪽과 collect가 발생하는 쪽의 시간차에 의해서 발생한 이슈이지 아닐까 합니다.ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;collect의 내부 코드를 보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.42.27.png&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXwOjz/btsbBToTElr/MlTj7EpQEc6KvAn77Jzpw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXwOjz/btsbBToTElr/MlTj7EpQEc6KvAn77Jzpw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXwOjz/btsbBToTElr/MlTj7EpQEc6KvAn77Jzpw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXwOjz%2FbtsbBToTElr%2FMlTj7EpQEc6KvAn77Jzpw1%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;648&quot; height=&quot;482&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.42.27.png&quot; data-origin-width=&quot;1552&quot; data-origin-height=&quot;1154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;현재 collect 하는 값과 이전에 collect한 값의 동등성을 비교하는 조건문이 있는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;addTag 메서드 내에서 _postUiEvent의 값이 매우 빠르게 변경되므로, 사실상 아래와 같이 Loading이 건너 띄어지는 것이죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.41.30.png&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsDFBb/btsbElEW01S/ukQBkk1sdB4AlDssOBFYKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsDFBb/btsbElEW01S/ukQBkk1sdB4AlDssOBFYKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsDFBb/btsbElEW01S/ukQBkk1sdB4AlDssOBFYKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsDFBb%2FbtsbElEW01S%2FukQBkk1sdB4AlDssOBFYKk%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;710&quot; height=&quot;166&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.41.30.png&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결을 해보자&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이러한 문제상황을 해결하기 위해 StateFlow가 아닌, SharedFlow로 Event를 다루기로 하였습니다. &lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.46.39.png&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6fdHf/btsbDtDkUOo/kRdqNF013mAppnzRnkuAU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6fdHf/btsbDtDkUOo/kRdqNF013mAppnzRnkuAU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6fdHf/btsbDtDkUOo/kRdqNF013mAppnzRnkuAU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6fdHf%2FbtsbDtDkUOo%2FkRdqNF013mAppnzRnkuAU0%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;809&quot; height=&quot;79&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.46.39.png&quot; data-origin-width=&quot;1224&quot; data-origin-height=&quot;120&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;초기값이 필요없으므로 PostUiEvent 코드도 수정해주고,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.48.11.png&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0lCiO/btsbChpEFC6/L8awiQS8RommJSZZm4DHx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0lCiO/btsbChpEFC6/L8awiQS8RommJSZZm4DHx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0lCiO/btsbChpEFC6/L8awiQS8RommJSZZm4DHx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0lCiO%2FbtsbChpEFC6%2FL8awiQS8RommJSZZm4DHx1%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;623&quot; height=&quot;265&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.48.11.png&quot; data-origin-width=&quot;1038&quot; data-origin-height=&quot;442&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Event가 발생하는 부분도  아래와 같이 수정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.49.05.png&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;578&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9Cdcp/btsbAXZLRvN/v8EJtUyJOb9WjbnskdESxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9Cdcp/btsbAXZLRvN/v8EJtUyJOb9WjbnskdESxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9Cdcp/btsbAXZLRvN/v8EJtUyJOb9WjbnskdESxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9Cdcp%2FbtsbAXZLRvN%2Fv8EJtUyJOb9WjbnskdESxk%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;688&quot; height=&quot;340&quot; data-filename=&quot;스크린샷 2023-04-20 오후 6.49.05.png&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결과&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  동일한 Event가 여러 번 발생해도 모두 collect하는 것을 확인하였습니다.  &lt;/blockquote&gt;</description>
      <category>기록/TIL</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/195</guid>
      <comments>https://rien-atelier.tistory.com/195#entry195comment</comments>
      <pubDate>Thu, 20 Apr 2023 18:50:24 +0900</pubDate>
    </item>
    <item>
      <title>[Android] 2023 Google Play Policy Bytes</title>
      <link>https://rien-atelier.tistory.com/194</link>
      <description>&lt;p data-ke-size=&quot;size14&quot;&gt;최근 Android 앱 개발 및 출시를 목표로 토이 프로젝트를 진행하면서, Google Play 정책에 대해 조사를 하던 중&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Android Developers에서 2023 새로운 정책을 발표하는 영상을 보개 되었습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;해당 글은 Google Play Policy Bytes 영상의 내용을 정리해본 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;( * 추후 점차 업데이트 해 나갈 예정 입니다. :-) )&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;정말 다행히도, 한국어로 된 영상도 제공해주어 어렵지 않게 정리할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;ㅠㅠ 매번 영어듣기 평가하는 게 얼마나 힘들었던가...&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;017&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/017.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/017.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;관련 영상 링크는 마지막에 첨부해두었습니다.  &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  아직 안드로이드 경력 0년차 초보 개발자입니다. 틀린 부분이 있다면 꼭! 댓글로 말씀 부탁드립니다.  &lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 2023 Initiative&lt;/h2&gt;
&lt;figure id=&quot;og_1681279121830&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;Keeping Android and Google Play safe with our key 2023 initiatives&quot; data-og-description=&quot;Today, we&amp;rsquo;re excited to share with you what&amp;rsquo;s ahead in 2023 to help protect your apps, and continue to collaborate with you to keep Google Play safe.&quot; data-og-host=&quot;android-developers.googleblog.com&quot; data-og-source-url=&quot;https://android-developers.googleblog.com/2023/03/keeping-google-play-safe-with-our-key-2023-initiatives.html&quot; data-og-url=&quot;https://android-developers.googleblog.com/2023/03/keeping-google-play-safe-with-our-key-2023-initiatives.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cGdGKQ/hySe1TwWnF/UDUv6FiVe68nOodjOCqgZK/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800,https://scrap.kakaocdn.net/dn/bEZRu7/hySeZg6MoB/vEvvQFQdI2oE7ypmI59dYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/jiSEc/hySgufv2sZ/mFbcaq1idM6pMsDVqlFu21/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800&quot;&gt;&lt;a href=&quot;https://android-developers.googleblog.com/2023/03/keeping-google-play-safe-with-our-key-2023-initiatives.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://android-developers.googleblog.com/2023/03/keeping-google-play-safe-with-our-key-2023-initiatives.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cGdGKQ/hySe1TwWnF/UDUv6FiVe68nOodjOCqgZK/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800,https://scrap.kakaocdn.net/dn/bEZRu7/hySeZg6MoB/vEvvQFQdI2oE7ypmI59dYK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/jiSEc/hySgufv2sZ/mFbcaq1idM6pMsDVqlFu21/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800');&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;Keeping Android and Google Play safe with our key 2023 initiatives&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Today, we&amp;rsquo;re excited to share with you what&amp;rsquo;s ahead in 2023 to help protect your apps, and continue to collaborate with you to keep Google Play safe.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;android-developers.googleblog.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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 신규 정책&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 사용자 데이터 정책&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. 모든 개발자는 Play Console의 데이터 보안 양식에 있는 모든 데이터 삭제 질문에 답변해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Google Play Console 데이터 보안 양식에  아래와 같은 질문이 이에 해당하는 듯 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-12 오후 2.02.45.png&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dE6KBC/btr9AkccDCf/k3xy0x5sG0MfF63FWVszQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dE6KBC/btr9AkccDCf/k3xy0x5sG0MfF63FWVszQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dE6KBC/btr9AkccDCf/k3xy0x5sG0MfF63FWVszQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdE6KBC%2Fbtr9AkccDCf%2Fk3xy0x5sG0MfF63FWVszQk%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;740&quot; height=&quot;178&quot; data-filename=&quot;스크린샷 2023-04-12 오후 2.02.45.png&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;데이터 보안 양식에서 작성한 답변은 실제 사용자가 Google Play 앱에서 확인할 수 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이를 통해 사용자의 신뢰를 확보하는 것에 의의를 두는 듯 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;예를 들어  는 네이버 안드로이드 앱의 데이터 보안 정책 화면입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;444&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0c8Wb/btr9Nip6xsr/qAxfV93cEqiF6KSxqfAQ91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0c8Wb/btr9Nip6xsr/qAxfV93cEqiF6KSxqfAQ91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0c8Wb/btr9Nip6xsr/qAxfV93cEqiF6KSxqfAQ91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0c8Wb%2Fbtr9Nip6xsr%2FqAxfV93cEqiF6KSxqfAQ91%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;355&quot; height=&quot;784&quot; data-origin-width=&quot;444&quot; data-origin-height=&quot;980&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;blockquote data-ke-style=&quot;style2&quot;&gt;2. 앱에서 계성 생성이 가능한 경우, 사용자에게 다음 두 가지 기능을 모두 제공해야 합니다.&lt;/blockquote&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;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  이를 통해 앱 계정 및 관련 데이터 삭제를 요청할 수 있어야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;3. 계상 삭제 요구사항 시행 일정&lt;/blockquote&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.5349%;&quot;&gt;2023년 4월&lt;/td&gt;
&lt;td style=&quot;width: 75.4651%;&quot;&gt;데이터 보안 양식에 새로운 데이터 삭제 관련&lt;br /&gt;질문이 포함되어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.5349%;&quot;&gt;2023년 12월 7일&lt;/td&gt;
&lt;td style=&quot;width: 75.4651%;&quot;&gt;모든 요구사항을 충족해야 하는 기한&lt;br /&gt;( * 기한은 연장할 수 있습니다. )&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.5349%;&quot;&gt;2024년 초&lt;/td&gt;
&lt;td style=&quot;width: 75.4651%;&quot;&gt;사용자가 Google Play에서 새로운&lt;br /&gt;데이터 관리 기능을 볼 수 있어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 24.5349%;&quot;&gt;2024년 5월 31일&lt;/td&gt;
&lt;td style=&quot;width: 75.4651%;&quot;&gt;기한 연장 종료&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  사용자 데이터를 제거할 때는 관련된 모든 데이터도 함께 제거되어야 함에 유의하셔야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 기기 및 네트워크 악용 정책&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;1. Foreground Service(FGS)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Foreground를 사용하면서 앱이 Android 14 이상을 타겟팅한다면 다음 요구사항을 축종해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용 사례별 Foreground 서비스 Type 및 해당 Type과 연관된 권한을 선언해야 합니다.&lt;/li&gt;
&lt;li&gt;Play Console에서 선언을 완료해야 합니다. (올해 말에 제공될 예정이라고 합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1681278089141&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;Foreground service types are required &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;Welcome to the Android 14 Developer Preview! Please give us your feedback, and help us make Android 14 the best release yet. Foreground service types are required Stay organized with collections Save and categorize content based on your preferences. To hel&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/about/versions/14/changes/fgs-types-required&quot; data-og-url=&quot;https://developer.android.com/about/versions/14/changes/fgs-types-required&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bxehal/hySe2dOUB1/9VxBH79TiiB8j3rRdfoC40/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/about/versions/14/changes/fgs-types-required&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/about/versions/14/changes/fgs-types-required&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bxehal/hySe2dOUB1/9VxBH79TiiB8j3rRdfoC40/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;Foreground service types are required &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Welcome to the Android 14 Developer Preview! Please give us your feedback, and help us make Android 14 the best release yet. Foreground service types are required Stay organized with collections Save and categorize content based on your preferences. To hel&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-12 오후 2.59.43.png&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnGFZs/btr9MoEoHzu/WviYfeMbtBtHSXhND6Yk60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnGFZs/btr9MoEoHzu/WviYfeMbtBtHSXhND6Yk60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnGFZs/btr9MoEoHzu/WviYfeMbtBtHSXhND6Yk60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnGFZs%2Fbtr9MoEoHzu%2FWviYfeMbtBtHSXhND6Yk60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;369&quot; data-filename=&quot;스크린샷 2023-04-12 오후 2.59.43.png&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(1) 반드시 사용자에게 유익하고 앱의 핵심 기능과 관련있는 기능을 제공해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(2) 작업실행의 시작과 종료를 사용자가 인식할 수 있어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(3) 앱이 작업을 완료하는데 필요한 만큼만! 사용되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;(4) 시스템 지연이나, 다른 서비스의 사용에 방해되는 일 없이 실행되어야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;2. 사용자가 시작한 데이터 전송 작업&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;Foreground 권한을 강화함에 따라 Foreground 서비스의 대안으로 사용할 수 있는 새로운 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;Job Scheduler API&lt;/b&gt;&lt;/span&gt;도 제공해주고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;해당 API는&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업이 사용자에 의해 시작되는 경우&lt;/li&gt;
&lt;li&gt;전송이 네트워크를 통해 이루어지는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;위 2가지의 경우 Foreground 대신 사용되어야 하며, 작업이 완료할 때까지만 실행되어야 합니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  자세한 사항은 아래 개발문서 참고!&lt;/p&gt;
&lt;figure id=&quot;og_1681277993701&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;Migrate foreground services to user-initiated data transfer jobs &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;Welcome to the Android 14 Developer Preview! Please give us your feedback, and help us make Android 14 the best release yet. Migrate foreground services to user-initiated data transfer jobs Stay organized with collections Save and categorize content based &quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/about/versions/14/changes/user-initiated-data-transfers&quot; data-og-url=&quot;https://developer.android.com/about/versions/14/changes/user-initiated-data-transfers&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/VmTSW/hySgnHr6WI/iougDHWS2HUeb8CcsoFI8K/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/about/versions/14/changes/user-initiated-data-transfers&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/about/versions/14/changes/user-initiated-data-transfers&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/VmTSW/hySgnHr6WI/iougDHWS2HUeb8CcsoFI8K/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;Migrate foreground services to user-initiated data transfer jobs &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Welcome to the Android 14 Developer Preview! Please give us your feedback, and help us make Android 14 the best release yet. Migrate foreground services to user-initiated data transfer jobs Stay organized with collections Save and categorize content based&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;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 정책 업데이트 및 설명&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1 사용자 평가, 리뷰, 설치&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;위 정보들은 사용자들이 앱을 평가할 때 사용되므로 반드시 신뢰할 만한 정보여야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&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;앱의 기본 기능으로 사용자가 다른 앱을 설치하도록 하는 경우&lt;/li&gt;
&lt;li&gt;앱 평가 또는 리뷰를 작성하도록 하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2 SDK 요구사항&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  구글은 앱에 사용된 모~~든 코드, 즉 third-party sdk, 라이브러리의 코드를 포함한 모든 코드가 구글 플레이 정책을 준수해야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이를 위해 개발자가 SDK 요구사항을 간편하게 확인하고 이해할 수 있도록 자료와 영상이 준비되어 있습니다.&lt;/p&gt;
&lt;figure id=&quot;og_1681278475711&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;Best practices for integrating SDKs into your app or game&quot; data-og-description=&quot;-&quot; data-og-host=&quot;playacademy.exceedlms.com&quot; data-og-source-url=&quot;https://playacademy.exceedlms.com/student/path/721800/activity/1286136?utm_source=shortlink&amp;amp;utm_medium=policycenter&amp;amp;utm_campaign=PolicyCenter&amp;amp;utm_content=sdkbestpractices&quot; data-og-url=&quot;https://playacademy.exceedlms.com//student/page/667398&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ecZDO1/hySe6N6k6S/frHT9w0Ke82h9SUA65GRQK/img.png?width=550&amp;amp;height=309&amp;amp;face=0_0_550_309&quot;&gt;&lt;a href=&quot;https://playacademy.exceedlms.com/student/path/721800/activity/1286136?utm_source=shortlink&amp;amp;utm_medium=policycenter&amp;amp;utm_campaign=PolicyCenter&amp;amp;utm_content=sdkbestpractices&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://playacademy.exceedlms.com/student/path/721800/activity/1286136?utm_source=shortlink&amp;amp;utm_medium=policycenter&amp;amp;utm_campaign=PolicyCenter&amp;amp;utm_content=sdkbestpractices&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ecZDO1/hySe6N6k6S/frHT9w0Ke82h9SUA65GRQK/img.png?width=550&amp;amp;height=309&amp;amp;face=0_0_550_309');&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;Best practices for integrating SDKs into your app or game&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;playacademy.exceedlms.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 정책 시행 일정&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.1 대상 API 수준 요구사항&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;모든 앱(신규 앱, 기존 앱 모두) 8월 31일까지 대상 API 수준 31 이상을 타겟팅해야 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  시간이 더 필요한 경우 11월 1일까지 기한을 연장할 수 있습니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4.2 2023년 정책 시행 일정&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 52px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 31.3953%; height: 35px;&quot; rowspan=&quot;3&quot;&gt;2023.05&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%; height: 18px;&quot;&gt;사용자 평가, 리뷰, 설치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 68.6047%; height: 17px;&quot;&gt;개인 대출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 68.6047%; height: 17px;&quot;&gt;가족&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot; rowspan=&quot;2&quot;&gt;2023.08&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;기기 및 네트워크 악용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;대상 API 수준&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 31.3953%;&quot;&gt;2023.12&lt;/td&gt;
&lt;td style=&quot;width: 68.6047%;&quot;&gt;사용자 데이터 정책 - 계정 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 참고 영상&lt;/h2&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=cFQjOqKE4o0&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/cc8hlW/hySguT5r8i/j7KNXfutDkLxFzxQLwxKfk/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=930_156_1176_424&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/cFQjOqKE4o0&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;  Goole 정책에 대한 강의&lt;/p&gt;
&lt;figure id=&quot;og_1681279047341&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;Google Play Policy&quot; data-og-description=&quot;Wherever you are in the app development lifecycle, key to our shared mission is maintaining the safety and integrity of Google Play as a trusted source of high quality apps for users to find and experience apps and games to enjoy. To support this effort an&quot; data-og-host=&quot;playacademy.exceedlms.com&quot; data-og-source-url=&quot;https://playacademy.exceedlms.com/student/collection/263275?utm_source=google&amp;amp;utm_medium=shortlink&amp;amp;utm_campaign=shortlink&amp;amp;utm_content=shortlink&quot; data-og-url=&quot;https://playacademy.exceedlms.com//student/collection/263275&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buBkJd/hySe0UCjGL/RLVkyKIy690AkMPYwfdzEK/img.png?width=550&amp;amp;height=309&amp;amp;face=0_0_550_309&quot;&gt;&lt;a href=&quot;https://playacademy.exceedlms.com/student/collection/263275?utm_source=google&amp;amp;utm_medium=shortlink&amp;amp;utm_campaign=shortlink&amp;amp;utm_content=shortlink&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://playacademy.exceedlms.com/student/collection/263275?utm_source=google&amp;amp;utm_medium=shortlink&amp;amp;utm_campaign=shortlink&amp;amp;utm_content=shortlink&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buBkJd/hySe0UCjGL/RLVkyKIy690AkMPYwfdzEK/img.png?width=550&amp;amp;height=309&amp;amp;face=0_0_550_309');&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;Google Play Policy&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Wherever you are in the app development lifecycle, key to our shared mission is maintaining the safety and integrity of Google Play as a trusted source of high quality apps for users to find and experience apps and games to enjoy. To support this effort an&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;playacademy.exceedlms.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;간단하게 앱 정책에 대해서 알아보고자 정리를 시작하였지만, 어째서인지 공부해야 될게 더 늘어버렸네요.ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이후에는 해당 글에서 세부적으로 알아보지 못한 내용들에 대해 다른 게시글로 작성해보고자 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;감사합니다.  &lt;/p&gt;</description>
      <category>Android/Android</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/194</guid>
      <comments>https://rien-atelier.tistory.com/194#entry194comment</comments>
      <pubDate>Wed, 12 Apr 2023 13:54:59 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스&amp;gt;LV2] 연속된 부분 수열의 합 (Kotlin, Python)</title>
      <link>https://rien-atelier.tistory.com/192</link>
      <description>&lt;figure id=&quot;og_1681115062001&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;프로그래머스&quot; data-og-description=&quot;코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.&quot; data-og-host=&quot;programmers.co.kr&quot; data-og-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/178870&quot; data-og-url=&quot;https://programmers.co.kr/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jhwbg/hySdwsuBVF/ENs2RCFp7r5DDVALZmAQ90/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b51NOX/hySdo2iiW7/tdkDl2pCEKsww3bYzBpNO0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/178870&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://school.programmers.co.kr/learn/courses/30/lessons/178870&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jhwbg/hySdwsuBVF/ENs2RCFp7r5DDVALZmAQ90/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/b51NOX/hySdo2iiW7/tdkDl2pCEKsww3bYzBpNO0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;programmers.co.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 문제&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[입력]&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수열을 나타내는 정수 배열 sequence&lt;/li&gt;
&lt;li&gt;부분 수열의 합을 나타내는 정수 k&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[결과]&lt;/h4&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;조건을 만족하는 부분 수열의 시작 인덱스와 마지막 인덱스를 배열에 담아 return&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 풀이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;알고리즘 문제 중에 잘 알려진 최대 연속 부분 수열의 합으로 생각하고 풀면 큰일나는 문제입니다.ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;조건 중에&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  기존 수열에서 임의의 두 인덱스의 원소와 그 사이의 원소를 모두 포함하는 부분 수열이어야 합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;에서 알 수 있듯이, 해당 문제는 부분 수열을 띄엄띄엄 만드는 것이 연속된 부분 수열들로 이루어진다는 것에 유의하셔야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;이를 통해 이 문제는&amp;nbsp;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;투 포인트&lt;/b&gt;&lt;/span&gt;를 사용해 풀 수 있다는 것을 알 수 있습니다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 코드&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Kotlin&lt;/blockquote&gt;
&lt;pre id=&quot;code_1681115339717&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
    fun solution(sequence: IntArray, k: Int): IntArray {
        var answer: IntArray? = null
        val n = sequence.size
        var minLength = n
        var maxSum = sequence.first()
        var left = 0
        var right = 1

        while (left &amp;lt; right) {
            val length = right - left
            if (maxSum == k &amp;amp;&amp;amp; minLength &amp;gt; length) {
                minLength = length
                answer = intArrayOf(left, right - 1)
            }
            if (maxSum &amp;lt;= k &amp;amp;&amp;amp; right &amp;lt; n) {
                maxSum += sequence[right]
                right += 1
            } else {
                maxSum -= sequence[left]
                left += 1
            }
        }

        return answer ?: intArrayOf(0, n - 1)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Python&lt;/blockquote&gt;
&lt;pre id=&quot;code_1681115367192&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def solution(sequence, k):
    answer = []
    
    n = len(sequence)
    # 부분수열의 길이, 부분수열의 합
    min_len, max_sum = n, sequence[0]
    left, right = 0, 1
    
    while left &amp;lt; right:
        length = right - left
        if min_len &amp;gt; length and max_sum == k:
            answer = [left, right-1]
            min_len = length
        if max_sum &amp;lt;= k and right &amp;lt; n:
            max_sum += sequence[right]
            right+=1
        else:
            max_sum -= sequence[left]
            left+=1
            
    if answer == []: return [0,n-1]
    return answer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm/프로그래머스</category>
      <author>RIEN </author>
      <guid isPermaLink="true">https://rien-atelier.tistory.com/192</guid>
      <comments>https://rien-atelier.tistory.com/192#entry192comment</comments>
      <pubDate>Mon, 10 Apr 2023 17:29:31 +0900</pubDate>
    </item>
  </channel>
</rss>