<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sysout-achieve.log</title>
        <link>https://velog.io/</link>
        <description>기술에 생각 더하기</description>
        <lastBuildDate>Thu, 30 Jan 2025 15:37:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. sysout-achieve.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sysout-achieve" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Coroutine Dispatchers의 특이점들]]></title>
            <link>https://velog.io/@sysout-achieve/Coroutine-Dispatchers-%ED%8A%B9%EC%9D%B4%EC%A0%90%EB%93%A4</link>
            <guid>https://velog.io/@sysout-achieve/Coroutine-Dispatchers-%ED%8A%B9%EC%9D%B4%EC%A0%90%EB%93%A4</guid>
            <pubDate>Thu, 30 Jan 2025 15:37:25 GMT</pubDate>
            <description><![CDATA[<p>Coroutine Dispatchers 는 간단하게 이해하고 넘어가기 쉬운 부분이다. 이름이 나름 직관적이고 사용법이 크게 어렵지 않아서 간단하게 이해하고 넘어가기 쉽다.</p>
<p>예를 들어 Dispatchers의 종류는 네 가지가 있고, 보통 &quot;<strong>네트워크 요청</strong>은 <strong>Dispatchers.IO</strong>, <strong>UI 작업</strong>은 <strong>Dispatchers.Main</strong>&quot; 정도로만 알고 있어도 개발하는 데 크게 문제는 없다.</p>
<p>사실 그 정도만 알고 개발을 하더라도 <del>큰 문제없이 티 안나게</del> 개발은 가능하다. </p>
<p>하지만 이런 접근 방식은 <strong>예상치 못한 성능 저하, 불필요한 컨텍스트 스위칭</strong>을 초래할 가능성이 크다.
디스패처를 제대로 이해하고 활용하면, <strong>앱 성능을 최적화</strong>하고 <strong>리소스 관리 효율성을 극대화</strong>할 수 있다.</p>
<p>Dispatchers 종류와 내부 동작 원리같은 기본적인 내용은 다른 곳에서도 쉽게 찾아볼 수 있기 때문에 간단하게 정리한 게시글에서 확인하자.</p>
<h2 id="dispatchersmain과-나머지-디스패처의-차이">Dispatchers.Main과 나머지 디스패처의 차이</h2>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/61adb600-b5b0-46b9-b12a-fbb0be4aab2a/image.png" alt=""></p>
<p>차이점:</p>
<ul>
<li><p><strong>Dispatchers.Main</strong>은 <strong>안드로이드 프레임워크</strong>에서 직접 <strong>제공</strong>하는 <strong>UI 스레드</strong>를 사용한다.</p>
</li>
<li><p><strong>Dispatchers.IO와 Dispatchers.Default</strong>는 <strong>Kotlin 코루틴 라이브러리가 자체적으로 관리</strong>하는 <strong>별도의 스레드 풀</strong>을 사용한다.</p>
</li>
</ul>
<br>

<h3 id="dispatchersmain">Dispatchers.Main</h3>
<ul>
<li><p>안드로이드의 <strong>Main Looper</strong>를 사용</p>
</li>
<li><p>UI 작업을 수행하는 안드로이드 시스템 스레드를 직접 활용</p>
</li>
<li><p><strong>별도의 스레드 풀을 생성하지 않으며</strong>, <strong>기존 UI 스레드 위에서 동작</strong></p>
</li>
<li><p><strong>UI 작업을 제외한 무거운 연산</strong>을 실행하면 <strong>ANR(Application Not Responding) 발생 가능</strong></p>
</li>
</ul>
<br>


<h3 id="dispatchersmain-사용-시-발생하는-에러">Dispatchers.Main 사용 시 발생하는 에러</h3>
<p><strong>JVM 환경</strong>에서 <strong>Dispatchers.Main</strong>을 사용하면 오류가 발생한다.</p>
<pre><code>fun main() = runBlocking&lt;Unit&gt; {
    launch(Dispatchers.Main) {
        println(&quot;Dispatchers Main : ${Thread.currentThread().name}&quot;)
    }
}</code></pre><p><strong>왜 문제가 발생할까?</strong>
<img src="https://velog.velcdn.com/images/sysout-achieve/post/a697f5ee-c7b1-41a1-84c0-4c8cd9477a22/image.png" alt=""></p>
<blockquote>
<ul>
<li>Dispatchers.Main은 안<strong>드로이드 환경에서만 제공되는 메인(UI) 스레드 디스패처</strong>이다.</li>
</ul>
</blockquote>
<ul>
<li><strong>JVM 환경</strong>에서는 <strong>MainLooper가 존재하지 않으므로</strong>, 이를 사용할 수 없음.</li>
<li>이를 해결하려면 Dispatchers.setMain()을 사용하여 테스트 환경에서 Main 디스패처를 설정해야 한다. </li>
</ul>
<pre><code>Dispatchers.setMain(Dispatchers.Default)</code></pre><br>

<h3 id="dispatchersio--dispatchersdefault">Dispatchers.IO &amp; Dispatchers.Default</h3>
<ul>
<li><strong>Kotlin 코루틴 라이브러리가 관리</strong>하는 <strong>별도의 스레드 풀</strong>에서 실행</li>
<li>OS의 기본 스레드가 아니라, 코루틴 런타임에서 동적으로 생성/관리하는 스레드를 활용</li>
<li>네트워크, DB, 파일 I/O 등 백그라운드 작업을 효율적으로 수행할 수 있도록 최적화됨</li>
</ul>
<h4 id="launchdispatchersdefault에서-launchdispatchersio를-실행하면-thread가-무조건-변경될까">launch(Dispatchers.Default)에서 launch(Dispatchers.IO)를 실행하면 Thread가 무조건 변경될까?</h4>
<pre><code>launch(Dispatchers.Default) {
    println(&quot;Default 실행 스레드: ${Thread.currentThread().name}&quot;)

    launch(Dispatchers.IO) {
        println(&quot;IO 실행 스레드: ${Thread.currentThread().name}&quot;) 
    }
}</code></pre><ul>
<li>코루틴이 이미 I/O 작업을 수행할 수 있는 스레드에서 실행되고 있다면, <strong>새로운 스레드로 변경되지 않고 같은 스레드를 재사용할 수 있다</strong>.</li>
<li>현재 실행 중인 스레드가 Dispatchers.Default(CPU 연산) 전용이라면, Dispatchers.IO에 맞는 스레드로 전환될 가능성이 크다.</li>
</ul>
<blockquote>
<h5 id="kotlin의-dispatchersio와-dispatchersdefault는-별도의-스레드-풀을-사용하지만-내부적으로-같은-executor-기반에서-관리되므로-스레드를-재사용할-수-있다">Kotlin의 Dispatchers.IO와 Dispatchers.Default는 별도의 스레드 풀을 사용하지만, 내부적으로 같은 Executor 기반에서 관리되므로 스레드를 재사용할 수 있다.</h5>
<h5 id="즉-dispatchersdefault에서-실행-중이던-스레드가-이미-io-작업이-가능한-상태라면-launchdispatchersio를-해도-같은-스레드에서-실행될-가능성이-있다">즉, Dispatchers.Default에서 실행 중이던 스레드가 이미 IO 작업이 가능한 상태라면, launch(Dispatchers.IO)를 해도 같은 스레드에서 실행될 가능성이 있다.</h5>
</blockquote>
<ul>
<li><strong>Dispatchers.IO와 Dispatchers.Default는 서로 다른 용도</strong>지만, <strong>같은 스레드 풀을 공유</strong>하면서 <strong>스레드를 재사용</strong>할 수 있다.</li>
<li>따라서, launch(Dispatchers.IO)를 해도 같은 스레드에서 실행될 수 있다.</li>
<li>하지만, 현재 실행 중인 스레드가 CPU 연산 전용으로 과부하가 걸려 있다면, 새로운 스레드를 생성해서 실행될 수도 있다.</li>
</ul>
<br>
<br>
<br>

<h2 id="retrofit과-dispatchersio의-관계">Retrofit과 Dispatchers.IO의 관계</h2>
<p>많은 개발자가 <strong>Retrofit을 사용</strong>할 때 <strong>Dispatchers.IO를 반드시 써야 한다고 생각</strong>하는데,
<strong>Retrofit은 자체적으로 비동기 처리</strong>를 하기 때문에 무조건 Dispatchers.IO를 붙일 <strong>필요는 없다</strong>.</p>
<pre><code>// Dispatcher 없이 호출해도 비동기로 실행됨
suspend fun fetchData(apiService: ApiService): List&lt;Data&gt; {
    return apiService.getData()
}</code></pre><p>이렇게 써도 문제가 없다.</p>
<p>하지만 네트워크 요청 이후 데이터베이스 저장 같은 추가적인 I/O 작업이 포함되면,
Dispatchers.IO를 활용하는 게 성능 면에서 유리하다.</p>
<pre><code>suspend fun fetchData(apiService: ApiService): List&lt;Data&gt; {
    return withContext(Dispatchers.IO) {  // I/O 최적화
        val response = apiService.getData()
        saveToDatabase(response)  // DB 저장
        response
    }
}</code></pre><p><strong>네트워크 요청만 있다면</strong> → <strong>Dispatchers.IO 없어도 된다</strong>.
<strong>추가적인 I/O 작업(DB 저장 등)이 있다면</strong> → <strong>Dispatchers.IO를 쓰는 게 낫다</strong>.</p>
<h6 id="📌-출처-httpsthdevtechkotlin20210112retrofit-coroutines">📌 출처: <a href="https://thdev.tech/kotlin/2021/01/12/Retrofit-Coroutines/">https://thdev.tech/kotlin/2021/01/12/Retrofit-Coroutines/</a></h6>
<h2 id="coroutine과-thread의-차이">Coroutine과 Thread의 차이</h2>
<p>코루틴과 스레드는 비슷해 보이지만, 동작 방식이 다르다.(Thread와 Coroutine은 비교 layer가 다름)</p>
<h4 id="coroutine-vs-thread">Coroutine VS Thread</h4>
<ul>
<li><p>Thread: <strong>OS 또는 런타임 환경에서 관리하는 실행 단위</strong>이며, <strong>독립적인 리소스</strong>를 사용하고 컨텍스트 스위칭 비용이 높음.</p>
</li>
<li><p>Coroutine: <strong>Thread 내부</strong>에서 <strong>동작</strong>하는 <strong>경량 실행 단위</strong>로, 중단(suspension)과 재개(resume)가 가능하여 더 효율적인 동작이 가능함.</p>
</li>
<li><p><strong>코루틴의 스레드</strong>는 <strong>안드로이드 시스템 스레드에 종속되지 않으며</strong>, <strong>Kotlin 코루틴 라이브러리 내부에서 관리</strong>됨. 즉, 코루틴을 사용하면 <strong>특정 플랫폼(OS)의 스레드 관리 정책에 의존하지 않고</strong>, <strong>코루틴 런타임이 직접 관리하는 최적화</strong>된 <strong>방식으로 실행</strong>된다.</p>
</li>
</ul>
<blockquote>
<p><strong>스레드</strong>는 <strong>실행 단위(Thread of execution)</strong>
<strong>코루틴</strong>은 <strong>실행 컨텍스트</strong>의 개념, <strong>코루틴은</strong> 독립적인 실행 단위라기보다는 <strong>특정 스레드 위에서 동작하는 경량 태스크</strong></p>
</blockquote>
<h4 id="threadsleep-vs-delay">Thread.sleep() vs delay()</h4>
<p>🛑 Thread.sleep(ms) </p>
<ul>
<li>호스레드 자체를 지정된 시간 동안 완전히 멈춘다.</li>
<li>그동안 다른 작업을 수행할 수 없음 → 비효율적</li>
</ul>
<p>🚀 delay(ms)</p>
<ul>
<li>코루틴만 일시 정지하고, 스레드는 계속 실행된다.</li>
<li>다른 작업을 병렬로 수행할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/329adf30-1a42-4bee-9e4d-ba4f0d592fe8/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/e1fc3330-bb70-4759-a2e0-c48073c33d25/image.png" alt=""></p>
<h5 id="threadsleep을-적용한-2초간-다음-코루틴은-동작하지-못함-그-이후-delay로-2000-1000ms-지연시켰지만-각각의-코루틴은-동작하여-총-4242ms에-모든-동작이-완료됨">Thread.sleep을 적용한 2초간 다음 코루틴은 동작하지 못함. 그 이후 delay로 2000, 1000ms 지연시켰지만 각각의 코루틴은 동작하여 총 4242ms에 모든 동작이 완료됨</h5>
<p><strong>Thread.sleep()</strong>은 <strong>스레드를 블로킹</strong>하지만,
<strong>delay()</strong>는 <strong>코루틴만 중단</strong>하고 <strong>스레드는 계속 실행</strong>된다.</p>
<hr>

<h3 id="정리">정리</h3>
<p>✅ Dispatchers.Main은 안드로이드 UI 스레드를 직접 활용하지만, 나머지 디스패처는 코루틴 라이브러리가 관리하는 별도 스레드 풀을 사용한다.</p>
<p>✅ Dispatchers.Main은 JVM 환경에서는 사용할 수 없으므로 주의해야 한다. 
(JVM 환경에서는 MainLooper가 없어 오류 발생 → Dispatchers.setMain() 으로 설정 가능)</p>
<p>✅ Dispatchers.IO와 Dispatchers.Default는 서로 다른 용도이지만, 같은 스레드 풀을 공유하면서 스레드를 재사용할 수 있다.
(현재 스레드가 IO 작업 수행 가능하면 새로운 스레드로 변경되지 않고 그대로 실행될 수도 있음)</p>
<p>✅ Retrofit을 사용할 때 Dispatchers.IO는 필수가 아니다.</p>
<p>✅ Thread.sleep()은 스레드를 블로킹하지만, delay()는 코루틴만 중단하고 스레드는 계속 실행됨 → 비동기 처리가 가능함</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2024. 08 회고록]]></title>
            <link>https://velog.io/@sysout-achieve/2024.08%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@sysout-achieve/2024.08%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Sun, 01 Sep 2024 13:01:45 GMT</pubDate>
            <description><![CDATA[<h4 id="2024년-6월">2024년 6월</h4>
<p>나는 <strong>컬리</strong>에 입사했다. <strong>약 50일</strong> 동안 컬리에서의 경험을 쌓으면서, 나의 적응 과정과 목표 설정, 행동 방식을 되돌아보게 되었다. 이 글은 그동안의 여정을 기록하고, 잘한 점과 아쉬운 점들을 정리해보려는 시도이다.</p>
<p>지금까지 연차에 비해 꽤 많은 회사를 거쳤다. 첫 번째 초기 스타트업(1년 반), <strong>부릉</strong>(1년), 두 번째 초기 스타트업(2년)을 거쳐 드디어 <strong>컬리</strong>에 입사했다. 이직을 통해 쌓은 경험치는 새로운 환경에 적응하는 데 큰 도움이 되었던 것 같다. <strong>기존의 적응 전략을 기본</strong>으로 삼고, 기술적으로 더 많은 협업을 시도하면서, 더욱 빠르고 쉽게 적응하려고 노력했다.</p>
<p>이전에 내가 세웠던 적응 전략은 컬리에서도 시도하고 있다. 이 전략이 궁금하다면 -&gt; <a href="https://velog.io/@sysout-achieve/2021.06-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%EC%9D%B4%EC%A7%81-1%EA%B0%9C%EC%9B%94%EC%B0%A8">2021.06 회고록 (이직 1개월차)</a>를 참고해보길 바란다.</p>
<p>더 나아진 점은 무엇일까?
컬리에서 경험하며 개선됐거나 시도한 점들을 꼽아보았다</p>
<h3 id="새로운-시도">새로운 시도</h3>
<h4 id="질문할-것과-고민할-것의-차이를-빠르게-구별하기">질문할 것과 고민할 것의 차이를 빠르게 구별하기</h4>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/a3ea0cd2-6f7e-4f43-a0ea-d1a84c7bab41/image.png" alt=""></p>
<p>새로운 환경에선 모르는 점이 많기 마련이다. 하지만 모든 것을 질문하기보다, 어떤 것은 스스로 고민해보고 해결할지, 어떤 것은 바로 질문할지 빠르게 구분하는 것이 중요했다. 이 능력이 특히 도움이 되었다.</p>
<h4 id="기술적으로-해결할-만한-부분들을-찾아내는-능력">기술적으로 해결할 만한 부분들을 찾아내는 능력</h4>
<p>문제를 발견했을 때, 내가 해결할 수 있는 부분과 그렇지 않은 부분을 빠르게 파악했다. 이전에는 <strong>질문해야할 부분인지 내가 코드를 읽고 고민해야하는 부분인지</strong>에 대해 생각하는 <strong>시간을 너무 많이 사용</strong>했다면, 이제는 히스토리가 있을 법한 것들은 바로 물어보고 소통하며 고민의 시간을 줄일 수 있었다.</p>
<h4 id="긍정적인-마인드로-회사-바라보기">긍정적인 마인드로 회사 바라보기</h4>
<p>새로운 조직에 대한 애착을 가지기 위해 <strong>의도적으로 긍정적인 마음가짐을 유지</strong>했다. 이 덕분인지, 컬리 조직과 구성원들의 장점 덕분인지 컬리에 대한 애정도가 더욱 깊어졌다.</p>
<h4 id="신뢰-구축-과정-이해하기">신뢰 구축 과정 이해하기</h4>
<p>처음에는 신뢰가 없는 상태에서 출발한다. 그래서 어떻게 나의 업무 방식을 팀원들에게 공유할지 고민했다. 내가 어떻게 일을 하고 있는지 자주 질문하거나 공유함으로써, 동료들이 나의 방식과 역량을 짐작할 수 있게 했다. 이로 인해 서서히 <strong>신뢰를 쌓는 과정</strong>을 가지고 있다고 생각한다.</p>
<p>이 과정을 통해 다른 팀원들이 환영할 만한 사람이 되려고 노력했다. 아직 앞으로 나아가야할 길이 멀고 험하겠지만 꾸준한 모습으로 팀에 기여하고 싶다.</p>
<hr>

<p>컬리를 다니면서 컬리의 장점도 느낄 수 있었다.
내가 컬리에서 느낀 장점들을 간단하게 나열해보면</p>
<h3 id="컬리의-장점">컬리의 장점</h3>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/159b95dc-0dbe-48fe-bcbb-093fb392f637/image.png" alt=""></p>
<h4 id="테크미팅">테크미팅</h4>
<p>정기적으로 열리는 테크미팅을 통해 팀의 기술 방향과 목표를 공유하고, 문제를 함께 논의할 수 있었다. 이러한 모임은 나에게 <strong>큰 배움의 기회</strong>가 되고 있고, 앞으로도 <strong>영양가있는 시간</strong>이 될 거라고 생각했다.</p>
<h4 id="친절한-질문사냥꾼들">친절한 질문사냥꾼들</h4>
<p>질문을 친절하게 받아주고, 이해할 때까지 자세히 설명해주는 모습이 인상 깊었다. 특히 질문을 던졌을 때, <strong>환영하는 분위기</strong>로 답변해주시는 모습이 질문에 대한 부담감을 줄여주셨다. 나도 누군가에게 이러한 태도로 도움을 줄 수 있는 사람이 되고싶다는 생각이 들었다. 이는 나의 적응을 돕는 중요한 요소였다.</p>
<h4 id="팀장님과-시니어들의-업무-교통정리">팀장님과 시니어들의 업무 교통정리</h4>
<p>팀장님과 시니어 분들이 업무의 흐름을 잘 정리해주어, <strong>개발에 더욱 집중할 수 있는 환경을 제공</strong>해주었다. 업무 정리를 해줄 때, 단순한 업무 분배뿐만 아니라 내가 <strong>빠뜨릴 수 있는 부분</strong>들에 대해 몇 번이고 편안한 분위기로 <strong>설명해주시는 모습</strong>들은 내가 <strong>배워야할 부분</strong>이라고 생각했다. </p>
<p>이러한 장점들이 이 팀의 장점이자 좋은 분위기를 이끌어가는 힘이라는 느낌이 들었다.</p>
<hr>

<p>물론, 모든 것이 순조로웠던 것은 아니었다. 
나의 부족한 점들도 되돌아보자면</p>
<h3 id="나의-부족했던-점">나의 부족했던 점</h3>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/cb25d0d5-963a-4950-8585-1a4f78578654/image.png" alt=""></p>
<h4 id="어설프게-아는-척했던-순간들">어설프게 아는 척했던 순간들</h4>
<p>경력이 쌓이면서 보이는 것들이 많아진 만큼, 뭔가 기여하는 모습을 보여줘야 한다는 생각에 조급함을 가지고 업무를 했던 것 같다. <strong>스스로를 증명하려는 마음</strong>에 <strong>정확하지 않은 말</strong>을 <strong>쉽게</strong> 했던 적이 있었다. 앞으로는 조금 더 <strong>신중하게 말하는 습관</strong>을 기르고자 한다. <strong>나보다 잘하고 똑똑한 사람은 너무나도 많고, 심지어 히스토리도 모르니 앞으로는 더 겸손한 모습으로 차근차근 기여하는 것</strong>이 좋을 것 같다. &lt;- (이 부분은 머리로는 계속 인지하고 있는데 빨리 고쳐지지 않아 더 신경써야지)</p>
<h4 id="좀-더-적극적인-인사와-교류">좀 더 적극적인 인사와 교류</h4>
<p>협업하는 분들과 좀 더 적극적으로 인사하며, 직접 찾아가 인사를 나누고 얼굴을 보는 기회를 늘렸다면 좋았을 것 같다. 동료애를 더 키울 수 있었을 텐데 아쉽다. 내가 외향적인 척하는 내향적인 사람이라 적극적으로 교류하는 모습이 좀 부족했던 것 같다. 새롭게 협업할 때 <strong>어려워도 적극적으로 물어보며 친분을 쌓는 과정</strong>을 가져야겠다.</p>
<h3 id="마무리">마무리</h3>
<p>약 50일간의 컬리 생활을 돌아보며, 나름대로 잘한 점도 있고 아쉬운 점도 있었다.(<strong>늘 아쉬운 점이 더 많이 보이는 것 같다</strong>)(<strong>성장할 길이 무궁무진</strong>..) 앞으로는 더 나은 성장을 위해 노력하면서, 컬리에서의 여정을 재미있고 의미 있게 만들어가고 싶다.</p>
<p>다음 회고 때는 더욱 발전된 모습으로 돌아오길 기대하며, 오늘은 여기서 마무리!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터를 근거로 의사결정 (with ABTest)]]></title>
            <link>https://velog.io/@sysout-achieve/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EA%B7%BC%EA%B1%B0%EB%A1%9C-%EC%9D%98%EC%82%AC%EA%B2%B0%EC%A0%95-with-ABTest</link>
            <guid>https://velog.io/@sysout-achieve/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EA%B7%BC%EA%B1%B0%EB%A1%9C-%EC%9D%98%EC%82%AC%EA%B2%B0%EC%A0%95-with-ABTest</guid>
            <pubDate>Mon, 29 May 2023 13:25:54 GMT</pubDate>
            <description><![CDATA[<p>클라이언트 제품을 만들다보면 사용자들이 어떤 UX가 제품을 더 편하게 만드는지, 어떤 UI가 우리 제품의 목적에 적합한 것인지 고민하는 상황을 많이 겪게된다. </p>
<br>
<br>

<p>직관적으로 &quot;이게 더 나을거야&quot; 
<img src="https://velog.velcdn.com/images/sysout-achieve/post/06fd04a2-6f55-47e8-acff-fbc716a79e00/image.png" width="400"/>
하는 것보다 Data를 근거로 접근할 수 없을까?
<br>
<br></p>
<h2 id="1-임시로그인-강제회원가입">1. 임시로그인? 강제회원가입???</h2>
<p>맨 처음 우리가 앱을 출시할 때 가장 먼저 고민했던 UX는 임시로그인...
로그인을 하지 않은 사용자는 우리 앱에 핵심기능인 문서 등록에 제한이 생긴다(문서를 분류해주거나 데이터를 추출해주는 기능, 안해줌)</p>
<p>그래서 우리는 임시로그인을 넣을건지, 강제로 본인인증을 통해 로그인을 하게 할 지 각자의 생각을 의논했다.</p>
<blockquote>
<p><strong>A(Baseline) - 임시로그인</strong>
    <strong>장점</strong>: 1. 앱을 임시유저로써 사용해보고 앱의 이점에 대해 파악이 가능함
         2. 임시로 문서 등록 후 사용자가 필요하다면 로그인하게 하여 자율성을 줌
          -&gt; 임시 등록이더라도 사용자가 락인될 가능성이 높다고 예상 <br>
    <strong>단점</strong>: 임시유저의 경우 메인 기능을 접하지 못하고 이탈할 가능성 있음
         본인 인증을 위한(로그인) 진입점을 여러 곳에 만들어야함 <br><br>
<strong>B(Force Authenticate) - 강제로그인</strong>
    <strong>장점</strong>: 1. 로그인한 사용자가 앱의 기능을 원활하게 수행할 수 있음
         2. 처음 앱을 공개했을 때 이 앱을 받는 사용자는 앱에 대한 이해도가 있을 것이라 예상,
          -&gt; 보안을 위해서라도 로그인이 더 자연스러울거라 생각함       <br>
       <strong>단점</strong>: 전화번호인증으로 로그인하게 하더라도 이탈자가 발생할 가능성 높음</p>
</blockquote>
<p>A, B안의 장단점이 뚜렷하고 납득할만한 의견들이어서 결정이 어려웠고, 우리는 ABTest를 통해 어떤 방법으로 앱을 진입할 때 쉽게 문서를 등록하는지 확인하기로 하였다.</p>
<p>Firebase에서 제공하는 RemoteConfig, ABTest를 활용하였다. remoteConfig를 생성하고 그 property에 값을 적절하게 나눠주도록(우리는 50:50으로 설정)하여 실험을 했다.</p>
<br>


<h3 id="결과는-임시유저의-승리">결과는 임시유저의 승리</h3>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/560ab08e-893c-4627-beb6-7ff06cb5cdd0/image.png" alt=""></p>
<p>짧은 기간, 적은 사용자를 대상으로 확인했지만 결과적으로 임시 유저를 두고 앱을 사용하도록 결정하였다.
(이 결과에는 임시유저일 때 앱의 핵심기능이 사용됐을 때의 화면을 더미데이터를 통해 보여주어 사용자가 더 쉽게 이 앱의 이점을 상상할 수 있도록 한 것이 효과적이지 않았을까 생각했다)</p>
<br>
<br>

<h2 id="2-사용자들은-이-기능에-대해-알고-있을까">2. 사용자들은 이 기능에 대해 알고 있을까?</h2>
<p>우리는 문서 공유하기 기능을 추가했다. </p>
<p>이 공유하기 기능을 통해 사용자들이 본인들의 문서를 공유하고, 그로 인해 바이럴되는 효과를 기대했다. 
(효과는 미비했다)(이벤트 호출 수 자체가 높지 않음) &lt;- 혹시 기능의 존재를 알지 못하는게 아닐까????</p>
<img src="https://velog.velcdn.com/images/sysout-achieve/post/7b2cbb1e-5b28-42c1-813b-0d236a35d1f2/image.png" width="300"/>

<p>우리는 <strong>공유하기</strong>기능을 문서 화면의 오른쪽 상단 overflow버튼 안에 숨겨두었고, 사용자들이 기능을 모를 수도 있다는 의견이 있어 이 또한 ABTest를 통해 사용자를 분석해보았다.</p>
<blockquote>
<p>A(Baseline) - 기존에 overflow메뉴버튼 내 공유하기 기능
B(Variant A) - 공유하기 버튼을 문서 정보 가장 상단에 노출시켜두기</p>
</blockquote>
<p>기존에 사용했던 방식과 마찬가지로 Firebase의 ABTest를 사용했다.</p>
<p><img src="https://velog.velcdn.com/images/sysout-achieve/post/40712f27-e2c9-4a3b-b4f4-c20a4935e5ce/image.png" alt=""></p>
<p>결과는 생각보다 꽤 많은 차이를 보였다.
공유하기 기능 중 <strong>jpg 공유하기</strong>의 경우 <strong>227%</strong>, PDF 공유하기의 경우 <strong>248%</strong> 의 수치로 공유하기 기능의 사용이 늘어남을 확인하게 되었다. 
: 화면의 버튼으로 빠져나온 기능은 사용자가 어떻게 동작하는지 궁금해서라도 눌러보는 케이스가 존재할 것이라 생각했고, 어떤 의도든 더 많은 공유하기 기능의 사용은 앱의 인지도를 높이기 위해 긍정적인 방향이라 판단하여 B안을 선택하게 되었다.</p>
<blockquote>
<p><strong>TIP!</strong> 
테스트할 때 본인의 앱이 <strong>A안</strong>의 앱인지, <strong>B안</strong>의 앱인지 QA분들이 확인하기 귀찮은 케이스가 있다(예를 들어, 분기되는 화면이 몇 번의 depth가 있는 화면일 경우)
이럴 때 <em><strong>dev, qa환경</strong>에서만 <strong>첫 화면</strong>에서 어떤 안의(A or B) 앱인지 확인하는 텍스트를 띄워주거나 표시를 해주어</em> QA분들도 편하게 테스트할 수 있도록 만들어줄 수 있다.</p>
</blockquote>
<h3 id="결론">결론</h3>
<p>ABTest를 통해 사용자들이 실제로 사용성에 변화를 일으키는 UX,UI가 무엇인지 실험을 할 수 있다. 
데이터로 확인하며 제품을 개발할 때 조금이라도 더 사용자에게 최적화된 제품을 만들 수 있다고 생각한다. 
그리고 이러한 과정들이 제품을 개발하는 사람들에게는 더 귀찮은 과정일 수 있다. _(같은 기능에 UI는 두 벌 작업해야함, 나중에는 제거해야할 수 있음) _
그러나 방법에 대한 의견 차이로 시간 낭비가 길어지는 것보다 더 좋은 근거를 얻고 그걸 바탕으로 더 좋은 제품을 만들어낼 수 있는 방법이 될 것이라 생각한다. </p>
<p>제품을 개발하는 사람이 귀찮고 힘들수록 더 좋은 제품이 나온다는 말을 믿으며... 앞으로도 ABTest를 해보며 사용자에 대해 고민하게 될 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React LifeCycle]]></title>
            <link>https://velog.io/@sysout-achieve/React-LifeCycle</link>
            <guid>https://velog.io/@sysout-achieve/React-LifeCycle</guid>
            <pubDate>Wed, 13 Apr 2022 02:57:31 GMT</pubDate>
            <description><![CDATA[<p>React lifeCycle</p>
<h3 id="case1-클래스의-경우">case1. 클래스의 경우</h3>
<blockquote>
<p>: constructor -&gt; render -&gt; ref -&gt; *componentDidMount -&gt; 동작중 -&gt; setState/props 바뀔때 -&gt; shouldComponentUpdate(true) -&gt; render -&gt; *componentDidUpdate -&gt; 
부모가 자식 Component를 없앴을 때 -&gt; *componentWillUnmount -&gt; 소멸 </p>
</blockquote>
<blockquote>
<ul>
<li>*componentDidMount()<br>: render() 함수 실행으로 jsx를 처음 react-dom에 attach하는 시점에 호출, rerendering할 때는 호출되지 않음
비동기 요청을 많이 함</li>
</ul>
</blockquote>
<ul>
<li>*componentDidUpdate()
: 리랜더링 이후 호출</li>
<li>*componentWillUnmount()
: 컴포넌트가 제거되기 직전에 호출되는 함수
비동기 요청 정리를 많이 함</li>
</ul>
<h3 id="case2-react-hooks">case2. React Hooks</h3>
<p>: useEffect를 이용하여 처리</p>
<pre><code>useEffect(()=&gt; {    // componentDidMount, componentDidUpdate의 역할 
    setState1();
    setState2();
    return (    // componentWillUnmount 역할 수행
    );
}, [state1, state2])</code></pre><p>-&gt; state 변경을 감지하여 hooks가 함수 컴포넌트 자체를 재실행한다고 볼 수 있음</p>
<br>

<h4 id="react-hooks-tips">React hooks Tips</h4>
<p>useMemo, useCallback 이용해서 캐싱이 가능 </p>
<br>


<p><strong>useMemo</strong> : state가 hooks에 의해서 컴포넌트 자체가 재실행 될 때 데이터를 재호출하는 것을 메모이제이션함</p>
<pre><code>useMemo(param1, param2)
    : 값을 기억해서 제공함
    param1은 캐싱할 데이터, param2의 변경에 따라 memo한 데이터 reload</code></pre><br>

<p><strong>useCallback</strong></p>
<pre><code>useCallback(param1, param2)
    : 함수 자체를 기억해서 제공함
    param1은 캐싱할 함수, param2의 변경에 따라 memo한 함수 reload
    useCallback에 쓰이는 state는 항상 param2에 추가해야함 -&gt; 그렇지 않을 경우 처음 캐싱한 state를 그대로 들고 있</code></pre><p>React Hooks는 절대 조건문안에 들어가면 안됨 -&gt; 실행 순서가 바뀌면 안됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Basic (리액트 환경설정)]]></title>
            <link>https://velog.io/@sysout-achieve/React-Basic</link>
            <guid>https://velog.io/@sysout-achieve/React-Basic</guid>
            <pubDate>Sat, 02 Apr 2022 08:35:10 GMT</pubDate>
            <description><![CDATA[<p>React를 개발하며 아주 기본적인 설정들을 정리한다.</p>
<p><strong>프로젝트 생성 후 환경설정</strong></p>
<pre><code>npm init

npm i react react-dom
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react
npm i -D react-refresh @pmmmwh/react-refresh-webpack-plugin 
npm i -D webpack webpack-cli webpack-dev-server    </code></pre><br>

<blockquote>
<p>webpack 왜 쓰는지?
: 여러 파일(.js, .jsx ...) 을 하나의 파일로 합쳐서 app.js파일로 만들어줌
최신 문법들을 옛날 브라우저에서도 돌아갈 수 있도록 만들어줌</p>
</blockquote>
<br>

<p>webpack.config.js 파일 생성 후 환경설정</p>
<p>webpack.config.js</p>
<pre><code>const path = require(&#39;path&#39;);
const RefreshWebpackPlugin = require(&#39;@pmmmwh/react-refresh-webpack-plugin&#39;);

module.exports = {
    name: ***,
    mode: &#39;development&#39;,
    devtool: &#39;eval&#39;,
    resolve: {
        extensions: [&#39;.js&#39;, &#39;.jsx&#39;]
    },
    entry: {
        app: [&#39;./Client&#39;],
    },
    module:{
        rules: [{
            test:/\.jsx?$/,
            loader: &#39;babel-loader&#39;,
            options:{
                presets: [
                    [&#39;@babel/preset-env&#39;,{
                        targets: {
                            browsers: [&#39;&gt; 1% in KR&#39;],
                        }
                    }],    
                    &#39;@babel/preset-react&#39;
                ],
                plugins: [
                    &#39;@babel/plugin-proposal-class-properties&#39;,
                    &#39;react-refresh/babel&#39;
                ],
            }
        }],
    },
    plugins: [
        new RefreshWebpackPlugin()
    ],
    output: {
        filename: &#39;app.js&#39;,
        path: path.join(__dirname, &#39;dist&#39;),
    },
    devServer: {
        devMiddleware: { publicPath: &#39;/dist/&#39; },
        static: { directory: path.resolve(__dirname) },
        hot: true
    }
}</code></pre><p>package.json 파일 </p>
<pre><code>{
  &quot;name&quot;: &quot;***&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;webpack serve --env development&quot;,
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;
  },
  &quot;author&quot;: &quot;***&quot;,
  &quot;license&quot;: &quot;***&quot;,
  &quot;dependencies&quot;: {
    &quot;react&quot;: &quot;^17.0.2&quot;,
    &quot;react-dom&quot;: &quot;^17.0.2&quot;,
    &quot;react-hot-loader&quot;: &quot;^4.13.0&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;@babel/core&quot;: &quot;^7.17.8&quot;,
    &quot;@babel/preset-env&quot;: &quot;^7.16.11&quot;,
    &quot;@babel/preset-react&quot;: &quot;^7.16.7&quot;,
    &quot;@pmmmwh/react-refresh-webpack-plugin&quot;: &quot;^0.5.4&quot;,
    &quot;babel-loader&quot;: &quot;^8.2.4&quot;,
    &quot;react-refresh&quot;: &quot;^0.11.0&quot;,
    &quot;webpack&quot;: &quot;^5.70.0&quot;,
    &quot;webpack-cli&quot;: &quot;^4.9.2&quot;,
    &quot;webpack-dev-server&quot;: &quot;^4.7.4&quot;
  }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[모바일 크로스플랫폼
Flutter vs React Native 차이가 뭘까?]]></title>
            <link>https://velog.io/@sysout-achieve/%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%81%AC%EB%A1%9C%EC%8A%A4%ED%94%8C%EB%9E%AB%ED%8F%BCFlutter-vs-React-Native-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EB%AD%98%EA%B9%8C</link>
            <guid>https://velog.io/@sysout-achieve/%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%81%AC%EB%A1%9C%EC%8A%A4%ED%94%8C%EB%9E%AB%ED%8F%BCFlutter-vs-React-Native-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EB%AD%98%EA%B9%8C</guid>
            <pubDate>Mon, 21 Feb 2022 10:46:04 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sysout-achieve/post/b6054413-c6b0-45bf-a040-afd7e7c96a74/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-25%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.58.18.png" alt="">모바일 네이티브 개발자인 나에게 크로스플랫폼 개발은 매력적이기도, 두려움을 주기도 하는 프레임워크로 여겨진다. 하나의 <strong>소스코드로 aos, ios 모두를 관리할 수 있다는 강점은 너무나 매력적</strong>이다. 그러나 크로스플랫폼이 가진 한계가 존재하기 때문에 모든걸 던지고 크로스플랫폼에 올인을 하기에는 어려운 <strong><em>계륵과 같은 존재</em></strong>이다. </p>
<p><img src="https://images.velog.io/images/sysout-achieve/post/26965e5a-9af8-48f6-867a-81a583513229/image.png" alt=""></p>
<p>그럼 크로스플랫폼이 한계를 개선해 나가며 최적화된 사용성을 제공한다면? 현재 네이티브 개발자들의 입지를 위협하기에 충분하다고 느껴진다. <del>(아직은...)</del></p>
<p>그러면 크로스플랫폼에 대해 미리 기술에 대한 이해도를 높여 위협받는 위치가 아닌 기회로 만들 수 있지 않을까?   </p>
<p>개발자 커리어뿐만 아니라, 내가 최종적으로 목표하는 나만의 자체서비스를 제공할 때 크로스플랫폼은 안드로이드 시장에 국한될 필요가 없어진다는 장점까지 추가된다. </p>
<p>나의 _게으르게 보낼 시간_만 <strong>비용</strong>으로 지급하면 많은 이득을 얻을 것이라 예상되어 탐구해보려한다.</p>
<hr>

<p>모바일 크로스플랫폼 프레임워크로 <strong>플러터</strong>와 <strong>리액트 네이티브</strong>가 있다. </p>
<p>각 프레임워크가 어떤 개념으로 출발했는지, 어떤 장단점이 있는지 분석해보고 기술을 선택해보자.</p>
<h2 id="flutter-vs-react-nativern">Flutter vs React Native(RN)</h2>
<p><img src="https://images.velog.io/images/sysout-achieve/post/9c93aa10-c4c6-4021-a6b4-e6de1d2b3b83/image.png" alt=""></p>
<h3 id="플러터-flutter">플러터 (Flutter)</h3>
<p>&lt;구글에서 만든 프레임워크&gt;</p>
<p>언어 <strong>Dart</strong>
: 구글에서 만든 객체지향언어. JS를 대체하겠다는 계획으로 출발했으나, JavaScript와 비교하기에 JS가 너무 거대해짐</p>
<h4 id="특징">특징</h4>
<ol>
<li><strong>구글 베이스 플랫폼</strong>
: 이건 장점이 될 수도, 단점이 될 수도 있다.</li>
</ol>
<ul>
<li>구글이 지원했을 때, 지원하는 스케일이 크고 발전하는 속도가 빨라 장점이 될 수 있다.</li>
<li>구글은 이전에도 투입했던 기술 리소스들을 제거했던 전적이 있음(리스크를 안고 사용해야함)<del>(플러터 또한 언제 사라질지 모름)</del></li>
</ul>
<ol start="2">
<li><p>기본으로 제공하는 베이스 디자인이 Material, Cupertino
OS특성에 맞게 알아서 UI컴포넌트가 구성되는 것이 아니라 설정하는대로 제공된다. (커스텀으로 앱의 UI를 변경해야할 수도 있음)
&lt;-&gt; 상대적으로 리액트네이티브의 경우 호스트의 규칙대로 사용됨 (ios, android) 각기 다른 결과물이 보임</p>
</li>
<li><p>Dart를 배워야하는데 객체지향언어에 대한 이해도가 없는 개발자에게는 러닝커브가 좀 있다. 대신 플러터를 공부할 때 구글이 문서화를 잘해놓아서 편하게 학습 가능하다.</p>
</li>
<li><p>Flutter는 개발자 경험이 매우 좋다. <strong>인하우스 시뮬레이터를 활용하여 빠르게 적용된 결과물 확인 가능</strong>하고, 버그가 거의 없다.</p>
</li>
<li><p><strong>애니메이션과 사용자 상호작용</strong> 관련하여 구글에서 만들어 놓은 것들이 많이 준비되어있다. 어려운 인터렉션 처리에 대해 많은 것들을 플러터에서 이미 준비해두었다.</p>
</li>
</ol>
<blockquote>
<p><strong>플러터가 더 효용가치가 큰 프로젝트는?</strong>
 : 스크립트 언어로 integration이 가능한 환경을 제외하면 플러터가 더 효과적으로 많은 기능들을 적용할 수 있음</p>
</blockquote>
<hr>

<h3 id="리액트-네이티브-react-native">리액트 네이티브 (React Native)</h3>
<p>&lt;페이스북에서 만든 프레임워크&gt;</p>
<p>언어 JavaScript</p>
<h4 id="특징-1">특징</h4>
<ol>
<li><strong>오픈소스 기반 플랫폼</strong></li>
</ol>
<ul>
<li>작은 기능들까지 오픈소스로 따로 제공받아 사용해야하는 경우가 생긴다.<pre><code>  1) 조달받은 오픈소스에 이슈발생하면 발생한 이슈를 오픈소스에서 해결해줘야하는 경우가 있다.(라이브러리에 의존성이 강함)
  2) 새로운 것들 발표한다면 서드파티가 호환성을 맞추는 것을 기다려야함</code></pre></li>
</ul>
<ol start="2">
<li><strong>리액트 네이티브는 리액트를 안다면 쉽게 개발이 가능</strong>하고, <strong>JavaScript</strong>가 가진 생태계가 워낙 크고 잘 이루어져있기 때문에 수많은 <strong>개발자들의 시행착오와 해결 방법들을 레퍼런스</strong>로 찾아볼 수 있다.</li>
</ol>
<blockquote>
<p><strong>리액트 네이티브의 효용가치가 큰 프로젝트는?</strong>
: 리액트 개발자, 자바스크립트, 타입스크립트, 리액트JS 웹이 있을 경우에 통합할 때 매우 효과적일 수 있음
    웹, ios, aos 모두 하나의 코드로 관리할 수도 있음</p>
</blockquote>
<br>
<hr>

<h3 id="그럼-어떻게-크로스플랫폼을-지원할까">그럼 어떻게 크로스플랫폼을 지원할까?</h3>
<br>

<p><strong>React Native</strong> : JavaScript Bridge</p>
<p><img src="https://images.velog.io/images/sysout-achieve/post/1e2d8bf1-f8d5-403c-828f-3947e1a27507/image.png" alt=""></p>
<p>Idea: xml, Json 등으로 서버 요청에 대한 응답받는 형식과 마찬가지로 완전히 &#39;모든것이 완벽하게 컴파일될 필요는 없지 않나?&#39;로 시작 -&gt; JSX가 Native에 이런 코드 만들어! 로 구현하면 가능하겠다는 생각에서 나온게 JavaScript Bridge. 따라서 네이티브 플랫폼과 소통할 수 있는 다리 역할로 사용하여 UI를 그려낸다.</p>
<p>다만, <strong>IOS, AOS가 가진 컴포넌트</strong>마다 파라미터, <strong>성격들을 별개</strong>로 가지고 있기 때문에 각각의 <strong>컴포넌트가 의도한 바를 이루고 있는지 따로 디버깅하여 확인이 필요</strong>하다. 때에 따라 소스 분기 필요할 수 있다.</p>
<p>OS업데이트에 민감하게 문제 발생을 확인해야한다.</p>
<br>

<p><strong>Flutter</strong> : <strong>C++로 작성된 그래픽엔진(Skia Engine)</strong>을 가지고 있다. <strong>Canvas에 컴포넌트 자체를 그려버린다는 개념</strong>으로 접근</p>
<p><img src="https://images.velog.io/images/sysout-achieve/post/22b7e1ec-f7ef-4d9e-8d59-95f8f7af778c/image.png" alt=""></p>
<p><strong>위젯을 구성품으로 가지고 있음 (Material-google, Cupertino-apple)</strong>
=&gt; Material로 디자인하면 ios에서도 android 형식의 버튼을 그려버린다. 반대로 cupertino로 사용하면 apple형식의 버튼을 android에 그려넣는다.(분기처리도 가능)</p>
<p><strong>렌더링 엔진을 통해 그림</strong>을 그린다는게 <strong>플랫폼에 의존적이지 않아</strong> 상황에따라 더 편하게 개발이 가능하다.</p>
<hr>

<p><img src="https://images.velog.io/images/sysout-achieve/post/774198b7-7030-49ee-83a2-e8d055c44e9d/image.png" alt=""></p>
<h3 id="결론">결론</h3>
<p>개발자라면 모든 상황을 고려하지 않고 플러터가 최고야! or 리액트네이티브가 더 쉽지! 라는 식의 접근은 경계해야 한다.
어떤 환경에 어떤 프레임워크가 좋을지는 그 프레임워크가 가진 강점과 장점들을 살펴보고 내가 개발하고자 하는 프로젝트의 특징은 어떠한지 연관지어서 생각해야한다. 더 효과적이고 효율적으로 개발이 가능한 기술을 선택적으로 사용하는 것이 중요하다.
상황에 따라 행동하자. 유연한 코드를 지향하면서 사람이 어떤 툴을 사용하면서 딱딱해지는 것을 경계하자.</p>
<h6 id="이미-입지를-다진-회사들의-경우-백엔드를-자바로-잡아둔-경우가-매우-많다-또한-내가-개인적으로-자체서비스를-만드는-경우에도-서버와-통합을-목적으로-두는-것-보다는-빠르게-개발하여-결과물을-내고-사용자에게-좋은-ux를-주는-것이-더-효과적이라는-생각을-했다-따라서-나에게-더-효용성이-더-크다고-생각되는-플러터를-중심으로-크로스플랫폼-공부를-이어나가려-한다">이미 입지를 다진 회사들의 경우 백엔드를 자바로 잡아둔 경우가 매우 많다. 또한 내가 개인적으로 자체서비스를 만드는 경우에도 서버와 통합을 목적으로 두는 것 보다는 빠르게 개발하여 결과물을 내고, 사용자에게 좋은 UX를 주는 것이 더 효과적이라는 생각을 했다. 따라서 나에게 더 효용성이 더 크다고 생각되는 플러터를 중심으로 크로스플랫폼 공부를 이어나가려 한다.</h6>
]]></description>
        </item>
        <item>
            <title><![CDATA[사용자 편의를 위한 고민 (feat. 상세화면 로드 속도 개선)]]></title>
            <link>https://velog.io/@sysout-achieve/%EC%83%81%EC%84%B8%ED%99%94%EB%A9%B4-%EB%A1%9C%EB%93%9C-%EC%86%8D%EB%8F%84-%EA%B0%9C%EC%84%A0</link>
            <guid>https://velog.io/@sysout-achieve/%EC%83%81%EC%84%B8%ED%99%94%EB%A9%B4-%EB%A1%9C%EB%93%9C-%EC%86%8D%EB%8F%84-%EA%B0%9C%EC%84%A0</guid>
            <pubDate>Thu, 13 Jan 2022 08:26:32 GMT</pubDate>
            <description><![CDATA[<p>앱서비스를 개발하다보면 많은 사용자의 다양한 상황에 따라 <strong>생각지도 못했던 voc 이슈들</strong>을 많이 만나게 된다. 여러 voc 이슈들 중 
<em>사용자 fault</em><del>(문자가 안보내져요&lt;-요금문제, 앱이 스토어에 안보여요&lt;-안드로이드 4.0 디바이스... 등등)</del> 들을 걸러내며 앱에서 <strong>개선할 수 있는 사항들을 찾아 업데이트하면서 우리 서비스의 가치를 증진시킬 때</strong> 내가 이 서비스에 제대로 <strong>기여함</strong>을 느낄 수 있었다.</p>
<h3 id="1-문제-인식">1. 문제 인식</h3>
<p>운영에서 사용자가 배송 상세화면에 들어갔을 때 특히 랙이 걸린다는 이슈를 전달받았다. 보통은 앱이 랙걸린다는 이슈가 들어오면 네트워크 환경 문제인 경우가 대부분이었지만, 이번 이슈는 <strong>특정 부분을 직접 언급</strong>했고 사용자들이 <strong>자주 진입하는 구간</strong>의 문제라 앱을 돌려보는 것과 함께 소스코드를 확인하며 시간을 투자했다.
<img src="https://images.velog.io/images/sysout-achieve/post/41775dc0-2f11-40ec-8fee-63ddebcafd1a/image.png" width="400px" height="300px"></p>
<br>

<h3 id="2-문제-분석">2. 문제 분석</h3>
<p>배송 상세 화면에 진입하면서 여러 api를 호출하는 구간을 확인했고, 그 호출이 모두 정상 응답을 받을 때까지 로딩바가 사용자 UI를 막는 방식으로 구현되어 있었다.
여기서 가장 큰 리소스를 잡아먹는 구간이 <strong>배송 필수요소 검증 api</strong> 였다.(3개의 api 한꺼번에 호출하여 모든 응답값을 받아야 완료)
배송 필수요소는 배송 상세 화면에 출력할 데이터가 아니라 다음 프로세스가 어떤 프로세스인지 미리 데이터를 호출하는 케이스였고 여러 네트워크 통신이 사용성을 저해하는 요소로 작용할 수 있음을 확인했다.</p>
<blockquote>
<h3>배송 오더 리스트의 상세화면으로 진입했을 경우</h3>

</blockquote>
<img src="https://images.velog.io/images/sysout-achieve/post/186dacc3-1462-47cd-8c04-8eafbdeebcb2/image.png" width="300" height="400">
>
: 로그에서 확인할 수 있듯이 **약 0.6 ~ 0.8초**의 대기시간이 소요되었다. (개인적으로 더 실험해본 결과 **0.5~1.4초**까지 확인할 수 있었고, 네트워크 환경이 일정하지 않을수록 더 큰 편차가 있을 것이라 예상할 수 있음)

<p><strong>개발 시점에는 아주 안정적인 네트워크 환경에서 1초 미만의 대기 시간으로 문제 인식조차 하지 못했다. 그러나 실제 사용자의 환경에 따라 답답한 사용성을 가진 앱으로 느끼게 만드는 요소가 된 것이다.</strong></p>
<h3 id="3-문제-해결">3. 문제 해결</h3>
<p>배송 필수 요소 검증 api 를 LoadingBar 출력 로직에서 제외했다. 배송 필수 요소 데이터 호출이 끝나지 않았을 때 해당 데이터가 필요한 경우만 다시 시도를 요청하며 사용자 편의를 중심으로 UI를 block 시키는 상황을 최대한 줄이도록 변경했다.(UX팀과 논의 후 결정함)</p>
<h6 id="또한-해당-데이터가-input-되는-시점에-앱의-화면이-변경되는-경우도-안정적으로-input을-무시하는-것을-확인했다-viewmodel의-state를-변경하고-그-변경된-state를-기준으로-화면이-subscribe하고-반영하는-구조여서-당연히-해당-화면이-없으면-무시할-것이라-예상했지만-혹시-모를-상황을-대비하여-또-검증">또한 해당 데이터가 input 되는 시점에 앱의 화면이 변경되는 경우도 안정적으로 input을 무시하는 것을 확인했다. ViewModel의 State를 변경하고 그 변경된 State를 기준으로 화면이 subscribe하고 반영하는 구조여서 당연히 해당 화면이 없으면 무시할 것이라 예상했지만, 혹시 모를 상황을 대비하여 또 검증!</h6>
<blockquote>
<h3>로드 속도 개선 후 상세화면 진입 </h3>

</blockquote>
<img src="https://images.velog.io/images/sysout-achieve/post/f48c231f-6622-464b-93ba-70816be3e9c5/image.png" width="300" height="400">
>
대략 0.2 ~ 0.4초의 대기시간으로 상세화면 진입이 완료됨을 확인할 수 있었다. 

<h3 id="4-결론">4. 결론</h3>
<p>새로운 프로젝트를 진행하고, 새로운 기능을 추가하고, 회사의 비즈니스를 수행하기 위한 다양한 개발들을 자연스럽게 하게 된다. 이런 업무들은 당연하게 해야하는 업무이고 점점 상향평준화가 되고있다. <del>(다들 너무 잘하셔서...)</del> 경쟁력있는 개발자가 되기 위해 기술적으로 deep하게 파고 들어 전문성을 준비하는 것도 계속해서 노력해야겠지만, 본인이 만드는 서비스의 <strong>가치를 높이기 위한 방법을 고민</strong>하며 개발할 때, <strong>주도적으로 일하는 개발자</strong>가 더 <strong>경쟁력있는 개발자</strong>가 되지 않을까?</p>
<h6 id="이후로-상세화면-진입-이슈는-들어오지-않고-있다-ㅎㅎㅎ">이후로 상세화면 진입 이슈는 들어오지 않고 있다 ㅎㅎㅎ</h6>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 앱 배터리 성능 측정 분석 툴-Battery Historian]]></title>
            <link>https://velog.io/@sysout-achieve/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EB%B0%B0%ED%84%B0%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EC%B8%A1%EC%A0%95-%EB%B6%84%EC%84%9D-%ED%88%B4-Battery-Historian</link>
            <guid>https://velog.io/@sysout-achieve/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1-%EB%B0%B0%ED%84%B0%EB%A6%AC-%EC%84%B1%EB%8A%A5-%EC%B8%A1%EC%A0%95-%EB%B6%84%EC%84%9D-%ED%88%B4-Battery-Historian</guid>
            <pubDate>Mon, 25 Oct 2021 06:38:20 GMT</pubDate>
            <description><![CDATA[<h2 id="안드로이드-배터리-및-성능-측정-tool-battery-historian---google">안드로이드 배터리 및 성능 측정 tool (Battery Historian - google)</h2>
<p>업무 중에 &#39;앱 사용 배터리가 빠르게 닳는다&#39;는 voc이슈를 받으면, 모바일팀은 그 이슈를 해결하기 위해 보이지 않는 적과 싸우는 경우가 있다. </p>
<blockquote>
<p>앱의 배터리 성능을 높여야 해! </p>
<ul>
<li>성능을 어떻게 높이지? </li>
<li>얼마나 높이지??? </li>
<li>높아진지 검증은 어떻게 하지?????  등등..</li>
</ul>
</blockquote>
<p>배터리와 관련된 voc가 들어왔을 경우 우리 앱의 버전 변경이 배터리에 큰 영향도가 없다 생각하지만 입증이 참 어렵다. 그래서 나는 객관적인 지표를 통해 현재 얼마나 배터리 성능이 나빠졌는지, 얼마나 성능을 높였는지 등을 확인하고 추상적인 목표를 구체화하여 해결하고 싶었다.
 성능분석툴을 이용해 구체적인 수치로 </p>
<h5 id="현-버전은-얼마의-배터리-소모를-하고-있고-수정버전은-의-배터리-효율이-증가했다"><em>&#39;현 버전은 얼마의 배터리 소모를 하고 있고 수정버전은 *%의 배터리 효율이 증가했다.&#39;</em></h5>
<p>라는 결과물을 내고 싶었다.(장점 : 커뮤니케이션 쉬움, 목표설정 명확, 구체화된 결과물 등등)</p>
<hr>

<p>기본 개념
모바일 디바이스에서 Battery Stats 파일을 생성하여 브라우저의 Battery Historian HTML로 파일 전달 -&gt; 데이터 시각화한다.</p>
<p>안드로이드 프레임워크 배터리 정보를 adb commands로 얻거나 안드로이드 시스템의 (개발자옵션 -&gt; 버그리포트 -&gt; 대화형 보고서)를 통해 bugreport file을 얻는다.</p>
<p><a href="https://github.com/google/battery-historian">https://github.com/google/battery-historian</a></p>
<h3 id="준비물for-mac">준비물(for mac)</h3>
<ol>
<li>docker
: <a href="https://docs.docker.com/desktop/mac/install">https://docs.docker.com/desktop/mac/install</a> 에서 설치 가능</li>
<li>adb(android debug bridge)
: brew install --cask android-platform-tools</li>
</ol>
<h3 id="battery-historian-준비-과정">Battery Historian 준비 과정</h3>
<ol>
<li>BatteryHistorian Image를 pull한다.<pre><code>docker pull gcr.io/android-battery-historian/stable:3.0
</code></pre></li>
</ol>
<pre><code>2. Battery Historian Iamge를 Run한다. ```&lt;port&gt;```는 사용자가 직접 지정 (ex.1234)
</code></pre><p>docker -- run -p <port>:9999 gcr.io/android-battery-historian/stable:3.0 --port 9999</p>
<pre><code>
3. browser를 이용하여 로컬호스트의 본인이 지정한 포트로 진입.(Battery Historian 툴 사용 준비 완료)
![배터리 히스토리안](https://images.velog.io/images/sysout-achieve/post/2e75835c-4261-48ca-a8f0-f0783f8c64b1/image.png)


### 배터리 성능 측정 과정

**step 1. 기기 배터리 충전을 완료.**
**step 2. pc에 기기를 연결시키고 BatteryStats를 초기화.**</code></pre><p>adb shell dumpsys batteryStats --reset</p>
<pre><code>**step 3. pc에서 분리시켜 측정하고자하는 앱을 사용.**

**step 3 시나리오**
    1) 테스트 방식에 맞춰 애플리케이션을 사용.
    2) 디바이스 배터리를 20~50%까지 사용하게 한 뒤 report를 확인하여 측정하고자 하는 앱의 배터리 소모를 확인.
    3) 디바이스를 다시 pc에 연결하고 command를 통해 로그를 얻음.
&gt; ** 로그 확보 방법 1) **
    os7.0이상 : </code></pre><p> adb bugreport &gt; /path/bugreport.zip</p>
<pre><code>os6.0이하 :</code></pre><p>adb bugreport &gt; /path/bugreport.txt</p>
<pre><code>
&gt; ** 로그 확보 방법 2) **
    디바이스의 개발자옵션 -&gt; 버그리포트 -&gt; 대화형 보고서 -&gt; 신고
    -&gt; dumpstate-****.zip 파일 확보


**step 4. Battery Historian 웹에 bugreport 로그 파일을 넣고 submit.**
![](https://images.velog.io/images/sysout-achieve/post/aff91e70-e7d8-4c87-8920-d6a5a57bd413/image.png)
이 화면처럼 디바이스에서 기기가 동작한 모든 정보들을 시각화. 그 정보를 가지고 우리는 어떻게 이 앱이 사용되고 있는지, 실제 어떤 동작이 많이 일어나고, 배터리가 얼마나 빠르게 소모되는지 등을 확인할 수 있다.



**step 5. 버전별 app 성능 비교**
같은 앱 사용 시나리오를 버전이 다른 두 앱에 똑같은 환경에서 돌려보고 battery historian을 이용하여 앱별 배터리 소모 상태를 확인하여 버전별로 앱 성능을 비교할 수 있게 된다.
![](https://images.velog.io/images/sysout-achieve/post/60823d90-a5e5-4eb3-b39d-0e1f81f274d1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-25%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.18.16.png)
\+ 네트워크 통신 호출 빈도, cpu 사용 등등에 대한 데이터는 덤!



&lt;hr&gt;
이슈를 구체화하여 업무를 수행할 때 더 명확하게 목표를 잡을 수 있고, 그 결과를 눈으로 확인할 수 있는 것까지 개발자의 역량이라 생각한다. 다양한 툴들을 이용하여 내가 만든 프로그램의 성능을 유지하고 개선시키는 과정을 거쳐 더 나은 개발자이자 같이 일하고 싶은 개발자로 성장하고 싶다.


</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[업무중 개선 사항과 시행착오 기록]]></title>
            <link>https://velog.io/@sysout-achieve/%EC%97%85%EB%AC%B4%EC%A4%91-%EA%B0%9C%EC%84%A0-%EC%82%AC%ED%95%AD%EA%B3%BC-%EC%8B%9C%ED%96%89%EC%B0%A9%EC%98%A4-%EA%B8%B0%EB%A1%9D</link>
            <guid>https://velog.io/@sysout-achieve/%EC%97%85%EB%AC%B4%EC%A4%91-%EA%B0%9C%EC%84%A0-%EC%82%AC%ED%95%AD%EA%B3%BC-%EC%8B%9C%ED%96%89%EC%B0%A9%EC%98%A4-%EA%B8%B0%EB%A1%9D</guid>
            <pubDate>Sat, 04 Sep 2021 09:27:16 GMT</pubDate>
            <description><![CDATA[<p><strong>업무중에 개선했던 과정과 개발 프로세스 이해도 부족으로 생긴 시행착오 기록</strong></p>
<h2 id="1-코드레벨에서의-개선-사항">1. 코드레벨에서의 개선 사항</h2>
<img src="https://images.velog.io/images/sysout-achieve/post/ab7119f5-0555-4f76-a6d1-794c826290cb/image.png" width="50%">

<p>과거에 문제를 해결하기 위해 만들었던 소스가 (이전 개발자의 의도를 정확하게 파악하지 못함 -&gt; 확장)의 과정을 거쳐 거대한(?) 관리포인트를 양산하는 상황이 발생할 수 있다. 이 상황을 예방하기 위해 과거에 만들어진 소스에서 <strong>신규 구현을 할 때</strong>에도, <strong>이전 코드에 대해 다시 검증하고 생각해보며 더 효율적으로 관리할 수 있는 코드에 대해 고민할 필요</strong>가 있다. </p>
<p>내가 업무 과정에서 개선했던 이슈를 공유하고 기록하고자 한다.</p>
<p>이전 코드는 Repository로 추상클래스를 만들고 그 추상클래스를 상속받은 DataSource class를 이용하여 데이터를 호출하는 형태로 구현되어있었다.</p>
<p>신규 요구사항은 기존에 사용했던 외부 api의 제공이 끝남에 따라 새로운 api로 데이터를 호출하는 과정이 적용돼야 했고 호출하는 데이터의 모양도 변경되는 상황이었다.
Repository로 만들어진 추상클래스 안에는 외부, 내부 데이터 호출을 모두 추상화한 상태로 작성되어있었고, 상속받은 각각의 DataSource클래스에는 사용하지 않는 method들이(외부에서만 사용하거나, 내부에서만 사용하는) 구현되어 있었다.</p>
<blockquote>
<p><strong>내가 생각하는 최악의 개발 case</strong>
 <em>: 문제점을 인식하지 못하고, 신규 기능을 붙여넣는 경우</em>
_ Repository 추상클래스에 신규 기능 추가 -&gt; 구현한 클래스로 기능 구현 (기능이 추가, 수정될 때에 구현 및 변경해야 할 클래스들이 거대해질 수 있음)_</p>
</blockquote>
<blockquote>
<p><strong>내가 해결한 방법</strong>
 : 포괄기능이 아닌 각자의 기능을 overide해서 각 datasource에 맞는 기능으로 만들어 사용하도록 변경.
  현재 Repository의 영향 범위가 넓어 기존 클래스는 남기고 개선한 클래스(변경된 기능에 해당하는 클래스)를 생성하여 나누어 개발함.
  기존 Repository 클래스의 영향도가 큰 DataSource 하나만 남겨 상속받고 다른 DataSource들은 다른 추상클래스 생성 및 상속받아 사용하도록 구현함.</p>
</blockquote>
<p>  장점!</p>
<p>  1) 잘못된 추상 클래스의 부피가 커지면 잘못 상속받은 클래스들의 관리포인트가 계속해서 늘어날 수 있고, 그 가능성을 없애기 위해 의도에 따라 추상화 클래스 생성.
  2) 잘못된 추상 클래스의 추가 기능들이 생성될 때 협업하는 개발자가 다른 레포지토리의 소스를 확인하여 나뉘어진 의도 파악이 쉬워질 것.</p>
<p>단순 구현 업무도 같은 일정내에 개선할 수 있는 부분을 찾아 개선하며 서비스에 기여하는 개발자가 되고싶다. 같이 일하고 싶은 개발자가 되려면 이러한 과정을 계속 겪으며 고민해야하지 않을까?</p>
<h2 id="2-업무-초반-개발-프로세스-이해도-부족으로-같은-업무-다시함">2. 업무 초반. 개발 프로세스 이해도 부족으로 같은 업무 다시함</h2>
<img src="https://images.velog.io/images/sysout-achieve/post/585146fc-bb76-4062-9f61-5421a1e291f8/image.png" width="50%">

<blockquote>
<h4 id="문제-인식">문제 인식</h4>
<p> : 앱 사용자가 비즈니스상 의도한 상황으로 움직이지 않는 이슈를 확인했다. 이슈 원인은 서버에서 내려주는 상태값과 클라이언트 상태값의 차이에 따라 발생한 동시성 이슈였다.</p>
</blockquote>
<p> 그 이슈를 해결하는 과정에서 동기화가 끝날 때까지 비즈니스 프로세스를 진행시키지 않는 것이 필요하다 생각했다. </p>
<p> 해결 방법으로 프로세스 진행을 위한 버튼을 비활성화 처리하고 동기화가 완료됐을 때 버튼을 활성화시키는 방향이 적절하다 판단했고 개발을 진행했다.<del>(혼자 서비스만드는게 아닌데..)</del></p>
<p> <strong>개발을 끝내고보니 &#39;UI처리는 UX팀의 의사결정이 필요하지 않을까?&#39; 라는 생각</strong>을 하게 되었고, 급하게 <strong>UX팀에게 이슈 상황과 처리 방향에 대해 문의</strong>했다. 내가 문제를 개선하기 위해 개발했던 방향으로도 처리는 가능하지만 동기화 이슈에 대해 UI 비활성화를 계속 사용자에게 보여주는 것보다는 클릭에 대한 에러 alert으로 처리하는 것이 기존 <strong>사용자 경험에 통일성을 줄 것이라는 답변</strong>을 받게되었다.</p>
<p> 개발했던 비활성화 기능보다 <strong>훨씬 간단하게 개발이 가능했고 기존의 사용자 경험에도 영향을 주지 않으면서 개선된 결과</strong>를 가져올 수 있었다. 어떤 개발을 하기 전에 정확하게 문제를 인식하고 그 문제를 해결하기 위해 필요한 리소스 파악을 명확하게 해야함을 느끼게 되었다. </p>
<p> 평소에도 커뮤니케이션의 중요성, 문제인식의 중요성, 리소스 파악의 중요성에 대해 알고 있다 생각했다. 그러나 실전에서 시행착오를 경험하게 되었다. 과감하게 커뮤니케이션하고, 필요한 것들에 대해 요구하며 함께 고민하는 과정을 통해 같이 서비스를 개발하는 동료로써 생산성을 계속해서 높일 수 있는 개발자가 되려 한다. 내가 낼 수 있는 퍼포먼스에서의 개선 사항을 계속 발견해나가자. <strong>더 좋은 동료가 되기 위해!</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[2021.09 회고록 (이직 3개월차)]]></title>
            <link>https://velog.io/@sysout-achieve/2021.09-%ED%9A%8C%EA%B3%A0%EB%A1%9D-3%EA%B0%9C%EC%9B%94%EC%B0%A8</link>
            <guid>https://velog.io/@sysout-achieve/2021.09-%ED%9A%8C%EA%B3%A0%EB%A1%9D-3%EA%B0%9C%EC%9B%94%EC%B0%A8</guid>
            <pubDate>Tue, 31 Aug 2021 10:38:13 GMT</pubDate>
            <description><![CDATA[<h1 id="이직-3개월차-회고">이직 3개월차 회고</h1>
<h2 id="인간은-적응의-동물">인간은 적응의 동물</h2>
<img src="https://images.velog.io/images/sysout-achieve/post/66735977-4247-49f6-8441-c0a3a6573f46/image.png" width="50%" height="50%">


<p>이직 초반에 어렵다 여겨졌던 아키택쳐와 RxKotlin, Git과 배포, 여러 협업 툴들이 이제 익숙해지고 있다.
기술적인 부분에서 못따라가면 어쩌지 하는 걱정은 괜한 조바심이었고, 도메인지식은 금방 습득한다고 생각했던 과거는 건방진 생각이었다는 것을 느낀 3개월이었다.</p>
<img src="https://images.velog.io/images/sysout-achieve/post/a481eb6f-93b3-4491-9392-9ec9377fa483/image.png" width="70%">


<p>생각보다 디테일한 상황들을 고려한 도메인 로직들은 History를 들을 때마다 만들어진 이유에 대해 납득할 수 있었고, 회사에서 운영하는 여러 앱(Vroong Agent, Vroong Friends, Vroong Pos, Vroong Rider, Vroong Manager)이 가진 디테일한 로직과 차이(데이터 갱신주기, 모듈 구성, DI, 아키텍처 등등 다르게 구성되어있음)들을 알아가고 있는 단계이다.</p>
<p>어떤 기능의 추가나 유지보수 또는 개선을 위해 들어오는 요구사항에 대해 분석하고 적용하는 과정은 퍼포먼스가 조금씩 나오기 시작했다. 회의에 들어가기 전, 필요한 정보를 수집하고 변경점 전반을 미리 분석해 최대한 나의 약점(백지수준의 History)을 보완하며 업무에 임하고 있다.<del>(노력은 당연... 잘해야할텐데...)</del></p>
<br>

<hr>

<p>슬금슬금 적응하며 어떤 업무를 어떻게 했는지 회고해보자.
잘못된 점과 실수는 회고를 통해 반복하지 않도록 하자.
또한 개선점을 찾아, 앞으로 어떤걸 녹일 수 있을지 계획을 세워보고 의식적으로 커리어를 쌓아가고자 한다.</p>
<h3 id="1-업무-정리">1. 업무 정리</h3>
<p>모바일팀은 함께 모든 도메인을 관리하는 방식으로 업무를 진행하고, 모두가 서로의 코드를 리뷰하며 서비스를 발전시키고 있다. 모든 서비스를 같이 바라보기 때문에 모두가 적당한 이해도를 가지고 있고, 소스 수준을 맞춰갈 수 있었다. </p>
<ul>
<li>이슈 대응
  : 실사용자가 시스템 사용중 생기는 이슈 처리</li>
<li>리팩토링
  : Legacy 구조로 되어있는 프로젝트를 최근 프로젝트의 구조로 변경하여 관리 용이성을 목적으로 리팩토링을 진행함</li>
<li>신규 기능 구현
  : 비즈니스상 or 사용자 요구사항에 맞는 신규 기능 구현</li>
<li>기존 기능 스펙 변경
  : 기존에 사용했던 외부 api를 더 이상 제공받을 수 없어 사용하고 있던 스펙을 변경하게 됨</li>
<li>형상관리 + 배포
  : 팀에서 사용하는 브랜치 전략과 배포 자동화 툴(현재 bamboo)을 사용하여 배포하고 그 과정을 프로세스에 맞춰 전달</li>
</ul>
<p>대표적으로 5가지 업무를 수행하며 더 관리가 편하고 이슈가 생기지 않는 구조를 만들기 위해 모두가 함께 고민하며 서비스를 발전시켜나가고 있다.</p>
<h3 id="2-시행착오">2. 시행착오</h3>
<img src="https://images.velog.io/images/sysout-achieve/post/18c0916a-8fea-4740-85db-11b2747b4477/image.png" width="30%">

<p>Legacy프로젝트를 기준으로 기존 기능 스펙 변경 업무 중에 단위테스트부터 도입하면, 데이터 처리 부분은 안드로이드 애플리케이션 프로세스를 진입하지 않고 확인이 가능 -&gt; 개발 속도를 개선할 수 있을 것이라 판단하여 유닛테스트 모듈을 만들었다.</p>
<p>결과: 실패...</p>
<p>안드로이드 context와 객체간 의존성이 크게 걸려있어 당장 테스트를 할 수 있는 부분이 제한적이었다. 의존성을 걷어내는 상황이 현재 일정에 맞지 않다고 판단하여 방향을 다시 돌려 업무를 최우선으로 처리했다.
원하는 요구 스펙에 맞는 결과물을 일정내에 처리할 수 있었지만, 테스트를 위한 고민에 꽤 많은 비용을 소모했다.</p>
<p>사실 어떤 업무든 고민을 통해 개선하는 과정은 필요하고 감당할 수 있는 실패는 성장의 자양분이라 생각한다. 그러나 이번 소요는 도입을 하기 전에 먼저 소스를 분석하고 판단했으면 어땠을까하는 아쉬움이 있다. 테스트 도입을 최근에 만든 프로젝트에 시도해보면서 적용했으면 더 좋은 효과를 기대할 수 있지 않았을까한다.</p>
<blockquote>
<p>이 과정을 다른 팀원들과 함께 전체적으로 개선하면 좋겠지만 현실적으로 팀에 주어진 과제가 먼저 완료되어야 할 것이라 생각된다. 다만, 내가 적용해서 개선된 점을 객관적인 수치로 보여주고 이를 전파하여 모바일팀의 전체적인 생산성 향상에 기여하는 것을 기대한다.</p>
</blockquote>
<br>

<h3 id="3-성장-경험">3. 성장 경험</h3>
<img src="https://images.velog.io/images/sysout-achieve/post/2cf98f7a-37ab-4643-b697-b26cbc66d3fa/image.png" width="60%">

<p>** 1) 잘못된 설계 확인 및 개선**
 : 예전 소스들을 보면 처음 생각했던 설계와 다른 방향으로 서비스가 진행되면서 기존의 설계가 개발과 유지보수에 방해가 되는 경우가 생길 수 있다. 그리고 잘못된 설계라는 것을 감지했을 때도, 지금 당장은 그 설계를 따라가면 본인이 맡은 업무를 가장 빠르게 쳐낼 수 있다... 그러나.. 이 설계를 기준으로 계속해서 서비스가 확장되면 확장되는만큼 계속해서 필요없는 인터페이스와 관리포인트들이 늘어날 것이 뻔히 보인다. </p>
<p> 이 때, 개발자의 역량에 따라 업무를 처리하는 방향이 많이 달라질 것 같다.(문제가 있는지 발견도 못하고 기능 구현에 급급하면 실력 향상에 더<del>욱 신경쓰자...)</del>(나도 실력 향상에 신경쓰자)~~
 일정 내에 개선된 설계를 소스 전체에 적용하는 것이 가장 좋은 방법이겠지만 현실적으로 불가능하다면 어떻게 해결해야할까?</p>
<p> 기존 설계 그대로 빠르게 일을 쳐내고 쉴 수도 있고, 과도한 일정을 잡거나 일정을 지연시키고, 모든 코드를 리팩토링할 수도 있다.</p>
<p> 나는 일정내에 해결할 수 있는 수준이 어느정도인지 파악하고, 관리포인트를 최소화할 수 있는 방향으로 코드를 개선하는 방법을 선택했다. </p>
<h6 id="--개선-사항을-찾아-어떻게-개선했는지-기록"><a href="https://velog.io/@sysout-achieve/%EC%97%85%EB%AC%B4%EC%A4%91-%EA%B0%9C%EC%84%A0-%EC%82%AC%ED%95%AD%EA%B3%BC-%EC%8B%9C%ED%96%89%EC%B0%A9%EC%98%A4-%EA%B8%B0%EB%A1%9D">-&gt; 개선 사항을 찾아 어떻게 개선했는지 기록. </a></h6>
<p><em>개발자는 코드만 짜는 사람이 아니라 현재 비즈니스를 풀어나가기 위해 가진 리소스가 어느정도인지 파악하여, 어떻게 활용하여 어떤 해결방안을 낼 것인지, 개선할 수 있는 것은 어떤게 있는지를 끊임없이 고민하는 역할이라 생각한다.</em>  </p>
<br>

<p> <strong>2) 서비스 회사에서 필요한 능력에 대해 다시 생각함(기술적 역량 + 커뮤니케이션 역량)</strong>
 : 회사에서 개발을 하며 기술적인 부분에서 많이 성장했음을 느꼈지만, 실제로 업무에서 의사소통 능력에 따라 내 퍼포먼스 차이가 많이 날 수 있음을 많이 느꼈다. 개발자는 어떤 요구사항에 따라 구현해주는 것이 전부가 아니다. </p>
<p>  (1) 구현하는 목적에 대해 정확하게 인지하는 것(해결하려는 문제가 뭔지 이해)
  (2) 어떤 예외상황들이 생길 수 있는지 예측하는 것
  (3) 어떤 리소스가 필요한지(UX리소스가 필요한지, 서버팀과 함께 작업해야하는지)
  (4) 어떻게 처리할 것이고 그 때 사용성은 어떠한지, 또 개선할 수 있는 방법은 무엇이 있는지</p>
<p>  등등에 대해 고민하여 우리의 서비스가 개선되어 사용자가 더 만족하며 서비스를 이용할 수 있도록 개발하는 역할을 해줬을 때 더 원활하게 서비스가 제공되고 의도한 결과를 더 잘 만들어 낼 수 있었다. 개발 능력은 기본적으로 중요하게 생각하지만, 이런 Soft Skill 또한 업무 퍼포먼스에 큰 영향을 준다.</p>
<h6 id="---초반-개발-프로세스-이해도가-부족하여-ux팀-협업이-필요한-부분인지-정확하게-알지-못하여-업무를-두-번-했던-상황"><a href="https://velog.io/@sysout-achieve/%EC%97%85%EB%AC%B4%EC%A4%91-%EA%B0%9C%EC%84%A0-%EC%82%AC%ED%95%AD%EA%B3%BC-%EC%8B%9C%ED%96%89%EC%B0%A9%EC%98%A4-%EA%B8%B0%EB%A1%9D">-&gt;  초반 개발 프로세스 이해도가 부족하여 ux팀 협업이 필요한 부분인지 정확하게 알지 못하여 업무를 두 번 했던 상황 </a></h6>
<p> 등을 겪으며 <strong>Soft Skill의 중요성을 몸으로 체감</strong>했다.</p>
<p>  익숙하지 않아 생긴 몇 번의 실수들을 동료들이 이해해주고 노력을 통해 극복할 수 있었고, 지금은 개발업무 시작 전 문제 인식과 리소스 파악의 중요성에 대해 더 많이 느껴 같은 실수를 반복하지 않고 있다. 
  스스로 느끼고 변화하는 것만큼 빠르게 변하는 건 없지만, 실수를 하는 상황은 유쾌하지 않다. 다른 사람들에게 유쾌하지 않은 상황을 피하게 하려면 개발프로세스 확립과 문서화를 통해 버벅거리는 상황을 피하도록 준비를 해두자.</p>
<h3 id="4-마무리">4. 마무리</h3>
<img src="https://images.velog.io/images/sysout-achieve/post/52510629-0a09-43bb-ac90-d7f44511b909/image.png" width="70%">
회고를 목표로 매일매일 업무를 개인적으로 관리하고, 관리한 업무를 다시 읽어보면서 내가 어떤 생각을 했고 어떻게 적용했는지 작성해보는 이 과정이 성장을 필요로 하는 나에게 좋은 영양분이 되는 것 같다.

<blockquote>
<h5 id="1-일과-시작-전-하루의-업무를-미리-작성하기-때문에-업무중에-특별한-이벤트가-생겨-집중이-흔들려도-방향을-잃지-않아-퍼포먼스가-나름-일정하게-나온다">1) 일과 시작 전, 하루의 업무를 미리 작성하기 때문에 업무중에 특별한 이벤트가 생겨 집중이 흔들려도 방향을 잃지 않아 퍼포먼스가 나름 일정하게 나온다.</h5>
<h5 id="2-정말-많은-일을-했던-시간보다-과거에-무엇을-했는지-명확하게-기억에-남는다">2) 정말 많은 일을 했던 시간보다 과거에 무엇을 했는지 명확하게 기억에 남는다.</h5>
<h5 id="3-일상적인-업무-처리가-아니라-의식적인-업무처리로-내가-무엇을-하고-있고-더-좋은-방향에-대해-계속해서-고민한다걷기만-많이-한다고-모델이-될-수-없다-걷는-연습을-해야지">3) 일상적인 업무 처리가 아니라 의식적인 업무처리로 내가 무엇을 하고 있고 더 좋은 방향에 대해 계속해서 고민한다.(걷기만 많이 한다고 모델이 될 수 없다. 걷는 연습을 해야지!)</h5>
</blockquote>
<p>이러한 장점들이 있어 회고를 계속해서 작성하고, 또 뿌듯해하고 있다. 앞으로도 의식적인 개발을 바탕으로 업그레이드하는 내 모습을 상상하며 천천히, 꾸준히 성장해가야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[String, StringBuilder, StringBuffer 차이점? 당황하지말자(feat. 면접몸풀기)]]></title>
            <link>https://velog.io/@sysout-achieve/String-StringBuilder-StringBuffer-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%8B%B9%ED%99%A9%ED%95%98%EC%A7%80%EB%A7%90%EC%9E%90feat.-%EB%A9%B4%EC%A0%91%EB%AA%B8%ED%92%80%EA%B8%B0</link>
            <guid>https://velog.io/@sysout-achieve/String-StringBuilder-StringBuffer-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EB%8B%B9%ED%99%A9%ED%95%98%EC%A7%80%EB%A7%90%EC%9E%90feat.-%EB%A9%B4%EC%A0%91%EB%AA%B8%ED%92%80%EA%B8%B0</guid>
            <pubDate>Sun, 15 Aug 2021 10:39:10 GMT</pubDate>
            <description><![CDATA[<p>면접을 보면 면접관들은 지원자의 긴장을 덜어주기 위해 아주 간단한 질문을 하는 경우가 있다.
그 질문중에 대표적인 것으로 String, StringBuilder, StringBuffer의 차이가 있다.</p>
<p>당연하게 알고 있는 지식들이라 생각해서, 면접자가 모를거라는 대비를 안한 면접관들은 함께 당황할 수 있다.(<del>면접관을 당황시킬 수 있는 절호의 기회!</del>)</p>
<img src="https://images.velog.io/images/sysout-achieve/post/b2e402a2-cfdd-4ea4-971e-0dbef50eaf9f/image.png" width="50%" height="50%">


<p>이 질문에 대해 궁금해서 찾아보고 가변객체와 불변객체의 차이, 동시성이 고려된 객체인지 아닌지를 외우고 있을 수 있다.</p>
<p>일단 <strong>기본적인 사용법</strong>은 다음과 같다.</p>
<blockquote>
<p>String : 문자열 연산이 적고 멀티쓰레드 환경에서 사용
StringBuffer : 문자열 연산이 많고, 멀티쓰레드 환경일 경우
StringBuilder : 문자열 연산이 많고, 단일쓰레드 환경이거나 동기화를 고려할 필요가 없는 경우</p>
</blockquote>
<p>위 설명을 보면 머릿속으로 <strong>가설</strong>을 만들 수 있다.
 : 단일쓰레드 환경에서 실험할 때 StringBuilder &gt; StringBuffer &gt; String 순으로 성능이 좋을 것</p>
<p>이 과정을 실제 코드로 실험해보고 어떤 차이가 있나 확인해보고 정확하게 알아보자!</p>
<p>세 가지 종류의 객체를 생성하여 반복적으로 객체에 값을 추가하며 연산처리속도와 쌓이는 메모리를 값을 출력하도록 실험해보았다.</p>
<p><strong>1. String</strong></p>
<pre><code>String str = &quot;&quot;;
while (i&gt;0){
     str = str + &quot;world&quot;;
     i++;
}</code></pre><blockquote>
</blockquote>
<p><strong>String</strong>
100,000번 반복 변경
HeapMemory -&gt; 721420288
실행 시간 : 4.393sec
<em>(다음 실험 무의미)</em></p>
<p><strong>2.StringBuilder</strong></p>
<pre><code>StringBuilder str = new StringBuilder();
while (i &gt; 0) {
      str = str.append(&quot;world&quot;);
      i++;
}</code></pre><blockquote>
<p>StringBuffer
100,000번 반복 변경
HeapMemory -&gt; 272629760
실행 시간 : 0.217sec
1,000,000번 반복 변경
HeapMemory -&gt; 272629760
실행 시간 : 2.947sec
10,000,000번 반복 변경
HeapMemory -&gt; 350224384
실행 시간 : 27.693sec</p>
</blockquote>
<p><strong>3.StringBuffer</strong></p>
<pre><code>StringBuffer str = new StringBuffer();
while (i &gt; 0) {
      str = str.append(&quot;world&quot;);
      i++;
}</code></pre><blockquote>
<p>StringBuilder
100,000번 반복 변경
HeapMemory -&gt; 272629760
실행 시간 : 0.219sec
1,000,000번 반복 변경
HeapMemory -&gt; 272629760
실행 시간 : 2.586sec
10,000,000번 반복 변경
HeapMemory -&gt; 350224384
실행 시간 : 27.811sec</p>
</blockquote>
<p>결론:
<strong>String</strong>으로 생성된 객체에 문자열을 추가할 경우 새로운 객체로 힙메모리에 저장, 기존의 객체도 메모리에 남아 객체가 쌓이게 된다. (지속적으로 변경되는 상황에 비효율)
: <strong>String은 불변객체로 사용하기 위해 만들어진 클래스 -&gt; 변경이 아닌 새로운 객체 생성</strong></p>
<p><strong>StringBuffer, StringBuilder</strong>는 실험결과에서 크게 <strong>차이가 없었음</strong>
클래스 내부를 확인해본 결과
: StringBuffer는 synchronized 키워드가 포함되어 동시성 문제를 막을 수 있는 가변객체.
StringBuilder의 경우 but with no guarantee of synchronization이라는 주석에서 알 수 있듯이 동시성을 고려하지 않은 가변객체임을 확인할 수 있었다.</p>
<p>추가적으로 생각했던 가설과는 다르게 StringBuffer와 StringBuilder의 성능차이는 거의 없었다.
다만 의도에 맞는 객체를 사용 -&gt; 작성한 코드 의도를 명확하게 보여주는 것이 좋을 것이라는 생각을 하게 됐다.</p>
<p>이 글을 통해, 면접관을 잘못된 방향으로 당황시키는 불상사가 조금이나마 줄었으면 좋겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Rx? 리액티브 프로그래밍? 왜 비동기 처리로 사용될까?]]></title>
            <link>https://velog.io/@sysout-achieve/Rx-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EB%90%A0%EA%B9%8C</link>
            <guid>https://velog.io/@sysout-achieve/Rx-%EB%A6%AC%EC%95%A1%ED%8B%B0%EB%B8%8C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%99%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%EB%A1%9C-%EC%82%AC%EC%9A%A9%EB%90%A0%EA%B9%8C</guid>
            <pubDate>Tue, 27 Jul 2021 14:47:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sysout-achieve/post/f44ecfc0-ead4-4363-ac35-af1cc3af9576/image.png" alt="">
