<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bom-bakbang.log</title>
        <link>https://velog.io/</link>
        <description>Best Ongoing Man, BOM. 최선의 자세로 살아 가고자 합니다. 모두의 마음에 봄의 씨앗이 자라길</description>
        <lastBuildDate>Thu, 01 Feb 2024 00:42:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bom-bakbang.log</title>
            <url>https://velog.velcdn.com/images/bom-bakbang/profile/1b926e00-3565-4a5c-beb7-9efb3c8408bd/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bom-bakbang.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bom-bakbang" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[AndroidRoadMap] Parcelable/Bundle은 무엇인가?]]></title>
            <link>https://velog.io/@bom-bakbang/AndroidRoadMap-ParcelableBundle%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@bom-bakbang/AndroidRoadMap-ParcelableBundle%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Thu, 01 Feb 2024 00:42:09 GMT</pubDate>
            <description><![CDATA[<p><a href="https://developer.android.com/guide/components/activities/parcelables-and-bundles">https://developer.android.com/guide/components/activities/parcelables-and-bundles</a></p>
<p>짧게 말하면 프로세스간 혹은 액티비티간 데이터 전달을 위해 설게된 객체다.</p>
<h3 id="특징">특징</h3>
<ul>
<li>기본적으로 데이터를 전송할때는 Bundle 활용. OS에서 인지되는 Primitive 형태로 사용(ex: <code>Int</code>, <code>String</code>, <code>Boolean</code> 등)</li>
<li>여러 Primitive가 담긴 Custom class를 사용하고 싶으면, 해당 클래스에 <code>Parcelable</code>을 정의하면 됨</li>
<li>n KB 정도로 담길 수 있는 사이즈의 제약이 있으니 너무 많은 데이터를 전송하는것은 주의해야함. =&gt; <code>TransactionTooLargeException</code> 발생위험. DB 활용할 것<ul>
<li>한 프로세스 내부에서 발생하는 모든 데이터 전달은 하나의 제한된 버퍼 사이즈를 가지기 때문에(1MB), 상황에 따라 몇 kb 이상으로 커지지 않았는데도 발생할 수 있을듯. (시스템과의 상호작용하는 부분까지 공유되기 때문)</li>
<li>특히나 <code>savedInstanceState</code> 같은 경우는 유저의 동작에 따라 언제 복구될지 모르기에 시스템에서 데이터를 들고있어서, 50k 미만으로 저장하기를 권장하고 있음.</li>
</ul>
</li>
<li>프로세스간 전달할 때(=서로 다른 앱간에 전달할 때)는 custom class 활용하지 않고 Primitive 형태로만 활용하는 게 좋음. 이유는 좀 더 공부+이해 필요</li>
</ul>
<h3 id="추가로-알아보고-정리할-것">추가로 알아보고 정리할 것</h3>
<ul>
<li>🤔 stripping?</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AndroidRoadMap] Android 디버깅]]></title>
            <link>https://velog.io/@bom-bakbang/AndroidRoadMap-Android-%EB%94%94%EB%B2%84%EA%B9%85</link>
            <guid>https://velog.io/@bom-bakbang/AndroidRoadMap-Android-%EB%94%94%EB%B2%84%EA%B9%85</guid>
            <pubDate>Mon, 29 Jan 2024 23:19:32 GMT</pubDate>
            <description><![CDATA[<p>참고자료: <a href="https://tv.naver.com/v/15354345?playlistNo=629240">https://tv.naver.com/v/15354345?playlistNo=629240</a></p>
<h2 id="에뮬레이터-기능">에뮬레이터 기능</h2>
<ul>
<li>PC에서 클립보드 복사하여 복붙가능: 초기 에뮬엔 한글 없는데 한글 복붙하기 편함</li>
<li>지도 위치 정보 기반 시뮬레이팅 가능: KML 기반 좌표 추적을 설명하시는데 KML이 뭔지는 추가확인 필요</li>
<li>여러 디바이스 dpi별 동시확인 가능: 폴더블 포함. 접히고 펴지는것도 확인가능</li>
<li>에뮬 카메라를 웹캠으로 연결가능: 초기 생성시만 설정에서 세팅가능</li>
<li>스냅샷 가능: 앱 DB 마이그레이션 테스트시 용이. 앱의 업데이트 버전 전후로 의도대로 안되면 복구 등</li>
<li>스크린 레코딩: 부드럽게 잘 됨</li>
<li>전화: 잘 됨</li>
<li>녹음: Virtual sensor 활성화 해야 됨</li>
<li>Virtual sensor: 기기 돌리며 테스트</li>
<li>x86, x64 실기기 구하기 어려운데 테스트로 활용가능</li>
</ul>
<h2 id="테스트폰-개발자-옵션">테스트폰 개발자 옵션</h2>
<ul>
<li>USB Debugging: ON<ul>
<li>당연한거. QR코드 통한 무선연결 디버깅도 가능.</li>
</ul>
</li>
<li>Stay Awake: ON<ul>
<li>충전 연결되어 있으면 안 꺼짐</li>
</ul>
</li>
<li>Enable view attribute inspection: ON<ul>
<li>Debugging 시 Layout Inspector에 특정 속성이 숨겨지고 아니고가 달라질 수 있음</li>
</ul>
</li>
<li>Show taps: ON<ul>
<li>터치 위치 확인가능</li>
</ul>
</li>
<li>Strict mode enabled: ON<ul>
<li>메인쓰레드에서 과도한 작업하면 빨간색으로 번쩍거림</li>
</ul>
</li>
<li>Always show crash dialog: ON<ul>
<li>문제가 발생하면 알아차려야 하니 켜두기</li>
<li>같은 이유로 background ANR, notification channel 모두 ON</li>
</ul>
</li>
</ul>
<h2 id="성능체크">성능체크</h2>
<ul>
<li>LeakCanary<ul>
<li>Activity 생명주기 기반. 디버그시에만 추가되니 유용하나 부족한 부분이 존재하긴함</li>
</ul>
</li>
<li>Memory Profiler in AS<ul>
<li>Heap Dump &gt; Activity/Fragment Leaks 체크해서 확인</li>
</ul>
</li>
<li>그 외 Systrace, Lint 추가 탐구해두면 좋음</li>
<li>Thread Dump: 프로세스 안에서 ANR 분석해나갈때 도움될 수 있음</li>
<li>Fill RAM memory 앱: LowMemory 상태에서 어떻게 동작하는지 강제로 만들어서 테스트 가능</li>
<li>Fill Device Disk 앱: 스토리지 부족 상태에서 어떻게 동작하는지 강제로 만들어서 테스트 가능</li>
</ul>
<h2 id="framework-이해">Framework 이해</h2>
<ul>
<li>SystemService에 연결된 것들이 어떻게 동작하는지 탐구 필요(ex: <code>Context.getSystemService()</code></li>
<li>SystemService는 주로 SystemServer 프로세스에 포함되어 있음</li>
<li>Application의 경우에는 XXX Manager, SystemSevice의 경우에는 XXX Manager Service형태로 네이밍 되어있음</li>
<li>둘은 또 각각 Proxy / Stub 이라는 이름으로 AIDL(Android Interface Definition Language)를 통해 Binder IPC 통신을 함 = 일종의 전처리 코드
<img src="https://velog.velcdn.com/images/bom-bakbang/post/579d1bf0-ac18-4e11-bae6-bb356de063e3/image.png" alt=""><img src="https://velog.velcdn.com/images/bom-bakbang/post/8b1cc5bc-0fda-480c-b71b-8ee1732d6187/image.png" alt=""></li>
<li>프레임워크 소스: <a href="https://android.googlesource.com/platform/frameworks/base">https://android.googlesource.com/platform/frameworks/base</a></li>
<li>dumpsys 명령어를 통해 서비스 상태를 확인할 수 있는 이유: Service 클래스들이 기본적으로 dump 메소드를 충실히 구현하고 있기 때문
<img src="https://velog.velcdn.com/images/bom-bakbang/post/3c08895d-1b9b-40fa-ba1e-8c38454a8243/image.png" alt=""></li>
<li>안드로이드 특정 버전들에 문제있는지 확인하는곳: <a href="https://source.android.com/setup/contribute/report-bugs">https://source.android.com/setup/contribute/report-bugs</a></li>
</ul>
<h2 id="다른앱-분석">다른앱 분석</h2>
<ul>
<li>Dexplorer 앱: Manifest 파일들 바로 확인</li>
<li>Apk Extractor 앱: 디바이스에 설치된 APK 파일을 외부저장소에 복사해줌</li>
<li>Apk Analyser(in AS): 바로 APK 올려서 확인 클래스들 분석 가능</li>
<li>디컴파일: <a href="https://ibotpeaches.github.io/Apktool">APK Tool</a>, <a href="https://github.com/pxb1988/dex2jar">Dex2Jar</a>, <a href="https://java-decompiler.github.io/">JD-GUI</a></li>
<li>Network Connections 앱: 앱별로 연결된 네트워크 물리적 위치/권한 등 확인</li>
<li>Developer Assistant 앱: 현재 화면에 보여지는 앱의 레이아웃 확인. 화면구조/속성/ResId/속한 액티비티. 유료.</li>
<li>Native Libs Monitor 앱: 네이티브 개발시</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android RoadMap] 안드로이드 스튜디오 기능 살펴보기]]></title>
            <link>https://velog.io/@bom-bakbang/Android-RoadMap-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4-%EA%B8%B0%EB%8A%A5-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@bom-bakbang/Android-RoadMap-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%8A%9C%EB%94%94%EC%98%A4-%EA%B8%B0%EB%8A%A5-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 28 Jan 2024 23:37:13 GMT</pubDate>
            <description><![CDATA[<p>개인적으로 유용하게 쓰는 안드로이드 스튜디오내의 단축키 및 기능들을 정리합니다.</p>
<h3 id="use-camelhumps-words-선택할때-camel-word-단위로-선택됨">Use &quot;CamelHumps&quot; words: 선택할때 camel word 단위로 선택됨</h3>
<p><img src="https://velog.velcdn.com/images/bom-bakbang/post/2c7e0fec-332f-467b-ba8b-298a27223bf2/image.png" alt=""></p>
<h3 id="quick-definition-정의된곳-구현부-바로-확인cmd--y">Quick Definition: 정의된곳 구현부 바로 확인(cmd + Y)</h3>
<p><img src="https://velog.velcdn.com/images/bom-bakbang/post/fd44f14d-0cb2-4f03-8fc6-9245dc8ffc7e/image.png" alt=""></p>
<h3 id="최근-수정된-파일-확인-cmde-최근-가져다댄-마우스-커서-수정위치-확인-shiftcmde">최근 수정된 파일 확인: cmd+E, 최근 가져다댄 마우스 커서 수정위치 확인: shift+cmd+E</h3>
<p><img src="https://velog.velcdn.com/images/bom-bakbang/post/d809906a-21d8-456c-b3e0-e332dc103960/image.png" alt=""></p>
<h3 id="surround-with-특정-구문을-자주-사용하는-블락으로-묶음option--cmd--t">Surround with: 특정 구문을 자주 사용하는 블락으로 묶음(option + cmd + T)</h3>
<p><img src="https://velog.velcdn.com/images/bom-bakbang/post/a8585612-5576-4582-8b8c-4b0eea2a2bcb/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[내가 짠 코드 줄 수 확인하기]]></title>
            <link>https://velog.io/@bom-bakbang/%EB%82%B4%EA%B0%80-%EC%A7%A0-%EC%BD%94%EB%93%9C-%EC%A4%84-%EC%88%98-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@bom-bakbang/%EB%82%B4%EA%B0%80-%EC%A7%A0-%EC%BD%94%EB%93%9C-%EC%A4%84-%EC%88%98-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 05 Dec 2023 05:11:52 GMT</pubDate>
            <description><![CDATA[<p>코틀린만 보려면
<code>git ls-files | grep &#39;\.kt&#39; | xargs wc -l</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정규표현식 활용법 정리]]></title>
            <link>https://velog.io/@bom-bakbang/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%ED%99%9C%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@bom-bakbang/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D-%ED%99%9C%EC%9A%A9%EB%B2%95-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 27 Oct 2023 02:21:50 GMT</pubDate>
            <description><![CDATA[<p>필요할때마다 덧붙이며 정리해나갈 예정.
안드로이드 스튜디오에서 실제 검색 잘 되는 케이스로 정리.</p>
<ul>
<li>Stub으로 시작하며, 뒤에 1개이상의 문자가 더 붙는 경우: <code>\bStub\w+?</code></li>
<li>sp로 끝나는 케이스 검색하고 싶은 경우: <code>\bsp\b</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ConstraintLayout 내부에서 ViewStub 사용시 주의할 점]]></title>
            <link>https://velog.io/@bom-bakbang/ConstraintLayout-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-ViewStub-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98%ED%95%A0-%EC%A0%90</link>
            <guid>https://velog.io/@bom-bakbang/ConstraintLayout-%EB%82%B4%EB%B6%80%EC%97%90%EC%84%9C-ViewStub-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98%ED%95%A0-%EC%A0%90</guid>
            <pubDate>Fri, 20 Oct 2023 06:32:03 GMT</pubDate>
            <description><![CDATA[<p>알고나니 당연한거긴 한데, <code>ViewStub</code> 개념 자체가 <code>inflate</code> 대상이 되는 뷰를 가져다 끼우는것.
그래서 아래처럼 <code>ConstraintLayout</code> 내부에 선언한 뷰의 id를 가지고 상대적 위치를 조정하려고 하면 실패한다. (아래에서 <code>view_stub_id</code>)</p>
<pre><code class="language-xml">&lt;ViewStub
    android:id=&quot;@+id/view_stub_id&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;
    app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;

&lt;androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;0dp&quot;
    app:layout_constraintTop_toBottomOf=&quot;@id/view_stub_id&quot;
    app:layout_constraintBottom_toBottomOf=&quot;parent&quot;/&gt;</code></pre>
<p><code>view_stub_id</code>에 <code>inflate</code> 되는 뷰가 아래와 같았고 (<code>inflated_id_one</code>)</p>
<pre><code class="language-xml">&lt;FrameLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:id=&quot;@+id/inflated_id_one&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;&gt;</code></pre>
<p>실제 <code>infalte</code> 이후에 <code>LayoutInspector</code> 가지고 살펴보니
<code>inflate</code> 된 뷰는 <code>view_stub_id</code>가 아니라 <code>inflated_id_one</code>을 id로 가지고 있었다.</p>
<p>제대로 조정하고 싶다면, 상대위치 조정에 사용될 id를 <code>inflated_id_one</code> 으로 바꿔야한다.</p>
<pre><code class="language-xml">&lt;ViewStub
    android:id=&quot;@+id/view_stub_id&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;wrap_content&quot;
    app:layout_constraintTop_toTopOf=&quot;parent&quot; /&gt;

&lt;androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;0dp&quot;
    app:layout_constraintTop_toBottomOf=&quot;@id/inflated_id_one&quot;
    app:layout_constraintBottom_toBottomOf=&quot;parent&quot;/&gt;</code></pre>
<hr>
<p>개발을 하다보면 <code>ViewStub</code>에 <code>inflate</code>될 뷰를 코드에서 동적으로 <code>layoutResource</code> 값 할당을 통해 설정할 수 있다.
별개의 루트뷰의 id가 같으면 상관없겠지만, 다르다면 이 경우 위에서 상대위치 조정에 사용될 id를 어떻게 해야되는지 고민할 수 있다.
생각해본 해결법은 아래처럼 <code>Barrier</code>를 사용하고, <code>Barrier</code>의 id를 가지고 상대위치 조정에 활용하면 잘 된다.</p>
<pre><code class="language-xml">&lt;androidx.constraintlayout.widget.Barrier
    android:id=&quot;@+id/inflate_barrier&quot;
    android:layout_width=&quot;wrap_content&quot;
    android:layout_height=&quot;match_parent&quot;
    app:barrierDirection=&quot;bottom&quot;
    app:constraint_referenced_ids=&quot;inflated_id_one, inflated_id_two&quot; /&gt;

&lt;androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;0dp&quot;
    app:layout_constraintTop_toBottomOf=&quot;@id/inflate_barrier&quot;
    app:layout_constraintBottom_toBottomOf=&quot;parent&quot;/&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android 프로모션 코드 적용 시 신경쓸 것 in 구독]]></title>
            <link>https://velog.io/@bom-bakbang/Android-%ED%94%84%EB%A1%9C%EB%AA%A8%EC%85%98-%EC%BD%94%EB%93%9C-%EC%A0%81%EC%9A%A9-%EC%8B%9C-%EC%8B%A0%EA%B2%BD%EC%93%B8-%EA%B2%83-in-%EA%B5%AC%EB%8F%85</link>
            <guid>https://velog.io/@bom-bakbang/Android-%ED%94%84%EB%A1%9C%EB%AA%A8%EC%85%98-%EC%BD%94%EB%93%9C-%EC%A0%81%EC%9A%A9-%EC%8B%9C-%EC%8B%A0%EA%B2%BD%EC%93%B8-%EA%B2%83-in-%EA%B5%AC%EB%8F%85</guid>
            <pubDate>Sun, 15 Oct 2023 22:57:42 GMT</pubDate>
            <description><![CDATA[<p>이 글을 시작으로, 앞으로 개발 관련된 글을 마구잡이 잡탕으로 적어가려고 한다. 기술 관련이라 틀린 내용이 있으면 안 된다는 부담감 때문에 글을 자주 적지 못했는데, 일단 적고 틀린걸 발견하면 나중에 고치면 된다는 마인드로 시작 예정.</p>
<p>오늘의 글은 안드로이드 개발시 인앱상품으로 구독이 존재하고, 해당 구독상품에 프로모션 코드를 통해 무료 사용을 제공하려고 할 때 고려해야할 내용을 공식 가이드에서 뽑아내 정리한 내용이다. 이 글에선 간략한 개념만 다루고 실제 코드 레벨에서의 구현 방식은 다음 글에서 다룰예정.</p>
<p>//TODO: 다음 글 작성하고 이 글 수정하기.</p>
<p>영어로 된 공식 가이드가 궁금하신 분은 
<a href="https://developer.android.com/google/play/billing/promo">https://developer.android.com/google/play/billing/promo</a>
이곳을 참조하시면 됩니다.</p>
<hr>
<p>프로모션 코드에는 두 종류가 있다. 이것을 알고가야함.</p>
<ul>
<li>One-time use codes: 사용자가 한 번만 사용가능한, 자동 생성되는 Redeem code. Play Store 에서 적용할 수도 있고 인앱으로도 가능</li>
<li>Custom Code: 사전 정의된 제한값까지 반복적으로 redeem 가능한 코드. 구독에만 사용가능하며 이전에 구독한 경험이 없는 사용자에게만 적용가능.</li>
</ul>
<h4 id="구독에-적용시-참고할점">구독에 적용시 참고할점</h4>
<ul>
<li>구독에 무료 이용기간을 할당할 수 있을뿐, 무료 구독상품을 제공하는것이 아님<ul>
<li>즉 프로모션을 통해 제공한 무료 이용기간이 끝나면, 사용자는 자동으로 결제됨 = 사용자에게 결제수단이 존재함을 구글 Play 측에서 검증하려고함 = 사용자는 결제수단 등록이 필요함</li>
<li>만약 프로모션으로 제공하고 사용자가 자동결제가 되지 않도록 하려면, 결제수단까지 등록한 뒤 구독결제를 하고 <strong>직접 취소해야함</strong>. 취소되어도 <strong>만료된것</strong>은 아니기 때문에 사용자는 구독상품이 제공하는 기능들은 사용가능</li>
<li>자세한 설명은 추가 가이드 참고: <a href="https://developer.android.com/google/play/billing/subscriptions#promote">https://developer.android.com/google/play/billing/subscriptions#promote</a></li>
</ul>
</li>
<li>기존의 무료 이용기간을 연장할 수 없음. 프로모션 코드를 적용하지 않고 무료 이용기간이 포함된 구독 상품을 이미 사용중일때, 프로모션 코드를 적용하면 원래의 무료 이용기간을 덮어쓰면서 중복해서 적용됨.</li>
<li>하나의 구독 상품에 하나의 프로모션 코드만 적용가능. (single base plan or offer)</li>
<li>프로모션은 생성후 최대 1년까지만 유효하고, 그 사이에 유저가 코드입력해야함.</li>
</ul>
<h4 id="제한-사항">제한 사항</h4>
<ul>
<li>구독상품에 대해서는 하나의 구독마다 분기당 1만개까지 코드 사용가능. 단 Custom code가 아니라 One-time use code 로만 가능.</li>
<li>구독상품을 Custom code로 만들면 2000번에서 99999번 까지만 반복 제한 설정가능. (🤔 Custom code에 대해선 개수 제한이 적혀있지 않음. 추후 작업하며 확인되면 업데이트 예정)</li>
<li>한 번 프로모션을 생성하면, 프로모션 코드개수 수정 불가. 다른 타입으로도 변경 불가.</li>
<li>분기에 프로모션 코드를 모두 소진하지 못하면, 접근권한을 잃어버림. 남은 개수가 다음 분기로 넘어가지 않음.</li>
</ul>
<h4 id="구현방식---구독">구현방식 - 구독</h4>
<ul>
<li>Custome code: 인앱으로만 구현 가능</li>
<li>One-time use code: 인앱/Play store 모두 가능</li>
<li>사용자가 코드를 사용한 뒤, 해당 코드가 적용된 구독 상품을 결제 진행까지 해야함. 코드적용된 무료 기간이 끝나면 자동으로 다음 결제 진행.</li>
</ul>
<hr>
<p>모지또는 현재 단일 구독상품인 GOLD 라는 BM이 있고 월결제/연결제를 지원함. 즉, 안드로이드 구독상품의 단어 정의를 적용하면 아래와 같음.</p>
<ul>
<li>base plan = GOLD</li>
<li>offer = monthly / annual
이 상황에서 과거 앱 버전(아예 package name이 다른, 별도 앱)의 인앱 단일상품을 구매했던 사용자들에게 현 버전의 GOLD 구독을 1년 무료로 제공할 예정. 아마 annual offer를 기준으로 프로모션을 생성하면 될 듯 한데, 다 적용하고 실제 구현방식을 정리할 때 재정리 예정!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[여러분이 출시한 앱이 조작되고 있을 수 있습니다 (feat. Frida)]]></title>
            <link>https://velog.io/@bom-bakbang/%EB%82%B4-%EC%95%B1%EC%9D%B4-%EC%A1%B0%EC%9E%91%EB%90%A0-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C-YES-FRIDA</link>
            <guid>https://velog.io/@bom-bakbang/%EB%82%B4-%EC%95%B1%EC%9D%B4-%EC%A1%B0%EC%9E%91%EB%90%A0-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C-YES-FRIDA</guid>
            <pubDate>Thu, 14 Sep 2023 14:02:39 GMT</pubDate>
            <description><![CDATA[<p>여러분, 혹시 내가 만든 앱이 조작될 가능성이 있지 않을까 하는 상상 한 번쯤은 해보시지 않았나요? 저는 안드로이드 개발자긴 하지만, 보안 지식이 그렇게 많진 않아 단순히 &#39;세상에 완벽한 방어막은 없겠지&#39; 정도로 순진하게 생각하고 살았는데요. 그랬던 제가 최근 업무를 하다가 알게 된 <a href="https://frida.re/">Frida</a>라는 툴킷으로 새롭고 놀라운 경험을 하게 되었습니다. 그건 바로 앱 자체를 수정이나 조작하는 것이 아니라, 정상적으로 앱이 실행되는 상황임에도 <strong>동적 후킹으로 앱의 동작을 조작할 수 있다</strong>는 것이었어요.</p>
<p>🤔 도대체 어떻게 하는거야?</p>
<p>Frida는 DBI(Dynamic Binary Instrumentation) 프레임워크입니다. Android 환경뿐만 아니라 Windows, macOS, GNU/Linux, iOS 모든 곳에서 동작하죠. Frida API를 사용하면 공격자가 원하는 코드를 작성해 후킹이나 함수 추적 등의 행위를 할 수 있다고 해요.</p>
<p>저도 알게 된 지 얼마 안 되었기에 기술적인 내부 원리를 이 글에서 면밀히 다루진 않습니다. 다만 Frida를 사용해 본 경험을 바탕으로 조작 방법 및 결과를 소개하고 이를 방지할 수 있는 방법을 간략히 다뤄보려고 합니다.</p>
<hr>
<h2 id="환경설정">환경설정</h2>
<p>Android 앱을 테스트하기 위해 PC 환경과 모바일 환경을 설정해야 합니다. PC에선 Frida가 조작 시도를 하고, 모바일에선 그 시도를 받아 처리할 Frida Server가 필요하기 때문입니다. 이 글에선 PC는 macOS, 모바일은 x86 기반의 Android API 30 AVD로 진행하겠습니다.</p>
<h3 id="pc-환경">PC 환경</h3>
<p>조작 시도를 하려면 분석을 먼저 해야겠죠? 안드로이드 APK를 분석할 수 있는 디컴파일러인 jadx-gui와 실제 조작을 담당하는 Frida를 설치합니다.</p>
<ul>
<li>Frida 설치: <code>pip install frida-tools</code> <a href="https://frida.re/docs/installation/">설치 가이드</a></li>
<li>jadx-gui 설치: <code>brew install jadx</code> <a href="https://github.com/skylot/jadx#install">설치 가이드</a></li>
</ul>
<h3 id="모바일-환경">모바일 환경</h3>
<p>APK 파일 분석 결과를 기반으로 조작용 코드가 완성되었다면, 이를 전달받아 모바일에서 동작시켜줄 주체가 필요합니다. 이를 frida-server라고 하며, 실행에 루트 권한이 필요하므로 아래와 같이 환경을 만들어줍니다.</p>
<ol>
<li>다음의 명령어로 테스트할 폰의 CPU ABI를 확인합니다.</li>
</ol>
<p><code>adb shell getprop ro.product.cpu.abi</code>
<img src="https://velog.velcdn.com/images/bom-bakbang/post/f697c59f-626c-4d46-bb2d-611944c2e239/image.png" height="100" /></p>
<ol start="2">
<li>확인한 CPU ABI에 맞는 frida-server를 <a href="https://github.com/frida/frida/releases">Frida release 목록</a>에서 다운로드합니다. 예제는 <code>x86</code> 기준입니다.</li>
</ol>
<p><code>wget https://github.com/frida/frida/releases/download/16.1.4/frida-server-16.1.4-android-x86.xz</code>
<img src="https://velog.velcdn.com/images/bom-bakbang/post/a89231eb-c7e9-4440-b375-54abc8ecae3a/image.png" height="100" /></p>
<ol start="3">
<li><p>다운로드한 파일을 압축 해제 후 파일명을 간단하게 바꿉니다.</p>
<ol>
<li><code>unxz frida-server-16.1.4-android-x86.xz</code></li>
<li><code>mv frida-server-16.1.4-android-x86 frida-server</code></li>
</ol>
</li>
<li><p>관리자 권한으로 frida-server를 테스트폰에 옮겨 실행되도록 만듭니다.</p>
<ol>
<li><code>adb root</code></li>
<li><code>adb push frida-server /data/local/tmp/</code></li>
<li><code>adb shell &quot;chmod 755 /data/local/tmp/frida-server&quot;</code></li>
</ol>
</li>
<li><p>마지막으로 테스트폰에서 frida를 실행하면 됩니다. 터미널에서 계속 돌아가기 때문에 일시정지된 것처럼 보여도 정상이니 걱정마세요. <font color="#FF0000">[주의]</font> 이때 테스트폰에 Frida가 사용하려고 하는 포트 번호가 이미 사용 중 일 수 있습니다. <a href="https://domdom.tistory.com/485">https://domdom.tistory.com/485</a> 참고하여, 해당 프로세스를 종료하면 됩니다. (Frida가 구동되는 포트를 바꾸는 방법도 있는데, 그거 바꾸면 또 바꾸고 오류메시지 뜨는 것들이 딸려와서.. 그냥 기존 프로세스를 죽이거나 다른 AVD 사용하는 게 더 정신건강에 이롭습니다.)</p>
<p> <code>adb shell &quot;/data/local/tmp/frida-server &amp;&quot;</code></p>
 <img src="https://velog.velcdn.com/images/bom-bakbang/post/5215c767-2f91-48a3-9b18-2895c30d340c/image.png" height="100" />


</li>
</ol>
<h2 id="실제-코드-분석을-통한-조작-방법-및-결과">실제 코드 분석을 통한 조작 방법 및 결과</h2>
<p>테스트용 앱 apk 파일: <a href="https://github.com/OWASP/owasp-mastg/tree/master/Crackmes/Android/Level_01">https://github.com/OWASP/owasp-mastg/tree/master/Crackmes/Android/Level_01</a></p>
<p>앱을 테스트폰에 설치하고 실행하면 아래와 같이 켜자마자 다이얼로그가 나오며, OK를 누르면 앱이 종료되어 버립니다.
<img src="https://velog.velcdn.com/images/bom-bakbang/post/2d18d958-b24a-4799-a4c1-8be7949eb655/image.png" width="150" /></p>
<p>우리의 목표는 저 다이얼로그가 표시되지 않도록 하는 것입니다. 다만 앱을 직접 수정하지 않고 앱의 코드를 후킹해야 하죠. 그러면 어떤 코드를 후킹 해야 할지 알아야겠죠? 그렇기에 위에서 APK 파일을 디컴파일해서 내부 소스를 확인하기 위해 <code>jadx</code>를 설치했던 겁니다.
<code>jadx-gui UnCrackable-Level1.apk</code>를 통해 아래와 같이 apk 파일을 디컴파일하고 내부 소스 트리 구조를 살펴보면, <code>MainActivity</code>를 찾을 수 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/bom-bakbang/post/8296ae90-a201-44b6-b3d6-4ebb18160b49/image.png" alt=""></p>
<p>노란색으로 강조된 부분에서 앱 강제 종료가 수행되고 있습니다. <code>MainActivity</code>내의 <code>a</code>라는 이름으로 난독화된 메서드에서 발생하고 있네요. 또한 <code>a</code>에서 다이얼로그를 띄워주고 있음도 알 수 있습니다. 여기서 Frida를 통해 <code>a</code>라는 메서드를 후킹 해서, 내부 구현부가 아무 동작도 하지 않도록 조작하면 어떻게 될까요? 다이얼로그도 뜨지 않을 테고, 다이얼로그가 뜨지 않으니까 앱도 강제 종료되지 않겠죠. 이것을 위한 자바스크립트 코드를 아래와 같이 작성할 수 있습니다.</p>
<pre><code class="language-js">// frida_uncrackable.js
console.log(&quot;Starting..&quot;);
Java.perform(function () {
var MainActivity = Java.use(&quot;sg.vantagepoint.uncrackable1.MainActivity&quot;);
MainActivity.a.implementation = function (arg1) {
return;
}
});</code></pre>
<p>이제 작성한 JS 코드를 아래와 같이 실행하면 됩니다. <code>owasp.mstg.uncrackable1</code>는 앱의 패키지명입니다.
<code>frida -U -l frida_uncrackable.js -f owasp.mstg.uncrackable1</code></p>
<p>그럼 아래처럼 다이얼로그가 안 뜹니다!!</p>
<img src="https://velog.velcdn.com/images/bom-bakbang/post/1c5be9b2-7522-41af-8f79-4fbb2c9b47b0/image.png" width="150" />

<hr>
<h2 id="조작-방지-방향">조작 방지 방향</h2>
<p>Frida는 앱을 동적으로 조작할 수 있으므로 앱에서 Frida 프로세스가 동작중인지 탐지한 뒤에야 조작을 방지할 수 있습니다. 탐지 방법에 대한 상세 내용은 <a href="https://core-research-team.github.io/2022-04-01/Android-%ED%99%98%EA%B2%BD-Frida-%ED%83%90%EC%A7%80">Android 환경 Frida 탐지</a>에서 확인하실 수 있습니다. 다만 예시 코드가 C로 되어있습니다. 아직 저는 NDK/JNI 개발 경험이 없고, 상세 방지책 적용법까지 다루면 글이 길어질 듯 하네요. 추후 시간적 여유가 되면 방지하는 방법을 다른 글로 정리해 보겠습니다. 상세 내용에서 다루는 코드를 통해 탐지한 Frida 프로세스를 강제 종료하는 방향으로 구현하면 되리라 생각합니다.</p>
<p><a href="https://youtu.be/iMNs8YAy6pk?si=RSRB1O9yTDRCXED5">https://youtu.be/iMNs8YAy6pk?si=RSRB1O9yTDRCXED5</a> 를 참고하시면 Frida와 관련된 보다 상세 내용을 확인하실 수 있습니다. 2023.09.14 작성 시점 기준으로 삽질했던 경험을 글에 담았으니, 여기까지 다 읽으신 분은 덜 삽질하시기를 바라요 :)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[AndroidRoadMap] Part 1. 개발 언어]]></title>
            <link>https://velog.io/@bom-bakbang/AndroidRoadMap-Part-1.-%EA%B0%9C%EB%B0%9C-%EC%96%B8%EC%96%B4</link>
            <guid>https://velog.io/@bom-bakbang/AndroidRoadMap-Part-1.-%EA%B0%9C%EB%B0%9C-%EC%96%B8%EC%96%B4</guid>
            <pubDate>Sat, 22 Jul 2023 13:39:20 GMT</pubDate>
            <description><![CDATA[<p>Android 개발자라면, 혹은 개발자가 되고 싶다면 도구인 개발 언어를 기본부터 응용까지 잘 다룰 줄 알아야 합니다. 이 포스트는 Kotlin 언어와 관련해 아래 내용들에 대해 정리합니다.
⚠️ 개인 경험을 기반으로 적은 지식이므로 잘못된 내용이나 추가로 덧붙일만한 내용이 있다면 댓글이나 개인적으로 언제든 알려주세요!</p>
<hr>
<blockquote>
<p><a href="#kotlin%EC%9D%98-%EA%B8%B0%EB%B3%B8-%ED%8A%B9%EC%A7%95-%EB%B0%8F-java%EC%99%80%EC%9D%98-%EB%B9%84%EA%B5%90"><strong>Kotlin</strong>의 기본 특징 및 <strong>Java</strong>와의 비교</a></p>
<p>Q) <a href="#null-safety%EC%9D%98-%EA%B0%9C%EB%85%90%EC%9D%B4%EB%9E%80"><strong>null safety</strong>의 개념이란?</a></p>
<p>Q) <a href="#mutable%EA%B3%BC-immutable-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EA%B0%81%EA%B0%81-%EC%96%B8%EC%A0%9C-%EC%93%B0%EB%8A%94-%EA%B2%8C-%EC%A2%8B%EC%9D%80%EA%B0%80"><strong>mutable</strong>과 <strong>immutable</strong> 컬렉션의 차이는 무엇이며 각각 언제 쓰는 게 좋은가?</a></p>
<p>Q) <a href="#lateinit-var%EC%99%80-lazy-delegate%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%99%80-%EA%B0%81%EA%B0%81%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%98%EB%8A%94%EA%B0%80"><strong>lateinit var</strong>와 <strong>lazy delegate</strong>의 차이와 각각의 장단점은 어떻게 되는가?</a></p>
<p>Q) <a href="#extension-%ED%95%A8%EC%88%98%EB%9E%80-%EC%96%B4%EB%96%A4-%EA%B8%B0%EC%A4%80%EC%9C%BC%EB%A1%9C-%EC%84%A0%EC%96%B8%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%A2%8B%EC%9D%80%EA%B0%80"><strong>extension</strong> 함수란? 어떤 기준으로 선언하는 것이 좋은가?</a></p>
<p>Q) <a href="#data-class%EA%B0%80-%EC%9D%BC%EB%B0%98-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EB%B9%84%EA%B5%90%ED%96%88%EC%9D%84%EB%95%8C%EC%9D%98-%EC%9E%A5%EC%A0%90-%EB%B0%8F-%EC%A3%BC%EC%9D%98%ED%95%A0-%EC%A0%90%EC%9D%80"><strong>data class</strong>가 일반 클래스와 비교했을때의 장점 및 주의할 점은?</a></p>
<p>Q) <a href="#sealed-class%EB%9E%80-enum-class%EC%99%80-%EB%B9%84%EA%B5%90%ED%95%B4-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%A2%8B%EC%9D%80%EA%B0%80"><strong>sealed class</strong>란? <strong>enum class</strong>와 비교해 언제 사용하기 좋은가?</a></p>
<p>Q) <a href="#collection%EA%B3%BC-sequence%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%99%80-%EA%B0%81%EA%B0%81%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90-%EB%B0%8F-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%A2%8B%EC%9D%80-%EA%B2%BD%EC%9A%B0%EB%8A%94-%EC%96%B8%EC%A0%9C%EC%9D%B8%EA%B0%80"><strong>collection</strong>과 <strong>sequence</strong>의 차이와 각각의 장단점 및 사용하기 좋은 경우는 언제인가?</a></p>
<p>Q) <a href="#inline-%ED%82%A4%EC%9B%8C%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%A9%B0-%EC%9E%A5%EB%8B%A8%EC%A0%90%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%98%EB%8A%94%EA%B0%80"><strong>inline</strong> 키워드는 어떻게 동작하며 장단점은 어떻게 되는가?</a></p>
<p>Q) <a href="#scope-%ED%95%A8%EC%88%98%EC%9D%98-%EC%A2%85%EB%A5%98%EC%99%80-%EA%B0%81-%ED%8A%B9%EC%A7%95%EB%93%A4%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%98%EB%8A%94%EA%B0%80"><strong>scope</strong> 함수의 종류와 각 특징들은 어떻게 되는가?</a></p>
<p><a href="#%EA%B7%B8-%EC%99%B8-%EB%94%94%ED%85%8C%EC%9D%BC%ED%95%9C-kotlin-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%98%88%EC%8B%9C%EB%93%A4">그 외 디테일한 Kotlin 개념 및 예시들</a></p>
</blockquote>
<hr>
<h3 id="kotlin의-기본-특징-및-java와의-비교">Kotlin의 기본 특징 및 Java와의 비교</h3>
<ul>
<li><p>IntelliJ IDEA의 개발사인 JetBrains에서 만든 언어입니다. JVM 기반으로 동작하고, Java와 100% 호환가능해서 2017년 5월 부터 안드로이드의 <a href="https://blog.jetbrains.com/kotlin/2017/05/kotlin-on-android-now-official/">공식 지원언어로 채택</a>되었고 2019년부터는 Java가 아닌 <a href="https://developer.android.com/kotlin/first">Kotlin을 첫 번째 언어</a>로 선정했습니다. 이제 모든 예제 코드 작성은 코틀린으로 하고있죠.</p>
</li>
<li><p>Java와 비교해보면 여러가지 특징이 있는데, 그 중 눈에 띄는 몇가지를 적어보자면</p>
<ul>
<li><strong>new 키워드 없이</strong> instance를 만들 수 있습니다. 그리고 생성된 instance가 할당되는 변수를 선언할 때 자료형을 명시적으로 지정하지 않아도 <strong>타입추론</strong>을 해줘 알아서 자료형이 결정됩니다. 물론 명시적으로 지정을 해도 문제는 없죠.</li>
<li>제가 정말 좋아하는 코틀린 특징인데, 매 코드 라인마다 <strong>세미콜론을 적지않아도 됩니다.😆</strong> 물론 적어도 오류가 나지는 않지만 누가 적을까요..?ㅋㅋㅋㅋ</li>
</ul>
<pre><code class="language-kotlin">val exampleClass = ExampleClass()</code></pre>
<pre><code class="language-java">ExampleClass exampleClass = new ExampleClass();</code></pre>
<ul>
<li>Property 개념 - 변수를 선언만 하면 Java로 컴파일 될 때 getter와 setter를 자동으로 만들어줍니다. value의 의미를 가진 <code>val</code>(저는 밸이라고 읽습니다)과 variable의 의미를 가진 <code>var</code> (저는 봐ㄹ 정도로 읽습니다)이 있는데요. <code>val</code> 는 선언 시점에 한 번 초기화되면 그 값을 새롭게 변경할 수 없습니다. 따라서 getter만 자동으로 생성되고요. 반면 <code>var</code>는 선언 후에 값을 계속 변경할 수 있습니다. 따라서 getter랑 setter 둘다 생성됩니다.</li>
<li><code>Java</code>의 <code>Integer</code>같은 Primitive Wrapper Class가 없이 기본형들을 바로 사용합니다: <a href="https://kotlinlang.org/docs/mapping-primitive-data-types-from-c.html#primitive-types-in-kotlin">Primitive Types In Kotlin</a></li>
</ul>
</li>
<li><p>몇 개 적다보니 차이가 너무 많을 것 같아.. 기본을 넘어서는 진짜 세세한 내용들은 맨 아래에 추가 정리 해두겠습니다!</p>
</li>
</ul>
<hr>
<h3 id="null-safety의-개념이란">null safety의 개념이란?</h3>
<ul>
<li><p><code>null</code>의 개념은 Tony Hoare가 처음 도입했지만, <a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/">당사자도 10억 달러의 실수라고 말할 정도로</a> 정말 많은 프로그래밍적 오류를 야기해왔죠. <code>Java</code>가 아니더라도 <code>NPE</code>(=Null Pointer Exception)은 익숙할겁니다. <code>Kotlin</code>은 이러한 <code>null</code> 처리를 안정적으로 하는 방법으로 아예 자료형이 <code>nullable</code>인 경우와 <code>non-null</code>인 경우를 구분할 수 있게 했는데요. <code>null</code>은 곧 <code>?</code>라고 생각하면 됩니다. ? 무슨말이냐고요? 코드 예시로 보죠.</p>
<pre><code class="language-kotlin">var nonNullIntValue: Int = 0
var nullableIntValue: Int? = 0

