<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>undefined</title>
    <link>https://not-yet-defined.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 23 Aug 2026 15:14:02 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>un-defined</managingEditor>
    <image>
      <title>undefined</title>
      <url>https://tistory1.daumcdn.net/tistory/6106212/attach/003ee331ec9d4bd8baef9f6b6e4a7a4f</url>
      <link>https://not-yet-defined.tistory.com</link>
    </image>
    <item>
      <title>[Android] Compose의 Arrangement와 Alignment 비교하기</title>
      <link>https://not-yet-defined.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;compose를 사용하다면 Column과 Row를 자주 사용하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 하위 뷰 들을 정렬하는 방식을 선택해야 하는데 종종 헷갈리는 옵션들이 있어서 정리를 해보기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Arrangement와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Alignment의 역할&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Arrangement와 Alignment은 역할에 차이가 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- Arrangement&lt;/b&gt;는 부모 내에서 자식들 간의 공간 배치를 설정하는데, 자식들 간의 &lt;b&gt;간격&lt;/b&gt;을 설정하는데 많이 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- Alignment&lt;/b&gt;는 부모 내의 자식들을 &lt;b&gt;정렬&lt;/b&gt;할 때 사용하는데, 위로 정렬, 가운데 정렬 처럼 자식들을 어떤 기준점에 맞춰 나열할 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Arrangement&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Horizontal Arrangement &lt;/b&gt;은&lt;b&gt; Row&lt;/b&gt;에서 사용하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Vertical Arrangement &lt;/b&gt;은&lt;b&gt; Column&lt;/b&gt;에서 사용한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 &lt;b&gt;Row&lt;/b&gt;에서 Arrangement를 사용한 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Arrangement.Start&lt;/span&gt;: 자식들을 왼쪽에 정렬.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/coiOQr/btsKeU9sXji/CtgLdEFAIzWFG8ErK4c1QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/coiOQr/btsKeU9sXji/CtgLdEFAIzWFG8ErK4c1QK/img.png&quot; data-alt=&quot;horizontalArrangement = Arrangement.Start&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/coiOQr/btsKeU9sXji/CtgLdEFAIzWFG8ErK4c1QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcoiOQr%2FbtsKeU9sXji%2FCtgLdEFAIzWFG8ErK4c1QK%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;404&quot; height=&quot;218&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;horizontalArrangement = Arrangement.Start&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Arrangement.End&lt;/span&gt;: 자식들을 오른쪽에 정렬.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G1UAg/btsKfqto3PW/lpeXJxJb38By5itvh8IFHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G1UAg/btsKfqto3PW/lpeXJxJb38By5itvh8IFHk/img.png&quot; data-alt=&quot;horizontalArrangement = Arrangement.End&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G1UAg/btsKfqto3PW/lpeXJxJb38By5itvh8IFHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG1UAg%2FbtsKfqto3PW%2FlpeXJxJb38By5itvh8IFHk%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;396&quot; height=&quot;104&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;horizontalArrangement = Arrangement.End&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Arrangement.Center&lt;/span&gt;: 자식들을 가운데에 정렬.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn0gCz/btsKfMJCtRo/Km9AmxpzOwSpzNnKsjSTW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn0gCz/btsKfMJCtRo/Km9AmxpzOwSpzNnKsjSTW1/img.png&quot; data-alt=&quot;horizontalArrangement = Arrangement.Center&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn0gCz/btsKfMJCtRo/Km9AmxpzOwSpzNnKsjSTW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn0gCz%2FbtsKfMJCtRo%2FKm9AmxpzOwSpzNnKsjSTW1%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;400&quot; height=&quot;105&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;horizontalArrangement = Arrangement.Center&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Arrangement.SpaceBetween&lt;/span&gt;: 자식들 사이에 남은 공간을 균등하게 분배.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byuJJn/btsKeDAlSG6/oBWf8swy09MIDsjYfIlkb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byuJJn/btsKeDAlSG6/oBWf8swy09MIDsjYfIlkb1/img.png&quot; data-alt=&quot;horizontalArrangement = Arrangement.SpaceBetween&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byuJJn/btsKeDAlSG6/oBWf8swy09MIDsjYfIlkb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyuJJn%2FbtsKeDAlSG6%2FoBWf8swy09MIDsjYfIlkb1%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;415&quot; height=&quot;109&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;horizontalArrangement = Arrangement.SpaceBetween&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Arrangement.SpaceAround&lt;/span&gt;: 자식들 사이와 가장자리에 동일한 간격을 둠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bThD57/btsKeMDQHoA/ee6kYP1nAykYlue2PC8to1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bThD57/btsKeMDQHoA/ee6kYP1nAykYlue2PC8to1/img.png&quot; data-alt=&quot;horizontalArrangement = Arrangement.SpaceAround&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bThD57/btsKeMDQHoA/ee6kYP1nAykYlue2PC8to1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbThD57%2FbtsKeMDQHoA%2Fee6kYP1nAykYlue2PC8to1%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;411&quot; height=&quot;218&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;horizontalArrangement = Arrangement.SpaceAround&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Arrangement.SpaceEvenly&lt;/span&gt;: 자식들 사이와 부모의 양쪽 가장자리까지 동일한 간격을 둠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SKutS/btsKeqBf0O6/zPiggYkKmtRGOx1seVrJ6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SKutS/btsKeqBf0O6/zPiggYkKmtRGOx1seVrJ6k/img.png&quot; data-alt=&quot;horizontalArrangement = Arrangement.SpaceEvenly&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SKutS/btsKeqBf0O6/zPiggYkKmtRGOx1seVrJ6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSKutS%2FbtsKeqBf0O6%2FzPiggYkKmtRGOx1seVrJ6k%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;411&quot; height=&quot;108&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;horizontalArrangement = Arrangement.SpaceEvenly&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 &lt;b&gt;spaceBy&lt;/b&gt; 함수도 많이 사용하는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Arrangement.spaceBy: 자식들 사이와 부모의 양쪽 가장자리까지 동일한 간격을 둠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHmhSN/btsKgaDjrgL/krDaR2C9HabpFKWqCpkF20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHmhSN/btsKgaDjrgL/krDaR2C9HabpFKWqCpkF20/img.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot; data-is-animation=&quot;false&quot; width=&quot;404&quot; height=&quot;106&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHmhSN/btsKgaDjrgL/krDaR2C9HabpFKWqCpkF20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHmhSN%2FbtsKgaDjrgL%2FkrDaR2C9HabpFKWqCpkF20%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;830&quot; height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5s3Q1/btsKfJ0q399/LLA5WExm4arCOoYI3xpnN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5s3Q1/btsKfJ0q399/LLA5WExm4arCOoYI3xpnN0/img.png&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;218&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5s3Q1/btsKfJ0q399/LLA5WExm4arCOoYI3xpnN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5s3Q1%2FbtsKfJ0q399%2FLLA5WExm4arCOoYI3xpnN0%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;830&quot; height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;horizontalArrangement = Arrangement.spacedBy(32.dp) / horizontalArrangement = Arrangement.spacedBy(4.dp)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;verticalArrangement도 각 아이템의 세로 간격을 조정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Alignment&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Vertical Alignment&lt;/b&gt;은&lt;b&gt; Row&lt;/b&gt;에서 사용하고&lt;b&gt;,&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Horizontal Alignment &lt;/b&gt;은&lt;b&gt; Column&lt;/b&gt;에서 사용한다&lt;b&gt;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;다음은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Row&lt;/b&gt;에서 Alignment를 사용한 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Alignment.Top&lt;/span&gt;: 자식들을 &lt;b&gt;위쪽&lt;/b&gt;으로 정렬.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CpBGl/btsKfq7Ym9N/odG2hA3vkuQ2NuKuXiGLTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CpBGl/btsKfq7Ym9N/odG2hA3vkuQ2NuKuXiGLTK/img.png&quot; data-alt=&quot;verticalAlignment = Alignment.Top&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CpBGl/btsKfq7Ym9N/odG2hA3vkuQ2NuKuXiGLTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCpBGl%2FbtsKfq7Ym9N%2FodG2hA3vkuQ2NuKuXiGLTK%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;427&quot; height=&quot;278&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;verticalAlignment = Alignment.Top&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Alignment.Bottom&lt;/span&gt;: 자식들을 &lt;b&gt;아래쪽&lt;/b&gt;으로 정렬.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGtBY0/btsKeLZfqSO/DA4lksCltbxYvlBgIpHzHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGtBY0/btsKeLZfqSO/DA4lksCltbxYvlBgIpHzHk/img.png&quot; data-alt=&quot;verticalAlignment = Alignment.Bottom&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGtBY0/btsKeLZfqSO/DA4lksCltbxYvlBgIpHzHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGtBY0%2FbtsKeLZfqSO%2FDA4lksCltbxYvlBgIpHzHk%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;415&quot; height=&quot;278&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;verticalAlignment = Alignment.Bottom&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&amp;bull;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;Alignment.CenterVertically&lt;span&gt;: 자식들을 &lt;b&gt;세로 중앙&lt;/b&gt;에 정렬.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bglY1k/btsKgDSIcb3/2ovbw2guEKYHuOkFsAN8B0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bglY1k/btsKgDSIcb3/2ovbw2guEKYHuOkFsAN8B0/img.png&quot; data-alt=&quot;verticalAlignment = Alignment.CenterVertically&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bglY1k/btsKgDSIcb3/2ovbw2guEKYHuOkFsAN8B0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbglY1k%2FbtsKgDSIcb3%2F2ovbw2guEKYHuOkFsAN8B0%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;415&quot; height=&quot;278&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;verticalAlignment = Alignment.CenterVertically&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>안드로이드</category>
      <category>Alignment</category>
      <category>Android</category>
      <category>ARRANGEMENT</category>
      <category>compose</category>
      <category>JetpackCompose</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/12</guid>
      <comments>https://not-yet-defined.tistory.com/12#entry12comment</comments>
      <pubDate>Tue, 22 Oct 2024 20:22:04 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] Inner class사용 시 주의할 점</title>
      <link>https://not-yet-defined.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;내부 클래스(Inner class)를 사용할 때 주의해야하는 이유를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;내부 클래스 종류&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 정적 내부 클래스 (static)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 클래스 인스턴스가 아닌 클래스 자체에 속하는 클래스로 외부 클래스의 멤버 변수만 접근 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1704565255671&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Outer {
    static class StaticInner {
        // ...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 멤버클래스 (non-static)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 클래스의 '인스턴스'에 속함. 외부 클래스의 모든 멤버에 접근 가능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 메모리 누수가 발생할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1704565421644&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Outer {
    class Inner {
        // ...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 지역클래스&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역변수처럼 함수가 실행되는 동안에만 사용 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1704565518508&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Outer {
    void someMethod() {
        class LocalInner {
            // ...
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 익명클래스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번만 쓰고 버릴 클래스를 만들 때 사용. 주로 인터페이스를 구현하거나 추상클래스 사용받아서 사용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서도 외부 클래스의 변수에 접근할 수 있고, 역시 메모리 누수가 발생할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1704565559479&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;new Thread(new Runnable() {
    public void run() {
        // ...
    }
}).start();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 메모리 누수가 어떻게 발생한다는건지 이해가 되지 않았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그래서 내부클래스가 어떻게 메모리 누수를 발생시키는데?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비 정적 멤버 클래스를 생성하려면 반드시 외부 클래스의 인스턴스가 필요하다. 즉 내부 클래스 인스턴스는 외부 클래스 인스턴스에 대한 참조를 가지고 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알겠는데 와닿지 않아.. 눈으로 확인해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1704566163749&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Outer {
    private int value = 10;

    public class Inner {
        public int getValue() {
            return value;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드가 컴파일되면,&lt;/p&gt;
&lt;pre id=&quot;code_1704566188080&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Outer {
    private int value = 10;

    public class Inner {
        final Outer this$0;  // 컴파일러가 추가한 참조

        public Inner() {
            this$0 = Outer.this;  // 생성자에서 외부 클래스에 대한 참조를 초기화
        }

        public int getValue() {
            return this$0.value;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 되는데, this$0이 outer클래스에 대한 참조이다. 그래서 inner에서 이 외부 클래스의 모든 변수에 접근할 수 있는 것!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;익명클래스도 마찬가지.&lt;/p&gt;
&lt;pre id=&quot;code_1704566333598&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class OuterClass {
    private int value = 10;

    public void someMethod() {
        Thread thread = new Thread(new Runnable() {  // Runnable 인터페이스를 구현하는 익명 클래스
            @Override
            public void run() {
                System.out.println(value);  // 외부 클래스의 멤버 변수에 접근
            }
        });
        thread.start();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1704566367196&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class OuterClass {
    private int value = 10;

    public void someMethod() {
        class AnonymousClass implements Runnable {
            final OuterClass this$0;  // 컴파일러가 추가한 참조

            AnonymousClass() {
                this$0 = OuterClass.this;  // 생성자에서 외부 클래스에 대한 참조를 초기화
            }

            @Override
            public void run() {
                System.out.println(this$0.value);
            }
        }

        Thread thread = new Thread(new AnonymousClass());
        thread.start();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f2f7ff; color: #3b3f4e; text-align: left;&quot;&gt;&lt;/span&gt;위 코드를 보면 AnonymousClass&lt;span style=&quot;color: #3b3f4e; text-align: left;&quot;&gt;의 생성자에서 참조가 초기화되며, 이를 통해 &lt;/span&gt;AnonymousClass&lt;span style=&quot;color: #3b3f4e; text-align: left;&quot;&gt;에서 &lt;/span&gt;OuterClass&lt;span style=&quot;color: #3b3f4e; text-align: left;&quot;&gt;의 멤버 변수 &lt;/span&gt;value&lt;span style=&quot;color: #3b3f4e; text-align: left;&quot;&gt;에 접근할 수 있게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;&lt;span style=&quot;background-color: #f2f7ff; color: #3b3f4e; text-align: left;&quot;&gt;코드를 디컴파일하면 컴파일러가 추가한 참조를 볼 수 있다고 한다&lt;/span&gt;&lt;/s&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메모리 누수를 막으려면?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연하지만 명시적으로 참조를 제거하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1704566634678&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class OuterClass {
    public class InnerClass {
        // ...
    }

    public void someMethod() {
        InnerClass inner = new InnerClass();
        // ... 
        // inner 인스턴스가 더 이상 필요하지 않으므로 null로 설정
        inner = null;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 이런 방법은 실수할 가능성이 높아서 피하라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 외에 정적 내부 클래스를 사용하거나,&lt;span data-token-index=&quot;1&quot;&gt;WeakReference를 사용하거나, Android에서 제공하는 Lifecycle-aware Component를 사용하는 방법도 있다. &lt;s&gt;는데 그건 다음에 알아보자&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>JAVA &amp;amp; KOTLIN</category>
      <category>Inner Class</category>
      <category>java</category>
      <category>memory leak</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/9</guid>
      <comments>https://not-yet-defined.tistory.com/9#entry9comment</comments>
      <pubDate>Sun, 7 Jan 2024 03:47:55 +0900</pubDate>
    </item>
    <item>
      <title>[Android] hidden-api</title>
      <link>https://not-yet-defined.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오류 해결을 위해 hidden-api 파일을 수정해야 했던 적이 있다. 검색을 해봤지만 의문투성이였던 이 파일의 역할을 정리해본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hidden-api 를 검색하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;hidden-api.xml&lt;/b&gt;&lt;span style=&quot;background-color: #131314; color: #e3e3e3; text-align: start;&quot;&gt; 파일은 Android 프레임워크에 포함된 Java API 중에서 개발자가 사용해서는 안 되는 API를 나열합니다. 이 파일에는 다음과 같은 API가 포함될 수 있습니다. &lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;라고 나온다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #131314; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;8:1-11:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;8:1-8:15&quot;&gt;&lt;b&gt;개발 중인 API&lt;/b&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;9:1-9:18&quot;&gt;&lt;b&gt;안정적이지 않은 API&lt;/b&gt;&lt;/li&gt;
&lt;li data-sourcepos=&quot;10:1-11:0&quot;&gt;&lt;b&gt;향후 버전의 Android에서 제거될 API&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;를 정의하는 파일이라고 하는데.. 여기에 api를 정의하지 않으면 jar에서 api를 찾지 못했다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이 당시 나는 굉장히 혼란스러웠는데&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 왜... 프레임워크에 있는 소스 그대로 뽑아서 만든 jar파일인데 함수를 못찾아...?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 아니 사용하면 안되는 api를 정의하라며.. 근데 왜 앱은 hidden-api 파일에 정의해야만 찾는거?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결론적으로,&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;hidden-api.txt에 정의된 api는 개발자가 '직접' 사용하면 안된다 -&amp;gt; 내가 코드로 직접 호출하지만 않으면 된다&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;jar를 통해 호출할 api는 hidden-api.txt에 정의해두어야 한다. -&amp;gt; 내가 직접 호출하는게 아니고 jar를 통해 호출하니까 괜찮다.. &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 의미였던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, hidden-api에는 &lt;b&gt;jar에서 사용해야하는 api들이 정의되어있어야 하며, 이 api들은&lt;/b&gt; 개발자가 '직접'사용하면 안된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여담으로 hidden-package 라는 파일도 있는데 여기에는 패키지 단위로 정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 @hide와 비슷하지만 hide와는 달리 강제성을 가진다고 한다.&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;추가 )&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #131314; color: #e3e3e3; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 156px;&quot; border=&quot;1&quot; data-sourcepos=&quot;13:1-17:116&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;background-color: #131314; color: #e3e3e3; text-align: start;&quot;&gt;기준&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;background-color: #131314; color: #e3e3e3; text-align: start;&quot;&gt;hidden-api&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;background-color: #131314; color: #e3e3e3; text-align: start;&quot;&gt;whitelist&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;background-color: #131314; color: #e3e3e3; text-align: start;&quot;&gt;graylist&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px;&quot;&gt;&lt;span style=&quot;background-color: #131314; color: #e3e3e3; text-align: start;&quot;&gt;blacklist&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot; data-sourcepos=&quot;15:1-15:88&quot;&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;15:1-15:4&quot;&gt;용도&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;15:6-15:30&quot;&gt;개발자가 사용해서는 안 되는 API를 나열&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;15:32-15:43&quot;&gt;허용된 항목을 나열&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;15:45-15:73&quot;&gt;허용 또는 차단 여부가 결정되지 않은 항목을 나열&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;15:75-15:86&quot;&gt;차단된 항목을 나열&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot; data-sourcepos=&quot;16:1-16:81&quot;&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;16:1-16:7&quot;&gt;포함 내용&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;16:9-16:32&quot;&gt;Android 프레임워크에 포함된 API&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;16:34-16:42&quot;&gt;허용된 API&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;16:44-16:69&quot;&gt;허용 또는 차단 여부가 결정되지 않은 API&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;16:71-16:79&quot;&gt;차단된 API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot; data-sourcepos=&quot;17:1-17:116&quot;&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;17:1-17:4&quot;&gt;목적&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;17:6-17:31&quot;&gt;개발자가 해당 API를 사용하지 않도록 강제&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;17:33-17:55&quot;&gt;개발자가 해당 API를 사용하도록 허용&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;17:57-17:86&quot;&gt;개발자가 해당 API를 사용해야 하는지 여부를 결정&lt;/td&gt;
&lt;td style=&quot;height: 40px;&quot; data-sourcepos=&quot;17:88-17:114&quot;&gt;개발자가 해당 API를 사용하지 못하도록 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>안드로이드</category>
      <category>Android</category>
      <category>hidden-api</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/8</guid>
      <comments>https://not-yet-defined.tistory.com/8#entry8comment</comments>
      <pubDate>Sun, 15 Oct 2023 18:10:45 +0900</pubDate>
    </item>
    <item>
      <title>[Android] 사용자와 Broadcast</title>
      <link>https://not-yet-defined.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 애플리케이션, 그리고 일반 애플리케이션에서 동일한 동작을 수행하려고 할 때 '사용자' 에 따라서 권한 문제가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오류 해결을 위해 찾아봤던 내용들을 잊어버릴 것 같아 정리해두기로 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;오류 내용 : &lt;/s&gt;&lt;s&gt;~ android.permission.INTERACT_ACROSS_USERS_FULL~이었던 것 같다&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;서비스에서 브로드캐스트를 &lt;span style=&quot;background-color: #1b1b1b; color: #c4c7c5; text-align: start;&quot;&gt;sendBroadcastAsUser(intent, UserHandle.USER_ALL)&lt;/span&gt;로 보내고 있었다. 이때 권한을 가지고 있지 않은 애플리케이션이 이 브로드캐스트를 받으면서 때 앱이 죽는 문제가 발생했다.&amp;nbsp;&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;용어&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 오류를 해결하기 위해서 먼저 헷갈렸던 용어에 대해서 정리를 해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 사용자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시스템 프로세스와 서비스가 사용하는 사용자&lt;/p&gt;
&lt;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;로그인한 사용자와는 별개의 개념?이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시스템 프로세스와 서비스에 필요한 모든 권한을 가지고 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ID는 0. (로그인한 사용자는 1000이상)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시스템 프로세스와 서비스에 필요한 모든 권한을 가짐.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Android os와 관련된 시스템 서비스가 사용하는 사용자.&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;시스템에는 시스템 프로세스, 서비스, 앱, 라이브러리 등이 포함됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ID는 1000이상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시스템 서비스에 필요한 권한을 가짐.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;- 안드로이드 운영체제의 부팅 제어.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fafafa; color: #333333; text-align: start;&quot;&gt;- 시스템 장치와 하드웨어에 엑세스&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인한 사용자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 현재 로그인한 사용자. (멀티 유저 환경에서 각 사용자는 별도의 계정을 가지는데 그 중 하나의 계정으로 로그인한 사용자)&lt;/p&gt;
&lt;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;사용자의 특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각 사용자는 고유한 환경과 권한을 가질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 앱의 실행 권한을 제어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 파일의 소유권을 제어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시스템 설정의 엑세스 제어&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;안드로이드의 사용자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드에서는 사용자의 유형을 구분하는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #131314; color: #e3e3e3; text-align: start;&quot; data-sourcepos=&quot;12:1-16:0&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-sourcepos=&quot;12:1-12:58&quot;&gt;UserHandle.SYSTEM은 시스템 사용자를 나타내는 상수입니다. 이 상수의 값은 0입니다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;13:1-13:74&quot;&gt;UserHandle.CURRENT은 현재 로그인한 사용자를 나타내는 상수입니다. 이 상수의 값은 로그인한 사용자의 ID입니다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;14:1-14:67&quot;&gt;UserHandle.USER_TYPE_GUEST는 게스트 사용자를 나타내는 상수입니다. 이 상수의 값은 2입니다.&lt;/li&gt;
&lt;li data-sourcepos=&quot;15:1-16:0&quot;&gt;UserHandle.USER_TYPE_RESTRICTED는 제한된 사용자를 나타내는 상수입니다. 이 상수의 값은 3입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UserAll은 모든 사용자를 나타내며 상수값은 -1이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;sendBroadcast와 sendBroadcastAsUser&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sendBroadcast는 현재 앱의 사용자에게 브로드캐스트를 날린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sendBroadcastAsUser는 지정된 사용자에게 브로드캐스트를 날린다. -&amp;gt; 이 때 A라는 앱에 broadcastAsUser를 사용해 브로드캐스트를 보내려면 A앱은 지정된 사용자의 권한을 가지고 있어야 한다.&lt;/p&gt;</description>
      <category>안드로이드</category>
      <category>Android</category>
      <category>broadcastAsUser</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/7</guid>
      <comments>https://not-yet-defined.tistory.com/7#entry7comment</comments>
      <pubDate>Sun, 15 Oct 2023 16:54:13 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 전력망을 둘로 나누기</title>
      <link>https://not-yet-defined.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 트리의 간선 하나를 끊어서 두개의 트리를 만드는데, 나눠진 두 트리의 노드 갯수를 최대한 비슷하게 만들었을 때, 두 트리의 노드 갯수 차이를 구하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 처음 봤을 때 hashmap이나 이차원 배열, 재귀 이 세가지 방법으로 풀 수 있을 것 같았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 문제를 꼼꼼히 안 읽어서 입력값이 [부모,자식] 인 줄 알았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;444&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhOVFU/btsuegm6hEK/pkwcq3kSk3qmetNa3FTlQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhOVFU/btsuegm6hEK/pkwcq3kSk3qmetNa3FTlQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhOVFU/btsuegm6hEK/pkwcq3kSk3qmetNa3FTlQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdhOVFU%2Fbtsuegm6hEK%2Fpkwcq3kSk3qmetNa3FTlQK%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;1128&quot; height=&quot;444&quot; data-origin-width=&quot;1128&quot; data-origin-height=&quot;444&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력값이 이차원 배열 형태인데, 각 노드의 숫자는 부모/자식 관계와는 상관이 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, 노드 번호가 연속된다는 보장이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 hashmap이나 Dequeue를 이용한 풀이가 더 효율적일것이다. (&lt;s&gt;하지만 다시짜기 귀찮아..서.. 그냥 제출했다&lt;/s&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼, 입력값을 받아서 노드들을 만들어주고, 연결해서 트리를 만든다음 첫번째 노드를 시작으로 트리를 순회하면서 각 노드의 자식 노드들의 갯수를 구하고, 현재 노드와 부모 노드를 연결한 간선을 끊었을 때 두 트리의 노드 차를 최소가 되도록 갱신하면서 답을 구했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방향성이 없어서 부모/자식을 판단할 수 없기 때문에 각 노드를 방문할 때 마다 visit 처리를 해줬다.&lt;/p&gt;
&lt;pre id=&quot;code_1695059414895&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Splitting_the_power_grid_in_half {

    var answer = 0
    lateinit var arr: MutableList&amp;lt;Node&amp;gt;
    lateinit var visited: MutableList&amp;lt;Boolean&amp;gt;
    var size = 0
    fun solution(n: Int, wires: Array&amp;lt;IntArray&amp;gt;): Int {
        answer = n
        size = n
        arr = MutableList&amp;lt;Node&amp;gt;(101) { Node(it) }
        visited = MutableList&amp;lt;Boolean&amp;gt;(101) { false }

        for (nxt in wires) {
            arr[nxt[0]].child.add(arr[nxt[1]])
            arr[nxt[1]].child.add(arr[nxt[0]])
        }

        find(arr[wires[0][0]])

        return answer
    }

    fun find(current: Node): Int {
        if (visited[current.no]) return 0
        visited[current.no] = true
        var value = 1
        for (nxt in current.child) {
            value += find(nxt)
        }
        answer = min(abs(size - value * 2), answer)
        return value
    }

    data class Node(
            var no: Int,
            var child: MutableList&amp;lt;Node&amp;gt; = mutableListOf()
    )
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘</category>
      <category>프로그래머스</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/6</guid>
      <comments>https://not-yet-defined.tistory.com/6#entry6comment</comments>
      <pubDate>Tue, 19 Sep 2023 02:52:38 +0900</pubDate>
    </item>
    <item>
      <title>[ANDROID] 백그라운드 상태에서 fcm메시지 알림 띄워주기</title>
      <link>https://not-yet-defined.tistory.com/5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;fcm 기능을 구현하면서 공식문서의 애매한 설명때문에 많은 시간을 날린? 나를 위해 구현했던 방법을 간략하게 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;FCM 메시지 유형&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fcm은 알림 메시지(notification)과, 데이터 메시지(data) 두 가지를 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서에는 이 두 메시지의 차이를 이렇게 설명한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;357&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KmoOS/btsudkii1h0/c97OKimETCHZ4fNDmqwk8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KmoOS/btsudkii1h0/c97OKimETCHZ4fNDmqwk8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KmoOS/btsudkii1h0/c97OKimETCHZ4fNDmqwk8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKmoOS%2Fbtsudkii1h0%2Fc97OKimETCHZ4fNDmqwk8K%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;909&quot; height=&quot;357&quot; data-origin-width=&quot;909&quot; data-origin-height=&quot;357&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요약하자면, 메시지를 보낼 때 넘길수 있는 값은 &quot;notification&quot;, &quot;data&quot;가 있는데, 앱이 백그라운드에 있는 경우 &quot;notification&quot;만 넘기거나, &quot;notification&quot; 과 &quot;data&quot;를 모두 넘기는 경우에는 onMessageReceived()가 호출되지 않는다 는 얘기다. &lt;s&gt;(이걸 제대로 읽었어야 했다)&lt;/s&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 백그라운드에서도 받은 데이터를 가지고 알림을 띄워주어야 했기 때문에 fcm메시지를 받을 경우 onMessageReceived()가 호출되어야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 데이터 페이로드만 넘겨주어야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;결과&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론부터 말하자면 &quot;data&quot; 만 넘겨 준 경우 백그라운드에서도 노티가 잘 뜬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rkzI0/btsuh6QGRVf/7dMDa0HQsegcevoY9hQWxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rkzI0/btsuh6QGRVf/7dMDa0HQsegcevoY9hQWxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rkzI0/btsuh6QGRVf/7dMDa0HQsegcevoY9hQWxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrkzI0%2Fbtsuh6QGRVf%2F7dMDa0HQsegcevoY9hQWxk%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;320&quot; height=&quot;114&quot; data-filename=&quot;edited_blob&quot; data-origin-width=&quot;320&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 테스트 해보기 위해 몇가지 방법을 시도했다.&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;Firebase Cloud Messaging API(V1)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 방법은 굉장히 간단한 편이기 때문에 넘기고, 구글에서 권장하는 방법으로 테스트 해보기로 했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ba9NA/btsuejpI2Vy/OfOcWBIeknPmc9z1A5NT5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ba9NA/btsuejpI2Vy/OfOcWBIeknPmc9z1A5NT5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ba9NA/btsuejpI2Vy/OfOcWBIeknPmc9z1A5NT5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBa9NA%2FbtsuejpI2Vy%2FOfOcWBIeknPmc9z1A5NT5k%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;849&quot; height=&quot;495&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;849&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1694932732200&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
    &quot;message&quot;: {
        &quot;token&quot;: &quot;{디바이스 토큰값}&quot;,
        &quot;data&quot;: {
            &quot;title&quot;: &quot;test title&quot;,
            &quot;body&quot;: &quot;test body&quot;,
            &quot;other&quot;: &quot; ~~~~~ &quot;
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 테스트를 해 보려면 디바이스 토큰값, 프로젝트 아이디, gcloud Auth token이 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1694933187839&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task -&amp;gt;
            if (task.isSuccessful) {
               	// 할 작업
                return@OnCompleteListener
            }
        })
        
        
 {Authorization=토큰값} // 이런 형태로 출력됨.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드를 실행해서 로그로 디바이스 토큰값을 확인하고 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;파이어 베이스 프로젝트 설정 창에서 id를 가져왔다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;마지막으로 OAuth id를 만들어서 서비스 엑세스 토큰을 받아 사용했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2430&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P0J0y/btsudZkCh5B/sZ8AJbTtIYlJsgJnY45351/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P0J0y/btsudZkCh5B/sZ8AJbTtIYlJsgJnY45351/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P0J0y/btsudZkCh5B/sZ8AJbTtIYlJsgJnY45351/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP0J0y%2FbtsudZkCh5B%2FsZ8AJbTtIYlJsgJnY45351%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;2430&quot; height=&quot;226&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2430&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock floatLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;40&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmYrHX/btsufmTZId1/tzhGK1oAnM1A5eT3kye9Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmYrHX/btsufmTZId1/tzhGK1oAnM1A5eT3kye9Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmYrHX/btsufmTZId1/tzhGK1oAnM1A5eT3kye9Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmYrHX%2FbtsufmTZId1%2FtzhGK1oAnM1A5eT3kye9Tk%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;606&quot; height=&quot;40&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;40&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 동일하게 python코드로도 테스트를 해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스 계정 탭에서 FirebaseAdmin SDK의 하단에 비공개 키 생성을 해서 받은 파일을 코드에 넣어주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1694934959535&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def fcm_test():
  cred = credentials.Certificate(&quot;서비스 계정 키 파일.json&quot;)
  firebase_admin.initialize_app(cred)
  
  registration_token = &quot;~~~~~~&quot;
 
  message = messaging.Message(
    data={
      &quot;title&quot;: &quot;test data title&quot;,
      &quot;body&quot;: &quot;test data body&quot;,
      &quot;others&quot;: &quot; ~~~ &quot;
    },
    token=registration_token,
  )
  
  response = messaging.send(message)
  print('Successfully sent message:', response)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDh05G/btsuk1IvxjC/j6vYuKfSQu09Zvk8IG8rqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDh05G/btsuk1IvxjC/j6vYuKfSQu09Zvk8IG8rqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDh05G/btsuk1IvxjC/j6vYuKfSQu09Zvk8IG8rqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDh05G%2Fbtsuk1IvxjC%2Fj6vYuKfSQu09Zvk8IG8rqK%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;1698&quot; height=&quot;192&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1698&quot; data-origin-height=&quot;192&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>삽질로그</category>
      <category>Android</category>
      <category>FCM</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/5</guid>
      <comments>https://not-yet-defined.tistory.com/5#entry5comment</comments>
      <pubDate>Sun, 17 Sep 2023 16:35:45 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 연속 부분 수열 합의 개수</title>
      <link>https://not-yet-defined.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;원형 수열의 연속 부분 수열 합으로 만들 수 있는 수의 개수를 구하는 문제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: left;&quot;&gt;단순하게 구현하는 문제인 듯.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1694450732184&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    fun solution(elements: IntArray): Int {
        val origin = elements + elements

        val temp = IntArray(elements.size)
        val caseSet = mutableSetOf&amp;lt;Int&amp;gt;()

        for (i in 0 until elements.size){
            for(j in i until i+elements.size){
                temp[j-i] += origin[j]
            }
            caseSet.addAll(temp.toSet())
        }

        return caseSet.size
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 풀고 다른사람 풀이를 보는데 slice랑 map을 사용해서 더 간결하게 푼 풀이를 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 코틀린 문법이 서툰 것 같다. 빨리 익숙해져야지&lt;/p&gt;
&lt;pre id=&quot;code_1694450985482&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;     fun solution(elements: IntArray): Int {
        val doubling = elements+elements
        val result = HashSet&amp;lt;Int&amp;gt;()
        for (i in 1..elements.size) {
            result.addAll((elements.indices).map {
                doubling.slice(it until it + i).sum()
            })
        }
        return result.size
    }&lt;/code&gt;&lt;/pre&gt;</description>
      <category>알고리즘</category>
      <category>programmers</category>
      <category>구현</category>
      <category>알고리즘</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/4</guid>
      <comments>https://not-yet-defined.tistory.com/4#entry4comment</comments>
      <pubDate>Tue, 12 Sep 2023 01:51:13 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 무인도 여행</title>
      <link>https://not-yet-defined.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1694369148342&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution {
	lateinit var map :Array&amp;lt;IntArray&amp;gt;
    val direction = arrayOf(Point(1,0), Point(-1,0), Point(0,1), Point(0,-1))
    var answer = mutableListOf&amp;lt;Int&amp;gt;()
    
    fun solution(maps: Array&amp;lt;String&amp;gt;): IntArray {

        map = Array(maps.size){IntArray(maps[0].length){-1} }

        for(r in 0 until maps.size){
            for(c in 0 until maps[r].length){
                val ch = maps[r][c]
                if(ch.isDigit()) map[r][c] = Character.getNumericValue(ch)
            }
        }

        for (r in map){
            println(r.toString())
        }

        for(y in 0 until map.size){
            for(x in 0 until map[y].size){
               if(map[y][x]&amp;lt;=0) continue
                dfs(y,x)
            }
        }
        answer.sort()
        return if(answer.isEmpty()) intArrayOf(-1) else answer.toIntArray()
    }

    fun dfs(sy:Int,sx:Int){
        var sum = map[sy][sx]
        val q :ArrayDeque&amp;lt;Point&amp;gt; = ArrayDeque()
        q.add(Point(sy,sx))
        map[sy][sx]=0
        var ny = 0
        var nx = 0
        var start:Point
        while (q.isNotEmpty()){
            start = q.removeFirst()

            for (d in direction){
                ny = d.y + start.y
                nx = d.x + start.x

                if(isOutOfBound(ny,nx) || map[ny][nx]&amp;lt;=0) continue
                q.add(Point(ny,nx))
                sum +=  map[ny][nx]
                map[ny][nx] = 0
            }
        }

        answer.add(sum)
    }

    fun isOutOfBound(y:Int,x:Int):Boolean{
        return if(x&amp;lt;0 || y&amp;lt;0 || x&amp;gt;=map[0].size || y&amp;gt;= map.size) true else false
    }

    data class Point(val y:Int,val x:Int)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>BFS</category>
      <category>프로그래머스</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/3</guid>
      <comments>https://not-yet-defined.tistory.com/3#entry3comment</comments>
      <pubDate>Mon, 11 Sep 2023 03:07:18 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘] NextPermutation</title>
      <link>https://not-yet-defined.tistory.com/2</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;알고리즘을 풀다 보면 순열,조합을 사용하게 되는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서도 순열/조합의 순서나 범위가 필요한 경우가 있는데, 이 때 NextPermutaion이 좋은 방법이 될 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨셉은 모든 수를 정렬해 두고 맨 뒤부터 앞쪽을 이동하며 꼭짓점을 찾고,꼭짓점 앞의 수 'a' 와, 맨 뒤에서 부터 앞으로 하나씩 움직이며 첫번째로 만난 a보다 큰 수'b' 를 swap하면서 하나씩 값을 증가시켜서 모든 가능한 수를 만든다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;term1.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12345 가 있다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨 뒤에서부터 앞으로 움직이며 본인 앞의 수가 본인보다 작은 경우(꼭짓점)를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 여기서 number[i] == 5이고, a는 그 앞의 수인 4이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 뒤로 돌아가서 a보다 큰 수를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 b는 5이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a와 b의 자리를 바꾸면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12354 가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 방식으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;term2.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;꼭짓점은 5, a=3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;b는 4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘을 바꾸면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12453&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a위치 뒷쪽 숫자를 오름차순으로 정렬하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12435&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;term3.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12453&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;term4.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;12534&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반복하다보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;54321이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1694346416567&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class NextPermutationTest { // nPn만 가능
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		int[] input = new int[N];

		for (int i = 0; i &amp;lt; N; i++) {
			input[i] = sc.nextInt();
		}
		
		//전처리 : 순열에 쓰일 수들을 오름차순 정렬
		Arrays.sort(input);
		do {
			System.out.println(Arrays.toString(input));
		}while(np(input));
	}

	public static boolean np(int[] numbers) {// numbers배열의 상태를 근거로 다음 순열 생성, 다음 순열 존재하면 true, 아니면 false

		//1. 꼭대기 찾기(맨 뒤부터 올라가는 모습인지 확인, 만약 줄어드는 부분이 생기면 얘가 꼭대기고 그 앞에 작은 값이 교환 대상.
		int N = numbers.length;
		
		int i= N-1;
		while(i&amp;gt;0 &amp;amp;&amp;amp; numbers[i-1]&amp;gt;=numbers[i]) --i;
		
		
		if(i==0) return false; // 다음 순열을 만들 수 없는 이미 가장 큰 순열의 상태.
		
		//2. 꼭대기의 바로 앞자리(i-1)의 값을 크게 만들 교환 값 뒤쪽에서 찾기.
		int j=N-1;
		while(numbers[i-1]&amp;gt;= numbers[j]) --j;
		
		//3. i-1위치값과 j 위치값 교환
		swap(numbers, i-1, j);
		
		//4. i위치부터 맨뒤까지의 수열을 가장 작은 형태의 오름차순으로 변경
		int k=N-1;
		while(i&amp;lt;k) {//맨 뒤부터 i-1보다 큰 애랑 변경할 애를 찾는다.
			swap(numbers, i++,k--);
		}
		
		return true;
	}
	
	public static void swap(int[] numbers,int i,int j) {
		int tmp = numbers[i];
		numbers[i]= numbers[j];
		numbers[j] = tmp;
	}
}

//결과
4
1 2 3 4
[1, 2, 3, 4] // arr[i-1] &amp;lt; arr[i] 이면 arr의 맨 뒤의 숫자부터 arr[i-1]보다 큰 수를 만날때 swap한다.  
[1, 2, 4, 3] // 다시 맨 뒤부터 arr[i-1] &amp;lt; arr[i] 만족하는애 찾고 swap. i 뒤는 오름차순으로 만들고
[1, 3, 2, 4] // 다시 맨 뒤부터 arr[i-1] &amp;lt; arr[i] 만족하는애 찾고 swap ... 반복
[1, 3, 4, 2]
[1, 4, 2, 3]
[1, 4, 3, 2]
[2, 1, 3, 4]
[2, 1, 4, 3]
[2, 3, 1, 4]
[2, 3, 4, 1]
[2, 4, 1, 3]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법과 동일하게 0과 1로 이루어진 숫자를 이용하면 조합 구현도 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 000111 이라면, 6개중 3개를 뽑든 모든 경우의 수를 구할 수 있다.&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>NextPermutaion</category>
      <category>알고리즘</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/2</guid>
      <comments>https://not-yet-defined.tistory.com/2#entry2comment</comments>
      <pubDate>Sun, 10 Sep 2023 22:50:16 +0900</pubDate>
    </item>
    <item>
      <title>[Android] 중첩된 뷰가 닫히지 않을 때</title>
      <link>https://not-yet-defined.tistory.com/1</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;근래 가장 나를 힘들게 했던 문제가 있었다. 아마 나중에 또 만나게 될 것 같아서 정리해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 구현해야 하는 기능 중 하나는 특정 주기마다 백그라운드에서 작업을 하고 완료되면 사용자에게 팝업으로 알리는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 팝업을 띄우는 데 까지는 크게 어려움이 없었다. 하지만 해결하기 어려웠던 문제 중 하나가 바로 팝업이 중첩되었을 때 비정상적인 동작을 하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대략, 특정 동작이 완료되면 -&amp;gt; 서비스를 실행하고, 조건에 맞는 분기를 타서 -&amp;gt; 뷰를 뿌려주고 -&amp;gt; 사용자의 동작에 따른 작업 + 뷰 닫기+ 서비스 종료 와 같은 프로세스를 타는 기능이다. 그런데 마지막 팝업의 닫기가 동작하지 않았다 (&lt;s&gt;뷰의 다른 버튼들이나 창 드래그는 잘 됐다. 하지만 이 문제의 뷰는 재부팅 할 때 까지 사라지지 않았다&lt;/s&gt;).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은 각 팝업에 대한 정보를 별도로 관리하지 않아서 발생한 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 보면 굉장히 당연한 문제였는데 찾는게 쉽지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 하나의 서비스가 시작되면서 A 뷰를 그렸다고 치면, 다시 서비스가 호출되면서 A 뷰에 대한 참조를 잃고 B뷰를 다시 그린것이다. 그래서 닫기를 눌렀을 때 B만 닫히고 서비스를 종료해버리면서 A뷰가 남게 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 서비스가 실행되며 생성되는 뷰와 뷰의 상태(파라미터, 위치값) 을 가지는 객체를 만들고, 이 객체들을 관리하는 리스트를 서비스 내의 전역 변수로 만들었다. 그리고 서비스가 실행되며 뷰를 생성할 때 마다 새로운 객체를 만들고 해당 객체에 저장하여 관리하도록 수정하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1694345377371&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    data class PopupInfo(
        val rootView: View,
        val params: WindowManager.LayoutParams,
        var initialX: Float = 0f,
        var initialY: Float = 0f
    )&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1694345058606&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    private val activePopups: MutableList&amp;lt;PopupInfo&amp;gt; = mutableListOf()

	override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        ...
        val popupInfo = PopupInfo(rootView, params!!)
        ...
        activePopups.add(popupInfo)
    }


    fun closeViewOrStopService(popupView: View){
		...
                try {
                    windowManager!!.removeView(popupView)
                    activePopups.removeIf { it.rootView == popupView }
                } catch (e: IllegalArgumentException) {
                    e.printStackTrace()
                }
		...
        if(activePopups.isNullOrEmpty()) stopSelf()
    }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>삽질로그</category>
      <category>Android</category>
      <category>삽질로그</category>
      <author>un-defined</author>
      <guid isPermaLink="true">https://not-yet-defined.tistory.com/1</guid>
      <comments>https://not-yet-defined.tistory.com/1#entry1comment</comments>
      <pubDate>Sun, 10 Sep 2023 20:44:17 +0900</pubDate>
    </item>
  </channel>
</rss>