많은 사람들이 Rx라이브러리로 안드로이드 비동기처리를 사용한다. 안쓰면 뒤쳐지는 것 같아서, 다른 사람들이 사용해서, 대세라서, 많은 회사들이 사용해서...(<del>나도 그렇게 시작했다</del>... 반성하자)</p>
<p>RxJava, RxKotlin으로 안드로이드 비동기처리를 하면서 이상함(?)을 느꼈다.
왜 굳이 Rx를 사용하는걸까?
계속 의문이 생겼다. 
이게 비동기 처리에 굳이 사용되어야 하는건가? 데이터를 스트림으로 처리하는게 왜 좋은거지?</p>
<p>*<em>의문을 해결하기 위해, 본질을 모르고 개발하고 싶지 않아 공부하며 글을 작성한다. *</em></p>
<p>리액티브 프로그래밍? 반응형 프로그래밍? 그게 뭔데?
: 아<del>~ 데이터를 스트림형식으로 받아서 처리하는걸 반응형이라고 하는구나</del> &lt;-- (완전 겉핥기, 특징과 본질을 아~주 정확하게 오해한 좋은 예)</p>
<img src="https://images.velog.io/images/sysout-achieve/post/4f5237b9-10e5-4cdc-af2d-226dc2728a08/image.png" width="30%"/>