nonNullIntValue = null // 선언된 자료형에 ?가 붙어있지 않으므로, null이 불가능
nullableIntValue = null // 가능!</code></pre>
</li>
<li><p>즉, <code>?</code>가 붙어있지 않는 경우엔 <code>NPE</code> 걱정이 전혀 없이 사용할 수 있답니다. 하지만 대부분의 경우 <code>Kotlin</code>만 가지고 안드로이드 개발을 할 수 없고, 애초에 안드로이드 내부 코드들도 여전히 <code>Java</code>로 개발되어 있기 때문에 여전히 초기화되지 않은 값들 등을 다룰때는 <code>null</code>을 마주하게 됩니다.</p>
</li>
<li><p>하지만 <code>nullable</code>인 변수라도 <code>?</code>(<code>safe-call operator</code>라고 부릅니다)를 통해 <code>NPE</code> 걱정없이 사용할 수 있습니다. 그리고 <code>!!</code>라는 <code>not-null assertion operator</code>도 존재하는데 이건 변수가 <code>null</code>인지 아닌지 관계없이 무조건 참조해서 사용하겠다는 의미입니다.</p>
<pre><code class="language-kotlin">var exampleClass: ExampleClass? = ExampleClass()
exampleClass.doSomething() // exampleClass는 null일 수 있기 때문에 컴파일 에러 발생
exampleClass?.doSomething() // exampleClass가 null이 아닌 경우에만 doSomething 수행
exampleClass = null
exampleClass!!.doSomething() // NPE 발생</code></pre>
</li>
<li><p>이런 기본 사용법 말고도 다양한 활용이 가능합니다.</p>
<pre><code class="language-kotlin">val aInt: Int? = a as? Int // a라는 변수의 자료형이 Int였다면 그대로 aInt는 a값으로 초기화되고, a의 자료형이 다른것이라면 aInt는 null로 초기화됨

