<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>evergreen_tree.log</title>
        <link>https://velog.io/</link>
        <description>android_developer</description>
        <lastBuildDate>Thu, 09 Mar 2023 13:09:55 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>evergreen_tree.log</title>
            <url>https://images.velog.io/images/evergreen_tree/profile/08f443fb-61a9-4e7e-9573-58dcb45a8f94/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. evergreen_tree.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/evergreen_tree" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Android] UDF 적용기 (feat : redux)]]></title>
            <link>https://velog.io/@evergreen_tree/Android-UDF-%EC%A0%81%EC%9A%A9%EA%B8%B0-feat-redux</link>
            <guid>https://velog.io/@evergreen_tree/Android-UDF-%EC%A0%81%EC%9A%A9%EA%B8%B0-feat-redux</guid>
            <pubDate>Thu, 09 Mar 2023 13:09:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4">https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4</a>
이전에 MVI에 대해 작성했던 글입니다. 연관되는 내용이 많으니, 보고 오시면 이해가 더 쉽습니다.
필자의 개인의 주관적인 생각이 섞여있습니다. 오타, 반박 언제나 환영합니다!</p>
</blockquote>
<br>

<h2 id="💁♂️udf란">💁‍♂️UDF란?</h2>
<p><strong>UniDirectional Data Flow</strong>의 약자로, 단방향 데이터 흐름을 의미합니다.</p>
<h3 id="udf의-규약">UDF의 규약</h3>
<ul>
<li><strong>단방향 수정</strong></li>
<li><strong>동기적 실행</strong></li>
<li><strong>View와 State의 분리</strong></li>
</ul>
<p>Android의 경우 Activity에 View, Data, Logic이 겹치는 경우 유지보수가 힘들고 테스트가 어렵기 때문에 이전부터 View와 Data를 분리시키려는 노력이 있었습니다. 현재는 AAC Viewmodel의 활약으로 MVVM 등의 아키텍처가 성행하고 있고, 그만큼의 장점을 가져오고 있습니다.</p>
<p><strong>UDF</strong>를 이러한 현재 View Data 분리 시스템에 녹일 수 있는데요,
UDF의 규약을 가지고 태어난 3개의 아키텍처로 <strong>Flux</strong>, <strong>Redux</strong>, <strong>MVI</strong>가 있습니다.
YAPP 21기로 활동하면서 참여한 프로젝트 <a href="https://github.com/YAPP-Github/21st-ALL-Rounder-Team-3-Android/tree/develop/core/src/main/java/com/yapp/timitimi/redux">티미티미</a>에서는 Redux 아키텍처를 채택하여 앱을 개발하였는데요, 왜 선택하게 되었고 어떻게 적용했는지 공유하려고 합니다.</p>
<h2 id="💁♀️왜-udf인가">💁‍♀️왜 UDF인가?</h2>
<h3 id="상태-문제-예방">상태 문제 예방</h3>
<ul>
<li>State가 여러 작업에서 참조되고, 뒤엉키게 됨</li>
<li>비동기 작업과 얽혀 상태가 복잡해지는 경우가 생김</li>
<li>ex) 페이스북의 알림 카운트의 동기화가 올바르지 않은 경우</li>
</ul>
<p>UDF를 적용하면 이러한 <strong>장점</strong>을 얻을 수 있습니다.</p>
<ul>
<li>상태 변경을 동기적으로 실행하기에, 상태 문제에 대해 안전함</li>
<li>State 변화 지점이 명확하여 디버깅이 쉽다.</li>
</ul>
<p>물론, <strong>단점</strong>도 있습니다.</p>
<ul>
<li>높은 러닝 커브</li>
<li>많은 코드 수와, 복잡한 구조</li>
</ul>
<blockquote>
<p>저도 해당 아키텍처를 적용하면서, 1번의 문턱을 넘기 위해 꽤나 많은 노력을 하였고, 프로젝트를 진행하면서 2번에도 많은 고통을 받았습니다. </p>
</blockquote>
<p>위에서 말했듯, UDF의 규약을 가지고 태어난 3개의 아키텍처로 Flux, Redux, MVI가 있다고 하였는데요, 각각의 아키텍처 또한 서로 다른 <strong>차이점</strong>을 가지고 있습니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/271e92d0-750e-4442-a165-a9df4b1ecb1a/image.png" alt=""></p>
<p>ViewModel이 가지고 있는 비즈니스 로직을 Reducer에 위임하면서, ViewModel이 책임지는 행위를 줄이고, 발생할 수 있는 문제를 디버깅할때 더 쉽게 찾을 수 있도록 Redux를 채택하게 되었습니다.</p>
<h2 id="💁♂️구현">💁‍♂️구현</h2>
<p>ViewModel에서 Intent를 받아, IntentFlow에 데이터를 발행 (View에서 ViewModel)</p>
<pre><code class="language-kotlin">fun dispatch(intent: INTENT) {
        viewModelScope.launch {
            _mutableIntentFlow.emit(intent)
        }
    }</code></pre>
<h3 id="다음-과정을-거쳐-state가-최종적으로-생산">다음 과정을 거쳐 state가 최종적으로 생산</h3>
<pre><code class="language-kotlin">open fun start() {
        val initialViewState = getInitialState()
        viewState = _mutableIntentFlow
            .mutate()
            .filter { registerReducer() != null }
            .scan(initialViewState) { prevState, mutate -&gt;
                registerReducer()!!.invoke(mutate, prevState)
            }
            .catch { processError(it) }
            .stateIn(
                viewModelScope,
                // 바로 생산 시작.
                SharingStarted.Eagerly,
                initialViewState
            )
    }</code></pre>
<p><code>mutate()</code> middleware에서 mutate 작업을 수행한 후 다시 intentflow에 병합됩니다. 이는 스트림의 가장 마지막 데이터를 반환하게 됩니다.</p>
<pre><code class="language-kotlin">private fun SharedFlow&lt;INTENT&gt;.mutate(): Flow&lt;INTENT&gt; {
        val middlewareList = registerMiddleware()
        return if (middlewareList.isEmpty()) {
            this
        } else {
            middlewareList
                .scan(this.filterNotNull()) { prevIntentFlow, nextMiddleware -&gt;
                    merge(
                        nextMiddleware.mutate(viewModelScope, prevIntentFlow, _singleEventFlow),
                        prevIntentFlow
                    )
                }.last()
        }
    }</code></pre>
<p>MiddleWare은 말그대로 중개자의 역할을 하는데, Intent와 Event를 받아서 상태(State)를 업데이트 할 것인지, SideEffect를 호출할지를 결정하게 됩니다.</p>
<pre><code class="language-kotlin">interface BaseMiddleware&lt;INTENT : BaseIntent, EVENT: BaseSingleEvent&gt; {
    fun mutate(scope: CoroutineScope, intentFlow: Flow&lt;INTENT&gt;, eventFlow: MutableSharedFlow&lt;EVENT&gt;): Flow&lt;INTENT&gt;
}</code></pre>
<br>

<p>피쳐에서 정의한 Intent에 대해 onEach로 SideEffect를 발생시킵니다.(여기서 event를 방출) 그리고 HotFlow로 변환하여 다시 intenFlow에 병합하게 됩니다.</p>
<pre><code class="language-kotlin">override fun mutate(
        scope: CoroutineScope,
        intentFlow: Flow&lt;CreateProjectIntent&gt;,
        eventFlow: MutableSharedFlow&lt;CreateProjectSingleEvent&gt;
    ): Flow&lt;CreateProjectIntent&gt; {
        return intentFlow.run {
            merge(
                filterIsInstance&lt;CreateProjectIntent.ChangeProjectName&gt;()
                    .onEach {
                        Timber.e(it.toString())
                    }
                    .shareIn(scope, SharingStarted.WhileSubscribed()),
...</code></pre>
<p>reducer에서는 이전 상태를 가지고 비즈니스 로직을 수행하게 되는데, 이 과정에서 상태 변경이 일어날 수 있게 됩니다.</p>
<pre><code class="language-kotlin"> .scan(initialViewState) { prevState, mutate -&gt;
     registerReducer()!!.invoke(mutate, prevState)
 }</code></pre>
<p>reducer를 통해 새로운 상태를 정의하는 과정입니다.</p>
<pre><code class="language-kotlin">override fun invoke(action: BaseIntent, state: CreateProjectState): CreateProjectState {
    var newState = state
    when (action) {
        is CreateProjectIntent.ChangeProjectName -&gt; {
            newState = newState.copy(
                projectName = action.name
            )
        }</code></pre>
<p><code>.catch { processError(it) }</code> 과정에서 발생하는 에러를 캐치하고, 처리할 수 있습니다.</p>
<p>또한 StateFlow로 변환하여 View에서 ViewState를 관찰하고, update 할 수 있는 환경을 제공합니다.</p>
<br>
<br>


<p>참고 : <a href="https://medium.com/@maryangmin/data-uni-directional-architecture-in-android-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A8%EB%B0%A9%ED%96%A5-%EA%B5%AC%EC%A1%B0-fd7ef26e80fc">https://medium.com/@maryangmin/data-uni-directional-architecture-in-android-데이터-단방향-구조-fd7ef26e80fc</a></p>
<blockquote>
</blockquote>
<p>Github : <a href="https://github.com/YAPP-Github/21st-ALL-Rounder-Team-3-Android/tree/develop/core/src/main/java/com/yapp/timitimi/redux">https://github.com/YAPP-Github/21st-ALL-Rounder-Team-3-Android/tree/develop/core/src/main/java/com/yapp/timitimi/redux</a></p>
<blockquote>
<p>참고 :<a href="https://maryang-developer.medium.com/data-uni-directional-architecture-in-android-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A8%EB%B0%A9%ED%96%A5-%EA%B5%AC%EC%A1%B0-fd7ef26e80fc">https://maryang-developer.medium.com/data-uni-directional-architecture-in-android-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%8B%A8%EB%B0%A9%ED%96%A5-%EA%B5%AC%EC%A1%B0-fd7ef26e80fc</a>
Feat : @jshme</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[YAPPX인프런 강의 후기] UX/UI 시작하기 : Figma 입문]]></title>
            <link>https://velog.io/@evergreen_tree/YAPPX%EC%9D%B8%ED%94%84%EB%9F%B0-%EA%B0%95%EC%9D%98-%ED%9B%84%EA%B8%B0-UXUI-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Figma-%EC%9E%85%EB%AC%B8</link>
            <guid>https://velog.io/@evergreen_tree/YAPPX%EC%9D%B8%ED%94%84%EB%9F%B0-%EA%B0%95%EC%9D%98-%ED%9B%84%EA%B8%B0-UXUI-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-Figma-%EC%9E%85%EB%AC%B8</guid>
            <pubDate>Sat, 25 Feb 2023 09:14:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>YAPP 후원을 통해 얻은 인프런 수강권으로 피그마 강의를 듣고, 스터디를 진행하며 작성하게 된 후기입니다.</p>
</blockquote>
<h3 id="figma-입문-스터디를-시작하게-된-계기">Figma 입문 스터디를 시작하게 된 계기</h3>
<p>IT 연합동아리 YAPP 21기로 활동하면서 느낀 점 중 하나는, 다른 기업과 연계하여 구성원들에게 스터디가 가능한 환경을 잘 조성해 준다는 점입니다. 이번 21기 기수에서는 인프런과 연계하여 수강권을 제공해 주었고, 다양한 강의들 중 어떤 걸 고르면 좋을까 고민하게 되었습니다. 
Android 개발자로서 디자이너와 협업하다 보면 Figma라는 툴을 많이 사용하게 되는데, 개발자와 디자이너를 편리하게 해주는 다양한 기능을 제공하는 Figma 툴에 매력에 한번 배워보고 싶어졌고, 수강하게 되었습니다.</p>
<h3 id="스터디-진행">스터디 진행</h3>
<p>YAPP에서 진행했던 다른 스터디(알고리즘, 이펙티브 코틀린)와는 다르게 이 스터디는 혼자서 차근차근 진행해 보고 싶어 1인 스터디를 진행하게 되었습니다. 강의를 처음부터 들으며 매주 할당량을 채워나가기 시작했습니다. 
스터디 도중에 회사 과제 전형을 마주치게 되었는데, 과제 전형을 진행하면서 컴포넌트화 같은 작업들을 적용해 보기도 했습니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/dd0214db-621f-4135-8ae0-2d86b51b7365/image.png" alt=""></p>
<h3 id="후기">후기</h3>
<p>스터디 후반에는 면접 일정과 YAPP 활동들이 겹쳐 완주하지는 못했지만, 피그마의 핵심 기능들을 익힐 수 있게 되었습니다. 또한 아래 사진처럼 배웠던 내용을 활용해서 디자인 시스템을 직접 만들어 보는 경험을 할 수 있었습니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/e60dd7e8-c180-4854-a32c-b61a8b428024/image.png" alt="">
Fimga 툴을 익힌다고 해서, 디자인의 신이 된다거나 현업 디자이너들의 센스를 따라갈 순 없지만, Android 개발하면서 자주 접하는 툴이고 디자이너와 소통을 위해서 한번쯤 배워보는 것도 나쁘지 않다고 생각합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] 코틀린은 람다식에서 외부 변수를 어떻게 참조할까? [람다 캡쳐링] [Lambda Capturing] ]]></title>
            <link>https://velog.io/@evergreen_tree/Kotlin-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%9D%80-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-%EB%B3%80%EC%88%98%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%B0%B8%EC%A1%B0%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@evergreen_tree/Kotlin-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%9D%80-%EB%9E%8C%EB%8B%A4%EC%8B%9D%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-%EB%B3%80%EC%88%98%EB%A5%BC-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%B0%B8%EC%A1%B0%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Thu, 05 Jan 2023 14:32:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>람다식 내에서 외부 변수를 변경하는 것에 대해 아무런 의문을 느끼지 않다가, 문득 궁금증이 생겼습니다. 함수를 작성하는 시점에는 람다식의 블럭 내부가 실행되지 않다가, 호출되는 시점에 실행될텐데 과연 어떻게 동작하게 될까요?</p>
</blockquote>
<br>

<h2 id="람다-캡쳐링-📸">람다 캡쳐링 📸</h2>
<ul>
<li><strong>람다식 외부에서 정의한 변수를 참조하는 것</strong></li>
</ul>
<pre><code class="language-kotlin">var tabIndex by remember { mutableStateOf(0) }
tabData.forEachIndexed { index, text -&gt;
    Tab(
        ...
        onClick = { tabIndex = index },
        ...
    )
}</code></pre>
<p>위 코드는 Jetpack Compose에서 <code>Tab</code> 클릭 시 <code>tabIndex</code>가 변경되는 예시입니다. tabIndex는 onClick에 할당된 람다식의 외부 변수이지만, 클릭하는 시점에서 tabIndex가 index에 할당됩니다. 
코틀린에서 어떻게 람다식에서 외부 변수를 변경시킬 수 있을까요?</p>
<br>

<h2 id="in-java-☕">In JAVA ☕</h2>
<blockquote>
<p>자바에 대한 이야기를 또 빼놓고 갈 수가 없습니다</p>
</blockquote>
<p>자바에서는 람다식이 <code>final</code> 변수로 선언된 <strong>지역 변수</strong>에 접근만 가능했고, 변경이 <strong>불가능</strong>했습니다. 왜냐하면** JVM 메모리 구조 상, 지역변수가 스택에 저장**되기 때문입니다. 람다식 바디의 생명주기가 종료되면, 내부의 지역변수 할당을 해제해야 하므로 외부 지역변수의 변경이 가능할 수가 없는 구조입니다. </p>
<p>따라서 자바는 외부 지역변수를 복제한 데이터를 통해 <strong>읽기가 가능한 캡쳐링</strong>만을 허용했습니다.</p>
<br>

<h2 id="in-kotlin-🎯">In Kotlin 🎯</h2>
<p>코틀린에서는 <code>val</code>을 포획했는지, <code>var</code>을 포획했는지에 따라 달라지는 것을 디컴파일 한 결과를 통해 확인할 수 있는데요, 한 번 살펴보겠습니다.</p>
<h3 id="val을-캡쳐한-경우"><code>val</code>을 캡쳐한 경우</h3>
<pre><code class="language-kotlin">fun main() {
    val immutable = 0
    exampleFunction(lambda = { immutable })
}

fun exampleFunction(
    lambda: () -&gt; Int
) {
    lambda()
}</code></pre>
<h4 id="디컴파일">디컴파일</h4>
<pre><code class="language-java">public final class MainKt {
   public static final void main() {
      final int immutable = 0;
      exampleFunction((Function0)(new Function0() {
         public Object invoke() {
            return this.invoke();
         }

         public final int invoke() {
            return immutable;
         }
      }));
   }

   public static void main(String[] var0) {
      main();
   }

   public static final void exampleFunction(@NotNull Function0 lambda) {
      Intrinsics.checkNotNullParameter(lambda, &quot;lambda&quot;);
      lambda.invoke();
   }
}
</code></pre>
<p><code>val</code>의 경우, 자바와 마찬가지로 <code>final</code>로 선언되어 해당 값이 캡쳐되는 것을 확인할 수 있습니다.</p>
<h3 id="var을-캡쳐한-경우"><code>var</code>을 캡쳐한 경우</h3>
<pre><code class="language-kotlin">fun main() {
    var mutable = 0
    exampleFunction(lambda = { mutable++ })
}

fun exampleFunction(
    lambda: () -&gt; Int
) {
    lambda()
}</code></pre>
<h4 id="디컴파일-1">디컴파일</h4>
<pre><code class="language-java">public final class MainKt {
   public static final void main() {
      final Ref.IntRef mutable = new Ref.IntRef();
      mutable.element = 0;
      exampleFunction((Function0)(new Function0() {
         public Object invoke() {
            return this.invoke();
         }

         public final int invoke() {
            Ref.IntRef var10000 = mutable;
            int var1;
            var10000.element = (var1 = var10000.element) + 1;
            return var1;
         }
      }));
   }

   public static void main(String[] var0) {
      main();
   }

   public static final void exampleFunction(@NotNull Function0 lambda) {
      Intrinsics.checkNotNullParameter(lambda, &quot;lambda&quot;);
      lambda.invoke();
   }
}
</code></pre>
<p>보이시나요? <code>IntRef</code>라는 <code>final</code> 클래스에 의해 래핑되고, 내부적으로 <strong>변경이 가능한 변수로 포획</strong>한다는 것을 확인할 수 있습니다. <code>IntRef</code>로 생성된 클래스는 JVM 힙에 할당 될 것이기 때문에 람다 블록에서 변수를 변경하고 생명주기가 끝나도 영향을 받지 않을 수 있습니다.</p>
<pre><code class="language-java">public static final class IntRef implements Serializable {
        public int element;

        @Override
        public String toString() {
            return String.valueOf(element);
        }
    }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Debug에서 발생하지 않던 에러가 Release에서 발생하는 경우   ]]></title>
            <link>https://velog.io/@evergreen_tree/Android-Debug%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EC%A7%80-%EC%95%8A%EB%8D%98-%EC%97%90%EB%9F%AC%EA%B0%80-Release%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0</link>
            <guid>https://velog.io/@evergreen_tree/Android-Debug%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EC%A7%80-%EC%95%8A%EB%8D%98-%EC%97%90%EB%9F%AC%EA%B0%80-Release%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0</guid>
            <pubDate>Tue, 30 Aug 2022 10:23:55 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/610958b3-f560-4621-aa4e-da954e815f3a/image.png" alt=""></p>