<p><strong>제대로 이해해보자.</strong></p>
<p>먼저 Rx부터 보자.</p>
<h1 id="rx-왜-나왔니">Rx 왜 나왔니?</h1>
<h3 id="rx란">Rx란?</h3>
<p>: 비동기와 이벤트 기반 프로그램을 잘 작성하기 위해 필요한 observable sequence 라이브러리
비동기 처리 해법으로 탄생 = (Observable + LINQ + Scheduler)
명료하게 설명한 듯 하지만 감이 잘 오지 않았다. </p>
<h4 id="rx는-어디에서-왔을까역사">Rx는 어디에서 왔을까(역사)</h4>
<p>: 비동기 프로그래밍 문제를 해결하기 위해 만들어짐 (비동기, 데이터 동기화의 중요성이 증가됨에 따라 점차 각광)
비동기처리를 위해 만들어진만큼 비동기에 특화되어있겠다라는 생각을 할 수 있다.</p>
<blockquote>
<p>소프트웨어 환경은 과거와 많이 달라짐 
-&gt; 인터넷이 발달하면서 트래픽이 이전에 비해 많이 달라짐
-&gt; 동시접속자 수 늘어남 
-&gt; 무어의 법칙 깨짐 
-&gt; 사용자의 요구사항은 늘어나고 더 빠른 반응속도를 기대함 
-&gt; 소프트웨어는 더 복잡해졌고, 안정성은 더 중요한 문제가 됨
-&gt; <strong>회복탄력성과 유연성을 갖도록 시스템을 설계해야한다!</strong>  &lt;-- 달성 방법으로 Message Driven이 탄생</p>
</blockquote>
<hr>
<h2 id="리액티브-프로그래밍">리액티브 프로그래밍</h2>
<p>: 변화의 전파와 데이터 흐름과 관련된 선언적 프로그래밍 패러다임
어렵네? 이름도 추상적인데 정의도 어려워...</p>
<p>외부에서 들어오는 자극에 반응<em>하는 구조를 만드는 것을 목적으로 프로그래밍을 함
반응\</em>: 밖에서 안으로, 그리고 수동적으로 
 &amp;nbsp&amp;nbsp - 프로그램이 외부와 상호작용하는 기존의 방식을 거꾸로 뒤집어서 수동적 반응을 획득하는 일</p>