val boilerPlateLength: Int = if (b != null) b.length else -1 // 아래와 동일
val length = b?.length ?: -1 // Elvis operator. ?:의 의미는 앞에있는게 null이라면 뒤에있는것으로 결정된다는 의미입니다.

val nullableList: List&lt;Int?&gt; = listOf(1, 2, null, 4) // Collection 내부 아이템 타입들에도 nullable 형태로 사용가능합니다.
val intList: List&lt;Int&gt; = nullableList.filterNotNull() // non-null로 바꾸고 싶다면 이런 함수도 사용할 수 있죠.</code></pre>
</li>
</ul>
<h3 id="mutable과-immutable-컬렉션의-차이는-무엇이며-각각-언제-쓰는-게-좋은가">mutable과 immutable 컬렉션의 차이는 무엇이며 각각 언제 쓰는 게 좋은가?</h3>
<ul>
<li><p>mutable: 말 그대로 수정가능하다는 의미입니다.</p>
<pre><code class="language-kotlin">val itemList = mutableListOf&lt;ExampleItem&gt;()
itemList.add(ExampleItem()) // 이렇게 리스트 변수 내부에 아이템을 추가할 수 도 있고
itemList.clear() // 내부 아이템을 모두 제거하는등의 작업이 가능하죠.</code></pre>
</li>
<li><p>immutable: 말 그대로 수정가능하지 않다는 의미입니다.</p>
<pre><code class="language-kotlin">val itemList = listOf&lt;Int&gt;()
itemListt.getOrNull(1) // 이렇게 초기화 된 후 내부에 접근만 가능하지, add나 remove등의 동작은 수행할 수 없습니다.</code></pre>
</li>
<li><p>그럼 그냥 내맘대로 수정할 수 있는 mutable을 늘 쓰면 좋지 않을까? 라고 생각할 수 있는데요, mutable을 사용하면 동시성 문제가 생길 수 있습니다. 하나의 변수를 서로 다른 쓰레드에서 접근해, 한 쪽은 수정하는 동시에 다른 한 쪽은 읽는 동작을 한다고 생각해보죠.</p>
<pre><code class="language-kotlin">val commonList = mutableListOf&lt;CoreData&gt;()