<blockquote>
<p>Nexters 21기의 첫 프로젝트인 &quot;클라이머를 위한 출석 시스템 홀디&quot; 를 배포하면서 생긴 Issue를 해결하기 위한 과정을 담았습니다.</p>
</blockquote>
<br>

<h2 id="issue💡">Issue💡</h2>
<p>Debug에서 정상적으로 작동하던 홀디 앱을, 출시를 위해 <strong>Release</strong> Variants로 설정하여도 잘 작동되는지 확인해 보고자 하였습니다.</p>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/47b709b0-1d5b-4e49-a703-9f15a39e34f5/image.png" alt=""></p>
<p>그런데 앱 로그인, 온보딩이 끝나고 홈 화면으로 들어가는 시점에서 갑자기 앱이 종료되는 현상이 발생하였습니다. 에러는 다음과 같았습니다.</p>
<pre><code>2022-08-30 15:13:33.779 28548-28548/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: team.nexters.semonemo, PID: 28548
    java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter &lt;this&gt;
        at team.nexters.data.moim.mapper.MoimMapperKt.toDomain(Unknown Source:2)
...</code></pre><br>
<br>

<h3 id="nullable의-문제❓">Nullable의 문제❓</h3>
<p>먼저, 로그에 Error가 찍혀있으니 주어진 에러를 해결하는 것을 초점에 두었습니다.</p>
<p>Data단에서 받은 <code>Response</code>를 Domain <code>Model</code>로 변환하는<code>Mapper</code>에서 위와 같은 에러가 발생하였습니다. 그렇기 때문에 에러의 이유를 다음 2 가지 중 하나일 거라고 생각했습니다.</p>
<ol>
<li>서버에서 <code>Nullable</code>한 값을 보내기 때문에 클라이언트 측에서도 모델의 프로퍼티를 <code>Nullable</code>로 지정해야 한다. </li>
<li>데이터 직렬화는 잘 되었으나, 역직렬화에 문제가 발생했다.</li>
</ol>
<p>위 두가지 에러를 해결해 보았으나, 근본적으로 해결되지는 않았습니다. </p>
<br>
<br>



<h3 id="debug와-release의-차이에서-오는-것인가❓">Debug와 Release의 차이에서 오는 것인가❓</h3>
<p>결국 다시 처음으로 돌아왔습니다. 
이번에는 <code>Debug</code>에서 발생하는 에러가 <code>Release</code>에서만 발생한다  에 초점을 두었고 <code>buildTypes</code> 에서 코드를 최적화 하는 코드를 살펴보았습니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/90f973b3-2956-41b8-8ef4-b4d800b11f8c/image.png" alt=""></p>
<p>각각의 역할은, 다음과 같습니다</p>
<ul>
<li><code>debuggable</code> : 디버깅 여부</li>
<li><code>minifyEnabled</code> : 사용하지 않는 코드 제거 및 코드 난독화</li>
<li><code>shrinkResources</code> : 사용하지 않는 리소스 제거</li>
</ul>
<p>StackOverFlow에서 비슷한 맥락의 오류를 발견했고, <code>minifyEnabled</code> , <code>shrinkResources</code> 를 적용하는 부분에서 에러가 발생했을 가능성을 염두하여 최적화 옵션 적용을 제거했습니다. </p>
<br>
<br>

<h3 id="해결❓">해결❓</h3>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/91b14af1-b4b9-4bd4-a447-b8afa2fdea0a/image.png" alt="">
최적화 옵션을 적용하지 않음으로서 당장은 해결할 수 있었습니다.
<code>minifyEnabled</code>은 난독화 여부를 결정하는데, 난독화란 클래스 명이나 함수 명들을 a,b 등의 문자로 치환시켜 기존 코드의 보안성을 강화하는 것을 말합니다. 
*<em>그러나, 단순히 최적화 옵션 적용이 안된다는 이유만으로 최적화 옵션을 버릴 수는 없었습니다. *</em></p>
<br>
<br>


<h3 id="진짜-해결❗">진짜 해결❗</h3>
<p><code>MinifyEnabled</code>를 통해 클래스 파일이 난독화가 되면, 클래스나 파일 명이 a,b,c 등이 되어 <code>Reflection</code>을 사용할 수가 없었습니다.
문제가 되는 코드에서 딱히 <code>Reflection</code>을 사용하지 않았는데, <strong>알고보니 Retrofit 에서 Generic Parameter에 대해 Reflection을 사용하고 있다는 것을 알게 되었습니다.</strong></p>
<p>Reflection을 사용하는 클래스 등, 난독화가 되면 안되는 것들은 Proguard에 정의함으로써 방지할 수 있게 됩니다. 
_따라서 아래와 같이 Proguard Rule을 설정함으로써, MinifyEnabled를 적용했을 때 문제 없이 작동될 수 있었습니다.
_</p>
<pre><code># Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Keep annotation default values (e.g., retrofit2.http.Field.encoded).
-keepattributes AnnotationDefault

...
</code></pre><blockquote>
<p>Retrofit, Glide 등 외부 라이브러리는 공식 문서에 가면 Proguard Rule이 기재되어 있습니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Compose] UI Test ]]></title>
            <link>https://velog.io/@evergreen_tree/Compose-UI-Test</link>
            <guid>https://velog.io/@evergreen_tree/Compose-UI-Test</guid>
            <pubDate>Thu, 25 Aug 2022 07:11:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/b48d94bd-6e90-4838-9e6c-e4706a10de18/image.png" alt=""></p>
<h2 id="💡개요">💡개요</h2>
<hr>
<p>협업하는 개발자들 끼리 서로의 코드를 다 살펴볼 순 없습니다. 특히, 각자 개발한 UI에 대해 한눈에 알아보는 것은 더욱 힘든 일입니다.</p>
<p>UI Test를 작성하는 것 만으로도 이것이 어떻게 작동하는지 유추가 가능하게 되고, 이것은 일종의 문서 역할을 하게 됩니다.</p>
<blockquote>
<p>프로젝트 덕키(Duckie)는 독립적인 디자인 시스템(Quack-Quack)을 가지고 있습니다. 덕키의 거의 모든 컴포넌트는 디자인 시스템의 컴포넌트로 이루어질 것이므로 중요성은 이루 말할 수가 없습니다. 따라서 UI Test를 통해 UI가 올바르게 작동하는지 검증하고, 안정성을 검증하는 과정을 필수로 거치게 되었습니다.
<a href="https://github.com/sungbinland/duckie-quack-quack">https://github.com/sungbinland/duckie-quack-quack</a></p>
</blockquote>
<p><br><br></p>
<h2 id="🎆semantic">🎆Semantic</h2>
<hr>
<p><strong>UI Tree에 의미론적인 요소를 부여한 체계 라고 할수 있습니다</strong></p>
<p>.View System에서는, 각 View가 계층 구조 내에 정의되고, 관련 속성을 보유할 수 있었습니다.</p>
<p>컴포저블같은 경우 시각적 표현에 중점을 두는 background라던지, text라던지 상위 컨테이너의 정렬을 포함한 트리 구조를 방출합니다. </p>
<p>이러한 의미에서 *<em>Semantic은 방출된 시점에 컴포저블에 의미를 부여하고 찾기 위한 시스템이라고 볼 수 있습니다.
*</em>
<img src="https://velog.velcdn.com/images/evergreen_tree/post/acffad21-a51d-493c-944f-bc3a3ae41621/image.png" alt=""></p>
<p>Compose의 UI 테스트는 Semantic을 사용하여 UI 계층 구조와 상호작용 하게 됩니다. Semantic tree는 UI 계층 구조와 함께 생성되고 UI 계층 구조를 형성합니다</p>
<p>각각의 UI요소에  시맨틱이 의미를 부여하게 되는데,  이 의미라는 것은 Test FrameWork에서 접근할수 있는 방식을 말합니다. 즉, <code>composeTestRule.*onNodeWithText(text)</code> 같은 방식으로 Semantic Node에 접근할 수 있는 방식을 말합니다.*</p>
<p><br><br></p>
<h2 id="💁♀️example">💁‍♀️Example</h2>
<hr>
<p><code>junit4</code> 환경에서 간단한 버튼 테스트를 진행하면서 테스트의 흐름을 짚어보겠습니다.</p>
<h3 id="composetestrule">ComposeTestRule</h3>
<p>ComposeTestRule은 <strong>component, Activity를 시작하는 방법 및 finder, actions, assertions 등을 제공하는 인터페이스</strong>입니다.
특정 액티비티를 지정하고 싶다면 createAndroidComposeRule<ACTIVITY_NAME>() 을 통해 생성할 수 있습니다. </p>
<pre><code class="language-kotlin">@get:Rule
val composeTestRule = createComposeRule()</code></pre>
<br>

<h3 id="setcontent">SetContent</h3>
<p>덕키 디자인 시스템(Quack-Quack) 에서는 각각의 컴포넌트를 테스트 해야하므로, createComposeRule()을 생성하여 노드를 제공해야 합니다.</p>
<p>Semantic Node를 식별하기 위해, Companion Object에 선언한 TEST_TAG를 지정해 줍니다.</p>
<pre><code class="language-kotlin">composeTestRule.setContent{
      Button(onClick = { }) {
        Text(TEST_TAG)
    }
}</code></pre>
<br>

<h3 id="finders">Finders</h3>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/8e7c7dc8-0f43-479a-b514-d0ee918f5c67/image.png" alt="">
Semantic은 UI 요소에 의미를 부여한다고 했습니다. </p>
<p>Finder는, 주어진 어떠한 조건을 통해 의미가 부여된 노드를 찾을 수 있게 해줍니다.</p>
<p>예를들어, argument로 주어진 Text를 가지고 있는 Semantic Node를 찾으려면
<code>composeTestRule.onNodeWithText(TEST_TAG)</code>  를 호출하면 됩니다. 
해당 메서드는 <code>SemanticsNodeInteraction</code> 를 반환하고, 찾은 노드에 대한 <code>Interaction</code>을 진행할 수 있습니다.</p>
<p>혹은 <code>onAllNodes</code> 를 사용하여 여러 노드를 선택할 수도 있습니다.
<br></p>
<h3 id="assertion">Assertion</h3>
<p>특정 요소가 있는지, 화면에 나타났는지 등에 대한 상태를 확인합니다.</p>
<pre><code class="language-kotlin">composeTestRule.onNodeWithText(TEST_TAG).assertIsDisplayed()</code></pre>
<p><br><br></p>
<p>이처럼 Finder, Assertion, Action을 적절히 활용하여 요소를 찾아 속성을 확인하고 Interaction을 실행하는 테스트를 작성하면 됩니다.</p>
<br>

<p><a href="https://developer.android.com/jetpack/compose/semantics?hl=ko">https://developer.android.com/jetpack/compose/semantics?hl=ko</a></p>
<p><a href="https://developer.android.com/jetpack/compose/testing?hl=ko">https://developer.android.com/jetpack/compose/testing?hl=ko</a></p>
<p><a href="https://joebirch.co/android/jetpack-compose-accessibility-semantics/">https://joebirch.co/android/jetpack-compose-accessibility-semantics/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 스케줄러]]></title>
            <link>https://velog.io/@evergreen_tree/OS-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC</link>
            <guid>https://velog.io/@evergreen_tree/OS-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC</guid>
            <pubDate>Tue, 19 Jul 2022 07:33:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Nexters 21기 CS 스터디의 첫 번째 주제로 이어서 스케줄러에 대해 포스팅하게 되었습니다. 스케줄러를 왜 사용하는지와 스케줄링 알고리즘의 종류에 대해 알아봅시다.</p>
</blockquote>
<br>
<br>

<h2 id="📕스케줄러">📕스케줄러</h2>
<hr>
<p><strong>어떤 프로세스에게 자원을 할당할지 결정하는 운영체제 커널의 모듈</strong> </p>
<p>다중 프로그래밍(Multi-programming)에서는, CPU의 이용을 극대화하기 위해 항상 어떤 프로세스가 실행될 수 있도록 하고, 시분할(Time-Shared)은 프로세스 간 문맥 전환이 빠르게 이루어질 수 있도록 해야 합니다.</p>
<p>운영체제는 스케줄러를 통해 CPU를 사용하려고 하는 프로세스 사이의 우선 순위를 관리하고 이것을 <strong>스케줄링</strong> 이라고 부릅니다.</p>
<p>프로세스는 일생 동안 다양한 <strong>스케줄링 큐</strong> 사이를 이동하는데, 프로세스를 스케줄링하기 위한 <strong>Queue</strong>에는 세 가지 종류가 존재합니다.</p>
<ol>
<li><strong>작업 큐(Job Queue)</strong> : 시스템 안의 모든 프로세스들로 구성</li>
<li><strong>준비 큐(ready queue)</strong> : 메인 메모리에 존재하며, 준비 완료 상태에서 실행을 대기하는 프로세스들로 구성</li>
<li><strong>장치 대기 큐(device queue)</strong> : 특정 입/출력장치를 대기하는 프로세스들의 리스트들로 구성</li>
</ol>
<br>

<h3 id="스케줄러의-종류">스케줄러의 종류</h3>
<ol>
<li><strong>단기 스케줄러(CPU Scheduler) : CPU와 메모리 사이를 담당하는 스케줄러</strong><ul>
<li>실행 준비가 완료되어 <strong>준비 큐</strong>에 있는(메모리에 존재하는) 프로세스들 중에서 선택하여 <strong>CPU를 할당</strong></li>
<li>단기 스케줄러는 자주 수행되므로 빨라야 함</li>
</ul>
</li>
<li><strong>중기 스케줄러(Swapper) : 메모리에서 CPU를 점유하기 위해 경쟁하는 프로세스를 디스크로 보내는 스케줄러</strong><ul>
<li>시분할 시스템(Time-shared)과 같은일부 운영체제에서 도입</li>
<li>다중 프로그래밍의 정도를 완화</li>
<li>차후 다시 프로세스를 메모리로 불러와서 중단되었던 지점에서 실행을 재개하는 <strong>스와핑(swapping)</strong> 기능을 함</li>
</ul>
</li>
<li><strong>장기 스케줄러(Job Scheduler) : 메모리와 디스크 사이를 담당하는 스케줄러</strong><ul>
<li><strong>디스크 상의 프로세스</strong>를 선택하여 <strong>준비 큐로 저장(메모리로 적재)</strong></li>
<li>다중 프로그래밍의 정도(<strong>메모리에 있는 프로세스의 수) 제어</strong></li>
<li>수십 초 내지 수 분 단위로 호출되기 때문에, 속도가 느릴 수 있음</li>
</ul>
</li>
</ol>
<p><br><br><br></p>
<h2 id="✒스케줄링">✒스케줄링</h2>
<hr>
<p>스케줄러가 메모리 내의 프로세스에게 CPU를 할당하는 것</p>
<br>

<h3 id="스케줄링의-목적">스케줄링의 목적</h3>
<p>CPU 이용률 최대화 (Max CPU utilization)
처리량 최대화 (Max throughput)
총 처리 시간 최소화 (Min turnaround time)
대기 시간 최소화 (Min waiting time)
응답 시간 최소화 (Min response time)</p>
<p>CPU의 스케줄링 결정은 다음 네 가지 상황 하에 발생합니다.</p>
<ol>
<li><strong>실행 상태 → 대기 상태</strong>(I/O 요청이나 자식 프로세스들 중의 하나가 종료되기를 기다리기 위해 wait를 호출할 때</li>
<li><strong>실행 상태 → 준비 완료 상태</strong>(인터럽트가 발생할 때)</li>
<li><strong>대기 상태 → 준비 완료 상태</strong>(I/O의 종료 시)</li>
<li><strong>실행 상태 → 종료 상태</strong></li>
</ol>
<p>스케줄링은 <strong>비 선점 스케줄링</strong>과, <strong>선점 스케줄링</strong>으로 나뉘어 집니다.</p>
<br>

<h3 id="비-선점-스케줄링">비 선점 스케줄링</h3>
<p>한 프로세스가 CPU를 할당 받으면 다른 프로세스에 CPU 할당이 불가능합니다.</p>
<ul>
<li>위 스케줄링 상황 중 1번과 2번인 경우, 현재 실행중인 프로세스도 없는 상황에서 준비 큐에 존재하는 프로세스를 실행시켜야 하므로 <strong>협조적</strong>인 성격을 가집니다.</li>
<li>스케줄러의 작업량이 적고 문맥 교환 오버헤드가 작지만, 처리율이 떨어집니다.</li>
<li>일괄 작업 방식 스케줄러에 사용됩니다.<strong>(FCFS, SJF, Priority)</strong></li>
</ul>
<br>

<h3 id="선점-스케줄링">선점 스케줄링</h3>
<p>현재 실행중인 프로세스를 밀어내고 더 우선 순위가 높은 프로세스를 스케줄링 합니다.</p>
<ul>
<li><strong>문맥 교환 오버헤드</strong>가 많음</li>
<li>공유 메모리를 사용할 경우, 이전 프로세스 데이터 내용을 <strong>선점</strong>된 프로세스가 영향을 줄 수 있으므로 <strong>조정에 필요한 비용</strong>을 유발합니다.</li>
<li>커널 프로세스 선점으로 생기는 경쟁을 통해 <strong>커널의 데이터 보안</strong> 문제가 발생합니다.</li>
<li>시분할 방식 스케줄러에 사용됩니다.<strong>(RR, SRT, 다단계 큐, 다단계 피드백 큐)</strong></li>
</ul>
<br>
<br>

<h2 id="⚙스케줄링-알고리즘">⚙스케줄링 알고리즘</h2>
<p>다음 문제를 통해 <strong>FCFS, SJF, SRTF, Pritority Scheduling, RR</strong>을 이해해 보겠습니다.</p>
<p><img src="https://user-images.githubusercontent.com/70064912/179690563-d1e4cd6b-991a-43d7-92e0-a5f62ac5bf02.png" alt="image"></p>
<p>다음과 같은 상황에서, 각 스케줄링 알고리즘에 대해 Gantt 차트를 그려 비교해 보겠습니다.</p>
<blockquote>
<p><strong>평균 대기시간</strong> = 대기시간/프로세스 개수</p>
<p> 대기시간 = 처리시간 - 버스트 시간</p>
<p>처리 시간 = 완료 시간 - 도착 시간</p>
</blockquote>
<br>
<br>

<p><img src="https://user-images.githubusercontent.com/70064912/179690745-d0087aec-e164-4439-b21a-959ac3726315.png" alt="image">
<strong>FCFS</strong></p>
<ul>
<li>먼저 도착한 프로세스에게 서비스 해주는 방식</li>
<li>비선점형 스케줄링</li>
<li>CPU를 할당받으면 버스트가 완료되기 전까지 CPU를 반환하지 않음</li>
</ul>
<p><strong>문제점</strong></p>
<ul>
<li>convoy effect : 버스트 시간이 짧은 프로세스들이 늦게오면 CPU를 양도 받기를 기다려야해서 <strong>CPU와 장치 이용률이 저하됨</strong></li>
</ul>
<br>
<br>

<p><img src="https://user-images.githubusercontent.com/70064912/179690828-cea582c0-cc20-4a0f-85fe-aa2890340eb8.png" alt="image">
<strong>SJF</strong></p>
<ul>
<li>버스트가 가장 짧은 프로세스에게 먼저 CPU를 할당</li>
<li>위의 경우는 <strong>비선점형의 경우로, 자신의 CPU 버스트를 끝내기 전에는 선점되지 않음</strong></li>
<li>선점형의 경우에는 남은 시간 보다도 더 짧은 CPU 버스트가 도착하면 그 프로세스가 선점됨(SRTF, Shortest-Remaining-Time-First, SRTF)</li>
</ul>
<p><strong>문제점</strong></p>
<ul>
<li><strong>starvation</strong> : 버스트 시간이 긴 프로세스들이 CPU를 무한히 대기하게 됨</li>
<li>SRTF의 경우새로운 프로세스가 도달할 때마다 스케줄링을 다시하기 때문에 CPU burst time(CPU 사용시간)을 측정할 수가 없음</li>
</ul>
<br>
<br>

<p><img src="https://user-images.githubusercontent.com/70064912/179691648-a711afb0-3625-4492-80c9-3bf225d701c3.png" alt="image">
<img src="https://velog.velcdn.com/images/evergreen_tree/post/f535a162-5efb-49f1-ab4e-78f4f7a588ff/image.png" alt="">
<strong>Priority Scheduling</strong></p>
<ul>
<li>가장 높은 우선순위를 가진 프로세스에게 CPU를 할당</li>
<li>선점형의 경우 더 높은 우선순위의 프로세스가 도착하면 <strong>선점당함</strong></li>
<li>비선점형의 경우 더 높은 우선순위의 프로세스가 도착하면 <strong>준비 큐의 head</strong>로 넣어짐</li>
</ul>
<p><strong>문제점</strong></p>
<ul>
<li><strong>starvation</strong> : 우선순위가 낮은 프로세스들이 CPU를 무한히 대기하게 됨</li>
</ul>
<p><strong>해결책</strong></p>
<ul>
<li><strong>aging</strong> : 오랫동안 대기하는 프로세스들의 우선순위를 점진적으로 증가시키는 방법
<img src="https://user-images.githubusercontent.com/70064912/179691671-a4d4686b-f307-4237-b05a-31c55c5467e0.png" alt="image"></li>
</ul>
<p><strong>Priority Scheduling</strong></p>
<ul>
<li><strong>시분할 시스템</strong>에 적합</li>
<li>각 프로세스는 <strong>시간 할당량(time quantum)</strong> 또는 <strong>시간 조각(time slice)</strong>라는 작은 단위의 시간을 획득하고, 이 시간이 지나면 프로세스는 선점되고 준비완료 큐의 tail에 추가됨</li>
</ul>
<p><strong>장점</strong> </p>
<ul>
<li>준비 완료 큐에 n개의 프로세스가 있고 시간 할당량이 q이면, 각 프로세스는 자신의 <strong>다음 시간 할당량이 할당될 때 까지 (n-1) x q 시간 이상</strong>을 대기하지 않음. 즉 <strong>response time이 빨라짐</strong></li>
</ul>
<p><strong>성능</strong> </p>
<ul>
<li>시간 할당량이 매우 크면, FCFS랑 같아짐</li>
<li>시간 할당량이 작으면 문맥 전환으로 인한 오버헤드가 발생함(최소 문맥 교환 시간 10 마이크로초 미만) 보다 커야함</li>
<li><strong>적정한 시간 할당량을 부여하는 것이 과제</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OS] 프로세스와 스레드]]></title>
            <link>https://velog.io/@evergreen_tree/OS-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</link>
            <guid>https://velog.io/@evergreen_tree/OS-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C</guid>
            <pubDate>Mon, 18 Jul 2022 13:20:12 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Nexters 21기 CS 스터디 첫 번째 주제로 프로세스와 스레드에 포스팅하게 되었습니다. 프로세스와 스레드의 차이를 알고 작동하는 방식에 대해 이해하는 것이 요점일 것 같습니다.</p>