<br>

<h4 id="외부-환경에-대한-결과를-반환하는-과정을-두-가지-시나리오로-설명할-수-있다">외부 환경에 대한 결과를 반환하는 과정을 두 가지 시나리오로 설명할 수 있다.</h4>
<p><strong>pull, push Scenario</strong></p>
<p><strong>pull 시나리오</strong> : 프로그램이 외부환경의 명령에 의해서 원하는 결과를 획득한다.
이경우 프로그램이 직접 제어의 흐름을 통제한다. (명령형 - 우리가 일반적으로 자주 사용하는 코드스타일)
(ex. Iterator)
<strong>push 시나리오</strong> : 환경이 프로그램 안으로 요청을 밀어넣는다. 요청이란 메세지, 데이터 상태 등등.
(ex. Observable)</p>
<blockquote>
<h5 id="pull-시나리오--비동기-request-던지고-response를-기다렸다가-callback으로-받아서-처리">pull 시나리오 : 비동기 request 던지고 response를 기다렸다가 callback으로 받아서 처리</h5>
<h5 id="vs">vs</h5>
<h5 id="push-시나리오--비동기-request를-던지고-외부데이터-스트림에-대한-subscribe형식으로-유입에-반응하도록-처리">push 시나리오 : 비동기 request를 던지고, 외부데이터 스트림에 대한 subscribe형식으로 유입에 반응하도록 처리</h5>
</blockquote>
<p>차이 - 외부 환경에 <strong>명령을 하고 응답이 오기까지 기다리는것이 pull</strong>, 
기다리지 않는 것이 <strong>push &lt;-- 그 순간에 반응</strong>하는 것</p>
<p>따라서 <strong>push 시나리오는 제어의 흐름을 직접 통제하지 않기 때문에 비동기처리에 유리하다.</strong></p>
<p>그럼 이 두 가지(Iterator, Observable)는 완벽히 다른 것인가?
-&gt; 이벤트를 여러번 호출하면 연속하는 데이터를 주입할 수 있다. -&gt; <strong>두 가지의 본질은 같다.</strong>(* 다만 데이터가 흐르는 방향이 다를 뿐) 
<strong><em>* 데이터의 방향이 다르다는 것이 늘 추상적으로 느껴졌고 암기의 개념처럼 느껴졌다. 그러나 알고보면 이것만큼 반응형을 표현할 방법이 없다...</em></strong></p>
<p>Rx는 Observer패턴에서 좀 더 진화된 인터페이스인 Observable을 제공한다.
Iterator로 사용하는 모든 것들을 Observable로 표현할 수 있다. 단,데이터가 흐르는 방향만 반대로! &lt;-- 이게 바로 <strong>리액티브 프로그래밍이 지향하는 수동적 방향성</strong>이다!!</p>
<br>