// 사용자가 특정 아이템의 삭제 버튼을 누른 경우
fun onRemoveData(position: Int) {
  commonList.remove(position)
}

// 사용자가 전체 아이템의 총합 버튼을 누른 경우
fun calculateTotalAmount(): Int {
  var amount = 0
  for (i in 0 until commonList.count()) {
    amount += commonList[i].calculate()
  }
}</code></pre>
<p>여기서 <code>calculate</code> 가 아주 오랜 시간이 걸리는 작업이라고 한다면(DB에 접근한다던지), <code>for</code>문 내부에서 하나씩 처리되는 와중에 <code>onRemoveData</code> 가 호출되는 상황이 발생할 수 있습니다. 그럼 마지막에서 유효하지 않는 index에 접근하게 될거고, <code>IndexOutOfBoundsExeption</code>이 발생하게 될 위험이 있습니다.</p>
<p>물론 실제 코드를 짤 때는 이런 위험성을 배제하기 위해 아예 <code>onRemoveData</code>가 호출되지 않도록 로딩뷰를 띄운다던지 <code>Mutex()</code>를 사용한다던지, <code>flag</code>용 변수를 사용해서 각 함수의 동작중 여부를 확인한다던지의  방법을 적용해서 해결할 수도 있겠지만, 가장 좋은 건 변경이 가능한 변수와 변경이 되지 않을 변수를 구분해서 사용하는것이겠죠? </p>
</li>
</ul>
<h3 id="lateinit-var와-lazy-delegate의-차이와-각각의-장단점은-어떻게-되는가">lateinit var와 lazy delegate의 차이와 각각의 장단점은 어떻게 되는가?</h3>
<ul>
<li><p><code>lateinit var</code>는 말 그대로 나중에 초기화를 한다는 의미입니다. (선언하며 초기화를 하면 <code>lateinit</code>에 빨간밑줄이 쳐지며 사용할 수 없다고 뜹니다.) <code>Java</code>에서는 보통 이런 경우 <code>null</code>로 초기화를 해두죠. 그런데 이때 문제는 분명이 해당 변수를 접근할 시점엔 초기화가 이미 되어있으리란것을 프로그래머는 알고 있는데도 번거롭게 <code>null</code> 체크를 해줘야 하는 경우가 생깁니다.</p>
<pre><code class="language-kotlin">lateinit var exampleData: ExampleData</code></pre>
<p>그런데 위처럼 선언해서 사용해주면 실제 사용하는 시점에서 굳이 <code>null</code> 체크없이 바로 사용할 수도 있고, property가 <code>var</code>이기 때문에 할당했던 값을 바꿔줄수도 있습니다. 물론 실제 초기화되었는지 여부또한 확인할 수 있긴 한데, 이걸 사용할 일이 있다면 차라리 <code>null</code>로 초기화해두고 사용하는게 더 직관적이라고 생각합니다. 뒤에서 다루겠지만 코틀린은 <code>null safety</code> 개념이 아주 잘 적용되어있어서 <code>nullable</code>을 다루는게 그렇게까지 번거로운건 아니라서요.</p>
<pre><code class="language-kotlin">::exampleData.isInitialized // isInitialzed를 사용하려면 변수값이 아닌 참조를 넘겨줘야 쓸 수 있어서, ::를 붙여줘야합니다.</code></pre>
</li>
<li><p><code>lazy</code> delegate: 앞서 <code>lateinit</code>은 <code>var</code>과 하나로 쓰였다면, <code>lazy</code>는 <code>val</code>과 하나로 쓰입니다. 비슷하게 초기화를 나중에 한다는 의미인데요, property가 <code>val</code>인 만큼 초기화 된 이후 값을 변경할 수 없습니다. 그렇다면 언제 초기화가 될까요? 실제 사용되는 예시를 보면,</p>
<pre><code class="language-kotlin">val lazyValue: String by lazy {
    println(&quot;computed!&quot;)
    &quot;Hello&quot;
}