</blockquote>
<p><br><br></p>
<h2 id="📜프로세스">📜<strong>프로세스</strong></h2>
<hr>
<p><strong>프로세스는 수행 중인 프로그램</strong>을 의미합니다.</p>
<p><strong>디스크에 저장된 실행 파일을 프로그램</strong>이라고 하고, 메모리에 적재되어 <strong>CPU 자원을 할당 받아 실행</strong>되고 있을 때 <strong>프로세스</strong>라고 부릅니다.</p>
<p>운영체제는 다양한 프로그램을 Windows에서 Ctr+Alt+Delete 명령어를 통해 운영체제에 의해 관리되고 있는 프로세스의 목록을 확인할 수 있습니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/4600b3ad-2293-4966-9210-353b7ba776c3/image.png" alt=""></p>
<br>

<h3 id="프로세스의-구성">프로세스의 구성</h3>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/b091176e-325c-4cc5-afb2-a75bd4d7ca39/image.png" alt=""></p>
<ul>
<li><strong>스택(Stack) :</strong> 임시 데이터 저장소(함수의 매개변수, 복귀 주소, 지역 변수)</li>
<li><strong>힙(Heap)</strong> : 실행 중에 동적으로 할당되는 메모리</li>
<li><strong>데이터 섹션(Data)</strong> : 전역 변수</li>
<li><strong>텍스트 섹션(Text)</strong> : 프로그램 코드를 포함</li>
<li>프로그램 카운터, 레지스터</li>
</ul>
<br>

<h3 id="프로세스-제어-블록pcb">프로세스 제어 블록(PCB)</h3>
<p>특정 프로세스와 연관된 여러 정보를 수록한 블록입니다.</p>
<p><strong>하나의 프로세스가 CPU 자원을 사용하다가 다른 프로세스가 CPU 자원을 사용하기 위해서는, 이전 운영체제가 프로세스의 상태를 기억하고 있어야 합니다.</strong> 프로세스 간 병행 수행을 위해 CPU 할당을 스위치 하는 것을 문맥 교환이라고 하고, 문맥 교환을 위해서 PCB를 필요로 합니다.</p>
<p>PCB에는 다음과 같은 프로세스의 상태가 저장됩니다.</p>
<ul>
<li>프로세스 상태, 번호</li>
<li>프로그램 카운터</li>
<li>CPU 레지스터</li>
<li>CPU 스케줄링 정보(우선순위, 스케줄링 큐 포인터)</li>
<li>메모리 관리 정보(프로세스에 할당된 메모리)</li>
<li>회계 정보(CPU 사용량, 경과 시간)</li>
<li>입/출력 상태 정보(프로세스에 할당된 입출력 장치, 열린 파일 리스트)</li>
</ul>
<br>

<h3 id="멀티-프로세스">멀티 프로세스</h3>
<p>하나의 응용 프로그램을 여러 개의 프로세스로 구성하여 각 프로세스가 하나의 작업을 처리 </p>
<p>ex) 구글 크롬 브라우저같은 경우 브라우저, 렌더러, 플러그인 프로세스로 나뉨</p>
<ul>
<li><strong>장점</strong> : 프로세스 간 독립적이어서 동기화 작업이 필요하지 않으므로 <strong>안정성이 확보</strong>된다.</li>
<li><strong>단점</strong> : 메모리를 공유하지 않으므로 IPC라는 통신 기법을 통해 프로세스 간 통신을 해야하는데, 이 <strong>과정이 어렵고 복잡</strong>하다. 또한 문맥 교환 과정에서 <strong>오버헤드가 발생</strong>할 가능성이 높다.</li>
</ul>
<p><br><br></p>
<h2 id="📍스레드">📍스레드</h2>
<hr>
<ul>
<li><strong>CPU 이용의 기본 단위</strong></li>
<li><strong>프로세스의 실행 단위</strong></li>
</ul>
<br>

<h3 id="스레드의-구성multi-thread">스레드의 구성(Multi Thread)</h3>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/e101c694-7ca6-435d-961e-7e07269b05ae/image.png" alt=""></p>
<p>스레드는 <strong>스레드 ID, 프로그램 카운터, 레지스터 집합, 스택</strong>으로 구성되어 있습니다. 그리고 같은 프로세스에 속한 다른 스레드와 <strong>코드, 데이터 섹션, 열린 파일이나 신호 같은 운영체제 자원을 공유</strong>합니다.</p>
<p>각자 <strong>스택</strong>과 <strong>PC 레지스터</strong> 값을 가지고 있어 각각의 스레드가 <strong>독립적인 작업</strong>을 수행할 수 있고, <strong>명령어의 어디까지 수행</strong> 했는지 알 수 있습니다.</p>
<br>

<h3 id="멀티-스레드">멀티 스레드</h3>
<p>하나의 프로세스를 멀티 스레드를 통해 자원을 공유하고, 자원의 생성 및 관리의 중복성을 최소화하여 수행 능력을 향상시키는 것을 <strong>멀티 스레딩</strong>이라고 합니다. </p>
<p>ex) 디스플레이 갱신, 네트워크 요청 응답들은 대부분 <strong>멀티 스레드(2개 이상의 스레드)</strong>로 동작합니다. </p>
<br>

<p><strong>장점</strong></p>
<ul>
<li><strong>응답성</strong> : 프로세스의 일부분이 봉쇄되거나, 긴 작업을 수행해도 프로그램을 지속 수행할 수 있습니다. 특히 사용자 인터페이스 설계에 유용합니다.</li>
<li><strong>자원 공유</strong> : 프로세스의 자원들과 메모리를 공유하기 때문에, 같은 주소 공간 내에 여러개의 다른 작업을 하는 스레드 수행이 가능합니다.</li>
<li><strong>경제성</strong> : 스레드 생성과 문맥교환 오버헤드가 프로세스 보다 적습니다.</li>
<li><strong>규모 적응성</strong> : 멀티 프로세서 구조에서 각각의 스레드를 병렬로 수행할 수 있습니다.</li>
</ul>
<p><strong>단점</strong></p>
<ul>
<li><strong>동기화 문제</strong> : 멀티 스레드가 멀티 프로세스보다 확연한 장점을 보이지만, 이는 멀티 스레드의 동기화 문제를 잘 해결했을 때의 이야기 입니다. <strong>서로 다른 스레드가 Data, Heap 공간을 공유하기 때문에, 스레드 동기화 기법을 통해 처리해야 합니다.</strong></li>
<li><strong>하나의 스레드에 문제가 발생</strong>하면 전체 프로세스가 영향을 받아 <strong>강제 종료</strong>될 수 있습니다.</li>
</ul>
<br>
<br>

<h2 id="📌-멀티-프로세스-대신-멀티-스레드를-사용하는-이유"><strong>📌 멀티 프로세스 대신 멀티 스레드를 사용하는 이유</strong></h2>
<hr>
<ul>
<li><p><strong>자원을 효율성 증대 및 처리 비용 감소 :</strong>  프로세스 생성 시 자원을 할당하는 <strong>시스템 콜이 감소하고, 스레드 간의 자원 공유가 간단</strong>하기 때문에 <strong>자원 소모가 적습니다</strong>.</p>
</li>
<li><p><strong>응답 시간 단축 :</strong> 프로세스의 경우 <strong>Context Switching 시</strong> CPU 레지스터, RAM과 CPU 사이의 <strong>캐시 메모리가 초기화되기 때문에 오버헤드</strong>가 큰 반면, <strong>스레드는 Context Switching시 Stack영역만 처리하면 되므로 스레드 간의 문맥 교환 속도가 빠릅니다.</strong></p>
</li>
</ul>
<br>

<h4 id="대신-서로-다른-스레드가-data-heap-공간을-공유하기-때문에-스레드-동기화-기법을-통해-처리해야-합니다">대신, 서로 다른 스레드가 Data, Heap 공간을 공유하기 때문에, 스레드 동기화 기법을 통해 처리해야 합니다.</h4>
<br>

<blockquote>
<p>참고 자료
<a href="https://cocoon1787.tistory.com/688">https://cocoon1787.tistory.com/688</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Compose] 재사용 가능한 Composable 함수 작성하기]]></title>
            <link>https://velog.io/@evergreen_tree/Compose-%EC%9E%AC%EC%82%AC%EC%9A%A9-%EA%B0%80%EB%8A%A5%ED%95%9C-Composable-%ED%95%A8%EC%88%98-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@evergreen_tree/Compose-%EC%9E%AC%EC%82%AC%EC%9A%A9-%EA%B0%80%EB%8A%A5%ED%95%9C-Composable-%ED%95%A8%EC%88%98-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 06 Jul 2022 10:32:43 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/c7257917-690e-40ee-93a8-7be1027cf92c/image.png" alt=""></p>
<blockquote>
<p>Compose로 UI를 작성하면서 &quot;Modifier&#39; can&#39;t be called in this context by implicit receiver. Use the explicit one if necessary&quot; 라는 이슈를 만나게 되었고, 그런 김에.. 재사용 가능한 Composable을 올바르게 작성하는 방법을 공유하려고 합니다.</p>
</blockquote>
<br>

<h2 id="1-문제-파악하기">1. 문제 파악하기</h2>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/5fc1ec87-60a9-4f0b-a953-073bac5d79ff/image.png" alt="">
예시로 쉽게 파악할 수 있게, 토이프로젝트의 일부 화면을 가져왔습니다. 
위 화면은 Compose로 작성된 UI입니다. 저는 여기서 반복적으로 사용되는 코드를 줄이고 이를 하나로 묶기 위해 Composable 함수를 작성하려고 합니다. </p>
<h4 id="그러면-여기서-반복적으로-사용되는-컴포넌트는-무엇일까요">그러면 여기서 반복적으로 사용되는 컴포넌트는 무엇일까요?</h4>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/f3f53a5e-7cec-4d17-b614-90b2423ed2fd/image.png" alt=""></p>
<p>흰색 배경에 Round Corner을 가지고 있는, 이 Box가 공통적으로 사용되고 있습니다. </p>
<pre><code class="language-kotlin">Box(
    modifier = modifier
            .fillMaxWidth()
            .height(68.dp)
            .clip(RoundedCornerShape(20.dp))
            .background(White),
        contentAlignment = alignment
    ) {
         Text(
                modifier = Modifier.padding(start = 16.dp),
                text = buildAnnotatedString {
                            withStyle(SpanStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold)) {
                                append(&quot;지금은 땅이 쉬는중이에요\n&quot;)
                            }
                            append(&quot;커밋해서 잔디 씨앗을 뿌려주세요&quot;)
                        },
                color = Black,
                fontSize = 12.sp,
                fontFamily = suit,
                fontWeight = FontWeight.Normal
         )
    }
}
</code></pre>
<p>기본적으로 위 화면은 이러한 코드들로 이루어 지고 있습니다. 하나의 컴포저블을 작성하는데에는 이렇게 처리해도 그렇게 큰 문제는 없습니다.</p>
<p>그러나, 이러한 컴포저블이 많아졌을 때 <strong>round corner와  background를 컴포저블을 작성할 때 마다 설정해 주는것은 너무 비효율적입니다.</strong>
그래서 우리는 이를 재사용이 가능하도록 컴포저블 함수로 분리시키겠습니다. 
<br>
<br></p>
<h2 id="2-composable-함수-만들기">2. Composable 함수 만들기</h2>
<p>이런 함수를 하나 만들어 줍니다. 유동적으로 변화해야 하는 부분은 <strong>parameter</strong>로, 공통적으로 적용되는 부분은 블록 내에 작성될 것입니다.</p>
<p> 먼저 <strong>공통점</strong>을 찾아야 겠죠?</p>
<pre><code>1.  RoundCornerShape(20.dp)
2.  background(white)</code></pre><p>이렇게 두개 정도가 될 것입니다. 
하지만 크기나 정렬, 클릭 가능 여부 등, <code>Modifier</code> 의 속성같은 경우는 유동적으로 변경될 수 있어야 합니다.</p>
<p><strong>예를 들면</strong> 아래 사진과 같이, 일정한 비율로 한 행에 존재할 수 있고, 들어갈 내용물도 달라야 합니다. 
<img src="https://velog.velcdn.com/images/evergreen_tree/post/0f977b1b-ed73-4f03-afb2-bdfdddcbf18a/image.png" alt=""></p>
<p>그러면 위의 것들을 종합하여 컴포저블 함수를 작성해 보겠습니다.</p>
<pre><code class="language-kotlin">@Composable
internal fun RoundCornerBox(
    modifier: Modifier,
    alignment: Alignment = Alignment.CenterStart,
    content: @Composable BoxScope.() -&gt; Unit
) {
    Box(
        modifier = modifier
            .clip(RoundedCornerShape(20.dp))
            .background(White),
        contentAlignment = alignment
    ) {
        content()
    }
}</code></pre>
<p>함수의 인자로 <code>modifier</code>, <code>alignment</code>, <code>content</code> 를 받고 있습니다. </p>
<p><code>modifier</code> 같은 경우 클릭 가능 여부나 크기 등이 바뀔 수 있으므로, 상태를 외부에서 전달 받을 수 있도록 합니다.</p>
<p><code>alignment</code> 같은 경우, 기본적으로 <code>CenterStart</code>를 사용하는데 잔디 이미지같은 경우 아래에 위치하므로 필요 시 다른 것을 전달하면 됩니다.</p>
<p><code>content</code> 를 통해서 다른 위젯들을 전달받을 수 있습니다. 
그러나 여기서 중요한 점이 하나 있습니다.</p>
<blockquote>
</blockquote>
<p><code>content</code> 들은 전달받아 Box 내부에서 사용됩니다. 그런데 여기서 수신 객체를 <code>BoxScope</code> 로 지정해 주지 않으면 다음 오류가 발생합니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/bf98dfdf-edae-4efa-8199-a1fa6f20ce23/image.png" alt="">
왜냐하면 Box같은 경우, BoxScope 내에 지정된 자식들이 align 될 수 있으므로, 전달받은 인자들이 BoxScope에 포함된다는 의미로 수신객체를 지정해줘야 합니다.</p>
<pre><code class="language-kotlin">@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -&gt; Unit
) </code></pre>
<br>
<br>

<h2 id="3-사용하기">3. 사용하기</h2>
<pre><code class="language-kotlin">RoundCornerBox(
        modifier = Modifier
               .fillMaxWidth()
               .height(106.dp),
       alignment = Alignment.BottomCenter
){
    Image(
         modifier = Modifier
                  painter = painterResource(id = R.drawable.refresh),
                  contentDescription = &quot;refresh&quot;
    )
    ...
}</code></pre>
<p>사용할 때는 이런식으로 사용하면 됩니다. 결과적으로 background와 round corner을 계속 지정해 주지 않아도 유동적으로 사용할 수 있게 되었습니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] sealed class]]></title>
            <link>https://velog.io/@evergreen_tree/Kotlin-sealed-class</link>
            <guid>https://velog.io/@evergreen_tree/Kotlin-sealed-class</guid>
            <pubDate>Fri, 24 Jun 2022 13:15:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/c1e55e8d-b991-4160-8e8a-89b42bdce21c/image.png" alt=""></p>