<img src="https://images.velog.io/images/sysout-achieve/post/0811afb4-c26c-451f-b800-8feb7728e277/image.png" width="300px" height="300px"/>



<h3 id="정리">정리</h3>
<ol>
<li>비동기 처리를 Rx로 해야하는 것이 아니라, 비동기 처리 개념을 반응이라는 다른 시선으로 바라보고 해결하려 했다는 것</li>
<li>callback을 기다리는 기존 pull시나리오 개념이 아닌, 외부 데이터 유입에 반응하여 더 자연스러운 비동기 처리가 가능하도록 push시나리오로 만드는 것</li>
<li>반응할 유입 데이터를 stream으로 받게되고 그 데이터를 의도한 모양의 데이터로 변형시켜 사용할 수 있다는 장점은 덤!</li>
</ol>
<p>다른 특징들과 장점들이 더 있겠지만, Rx가 가진 패러다임을 이해하는 데에 꽤 중요한 개념들을 알게되었다. 앞으로의 Rx사용은 단순한 적용보다 Rx가 가진 본질적인 의도에 맞게 사용이 가능해질 것 같다.</p>
<br>
<br>

<p>다만...</p>
<img src="https://images.velog.io/images/sysout-achieve/post/d6ca1d66-b555-4482-8b8a-691e77c3d829/image.png" width="400px" height="400px"/>