fun main() {
    println(&quot;main start&quot;)
    println(lazyValue)
    println(lazyValue)
}

/** 출력 결과
main start
computed!
Hello
Hello
*/</code></pre>
<p>위처럼 <code>lazyValue</code>를 선언할 때 초기화를 code block으로 미뤄두고, 실제 <code>lazyValue</code>를 접근할 때 초기화 블록이 수행되며 블록의 결과인 <code>&quot;Hello&quot;</code>가 <code>lazyValue</code>에 할당됩니다. 이후에는 <code>lazyValue</code>를 접근할 때 이미 초기화된 <code>&quot;Hello&quot;</code>만 가져다 쓰게되죠.</p>
</li>
<li><p>이런 차이로 인해서 <code>lateinit var</code>는 초기화 로직이 실제 변수에 초기화를 하는곳에 들어가서 선언부는 깔끔해지지만 함수가 뚱뚱해질 수 있는 반면, <code>lazy</code>는 선언부에 초기화 로직이 들어가 뚱뚱해질 수 있지만 변수를 사용할 때는 함수 내부에서 그냥 사용하면 되는 장단점이 있네요.</p>
</li>
</ul>
<h3 id="extension-함수란-어떤-기준으로-선언하는-것이-좋은가">extension 함수란? 어떤 기준으로 선언하는 것이 좋은가?</h3>
<ul>
<li><p>Kotlin의 강점 중 하나인데, 기존에 존재하는 클래스에 마치 새로운 변수나 함수가 존재하는것처럼 추가하여 사용할 수 있는 기법입니다. 바로 예시를 살펴보죠.</p>
<pre><code class="language-kotlin">fun View.fadeOut(duration: Long = 500L): ViewPropertyAnimator {
  animate().alpha(0.0f).setDuration(duration)
}
// 실제 사용하는 코드
titleView.fadeOut(1000L)</code></pre>
</li>
<li><p>위에 보시면 <code>View</code>클래스에는 사실 <code>fadeOut</code>이라는 함수가 없습니다. 그런데 <code>extesion</code> 함수로 만들어두면, 실제로 사용하는곳에선 마치 <code>fadeOut</code>이라는 함수가 원래 있던것처럼 사용할 수 있습니다. 이렇게 등록되는 <code>fadeOut</code> 함수에서는 <code>this</code>를 통해 대상이 되는 클래스(예시에서는 <code>View</code>)를 접근할 수 있고, <code>this</code> 키워드는 생략이 가능하기 때문에 바로 <code>View</code>클래스에 원래 존재하는 <code>animate</code>함수를 <code>fadeOut</code>함수 내부에서 사용할 수 있는것이죠. 이런 확장성은 단순히 함수에만 국한된것이 아닙니다.</p>
<pre><code class="language-kotlin">val Fragment.viewLifecycleScope: CoroutineScope
    get() = viewLifecycleOwner.lifecycleScope</code></pre>