<blockquote>
<p>개발하면서 sealed class를 잘 모르고 썼던 것 같아서 이번 토픽으로 정하게 되었습니다. Navigation, State등의 상태 나열 등에 썼었던 것 같은데 왜 sealed로 선언하는지 그 이유를 보겠습니다.   </p>
</blockquote>
<br>
<br>

<h2 id="🧷sealed-class">🧷sealed class</h2>
<hr>
<p><code>Sealed class</code>는 자기 지신이 추상 클래스이고, 자신을 상속받는 서브 클래스를 가질 수 있습니다.또한 부모 클래스를 상속받는 <strong>자식 클래스의 종류을 제한</strong>합니다.
<br></p>
<pre><code class="language-kotlin">open class State(
    val data: String? = null
)</code></pre>
<p><code>open class</code>로 선언된 <code>State</code>를 상속받는 클래스는 다른 곳에서 참조하여 상속받아 사용할 수 있습니다.
<br></p>
<pre><code class="language-kotlin">sealed class State()</code></pre>
<p>하지만 <code>sealed class</code>는 <strong>해당 패키지 내</strong>에서만 자식 클래스를 상속할 수 있는 <strong>추상 클래스</strong>입니다. </p>
<p>추상 클래스이기 때문에 sealed class의 인스턴스를 생성할 수는 없습니다.</p>
<br>

<h3 id="sealed-class-선언-방법">sealed class 선언 방법</h3>
<ol>
<li><strong>보통 선언 방법</strong></li>
</ol>
<pre><code class="language-kotlin">sealed class State

object Success : State()
object Failure : State()</code></pre>
<br>

<ol start="2">
<li><strong>Nested class(중첩 클래스로 선언)</strong><pre><code class="language-kotlin">sealed class State{
 object Success : State()
 object Failure : State()
}</code></pre>
<br>
<br>

</li>
</ol>
<h2 id="🚩sealed-class의-특징">🚩sealed class의 특징</h2>
<hr>
<h3 id="자식-클래스-제한">자식 클래스 제한</h3>
<p><code>sealed class</code>와 그 하위 클래스는 한 <strong>패키지</strong> 내에 존재하기 때문에, 컴파일 시간에 컴파일러에게 이들이 알려집니다. 그렇기 때문에 반대로 <strong>다른 파일에서는 하위 클래스들을 선언하면 오류가 발생</strong>합니다.</p>
<blockquote>
<p>동일 패키지 내라는 것은 <code>package com.evergreen.todaycommit.domain.usecase</code> 라는 패키지가 있다면 이 <code>usecase</code> 내에서만 선언되어야 한다는 뜻입니다.</p>
</blockquote>
<pre><code class="language-kotlin">sealed class State()

class Success : State()
class Failure : State()</code></pre>
<br>

<h3 id="생성자-제한">생성자 제한</h3>
<pre><code class="language-kotlin">sealed class State(){
    private constructor(data: String): this()
    protected constructor(data: String, data2: String): this()
}</code></pre>
<p><code>private</code>,  <code>protected</code> 이 두 가시성 만을 가질 수 있습니다.
<br>
<br></p>
<h2 id="✋sealed-class의-장점">✋sealed class의 장점</h2>
<hr>
<p>여태까지 특징을 설명했지만 어떤 상황에 사용하는 지 아직까지 감이 오지 않을 것 입니다.
<code>sealed class</code>의 장점은 자식 클래스를 패키지 내에 제한하는 <code>sealed class</code>의 특징에서 나옵니다.
<strong>when을 사용할 때, 하위 클래스들의 케이스를 else 없이 커버할 수 있습니다.</strong></p>
<p>이런 예시를 한번 생각해 봅시다. </p>
<p>어떠한 상태를 표현하려고 합니다</p>
<ol>
<li>성공</li>
<li>로딩중</li>
<li>에러</li>
</ol>
<p>이러한 경우에 따라 메시지를 출력하는 예제를 만들어 보겠습니다.
<br></p>
<h3 id="abstract-class로-구현">abstract class로 구현</h3>
<pre><code class="language-kotlin">abstract class State

class Loading : State()
class Success : State()
class Error : State()

fun test(state: State){
    return when(state){
        is Success -&gt; &quot;성공&quot;
        is Loading -&gt; &quot;로딩중&quot;
        is Error -&gt; &quot;에러&quot;
    }
}</code></pre>
<p>위 코드는 컴파일시 아무런 오류가 없을까요? 개발자의 입장에서는 <code>State</code>의 모든 케이스를 커버해 줬으니 오류가 나지 않을 것이라고 예상합니다. 하지만 결과는 이렇습니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/1ca60fa3-c791-4168-98dd-2693c3432a70/image.png" alt="">
내용을 보면, else 브랜치를 필요로 한다는 문구를 볼 수 있습니다.  왜냐하면, <strong>컴파일러가 State를 상속받은 하위 클래스들의 종류를 다 알지 못하기 때문입니다.</strong></p>
<p>그렇다면 이 코드에서 else -&gt; &quot;그 외 상태&quot; 문구를 추가해 주면 끝이지 않겠느냐 하는 생각이 드실 수도 있습니다. </p>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/d8c659d9-22db-4240-8704-8df77853e617/image.png" alt=""></p>
<p>컴파일러가 딱히 오류를 잡아주지 않아 정상적인 코드라고 생각할 수 있지만, *<em>Error라는 상태를 else에서 상태가 없음으로 체크하고 있습니다. *</em></p>
<p>프로그래밍에 있어서 개발자의 실수를 줄이기 위해 많은 노력을 해야만 하는데, 이러면 실수는 커녕 <strong>실수했는지도 모르는 상황이 발생</strong>해 버립니다.
<br></p>
<h3 id="sealed-class로-구현">sealed class로 구현</h3>
<pre><code class="language-kotlin">sealed class State

class Loading : State()
class Success : State()
class Error : State()