<p>아직 Rx에 대해 궁금한 점이 남아있다.</p>
<h4 id="풀리지-않은-궁금증">풀리지 않은 궁금증</h4>
<ol>
<li>Rx라이브러리의 Observable + LINQ + Scheduler의 각 요소들의 동작 원리</li>
<li>Rx가 스트림으로 데이터를 처리한다면 그 데이터가 메모리에 적재되는 공간은 어디이며 어떻게 사용되고 소멸되는가</li>
</ol>
<p>남은 궁금증은 계속 탐구하면서 다음 글로 남겨봐야겠다.</p>
<hr>
<blockquote>
<p>참고 자료: <a href="https://www.youtube.com/watch?v=3FKlYO4okts">https://www.youtube.com/watch?v=3FKlYO4okts</a> </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[WorkManager (미완)]]></title>
            <link>https://velog.io/@sysout-achieve/WorkManager</link>
            <guid>https://velog.io/@sysout-achieve/WorkManager</guid>
            <pubDate>Fri, 23 Jul 2021 09:49:31 GMT</pubDate>
            <description><![CDATA[<h2 id="workmanager">WorkManager?</h2>
<p>안드로이드에는 백그라운드 작업을 위한 다양한 옵션이 있다. 그 중 WorkManager는 유연하게 하위 버전과 호환되는 라이브러리. 안드로이드에서 권장되는 작업스케줄러로, 지연 작업을 실행하도록 보장한다.</p>
<p>WorkManager는 *상황별 실행, *보장된 실행을 조합하여 적용해야하는 백그라운드 작업을 위한 아키텍처 구성요소로 Jetpack에 포함되어있다. </p>
<blockquote>
<p>* 상황별 실행 - WorkManager가 최대한 빨리 백그라운드 작업을 실행함
* 보장된 실행 - WorkManager가 앱이 Foreground상태를 벗어난 다양한 상황에서도 로직을 처리하며 작업을 진행함</p>
</blockquote>
<h4 id="장점">장점</h4>
<ul>
<li>제약 조건에 따라 스케줄링된 작업을 수행할 수 있다.</li>
<li>비동기 일회성 작업과 주기적인 작업을 모두 지원함</li>
<li>네트워크 상태, 저장공간, 충전상태와 같은 제약조건을 지원함</li>
<li>동시 작업 실행을 포함한 순차적 요청 체이닝이 가능함</li>
<li>한 작업 요청의 출력이 다음 작업 요청의 입력으로 사용할 수 있음</li>
<li>하위버전 호환성 처리되어있음(WorkManager는 JobScheduler, AlarmManager 등의 api위에 존재 - 사용자의 기기 API수준을 조건에 따라 사용함)</li>
<li>GooglePlay 서비스 사용 여부 관계없이 동작</li>
<li>UI 작업 요청 LiveData 지원함</li>
</ul>
<h3 id="어디서-쓰이지">어디서 쓰이지??</h3>
<p>사용자가 특정화면이나 앱에서 나가도 완료하는 것을 의도한 작업에 적합하다. </p>
<p>1) 로그 업로드
2) 이미지에 필터 적용 및 이미지 저장
3) 로컬데이터를 주기적으로 네트워크에 동기화할 경우 등</p>
<h4 id="두-가지-클래스로-구성">두 가지 클래스로 구성</h4>
<p>worker</p>
<ul>
<li>백그라운드에 실행하고자 하는 실제 로직 코드 정의
: doWork() Overide함 -&gt; Result 반환 (이 작업을 성공적으로 수행했는지 여부를 반환)</li>
</ul>
<p>workerRequest</p>
<ul>
<li>체인형식으로 작업을 추가하기 위한 클래스
<a href="https://developer.android.com/codelabs/android-workmanager/img/54832b34e9c9884a.png">https://developer.android.com/codelabs/android-workmanager/img/54832b34e9c9884a.png</a>
: 이를 이용해 순서대로 실행하거나 동시에 실행되는 별도의 WorkerRequest를 만들 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[2021.06 회고록 (이직 1개월차)]]></title>
            <link>https://velog.io/@sysout-achieve/2021.06-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%EC%9D%B4%EC%A7%81-1%EA%B0%9C%EC%9B%94%EC%B0%A8</link>
            <guid>https://velog.io/@sysout-achieve/2021.06-%ED%9A%8C%EA%B3%A0%EB%A1%9D-%EC%9D%B4%EC%A7%81-1%EA%B0%9C%EC%9B%94%EC%B0%A8</guid>
            <pubDate>Tue, 29 Jun 2021 14:08:02 GMT</pubDate>
            <description><![CDATA[<p>입사한 지 한 달, 이제 업무에 대해 이해하고 기본적으로 사용하는 아키텍처와 소스 코드 의도에 대해 파악이 된 것 같다. 친절하고 실력있는 동료들과 협업을 하며 내 능력치를 끌어올릴 수 있는 기회를 얻게 됐다는 것에 뿌듯했다.</p>
<p>온보딩을 하며 내가 가장 신경썼던 부분은 최대한 빠르게 업무에 적응하여 우리 팀의 업무를 분담하고 능력치를 키우며 적응하는 것이었다.
    이를 위해, 나는 <strong>나름의 전략</strong>을 사용하여 적응하려고 노력했다.</p>
<blockquote>
<ol>
<li>&#39;단순적용&#39;이 아닌 &#39;고민 후 적용&#39; <ol start="2">
<li>청소라도해서 팀에 기여하자 (진짜 청소말고...)</li>
<li>시행착오는 나 하나로 종결</li>
</ol>
</li>
</ol>
</blockquote>
<br>
내가 사용했던 전략(?)들 덕분에 도움이 됐던 것과 아쉬웠던 부분을 정리하고 회고하려 한다.


<br>
<br><br>



<h3 id="1-단순적용이-아닌-고민-후-적용">1. &#39;단순적용&#39;이 아닌 &#39;고민 후 적용&#39;</h3>
<p><img src="https://images.velog.io/images/sysout-achieve/post/daac495b-630e-464b-87f8-9522588ebbdd/image.png" alt=""></p>
<p>-&gt; 온보딩 과제를 이해하고, 단순하게 구현하는 것이 아니라 왜 이 구조를 적용하고 어떻게 사용하는지, 회사에서 사용하는 프로덕트와 비슷하게 만들기 위해 고민하며 과제를 진행했다.(구조가 가진 장점과 단점도 자연스럽게 느낄 수 있었음)
    회사에서는 MvRx architecture를 사용하여 프로젝트를 구성했고, 오래된 프로젝트는 Rx를, 비교적 최근 프로젝트는 코루틴을 비동기 처리에 사용하고 있었다. 나는 효과적인 업무를 하기 위해 상대적으로 약한 Rx 적용에 대해 고민하는 시간을 길게 가졌고, MvRx를 원활하게 사용하기 위해 근본적으로 구조가 나온 목적을 이해하려고 노력했다.</p>
<blockquote>
<h4 id="tl님-tip이-있었다">TL님 Tip이 있었다</h4>
<ul>
<li>비슷한 구조를 이해하면서 공부하면 도움이 될 수 있다. ex) MVI</li>
<li>한 번에 다 적용하려면 어렵다. 나눠서 공부하면 도움이 된다. 
ex) Clean Architecture, Dagger2 등</li>
</ul>
</blockquote>
<p>이 과정을 통해 MvRx는 MVVM이나 MVP와는 다른 장점과 단점을 파악할 수 있었다.</p>
<h4 id="장점">장점</h4>
<p>-&gt; 상태관리를 통한 애플리케이션 컨트롤</p>
<p>: 상태를 관리한다는 개념을 써본 사람과 안써본사람이 느끼는 차이가 클 것이라 생각한다. 기존에 MVVM이나 MVP로 개발해왔던 사람에게는 조금 추상적인 개념일 수 있다. 상태를 일괄적으로 처리할 수 있도록 지정해두고 상태의 변경점을 view에서 구독하며 화면에 출력 또는 로직을 수행하는 개념이다. 이 방법으로 개발자는 &#39;화면의 IO&#39;와 &#39;상태의 변경&#39;에 집중할 수 있고, 협업하는 동료들이 어떤 의도로 코드를 작성했는 지 쉽게 파악이 가능하다.(구조를 이해하는 개발자에 한하여..)
의도치 않은 상황과 사이드이펙트(side effect)를 최대한 억제할 수 있는 구조라는 생각이 들었다.</p>
<h4 id="단점">단점</h4>
<p>-&gt; 어느 정도의 진입장벽과 테스트(Unit, UI) 어려움</p>
<p>: MVVM, MVP만 사용했던 개발자들이 바로 도입할 때 어려움이 조금 있을 것이라 생각된다. MVP는 View에서 Presenter로, Presenter에서 View로 메서드를 호출하며 직관적으로 이해하기 좋았던 것 같다. MVVM의 경우 ViewModel의 데이터를 라이브데이터로 생성하고 구독하는 개념에서 살짝 더 이해가 어려웠던 것 같다.
여기서 MvRx는 상태를 컨트롤하고 구독하는 과정에서 한 단계 거시적으로 구조를 잡은 것처럼 느껴졌다. 그리고 상태를 컨트롤한다는게 말은 쉽지만 막상 코드로 접했을 때 이렇게 하면 되는건가?라는 의문을 계속해서 갖게 하는 과정이 있었다.</p>
<p>테스트에 대해 생각해보면 MVP가 MVVM보다 직관적인 호출 형태를 취하기 때문에 테스트를 작성하기에 상대적으로 더 쉬운 구조라 생각한다. 여기서 한 단계 더 추상적인 MvRx는 어떻게 테스트해야할지 아직 감이 잡히지 않는다. 또한 레퍼런스도 다른 아키텍처에 비해 부족하고 적용이 어렵다.
<strong>(잘하는 사람의 테스트를 보고 따라가면 좋겠지만 아직 프로덕트에 테스트가 도입되지 않아 아쉬웠다ㅜㅜ <em>다니면서 내가 도전해본다!!!</em>)</strong></p>
<blockquote>
<p>그냥 단순 코드 구현이 아니라 고민과 함께 했기에 구조를 적용하며 느꼈던 것들이 더 기억에 오래 남고 내 것으로 남는 느낌을 받았다. 아쉬운 점이 있다면 너무 깊은 고민과 이해가 안될 때 <em>뇌정지_로 인한 _더딘 진행</em>... 퍼포먼스가 내 욕심보다는 안나왔던 것 같다. 좋은 팀원들이 급하게 생각하지 말고 천천히 하라고 잘하고 있다고 해주는 것으로 큰 힘이 되었고, 이런 <strong>동료들이 있다는 것에 감사</strong>했다ㅜㅜ
<strong>앞으로도 &#39;고민 후 적용&#39;하며 내 성장에 집중하려고 한다. 그리고 나의 성장으로 또 회사에 기여하며 긍정적인 피드백을 계속해서 만들어내고 싶다.</strong></p>
</blockquote>
<h6 id="-mvrx에-대해서는-개인적인-공부와-실험들을-가지고-다시-정리해서-따로-글을-올릴-예정이다대단한거-아니고-내-공부용으로">* MvRx에 대해서는 개인적인 공부와 실험들을 가지고 다시 정리해서 따로 글을 올릴 예정이다.(대단한거 아니고 내 공부용으로!)</h6>
<br>

<h3 id="2-청소라도해서-팀에-기여하자-진짜-청소말고">2. 청소라도해서 팀에 기여하자 (진짜 청소말고...)</h3>
<p><img src="https://images.velog.io/images/sysout-achieve/post/90200a03-4bdc-46ba-a024-c0a3e3524931/image.png" alt=""></p>
<p>-&gt; <strong>작고 번거로운 업무라도 적극적으로 맡아 처리하면서 기여할 수 있는 부분을 최대한 찾으려고 노력했다.</strong>(3rd party 서비스 종료로 인한 프로덕트 수정)
    : &#39;배달통&#39;서비스 종료로 프로덕트에 흩어져있는 관련 소스들을 정리해야하는 이슈가 있었다. 프로덕트 내에 있는 소스를 확인해보고 내가 할 수 있는 수준의 업무라는 생각을 하게 되었다. 먼저 이 <strong>이슈에 대해 언급하며 업무를 맡아 처리</strong>했다.</p>
<p><strong>사소하고 귀찮은 업무</strong>지만, 아직 미숙하게 적응하고 있는 내가 할 수 있는 일이라면 <strong>먼저 맡아서 처리</strong>하려고 노력했다. 이러한 과정을 통해 나는 노력으로 조금씩이나마 업무에 기여 할 수 있게되었다.</p>
<p>처음에 미숙하여 어떤 부분에 있어서 다른 팀원들보다 일을 덜 하는 경우가 생길 수 있다. 그러나 그것을 당연하게 생각하기보다는 <strong>미숙한만큼 다른 노력으로라도 채우기위한 노력</strong>이 있다면 좋을 것 같았다. 내가 일할 때 이런 생각을 하는 새로운 팀원이 있다면 반가울 것 같았고, <strong>내가 먼저 그런 사람</strong>이 되고 싶었다.</p>
<br>