</li>
<li><p>함수에 parameter가 없다면 이렇게 extension property 형태로도 사용 가능합니다. property 내부에서 당연히 다른 함수 호출하는 형태도 사용가능하죠.</p>
<pre><code class="language-kotlin">val ViewGroup.layoutInflater: LayoutInflater
    get() = LayoutInflater.from(context)</code></pre>
</li>
<li><p>개인적으로는 이런 확장 함수들을 모아둔 .kt 파일을 별도로 관리하고 있습니다. <code>Extensions_View.kt</code> 라던지 <code>Extensions_Fragment.kt</code> 라던지요. 만약 확장 함수가 여기저기서 public으로 무분별하게 많이 사용되면 모아서 관리하기 어렵겠죠? 또한 진짜 필요한 함수가 아닌데 단순 편의성을 위해 마구잡이로 만든다면, 해당 클래스에서 .으로 참조했을때 IDE가 추천해주는 목록이 너무 많이 나오겠네요.</p>
</li>
</ul>
<h3 id="data-class가-일반-클래스와-비교했을때의-장점-및-주의할-점은">data class가 일반 클래스와 비교했을때의 장점 및 주의할 점은?</h3>
<ul>
<li><p>Kotlin의 기본 클래스는 Java의 <code>final class</code>와 같아, 기본적으로는 상속이 불가능 합니다. 상속을 시켜주려면 <code>open</code> 키워드를 앞에 붙여줘야 하죠. 그런데 <code>data class</code>는 말그대로 데이터를 다루기 위한 특수 클래스라서, 상속이 불가능합니다. <code>open</code>과 함께 쓰려고 하면 컴파일이 불가해요.</p>
<pre><code class="language-kotlin">open data class ExampleDataClass() // 불가
data class ExampleDataClass() // 불가</code></pre>
</li>
<li><p><code>open</code>을 붙이지 않았는데 아랫줄은 왜 불가일까요? 그건 아무런 데이터가 생성자에 정의되지 않았기 때문입니다. 일반 클래스와는 다르게, 최소 하나의 property가 존재해야 합니다. 이걸 만족시키며 <code>data class</code>를 만들어보면,</p>
<pre><code class="language-kotlin">class ExampleClass()
data class ExampleDataClass(
    val intData: Int,
    val stringData: String,
    val exampleData: ExampleClass,
)</code></pre>
<p>실제 컴파일 될 때는 아래의 <code>Java</code> 코드와 같아집니다. 핵심만 살펴보자면 아래와 같네요.</p>
<ul>
<li>깊은 복사인 <code>copy</code> 함수 자동생성: 얕은 복사는 단순 참조를 복사하는 것이고, 깊은 복사는 실제 값들과 같은 값을 가진 새로운 <code>instance</code>를 만들어서 <code>return</code> 시켜주는 것입니다.</li>
<li><code>toString()</code> 생성: 로그를 확인할 때 내부 변수들을 알아서 이쁘게 보여주도록 기본적으로 오버라이딩 해줍니다.</li>
<li><code>equals()</code>, <code>hashCode()</code> 생성: 생성자에 선언된 변수들이 모두 같은 경우에만 같다고 판별해주도록 기본적으로 오버라이딩 해줍니다.</li>
</ul>
<details>
<summary>Kotlin에서 생성된 Bytecode를 디컴파일한 Java code</summary>