fun test(state: State): String{
    return when(state){
        is Success -&gt; &quot;성공&quot;
        is Loading -&gt; &quot;로딩중&quot;
        is Error -&gt; &quot;에러 발생&quot;
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/8a9ef20e-1799-475b-b159-bfa7496cdce5/image.png" alt="">
<code>sealed class</code>로 선언된 <code>State</code>를 상속 받았을 때에는 <code>else</code> 브랜치가 없어도 오류가 나지 않고 있습니다. 왜냐하면 컴파일러가 <code>State</code>의 자식 클래스를 알고있기 때문입니다.
<br></p>
<p>자 이제 <code>sealed class</code>의 장점에 대해 알 수 있게 되었습니다. 추가적으로 <strong>왜 class에 초록색 출이 쳐져 있는지 알아보겠습니다.</strong>
<img src="https://velog.velcdn.com/images/evergreen_tree/post/9defb05a-726a-417d-b54c-7b190c57dab0/image.png" alt="">
<code>sealed class</code>의 sub class가 상태를 가지고 있지 않거나, <code>equals</code>를 상속받고 있지 않다고 말합니다. 따라서 이를 <code>object</code>로 변환하는 것을 권하고 있습니다.</p>
<p>왜냐하면 클래스는 기본적으로 <code>hashcode()</code>,<code>eqauls()</code>를 가지고 있고, 딱히 상태를 가지고 있지 않은데 <code>class</code>로 선언되면 생성 될 수록 메모리가 낭비되기 때문입니다.</p>
<p>코틀린에서 <code>object</code>로 선언하게 되면 singleton으로 생성되므로, 이럴 때에는 object로 선언해 줘야 합니다.</p>
<br>

<h3 id="sealed-class-vs-enum-class">sealed class VS enum class</h3>
<pre><code class="language-kotlin">sealed class State{
    object Loading : State()
    data class Success(val data: String) : State()
    data class Error(val throwable: Throwable, val message: String) : State()
}</code></pre>
<p>또한 여기서enum class와의 차이점이 나오는데, enum class에서는 열거형을 표현하기 위해 클래스의 타입이 정해져 있고 object로 설정되지만 sealed class 는 하위 클래스들이 여러 객체를 생성할 수 있습니다.</p>
<br>

<blockquote>
<p>참고 자료
<a href="https://kotlinlang.org/docs/sealed-classes.html#location-of-direct-subclasses">https://kotlinlang.org/docs/sealed-classes.html#location-of-direct-subclasses</a>
<a href="https://kotlinworld.com/165?category=924651">https://kotlinworld.com/165?category=924651</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin][Delegation] 2. Delegated Property]]></title>
            <link>https://velog.io/@evergreen_tree/KotlinDelegation-2.-Delegated-Property</link>
            <guid>https://velog.io/@evergreen_tree/KotlinDelegation-2.-Delegated-Property</guid>
            <pubDate>Tue, 21 Jun 2022 13:11:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/3b747a17-fead-4d55-8ece-5fec63a913c7/image.png" alt=""></p>
<blockquote>
<p><a href="https://velog.io/@evergreen_tree/KotlinDelegation">https://velog.io/@evergreen_tree/KotlinDelegation</a> 
이전 글에 이어서, 이번에는 Delegated Property에 대해 포스팅하겠습니다. KProperty 라는 생소한 개념도 나와서 함께 작성해 보았습니다.</p>
</blockquote>
<p><br><br></p>
<h2 id="📢delegated-property">📢Delegated Property</h2>
<hr>
<ul>
<li>Class Delegation과 비슷하게 프로퍼티의 getter, setter를 다른 객체에 위임하는 것을 의미합니다.</li>
</ul>
<br>

<p><code>Character</code>라는 클래스에서 프로퍼티를 갱신하거나, 참조할 때 마다 값을 출력하는 상황을 예로 들어 보겠습니다.</p>
<pre><code class="language-kotlin">class Character {
    var hp: Int = 0
        get() {
            println(&quot;Get hp : $field&quot;)
            return field
        }
        set(value) {
            println(&quot;Get hp : $value&quot;)
            field = value
        }

    var mp: Int = 0
        get() {
            println(&quot;Get hp : $field&quot;)
            return field
        }
        set(value) {
            println(&quot;Get mp : $value&quot;)
            field = value
        }
}

fun main() {
    val character = Character()
    character.hp = 100
    character.mp = 200
    println(&quot;캐릭터의 hp는 ${character.hp} mp는 ${character.mp}&quot;)
}</code></pre>
<p><code>Character</code>라는 클래스 내에서 접근자를 작성해주고, main에서 프로퍼티를 변경해 준 후 결과를 보겠습니다.</p>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/21cf6ee1-beec-443d-8ff1-5725e2112f3f/image.png" alt=""></p>
<p>그리 복잡하지 않은 코드이지만, <strong>이 다섯줄을 출력하기 위해 소비하는 코드가 너무 많습니다. 또한 중복된 내용을 담고 있습니다.</strong> 
코드를 보면 접근자의 동작이 같은 형식을 띄고 있기에, 이를 따로 클래스에 담아서 수작업으로 <strong>위임</strong>해보겠습니다.
<br></p>
<h3 id="delegation-without-by">Delegation without by</h3>
<pre><code class="language-kotlin">class Delegator(private val name: String) {
    var value: Int = 0
    fun getter(): Int{
                println(&quot;get $name : $value&quot;)
        return value
    }
    fun setter(newValue: Int){
                println(&quot;set $name : $value&quot;)
        value = newValue
    }
}

class Character {
    var hpDelegator = Delegator(&quot;hp&quot;)
    var mpDelegator = Delegator(&quot;mp&quot;)

    var hp: Int
        get() = hpDelegator.getter()
        set(value) = hpDelegator.setter(value)

    var mp: Int
        get() = mpDelegator.getter()
        set(value) = mpDelegator.setter(value)
}

fun main() {
    val character = Character()
    character.hp = 100
    character.mp = 200
    println(&quot;캐릭터의 hp는 ${character.hp} mp는 ${character.mp}&quot;)
}</code></pre>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/22c40846-1d19-4836-bd94-ea9e1c0ad0e3/image.png" alt="">
결과는 전과 똑같습니다.</p>
<p>총 코드 라인 수는 그렇게 달라지지 않았지만, <strong>Delegator에게 공통 형식의 접근자를 만들고 위임함으로써 재사용성이 올라갔습니다</strong>. 하지만 아직까지 중복되는 코드가 많아보입니다.</p>
<p>이제 코틀린의 <strong><code>by</code></strong> 키워드를 통해 이를 더 줄여보겠습니다.
<br></p>
<h3 id="delegation-using-by">Delegation using by</h3>
<pre><code class="language-kotlin">import kotlin.reflect.KProperty

class Delegator(private var value: Int) {
    operator fun getValue(thisRef: Any?, property: KProperty&lt;*&gt;): Int {
        println(&quot;get ${property.name} : $value&quot;)
        return value
    }

    operator fun setValue(thisRef: Any?, property: KProperty&lt;*&gt;, newValue: Int) {
        println(&quot;set ${property.name} : $newValue&quot;)
        value = newValue
    }
}

class Character(hp: Int, mp: Int) {
    var hp by Delegator(hp)
    var mp by Delegator(mp)
}

fun main() {
    val character = Character(0,0)
    character.hp = 100
    character.mp = 200
    println(&quot;캐릭터의 hp는 ${character.hp} mp는 ${character.mp}&quot;)
}</code></pre>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/0ee563d0-e942-44ca-99e4-9fe7061c0c48/image.png" alt=""></p>
<p><code>Delegator</code>클래스에서 연산자 오버로딩을 통해, <strong><code>getValue</code>와 <code>setValue</code>를 제공하게 되면</strong>, <code>Character</code>클래스의 <code>hp</code>와 <code>mp</code>에 접근자를 위임받아 사용할 수 있게 됩니다.</p>
<p>getValue와 setValue 라는 연산자 오버로딩을 구현하기 위해서는, 지정된 파라미터를 꼭 제공해야 합니다.</p>
<table>
<thead>
<tr>
<th>getValue</th>
<th>setValue</th>
</tr>
</thead>
<tbody><tr>
<td>*<em>thisRef *</em> : 위임을 사용하는 클래스와 같은 타입이거나 Any 타입이어야 한다</td>
<td><strong>thisRef</strong> : 위임을 사용하는 클래스와 같은 타입이거나 Any 타입이어야 한다</td>
</tr>
<tr>
<td><strong>property</strong> : Property&lt;*&gt;거나 Any 타입이어야 한다.</td>
<td><strong>property</strong> : Property&lt;*&gt;거나 Any 타입이어야 한다.</td>
</tr>
<tr>
<td></td>
<td><strong>newValue</strong> : 위임을 사용하는 프로퍼티와 같은 타입이거나 Any 타입이어야 한다</td>
</tr>
</tbody></table>
<blockquote>
<p><code>Kproperty</code>는 kotlin.reflect 패키지에 선언된 인터페이스로, <code>val</code> or <code>var</code>로 선언된 프로퍼티의 정보를 가지는 인터페이스입니다.</p>
</blockquote>
<pre><code class="language-kotlin">operator fun getValue(thisRef: Any?, property: KProperty&lt;*&gt;) {
        println(&quot;set ${property.name} : $newValue&quot;)
 }</code></pre>
<p> 만약 <code>val hp</code>라면  <code>property.name</code> 에서 “hp”에 대해 얻을 수 있습니다. <code>KProperty</code>의 인스턴스는 Reflection 연산자인 ::으로부터 얻을 수 있습니다.</p>
<br>
Delegated Property를 통해, 보일러 플레이트 코드를 줄이고 코드의 재사용성을 늘릴 수 있겠군요!
<br>

<blockquote>
<p>참고 자료
<a href="https://kotlinlang.org/docs/delegation.html#overriding-a-member-of-an-interface-implemented-by-delegation">https://kotlinlang.org/docs/delegation.html#overriding-a-member-of-an-interface-implemented-by-delegation</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin][Delegation] 1. Class Delegation]]></title>
            <link>https://velog.io/@evergreen_tree/KotlinDelegation</link>
            <guid>https://velog.io/@evergreen_tree/KotlinDelegation</guid>
            <pubDate>Tue, 21 Jun 2022 12:51:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/f9e743e1-7bd7-446a-9d3b-18835de42bd8/image.png" alt=""></p>
<blockquote>
<p>코틀린에서 많이 사용하는 by 키워드, 특히나 뷰모델을 사용할 때 많이 봤었습니다. 
val viewModel: MainViewModel by viewModels()
단순히 viewModel을 위임하는 것이라고 알고 있었는데, 위임한다는 것이 무슨 의미이고, 어떤 식으로 작동하는지 알아보려고 합니다.</p>
</blockquote>
<br>
<br>

<h2 id="🌎delegate-pattern">🌎Delegate Pattern</h2>
<hr>
<blockquote>
<p>소프트웨어 엔지니어링에서 delegate pattern(위임 패턴)
은 객체 합성이 상속과 동일하게 코드 재사용을 할 수 있도록 하는 객체 지향 디자인 패턴이다.
<a href="https://en.wikipedia.org/wiki/Delegation_pattern">https://en.wikipedia.org/wiki/Delegation_pattern</a>
Delegate Pattern은 디자인 패턴 중 하나로, 한 객체가 다른 객체로부터 기능 일부를 넘겨받아 데이터를 제공하거나 특정 작업을 수행 할 수 있게 하는 것입니다. </p>
</blockquote>
<br>
<br>

<h2 id="🚩in-oop">🚩In OOP</h2>
<hr>
<p>객체지향에서는 <strong>상속을 허용하지 않는 클래스에 새로운 기능을 추가</strong>할 때 위임을 사용할 수 있습니다. 
위임을 사용하면 <strong>상속하지 않고 기존 기능을 그대로 사용하면서 새로운 기능을 추가</strong> 할 수 있습니다.</p>
<br>
<br>

<h2 id="🏬delegation-in-kotlin">🏬Delegation In Kotlin</h2>
<hr>
<p>Kotlin에서는 Delegate Pattern을 <code>by</code>라는 키워드로 제공하고 있습니다. 
이 <code>by</code> 라는 키워드는 Delegation을 직접 구현하는 데에서 생기는 보일러 플레이트 코드의 감소를 목적으로 사용됩니다. </p>
<br>

<h3 id="class-delegation">Class Delegation</h3>
<pre><code class="language-kotlin">interface Champion {
    val hp: Int,
    val mp: Int,
    fun info()
}</code></pre>
<p>League of Legend라는 게임을 예시로 들어서 설명해 보겠습니다.
<code>Champion</code>이라는 인터페이스는 체력과 마나를 뜻하는 <code>hp</code>, <code>mp</code>, 체력과 마나 정보를 출력하는 <code>info()</code> 메서드를 가지고 있습니다.
<Br></p>
<pre><code class="language-kotlin">class User(private val champ: Champion) : Champion {
    override val hp: Int = champ.hp
    override val mp: Int = champ.mp
    override fun info() = champ.info()
}</code></pre>
<p><code>User</code>라는 클래스는, <code>Champion</code>을 직접 구현하는 것이 아닌, <code>Champion</code> 인터페이스의 구현체를 인자로 받아, <strong>프로퍼티와 메서드를 위임 받고 있습니다</strong></p>
<pre><code class="language-kotlin">class Ezreal : Champion {
    override val hp: Int = 500
    override val mp: Int = 400

    override fun info() {
print(&quot;체력은 $hp, 마나는 $mp 입니다.&quot;)
    }
}

fun main(){
    val user = User(Ezreal())
    user.info()
}</code></pre>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/f2e1706a-c68a-4da0-aa51-e5097cc94398/image.png" alt=""></p>
<p>자 그러면 이렇게 <code>User</code>에 <code>Champion</code>의 구현체인 <code>Ezreal</code>을 전달하면, <code>User</code> 객체는 <code>Ezreal</code>을 상속받지 않더라도 <code>Ezreal</code>의 기능을 사용할 수 있습니다. </p>
<p>  하지만, 프로퍼티가 많으면 많아질수록 위임하는 코드는 많아질 것이고, 이는 불필요한 코드를 야기할 것입니다.
<br></p>
<p>  <strong>이를 위해 코틀린에서 지원하는 키워드가 by 입니다.</strong></p>
<pre><code class="language-kotlin">class User(private val champ: Champion) : Champion by champ</code></pre>
<p><code>Champ by champion</code> parameter로 들어온 <code>champion</code>을 <strong><code>by</code></strong> 라는 키워드를 통해 위임해주면 이처럼 한줄로 해결할 수 있게 됩니다.</p>
<br>

<h3 id="위임-된-클래스에서-변수를-선언하는-경우">위임 된 클래스에서 변수를 선언하는 경우</h3>
<pre><code class="language-kotlin">class User(private val champ: Champion) : Champion by champ {
    override val hp = 300
}

fun main(){
    val user = User(Ezreal())
    user.info()
}</code></pre>
<p>하지만, by 키워드를 사용하여 위임된 클래스에서 <strong>프로퍼티를 따로 설정해 두었을 때</strong>, delegate object인 <code>user</code>는 이를 모르게 됩니다. 즉, hp 300은 출력되지 않게 됩니다.
<br></p>
<h3 id="그렇다면-왜-delegation을-사용하는가-상속-받으면-되는-것-아닌가">그렇다면 왜 Delegation을 사용하는가? 상속 받으면 되는 것 아닌가?</h3>
<p>코틀린의 기본 라이브러리는 <code>open</code>되지 않은 <code>final</code>클래스입니다. </p>
<p>코틀린에서는 표준 라이브러리의 무분별 상속을 지양합니다. <strong>상속을 통해 클래스의 확장을 필요로 할 때가 있는데 위임을 사용하면 상속과 비슷하게 <code>final</code> 클래스의 모든 기능을 사용하면서, 동시에 기능을 확장할 수 있습니다.</strong></p>
<br>

<blockquote>
<p>다음 포스팅으로 이어집니다!
<a href="https://velog.io/@evergreen_tree/KotlinDelegation-2.-Delegated-Property">https://velog.io/@evergreen_tree/KotlinDelegation-2.-Delegated-Property</a>  </p>
</blockquote>
<br>



<blockquote>
<p>참고 자료
<a href="https://kotlinlang.org/docs/delegation.html#overriding-a-member-of-an-interface-implemented-by-delegation">https://kotlinlang.org/docs/delegation.html#overriding-a-member-of-an-interface-implemented-by-delegation</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] inline class]]></title>
            <link>https://velog.io/@evergreen_tree/Kotlin-inline-class</link>
            <guid>https://velog.io/@evergreen_tree/Kotlin-inline-class</guid>
            <pubDate>Mon, 13 Jun 2022 14:10:26 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/c39bd982-8561-4729-b046-fe529e13613e/image.png" alt=""></p>
<blockquote>
<p>이번 토픽은 inline class입니다. inline fun은 사용해 봤지만 inline class는 사용해 본적이 없는 것 같아서 주제로 선정하게 되었습니다. 클래스 내부 구조를 살펴볼 때 몇번 본 것 같은데, 왜 사용하는지 알아보겠습니다.</p>
</blockquote>
<br>
<br>

<h2 id="🏃♀️inline-class--컴파일러가-부리는-마법">🏃‍♀️Inline Class : 컴파일러가 부리는 마법</h2>
<hr>
<pre><code class="language-kotlin">@JvmInline
value class Password(private val s: String)</code></pre>
<ul>
<li>다른 변수와 혼동되지 않기 위해 값을 래핑할 수 있는 클래스</li>
<li>value로 선언함으로써 컴파일러가 객체를 생성하지 않고 값을 매핑해 줌</li>
</ul>
<p><code>@JvmInline</code> 어노테이션과 <code>value</code> 키워드를 붙이면 value(inline) 클래스를 선언할 수 있습니다.</p>
<br>
코틀린의 1.3 버전에서는 inline class가 alpha 버전으로 사용되었는데, 1.5.0 이후부터는 `value`와 `@JvmInline`을 붙여서 사용하도록 권장하고 있습니다.

<blockquote>
<p><code>@JvmInline</code> 은 KotlinJs, KotlinNative 등 다른 버전과 호환을 위해 존재하는 어노테이션 입니다.</p>
</blockquote>
<p>이렇게만 보면 어차피 wrapper 클래스를 사용할거면 data class를 사용하지 굳이? 라는 생각이 들 수 있습니다. 물론 저도 맨 처음에는 그렇게 생각했었습니다. 그런 여러분들을 위해 준비해보았습니다!</p>
<br>
<br>

<h2 id="🤷♂️why-use-inline-class">🤷‍♂️Why use Inline Class?</h2>
<hr>
<blockquote>
<p>Sometimes it is necessary for business logic to create a wrapper around some type. However, it introduces runtime overhead due to additional heap allocations. Moreover, if the wrapped type is primitive, the performance hit is terrible, because primitive types are usually heavily optimized by the runtime, while their wrappers don&#39;t get any special treatment.</p>
<p>To solve such issues, Kotlin introduces a special kind of class called an <em>inline class</em>. Inline classes are a subset of <a href="https://github.com/Kotlin/KEEP/blob/master/notes/value-classes.md">value-based classes</a>. They don&#39;t have an identity and can only hold values.</p>
</blockquote>
<p>공식 문서에 나와 있는 내용을 요약하면 이렇습니다.</p>
<p>비즈니스 로직을 위해 특정 type을 <strong>Wrapper</strong>로 감싸줘야 할 때가 있는데, 이 때 <strong>객체를 생성하기 위해 힙 할당을 하게 됨으로써 런타임 오버헤드가 발생</strong>하고 성능 저하가 일어난다는 이야기를 하고 있습니다. 
따라서 <strong>이를 해결하기 위해 inline 클래스</strong>를 사용할 수 있습니다.</p>
<p>솔직히 아직까진 잘 모르겠습니다. 처음부터 차근차근 짚어나가겠습니다.</p>
<pre><code class="language-kotlin">fun alarm(millis: Long){
    print(&quot;$millis 밀리 후에 알람이 울립니다.&quot;)
}

fun test(){
    alarm(2000L)
}</code></pre>
<p><code>alarm</code>이라는 함수를 통해 간단히 출력하는 것을 구현해 보았습니다.</p>
<p> 여기서 2000L이라는 parameter를 넣어 호출해 보겠습니다. </p>
<p>별 문제 없어 보이는 코드일 수 있습니다. 하지만 2000밀리세컨드는 2초라는 사실을 잘 모르는 경우도 있을 것이고 , <strong>다른 비즈니스 로직을 적용하더라도 소수만 알아 들을 수 있는 어떠한 값이 있을 것입니다.</strong>
<br></p>
<p>이러한 문제를 Wrapper Class를 작성함으로써 해결할 수 있습니다.</p>
<pre><code class="language-kotlin">fun alarm(duration: Duration){
        print(&quot;{$duration.millis} 밀리 후에 알람이 울립니다.&quot;)
}

fun test(){
        alarm(Duration.seconds(2))
}

data class Duration private constructor(
        val millis: Long,
) {
    companion object {
        fun millis(millis: Long) = Duration(millis)
        fun seconds(seconds: Long) = Duration(seconds * 1000)
    }
}</code></pre>
<p>이런 식으로 <code>Duration class</code>를 통해 2초라는 사실을 바로 확인할 수 있습니다. </p>
<p><strong>그러나</strong>, <code>data class</code>를 사용한다면 <strong>매번 alarm을 호출할 때 마다, Duration이라는 객체를 생성하여 할당해 줘야 합니다.</strong>
<br></p>
<p>추가 힙 할당으로 생기는 런타임 오버헤드를 방지하기 위해, <strong>value</strong>라는 modifier를 앞에 붙여주어 inline class를 선언해 보겠습니다. </p>
<pre><code class="language-kotlin">fun alarm(duration: Duration){
        print(&quot;{$duration.millis} 밀리 후에 알람이 울립니다.&quot;)
}

fun test(){
        alarm(Duration.seconds(2))
}

@JvmInline
value class Duration private constructor(
        val millis: Long,
) {
    companion object {
        fun millis(millis: Long) = Duration(millis)
        fun seconds(seconds: Long) = Duration(seconds * 1000)
    }
}</code></pre>
<p>value class로 선언된 Duration은, 파라미터로 들어올 때, <strong>객체의 프로퍼티로 대체됩니다.</strong> 
자바로 디컴파일 하면서 무슨일이 일어나고 있는지 확인해 보겠습니다.
<br></p>
<h3 id="data-class">data class</h3>
<pre><code class="language-kotlin">public static final void alarm(@NotNull Duration duration) {
      Intrinsics.checkNotNullParameter(duration, &quot;duration&quot;);
      String var1 = duration.getMillis() + &quot; 밀리 후에 알람이 울립니다.&quot;;
      System.out.print(var1);
}</code></pre>
<p>자바로 디컴파일 했을 때, <code>data class</code>로 선언된 <code>Duration</code> 의 경우, 객체 자체를 받아서 객체 내의 프로퍼티를 가져오고 있습니다.
<br></p>
<h3 id="value-class">value class</h3>
<pre><code class="language-kotlin">public static final void alarm_ZlhV6Ys(long duration) {
      String var2 = duration + &quot; 밀리 후에 알람이 울립니다.&quot;;
      System.out.print(var2);
   }</code></pre>
<p><code>fun alarm(duration: Duration)</code> 분명히 코틀린 코드는 <code>Duration</code>을 받고 있으나, 자바로 디컴파일 했을 때 위와 같이 <code>Duration</code> 내의 프로퍼티를 가져오고 있습니다.</p>
<br>

<p>여기서 디컴파일 시 <strong><code>_ZlhV6TYs</code> 처럼 특정한 문자열을 붙이는 것을 맹글링이라고 합니다.</strong></p>
<pre><code class="language-kotlin">@JvmInline
value class People(val name: String){
    fun f(people: People) {}
    fun f(name: String) {}
}</code></pre>
<p>위와 같이 메서드 오버로딩을 적용했을 때, 맹글링이 적용되지 않는다면 디컴파일 하였을 때 메서드 이름과 parameter가 중복이 됩니다.</p>
<p>이처럼, 컴파일러가 자동으로 최적화를 진행해 주기 때문에, 객체를 할당하지 않아도 Wrapper 클래스를 통해 가독성을 높일 수 있습니다.
<br>
<br></p>
<h2 id="🧏♀️value-class의-특징">🧏‍♀️value class의 특징</h2>
<hr>
<ul>
<li><strong>프로퍼티를 하나만 가질 수 있음</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/d4c9a7bc-bfe3-46eb-8dd4-e5a9148dcd16/image.png" alt=""></p>
<ul>
<li><p><code>equals()</code>, <code>toString()</code>, <code>hashCode()</code> 메서드만 생성(data class는 <code>copy()</code>, <code>componentN()</code> 까지 생성)</p>
</li>
<li><p>=== 연산을 지원하지 않음</p>
</li>
<li><p>val 프로퍼티만 허용</p>
</li>
</ul>
<blockquote>
<p>결론은 어떠한 데이터를 Wrapper 클래스로 감쌀 때, value class를 사용하자가 되겠습니다..!</p>
</blockquote>
<br>

<blockquote>
<p>참고 자료
<a href="https://kotlinlang.org/docs/inline-classes.html">https://kotlinlang.org/docs/inline-classes.html</a>
<a href="https://velog.io/@dhwlddjgmanf/Kotlin-1.5%EC%97%90-%EC%B6%94%EA%B0%80%EB%90%9C-value-class%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90">https://velog.io/@dhwlddjgmanf/Kotlin-1.5에-추가된-value-class에-대해-알아보자</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] Generic And Variance]]></title>
            <link>https://velog.io/@evergreen_tree/Kotlin-Generic-And-Variance</link>
            <guid>https://velog.io/@evergreen_tree/Kotlin-Generic-And-Variance</guid>
            <pubDate>Fri, 10 Jun 2022 15:48:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/559d3248-6a13-4c2c-a60e-3fadcfc80a34/image.png" alt=""></p>
<blockquote>
<p>자바에서도 Generic에 대해 얕게 이해하고 있었는데, Kotlin에서 in, out를 만나니까 제대로 이해하지 못하고 있다는걸 깨닫게 되었습니다. Kotlin 공식 문서를 참고하여 포스팅하였습니다.</p>
</blockquote>
<br>

<h2 id="🖐generic">🖐Generic</h2>
<hr>
<ul>
<li>Data Type Generalize</li>
<li>클래스 내부에서 사용할 Data Type을 컴파일 시 미리 지정하는 것을 의미합니다.</li>
</ul>
<br>

<h3 id="class-혹은-interface-선언">Class 혹은 Interface 선언</h3>
<pre><code class="language-kotlin">class State&lt;T&gt;(t: T) {
    var value = t
}</code></pre>
<p>자바와 마찬가지로, 클래스에 <code>&lt;type argument&gt;</code>를 붙여, 선언합니다.</p>
<br>

<h3 id="인스턴스-생성">인스턴스 생성</h3>
<pre><code class="language-kotlin">val state: State&lt;String&gt; = State&lt;String&gt;(&quot;ㅎㅇ&quot;)</code></pre>
<p>클래스의 인스턴스를 생성할 때, <code>&lt;&gt;</code> 안에 데이터 타입을 명시하고 인스턴스를 생성할 수 있습니다.</p>
<pre><code class="language-kotlin">val state = State(&quot;ㅎㅇ&quot;)</code></pre>
<p>혹은, 따로 데이터 타입을 명시하지 않고 생성자를 통해 유추할 수 있다면 <strong>컴파일러가 자료형을 자동으로 추론</strong>합니다.</p>
<br>

<h2 id="🤷♂️variance">🤷‍♂️Variance</h2>
<hr>
<p>제네릭을 설명할때 이 <strong>Variance(가변성)</strong> 이라는 개념을 빼놓을 수 없습니다. </p>
<p>Variance는 <strong>제네릭 타입의 계층 관계</strong>가 어떤지를 나타내는 개념입니다. Variance에는 세 가지 종류가 있습니다.</p>
<p><strong>Type A가 Type B의 하위 타입일 때</strong></p>
<ul>
<li>invariance(무공변) : <code>Class&lt;A&gt;</code> 와 <code>Class &lt;B&gt;</code>의 상속 관계가 없음</li>
<li>covariance(공변) : <code>Class&lt;A&gt;</code>는 <code>Class&lt;B&gt;</code>의 하위 타입</li>
<li>contravariance(반공변) : <code>Class&lt;A&gt;</code>는 <code>Class&lt;B&gt;</code>의 상위 타입</li>
</ul>
<p>코틀린에서는 자바와 마찬가지로 기본적으로 <strong>Generic Class 간 invariance(무공변) 상태</strong>입니다. 이는 밑에서 공변성을 이해할 때 더 쉽게 이해할 수 있을 것입니다.</p>
<blockquote>
<p>객체지향 제 5원칙 중, <strong>리스코프 치환 원칙</strong>을 지키기 위해서는, 상위 타입이 사용되는 경우 하위 타입의 인스턴스를 통해 동작할 수 있어야 합니다. 
Generic에서도 이를 지키기 위해서 Variance라는 개념을 사용하게 됩니다.</p>
</blockquote>
<br>
<br>

<h2 id="👨🦳covariance">👨‍🦳Covariance</h2>
<hr>
<p>먼저 자바에서 예를 들어보겠습니다.</p>
<h3 id="java에서의-covariance공변">Java에서의 covariance(공변)</h3>
<pre><code class="language-java">
interface Collection&lt;E&gt; ... {
    void addAll(Collection&lt;E&gt; items);
}</code></pre>
<p>임의로 <code>addAll</code>이라는 메서드를 작성합니다.</p>
<p>매개변수로 받은 <code>items</code>에게 <code>E</code> 타입의 모든 <code>item</code>을 넘겨주는 메서드입니다.</p>
<pre><code class="language-java">void copyAll(Collection&lt;Object&gt; to, Collection&lt;String&gt; from) {
    to.addAll(from);
}</code></pre>
<p><code>copyAll</code>을 통해 <code>to</code>로 받은 인자가 <code>addAll</code>을 호출하여 <code>from</code>에게 데이터를 넘겨줘봅시다. _위의 코드는 오류가 날까_요?</p>
<p>네. <strong>왜냐하면 String는 Object의 하위 타입이지만, <code>List&lt;String&gt;</code>은 <code>List&lt;Object&gt;</code>의 하위 타입이 아니기 때문입니다.</strong> 이를 <strong>무공변(invariance) 관계라고 합니다</strong>
위와 같은 경우 런타임 안정성을 보장하기 위해 컴파일 타임에 오류를 발생 시킵니다.</p>
<br>

<p>자바에서는 이를 해결하기 위해 <strong>와일드 카드</strong>라는 개념을 이용합니다. </p>
<pre><code class="language-java">interface Collection&lt;E&gt; ... {
    void addAll(Collection&lt;? extends E&gt; items);
}</code></pre>
<p>익숙하지 않은 <code>&lt;? extends E&gt;</code> 가 보이는군요. 자바에서는 <code>&lt;? extends E&gt;</code> 를 통해서 <strong>E 타입 또는 E의 하위 유형을 허용</strong>할 수 있게 하여 <strong>covariance</strong>(공변) 관계로 만듭니다.</p>
<p>자 다시 공변성에 대해서 짚고 가보겠습니다.</p>
<ul>
<li>covariance(공변) : Type A가 Type B의 하위 타입일 때 <code>Class&lt;A&gt;</code>는 <code>Class&lt;B&gt;</code>의 하위 타입</li>
</ul>
<p>Generic Type 이 상속 관계를 가지고 있더라도 Generic Class는 상속 관계가 아닙니다. 
즉, 기본적으로 무공변 상태인 것입니다. 자바에서는 이를 해결하기 위해 <strong>wildcard type argument</strong>를 제공하고, <strong>공변 관계로 만들 수 있습니다.</strong></p>
<p>이제 우리는 Generic의 covariance에 대해 이해할 수 있게 되었습니다.🎈 
<br></p>
<h3 id="kotlin에서의-covariance공변">Kotlin에서의 covariance(공변)</h3>
<pre><code class="language-kotlin">interface List&lt;out E&gt; ... {
   addAll(items: List&lt;E&gt;)
}</code></pre>
<pre><code class="language-java">fun copyAll(to: List&lt;Object&gt;, from:List&lt;String&gt;) {
    to.addAll(from);
}</code></pre>
<p>Kotlin에서는 클래스에 <code>out</code>을 붙여 컴파일러에게 공변 상태임을 알려주게 됩니다.</p>
<p>보통 제네릭 인자가 생산자가 되는 경우, 즉 제네릭 타입에서 들어온 인수가  하위 타입이어서 상위 타입에 안전하게 할당하는 경우 <code>out</code>이 사용됩니다. 
코틀린에서는 이 <code>out</code> 이라는 수식어를 Variance Annotation이라고 부릅니다.</p>
<br>
<br>

<h2 id="👩🦰contravariance반공변">👩‍🦰contravariance(반공변)</h2>
<hr>
<p>공변의 반대 개념으로,  공변을 보완하기 위해 나온 개념입니다. 공변과는 대조적으로 <strong>제네릭 인자가 소비자가 되는 경우에 반공변성을 부여</strong>합니다.</p>
<p>Java에서는 <code>&lt;? super E&gt;</code> , 코틀린에서는 <code>in</code> 을 통해서 반공변성을 부여합니다.</p>
<p>Kotlin의 예시를 하나 들어보겠습니다.</p>
<pre><code class="language-kotlin">interface Comparable&lt;in T&gt; {
    operator fun compareTo(other: T): Int
}

fun demo(x: Comparable&lt;Number&gt;) {
    x.compareTo(1.0) 
    val y: Comparable&lt;Double&gt; = x
}</code></pre>
<p>Type Number의 하위 Type은 Double입니다. 하지만 T가 <code>in</code> 이기 때문에 인수로 들어오는 <code>Comparable&lt;Number&gt;</code> 는 하위 타입이 됩니다. 따라서  x의 데이터를 Double로 형변환 한 후, y에게 x를 대입하여도 컴파일 에러가 발견되지 않게 됩니다.</p>
<blockquote>
<p><strong><a href="https://en.wikipedia.org/wiki/Existentialism">The Existential</a> Transformation: Consumer in, Producer out!</strong>
:-)
→ T가 소비자면 in, T가 생산자면 out으로 이해하면 될 것입니다.</p>
</blockquote>
<br>
<br>

<h2 id="🏓kotlin-vs-java">🏓Kotlin VS JAVA</h2>
<hr>
<p>근데 여기서 Kotlin과 Java와 차이점은 무엇일까요? </p>
<p><strong>Kotlin</strong>에서는 <strong>interface(혹은 class)에 out 키워드</strong>를 붙여 공변성을 제공하고, <strong>자바</strong>는<code>void addAll(Collection&lt;? extends E&gt; items);</code>  E를 사용하는 메서드에 공변성을 제공합니다.</p>
<ul>
<li><p>Kotlin에서는 이 방식을 Declaration-Site Variance(선언 위치 변환)</p>
</li>
<li><p>자바에서는 이 방식을 Use-Site Variance(사용 위치 변환) 이라고 부릅니다.</p>
</li>
</ul>
<p>뭐가 더 좋을까요? 라고 부른다면, 뻔하지만 Java의 상위 호환인 <strong>Kotlin</strong>입니다.</p>
<p><em>Kotlin에서는 클래스에서 out을 한번 선언하면 되지만, Java에서는 매번 붙여줘야 하기 때문에 불필요한 코드가 발생합니다. 물론 코틀린에서도 Use-Site Variance를 제공합니다.</em></p>
<br>

<blockquote>
<p>참고 문서
<a href="https://kotlinlang.org/docs/generics.html#type-erasure">https://kotlinlang.org/docs/generics.html#type-erasure</a></p>
</blockquote>
<p>✅ 코틀린의 다른것이 궁금할땐 아래 링크 확인
<a href="https://velog.io/@ham2174">https://velog.io/@ham2174</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] Property]]></title>
            <link>https://velog.io/@evergreen_tree/Kotlin-Property</link>
            <guid>https://velog.io/@evergreen_tree/Kotlin-Property</guid>
            <pubDate>Tue, 07 Jun 2022 14:29:05 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/f6246a75-67d8-4b15-9ab1-9ecb4265789f/image.png" alt=""></p>
<blockquote>
<p>코틀린 기본 문법 중 프로퍼티에 대한 이해를 확실하게 하고, 설명할 수 있을 정도로 이해하기 위해 글을 작성하게 되었습니다.</p>
</blockquote>
<br>


<h2 id="🧴캡슐화">🧴캡슐화</h2>
<hr>
<p>객체지향 프로그래밍에는 <strong>캡슐화</strong>라는 개념이 있습니다. 
데이터와, 그 데이터를 처리하는 메소드를 묶어 클래스 안에 보호하는 것입니다. 
이를 위해 내부 데이터는 <code>private</code>으로 접근할 수 없게 하고, <code>public</code>으로 선언된 메서드를 통해 접근했었습니다.</p>
<br>

<h2 id="☕at-java">☕At Java</h2>
<hr>
<p>자바에서는 이를 구현하기 위의 아래 방식을 사용했었습니다.</p>
<pre><code class="language-kotlin">class Person {
    private String name;

    Person() {

    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}</code></pre>
<p>객체의 상태를 나타내는 데이터를 Field, 객체가 하는 행동을 Method라고 하였습니다. 또한 캡슐화를 위해 getter와 setter 메서드를 작성하여 데이터에 접근하도록 하였습니다.</p>
<p>하지만 위 방식은 필드가 많아짐에 따라 <strong>getter와 setter의 보일러 플레이트 코드를 증가시켰습니다.</strong></p>
<br>

<h2 id="🙋♂️코틀린의-property란">🙋‍♂️코틀린의 Property란?</h2>
<hr>
<ul>
<li><strong>field + getter + setter</strong> == <strong>property</strong></li>
</ul>
<pre><code class="language-kotlin">class Person {
    var name: String = &quot;&quot;
}</code></pre>
<p>코틀린에서는, 다음과 같이 변수를 선언하기만 하여도 <strong>내부적</strong>으로 getter와 setter를 구현합니다. 이를 함께 불러 property라고 부릅니다.</p>
<pre><code>public final class Person {
   @NotNull
   private String name = &quot;&quot;;

   @NotNull
   public final String getName() {
      return this.name;
   }

   public final void setName(@NotNull String var1) {
      Intrinsics.checkNotNullParameter(var1, &quot;&lt;set-?&gt;&quot;);
      this.name = var1;
   }
}</code></pre><p>코틀린에서 작성된 Person를 디컴파일 해보면, 다음과 같은 자바 코드를 볼 수 있습니다.</p>
<br>

<h2 id="🙋♂️getter와-setter">🙋‍♂️getter와 setter</h2>
<hr>
<pre><code class="language-kotlin">var &lt;propertyName&gt;[: &lt;PropertyType&gt;] [= &lt;property_initializer&gt;]
    [&lt;getter&gt;]
    [&lt;setter&gt;]</code></pre>
<p>property를 선언하는 문법입니다. getter와 setter를 따로 선언하지 않아도, getter와 setter가 내장되어 있습니다.  </p>
<pre><code class="language-kotlin">class Person {
    var name: String = &quot;&quot;
        get() = field
        set(value){ field = value }
}</code></pre>
<p> 만약 이를 직접 구현한다면 이렇게 생겼습니다. <code>field</code>라는 식별자를 통해 데이터에 접근할 수 있고, 매개변수를 <code>value</code> 등으로 제공할 수 있습니다.</p>
<blockquote>
<p>field 식별자는, backing field입니다. 적어도 하나 이상의 기본 접근자를 사용할 때 만들어집니다.</p>
<pre><code class="language-kotlin">val isEmpty: Boolean
    get() = this.size == 0
</code></pre>
<p> 이런 상황에서는 field가 만들어지지 않습니다.</p>
</blockquote>
<br>

<p>또한 접근자를 커스텀 할 수도 있습니다.</p>
<h3 id="get-응용">get() 응용</h3>
<pre><code class="language-kotlin">class Person(val height: Int, val weight: Int) {
    val bmi: Int 
        get() = this.weight / (this.height * this.height)
}</code></pre>
<p>area라는 변수는, width x height의 값을 가지고 있어야 합니다. 만약 코드를 이처럼 작성하게 된다면, area를 호출할 때 마다, width x height를 계산하여 호출하게 됩니다.</p>
<h3 id="set-응용">set() 응용</h3>
<pre><code>var date: String
    get() = this.toString()
    set(value) {
        dateToString(value)
    }</code></pre><p>마찬가지로 setter에 setDataFromString이라는 어떠한 데이터를 가져와 String으로 Parsing하는 메서드를 통해 호출할 때 마다 할당할 수 있습니다.</p>
<h3 id="private-접근자">private 접근자</h3>
<pre><code class="language-kotlin">class Person{
    private var cardNumber: String = &quot;121-121-1&quot;
        private get() = field
}</code></pre>
<pre><code class="language-kotlin">class Person{
    var cardNumber: String = &quot;121-121-1&quot;
        private set
}</code></pre>
<p>위와 같이 접근자를 private으로 선언할 수도 있습니다. </p>
<ul>
<li><strong>getter</strong>을 private으로 선언했다면, 변수도 private 해야합니다. 외부에서 참조하지 않고 내부에서 사용될 수 있습니다.</li>
<li><strong>setter</strong>를 private으로 선언했다면, 외부에서 값을 가져올 순 있지만, 할당할 수는 없게 됩니다.</li>
</ul>
<br>

<blockquote>
<p>참고문서
<a href="https://kotlinlang.org/docs/properties.html">https://kotlinlang.org/docs/properties.html</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] 클린 아키텍처, 어느정도가 적당한가?]]></title>
            <link>https://velog.io/@evergreen_tree/Android-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%96%B4%EB%8A%90%EC%A0%95%EB%8F%84%EA%B0%80-%EC%A0%81%EB%8B%B9%ED%95%9C%EA%B0%80</link>
            <guid>https://velog.io/@evergreen_tree/Android-%ED%81%B4%EB%A6%B0-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%96%B4%EB%8A%90%EC%A0%95%EB%8F%84%EA%B0%80-%EC%A0%81%EB%8B%B9%ED%95%9C%EA%B0%80</guid>
            <pubDate>Fri, 03 Jun 2022 04:26:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클린 아키텍처로 프로젝트를 진행하면서 겪었던 많은 고민을 공유하기 위해 글을 작성하게 되었습니다. 늘 그랬듯 많은 관심과 지적 부탁드립니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/52fce79c-bd35-4739-8474-f370f8cf63e6/image.png" alt=""></p>
<p>안드로이드 앱 개발을 시작하던 초기에, 같이 프로젝트를 진행하면서 멘토였던 선배로부터 디자인 패턴과 함께 <strong>아키텍처</strong>라는 개념을 처음 접하게 되었습니다.
당시 RecyclerView조차 제대로 이해도 못하는 수준이었고, 모듈을 나눠서 관리하구나! 정도로 이해했습니다. 클린아키텍처의 ㅋ자도 몰랐던 거였죠..</p>
<blockquote>
<p>클린 아키텍처에 대한 좋은 글들이 매우 많습니다. 안드로이드에서의 클린 아키텍처를 한마디로 말하자면 <strong>관심사의 분리</strong>라고 할 수 있습니다. 
<del>사실 한마디로 말하기에는 너무 크고 심오한 내용인 것 같습니다.</del></p>
</blockquote>
<br>
<br>

<h2 id="💁♂️monolithic-clean-architecture">💁‍♂️Monolithic Clean Architecture</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/dd862f49-8478-4999-b7d7-e9f2f0890ccd/image.png" alt=""></p>
<p>초기 안드로이드 개발을 시작할때 진행했던 프로젝트의 구조가 위 그림과 같았습니다. 
기본적으로 주어지는 app 모듈 내에, domain, Data, Presentation 패키지로 나누었습니다. 
Domain Layer를 한번 살펴볼까요?</p>
<pre><code class="language-kotlin">public class ConvenienceUseCase implements RetrofitOnSuccess {

    private ConvenienceMarkerRepository convenienceMarkerRepository;
    private ConvenienceFragment convenienceFragment;
    private ArrayList&lt;Document&gt; list = new ArrayList&lt;&gt;();

    public ConvenienceUseCase(ConvenienceFragment convenienceFragment){
        convenienceMarkerRepository = new ConvenienceMarkerRepository();
        this.convenienceFragment = convenienceFragment;
    }

    public void sendLocalName(String Category){ 

    ...
</code></pre>
<p>먼저 Repository를 멤버변수로 참조하고 있기에 <strong>객체 간 결합도가 강한 것을 확인</strong>할 수 있습니다. 
또한 안드로이드 프레임워크에 의존성이 없어야 하고, Presentation에 대해 몰라야 하는데 Fragment를 알고 있습니다. 
부분적인 코드 뿐 아니라, 전체적으로 보더라도 단일 모듈로 클린 아키텍처를 적용하게 되면서,** 클린 아키텍처의 약속이 잘 지켜지지 않고 있었습니다.** </p>
<br>
<br>

<h2 id="💁♂️multi-module-clean-architecture">💁‍♂️Multi Module Clean Architecture</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/0f275d9f-e6cc-459e-969c-dfa2322d17b9/image.png" alt=""></p>
<p>최근 진행한 프로젝트에서는 위와 같은 구조를 설계했습니다. 먼저 Multi Module로 구성되어 있기 때문에, 각 모듈이 가지는 종속성을 구분할 수 있습니다.
Clean Architecture을 기반으로 설계한 모듈 구조이므로, 아래의 의존성 관계를 가집니다.
<code>Presentation -&gt; Domain &lt;- Data</code>
<strong>Domain은 다른 레이어를 몰라야 합니다.</strong> 또한 안드로이드에 대한 종속성을 가지면 안됩니다.</p>
<blockquote>
<p><em>Domain 단에 작성한 코드는 당장 다른 플랫폼으로 옮기더라도 사용 가능할 수 있을 정도여야 한다. 따라서 순수한 Java,Kotlin 언어로 작성되어야한다.</em></p>
</blockquote>
<h4 id="자이제-여기서-우리는-domain에-안드로이드-종속성을-부여하지-않기-위해-많은-고민을-하고-얼마나-layer를-나눌-것인가-고민합니다">자,이제 여기서 우리는 Domain에 안드로이드 종속성을 부여하지 않기 위해 많은 고민을 하고, 얼마나 Layer를 나눌 것인가 고민합니다.</h4>
<br>

<h3 id="🤾♀️첫-번째-시련---paging3-구현">🤾‍♀️첫 번째 시련 - Paging3 구현</h3>
<pre><code class="language-kotlin">interface UserRepository {
    ...
    suspend fun getUser(query: Int): Flow&lt;PagingData&lt;UserModel&gt;&gt;
    ...
}</code></pre>
<p>Domain/Repository에 있는 <code>UserRepository</code> 인터페이스 입니다.
User의 행동 단위를 모아 인터페이스로 선언하게 되고, Data Layer에서는 이를 구현하여 네트워크 작업이나 DB 작업을 하게 됩니다.</p>
<p>💁‍♂️혹시 코드에서 이상한 점을 느끼셨나요? 맞습니다. Domain Layer에 안드로이드 라이브러리인 <code>PagingData</code>가 있습니다.</p>
<p>물론 Data단에서는 <code>PagingData</code>를 다루고 성공 실패에 대한 로직을 처리하여, Wrapper로 감싸서 전달할 수도 있습니다. 다만 그것은 네트워크 통신을 담당하는 Data Layer에 어울리지 않다고 생각했습니다.
테스트 종속성을 추가하거나, flow로 감싸서 전달하는 방법도 있으나, ViewModel단에서 성공 실패를 처리하기 위해 위 방법을 사용했습니다.</p>
<p>또한 이런 이야기를 들었습니다.</p>
<blockquote>
<p>아키텍처는 결국 협업을 위해 존재하는 것이다.  타당한 이유가 있다면, 어느 정도 틀 안에서 같이 일하는 동료들과의 협업을 위해 어떠한 룰을 만들어 정하면 되는 것이다.</p>
</blockquote>
<p>안드로이드의 특성상, 아키텍처를 통해 완전히 코드를 분리하는 것은 어렵다고 생각하였기에, 특수한 경우에는 의존성을 주입하기로 결정하였습니다.(ex <code>hilt</code>)</p>
<h3 id="🤾♀️두-번째-시련---retrofit-response">🤾‍♀️두 번째 시련 - Retrofit Response</h3>
<p>한번이 어렵지,, 두번은 어렵지 않았습니다. 
SkyDoves에서 제공하는 Sandwich의 <code>ApiResponse</code> 를 사용하여 결과 콜백을 처리할 수 있게 하였습니다.</p>
<pre><code class="language-kotlin">interface UserRepository {
    suspend fun login(loginModel: LoginModel): ApiResponse&lt;AuthResponse&gt;
}</code></pre>
<pre><code class="language-kotlin">loginUseCase(LoginModel(email, password.sha256())).suspendOnSuccess{
    ...
}</code></pre>
<p>안드로이드 의존성이 아니며 Result class를 따로 만들지 않아도, 거의 모든 통상적인 경우를 처리할 수 있기에 충분히 도입할 만 하다고 생각하였습니다.</p>
<blockquote>
<p><em>ApiResponse is an interface to construct standardized responses from Retrofit calls.</em></p>
</blockquote>
<br>

<h2 id="🤸♂️결론">🤸‍♂️결론</h2>
<hr>
<p>클린 아키텍처는 <strong>관심사의 분리</strong>를 통해 테스트가 용이하게 하고, <strong>유지보수가 용이</strong>할 수 있게 <strong>계층을 나누어 코드를 분리</strong>하는 것이라고 할 수 있습니다.
<strong>코드라는것이 개발자마다 다양</strong>하고, 생각하는 기준도 다르기에 각각 다르게 적용될 수밖에 없다고 생각합니다.
물론 _Uncle Bob_이 의도하는 클린아키텍처의 성격에서 크게 벗어나면 안되겠지만, <strong>협업하는 공동체에서의 룰을 지키며 적절히 사용한다면 되는 것이 아닐까 생각합니다.</strong>
긴 글 읽어주셔서감사합니다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] MVVM + AAC + FireBase Google Login #2]]></title>
            <link>https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Login-2</link>
            <guid>https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Login-2</guid>
            <pubDate>Wed, 01 Jun 2022 07:41:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/dfbcd559-eaf4-4d08-a0a8-ba075c3d1fa5/image.png" alt=""></p>
<blockquote>
<p><a href="https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Logind">https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Logind</a> 에서 이어집니다!</p>
</blockquote>
<br>


<h2 id="🤜example">🤜Example</h2>
<hr>
<p>다음 예제는 FireBase를 통해 Google 로그인을 구현하는 예제입니다. </p>
<p>AAC와 MVVM을 이용해 구현해 보았습니다.</p>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/295bec4f-7958-4367-97fe-ca88ff8514f6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/f12d84ab-8673-4f40-a3ab-ddd4fe3211f2/image.png" alt=""></p>
<br>
<br>

<h2 id="🏬structure">🏬Structure</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/35a34ff3-b835-4e72-84e9-2f43b7b79f10/image.png" alt="">
간단하게 두 패키지로 구분하였습니다. </p>
<p>repository에는 <strong>Repository</strong>, ui에는 Activity와 <strong>ViewModel</strong>이 있습니다.
<br>
<br></p>
<h2 id="🌎repository">🌎Repository</h2>
<hr>
<pre><code class="language-kotlin">class AuthRepository(
) {
    private val firebaseAuth = FirebaseAuth.getInstance()
    private val _userLiveData = MutableLiveData&lt;FirebaseUser&gt;()
    val userLiveData: LiveData&lt;FirebaseUser&gt;
        get() = _userLiveData

    fun getUser(idToken: String) {
        val credential = GoogleAuthProvider.getCredential(idToken, null)
        firebaseAuth.signInWithCredential(credential).addOnCompleteListener {
            if (it.isSuccessful) {
                _userLiveData.postValue(firebaseAuth.currentUser)
            } else {
                //실패처리
            }
        }
    }
}</code></pre>
<p>먼저 Repository입니다. MVVM의 <strong>Model</strong> 부분을 담당하게 됩니다.</p>
<p> <strong>ViewModel에서 위 코드를 작성하고 처리하지 않는 이유</strong>는, 역시나 유지보수 때문입니다.</p>
<p>위 예제는 단순히 Firebase 로그인만을 구현하고 있지만, DB에서 다른 테이블을 조회할 수도 있고, Github API를 통해 어떤 데이터를 가져올 수도 있습니다. 그러한 단위들을 R<strong>epository로 구현하여 제공한다면 좀 더 효율적으로 코드를 관리</strong>할 수 있습니다.</p>
<h3 id="livedata"><strong>LiveData</strong></h3>
<p>AAC에서 제공하는 라이브러리로, RxJava,와 유사하게 Observer 패턴을 통해 데이터를 구독하고 발행할 수 있습니다. <del>(추후 Observer 패턴에 대해 포스팅 하겠습니다.)</del></p>
<p><strong>Repository에서 LiveData 형태로 ViewModel에 제공함으로써, 추후 Activity가 데이터 변경을 관찰하여 View에 보여줄 수 있게 할 것입니다.</strong>
<br>
<br></p>
<h2 id="🚀viewmodel">🚀ViewModel</h2>
<hr>
<pre><code class="language-kotlin">class MainViewModel : ViewModel(){
    private var authRepository: AuthRepository = AuthRepository()
    private val _userLiveData = authRepository.userLiveData

    val userLiveData: LiveData&lt;FirebaseUser&gt;
        get() = _userLiveData


    fun getUser(idToken: String){
        authRepository.getUser(idToken)
    }
}</code></pre>
<p>View는 <strong>ViewModel에 대해 알고 있기 때문에</strong>, getUser을 호출하여 <strong>데이터를 변경</strong>시킬 수 있습니다. </p>
<p>즉, <strong>View의 Event</strong>를 통해 <strong>ViewModel의 메소드를 실행</strong>시켜 주면, Repository를 알고있는 ViewModel은 <strong>데이터 변경을 요청</strong>한다는 의미입니다.
<br></p>
<pre><code class="language-kotlin">fun getUser(idToken: String) {
        ...
        firebaseAuth.signInWithCredential(credential).addOnCompleteListener {
            if (it.isSuccessful) {
                _userLiveData.postValue(firebaseAuth.currentUser)
            } 
                ...
        }
    }</code></pre>
<p>ViewModel에서 데이터 변경을 요청하였고, 성공시 liveData를 currentUser로 변경합니다.</p>
<p><strong>Activity는 이 LiveData를 ViewModel을 통해 관찰하고 있기 때문에, 이 변경을 감지하게 됩니다.</strong>
<br>
<br></p>
<h2 id="🚆activity">🚆Activity</h2>
<hr>
<pre><code class="language-kotlin">class MainActivity : AppCompatActivity() {
    private lateinit var getResult: ActivityResultLauncher&lt;Intent&gt;
    private val viewModel: MainViewModel by viewModels()
    private val binding: ActivityMainBinding by lazy {
        ActivityMainBinding.inflate(layoutInflater)
    }
    private lateinit var googleSignInClient: GoogleSignInClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        setGoogleLogin()
        viewModel.userLiveData.observe(this) {
            binding.textView.text = it.displayName
        }
        binding.loginButton.setOnClickListener {
            login()
        }
        getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == Activity.RESULT_OK) {
                val task = GoogleSignIn.getSignedInAccountFromIntent(it.data)
                try {
                    val account = task.getResult(ApiException::class.java)!!
                    viewModel.getUser(account.idToken!!)
                    Toast.makeText(this, &quot;로그인 성공&quot;, Toast.LENGTH_SHORT)
                        .show()

                } catch (e: ApiException) {
                    Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    private fun login() {
        getResult.launch(googleSignInClient.signInIntent)
    }

    private fun setGoogleLogin() {
        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(BuildConfig.WEB_CLIENT_ID)
            .requestEmail()
            .build()
        googleSignInClient = GoogleSignIn.getClient(this, gso)
    }
}</code></pre>
<p>액티비티 코드입니다. 중요한 부분만 짚고 넘어가겠습니다.</p>
<pre><code class="language-kotlin">private val viewModel: MainViewModel by viewModels()</code></pre>
<p>여러가지 방법이 있을 수 있겠지만, <code>Android KTX</code> 라이브러리를 통해 뷰모델을 위임하였습니다.</p>
<p>여기서 뷰모델을 생성하게 되는데, 여기서 중요한 부분이 있습니다.</p>
<p><strong>ViewModel은 activity, Fragment 등의 Context를 참조하는 클래스를 가지고 있으면 안됩니다.</strong>
<img src="https://velog.velcdn.com/images/evergreen_tree/post/a82fc429-bb9f-4e7d-8df3-42323917d00d/image.png" alt=""></p>
<blockquote>
<p><a href="https://velog.io/@evergreen_tree/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%A1%ED%8B%B0%EB%B9%84%ED%8B%B0-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0">https://velog.io/@evergreen_tree/안드로이드-액티비티-생명주기</a> 먼저 보고 가시면 좋습니다.</p>
</blockquote>
<p><strong>ViewModel의 생명주기는 View의 생명주기와 다릅니다</strong>. 기기의 화면이 회전되어 액티비티가 onDestroy를 호출할 때에도 ViewModel은 살아있습니다. 잘못되면 <strong>Memory Leak(</strong>필요하지 않은 메모리를 계속 점유하고 있는 현상<strong>)</strong>이 발생할 수 있습니다.</p>
<p>따라서 intent 같은 activity를 참조하는 메서드도 ViewModel에서 실행하지 않았습니다.</p>
<p>하지만 여기서 또 문제가 생깁니다. MVVM을 지키려면 intent 결과 메서드도 ViewModel에 작성해야 하는 거 아니야?</p>
<h3 id="🤷♂️관심사-분리의-선">🤷‍♂️관심사 분리의 선?</h3>
<pre><code class="language-kotlin">override fun onCreate(savedInstanceState: Bundle?) {
        ...
        binding.loginButton.setOnClickListener {
            login()
        }
        getResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode == Activity.RESULT_OK) {
                val task = GoogleSignIn.getSignedInAccountFromIntent(it.data)
                try {
                    val account = task.getResult(ApiException::class.java)!!
                    viewModel.getUser(account.idToken!!)
                    Toast.makeText(this, &quot;로그인 성공&quot;, Toast.LENGTH_SHORT)
                        .show()

                } catch (e: ApiException) {
                    Toast.makeText(this, e.localizedMessage, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }
...
private fun login() {
        getResult.launch(googleSignInClient.signInIntent)
}</code></pre>
<p>필자도 저 위의 짧은 코드를 작성하기 위해 많은 고민을 했습니다.</p>
<p>Google 로그인은 <strong>intent</strong>를 통해 유저의 토큰을 가져오게 되는데, 이는 분명히 <strong>UI와 관련되지 않은 로직</strong>일 것이고 <strong>ViewModel에 들어가야 한다고 생각</strong>했습니다.</p>
<p>그래서 ViewModel을 생성할 때, interface를 만들어 제공하고 Callback형식으로 사용하는 것은 어떤가 고민을 하였지만,, 실력 좋은 현업자의 이야기도 들어보고, 여러 사람들의 코드를 확인해본 결과 <strong>intent 로직은 Activity에서 처리하는 것이 좋다고 생각하였습니다.</strong></p>
<blockquote>
<p><em>디자인 패턴에 너무 얽매이는 것은 좋지 않다. 디자인패턴, 아키텍처는 결국 협업을 위해 존재하는 것이다. 물론 디자인 패턴을 지키지 말라는 것이 아니다. 타당한 이유가 있다면, 어느 정도 틀 안에서 같이 일하는 동료들과의 협업을 위해 어떠한 룰을 만들어 정하면 되는 것이다.</em></p>
</blockquote>
<p><strong>Data Obersving</strong></p>
<pre><code class="language-kotlin">override fun onCreate(savedInstanceState: Bundle?) {
        ...
        viewModel.userLiveData.observe(this) {
            binding.textView.text = it.displayName
        }
       ...</code></pre>
<p>위 코드가 Activity에서 뷰모델의 데이터를 관찰하고 있는 부분입니다.</p>
<p>button onclick → intent → viewModel.getUser() → repository.getUser() 을 통해 데이터가 변경되면,</p>
<p>이렇게 관찰하고 있다가 바뀐 데이터를 View에 보여주게 됩니다.</p>
<p>긴 글 읽어주셔서 감사합니다.</p>
<blockquote>
<p>github : <a href="https://github.com/EvergreenTree97/MVVM-LiveData-Firebase-Example">https://github.com/EvergreenTree97/MVVM-LiveData-Firebase-Example</a></p>
</blockquote>
<blockquote>
<p>참고 자료
<a href="https://blog.gangnamunni.com/post/aac_mvvm/">https://blog.gangnamunni.com/post/aac_mvvm/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] MVVM + AAC + FireBase Google Login #1]]></title>
            <link>https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Login</link>
            <guid>https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Login</guid>
            <pubDate>Wed, 01 Jun 2022 07:27:47 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/db7f5021-3686-41b6-8d5a-d709b6bad3bb/image.png" alt=""></p>
<blockquote>
<p>구글 로그인을 구현해볼 겸 안드로이드 Architecture Components를 활용해 mvvm과 observer pattern을 이해하기 위해 예제를 만들게 되었습니다.
생각보다 mvvm의 View와 Logic 분리에 고민을 많이 하게 되는 시간이었습니다.
조언, 반박 언제든 환영입니다!</p>
</blockquote>
<br>

<h2 id="👨🦳서론">👨‍🦳서론</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/9f001544-52fb-478f-8185-eb3c7031b594/image.png" alt="">
위 이미지는 필자가 처음 안드로이드 개발을 시작했을 때의 패키지 구조입니다.</p>
<p>Model, Network, Activity 등등의 코드가 아주 <del>자유분방</del>하게 섞여있습니다.</p>
<p>또한  내부 코드도 마찬가지로 다양하게 혼재하고 있습니다.</p>
<p>앱의 규모가 작고, 혼자서 진행하는 프로젝트이면 그렇게 큰 문제가 일어나지는 않을 것입니다.</p>
<p><strong>하지만,</strong> 앱의 규모가 커지면 커질수록, Activity와 Fragment의 코드는 방대해질 것이며, app 패키지 내에서 원하는 파일을 찾기 위한 비용이 만만치 않을 것입니다.</p>
<p>먼저 소개할 <strong>AAC</strong>라는 개념은 위 문제를 포함하여 앱을 개발하면서 만날 수 있는 문제들을 해결하기 위해 라이브러리를 제공합니다.</p>
<br>
<br>

<h2 id="🤷♂️android-architecture-componentsaac">🤷‍♂️Android Architecture Components(AAC)</h2>
<hr>
<p>→ 안드로이드 앱을 개발하면서 <strong>자주 만날 수 있는 문제들을 쉽게 해결</strong>할 수 있는 <strong>라이브러리</strong>들의 모음</p>
<ul>
<li>테스트와 유지관리가 쉬운 앱을 디자인하도록 돕는 <strong>라이브러리 모음</strong></li>
<li><strong>ViewModel</strong>은 앱 회전시 제거되지 않는 UI 관련 데이터를 저장</li>
<li><strong>LiveData</strong>를 사용하여 모델 변경되면 뷰에 알리는 데이터 개체를 빌드</li>
<li><strong>~~Room</strong>은 SQLite 개체 매핑 라이브러리 RxJava, Flowable, LiveData Observable을 반환~~</li>
</ul>
<br>

<h3 id="그래서-왜-사용하는가">그래서 왜 사용하는가?</h3>
<p>개발자의 평생 과제인 유지보수를 위해서 ,정확히는 <strong>관심사의 분리</strong>를 위해서 입니다.</p>
<p><strong>View에 모든 코드를 관리하는 것에서 생기는  문제를 해결</strong>하기 위해  AAC인 LiveData, ViewModel을 사용하면서 <strong>아래의 아키텍처를 적용합니다.</strong>
<img src="https://velog.velcdn.com/images/evergreen_tree/post/24cc9e9d-af82-4997-85f1-1cbd1bafbeac/image.png" alt="">
위의 다이어그램은 구글에서 권장하는 아키텍처를 다이어그램으로 표현한 것입니다.</p>
<ol>
<li><strong>View</strong>(Activity/Fragment)는 <strong>ViewModel의 LiveData를 구독</strong>하고 있습니다. 따라서 ViewModel의 <strong>데이터가 변경되면 Activity에 보여지게</strong> 됩니다.</li>
<li><strong>ViewModel은 LiveData</strong>를 가지고 있으며, 로컬의 DB, 혹은 Network 통신을 통해 데이터를 가져와 발행할 수 있습니다.</li>
</ol>
<p>한줄 요약하면, <strong>Repository로부터 ViewModel에서 데이터를 관리하고 View는 이를 관찰하고 있다는 것입니다.</strong> 이제 여기서 <strong>AAC를 적용하여 MVVM 패턴</strong>을 적용할 수 있습니다.
<br>
<br></p>
<h2 id="💁♂️mvvm-패턴">💁‍♂️MVVM 패턴</h2>
<hr>
<p>→ UI와 Logic을 분리할 수 있는 <strong>디자인 패턴</strong>입니다. 구조는 아래 이미지와 같습니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/e56aa615-99f5-459a-8255-35856720a924/image.png" alt=""></p>
<p><strong>Model</strong></p>
<ul>
<li>데이터 모델, 비즈니스 로직, 데이터를 포함합니다</li>
<li>보통 <strong>Repository(DB,Network)에서 데이터를 반환</strong>합니다.</li>
</ul>
<p><strong>View</strong></p>
<ul>
<li>Activity, Fragment등, 뷰의 역할을 담당합니다. </li>
<li><strong>비즈니스 로직은 배제</strong>하지만, <strong>UI 관련 로직, intent는 수행</strong>할 수 있습니다.</li>
</ul>
<p><strong>ViewModel</strong></p>
<ul>
<li>View에서 보여줘야 하는 데이터와, 명령을 담고 있습니다.</li>
<li>보통 View가 Viewmodel의 데이터를 바라보고 있는 &quot;<strong>Observer Pattern</strong>&quot;의 형태로 사용되기 때문에
  <strong>ViewModel에서 모델의 데이터를 변경하면 이를 감지하고 변경된 데이터를 View가 보여줍니다.</strong></li>
</ul>
<blockquote>
<p>여기서 AAC ViewModel과 MVVM의 ViewModel은 다릅니다.
<strong>AAC ViewModel</strong> : UI 데이터를 유지, 관리하기 위해 AAC에서 제공하는 <strong>클래스</strong>
<strong>MVVM ViewModel</strong> : MVVM 아키텍처에서의 한 <strong>역할</strong>, View의 데이터를 관리해주고 바인딩 해주는 역할</p>
</blockquote>
<br>

<h3 id="의존성-관계">의존성 관계</h3>
<p>View → ViewModel → Repository</p>
<p>View는 <strong>ViewModel을 알지만</strong>, <strong>Repository는 ViewModel을 모릅니다.</strong> (import 하고 있지 않다는 뜻입니다.)</p>
<p>FireBase와 GoogleLogin 관련된 문서는 공식 문서만큼 잘 나와있는 곳이 없는 것 같습니다.</p>
<p><a href="https://firebase.google.com/docs/android/setup?hl=ko">여기</a>를 참고해주세요!</p>
<blockquote>
<p>다음 포스팅에서 예제를 통해 이해해 보겠습니다.
<a href="https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Login-2">https://velog.io/@evergreen_tree/Android-MVVM-AAC-FireBase-Google-Login-2</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android][MVI] 3. MVI의 SideEffect Cycle]]></title>
            <link>https://velog.io/@evergreen_tree/AndroidMVI-3.-MVI%EC%9D%98-SideEffect-Cycle</link>
            <guid>https://velog.io/@evergreen_tree/AndroidMVI-3.-MVI%EC%9D%98-SideEffect-Cycle</guid>
            <pubDate>Mon, 09 May 2022 15:00:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@evergreen_tree/AndroidMVI-2.-MVI%EC%9D%98-Purecycle">https://velog.io/@evergreen_tree/AndroidMVI-2.-MVI%EC%9D%98-Purecycle</a> 에서 이어집니다.</p>
</blockquote>
<br>

<h2 id="🚶♂️sideeffect">🚶‍♂️SideEffect</h2>
<hr>
<p>항상 모든 흐름이 <strong>Pure cycle</strong>로 이루어 질 순 없습니다. </p>
<p>앱은 <strong>외부세계의 상태를 변화</strong>시켜야 하는 상황이 발생하기도 하고, <strong>외부 세계로부터 상태가 변화</strong>될 수 있습니다.</p>
<p>예를들어, Composable 함수 같은 경우, compose 내의 <strong>State가 변경되면 이를 통해 재 호출 되어 Recomposition 되는 형태</strong>로만 존재해야 합니다.</p>
<pre><code class="language-kotlin">@Composable
fun ExampleButton{
    SideEffect{
        systemUiController.setStatusBarColor(
            color = BackgroundColor
        )
    }
}</code></pre>
<p>하지만 위 코드처럼, <strong>systemUiController의 상태를 변경</strong>시켜야 하는, 즉 <strong>SideEffect를 발생</strong>시켜야 하는 상황에 직면하기도 합니다.</p>
<p>SideEffect()는, Composition이 성공적으로 완료되었을 때 부수 효과를 예약하는 함수로 Compose에서 부수 효과를 처리하기 위한 방법 중 하나입니다.</p>
<p>Compose 얘기는 여기하지 하고, 안드로이드의 Orbit FrameWork 이용하여 MVI의 SideEffect Cycle을 구현해 보겠습니다.</p>
<br>
<br>

<h2 id="👨💼side-effect-cycle">👨‍💼Side effect Cycle</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/e565b9a0-6443-49d3-8189-cbc378c65cb8/image.png" alt=""></p>
<p>SideEffect Cycle은 기존 Pure Cycle에 SideEffect를 추가한 것입니다.
<code>intent()</code>를 사용하여 <strong>새로운 상태를 생성함</strong>과 <strong>동시에 부수 효과를 실행</strong>하고, 인텐트로 결과를 내보내거나 아무 일도 일어나지 않습니다.</p>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/72eb1d99-a517-4114-8a85-6155ffc5359d/image.gif" alt=""></p>
<p>버튼을 클릭했을 때 토스트 메시지를 출력하는 간단한 앱을 MVI + Compose로 만들어 보겠습니다.</p>
<h3 id="sideeffect-정의">SideEffect 정의</h3>
<pre><code class="language-kotlin">data class CalculatorState (
    val total: Int = 0
)

sealed class CalculatorSideEffect{
    data class Toast(val text: String): CalculatorSideEffect()
}</code></pre>
<ul>
<li>SideEffect에 대한 클래스를 생성합니다.</li>
</ul>
<blockquote>
<p>함수형 프로그래밍인 자바스크립트에서도, Side Effect는 존재합니다. 함수가 일관된 결과를 보장하지 못하거나, 함수 외부 어디든지 조금이라도 영향을 주는 경우에 발생하게 됩니다.</p>
</blockquote>
<br>

<h3 id="sideeffect-post">SideEffect Post</h3>
<pre><code class="language-kotlin">class CalculatorViewModel: ViewModel(), ContainerHost&lt;CalculatorState, CalculatorSideEffect&gt; {

    override val container =container&lt;CalculatorState,CalculatorSideEffect&gt;(CalculatorState())

    fun add(number :Int) = intent{//intent - 앱의 상태를 변화시키려는 의도
        postSideEffect(CalculatorSideEffect.Toast(&quot;Adding $number to ${state.total}!&quot;))
        reduce{
                 state.copy(total = state.total + number)
        }
    }
}</code></pre>
<ul>
<li><p>ViewModel 에서 State와 SideEffect를 가지고 container를 재정의합니다. </p>
</li>
<li><p>add라는 메소드를 intent에 담고 <strong>Toast라는 SideEffect에 Toast에 들어갈 메시지를 담아 SideEffect를 보냅니다.</strong></p>
</li>
<li><p>연산된 결과를 reduce를 통해 새로운 state를 만들어 내는 과정은 Pure Cycle과 같습니다. 단지 postSideEffect가 추가 되었을 뿐입니다.</p>
</li>
</ul>
<br>

<h3 id="launch-sideeffect">Launch SideEffect</h3>
<pre><code class="language-kotlin">@Composable
fun TestPage(
    viewModel: CalculatorViewModel,
)

...

LaunchedEffect(viewModel){
viewModel.container.sideEffectFlow.collect{
when(it){
            is CalculatorSideEffect.Toast -&gt; Toast.makeText(context,it.text, Toast.LENGTH_SHORT).show()
        }
    }
}
...</code></pre>
<ul>
<li>Composable 함수에서 LaunchedEffect 블럭 안에서, SideEffect를 수집하고, Toast를 실행하면 끝입니다.</li>
</ul>
<br>
<br>

<h2 id="✍mvi의-장단점">✍MVI의 장단점</h2>
<hr>
<h3 id="장점">장점</h3>
<ul>
<li>앱의 상태가 하나뿐이기 때문에, 상태 충돌이 없음</li>
<li>데이터의 흐름이 정해져 있어 흐름을 이해하고 관리하기 쉬움</li>
<li>각각 값이 불변하기 때문에, 스레드 안정성을 갖는다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li>러닝커브가 높음 <del>(필자도 이해하느라 어려웠습니다.)</del></li>
<li>위의 앱에서 봤듯이, 간단한 앱이라도 Intent와 Model을 가져야 함</li>
<li>작은 변경도 Intent를 거쳐야함</li>
<li>Model Update를 위해 매번 새로운 인스턴스를 생성해야 해서, 너무 많은 객체가 만들어지면 GC가 빈번하게 작동될 수 있음</li>
</ul>
<br>

<p>🙋‍♂️이상으로 MVI 시리즈를 마치겠습니다. 감사합니다.</p>
<br>

<blockquote>
<p>참고 자료 
<a href="https://sungbin.land/%EC%95%84%EC%A7%81%EB%8F%84-mvvm-%EC%9D%B4%EC%A0%A0-mvi-%EC%8B%9C%EB%8C%80-319990c7d60">https://sungbin.land/아직도-mvvm-이젠-mvi-시대-319990c7d60</a>
<a href="https://medium.com/@kimdohun0104/mvi-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%B0%A9%EB%B2%95-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%95%9C%EA%B3%84-767cc9973c98">https://medium.com/@kimdohun0104/mvi-패턴에-대한-고찰-이유와-방법-그리고-한계-767cc9973c98</a>
<a href="https://dev.to/kaleidot725/implementaing-jetpack-compose-orbit-mvi-3gea">https://dev.to/kaleidot725/implementaing-jetpack-compose-orbit-mvi-3gea</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android][MVI] 2. MVI의 Pure cycle]]></title>
            <link>https://velog.io/@evergreen_tree/AndroidMVI-2.-MVI%EC%9D%98-Purecycle</link>
            <guid>https://velog.io/@evergreen_tree/AndroidMVI-2.-MVI%EC%9D%98-Purecycle</guid>
            <pubDate>Mon, 09 May 2022 09:00:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4">https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4</a> 에서 이어집니다.</p>
</blockquote>
<br>

<h2 id="✌pure-cycle과-side-effect-cycle">✌Pure Cycle과 Side effect Cycle</h2>
<hr>
<p>MVI는 순수 함수로 이루어진 <strong>Pure Cycle</strong>과, 부수효과가 포함되어있는 <strong>SideEffct Cycle</strong>로 표현할 수 있습니다.  </p>
<p>이번 포스팅에서는, <a href="https://orbit-mvi.org/">https://orbit-mvi.org/</a> MVI 프레임워크 중 하나인, <strong>orbit</strong>을 사용해 MVI의 Pure cycle에 대해 이해하겠습니다.</p>
<blockquote>
<p><strong>순수 함수와 부수 효과에 대해 들어보신 적이 있나요</strong>❓</p>
<hr>
<p><strong>함수형 프로그래밍</strong> : 부수 효과를 없애고 순수 함수를 만들어 모듈화 수준을 높이는 프로그래밍 패러다임</p>
<ul>
<li><strong>부수 효과 :</strong> 외부의 상태를 변경하는 것, 함수로 들어온 인자의 상태를 직접 변경하는 것</li>
<li><strong>순수 함수 :</strong> 부수효과가 없는 함수 즉, 어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수</li>
</ul>
</blockquote>
<br>
<br>

<h2 id="👰pure-cycle">👰Pure Cycle</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/5c32c18a-879a-465e-b22d-3c3848a0d721/image.png" alt=""></p>
<p>순수 함수는 어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수입니다. </p>
<p><strong>Pure cycle</strong>은 순수 함수와 동일한 맥락입니다</p>
<p>_Pure cycle_은 <code>intent(event) == state</code>,을 만족하는 사이클입니다. 즉 어떠한 의도로 인해 발생하는 결과는 항상 “<strong>상태</strong>” 입니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/6981ab38-94f3-4665-87dc-af51bd4d07d0/image.gif" alt=""></p>
<p>버튼을 누르면 아래 숫자가 증가하는 프로그램을 Compose와 MVI 형식으로 아주 간단하게 만들어 보았습니다.</p>
<p><code>intent()</code>를 통해, <code>add(number: Int)</code>이벤트를 발생 시켜서 기존 count 값에 number을 더해 새로운 상태를 생성하는 예제를 살펴보겠습니다. </p>
<br>

<h3 id="의존성-추가">의존성 추가</h3>
<pre><code>implementation(&quot;org.orbit-mvi:orbit-viewmodel:4.3.2&quot;)
testImplementation(&quot;org.orbit-mvi:orbit-test:4.3.2&quot;)</code></pre><p>→ 먼저 orbit 의존성을 추가합니다. 왜 Framework를 이용해야 하는지는 이전 글(<a href="https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4)%EC%97%90">https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4)에</a> 나와있습니다.
<br></p>
<h3 id="model-생성">Model 생성</h3>
<pre><code>data class CalculatorState (
    val total: Int = 0
)</code></pre><p>→ View에 렌더링 할 값인 total 을 가지는 Model(State)을 생성합니다. </p>
<br>

<h3 id="viewmodel-생성-및-intent-정의">ViewModel 생성 및 intent 정의</h3>
<pre><code>class CalculatorViewModel: ViewModel(), ContainerHost&lt;CalculatorState, CalculatorSideEffect&gt; {

    override val container = container&lt;CalculatorState,CalculatorSideEffect&gt;(CalculatorState())

    fun add(number :Int) = intent{ 
        reduce{
            state.copy(total = state.total + number)
        }
    }
}</code></pre><ul>
<li><p>ViewModel 에서 State와, SideEffect를 가지고 container를 재정의합니다. 
container에 선언에는 State와 SideEffect를 명시해 줘야 하는데, 일단은 Pure cycle에 대해서만 설명할 것이므로 임의로 넣어놨습니다. </p>
</li>
<li><p>add라는 메소드를 intent에 담고 연산된 결과를 <strong>reduce를 통해 새로운 state</strong>를 만들어 냅니다.</p>
</li>
<li><p>reduce는 <strong>순수 함수</strong>로, 그저 새로운 <strong>state</strong>를 만들어 냅니다.</p>
</li>
</ul>
<br>

<h3 id="rendering">Rendering</h3>
<pre><code>@Composable
fun TestPage(
    viewModel: CalculatorViewModel,
) {
    val state by viewModel.container.stateFlow.collectAsState()
        Column{
                Button(onClick ={viewModel.add(1)}){
                Text(&quot;1이 증가하는 버튼&quot;)
    }
      Text(&quot;${state.total}&quot;)
}</code></pre><p>→ 버튼의 이벤트로 <code>viewModel.add()</code> 를 전달하게 되면 ViewModel에서 Intent로 감싸져 새로운 상태를 생성하게 됩니다.</p>
<p><strong><code>val state by viewModel.container.stateFlow.collectAsState()</code></strong></p>
<p><strong>view</strong>는 상태를 관찰하고 있기 때문에, <strong>Recomposition</strong>이 발생하여 새로운 상태를 렌더링 하게 됩니다. </p>
<blockquote>
<p>이처럼 pure cycle은 상태 관리에서 가장 중요한 역할을 하고, 개발자의 실수를 줄여줍니다. <em>예상 가능한 값을 반환</em>하기 때문에 테스트 코드를 작성하기에 용이합니다</p>
</blockquote>
<br>

<p>👉다음 포스팅에서는 SideEffect Cycle 에 대해서 알아보겠습니다.</p>
<br>

<blockquote>
<p><a href="https://sungbin.land/%EC%95%84%EC%A7%81%EB%8F%84-mvvm-%EC%9D%B4%EC%A0%A0-mvi-%EC%8B%9C%EB%8C%80-319990c7d60">https://sungbin.land/아직도-mvvm-이젠-mvi-시대-319990c7d60</a>
<a href="https://medium.com/@kimdohun0104/mvi-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%B0%A9%EB%B2%95-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%95%9C%EA%B3%84-767cc9973c98">https://medium.com/@kimdohun0104/mvi-패턴에-대한-고찰-이유와-방법-그리고-한계-767cc9973c98</a>
<a href="https://dev.to/kaleidot725/implementaing-jetpack-compose-orbit-mvi-3gea">https://dev.to/kaleidot725/implementaing-jetpack-compose-orbit-mvi-3gea</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android][MVI] 1. MVI란 무엇인가? ]]></title>
            <link>https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@evergreen_tree/Android-MVI-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Fri, 06 May 2022 09:15:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>TDD에 대해 공부하면서, 테스트 코드 예시를 작성하면서 이해하기 전에, 사용하는 디자인 패턴에 대해 이해하는 것이 테스트 코드 작성에 도움을 줄 수 있을 것 같아, 이번 프로젝트에서 사용할 MVI 패턴에 대해 알아보려고 합니다. </p>
</blockquote>
<br>
<br>

<h2 id="❓디자인패턴-mvc-mvp-mvvm">❓디자인패턴, MVC, MVP, MVVM</h2>
<hr>
<p><strong>디자인 패턴은, 개발하면서 생기는 공통적인 문제를 해결하기 위한 솔루션 입니다.</strong></p>
<p><strong>mvc, mvp, mvvm</strong> 같은 패턴들은 관심사의 분리를 통해 테스트 코드의 작성을 용이하게 해주는 등, 대규모 개발에 있어서 필수 불가결한 요소가 되었습니다.</p>
<p><strong>mvc 패턴</strong>의 View와 Model 사이의 <strong>높은 결합도</strong>는 <strong>mvp, mvvm 패턴을 파생</strong>시켰고, <strong>데이터와 뷰를 분리함으로써 얻는 장점</strong>으로 인해 현재는 많은 프로젝트에 <strong>mvp, mvvm 패턴</strong>을 사용하고 있습니다.</p>
<p><strong>하지만</strong>, 이들도 마냥 장점만 있는 것은 아니었습니다. </p>
<p>mvp, mvvm 패턴을 적용하면서 직면하는 <strong>문제</strong>로 <strong>두 가지</strong>가 발생하였습니다
<br></p>
<h3 id="🤦♀️상태-문제"><strong>🤦‍♀️상태 문제</strong></h3>
<ul>
<li>안드로이드는 어떻게 보면 <strong>상태의 집합</strong>입니다. 화면에 나타나는 모든 정보나, 버튼 활성화 등 <strong>상태</strong>들로 구성되어 있습니다.</li>
<li>상태들을 관리하기 힘들어지고, 의도하지 않는 방향으로 제어가 된다면 상태 문제가 됩니다.
<img src="https://velog.velcdn.com/images/evergreen_tree/post/8e86fcc4-7a63-46bd-bcba-a9b75856cf92/image.gif" alt="">
상태 문제의 좋은 예시로, 로그인이 실패했을 때 지속적으로 다음 화면이 보여 지는 상황이 발생합니다.</li>
</ul>
<h3 id="🤦♂️부수-효과"><strong>🤦‍♂️부수 효과</strong></h3>
<ul>
<li>네트워크 통신, 데이터베이스 접근 등의 <strong>부수 효과</strong>들로 인해 발생합니다.</li>
<li>다이얼로그, 토스트 메시지, 액티비티의 이동도 이에 포함됩니다.</li>
<li>여러 비동기 작업들이 섞여서 어떤 결과를 얻을 지 예상할 수 없고 이에 따라 상태변화에 어려움을 겪습니다.</li>
</ul>
<p>위 문제들을 MVI에서 어떻게 풀어나가는지 확인해 보면서, MVI에 대해 알아보겠습니다.</p>
<br>
<br>

<h2 id="🔥mvi">🔥MVI</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/evergreen_tree/post/63f56523-9cda-479e-8bab-8dd79a9a4331/image.png" alt=""></p>
<p>MVI는 <strong>Model</strong>, <strong>View</strong>, <strong>Intent</strong>로 이루어져 있으며, 데이터베이스, 네트워크 통신등의 작업을 진행하기 위해 <strong>SideEffect</strong>를 포함합니다.</p>
<ul>
<li>사용자 또는 시스템의 <strong>Action</strong>을 포함하는 <strong>Intent</strong>가 발생합니다.</li>
<li><strong>Intent</strong>를 통해 <strong>Model</strong>이 <strong>reducing</strong>되고, <strong>Model</strong>을 <strong>Observing</strong> 하고 있던 <strong>View</strong>는 <strong>Rendering</strong>을 통해 <strong>View</strong>를 업데이트 합니다. <br>

</li>
</ul>
<h3 id="view"><strong>View</strong></h3>
<ul>
<li>Activity나 Fragment, 즉 하나 이상의 <strong>View</strong></li>
</ul>
<h3 id="model"><strong>Model</strong></h3>
<ul>
<li>앱의 단일 <strong>상태</strong></li>
<li>뷰가 화면에 렌더링 해야 하는 것들을 명시해주는 응답으로 다음과 같은 것들이 포함 될 수 있습니다.<ul>
<li>프로그레스 진행률</li>
<li>서버로부터 받은 데이터 목록</li>
<li>예외</li>
</ul>
</li>
<li>다른 레이어와의 단방향 레이어 흐름을 위해 <strong>변경 불가</strong> 해야합니다.</li>
</ul>
<h3 id="intent">Intent</h3>
<ul>
<li>사용자 또는 앱 내의 상태를 바꾸려는 <strong>의도</strong></li>
<li>모델은 인텐트를 통해서, 새로운 상태로 변화할 수 있다.<ul>
<li>MVI의 단점 중 하나로, 가벼운 변경 사항도 이를 적용해야 합니다.</li>
</ul>
</li>
</ul>
<br>
기존의 MVC나 MVVM 같은 디자인 패턴은, 어떠한 형식에 따라 개발자가 직접 설계하는 방식이었습니다.  
MVI 패턴 같은 경우에도 직접 작성할 수 있으나, MVI 패턴은 제공되는 프레임워크를 통해 설계 하는것이 좋습니다.

<blockquote>
<p><strong>이유는 <a href="https://appmattus.medium.com/top-android-mvi-libraries-in-2021-de1afe890f27">https://appmattus.medium.com/top-android-mvi-libraries-in-2021-de1afe890f27</a> 에서 확인할 수 있습니다.</strong> 
위 링크에서, 왜 MVI 라이브러리를 사용하는지, MVI 프레임워크들의 종류들과 평가에 대해서 확인할 수 있습니다.</p>
</blockquote>
<br>

<h3 id="예고편">예고편</h3>
<p>MVI는 순수 함수로 이루어진 <strong>Pure cycle</strong>과, 부수효과가 포함되어있는 <strong>Side effct cycle</strong>로 표현할 수 있습니다.  </p>
<p>다음 포스팅에서는, <a href="https://orbit-mvi.org/">https://orbit-mvi.org/</a> MVI 프레임워크 중 하나인, <strong>orbit</strong>을 사용해 MVI의 Pure cycle에 대해 이해하겠습니다. </p>
<blockquote>
<p><strong>순수 함수와 부수 효과에 대해 들어보신 적이 있나요</strong>❓</p>
<hr>
<p><strong>함수형 프로그래밍</strong> : 부수 효과를 없애고 순수 함수를 만들어 모듈화 수준을 높이는 프로그래밍 패러다임</p>
<ul>
<li><strong>부수 효과 :</strong> 외부의 상태를 변경하는 것, 함수로 들어온 인자의 상태를 직접 변경하는 것</li>
<li><strong>순수 함수 :</strong> 부수효과가 없는 함수 즉, 어떤 함수에 동일한 인자를 주었을 때 항상 같은 값을 리턴하는 함수</li>
</ul>
</blockquote>
<br>
<br>

<blockquote>
<p>참고 문서 
<a href="https://sungbin.land/%EC%95%84%EC%A7%81%EB%8F%84-mvvm-%EC%9D%B4%EC%A0%A0-mvi-%EC%8B%9C%EB%8C%80-319990c7d60">https://sungbin.land/아직도-mvvm-이젠-mvi-시대-319990c7d60</a>
<a href="https://medium.com/@kimdohun0104/mvi-%ED%8C%A8%ED%84%B4%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%B0%A9%EB%B2%95-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%ED%95%9C%EA%B3%84-767cc9973c98">https://medium.com/@kimdohun0104/mvi-패턴에-대한-고찰-이유와-방법-그리고-한계-767cc9973c98</a>
<a href="https://dev.to/kaleidot725/implementaing-jetpack-compose-orbit-mvi-3gea">https://dev.to/kaleidot725/implementaing-jetpack-compose-orbit-mvi-3gea</a>
<a href="https://yoon-dailylife.tistory.com/117">https://yoon-dailylife.tistory.com/117</a></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>