<h3 id="3-시행착오는-나-하나로-종결">3. 시행착오는 나 하나로 종결</h3>
<p><img src="https://images.velog.io/images/sysout-achieve/post/aa14b9a2-6f80-418e-98d7-4c7c7f6c0ff7/image.png" alt=""></p>
<p>-&gt; 업무처리 중 동료들의 기억이 가물가물한 처리, 다른 팀에 요청했던 <strong>과정을 위키에 문서로 남겨 다른 사람의 시행착오를 줄이기 위해 노력</strong>했다.
    : 당연한 이야기지만 <strong>시행착오는 적을수록 좋다</strong>. 물론 시행착오를 통해 더 깊은 지식을 습득하는 경우도 있지만(CS한정이라 생각됨) 회사 시스템 특성에 따른 시행착오는 최대한 줄여나가는 것이 생산성 향상과 쓸데없는 비용을 줄이는 데 효과적이라 생각한다. 
그럼 이런 시행착오를 줄이는 것에 기여할 수 있는 방법이 있다면? <strong>해야지!</strong>
크고 대단한 시행착오를 줄일 수 있으면 더 좋겠지만 <strong>작은 것이라도 시행착오를 줄이기 위해 문서화</strong>하여 처리하려 했다.</p>
<p>내가 했던 시행착오를 똑같이 하는 상황을 없애려는 노력은 앞으로도 계속해서 좋은 습관으로 남기고 싶다.</p>
<br>

<h3 id="아쉬웠던-것">아쉬웠던 것</h3>
<p>말한 전략들을 모두 완벽하게 해냈으면 좋았겠지만 아주아주 미약하게 적용한 것 같다. 하지만 생각하지 않고 업무에 임했으면 그 미약함마저 없지 않았을까...?
다만 업무적응과 회사적응이라는 <del>합리적인?</del> 변명으로 계속해오던 공부와 사이드 프로젝트를 내려놓았던 것이 아쉽다. 분명 할 수 있는 시간들이 많이 있었는데 더욱 격렬하게 밍기적거리고 싶었다. 이 밍기적거림이 습관이 되지 않게 다시 정신차려야지..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Clean Architecture]]></title>
            <link>https://velog.io/@sysout-achieve/Android-Clean-Architecture</link>
            <guid>https://velog.io/@sysout-achieve/Android-Clean-Architecture</guid>
            <pubDate>Fri, 21 May 2021 10:00:37 GMT</pubDate>
            <description><![CDATA[<p><strong>잘못된 정보나 수정할 사항 or 피드백은 언제든 환영합니다.</strong></p>
<p><br><br></p>
<p>개발 공부를 하면서 굉장히 자주 만나는 익숙하지만 친하지 않은 용어가 있다. 
<strong>Clean Architecture</strong>...<del>(잘 안친해짐, 낯가림)</del></p>
<p>클린아키텍처와 친해지기 위해 왜 적용하는지, 그때 이점은 뭐가 있고, 어떤 비용이 생기는지에 대해서 정리하고자 한다. (Clean Architecture 책 정독 후 업데이트 예정)</p>
<h2 id="클린아키텍처란">클린아키텍처란?</h2>
<p>: Clean Architecture란 코드를 잘 분리하기 위한 기법 
코어 비즈니스 로직을 바뀔 수 있는 모든 것으로부터 분리하여 제품을 유연하게 구성하려는 목표를 가짐</p>
<p>왜 적용하는가?</p>
<ul>
<li>프레임워크에 독립적임
: Clean Architecture의 경우 일부 기능이 포함된 라이브러리, 프레임워크에 의존하지 않음</li>
<li>UI에 독립적임
: 다른 시스템 변경없이 UI를 쉽게 변경할 수 있음</li>
<li><blockquote>
<p>독립적이기 각 Layer의 변경에도 영향을 적게 받음. 시스템의 추가 및 변경이 필요한 시기에 유연한 대응이 가능함</p>
</blockquote>
</li>
</ul>
<p><img src="https://blog.kakaocdn.net/dn/Tc5dZ/btqJV6DkmDt/igcmZnGORlSkuBjo9Uokqk/img.jpg" width="80%" height="80%"></img>
<em>ref. Clean Archtecture 2012 Uncle Bob</em></p>
<ul>
<li>그림의 안쪽으로 갈수록 의존성을 갖지 않음(반대 방향의 의존성을 가져야할 경우 interface를 사용해야함</li>
<li>안쪽계층은 바깥계층에게서 독립적으로 존재해야함</li>
<li>UseCase를 중심으로 서비스를 개발해야함</li>
</ul>
<br>

<h3 id="각-layer에-대한-설명">각 Layer에 대한 설명</h3>
<p>Entity</p>
<ul>
<li>유저가 해당 앱에서 기대하는 어떠한 기능과 개념을 정의한 Layer</li>
<li>순수 모듈, 프레임워크단에 대한 의존성이 없음</li>
</ul>
<p>Domain</p>
<ul>
<li>사용자가 앱을 사용할 때 생각하는 일련의 개념 단위</li>
<li>앱에서 일어날 동작(UseCase)을 작성</li>
<li>Domain 레이어에서 Repository Interface 정의</li>
</ul>
<p>Data</p>
<ul>
<li>Data 레이어는 Domain 레이어에서 설계한 Repository를 실제 구현</li>
<li>DataSource 의존성을 가짐</li>
<li>Data를 호출하는 라이브러리(내부DB, Network 라이브러리 등)에 대한 의존성을 갖게 됨</li>
</ul>
<p>Presentation</p>
<ul>
<li>화면 UI/UX를 정의하는 레이어</li>
<li>View, ViewModel 등이 구현되며 안드로이드 의존성을 갖게 됨</li>
</ul>
<blockquote>
<p>클린아키텍처의 장,단점</p>
<p>장점</p>
</blockquote>
<ul>
<li>클래스 집중화로 유지보수 쉬워짐(이슈발생 시 예측 용이)</li>
<li>내부 레이어가 사용자 인터페이스로부터 독립됨</li>
<li>내부 레이어가 다른 외부관련 요소와 독립됨</li>
<li>모든 것이 계층화, 각각 책임이 명확하여 클린아키텍처를 이해한 사람은 비교적 빠르게 코드 파악가능<blockquote>
<p>단점</p>
</blockquote>
</li>
<li>클린 아키텍처를 구현하려면 별도의 모듈이 필요하고 새로운 인터페이스를 구현하는 과정에서 초반에 비교적 큰 비용 발생 </li>
</ul>
<br>

<h4 id="use-case">Use Case</h4>
<p>: 어플리케이션이 수행하고자 하는 핵심적인 기능(우리가 만드는 애플리케이션에서 절대로 바뀌지 않을 기능 - 기획이 바뀌면 만드는 제품이 바뀌는 것이기 때문에 당연히 바뀜)
클린아키텍쳐의 핵심은 코어 비즈니스 로직을 바뀔 수 있는 모든것으로부터 분리하자라는 생각</p>
<p>UseCase를 사용하는 이유</p>
<ul>
<li>사용자가 수행하려는 기능 명확하게 정의</li>
<li>UseCase로 시스템이 수행해야하는 행위를 정의 -&gt; 프로젝트의 구성요소를 UseCase내에서 사용하여 목적을 달성 -&gt; 각 Layer의 격리 도움</li>
</ul>
<br>

<p> 장점</p>
<ul>
<li>UseCase를 사용함으로써 ViewModel에 로직이 집중되는 것을 피할 수 있음</li>
<li>다른 ViewModel에서 같은 로직을 사용할 경우 재활용가능</li>
<li>각 기능을 명확하게 나누게 됨(직관적으로 UseCase가 할 행동에 대해 예측 가능)</li>
<li>기능에 따른 클래스 분리는 테스트를 용이하게 함</li>
<li>협업할 경우 소스의 일관성을 기대할 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Fragment간 데이터 전달 방법들]]></title>
            <link>https://velog.io/@sysout-achieve/Android-Fragment%EA%B0%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC-%EB%B0%A9%EB%B2%95%EB%93%A4</link>
            <guid>https://velog.io/@sysout-achieve/Android-Fragment%EA%B0%84-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC-%EB%B0%A9%EB%B2%95%EB%93%A4</guid>
            <pubDate>Sat, 06 Mar 2021 09:22:21 GMT</pubDate>
            <description><![CDATA[<p>Fragment간 데이터를 전달하는 방법은 여러가지가 있다.</p>
<blockquote>
<ol>
<li>bundle과 FragmentManager로 전달</li>
<li>Fragment Result API를 사용하여 Data 전달</li>
<li>Fragment간 공통의 ViewModel(ex. HostActivity의 ViewModel)로 전달</li>
<li>Jetpack의 Navigation에서 제공하는 safe-args로 전달</li>
</ol>
</blockquote>
<p>이러한 방법들이 있다.</p>
<p>1번 방법은 구글에 &#39;Fragment간 Data전달&#39;을 검색하면 옛날 방식의 예시 코드들이 많이 존재한다.</p>
<p>그리고 구글 공식 문서에서는 위 세 가지 방법(2~4)을 확인할 수 있다.</p>
<p><a href="https://developer.android.com/guide/fragments/communicate">Google Docs1 (Communicating with fragments) 2, 3번 방법</a>
<a href="https://developer.android.com/guide/navigation/navigation-pass-data">Google Docs2 (Pass data between destinations) 4번 방법</a></p>
<p>위 방법들에 대해 직접 프로젝트를 만들어서 구현해보고 소개한다.</p>
<br>

<h3 id="1-fragmentmanager에-bundle로-data를-담아-전달">1. FragmentManager에 Bundle로 Data를 담아 전달</h3>
<p><strong>전송하려는 Fragment class</strong></p>
<pre><code>//PassBundleFragment는 본인이 전달하고자 하는 Fragment class
 val bundle = Bundle()
 bundle.putString(&quot;key&quot;, &quot;value&quot;)

val passBundleBFragment = PassBundleBFragment()
passBundleBFragment.arguments = bundle parentFragmentManager.beginTransaction()
    .replace(R.id.fragment_container_bundle, PassBundleFragment())
    .commit()
</code></pre><p><strong>받을 Fragment class</strong></p>
<pre><code>override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
    var result = arguments?.getString(&quot;key&quot;)

        return inflater.inflate(R.layout.fragment_pass_b, container, false)
    }</code></pre><p>로 data를 전달할 수 있다.</p>
<br>

<h3 id="2-fragment-result-api를-사용하여-data-전달">2. Fragment Result API를 사용하여 Data 전달</h3>
<img src="https://developer.android.com/images/guide/fragments/fragment-a-to-b.png" width="70%" height="70%">
2020년 androidx.fragment:fragment-ktx:1.3.0-alpha 버전에 도입된 기능이다.

<p>작성일 기준 androidx.fragment:fragment-ktx:1.3.0 안정화버전이 나온 상태. 의존성 추가 후 사용할 수 있다.</p>
<pre><code>//(AppModule)
dependencies {
    ...
    implementation &quot;androidx.fragment:fragment-ktx:1.3.0&quot;
}</code></pre><p><strong>전송하려는 Fragment class</strong></p>
<pre><code>//PassBundleFragment는 본인이 전달하고자 하는 Fragment class
//fragment_container_bundle은 본인의 Fragment가 담겨있는 Container
button.setOnClickListener {
    val result = &quot;result&quot;
    setFragmentResult(&quot;requestKey&quot;, bundleOf(&quot;bundleKey&quot; to result))
    parentFragmentManager.beginTransaction()
       .replace(R.id.fragment_container_bundle, PassBundleFragment())
       .commit()
}</code></pre><p><strong>받을 Fragment class</strong></p>
<pre><code>setFragmentResultListener(&quot;requestKey&quot;) { requestKey, bundle -&gt;
        val result = bundle.getString(&quot;bundleKey&quot;)
        // Do something with the result
    }</code></pre><p>&quot;requestKey&quot;는 사용하는 Fragment에서 어떤 listener에게 데이터를 전달할 지 결정하기 위한 식별자로 사용된다.
다른 fragment에서 같은 &quot;requestKey&quot;로 setFragmentResultListener를 적용할 경우 마지막에 적용한 listener가 호출된다.</p>
<br>

<h3 id="3-viewmodel을-이용한-data-전달">3. ViewModel을 이용한 Data 전달</h3>
<p>하나의 Activity에서 container로 여러 Fragment를 이동하는 경우 사용할 수 있다.
Fragment가 Activity 하위에 위치하기 때문에 사용할 수 있는 아이디어!</p>
<p>Activity를 공유하는 여러 Fragment들은 Activity의 메모리를 공유할 수 있고 AAC의 ViewModel은 Activity의 lifecycle보다 오래 살아있는 것이 보장되기 때문에 안전하게 공통의 Activity의 ViewModel을 사용하여 데이터 전달이 가능하다.</p>
<img src="https://images.velog.io/images/sysout-achieve/post/38b2703e-7642-41e4-8b92-3951ea4dbb1c/image.png" width="70%" height="70%">
구조를 그려보면 더 직관적인 이해가 가능하다. 공통의 메모리 데이터를 이용하여 서로의 데이터를 전달하는 방법. 

<p><strong>Code</strong></p>
<pre><code>class ViewModelPassActivity : AppCompatActivity() {
    val viewModel : PassingViewModel by viewModels()
}

class PassAFragment : Fragment() {
    val viewModel : PassingViewModel by activityViewModels()
}

class PassBFragment : Fragment() {
    val viewModel: PassingViewModel by activityViewModels()
}</code></pre><p>Activity에 Fragment들이 같은 데이터를 바라볼 수 있게 ViewModel 객체를 만들고 그 객체에 데이터를 전달하여 간단하게 Fragment 통신을 달성할 수 있다.</p>
<blockquote>
<p>*<em>IPC기법 중 Shared Memory와 비슷한 아이디어라는 생각에 도달 *</em></p>
<p>실제 프로젝트에서는 당연히 구글에서 권장하는 방법을 사용하겠지만 IPC기법까지 생각이 도달하면 더 많은 종류의 Data전달 방법이 떠오를 수 있다. (IPC의 여러방법들) </p>
<p>IPC 기법들 : </p>
</blockquote>
<ul>
<li>File 사용</li>
<li>Message Queue</li>
<li><strong>Shared Memory</strong> (ViewModel을 이용한 데이터 전달 방법을 보고 비슷하다고 느낌)</li>
<li>Pipe</li>
<li>...</li>
</ul>
<br>

<h3 id="4-jetpack의-navigation에서-제공하는-safe-args로-전달">4. Jetpack의 Navigation에서 제공하는 safe-args로 전달</h3>
<p>이 방법은 Jetpack의 Navigation을 사용하는 프로젝트에서 적용하기 좋은 방법이다.</p>
<p>장점으로 navigation.xml에서 화면전환에 필요한 액션과 data를 정의할 수 있고, defaultValue도 정의가 가능하다. 또한, 전달하는 데이터 타입이 받는 곳의 데이터 타입과 다를 경우 compile 에러를 일으켜 의도하지 않은 상황이 발생할 확률을 줄여줬다.</p>
<p>간결하고 안정적인 처리의 장점이 있지만 다른 Data전달 방식보다 환경세팅하는 것이 많고 알아야 할 기능들이 많다. 단지 Fragment Data전달을 위해 Navigation을 쓰는 것은 비효율적이지만 전체 프로젝트에 Navigation을 적용했을 때 얻을 수 있는 이점이 많으니 참고하고 적용할 지 결정해야겠다.</p>
<p>여기서는 세팅을 제외하고 어떤 식으로 전달하는지 설명한다.</p>
<pre><code>//Navigation.xml에서 이동할 화면에 대한 정의(action)와 전달할 데이터(argument)를 정의한다.
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;navigation xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    android:id=&quot;@+id/nav_graph&quot;
    app:startDestination=&quot;@id/passArgsAFragment&quot;&gt;

    &lt;fragment
        android:id=&quot;@+id/passArgsAFragment&quot;
        android:name=&quot;com.gunt.fragmentdatapassexample.pass.passargs.PassArgsAFragment&quot;
        android:label=&quot;PassArgsAFragment&quot; &gt;
        &lt;action
            android:id=&quot;@+id/passAToB&quot;
            app:destination=&quot;@id/passArgsBFragment&quot;
            app:launchSingleTop=&quot;true&quot; /&gt;
        &lt;argument
            android:name=&quot;argsString&quot;
            app:argType=&quot;string&quot;
            android:defaultValue=&quot;&quot;/&gt;
    &lt;/fragment&gt;
    &lt;fragment
        android:id=&quot;@+id/passArgsBFragment&quot;
        android:name=&quot;com.gunt.fragmentdatapassexample.pass.passargs.PassArgsBFragment&quot;
        android:label=&quot;PassArgsBFragment&quot; &gt;
        &lt;action
            android:id=&quot;@+id/passBToA&quot;
            app:destination=&quot;@id/passArgsAFragment&quot;
            app:launchSingleTop=&quot;true&quot;
            app:popUpTo=&quot;@id/nav_graph&quot; /&gt;
        &lt;argument
            android:name=&quot;argsString&quot;
            app:argType=&quot;string&quot;
            android:defaultValue=&quot;&quot;/&gt;
    &lt;/fragment&gt;
&lt;/navigation&gt;</code></pre><p><strong>전송하려는 Fragment class</strong></p>
<pre><code>//액션에 본인이 보내려는 데이터를 담아 액션을 수행한다.
binding.btnSend.setOnClickListener {
            val action = PassArgsAFragmentDirections.passAToB(binding.etText.text.toString())
            findNavController().navigate(action)
        }
</code></pre><p><strong>받을 Fragment class</strong></p>
<pre><code> val args: PassArgsAFragmentArgs by navArgs()
 binding.textView.text = args.argsString</code></pre><p>위 방식으로 Fragment간 데이터 전달이 가능하다.</p>
<p><a href="https://github.com/sysout-achieve/FragmentDataPassExample">전체 예제 소스 : https://github.com/sysout-achieve/FragmentDataPassExample</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] DI Framework 선택지(Dagger2, Koin, Hilt)]]></title>
            <link>https://velog.io/@sysout-achieve/Android-DI-Framework-%EC%84%A0%ED%83%9D%EC%A7%80Dagger2-Koin-Hilt</link>
            <guid>https://velog.io/@sysout-achieve/Android-DI-Framework-%EC%84%A0%ED%83%9D%EC%A7%80Dagger2-Koin-Hilt</guid>
            <pubDate>Fri, 05 Mar 2021 06:46:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sysout-achieve/post/c12e11c2-7147-4db3-b229-cb22dec56a13/image.png" alt=""><strong>잘못된 정보나 수정할 사항 or 피드백은 언제든 환영합니다.</strong></p>
<br>

<p>안드로이드의 Dependency Injection과 관련하여 핫한 framework는 </p>
<blockquote>
<p>Dagger2
Koin
Hilt</p>
</blockquote>
<p>를 들 수 있다.</p>
<h4 id="그럼-이-세-가지의-차이가-무엇이고-어떤-것을-쓰는-것이-좋을까">그럼 이 세 가지의 차이가 무엇이고 어떤 것을 쓰는 것이 좋을까?</h4>
<p>각각의 특징과 장단점을 파악해보고 본인의 사용하려는 프로젝트에 대입해서 읽어보는 것이 좋을 것 같다. 의존성 주입 개념은 다른 글에서도 쉽게 찾을 수 있으니 의존성 주입 개념에 대해서는 설명하지 않는다.</p>
<h3 id="1-dagger2">1. Dagger2</h3>
<p>가장 많이 보이는 DI Framework라 생각된다.</p>
<p>사용하려는 프로젝트에 Java와 Kotlin이 함께 작성되어있는 프로젝트에서 DI를 적용하려면 Koin이라는 선택지가 제외된다. (2.Koin에서 추가 설명할거임) </p>
<p>Dagger2 적용을 위해 환경을 세팅해야하는 과정과 원활한 적용에 필요한 러닝커브가 크다.
러닝커브에 사용되는 비용이 상대적으로 저렴한 (이미 알거나 금방 배울 수 있는 수준의 개발 역량을 가진)사람들이 쓰기에 알맞은 Framework이지 않을까 한다. </p>
<p>컴파일 시 애노테이션을 읽고 scope에 맞는 DI container와 주입할 class파일을 generate하여 의존성을 주입한다. 당연히 compile에 필요한 시간이 늘어나게 되고, 문제가 있을 경우 Compile 시점에서 에러를 발생시킨다. 때문에 빌드가 완료된 파일은 어느 정도의 의존성 주입에 대해서는 안정성이 보장된다고 생각한다. </p>
<h3 id="2-koin">2. Koin</h3>
<p>Koin은 Kotlin DSL(Domain Specific Language, 도메인 특화 언어)로 만들어졌다. </p>
<p><em><strong>Koin의 특징이자 장점이자 단점은 Kotlin DSL?</strong></em></p>
<p>Kotlin 개발 환경에 쉽게 적용할 수 있는 경량화된 DI 프레임워크. Kotlin 개발 환경에서 도입하기에 가장 가볍고 빠르게 적용할 방법이라 생각한다. 
Dagger2에 비해 사용법이 쉽고, DSL을 활용하여 runtime에 의존성을 주입한다.
이는 runtime error가 발생할 수 있고 compile시점에서 오류 확인이 어려울 수 있다.</p>
<p>대신 test를 잘 작성하여 애플리케이션의 퀄리티를 보장하는 것이 개선할 수 있는 방법이 될 수 있다.</p>
<p>그러나 Framework에서 compile error를 확인하는 것과 unit Test를 통해 error를 확인하는 것. 어느 것이 배포 전 오류를 잘 잡아낼 수 있을까? 라고 한다면 Dagger2 &gt;= Koin이 되지 않을까 한다. 아무래도 unit Test 또한 사람이 하는 것이기 때문에 어쩔 수 없을 것이다.</p>
<blockquote>
<p><strong>Dagger2와 Koin의 에러시점이 다른 이유</strong></p>
<p>Dagger2 동작 방식</p>
</blockquote>
<pre><code>Build -&gt; (DI container, DI 객체 생성 및 주입 관련 class) generate -&gt; run</code></pre><blockquote>
<p>Koin의 동작 방식</p>
</blockquote>
<pre><code>Build -&gt; run(DI container, DI 객체 생성 및 주입)</code></pre><blockquote>
<p><em>&#39;동작 방식의 차이&#39; 때문에 에러 시점이 다르다.</em></p>
</blockquote>
<h3 id="3-hilt">3. Hilt</h3>
<p>Dagger를 기반으로 만들어진 DI Framework로 Dagger2와 비슷한 동작방식을 가지고 있다. </p>
<p>Hilt를 적용할 때 가장 걱정하는 부분은 안정성 보장과 현재 개발 환경과의 충돌이 될 수 있다. </p>
<p>Beta버전이 나온 것도 최근이고(얼마전까지는 2.32-alpha버전이었다.) 공식 문서에도 Android Studio 4.0 버전 이상부터 사용가능하다고 나와있다.
보수적이고 안정적인 서비스를 유지하는 프로젝트에는 사용이 어렵지만, 개인 프로젝트 단계에서는 써볼만하다.</p>
<p>현재(작성일 기준) 2.33-Beta버전까지 출시됐고, 계속해서 업그레이드된 버전들이 나올 것이라 예상된다. <strong>구글 문서도 빠르게 업데이트 중이고, 낮은 러닝커브와 Dagger의 장점을 모아</strong> 구글에서 만들어지고 있는 Framework인만큼 앞으로도 계속해서 발전하고 자주 사용될 것이라 예상한다.</p>
<h2 id="결론">결론</h2>
<table>
<thead>
<tr>
<th></th>
<th>Dagger2</th>
<th>Koin</th>
<th>Hilt</th>
</tr>
</thead>
<tbody><tr>
<td>러닝커브</td>
<td>높음</td>
<td>낮음</td>
<td>낮음</td>
</tr>
<tr>
<td>Java</td>
<td>O</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>Kotlin</td>
<td>O</td>
<td>O</td>
<td>O</td>
</tr>
<tr>
<td>에러 검출 시점</td>
<td>Compile Time</td>
<td>Runtime</td>
<td>Compile Time</td>
</tr>
<tr>
<td>실제 프로젝트 검증</td>
<td>많은 앱에서 검증됨</td>
<td>많은 앱에서 검증됨</td>
<td>beta버전(조금씩 보임)</td>
</tr>
</tbody></table>
<p>개발자는 완벽한 프로그램을 만들어내는 것이 목적이 아니다.</p>
<p>비즈니스를 위한 제품을 개발하는 사람이라 생각한다. </p>
<p>좋은 퀄리티의 Product를 지향하지만 가장 우선 시 되어야 할 부분은 비즈니스에 맞는 기술을 선택하는 것이다. 어떤 기술을 선택함에 있어서 현 상황과 비용을 고려하여 합리적인 선택을 하는 것도 개발자의 역량이라 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] MVVM 아키텍처 따라하기]]></title>
            <link>https://velog.io/@sysout-achieve/Android-MVVM-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@sysout-achieve/Android-MVVM-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 03 Mar 2021 12:49:25 GMT</pubDate>
            <description><![CDATA[<p><strong>MVVM 아키텍처를 이용하여 작은 Application Project를 만들고, 사용하면서 느낀점을 정리합니다.</strong>
<strong>잘못된 정보나 수정할 사항 or 피드백은 언제든 환영합니다.</strong></p>
<h3 id="mvvm">MVVM</h3>
<p>안드로이드의 MVVM아키텍처의 처리과정
<img src="https://images.velog.io/images/sysout-achieve/post/f9aff488-cea2-4f13-948b-61303bf5e7e8/mvvm%E1%84%91%E1%85%B3%E1%84%85%E1%85%A9%E1%84%89%E1%85%A6%E1%84%89%E1%85%B3.png" width="80%" height="80%"></img></p>
<br>

<p>Android Developer에서 AAC와 함께 소개되었던 MVVM아키텍처 
 :  Model, View, ViewModel이 역할을 가진 layer로 나뉘어져 각자의 역할에 맞는 기능을 수행합니다. </p>
<p>장점 :</p>
<ul>
<li>layer를 나눠서 각 계층이 역할을 명확하게 가져갑니다. (각 계층에 구성된 객체가 하나의 책임을 가짐 - 단일책임원칙)</li>
<li>미리 구조화시켜놓은 계층은 통일성 있는 코드를 만들 수 있습니다. (협업에 효율적)</li>
<li>ViewModel에 View 관련(android.* 패키지) 클래스를 import하지 않습니다. Model과 View의 의존성이 약해졌다는 증거라 생각합니다.</li>
<li>jetpack의 AAC에 포함된 library들이 MVVM에서 특히 사용 및 관리를 효과적으로 할 수 있게 설계되어졌다고 느꼈습니다. (구글에서 더 좋은 기능들을 개발 및 지원할 가능성이 큼)</li>
<li>분리된 역할에 따라 테스트가 용이해집니다. (Testable한 코드 지향)</li>
</ul>
<pre><code>  ex) DataBinding -&gt; xml에 데이터를 바인딩하여 데이터 객체와 뷰컴포넌트간 데이터 전달이 가능 
            : viewmodel에 ViewComponent 코드 차단 가능하게 함
      LiveDate -&gt; ViewComponent에서 데이터 감지 
            : 바인딩한 viewmodel의 데이터를 간접적으로 사용 가능하게하여 데이터와 뷰의 분리 실현 </code></pre><p>MVP와 차이점
: MVP패턴의 경우 View와 Model간의 상호작용을 Presenter를 매개로 동작합니다.</p>
<ul>
<li>Presenter와 View간의 loose coupling을 위한 interface구성</li>
<li>MVP: Presenter와 View가 서로를 바라보고 기능 수행 vs MVVM: ViewModel은 데이터 변형과 비즈니스 로직에 대한 처리 + View는 화면의 IO만 관리하고 ViewModel을 관찰하며 결과 수행</li>
</ul>
<br>


<h4 id="-개인적인-생각-br">( 개인적인 생각 )<br></h4>
<blockquote>
<p>그러나 View에서 입출력에 대한 내용을 Presenter에게 전달해야하는 과정에서 결합도가 올라감<br>
MVP의 경우 Presenter라는 매개를 거쳐야만 하는 것이 단점으로 작용할 수 있음 <br></p>
</blockquote>
<pre><code>  ex) 텍스트입력 후 입력한 데이터와 함께 화면전환 
   ( view에서 input받은 데이터 -&gt; presenter로 데이터 전달하여 model 데이터 변경 -&gt; 변경된 데이터 화면 전달 -&gt; Intent or Bundle에 담아 화면 전환 ) 필요없는 과정이 추가될 수 있음</code></pre><blockquote>