<pre><code class="language-java">public final class ExampleDataClass {
   private final int intData;
   @NotNull
   private final String stringData;
   @NotNull
   private final ExampleClass exampleData;

   public final int getIntData() {
      return this.intData;
   }

   @NotNull
   public final String getStringData() {
      return this.stringData;
   }

   @NotNull
   public final ExampleClass getExampleData() {
      return this.exampleData;
   }

   public ExampleDataClass(int intData, @NotNull String stringData, @NotNull ExampleClass exampleData) {
      Intrinsics.checkNotNullParameter(stringData, &quot;stringData&quot;);
      Intrinsics.checkNotNullParameter(exampleData, &quot;exampleData&quot;);
      super();
      this.intData = intData;
      this.stringData = stringData;
      this.exampleData = exampleData;
   }

   public final int component1() {
      return this.intData;
   }

   @NotNull
   public final String component2() {
      return this.stringData;
   }

   @NotNull
   public final ExampleClass component3() {
      return this.exampleData;
   }

   @NotNull
   public final ExampleDataClass copy(int intData, @NotNull String stringData, @NotNull ExampleClass exampleData) {
      Intrinsics.checkNotNullParameter(stringData, &quot;stringData&quot;);
      Intrinsics.checkNotNullParameter(exampleData, &quot;exampleData&quot;);
      return new ExampleDataClass(intData, stringData, exampleData);
   }

   // $FF: synthetic method
   public static ExampleDataClass copy$default(ExampleDataClass var0, int var1, String var2, ExampleClass var3, int var4, Object var5) {
      if ((var4 &amp; 1) != 0) {
         var1 = var0.intData;
      }

      if ((var4 &amp; 2) != 0) {
         var2 = var0.stringData;
      }

      if ((var4 &amp; 4) != 0) {
         var3 = var0.exampleData;
      }

      return var0.copy(var1, var2, var3);
   }

   @NotNull
   public String toString() {
      return &quot;ExampleDataClass(intData=&quot; + this.intData + &quot;, stringData=&quot; + this.stringData + &quot;, exampleData=&quot; + this.exampleData + &quot;)&quot;;
   }

   public int hashCode() {
      int var10000 = Integer.hashCode(this.intData) * 31;
      String var10001 = this.stringData;
      var10000 = (var10000 + (var10001 != null ? var10001.hashCode() : 0)) * 31;
      ExampleClass var1 = this.exampleData;
      return var10000 + (var1 != null ? var1.hashCode() : 0);
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof ExampleDataClass) {
            ExampleDataClass var2 = (ExampleDataClass)var1;
            if (this.intData == var2.intData &amp;&amp; Intrinsics.areEqual(this.stringData, var2.stringData) &amp;&amp; Intrinsics.areEqual(this.exampleData, var2.exampleData)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}</code></pre>
</details>


</li>
</ul>
<ul>
<li>이외에도 아래와 같은 특징이 있긴 한데, 저는 주의해볼 경험이 아직은 없어서 딱히 강조할 이유는 없어보이네요.<ul>
<li>각각의 property에 상응하는 <code>componentN()</code> 존재</li>
<li><code>open</code>과 함께쓰지 못하는 것처럼 <code>abstract</code>, <code>sealed</code>, <code>inner</code> 키워드와 함께 사용 불가</li>
</ul>
</li>
</ul>
<h3 id="sealed-class란-enum-class와-비교해-언제-사용하기-좋은가">sealed class란? enum class와 비교해 언제 사용하기 좋은가?</h3>
<ul>
<li><p><code>sealed</code>는 봉인된이란 뜻을 지니는데요, 비슷한 사용성을 가진 클래스들을 하나로 묶어서 취급하고 싶을 때 사용할 수 있습니다. 말로 표현하기가 좀 어려워 예시부터 보자면,</p>
<pre><code class="language-kotlin">sealed class MainUiEvent {
    object ShowForceUpdateVersionPopup : MainUiEvent()
    object ShowNewFeatureVersionPopup : MainUiEvent()
    class ShowNewFeatureImagePopup(val newFeatureImageList: List&lt;String&gt;) : MainUiEvent()
}</code></pre>
</li>
<li><p>이처럼 Main화면에서 다뤄지는 UiEvent는 여러개가 있을텐데요. 크게 보면 모두 메인화면 이벤트들이자면 세부적으로는 구분해야할 필요성이 있고 거기다가 개별 이벤트중 일부는 추가정보(<code>parameter</code>)가 필요할 수 있습니다. 이런 경우에 사용될 수 있고, <code>sealed</code>라는 이름의 특성상 <code>sealed class</code>를 상속받는 클래스들은 모두 해당 <code>sealed class</code>와 같은 패키지안에 있어야 합니다. 위의 예시 클래스가 실제 사용될 때는 아래처럼 when과 is의 조합으로 개별적 처리가 가능합니다.</p>
<pre><code class="language-kotlin">when (mainUiEvent) {
    is MainUiEvent.ShowForceUpdateVersionPopup -&gt; showForceUpdateVersionPopup()
    is MainUiEvent.ShowNewFeatureVersionPopup -&gt; showNewFeatureVersionPopup()
    is MainUiEvent.ShowNewFeatureImagePopup -&gt; showNewFeatureImagePopup(it.newFeatureImageList)
}</code></pre>
</li>
<li><p>enum class 또한 목적은 비슷한데, 단어 그대로 열거 가능할 때 쓰이다보니 위처럼 개별적인 다양성이 필요하지 않은 단순한 경우에 쓰기 좋습니다.</p>
<pre><code class="language-kotlin">enum class TransitionMode {
    NONE, HORIZON, VERTICAL
}</code></pre>
</li>
</ul>
<h3 id="collection과-sequence의-차이와-각각의-장단점-및-사용하기-좋은-경우는-언제인가">collection과 sequence의 차이와 각각의 장단점 및 사용하기 좋은 경우는 언제인가?</h3>
<p>사실 안드로이드 개발 수준에서 sequence를 다룰 경우는 거의 없는 것 같긴 합니다. 그럼에도 개념적으로 비교해보자면,</p>
<ul>
<li><p>Collection: 기본적으로 쓰이는 List, Set, Map 으로 구성됩니다. 데이터에 빠르게 접근해서 다양한 연산을 수행할 수 있는데, 모든 데이터가 메모리에 올라가기 때문에 대용량의 데이터를 처리해야 한다면 느리고 메모리 사용량이 많아지는 단점이 있습니다. 또한 여러 연산을 수행할때 중간 결과가 계속 생성됩니다.</p>
</li>
<li><p>Sequence: 데이터에 접근하는 속도는 느려서 적은양의 데이터를 처리할 때는 부적합하지만, Collection과 달리 모든 데이터를 메모리에 올리지 않고 필요할 때만 사용하기 때문에 대용량의 데이터를 처리할 때 사용하기 적합합니다. 중간 데이터를 생성하지도 않고, 연산의 결과를 실제로 사용할 시점에 각 데이터 요소들이 개별적으로 연산되기 때문에 각각의 데이터에 연산을 모두 적용하고 바로바로 그 결과를 받아 처리하고 싶다면 Sequence를 사용하기에 좋습니다.</p>
</li>
<li><p>공식 도큐먼트의 예제코드를 수행시켜보면 바로 이해하기가 쉽습니다: <a href="https://kotlinlang.org/docs/sequences.html#sequence-processing-example">https://kotlinlang.org/docs/sequences.html#sequence-processing-example</a></p>
</li>
<li><p>또한 실제 메모리를 차지하는지도 아래의 코드로 Kotlin Playground에서 수행시켜보면 확인할 수 있습니다.</p>
<pre><code class="language-kotlin">import kotlin.random.Random
import kotlin.system.*
import kotlinx.coroutines.*

fun main() {
    val largeNumberList = (1..10_00_000).toList() // 100만개의 데이터를 메모리에 올려두고

    val startTimeSequence = System.currentTimeMillis()
    val squaredSequence = largeNumberList.asSequnce().map { it * it } // Sequence를 사용하여 map 연산이 수행되도록 하면 squaredSequence는 중간결과로 만들어지지 않기때문에 수행이 됩니다. 그러나 중간의 asSequence()를 없애서 Collection으로 동작시키면, squaredSequence는 중간결과로 또 하나의 100만개의 데이터를 가진 새로운 메모리를 차지하게 되고 OutOfMemoryError가 출력됩니다.
    val endTimeSequence = System.currentTimeMillis()
    val sequenceTime = endTimeSequence - startTimeSequence

    println(&quot;Time taken using Sequence: $sequenceTime ms&quot;)
}</code></pre>
</li>
</ul>
<h3 id="inline-키워드는-어떻게-동작하며-장단점은-어떻게-되는가">inline 키워드는 어떻게 동작하며 장단점은 어떻게 되는가?</h3>
<ul>
<li><code>inline</code> 키워드가 붙으면, 컴파일러는 실제 컴파일을 할 때 해당 함수나 property가 붙은 코드의 내용을 호출하는곳에 그대로 복사하여 붙여넣습니다. 그래서 함수를 호출하는 오버헤드를 줄여 성능 향상이 되기 때문에, 작은 크기의 함수들을 자주 호출하는 경우 효율성을 높일 수 있습니다. 또한 <code>Kotlin</code>에서는 모든 함수가 객체로 취급되기 때문에 <code>closure</code>를 형성합니다. 함수내부에 선언된 변수들이 접근될 수 있는 영역(<code>scope</code>)를 뜻하는데 이런 특징들로 인한 메모리 할당과 virtual call이 실행시점에서의 오버헤드를 야기할 수 있습니다. (잘 쓰지 않아서 풀어내기가 어렵네요😅) <a href="https://kotlinlang.org/docs/inline-functions.html">공식 도큐</a>에서 원문으로 참고하면 더 이해하기 쉬울 것 같습니다.</li>
<li>반면 붙여넣어지다보니 컴파일된 코드의 전체양이 많아지거나 복잡해지고, 디버깅하기 어려워질 수 있습니다. 따라서 함수 코드가 크다면 <code>inline</code>을 붙이지 않는것이 좋습니다.</li>
</ul>
<h3 id="scope-함수의-종류와-각-특징들은-어떻게-되는가">scope 함수의 종류와 각 특징들은 어떻게 되는가?</h3>
<ul>
<li><p>Kotlin에는 기본적으로 모든 객체에서 사용가능한 scope 함수들이 있습니다: <code>run</code>, <code>with</code>, <code>apply</code>, <code>also</code>, <code>let</code>, <code>takeIf</code>, <code>takeUnless</code>. <a href="https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Standard.kt"><code>Standard.kt</code></a>를 들어가면 실제 내부 구현 코드를 볼 수 있습니다. 우선 모든 <code>scope</code> 함수에 적용되는 기본은 처리할 객체의 타입을 <code>T</code>로 취급하는 것입니다.(<code>generic</code>) 그럼 확장함수에서 다룬 것처럼, <code>T.()</code> 타입으로 전달받으면 내부에서 <code>this</code>로 접근가능할테고 <code>(T)</code> 타입으로 전달받으면 내부에서 <code>it</code>으로 접근가능하겠죠. 이걸 기본으로 생각하고 각 함수들을 살펴보면 이해가 쉽습니다.</p>
</li>
<li><p><code>run</code></p>
<ul>
<li>대상이 되는 객체를 <code>this</code>로 접근하는 code block을 수행한 뒤 그 수행 결과를 return 시켜줍니다.</li>
</ul>
</li>
<li><p><code>with</code></p>
<ul>
<li>대상이 되는 객체를 receiver로 받은뒤 <code>this</code> 로 접근하는 code block을 수행한 뒤 그 수행 결과를 return 시켜줍니다.</li>
</ul>
</li>
<li><p><code>apply</code></p>
<ul>
<li>대상이 되는 객체를 <code>this</code>로 접근하는 code block을 수행한 뒤 다시 해당 객체를 return 시켜줍니다.</li>
</ul>
</li>
<li><p><code>also</code></p>
<ul>
<li>대상이 되는 객체를 <code>it</code>으로 전달받는 code block을 수행한 뒤 다시 해당 객체를 return 시켜줍니다.</li>
</ul>
</li>
<li><p><code>let</code></p>
<ul>
<li>대상이 되는 객체를 <code>it</code>으로 전달받는 code block을 수행한 뒤 그 수행 결과를 return 시켜줍니다.</li>
</ul>
</li>
<li><p>나열하고 보면 비슷한 듯 살짝씩 다른데요. 5개가 주로 쓰이는 예시를 보자면(개인적으로 잘 안써서 적절하지 않아 보이는것도 있습니다.)</p>
<pre><code class="language-kotlin">// run: 어떤 객체에 일부 동작을 수행시키고, 최종 결과만 필요할 때 사용하기 좋습니다.
val textView = findViewById(R.id.text_view)
val textLength = if (textView != null) {
  textView.text = &quot;New Text&quot;
  textView.length
} else {
  -1
}

// 위와 같은 코드지만, 결국 length 값만 필요하다면 중간에 textView 변수를 만들 필요없이 사용할 수 있습니다.
// 또한 safe call과 함께 null이 아닌 경우에만 객체를 접근하여 필요한 code block이 수행되도록 할 수 있습니다.
val textLength = findViewById(R.id.text_view)?.run {
  text = &quot;New Text&quot;
  length
} ?: -1</code></pre>
<pre><code class="language-kotlin">// with: 이미 객체는 존재하는 상황에서 계속 객체 변수를 코드에서 참조하지 않고 사용하려고 할 때 사용하기 좋습니다.
val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
binding.textView.text = &quot;New Text&quot;
binding.textView2.text = &quot;New Text2&quot;

// 위와 같은 코드지만, 아래처럼 계속 binding 변수를 써주면서 코드가 길어지는것을 방지할 수 있습니다.
with (binding) {
  textView.text = &quot;New Text&quot;
  textView2.text = &quot;New Text2&quot;
}</code></pre>
<pre><code class="language-kotlin">// apply: 객체를 추후에도 사용하고, 생성 시점에 초기화 동작들을 해줘야 할 경우 사용하기 좋습니다.
val newTextView = TextView().apply {
  text = &quot;New Text&quot;
}</code></pre>
<pre><code class="language-kotlin">// also: 객체를 사용하는데, 생성 시점에 객체 내부의 함수 호출등으로 초기화 동작을 하는 것이 아닌 다른 요인으로 초기화 되는 경우 해당 객체를 argument로 넘겨줘야 하므로 이를 code block 내부에서 it으로 전달해 줄 때 사용하기 좋습니다.
val display = getSystemService(WindowManager::class.java).defaultDisplay
val metrics = if (display != null) {
    DisplayMetrics().also { display.getRealMetrics(it) }
} else {
    Resources.getSystem().displayMetrics
}</code></pre>
<pre><code class="language-kotlin">// let: 거의 대부분 nullable 객체를 다룰때 safe call과 함께 적용해서 사용하기에 좋습니다.
override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    intent?.data?.let { uri -&gt;
        handleUri(uri)
    }
}</code></pre>
</li>
<li><p><code>takeIf</code>, <code>takeUnless</code>: 말그대로 해당 객체가 특정 조건을 만족할때 or 만족하지 않을때만 사용하고자 할때 safe call과 함께 사용됩니다. 개인적으로는 <code>takeUnless</code>는 한번 더 꼬아 생각하는 느낌이라, 가능한 <code>takeIf</code>만 사용하곤 합니다.</p>
<pre><code class="language-kotlin">imageList.takeIf { it.isNotEmpty() }?.let { 
  notifyDataSetChanged() 
}
imageList.takeUnless { it.isEmpty() }?.let {
  notifyDatasetChanged()
}</code></pre>
</li>
</ul>
<hr>
<h3 id="그-외-디테일한-kotlin-개념-및-예시들">그 외 디테일한 Kotlin 개념 및 예시들</h3>
<p>//TODO 추후 시간 될 때 다룰 내용들..</p>
<ul>
<li>crossinline</li>
<li>typealias</li>
<li>generic: in / out / where</li>
<li>nested / inner</li>
<li>SAM</li>
<li>Delegation</li>
<li>Java Annotations</li>
<li>label</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android RoadMap - 안드로이드 개발자가 알아야 할 AtoZ]]></title>
            <link>https://velog.io/@bom-bakbang/Android-RoadMap-List</link>
            <guid>https://velog.io/@bom-bakbang/Android-RoadMap-List</guid>
            <pubDate>Thu, 20 Jul 2023 02:05:01 GMT</pubDate>
            <description><![CDATA[<p>Velog의 첫 글의 테마를 Android RoadMap 정리로 정했습니다.
어느덧 네이버에 입사하고 4년차 안드로이드 개발자로 일하고 있는데, 배운 지식과 겪은 경험을 정리하며 다시금 내것으로 만들고자 합니다.
아래 목차들 마다 하나의 포스트로 정리할 예정이며, 깔끔한 정보전달 목적보다는 개념 소개 + 살아있는 경험전달을 목적으로 적을것이라(단순 정보습득을 위해서라면 여러 공식도큐 보는 게 최고니까요) 참고부탁드립니다.</p>
<ol>
<li><a href="https://velog.io/@bom-bakbang/AndroidRoadMap-Part-1.-%EA%B0%9C%EB%B0%9C-%EC%96%B8%EC%96%B4">개발 언어</a></li>
<li>개발 도구</li>
<li>앱 기본 항목</li>
<li>UI, 내비게이션</li>
<li>뷰, 레이아웃</li>
<li>아키텍처</li>
<li>멀티쓰레딩</li>
<li>데이터 처리</li>
<li>네트워킹</li>
<li>메모리</li>
<li>코루틴</li>
<li>Jetpack Compose</li>
<li>DI</li>
<li>알림, 푸시</li>
<li>웹뷰</li>
<li>Media</li>
<li>NDK</li>
<li>협업</li>
<li>테스트</li>
<li>품질관리</li>
<li>유지보수</li>
<li>형상관리</li>
<li>빌드/배포</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>