<p>MVP와 MVVM 모두 애플리케이션을 만들기 위해 layer를 만들고 각 계층에 맞는 코드를 작성하게 하여 코드의 통일성과 이슈트래킹을 효과적으로 할 수 있는 디자인패턴들이라 생각함 <br>
복잡도가 높거나 높아질 가능성이 큰 프로젝트일수록 각 객체들간의 역할이 명확해야함 (부수효과를 막기 위해)<br>
숙련도가 떨어져도 디자인 패턴에 맞추어서 작업을 했을 때 가장 명확한 책임을 가지게 만드는 패턴이 효과적인 패턴이라 생각함<br>
패턴에 숙련되는 과정은 mvvm이 더 어렵다고 느껴짐, 대신 패턴이 지향하는 목표는 mvvm이 더 적합하다고 생각<br></p>
<ul>
<li>디자인패턴에 집중하는 것이 아니라 더 객체지향적으로 코딩하기 위해서 어떻게 사용하는 것이 더 적합할 것인지를 고민할 것
(개인프로젝트에서는 MVVM 활용 예정) </li>
</ul>
</blockquote>
<p><a href="https://github.com/sysout-achieve/SearchImage">Sample Project : https://github.com/sysout-achieve/SearchImage </a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] 성장에 답답한 안드로이드 개발자가 시도했던 것들]]></title>
            <link>https://velog.io/@sysout-achieve/Android-%EC%84%B1%EC%9E%A5%EC%97%90-%EB%8B%B5%EB%8B%B5%ED%95%9C-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%ED%96%88%EB%8D%98-%EC%9E%91%EC%9D%80-%EB%B3%80%ED%99%94</link>
            <guid>https://velog.io/@sysout-achieve/Android-%EC%84%B1%EC%9E%A5%EC%97%90-%EB%8B%B5%EB%8B%B5%ED%95%9C-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EA%B0%9C%EB%B0%9C%EC%9E%90%EA%B0%80-%ED%96%88%EB%8D%98-%EC%9E%91%EC%9D%80-%EB%B3%80%ED%99%94</guid>
            <pubDate>Tue, 02 Mar 2021 11:47:42 GMT</pubDate>
            <description><![CDATA[<p><strong>내가 시도했던 방법과 고민들을 적으며 피드백을 받고 함께 성장하고자 글을 작성합니다.</strong></p>
<h2 id="축복받은-개발자">축복받은 개발자</h2>
<p>회사 프로젝트 또는 개인 프로젝트를 하며 처음부터 잘 짜여진 아키텍처를 이해하고 자연스럽게 구조에 맞춰 적용하고 피드백 받는 것. 성장을 위해 잘 닦인 도로를 달리며 꾸준한 성장을 도모할 수 있는 것.
개발자로써 축복에 가까운 일이라 할 수 있을 것 같다. (좋겠다. <del>물론 그 길을 달리기 위한 과정과 노력이 있었기에 가능했고 대단하다 생각하지만</del> 부럽다.)</p>
<br>

<h2 id="그렇지-못한-개발자">그렇지 못한 개발자</h2>
<p>그러나 프로젝트를 개발하면서 아키텍처의 존재도 모르고 기능구현에만 급급하며 레거시 코드 위에 다른 기능들을 추가하며 언제 무너질지 모르는 젠가를 계속하는 개발자들도 있다. 
이런 개발자들 중 욕심이 있는 개발자는 노력을 각오하지만 방향을 잡기가 쉽지 않다. (내가 그랬다.)
<br></p>
<p>현실은 기능구현에 집중해야하는 일정과 새로운 기술, 아키텍처를 도입하는 것 자체가 부정적이고 어려운 환경에 놓여있는 사람들이 많을 것이라 생각한다. 어떻게 시도해야하는지, 어떻게하면 코드퀄리티가 좋아지는지, 좋은 코드가 뭔지 조차 애매한 사람도 있을 것이다.</p>
<br>


<h3 id="그럼-포기해야하나">그럼 포기해야하나...</h3>
<p>본인의 성장을 고민하고 포기하고 싶지 않은 사람들이 이 글을 읽고 있을 것이라 생각한다. 나도 고민했고 계속해서 고민하고 있다. 내가했던 시도들과 시행착오들을 계속해서 작성해나갈 예정이고 기술에 대해 이해한 것들 또한 적어나가려고 한다. 그럼 어떤 활동을 통해 누군가에게 보여줄 때 덜 민망한 코드 퀄리티를 만들 수 있을까? </p>
<h4 id="시도해보자">시도해보자!!</h4>
<p>나는 이렇게 시작했다.</p>
<ol>
<li>지금 하고 있는 프로젝트의 if지옥 줄이기
<img src="https://images.velog.io/images/sysout-achieve/post/05134c53-cb79-454d-bf0f-f4b9f17da32f/ifHell.png" alt=""></li>
</ol>
<p>if else로 엄청난 일들이 일어나고 있는 프로젝트가 꽤 많이 있을 것이라 생각한다. 심지어 모든 로직처리가 if else로 처리되는 경우도 있을 것이다. 상태에 따른 로직을 일부 클래스로 나누거나 위임할 수도 있고, enum을 이용하여 상태별 정의를 만들어 둘 수도 있다.
다양한 if 줄이기위한 방법이 있으니 찾아보고 자신이 하고있는 프로젝트에 적용해보자. </p>
<p>이를 통해 </p>
<ul>
<li>객체지향이 갖는 의미 </li>
<li>다형성, 확장성을 고려한 코드가 기능구현에 어떻게 녹아드는지</li>
<li>자주 만나게되는 기능구현의 장애물들이 생각보다 쉽게 처리가 되는 과정</li>
</ul>
<p>을 느껴볼 수 있고 refactoring의 의미를 조금씩 알게 될 것이다.</p>
<br> 

<ol start="2">
<li>아키텍처 일단 적용해보기</li>
</ol>
<p>MVP, MVVM 등 많은 아키텍처가 있고 뭐가 더 자신의 프로젝트에 맞는지 어떻게 적용하는게 좋은지 고민하다 관두는 경우가 많을 것이다. 나도 그 고민속에서 꽤 많은 시간을 소비했다. 개인적으로 아주 작은 단위(뷰 나열과 버튼 클릭만 있는 단순한 화면)의 작업에 적용해보면서 시작했다. 
일부분만 적용해도 되는가? 일단 yes. 프로젝트 기준 엄청난 변화가 일어나지는 않을 것이다. 그러나 문제없이 돌아가는 코드, 분리되며 activity, fragment의 코드가 줄어드는 것을 눈으로 확인할 수 있다. 또한 각 역할에 해당하는 클래스를 구성하면서 단일책임원칙을 상기시켜볼 수도 있다. 
시도하지 않았을 때는 누군가한테 배워야 할 수 있다 생각했던 것들이 하나씩 적용되는 경험을 할 수 있을 것이다.</p>
<br>

<ol start="3">
<li>깃허브 오픈소스, 구글 codelab 따라해보기</li>
</ol>
<p>깃허브의 open repository에 아키텍처 관련 좋은 퀄리티의 프로젝트들이 정말 많이 존재한다. 그들의 코드를 따라 읽어보며 적용해보는 것도 좋은 과정이라 생각한다. 구글의 codelab에서 jetpack의 aac를 들어가 하나씩 배워나가는 것은 안드로이드 개발자라면 꼭 해봐야하는 과정이라 생각한다.
(codelab에서 자신이 배우고자 하는 기술 키워드를 치면 아마 거의 다 나올것이다.) (<em>ex. codelab Mvvm 등</em>)</p>
<br> 

<p>사수가 있고 그가 정말 노력하며 얻은 결과물들을 전수해주고 그 과정을 통해 좋은 개발자로 성장한다면 참 행복한 상황일 수 있다. 그러나 무엇을 배워야하는지 모르고 성장하는 방법도 모르는데 노력하는 사람들이 하는 삽질이 안타깝다. *<em>이런 안타까운 상황에 있는 개발자가 본인의 실력 향상을 통한 업무능력 증진이 아닌 업무에 투입하는 노력을 늘려 결과물을 뽑아내는 상황이라면 조금 더 영리하게 본인의 커리어를 쌓아가는 과정을 밟았으면 좋겠다. *</em></p>
]]></description>
        </item>
    </channel>
</rss>