<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ha_jni.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 13 Sep 2021 06:19:30 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ha_jni.log</title>
            <url>https://images.velog.io/images/ha_jni/profile/85dbc739-d9c5-4c04-9855-db52b20145c0/90BF8FDC-8EB1-487C-A480-F911CA9CB3A8.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ha_jni.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ha_jni" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[RX]]></title>
            <link>https://velog.io/@ha_jni/RX-cm78l3xo</link>
            <guid>https://velog.io/@ha_jni/RX-cm78l3xo</guid>
            <pubDate>Mon, 13 Sep 2021 06:19:30 GMT</pubDate>
            <description><![CDATA[<h2 id="우선-reactivex가-무엇인가">우선 ReactiveX가 무엇인가?</h2>
<p>ReactiveX의 풀네임은 &#39;Reactive eXtensions&#39;</p>
<p><strong>reactive : 반응을 보이는 , extension: (세력, 영향력, 혜택 등의) 확대</strong>
ReactiveX가 반응을 확장한다는 의미를 가지고 있다는 것을 알 수 있다.</p>
<p>Rx = ReactiveX 줄임말
참조 : <a href="http://reactivex.io/">reactivex</a></p>
<p>참조 링크를 들어가면 보이는 이미지
<img src="https://images.velog.io/images/ha_jni/post/9493d98f-1bb3-4fd3-98de-6165bfc5b2b4/image.png" alt="">
Marble Diagram(마블 다이어그램)이다.
마블다이어그램은 RX의 기능을 사용할 때, 그 기능의 이해에 도움이 되기 위한 도표라고 생각하면될듯!!
처음에 그림을 잘 보지 않았는데..ㅠ 그림을 보며 이해하는게 훨씬 이해가 잘 되었다. 마블다이어그램은 밑에서 다시 정리하겠다.</p>
<h3 id="여튼-참조링크를-보면">여튼, 참조링크를 보면</h3>
<p><img src="https://images.velog.io/images/ha_jni/post/9a6b355e-d786-4a66-a288-475b0fb5a64b/image.png" alt=""></p>
<pre><code>두 목록이 보이는데, 오른쪽에 LANGUAGES의 밑 여러가지 RX가 붙은 것들이 있다.
  → 바로 Rx를 어느 플랫폼에서나 쓸 수 있게 해주는 것.
Rx는 각 플랫폼마다 지원하는 시리즈, 정확히는 라이브러리가 존재하기 때문.

iOS 플랫폼에서 Rx를 사용할 수 있게 해주는 RxSwift,
Android 플랫폼에서 Rx를 사용할 수 있게 해주는 RxJava</code></pre><hr>
<pre><code>다음 왼쪽의 DOCUMENTATION
밑에 Observable, Operators, Single, Subject, Scheduler 이 다섯가지가 Rx의 주요 기능이다.
이중에서도 가장 중요한 것은 바로 Observable인데.
그럼 Observable과 옵저버블 스트림과 연관이 있나?
 = OK</code></pre><p><strong>예를들어보자.</strong></p>
<pre><code>**상황**
  1.오직 한 방향으로만 흐르는 강(stream)이 있다.
  2. 물고기(value)는 강(stream)의 흐름방향으로 흘러간다.
  3. 강에는 가끔 쓰레기(value)도 흘러간다.
**목표**
  &#39;rx&#39;라는 강에서 물고기를 건져, 회를 뜬다음, 팔아야 한다.
**내가 한 일**
  강에서 자동으로 물고기만 건져(filter) 회로 변환(map)하는 &#39;A&#39;시스템을 만들었다.
  이 시스템은 사용자가 버튼을 누를 시(subscribe) 가동된다.</code></pre><blockquote>
<p>스트림은 주로 강으로 비유된다.
상식적으로 강은 한 방향으로만 흐르며, 우리가 무언가를 하지 않는 이상 아무 일도 생기지 않는다
이것이 강과 스트림을 비유하는 이유이다.
어떠한 이벤트 스트림이 있다고 해도, 그것만으로는 아무 일도 일어나지 않는다.</p>
</blockquote>
<hr>
<p>Observable도 강이라고 생각하시면 된다.
하지만
Observable은 강이지만, 특정한 강을 지칭할 때 사용한다고 생각하시면 된다.
다 같은 강이지만 한강, 낙동강처럼 특정한 강이 있다.
스트림은 포괄적인 느낌이며, Observable은 스트림이지만, 특정 스트림을 의미한다고 생각하면 된다.</p>
<p>&#39;A&#39;시스템을 가동하기 위해서는 버튼을 눌러야 한다.
이 버튼을 누르는 행동인 subscribe 즉, 구독을 함으로써 시스템이 가동이된다.
하지만 우리는 그냥 강이 아닌 &#39;rx&#39;라는 강에서 가동이 되야한다.</p>
<p>그래서 우리는 &#39;A&#39;시스템을 &#39;rx&#39;강에서 가동하기 위해, &#39;rx&#39;라는 Observable을 구독해야 한다.
강은 수없이 많다. 그 수많은 강 중에서 특정한 강(Observable)을 구독해야 시스템은 그 강에서 가동될 것이다.
<u>그리고 Observable을 구독하지 않는 이상 아무 변화가 일어나지 않을 것이다.</u></p>
<p>Operators는 스트림에 흐르는 값(value)을 가공하는 장치다.
위 퀘스트에서 우리는 물고기만 건져 회로 변환한다고 했다.
장치(Operators)의 기능 중 물고기만을 건지게 해 주는 건 filter, 회로 변환하는 건 map.
Operators안에 filter와 map과 같은 기능들이 있다고 생각하면 된다.</p>
<p>코드로 보면</p>
<p><img src="https://images.velog.io/images/ha_jni/post/e278c842-e59b-44c8-befc-f32a3f4006b2/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Observable이란.]]></title>
            <link>https://velog.io/@ha_jni/Observable%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@ha_jni/Observable%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Fri, 10 Sep 2021 10:27:21 GMT</pubDate>
            <description><![CDATA[<p>Observable</p>
<p>Observable은 데이터의 흐름을 관장하는 클래스로써 RxJava에서는 Observable이 절대 빠질 수 없다.</p>
<p>Observable은 데이터의 흐름에 맞게 알림을 보내 구독자가 데이터 처리를 할 수 있도록 만든다.
Observable은 세 가지의 알림을 구독자에게 전달하며 그 종류는 아래와 같다.</p>
<p>RxJava에서는 Observable을 구독하는 Observer가 존재하고, Observable이 순차적으로 발행하는 데이터에 대해서 반응한다. Observable은 다음의 3가지 이벤트를 사용하여 동작한다.</p>
<ul>
<li>onNext() : 하나의 소스 Observable에서 Observer까지 한 번에 하나씩 순차적으로 데이터를 발행한다.</li>
<li>onComplete() : 데이터 발행이 끝났음을 알리는 완료 이벤트를     Observer에 전달하여 onNext()를 더 호출하지 않음을 나타낸다.</li>
<li>onError() : 오류가 발생했음을 Observer에 전달한다.
위 이벤트들은 Emitter라는 인터페이스에 의해 선언된다.</li>
</ul>
<p>Emit [imít] 1.방출하다 2.내뿜다 3.발산하다</p>
<p>그리고 데이터나 오류 내용을 발행할 때 null은 발행할 수 없다.
<span style="color:slateblue">색을 바꿀 텍스트 블라블라</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MVVM 패턴과 AAC]]></title>
            <link>https://velog.io/@ha_jni/MVVM-%ED%8C%A8%ED%84%B4%EA%B3%BC-AAC</link>
            <guid>https://velog.io/@ha_jni/MVVM-%ED%8C%A8%ED%84%B4%EA%B3%BC-AAC</guid>
            <pubDate>Fri, 20 Aug 2021 05:23:32 GMT</pubDate>
            <description><![CDATA[<p>Android 앱 개발을 하다보면, Activity 클래스에 모든 앱 동작 코드를 전부 집어넣는 경우가 많았다.
물론 동작에 있어 큰 문제는 없지만 체계적인 구조가 전혀 없어 추후에 유지 보수 또는 어떠한 곳에서 오류가 터지면 어딘지 찾는 것 또한 일이였는데..</p>
<p>실제로 회사에 다니며 작은 프로젝트를 맡아 진행을 했는데, 위의 방식으로 개발을 하다보면, 기능을 바꾸거나 추가할 때 어떤 부분을 수정해야 할지, 다 만들고 난 후 리팩토링 하자는 핑계로 넘어간적이 많았다.</p>
<blockquote>
<p>이러한 코드가 계속쌓이면 <strong>스파게티 코드</strong> 가 된다.</p>
</blockquote>
<h2 id="mvc와-mvvm-차이점-🧐">MVC와 MVVM 차이점 🧐</h2>
<p><img src="https://images.velog.io/images/ha_jni/post/7a639350-fbf3-450f-aeda-8bede3a731e0/image.png" alt=""></p>
<h3 id="mvcmodel--view--controller">MVC(Model / View / Controller)</h3>
<p><strong>Model</strong>
Model class로 비즈니스 로직에서의 알고리즘, 데이터 등의 기능 처리함</p>
<p><strong>View</strong>
acticity_main.cml화면 부분 UI역할 담당</p>
<p><strong>Controller</strong>
Activity부분에서 View에게는 화면 업데이트, Model에게는 데이터 갱신을 알린다.
View와 Model을 연결해주며 비즈니스 로직을 처리하기위해 많은일을 해야함</p>
<p>여튼 기존 만들어왔던 MVC패턴에 따라 Activity에 모든 코드를 다 넣으니 단점이 생긴다.</p>
<ul>
<li>앱 동작이 많아질 수록 <strong>Activity 자체가 무거워진다.</strong></li>
<li><strong>스파게티 코드가 될 가능성이 높음.</strong>
코드 복사/붙여넣기가 많아지게 되면서 코드 분리조차 되지않으면 코드가 아주 제대로 꼬여버립니다. 그렇기에 복잡도는 증가합니다. 다만, 이는 설계 단계에서 제대로해서 분리를 잘하면 어느정도 해소는 된다.</li>
<li><strong>Model과 View사이에 의존성 발생</strong>함. (서로간의 의존성 완전히 없앨 수 없음)
즉, View의 UI 갱신을 위해 Model을 직/간접적으로 참조하므로 앱 자체가 커지고 로직이 복잡해질수록 유지보수가 힘들어진다.</li>
</ul>
<blockquote>
<p>💡 왜?  MVC패턴의 동작이 아래와 같기 때문</p>
</blockquote>
<p><img src="https://images.velog.io/images/ha_jni/post/80203933-1486-4fe8-b0d9-7a0f770dc015/image.png" alt=""></p>
<blockquote>
<ol>
<li>Controller 가 사용자 동작을 받아들임 (텍스트 입력, 버튼 터치 등)</li>
<li>Controller 가 사용자의 동작에 따른 Model 업데이트를 요청함</li>
<li>Controller 가 Model 을 나타낼 View 를 선택함</li>
<li>View 는 Model 을 참조하여 UI 를 업데이트함</li>
</ol>
</blockquote>
<p>자연스레 V<strong>iew 와 Model 간의 의존성이 높아질 수 밖에</strong> 없다.
또한 <strong>Controller</strong> (Activity) 가 <strong>Model 과 View 사이에서 바쁘게 움직이고 있다.</strong>
혼자서 여기저기 요청을 보내야 하고 가운데에 껴서 고생하는 <strong>Controller 는 당연히 동작이 무거워진다.</strong>
따라서 코드 유지보수를 하다가 까딱했다간 <strong>UI 프레임 스킵 현상 및 메모리 릭 (Memory Leak)의 위험</strong>에 빠질지도 모름,,.</p>
<p>한마디로 <strong>MVC 패턴은 구현하기 쉬운 장점이 있는 반면, 기능 추가 및 변경에 있어 유지보수가 어렵다.</strong></p>
<pre><code>이러한 MVC패턴의 단점을 보완하고자 등장한 디자인 패턴이 바로 MVVM이다!
MVVM은 기존 MVC에서 Controller에게 많은 역할을 부여하기보다, 이 동작 자체를 분리하여
동작의 흐름을 더욱 체계적으로 만들어주고 유지보수를 편리하게 할 수 있도록 해주는 디자인 패턴</code></pre><p><img src="https://images.velog.io/images/ha_jni/post/24be71ef-b64f-48eb-ba2a-af98d6bacea4/image.png" alt=""></p>
<h3 id="mvvmview--viewmodel--model">MVVM(View / ViewModel / Model)</h3>
<p><strong>View</strong>
Activity 역할을 담당하고 UI를 갱신하는 역할에만 충실!
사용자의 Action을 받음(텍스트입력, 버튼터치 등)
ViewModel의 데이터를 관찰(Observe)한다.
→ <strong>데이터의 변화를 알아차리고 자동으로 화면을 갱신</strong> 할수 있음</p>
<p><strong>ViewModel</strong>
View가 요청한 데이터를 Model로 요청
Model로부터 요청한 데이터를 받음</p>
<p><strong>Model</strong>
ViewModel이 요청한 데이터를 반환
Room, Realm과 같은 DB사용이나 Retrofit을 통한 백엔드 API호출(네트워킹)이 보편적
Repository, DataBase부분으로 데이터 처리 역할</p>
<p>결국 View 가 필요로 하는 데이터는 ViewModel 이 쥐고 있고,
View 는 그것을 필요로 하기 때문에 ViewModel 이 쥐고 있는 데이터를 관찰 (Observing) 한다.</p>
<p>때문에 MVC 패턴과 다르게, View 가 DB 에 직접 접근하는 것이 아닌 UI 업데이트에만 집중한다.
또한 관찰하고 있는 만큼 데이터 변화에 더욱 능동적으로 움직이게 된다.</p>
<p>따라서 MVVM 패턴은 다음과 같은 장점을 가진다.</p>
<h3 id="mvvm-장점">MVVM 장점</h3>
<pre><code>1. 뷰가 데이터를 실시간으로 관찰
-  LIveData (= Observable 패턴) 을 이용해 DB를 관찰하고 자동으로 UI를 갱신
- 직접 뷰를 바꾸어주는 번거로움을 없애고 데이터와 불일치할 확률이 줄어듦

2. 생명주기로부터 안전하여 메모리 릭을 방지할 수 있음
-  ViewModel을 통해 데이터를 참조하기 때문에 Activity / Fragment의 생명주기를 따르지 않는다. 
    → 화면전환과 같이 액티비티가 파괴된 후 재구성이 되어도 뷰모델이 데이터를 홀드하고 있기 때문에 영향 X
- 뷰가 활성화 되어있을 경우에만 작동하기 때문에 불필요한 메모리 사용을 줄일 수 있음.

3. 기능별로 모듈화가되어 있어 역할을 분리 할 수 있고 유닛 테스트에 한결 용이
    → 내장 DB를 통째로 바꾸고 싶다고 할 때, 뷰나 다른 코드에 깊게 종속되어 있지 않아, DB교체가 쉽다!
          뷰모델과 뷰가 1:N 연결이 가능!!
          따라서, 뷰모델에 하나의 메소드를 구현해두면, A 액티비티든 B 액티비티든 여러 뷰에서 호출하여, 재사용하기 편리</code></pre><p>이러한 장점을 가지고 있는 MVVM 패턴은 잘만 사용한다면 훌륭한 앱을 만들 수 있다.
그렇지만 그만큼 구조가 복잡하다는 단점이 크게 다가오는데... 바로 진입장벽이 높음 😂.</p>
<p>그래도 MVVM 패턴을 간편하게 적용해볼 수 있게끔 구글에서 AAC 라는 것을 제공을 해준다.</p>
<h2 id="aac-android-architecture-component">AAC (Android Architecture Component)</h2>
<p>참고 : <a href="https://velog.io/@jojo_devstory/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90-%ED%8C%A8%ED%84%B4-MVC%EA%B0%80-%EB%AD%98%EA%B9%8C">https://velog.io/@jojo_devstory/안드로이드-아키텍쳐-패턴-MVC가-뭘까</a>
<a href="https://velog.io/@haero_kim/Android-%EA%B9%94%EC%8C%88%ED%95%98%EA%B2%8C-MVVM-%ED%8C%A8%ED%84%B4%EA%B3%BC-AAC-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0">https://velog.io/@haero_kim/Android-깔쌈하게-MVVM-패턴과-AAC-알아보기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android Graphql]]></title>
            <link>https://velog.io/@ha_jni/Android-Graphql</link>
            <guid>https://velog.io/@ha_jni/Android-Graphql</guid>
            <pubDate>Wed, 18 Aug 2021 04:40:15 GMT</pubDate>
        </item>
        <item>
            <title><![CDATA[Clean Architecture 3장]]></title>
            <link>https://velog.io/@ha_jni/Clean-Architecture-3%EC%9E%A5</link>
            <guid>https://velog.io/@ha_jni/Clean-Architecture-3%EC%9E%A5</guid>
            <pubDate>Mon, 09 Aug 2021 09:39:10 GMT</pubDate>
            <description><![CDATA[<p>SOLID의 목적은 중간 수준의 소프트웨어 구조가 아래와 같도록 만드는 데 있다.</p>
<ul>
<li>변경에 유연해야한다.</li>
<li>이해하기 쉬워야 한다.</li>
<li>많은 소프트웨어 시스템에 사용될 수 있는 컴포넌트의 기반이 된다.</li>
</ul>
<p>여기서 &#39;중간 수준&#39;이란 프로그래머가 원칙을 모듈 수준에서 작업할 때 적용할 수 있다는 뜻.</p>
<p>클린 코드가 건물을 짓기 위한 좋은 벽돌이라면, SOLID 원칙은 이 벽과 방에 올바르게 벽돌을 배치하는 방법을 알려주는 원칙이라고 볼 수 있다.</p>
<p>SOLID!! 예전에 Eli와 Bart가 SOLID가 뭔지 아세요? 라고 했던 기억이 난다.
&quot;어,, 테두리 속성 아닌가요,, 솔리드&quot; 가 나의 대답이였는데. 그때 &quot;Clean Architecture 에 나와요 나중에 읽어보세요&quot; 해서 얼핏 찾아봤던 기억이 있는데 이제서야 Clean Architecture를 읽으며 정리를 시작한다.</p>
<h2 id="srp--단일-책임-원칙"><strong>S</strong>RP : 단일 책임 원칙</h2>
<p>대부분 이 원칙을 듣는다면 <strong>모든 모듈이 단 하나의 일만 해야 한다</strong> 는 의미로 알고 있을것이다. 역사적으로도 단일 모듈은 변경의 이유가 하나, 오직 하나뿐 이어야 한다. 라고 기술되어있다.
하지만 밥아저씨는 SRP 의 최종 버전을</p>
<pre><code>💡 하나의 모듈은 하나의, 오직하나의 **액터** 에 대해서만 책임져야한다.</code></pre><p> = 클래스의 역할과 책임을 너무 많이 주지 마라.</p>
<p>즉 클래스를 설계할 때 어플리케이션의 경계를 정하고, 추상화를 통해 어플리케이션 경계 안에서 필요한 속성과 메서드를 선택하여 설계 해야 한다.</p>
<p>책임이란? </p>
<ul>
<li>여러관점에서 해석할 수 있지만,, 보통 &quot;해야 하는 것&quot; 또는 &quot;할 수 있는 것&quot;으로 간주된다.</li>
<li>객체에 책임을 할당할 때는 어떤 객체보다도 작업을 잘할 수 있는 객체에 책임을 할당해야 한다.</li>
</ul>
<p><strong>액터란 이해 관계를 가진, 변경을 요청하는 집단</strong>을 말한다.</p>
<h3 id="srp의-예를-가져와-봤다">SRP의 예를 가져와 봤다.</h3>
<p>예를 들면, Student(학생) 클래스가 수강 과목을 추가하거나 조회하고, 데이터베이스에 객체 정보를 저장하거나 데이터베이스에서 객체 정보를 읽는 작업도 처리하고, 성적표와 출석부에 출력하는 일을 실행한다고 가정한다.</p>
<p>사실 클래스가 여러 개로 나눠지면, 클라이언트 쪽(클래스를 사용하는 쪽) 입장에서는 알아야될 클래스가 많아진다. 클라이언트는 액터보다, 유저의 행동에 따라 로직을 짤 수 있기 때문이다.이럴 때는 <strong>퍼사드 패턴으로 액터를 외부로 숨길 수 있다.</strong></p>
<pre><code class="language-java">public class Student {
        public void getCourses(){}
        public void addCourse(Course course){}
        public void save(){}
        public void Student load(){}
        public void printOnReportCard(){}
        public void printOnAttendanceBook(){}
}</code></pre>
<p>위의 코드는 학생 클래스가 너무 많은 책임을 수행하고있ㄷ. 학생 클래스에서 가장 잘할 수 있는 것을 생각해보면 수강 과목을 추가하거나 조회하는 것이다.
나머지 기능들은 학생클래스가 아닌 다른 클래스에서 더 잘할 수 있는 여지가 많다. 그래서 학생 클래스는 수강과목을 조회하고, 추가하는 책임만 수행하는 것이 바람직함.</p>
<pre><code class="language-java">public class Student {
        public void getCourses(){}                  //학생 클래스에서 잘할 수 있는 것

        public void addCourse(Course course){}      //학생 클래스에서 잘할 수 있는 것
//        public void save(){}                      //다른 클래스에서 잘할 수 있는 것
//        public void Student load(){}              //다른 클래스에서 잘할 수 있는 것
//        public void printOnReportCard(){}         //다른 클래스에서 잘할 수 있는 것
//        public void printOnAttendanceBook(){}     //다른 클래스에서 잘할 수 있는 것
}</code></pre>
<h3 id="결론">결론.</h3>
<pre><code>💡 단일 책임 원칙은 메서드와 클래스 수준의 원칙이다.</code></pre><p>하지만 밥아저씨는 이보다 상위의 두 수준에서도 다른 형태로 다시 등장한다고 한다. </p>
<p>컴포넌트 수준에서는 → 공통 폐쇄 원칙</p>
<p>아키텍처 수준에서는 → 경계의 생성을 책임지는 변경의 축이된다.</p>
<h2 id="ocp--개방-폐쇄-원칙"><strong>O</strong>CP : 개방-폐쇄 원칙</h2>
<p>개방-폐쇄 원칙은 1988년에 버트란트 마이어가 만들었다고한다.</p>
<pre><code>💡 **OCP**  &quot;소프트웨어 개체는 확장에 열려 있어야 하고, 변경에는 닫혀 있어야한다.&quot;</code></pre><p>= 자신의 확장에는 열려 있고, 주변의 변화에 대해서는 닫혀 있어야 한다.</p>
<p>OCP의 목표</p>
<ul>
<li>시스템을 확장이 쉬워야한다.</li>
<li>변경으로 인해 시스템이 너무 많은 영향을 받지 않아야한다.</li>
</ul>
<p>목표를 달성하기위해서는</p>
<ul>
<li>시스템을 컴포넌트 단위로 분리해야한다.</li>
<li>저수준에서 고수준을 보호할 수 있는 형태의 의존성의 계층구조가 만들어져야 한다.</li>
</ul>
<h3 id="예제">예제</h3>
<p>SoundPlayer는 음악을 재생해주는 클래스이고 기본적으로 wav 파일을 재생할 수 있다. 여기서 만약 다른 파일 형식 mp3 파일을 재생하도록 요구사항이 변경 되었을 경우, SoundPlayer의 play 메서드를 수정해야 한다면?</p>
<pre><code class="language-java">class SoundPlayer {
        void play() {
                System.out.println(&quot;paly wav&quot;);
        }
}

public class Client {
        public static void main(String[] args) {
                SoundPlayer sp = new SoundPlayer();
                sp.play();
        }
}</code></pre>
<p>위의 예제에서 OCP를 만족시키기 위해서는 play 메서드를 인터페이스로 분리해야 한다. SoundPlayer 클래스에서는 playAlgorithm 인터페이스를 멤버 변수로 만들고 play() 함수에는 인터페이스를 상속받아서 구현된 클래스의 play를 실행하도록 한다.</p>
<pre><code class="language-java">interface playAlgorithm {
        public void play();
}

class Wav implements playAlgorithm {
        @Override
    public void play() {
                System.out.println(&quot;paly wav&quot;);
    }
}

class Mp3 implements playAlgorithm {
        @Override
    public void play() {
                System.out.println(&quot;paly Mp3&quot;);
    }
}

class SoundPlayer {
        private playAlgorithm file;
        public void setFile(playAlgorithm file) {
                this.file = file;
        }
        public void play() {
                file.play();
        }
}

public class Client {
        public static void main(String[] args) {
                SoundPlayer sp = new SoundPlayer();
                sp.setFile(new Wav());
                sp.setFile(new Mp3());
                sp.play();
        }
}</code></pre>
<h2 id="lsp--리스코프-치환-원칙"><strong>L</strong>SP : 리스코프 치환 원칙</h2>
<p>1988년 바바라 리스코프는 하위 타입을 아래와 같이 정의 했다.</p>
<pre><code>💡 서브 타입은 언제든 자신의 기반 타입으로 교체할 수 있어야 한다.</code></pre><p> <strong>= 하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 하는데 문제가 없어야 한다.</strong></p>
<p> <strong>= 즉 Upcating 된 객체 참조 변수가 논리적으로 그 역할이 문제가 없어야 한다.</strong></p>
<p>객체 지향에서의 상속은 조직도나 계층도가 아닌 분류도가 되어야 한다. </p>
<p>하위클래스 is a kinid of 상위 클래스 - 하위 분류는 상위 분류의 한 종류다.
구현클래스 is able to 인터페이스 - 구현 분류는 인터페이스 할 수 있어야 한다.</p>
<p>하지만 문장대로 구현되지 않은 코드가 존재 할 수 있는데 바로 상속이 조직도나 계층도 형태로 구축된 경우이다.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/82dee0c9-019e-4e15-9576-355dadd979dc/image.png" alt=""></p>
<p><code>아버지 춘향이 = new 딸();</code></p>
<p>춘향이는 아버지형의 객체 참조 변수기에 아버지 객체가 가진 행위(메서드)를 할 수 있어야하는데 춘향이 에게 아버지의 어떤 역할이 가능할까?</p>
<p><img src="https://images.velog.io/images/ha_jni/post/623dd34c-2b39-40bc-b985-cfcddce164c0/image.png" alt=""></p>
<p><code>동물 뽀로로 = new 펭귄();</code></p>
<p>펭귄 한마리 이름은 뽀로로이고, 동물의 행위(메서드)를 잇게 하는데 전혀 이상이 없다.</p>
<p>아버지 - 딸 구조(계층도/조직도)는 리스코프 치환 원칙을 위배하고 있는 것이고, 동물 - 펭귄 구조(분류도)는 리스코프 치환 원칙을 만족하는 것이다.</p>
<h3 id="결론-1">결론</h3>
<pre><code>💡 하위 클래스의 인스턴스는 상위형 객체 참조 변수에 대입해 상위 클래스의 인스턴스 역할을 하는데 문제가 없어야 한다.</code></pre><h2 id="isp--인터페이스-분리-원칙"><strong>I</strong>SP : 인터페이스 분리 원칙</h2>
<pre><code>💡 클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안된다.</code></pre><p> =  상황과 관련 있는 메서드만 제공해라.</p>
<p>책에서는 다음과 같이 예를드는데.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/be77e33e-53f2-4c02-aa1b-803a70bec220/image.png" alt=""></p>
<p><code>User1</code>은 오직 <code>op1</code>을 <code>User2</code>는 <code>op2</code>만을, <code>User3</code>은 <code>op3</code>만 사용한다 하지만 Ops 라는 메서드에 들어있어서</p>
<p><code>User1</code>같은 경우는 <code>op2</code> 와 <code>op3</code>을 사용하지 않음에도 두 메서드에 의존하게 된다.</p>
<h3 id="isp-를-적용하면">ISP 를 적용하면?</h3>
<p> <img src="https://images.velog.io/images/ha_jni/post/82ce2724-8af0-4051-b368-879a691d773c/image.png" alt=""></p>
<p><code>User1</code>는 <code>U10ps</code>와 <code>op1</code>에 의존하지만 OPS에는 의존하지 않게된다. 따라서 OPS에서 수정은 <code>User1</code>에 관계없는 수정이면 불필요한 컴파일과 재배포를 막을 수 있다.</p>
<p>여기서 얻을 수 있는 교훈은 &quot;불필요한 점을 실은 무언가에 의존하면 예상치 못한 문한 문제를 일으킬 수 있다&quot; 는 것 </p>
<h2 id="dip--의존성-역전-원칙"><strong>D</strong>IP : 의존성 역전 원칙</h2>
<pre><code>💡 의존성은 추상에 의존해야 하며, 구체에 의존하지 않아야 한다.</code></pre><p> = 자신보다 변하기 쉬운 것에 의존하지 마라.</p>
<p>이것을 규칙으로보기엔 비현실적이다. 소프트웨어 시스템이라면 구체적인 많은 장치에 반드시 의존하기 때문이다.</p>
<p>우리가 의존하지 않도록 피하고자 하는 것은 바로 변동성이 큰 구체적인 요소다. 그리고 이 구체적인 요소는 우리가 열심히 개발하는 중이라 자주 변경될 수 밖에 없는 모듈들이다.</p>
<p>안정된 아키텍처는 변동성이 큰 구현체에 의존하는 일은 지양하고, 안정된 추상 인터페이스에 의존해야 한다.
실천법은 다음과 같이 요약할 수 있다.</p>
<ul>
<li>변동성이 큰 구체 클래스를 참조하지 말자<ul>
<li>대신 추상 인터페이스를 참조</li>
</ul>
</li>
<li>변동성이 큰 구체 클래스로 부터 파생하지 말자</li>
<li>구체 함수를 오버라이드 하지 말자</li>
<li>구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 말자.</li>
</ul>
<h3 id="예제-1">예제</h3>
<pre><code class="language-java">// A사의 알람 서비스
 */
public class A {
      public String beep() {
            return &quot;beep!&quot;;
      }
}

// 서비스 코드
public class AlarmService {
      private A alarm;

      public String beep() {
            return alarm.beep();
      }
}</code></pre>
<h3 id="만약-b사가-추가된다면">만약 B사가 추가된다면?</h3>
<pre><code class="language-java">//B사의 알림 서비스
public class B {
      public String beep() {
                return &quot;beep&quot;;
      }
}

// 서비스 코드
public class AlarmService {

        private A alarmA;
      private B alarmB;

      public String beep(String company) {
                if (company.equals(&quot;A&quot;)) {
                        return alarmA.beep();
                } else {
                       return alarmB.beep();
                }
      }
}</code></pre>
<h3 id="c사의-알림서비스가-추가된다면">C사의 알림서비스가 추가된다면?</h3>
<ul>
<li>서비스 코드를 바꿔야 한다.</li>
<li>C사의 알림 서비스 메소드를 추가하거나, if 문을 사용해서 B사의 알림 서비스를 사용할지, 
C사의 알림 서비스를 사용할지 정해야합니다.</li>
</ul>
<h3 id="dip">DIP</h3>
<ul>
<li>고수준 모듈 : 알림</li>
<li>저수준 모듈 : A사의 알림 서비스, B사의 알림 서비스</li>
</ul>
<p>지금까지 사용한 방법은 <strong>고수준 모듈이 저수준 모듈에 의존</strong>하는 방법이지만, DIP를 적용하게 되면 <strong>저수준 모듈이 고수준 모듈에 의존</strong>해야 한다. 그러기 위해 사용하는 것이 <strong>추상 타입</strong>(ex. 인터페이스, 추상 클래스)이다.</p>
<pre><code class="language-java">public interface Alarm {
      String beep();
}

// A사의 알람서비스
public class A implements Alarm {
      @Override
      public String beep() {
            return &quot;beep!&quot;;
      }
}

// B사의 알람서비스
public class B implements Alarm {
      @Override
      public String beep() {
            return &quot;beep&quot;;
      }
}</code></pre>
<p>저수준 모듈들이 Alarm을 구현하게 하면 된다. 그렇게 되면 위의 서비스 코드를 아래와 같이 변경할 수 있다.</p>
<pre><code class="language-java">// 서비스 코드
public class AlarmService {

      private Alarm alarm;

      public AlarmService(Alarm alarm) {
            this.alarm = alarm;
      }

      public String beep() {
            return alarm.beep();
      }

}</code></pre>
<p>더이상 AlarmService는 알람 서비스가 추가된다고해서 코드를 변경하거나 추가할 일이 없음.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/c864afbf-4ff9-4677-b49f-f956e377e14c/image.png" alt=""></p>
<p>코드를 다이어그램으로 나타내면 위와 같다. 
저수준 모듈이 고수준 모듈에 의존하게 되는 것을 DIP(의존관계 역전 원칙)라고 한ㄷㅏ.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Clean Architecture 2장]]></title>
            <link>https://velog.io/@ha_jni/Clean-Architecture-2%EC%9E%A5</link>
            <guid>https://velog.io/@ha_jni/Clean-Architecture-2%EC%9E%A5</guid>
            <pubDate>Mon, 09 Aug 2021 09:34:47 GMT</pubDate>
            <description><![CDATA[<p>밥아저씨는 2장을 들어가며 세 가지의 패러다임에 대해 설명을 한다.</p>
<ol>
<li>구조적 프로그래밍<ul>
<li>goto문장 → if / then / else 와 do / while / until 로 대체</li>
<li>제어흐름의 직접적인 전환에 대해 규칙을 부과</li>
</ul>
</li>
<li>객체 지향 프로그래밍<ul>
<li>stack frame을 heap으로 옮기면, 함수 호출이 반환된 이후에도 함수에 선언된 지역변수가 오랫동안 유지되는 것을 발견 → 함수가 클래스의 생성자, 지역변수는 인스턴스 변수, 중첩 함수는 메서드가 되었다.</li>
<li>함수 포인터를 특정 규칙에 따라 사용하는 과정을 통해 필연적으로 <strong>다형성</strong> <strong>등장</strong></li>
<li>제어흐름의 간접적인 전환에 대해 규칙을 부과</li>
</ul>
</li>
<li>함수형 프로그래밍<ul>
<li>제일 먼저 만들어졌지만 최근 들어서 겨우 도입되기 시작</li>
<li>John McCarthy가 만든 LISP 언어의 근간이 되는 개념이 바로 이 <strong>람다 계산법!</strong></li>
<li>할당문에 대해 규칙을 부과</li>
</ul>
</li>
</ol>
<p>이러한 패러다임들은 우리에게 무엇을 해야할 지를 말하기보다는 &quot;무엇을 해서는 안 되는지를 말해준다.</p>
<h2 id="구조적-프로그래밍">구조적 프로그래밍</h2>
<p>goto문장 → if / then / else 와 do / while / until 로 대체하라는 규칙이다.</p>
<p>goto문장을 사용하게 되면, 모듈을 더 작은 단위로 분해하는 과정에 방해가 되는 경우가 있다.</p>
<p>예를한번 찾아보았다.</p>
<pre><code class="language-csharp">int main()
{
        int num;
        printf(&quot;숫자 입력 : &quot;);
        scanf(%d&quot;, &amp;num);

        if(num == 1)
                 goto label1;    //label1이라는 레이블로 이동하라는 의미이다.
        elseif(num == 2)
                 goto label2;    //label2이라는 레이블로 이동하라는 의미이다.
        else
                 goto label3;    //label3이라는 레이블로 이동하라는 의미이다.

        //label

        label1:
        printf(&quot;1을 입력 했습니다.&quot;);

        label2:
        printf(&quot;2을 입력 했습니다.&quot;);

        label3:
        printf(&quot;3혹은 다른값을 을 입력 했습니다.&quot;);

        return 0;
}</code></pre>
<h3 id="왜일까">왜일까?</h3>
<p>사실 goto문을 들어만 봤지 사용해본 적도 없고 해서 왜인지 궁금하여 찾아보았다.</p>
<p>goto문은 프로그램의 구조를 해치기 때문에 goto문을 사용한 소스는 이식성과 재사용에 무척 불리하다. 특정 동작을 하는 코드를 다른 프로그램에서 재사용하려면 goto문에 의해 <strong>엉켜 있는 실</strong> 을 다 풀어야 하고 옮긴 후에 다시 그 프로그램에 맞게 연결해야 하기 때문 </p>
<p>→ 위의 예제는 goto문이 3개 뿐이지만 만약 수십개가 있다고하면,, goto문이 여기저기를 돌아다니고있을 것이다.
코드 순서도 어떻게되는지 알기 힘들것이다, 만약 분리해야 할 경우 분리해야 할 부분도 잡기 힘들것같은데. 그래서 위의 표현처럼 엉켜 있는 실 이라고 표현이 딱 맞을것 같다. 그래서 그뒤를 조금더 읽어보니 goto 문 없이도 사실 프로그램을 순차, 분기, 반복 이라는 세가지 구조만으로 표현할 수 있다.</p>
<p>구조적 프로그래밍을통해  우리는 더 작은 단위의 모듈로 분리할 수 있을 것이다. 또한 분리해야 될 부분도 좀 더 명확해질것이다.</p>
<h3 id="결론은">결론은?</h3>
<p>모듈, 컴포넌트, 서비스가 쉽게 반증 가능하도록(테스트하기 쉽도록) 만들기 위해 컴포넌트를 잘 분리해야 한다.</p>
<h2 id="객체-지향-프로그래밍">객체 지향 프로그래밍</h2>
<p>객체 지향을 들어가며, 좋은 아키텍처를 만드는 일은 객체지향(Object-Oriented, OO) 설계 원칙을 이해하고 응용하는 것 부터 출발한다고 말한다. </p>
<h3 id="oo란">OO란?</h3>
<p>OO 는 세 가지 부류가 있는데 캡슐화, 상속, 다형성이다. 
이에 대해 밥아저씨의 생각을 정리 해 보았다.</p>
<ul>
<li><p>캡슐화</p>
<p>  캡슐화가 존재하긴 하지만, C언어 외의 다른언어는 실제로 C 언어에서 누렸던 완벽한 캡슐화를 약화시켜왔다.</p>
</li>
<li><p>상속</p>
<p>  상속이 객체지향에서 새롭게 등장한 방식은 아니지만, 나름대로 편리하게 사용할 수 있게 하긴 했다.</p>
</li>
<li><p>다형성</p>
<p>  다형성도 사실 이전부터 표현할수있는 방식이었다.</p>
<p>  하지만, 기존의 방식을 크게 개선했고 매우 큰 효과를 냈다.
  사실 밥 아저씨는 캡슐화에 대해서는 별로 인정 안 하고 <strong>상속 그리고 다형성</strong> 에 주로 의의로 두었는데, 그중에서 특히 &quot;<strong>다형성</strong>&quot; 이 아키텍트 적인 관점에서 가지는 의의를 매우 크게 보였다.</p>
</li>
</ul>
<h3 id="다형성이란-뭘까">다형성이란 뭘까?</h3>
<p>일반적으로 제어 흐름은 시스템의 행위에 따라 결정되며, 소스 코드의 의존성은 제어 흐름에 따라 결정된다. 그래서 제어 흐름의 방향은 항상 고수준 로직에서 저수준 로직으로 향하게 된다.</p>
<p>이때 다형성이 끼어들면 무언가 특별한 일이 일어난다. 다형성이란, 쉽게 말해 고수준 로직에서 저수준 로직으로 흘러가는 흐름 사이에 추상 로직을 두는 것을 말한다. 이렇게 하면, 프로그램의 로직은 다음처럼 바뀐다.</p>
<ul>
<li>기존 : (고수준 로직) → (저수준 로직)</li>
<li>다형성 적용 후 : (고수준 로직) → (추상 로직) ← (저수준 로직)</li>
</ul>
<p>→ 로 흘러가던 방향이 ← 로 제어흐름이 반대인 점을 주목, 이를 <strong>의존성 역전</strong>이라고 한다.
이러한 현상은 특별한 의미를 갖는다. OO가 안전하고 편리하게 다형성을 제공한다는 사실은 소스 코드 의존성을 어디서든 역전시킬 수 있다는 뜻이다.</p>
<h3 id="결론">결론</h3>
<pre><code>💡 OO란 다형성을 이용하여 전체 시스템의 모든 소스 코드 의존성에 대한 절대적인 제어 권한을 획득 할 수 있는 능력.</code></pre><h2 id="함수형-프로그래밍">함수형 프로그래밍</h2>
<p>Eli가 어려울거라던 함수형 프로그래밍에 들어왔다.</p>
<p>함수형 프로그래밍의 개념은 프로그래밍 그 자체보다 앞서 등장 했는데, 이 패러다임에서 핵심이 되는 기반은 람다계산법으로 알론조 처치가 1930에 개발 했다.</p>
<p>자바는 가변 변수를 사용하는데, 가변 변수는 실행 중에 상태가 변할 수 있다. 
하지만 클로저에서는 이러한 가변변수가 전혀 없다ㅏ. 클로저에서는 x와 같은 변수가 한 번 초기화되면 절대 변하지 않는다.</p>
<p>이는 우리에게 놀라운 것을 알려주는데,  <strong>함수형 언어에서 변수는 변경되지 않는다</strong>  는 것이다.</p>
<p>하지만 불변성이 정말로 실현 가능한가?</p>
<ul>
<li>저장공간이 무한하고 프로세서의 속도가 무한이 빠른 전제한다면 가능하다.</li>
</ul>
<p>자원이 무한대가 아니라면 조금 미묘하다. </p>
<p>그래서 불변성은 실현 가능하지만 타협을해야한다.</p>
<p>하나는 가변 컴포넌트와 불변 컴포넌트로 분리하는 하고, 가능한 한 많은 처리를 불변 컴포넌트로 옮기고, 가변 컴포넌트에서는 가능한 한 많은 코드를 빼내야 한다.</p>
<h3 id="결론-1">결론</h3>
<pre><code>💡 불변 컴포넌트와 가변 컴포넌트를 분리하여 관리하자.
</code></pre><p>정리하자면</p>
<ul>
<li>구조적 프로그래밍은 제어흐름의 직접적인 전환에 부과되는 규율이다.<ul>
<li>직접적이란 <code>goto</code>를 사용하지않는것.</li>
</ul>
</li>
<li>객체 지향 프로그래밍은 제어흐름의 간접적인 전환에 부과되는 규율이다.</li>
<li>함수형 프로그래밍은 변수 할당에 부과되는 규율이다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Clean Architecture 1장]]></title>
            <link>https://velog.io/@ha_jni/Clean-Architecture-1%EC%9E%A5</link>
            <guid>https://velog.io/@ha_jni/Clean-Architecture-1%EC%9E%A5</guid>
            <pubDate>Mon, 09 Aug 2021 09:33:04 GMT</pubDate>
            <description><![CDATA[<p>처음 1장을 들어가면 익명의 회사의 데이터를 보여준다.</p>
<p>데이터를 살펴보면 시간이 지날수록 <strong>Software Engineer수는 늘어나지만 생산성은 점점 떨어진다.</strong></p>
<p>Robert C. Martin(밥아저씨) 는 토끼와 거북이 우화로 예를들며 어리석음을 말했는데, 현대의 개발자도 토끼와 유사한 과신을 드러낸다고 한다. </p>
<ul>
<li>느려도 꾸준하면 이긴다.</li>
<li>발 빠른 자가 경주에 이기는 것도 아 니며, 힘센 자가 싸움에서 이기는 것도 아니다.</li>
<li>급할수록 돌아가라</li>
</ul>
<p>밥아저씨는 현대의 개발자들을 우화의 토끼와 다를것없다고 말을한다.</p>
<h2 id="왜-그럴까">왜 그럴까?</h2>
<ol>
<li>코드는 나중에 정리하면돼. 당장은 시장에 출시하는게 먼저라는 흔해 빠진 거짓말에 속는다.<ul>
<li>나또한 그런적이 있는가? → 맞다 첫 프로젝트 당시. 마감기한이 다가오자,, 당장 배포 먼저 해야한다는 생각으로 코드정리는 나중에 하자 하고 흐린 눈 하고 넘어간적이있다</li>
</ul>
</li>
<li>지저분한 코드를 작성하면 단기간에는 빠르게 갈 수 있고, 장기적으로 볼 때만 생산성이 낮아진다.<ul>
<li>엉망으로 만들면 깔끔하게 유지할 때보다 항상 더 느리다. → 프로젝트를하는 당시 오류가 터진 적이 있다. 하지만 그때같은반 동기와 협업을 배우자는 이유로 같이 개발을 하였는데, 그결과 두명이 합쳐,, 매우 엉망인 코드가 되었다. 그로인해 오류 해결하는데 생각보다 오랜시간이 걸렸던 기억이 난다.</li>
<li>사실 이번 EasterEgg기능 또한 처음엔 지저분한 코드에 속해있다고 생각한다. Eli 와 Bart의 피드백으로 인해 그나마 코드가 간결해지고 Timer 또한 Activity에서 처리하던 것들을 Timer Class 안에서 처리를 해 Activity쪽 코드는 매우 깔끔해졌다.</li>
</ul>
</li>
</ol>
<h2 id="jason-gormantdd-→-테스트-주도-개발">Jason Gorman(TDD) → 테스트 주도 개발</h2>
<p>예전에 입사 초기 Bart 와 Eli 가 TDD 이야기를 하던 것을 들은적이 있어 그때당시 잠깐 찾아봤는데,</p>
<ul>
<li><strong>테스트를 먼저 만들고 테스트를 통과하기 위한 것을 짜는 것</strong> 이라고 간단하게 이해하고 넘어갔던 기억이있다.</li>
</ul>
<p>Clean Architecture를 읽으며 다시 TDD를 마주하게 되엇는데.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/8ff41afa-a08a-489a-be05-7f2a599f6b18/image.png" alt=""></p>
<p>표를 보여주며 예를 들었는데, 결과가 흥미로웠다.
TDD를 적용한 개발 방식이 적용하지 않은 방식보다 매우 빠르게 작업이 완료 되었다는 것이다.</p>
<p>밥아저씨는 예제를 보이고 이런말을했는데,</p>
<pre><code>💡 빨리 가는 유일한 방법은 제대로 가는것.
</code></pre><h2 id="두-가지의-가치">두 가지의 가치</h2>
<p>첫 번째 가치는 바로 <strong>&quot;행위&quot;</strong></p>
<ul>
<li>프로그래머는 이해관계자가 기능 명세서나 요구사항 문서를 구체화할 수 있도록 돕는다.</li>
</ul>
<p>두 번째 가치는 &quot;소프트웨어(software)&quot;</p>
<ul>
<li>본연의 목적을 추구하려면 소프트웨어는 반드시 &#39;부드러워&#39;야한다. → 변경하기 쉬워야한다.</li>
<li>아키텍처가 특정 형태를 다른 형태보다 선호 할수록 → 새로운 기능을 구조에 맞추는게 더 힘듦</li>
<li>아키텍처는 형태에 독립적 이어야 하고, 그럴수록 더 실용적이다.</li>
</ul>
<h3 id="더-높은-가치">더 높은 가치?</h3>
<p><strong>기능 or 아키텍처?</strong></p>
<ul>
<li><p>관리자에게 묻는다면 동작하는 것이 중요하다고 할 것이다.</p>
<p>  → 여기서 나는,, 동조하고있었다 하지만, 바로 뒤 잘못된 태도라고, 사례를 들었다.</p>
</li>
<li><p>수정이 불가능한 코드는 결국 만들 수 없게 되며 따라서 이 프로그램은 쓸모가 없다.</p>
</li>
<li><p>사실 수정이 완전 불가능한 코드는 존재하지 않지만, 수정이 현실적으로 불가능한 프로그램은 존재한다.</p>
<p>  → 수정에 드는 비용이 창출되는 수익을 초과하는 경우.</p>
</li>
</ul>
<h3 id="아키텍처를-위해-투쟁하라">아키텍처를 위해 투쟁하라</h3>
<p>여기서 밥아저씨는 책임을 다하려면 싸움판에 뛰어들어야 한다고 말을했다.</p>
<p>→ 나는 한번이라도 싸움판에 뛰어든 적이 있는가,,? 음,,대답은 아니요. </p>
<p>나 또한 소프트웨어를 안전하게 보호해야 할 책임이 있다 는 것을 명심해야 할 것</p>
<p>아키텍처가 후순위가 되면 개발하는 비용이 더 많이들고,
일부 또는 전체 시스템에 변경을 가하는 일이 현실적으로 불가능해진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PR(pull request)]]></title>
            <link>https://velog.io/@ha_jni/PRpull-request</link>
            <guid>https://velog.io/@ha_jni/PRpull-request</guid>
            <pubDate>Mon, 09 Aug 2021 09:31:04 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/ha_jni/post/4c10d72e-1e4b-476b-800c-fe65abca6d3e/image.png" alt=""></p>
<h2 id="pull-request란-이하-pr--피알">Pull Request란? (이하 PR : 피알)</h2>
<p>Pull requests let you tell others about changes you&#39;ve pushed to a branch in a repository on GitHub. </p>
<p>풀리퀘스트는 당신이 변경한 내용에 대해서 다른 사람들(동료)에게 말해준다.                              출처 : github.com</p>
<p><strong>&quot;Eli Bart!!! 제가 commit 을 했어요. master에 Marge해도 될까요?&quot; 라고 알리는 활동 = Pull Request(PR)</strong></p>
<h2 id="개요">개요</h2>
<p>pull request를 위해서 아래와 같은 절차를 거치려고 한다.</p>
<ul>
<li><p>Repo 내려받기</p>
<ol>
<li><p>자신이 원하는 위치로 이동한다.(Github/orderplus 라는 폴더를 만듦)</p>
</li>
<li><p>git clone으로 해당 레포지토리를 내려받는다.</p>
<pre><code class="language-php">git clone https://hajnii@bitbucket.org/xbarx/ohpl_android.git</code></pre>
</li>
</ol>
</li>
<li><p>서버에 커밋</p>
<ol>
<li>나의 브랜치를 만든다. → FeatureEasterEgg</li>
<li>나의 글을 맞는 폴더 위치에 만든다.</li>
<li>작성이 완료되면 add, commit을 한다.</li>
<li>서버에 push</li>
</ol>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[submodule]]></title>
            <link>https://velog.io/@ha_jni/submodule</link>
            <guid>https://velog.io/@ha_jni/submodule</guid>
            <pubDate>Mon, 09 Aug 2021 09:30:02 GMT</pubDate>
            <description><![CDATA[<p>Git 의 <code>서브모듈(Submodule)</code> 이란 하나의 저장소 안에 있는 또 다른 별개의 저장소이다.</p>
<p>보통 다른 원격 저장소를 가져와(pull) 서브모듈로 사용하게 된다.</p>
<h3 id="submodule-사용-방법">Submodule 사용 방법</h3>
<p>Submodule을 추가하려는 부모를 parent라고 하고, Submodule로써 추가되려는 자식을 child라고 가정할 때, 다음과 같이 사용할 수 있다.</p>
<pre><code class="language-java">// 아직 parent에 submodule이 추가되지 않은 상태에서 다음 명령어를 입력하여
// submodule 추가를 해줄 수 있다.
git clone git@github.com:snowdeer/parent.git

cd parent
git submodule add git@github.com:snowdeer/child.git child
git commit -m &quot;submodule is added.&quot;
git push</code></pre>
<p>이 때, 별도로 git add 절차 없이 바로 commit 할 수 있다.</p>
<h3 id="git-clone으로-parent를-가져왔을-때">git clone으로 parent를 가져왔을 때</h3>
<p>git clone으로 parent를 가져왔을 때, 내부의 child는 디렉토리만 만들어져 있고 내부가 없다. 이 때 submodule 초기화 및 업데이틀 해 주어야 한다. 루트에서 다음 명령어를 실행하면 된다.</p>
<pre><code class="language-java">git submodule init

git submodule update</code></pre>
<p>다만, 이 때 submodule의 소스 버전은 최신 버전을 가리키는 것이 아니라, submodule add를 수행했을 때의 버전을 가리키고 있다. submodule은 리파지토리가 실제로는 분리되어 있기 때문에 각 모듈의 버전이 따로 관리되는데, parent 프로젝트에서는 현재 submodule의 버전이 최신인지 아닌지 신경쓸필요없이 안정적인 특정 버전만 가리키면 되기 때문에 프로젝트 배포 등에서는 관리가 수월한 장점이 있다. 물론, 개발중인 프로젝트에서는 각 submodule들을 최신 버전으로 유지해야 할 경우 각 submodule들의 업데이트를 수동으로 한 번씩 더 해줘야 하는 단점이 있기도 한다.</p>
<p><strong>submodule을 최신 버전으로 교체하는 방법</strong></p>
<ul>
<li><code>child</code> 디렉토리에 들어가서 각각의 submodule들을 개별 업데이트 해주는 방식. 각 submodule 디렉토리에서 <code>git pull</code> 명령어나 <code>git checkout</code> 명령어 등을 이용해서 업데이트 가능.</li>
<li><code>parent</code>내에서 <code>git submodule foreach git pull origin master</code> 명령어를 실행하여 하위 submodule들을 전부 업데이트 해주는 방법</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[reset / revert]]></title>
            <link>https://velog.io/@ha_jni/reset-revert</link>
            <guid>https://velog.io/@ha_jni/reset-revert</guid>
            <pubDate>Mon, 09 Aug 2021 09:29:42 GMT</pubDate>
            <description><![CDATA[<h2 id="reset">Reset</h2>
<p>Reset은 시계를 다시 맞추는 것,</p>
<p>돌아 가려는 커밋으로 리파지토리는 재설정되고, 해당 커밋 이후의 이력은 사라진다. 
EX) 일반적인 개발 이력과는 차이가 있지만, 이해가 쉽게 하기 위해 영화 “유주얼 서스펙트”를 이용했고 이에 대한 스포일러를 포함.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/40a4672b-7214-458e-9083-a1e9886f9d0e/image.png" alt=""></p>
<p>기대했던 영화를 예매하였으나 스포일러 때문에 실망했던 이력을 볼 수 있다. 그래서 스포일러를 보기 전으로 이력을 되돌리기로 한다. 마치 내가 기억하고 있는 내용을 변경하는 것. 커밋 a3bbb3c 이후의 기억은 지우고 싶다. Reset은 다음과 같이 사용한다.</p>
<pre><code class="language-java">$ git reset &lt;옵션&gt; &lt;돌아가고싶은 커밋&gt;</code></pre>
<p>옵션이 몇가지 있는데 자주 쓰는 것 <strong>hard, mixed, soft 세가지</strong>가 있다. 영화를 예매하고 검색한 이력인 a3bbb3c 이후에 발생했던 ( 표를 예매하고, 팝콘과 사이다를 구매 같은)변화에 대해서 어떻게 할지에 대한 것이다.</p>
<ol>
<li><p><strong>hard</strong></p>
<p> 돌아가려는 이력이후의 모든 내용을 지워 버린다. 이렇게 하면 표를 예매하고, 팝콘과 사이다를 구매했던 모든 것들이 지워지고 모든것이 초기화 된다.</p>
<pre><code class="language-java"> $ git reset --hard  a3bbb3c</code></pre>
</li>
</ol>
<p><img src="https://images.velog.io/images/ha_jni/post/96cf1d0b-4915-4507-b8e7-6cc8fcc957d7/image.png" alt=""></p>
<pre><code>hard 옵션으로 reset한 후의 이력</code></pre><ol start="2">
<li><p><strong>soft</strong></p>
<p> 돌아가려 했던 이력으로 되돌아 갔지만, 이후의 내용이 지워지지 않고, 해당 내용의 인덱스(또는 스테이지)도 그대로 있다. 바로 다시 커밋할 수 있는 상태로 남아있는 것이다. 기억은 되돌려졌지만, 표와 팝콘과 사이다는 손에 들려있는 상태이다.</p>
<pre><code class="language-java"> $ git reset --sorf a2bbb3c</code></pre>
<p> <img src="https://images.velog.io/images/ha_jni/post/d827c26d-f9a3-47e9-81db-729b91df24f7/image.png" alt=""></p>
<p> soft옵션으로 reset한 후의 이력</p>
<p> <img src="https://images.velog.io/images/ha_jni/post/7ffe6edc-136d-48cb-985b-7e2d1c923e2b/image.png" alt=""></p>
<p> soft옵션으로 reset한 후의 파일 상태</p>
</li>
<li><p><strong>mixed ( 옵션을 적지 않으면 mixed로 동작한다. )</strong></p>
<p> 이력은 되돌려진다. 이후에 변경된 내용에 대해서는 남아있지만, 인덱스는 초기화 된다. 커밋을 하려면 다시 변경된 내용은 추가해야 하는 상태이다. 기억도 되돌려 졌고, 표와 팝콘 그리고 사이다는 사야겠다는 마음만 남아있다고 할 수 있다.</p>
<pre><code class="language-java"> $ git reset --mixed a2bbb3c</code></pre>
<p> <img src="https://images.velog.io/images/ha_jni/post/e9436e39-becc-4685-afd8-99e0a3ec774f/image.png" alt=""></p>
<p> mixed 옵션으로 reset한 후의 이력</p>
<p> <img src="https://images.velog.io/images/ha_jni/post/663234be-5b8c-4b99-897a-d6e466af9908/image.png" alt=""></p>
<p> mixed 옵션으로 reset한 후의 파일 상태</p>
<p> 되돌아가는 커밋을 커밋 해쉬를 통해서 직접 지정할 수도 있고 현재부터 몇개의 커밋을 되돌릴 수도 있습니다 <strong>맨위이미지</strong> 처럼 15413dc 부터 a3bbb3c로 돌아가려면</p>
<pre><code class="language-java"> $ git reset HEAD~6</code></pre>
<p> 위와 같이 현재부터 6개 이전 이력으로 돌아가라라고 상대적으로 지정할 수도 있다.</p>
</li>
</ol>
<h2 id="revert">Revert</h2>
<p>Revert는 상태를 되돌린다고 볼 수 있다. 스포를 당한 커밋을 revert하고 현재 작성중인 코드만 본다면 reset과 동일한 (hard 옵션 준거만 빼고) 결과를 가진다. 하지만 이력은 같지 않는다. (reset과 동일하게 스포일러를 당한 것을 되돌린다)</p>
<p><img src="https://images.velog.io/images/ha_jni/post/970f799d-f78c-4bc7-a3a2-b1000a0c3445/image.png" alt=""></p>
<p>스포일러 당한 커밋을 되돌림</p>
<p>이전 이력은 그대로 있고, 스포일러를 당했던 커밋만을 되돌렸다. 마치 스포일러 당한것에 대한 것을 기억하고 있지만, 그 내용은 알지 못하는 것처럼.( 이 내용은 앞에서 언급했던 Devpools의 설명에 나온 모나리자 눈썹의 내용이 더 이해가 쉬울것 같다. )</p>
<p>revert 를 하는 방법과 스포일러 댓글의 커밋을 되돌리는 것은</p>
<pre><code class="language-java"># git revert &lt;되돌릴 커밋&gt; 
git revert 2664ce8</code></pre>
<p>되돌릴 커밋이 여러개라면 범위를 주어서 여러개를 선택할 수도 있다. <strong>맨위이미지에서</strong> 
ex) 댓글을 읽은 것부터 영화관을 나설때까지 모두 되돌리려면 아래 코드처럼 범위를 주면 된다.</p>
<pre><code class="language-java">$ git revert 2664ce8..15413dc</code></pre>
<h2 id="reset--revert-언제사용">reset / revert 언제사용?</h2>
<p>단순하게 생각하면 reset을 하는 것이 revert를 하는 것보다 이력을 더 단순하게 만들어주기 때문에 revert의 장점이 많지 않아 보인다. 하지만 이력 중간에 로그 출력하도록 한 커밋이 있고 그 커밋만을 취소하려고 한다면 reset을 사용하여 이후의 이력을 모두 제거하는 것은 이후 이력을 모두 날려버리는 결과를 나을 것이다. 이런 때 revert를 사용하여 해당 커밋의 내용만 되돌릴 수 있다. 또한 이미 원격 리파지토리에 push 를 한 상태라면 reset을 사용하면 reset 하기 이전으로 되돌리기 전까지는 push 할 수 없게된다. (물론 force라는 무시무시한 옵션이 있다. ) 그래서 이미 push 한 코드라면 미련을 버리고 revert를 해야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Proguard / 암호화 / 난독화]]></title>
            <link>https://velog.io/@ha_jni/Proguard-%EC%95%94%ED%98%B8%ED%99%94-%EB%82%9C%EB%8F%85%ED%99%94</link>
            <guid>https://velog.io/@ha_jni/Proguard-%EC%95%94%ED%98%B8%ED%99%94-%EB%82%9C%EB%8F%85%ED%99%94</guid>
            <pubDate>Mon, 09 Aug 2021 09:27:33 GMT</pubDate>
            <description><![CDATA[<p>난독화 솔루션의 종류</p>
<ol>
<li><p>에러기반 난독화</p>
<p> 리패키징 방지 기능</p>
<p> smali 코드에서 자바 코드로 ㅂㄴ환할 때 사용되는 dex2jar 도구의 에러를 유발</p>
</li>
<li><p>소스코드기반 난독화</p>
<p> 자바 코드의 클래스, 메소드명, 필드명, 변수명 등을 임의의 문자(a, b, c)로 치환</p>
<p> 오픈소스 Proguard가 존재</p>
</li>
<li><p>문자열기반 난독화</p>
<p> 중요 정보를 다루는 평문의 문자열을 암호화하여 숨김(대칭키)</p>
<p> 꼭 필요한 구간에 난독화를 적용해야 하며, 모든 곳에 적용하면 앱 설치 및 기능이 정상적으로 동작하지않음</p>
</li>
<li><p>멀티덱스기반 난독화</p>
<p> 실질적인 앱을 구동하기 위한 코드, 원본 코드가 존재 </p>
<p> 원본 코드는 리소스 파일에 암호화하여 숨김</p>
<p> 상용 Dexguard가 존재</p>
</li>
</ol>
<p>소스코드 및 멀티덱스 난독화 (난독화 솔루션)</p>
<ol>
<li><p>프로가드</p>
<p> 대표적인 소스코드 난독화</p>
<p> 특정프로그램을 이용하면 일부 난독화 해제 기능 제공</p>
<p> 분석가는 특정 프로그램을 이용하지 않음(경험으로 인해 평문 수준의 소스코드 확인 가능)</p>
</li>
<li><p>덱스가드</p>
<p> 대표적인 멀티덱스 상용 난독화</p>
<p> 실제 앱을 구동하기 위한 코드를 숨기거나 삭제할 때 취약점 발생</p>
</li>
</ol>
<h3 id="android-proguard-가-필요한-이유">Android Proguard 가 필요한 이유</h3>
<ol>
<li><p>코드 난독화를 통해 디컴파일시 본인의 코드가 노출되는 것을 방지 할 수 있다.</p>
</li>
<li><p>불필요한 메서드를 제거하여 멀티덱스를 피할 수 있다.</p>
</li>
</ol>
<ul>
<li><p><strong>멀티덱스란?</strong></p>
<p>  안드로이드 앱을 구성하는 코드는 컴파일 되어 덱스(dex) 파일로 만들어 진다. 하나의 덱스(dex) 파일에는 최대 65536개의 메소드만 참조 할 수 있다. 만약 프로젝트의 코드가 65536개의 메소드를 초과하게 되면 덱스(dex)파일이 여러개가 생성된다.</p>
<p>  그러면 멀티 덱스를 사용하여 컴파일 할 수 있지만, 빌드 과정에서 앱 내의 파일을 여러개의 덱스파일로 나누어야 하므로 빌드 속도가 느려지고 APK 의 용량이 커지게 된다</p>
</li>
</ul>
<h3 id="코드-난독화-및-apk-최적화를-위한-android-proguard-사용법">코드 난독화 및 APK 최적화를 위한 Android Proguard 사용법</h3>
<ol>
<li>빌드타입을 디버깅과 릴리즈 버전으로 나눠 준다.</li>
</ol>
<pre><code class="language-java">buildTypes {

    debug {
        // 프로가드 활성화
        minifyEnabled true

        // 기본 프로가드 설정
        proguardFiles getDefaultProguardFile(&#39;proguard-android.txt&#39;), &#39;proguard-rules.pro&#39;

    }

    release {
        // 프로가드 활성화
        minifyEnabled true

        // 기본 프로가드 설정
        proguardFiles getDefaultProguardFile(&#39;proguard-android.txt&#39;), &#39;proguard-rules.pro&#39;


    }
}</code></pre>
<p>minifyEnabled 값을 true 로 바꿔 주면 프로가드가 적용된다.</p>
<p>디버그 시 proguard-debug.pro 를 추가해 줌으로써 난독화 및 라인 지워지는 것을 예외 처리 할 수 있다.</p>
<ol start="2">
<li>프로가드 활성화 및 <a href="http://debug.pro/">debug.pro</a> 추가</li>
</ol>
<pre><code class="language-java">buildTypes {

    debug {
        // 프로가드 활성화
        minifyEnabled true

        // 기본 프로가드 설정
        proguardFile getDefaultProguardFile(&#39;proguard-android.txt&#39;)
        // 프로젝트에 필요한 프로가드 설정
        proguardFile &#39;proguard-rules.pro&#39;
        // 디버그에 필요한 프로가드 설정
        proguardFile &#39;proguard-debug.pro&#39;
    }

    release {
        // 프로가드 활성화
        minifyEnabled true

        // 기본 프로가드 설정
        proguardFile getDefaultProguardFile(&#39;proguard-android.txt&#39;)
        // 프로젝트에 필요한 프로가드 설정
        proguardFile &#39;proguard-rules.pro&#39;
    }
}</code></pre>
<p>전체 적용된 코드로 빌드타입을 위와 같이 적용해 준다.</p>
<p>minifyEnabled 값을 true 로 바꿔 주면 프로가드가 적용된다.</p>
<p>디버그 시 proguard-debug.pro 를 추가해 줌으로써 난독화 및 라인 지워지는 것을 예외 처리 할 수 있다.</p>
<pre><code class="language-java">// proguard-debug.pro
# Begin: Debug Proguard rules

-dontobfuscate                              #난독화를 수행하지 않도록 함
-keepattributes SoureFile,LineNumberTable   #소스파일, 라인 정보 유지

# End: Debug ProGuard rules

//roguard-debug.pro 파일로 똑같이 작성하면 된다.</code></pre>
<ol start="3">
<li>적용 결과 확인</li>
</ol>
<h3 id="프로가드-사용-전">프로가드 사용 전</h3>
<p><img src="https://images.velog.io/images/ha_jni/post/2a98fa06-cce2-48fc-b441-66ec84a01b29/image.png" alt=""></p>
<p>프로가드를 사용하기 전에는 용량이 37.2MB 였으며 classes.dex 파일이 무려 3개나 있다.</p>
<p>classes.dex 의 메소드 개수를 보면 65501 개이며 이와 같은게 classes2.dex 에도 있다</p>
<p><img src="https://images.velog.io/images/ha_jni/post/6a236ddb-3ae6-4edb-9313-63a2036a9444/image.png" alt=""></p>
<p>classes3.dex 는 1505 개의 메소드가 있다.</p>
<h3 id="프로가드-적용-후">프로가드 적용 후</h3>
<p><img src="https://images.velog.io/images/ha_jni/post/912fe33a-1d4a-4cd0-90c7-a8aa2f95a907/image.png" alt=""></p>
<p>프로가드 적용 후 3개로 나뉘어져 있던 dex 파일이 classes.dex 파일 하나로 바뀌었습니다. 메소드 개수도 56673 으로  프로가드를 적용 안한 APK 보다 반이상이나 줄어들었다. 또한 용량은 기존 37.2MB 에서 33.7MB 로 감소하였다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Picasso]]></title>
            <link>https://velog.io/@ha_jni/Picasso</link>
            <guid>https://velog.io/@ha_jni/Picasso</guid>
            <pubDate>Mon, 09 Aug 2021 09:25:54 GMT</pubDate>
            <description><![CDATA[<p>피카소(Picasso)는 외부로부터 이미지를 불러와야 할 경우 유용하게 사용할 수 있는 라이브러리다.</p>
<p>매우 간단한 코드 몇 줄로 이미지 로딩, 메모리 &amp; 디스크 캐싱, 변형(Transforming)을 가능케 한다.</p>
<h3 id="장점"><strong>장점</strong></h3>
<p><strong>1. 직관적이다.</strong></p>
<p>반응형 프로그래밍은 3가지 부분으로 나뉘어져 있습니다. Input → Operator → Output 딱히 복잡하게 생각하지 않아도 코드를 봤을 때, 이해할 수 있다.</p>
<p><strong>2. 네트워크에서 이미지를 불러오기 간편하다.</strong></p>
<p>HTTP 요청을 자동으로 만들어준다. 페이지가 어떤 종류인지 어떤 식으로 불러들여야 할지도 생각할 필요가 없다. 네트워킹을 하면서 발생하는 예외도 걱정할 필요가 없다. 메인 스레드에서 다운로드하지 않기 때문. 이미지 캐싱도 해주기 때문에 재요청한다고 해서 많은 비용이 발생하는 것도 아니다.</p>
<p><strong>3. 이미지를 편집하기 쉽다.</strong></p>
<p>자르거나 중심을 맞추거나, 크기를 조정하는 등의 작업은 매우 간편하게 할 수 있다.</p>
<p><strong>피카소(Picasso)</strong>와 비슷한 <strong>글라이드(Glide)</strong>라는 라이브러리도 있으며, 사용법 또한 거의 같다.</p>
<p>다음 아티클에 어떤 차이점이 있는지 상세하게 설명되어 있으니 사용하고자 하는 시스템에 맞게 사용하면 된다.</p>
<h3 id="picasso-사용법">PICASSO 사용법</h3>
<pre><code class="language-java">// Gradle추가
dependencies {
    compile fileTree(dir: &#39;libs&#39;, include: [&#39;*.jar&#39;])
    androidTestCompile(&#39;com.android.support.test.espresso:espresso-core:2.2.2&#39;, {
        exclude group: &#39;com.android.support&#39;, module: &#39;support-annotations&#39;
    })
    compile &#39;com.android.support:appcompat-v7:25.1.0&#39;
    testCompile &#39;junit:junit:4.12&#39;
    // 여기에 하단코드 추가
        compile &#39;com.squareup.picasso:picasso:2.5.2&#39;
}</code></pre>
<p>매우 간단하게 imageView에 이미지를 할당할 수 있다.</p>
<pre><code class="language-java">ImageView imageView = (ImageView) findViewById(R.id.imageView);
Picasso.with(this)
.load(&quot;https://www.google.co.kr/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png&quot;)
.into(imageView);</code></pre>
<p>Picasso(Reactive Programming)는 3부분으로 이루어져있다.
Input, Operator, Output 이렇게 3가지 Picasso에서의 Input은 with(Context) Method로 이루어져있다. Context 객체를 잘 넣으면 된다. 간단하다. Picasso에서 Output은 이미지를 보여주고 싶은 ImageView 객체를 지정해주면 된다. 중요한 건 Operator 부분이다. Picasso에서의 Operator는 많은 Method로 이루어질 수 있다. &gt; 하단 예시</p>
<pre><code class="language-java">Picasso.with(context) // Input 부분
    .load(url) // Operator 시작: URL에서 이미지를 불러온다.
    .placeholder(R.drawable.user_placeholder) // 불러오는 시간 동안 보여줄 이미지 파일
    .error(R.drawable.user_placeholder_error) // 불러오지 못하면 보여주는 이미지 파일
    .resize(100, 100) // 이미지의 크기를 100x100 사이즈로 리사이즈 해준다.
    .rotate(90f) // 사진 파일을 회전해준다. Operator 끝났다.
    .into(imageView); // Output 부분: 변수 이름을 imageView라고 지정한 ImageView에 이미지를 보여준다.</code></pre>
<h2 id="기본-bitmap포맷">기본 Bitmap포맷</h2>
<p>1920x1080의 이미지를 768x432크기의 ImageView에서 각각 로드한경우 보여지는 이미지다.</p>
<p>그림을 잘 보면 Glide의 이미지화질이 Picasso보다 좋지 않음을 알 수 있다.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/83b27dfd-057b-4ac1-b2b7-b0b9eb3709c4/image.png" alt=""></p>
<p>Picasso는 Bitmap포맷을 ARGB_8888로 사용하고 Glide는 Bitmap포맷을 RGB_565를 사용한다.
RGB_565는 ARGB_8888에 비해서 화질은 떨어지지만 메모리 용량을 50% 적게 사용한다.
래 그림은 Picasso와 Glide의 메모리 사용 그래프 비교
(8MB정도는 기본적으로 어플리케이션에서 사용하는 메모리 사용량이고 그 이상이 각각 이미지로딩 라이브러리에서 사용하는 메모리 사용량)</p>
<p><img src="https://images.velog.io/images/ha_jni/post/07053ca8-d0d6-47b8-8551-eb62defa3480/image.png" alt=""></p>
<p>만약 메모리용량보다 화질이 더 중요하다고 생각한다면 Glide의 기본 Bitmap포맷을 ARGB_8888로 변경할수 있다.</p>
<p>GlideModule을 상속받는 클래스를 하나 지정해서 기본 포맷을 ARGB_8888로 설정하고 이 GlideModule을 매니페스트에 등록시켜주면 된다.</p>
<p>만약 Glide의 기본 Bitmap포맷을 ARGB8888로 변경했다면 Glide는 이전에 비해 2배정도 더 많이 메모리를 사용하는것을 볼 수 있다.</p>
<p>같은 Bitmap포맷을 사용하는데도 Picasso가 Glide보다 훨씬 많은 메모리 사용량을 보이고 있다.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/417ba6df-8896-4c06-83fb-d78911c529e5/image.png" alt=""></p>
<p>그 이유는, Picasso는 1920x1080크기의 원본 이미지를 메모리로 가져와서 GPU에서 실시간으로 리사이징해서 768x432의 ImageView에 할당한다.</p>
<p>하지만 Glide는 바로 768x432크기로 메모리에 가져와서 ImageView로 할당 시키기때문에 메모리 사용량이 적다.</p>
<p>만약, Picasso에서도 Glide와 같은 방식을 취하고싶다면 resize()함수를 이용하면 된다.</p>
<pre><code class="language-java">Picasso.with(context)
        .load(&quot;http://www.selphone.co.kr/homepage/img/team/3.jpg&quot;)
        .resize(768,432)
        .into(imageView); 

//fit()함수를 사용하면 같은 효과를 볼 수 있다.
Picasso.with(context)
        .load(&quot;http://www.selphone.co.kr/homepage/img/team/3.jpg&quot;)
        .fit()
        .into(imageView); </code></pre>
<h3 id="이미지-캐시">이미지 캐시</h3>
<p>이미지를 캐시하는 하는 방식에서도 기본적인 정책은 Glide와 Picasso는 다르다.</p>
<p>위의 예시처럼 1920x1080이미지를 768x432크기의 ImageView에 로드하는경우 Glide는 768x432크기의 이미지를 캐시하는 반면, Picasso는 1920x1080의 원본 이미지를 캐시하게 된다.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/b83559b9-7322-4f79-bec2-fcf64d610422/image.png" alt=""></p>
<p>만약 1920x1080 이미지를 다시 384x216크기의 ImageView로 로드한다고 할 경우 Picasso는 이미 원본 이미지를 그대로 가지고 있지만 Glide는 또하나의 384x216 크기의 이미지파일을 캐시하게 된다.</p>
<p>Glide는 같은 이미지를 다른 크기의 ImageView에 로드한다는 이유로 2번의 이미지 다운로드와 리사이징 작업이 필요하게 된다.</p>
<h3 id="문제-1--fit-또는-resize를-사용하지-못하는-경우">문제 1 : Fit 또는 Resize를 사용하지 못하는 경우</h3>
<p>피카소는 해야 할 일을 모두 마친 후 (캐시, 가공 등) into 메서드를 통해 imageView에 이미지를 할당한다.</p>
<p>그러면 Bitmap을 사용해야 하는 경우는? 다음과 같이 Target Callback을 활용해야 한다.</p>
<pre><code class="language-java">Target target = new Target() {

        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                doSomething(bitmap)
        }

        @Override
        public void onBitmapFailed(Drawable errorDrawable) {

        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {

        }
};

Picasso
        .with(this)
        .load(&quot;http://i.imgur.com/DvpvklR.png&quot;)
        .into(target);</code></pre>
<p>그런데, Target Callback을 통해 bitmap을 전달받는 경우는 resize, centerCrop, fit 등 몇몇 함수를 사용할 수 없다.
만약 함께 사용하려 시도한다면 다음 에러가 발생한다.</p>
<blockquote>
<p>Caused by: java.lang.IllegalStateException: Fit cannot be used with a Target.</p>
</blockquote>
<p>개발할 때, Bitmap을 사용해야 하는 경우는 다음과 같았는데</p>
<ul>
<li>Notification의 thumbnail</li>
<li>락 스크린의 미디어 thumbnail</li>
</ul>
<p>피카소 라이브러리의 Fit 함수 구현체를 보면 다음과 같이 기술되어 있다.</p>
<pre><code class="language-java">/**
 * Attempt to resize the image to fit exactly into the target {@link ImageView}&#39;s bounds. This
 * will result in delayed execution of the request until the {@link ImageView} has been laid out.
 * Note: This method works only when your target is an {@link ImageView}.
 */
public RequestCreator fit() {
  deferred = true;
  return this;
}</code></pre>
<p>Resize, centerCrop 등의 함수를 사용할 수 없으니, 정사각형 1:1 비율이 아닌 이미지의 경우는 어쩔 수 없이 아래처럼 Bitmap을 후가공하는 커스텀 함수를 구현하여 아래와 같이 처리</p>
<pre><code class="language-java">Transformation transformation = new Transformation() { 
        @Override
        public Bitmap transform(Bitmap source) {
                return myCenterCrop(source);
        }

        @Override
        public String key() {
                return null;
        }
}

Picasso
        .with(this)
        .load(url)
        .transform(transformation)
        .into(target);
</code></pre>
<h3 id="문제-2--메모리-증가-문제">문제 2 : 메모리 증가 문제</h3>
<p>onBitmapLoaded를 통해 넘어온 bitmap을 가공해서 사용할 경우, 가공된 bitmap을 캐시할 수 없기 때문에 계속해서 메모리 누수가 발생하는 것 (가공된 bitmap을 캐시하도록 따로 추가한다면 모르지만)</p>
<p><strong>해결방법</strong></p>
<p>이를 해결하기 위해서는 다음과 같이 onBitmapLoaded로 넘어오기 이전에 이미지 처리를 해야 가공된 이미지를 메모리에 올릴 수 있다.</p>
<pre><code class="language-java">Transformation transformation = new Transformation() {
        @Override
        public Bitmap transform(Bitmap source) {
                return myCenterCrop(source);
        }

        @Override
        public String key() {
                return null;
        }
}

Picasso
        .with(this)
        .load(url)
        .transform(transformation)
        .into(target);</code></pre>
<p>이후, transform 함수 내에서 로그를 찍어보면 최초 한 번만 호출되는 것을 확인할 수 있고, 더불어 메모리 누수도 막을 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RX]]></title>
            <link>https://velog.io/@ha_jni/RX</link>
            <guid>https://velog.io/@ha_jni/RX</guid>
            <pubDate>Mon, 09 Aug 2021 09:24:15 GMT</pubDate>
            <description><![CDATA[<h3 id="rx란">RX란?</h3>
<p>RxKotlin, RxAndroid을 알아보기전에 먼저 알아볼것이 있다.</p>
<p>바로 <strong>Reactive Programing</strong> 이다.</p>
<p>컴퓨터 프로그램에는 세가지 종류가 있다.</p>
<ul>
<li>주어진 입력 값을 바탕으로 결과를 계산하는 변환 프로그램이며 일반적으로 컴파일러와 수치 계산 을 하는 프로그램이다.</li>
<li>상호작용 프로그램으로 프로그램이 작업을 주도하며 사용자 혹은 다른 프로그램과 상호작용을 합니다. 사용자의 관점에서 볼때는 흔히 말하는 시분할 시스템은 상호작용 프로그램이다.</li>
<li>리액티브 프로그램은 <strong>프로그램 자신의 주변 환경과 끊임없이 상호작용을 하는데 프로그램이 주도하는 것이 아니라 환경이 변하면 이벤트를 받아 동작한다.</strong></li>
</ul>
<p>즉, 근래에 떠오르고 있는 <strong>Reactive Programing는 데이터의 흐름과 전달에 관한 프로그래밍 패러다임</strong>이라고 볼 수 있다.</p>
<h3 id="왜-사용할까">왜 사용할까?</h3>
<p>개발자들은 사용자 경험(UX)을 향상시키고 싶어한다. 사용자 경험(UX)이 좋은 앱을 만든다는것은 반응형 앱을 개발하고 싶은 것 이다.</p>
<p>그래서 앱에서 메인스레드가 멈추거나 느려지지 않도록 해야하며 사용자들에게 부드러운 사용자 경험(UX)과 좋은 앱 성능을 제공하고 싶어 한다.</p>
<p>하지만 메인 스레드를 자유롭게 핸들링 하면서 유지하려면 무겁고 시간이 오래 걸리는 작업은 백그라운드에서 해야 한다. 그리고 백그라운드에서 조차 무겁고 복잡한 계산 작업이라면 서버에서 수행 하는 것이 Best 이다. 그렇기 때문에 <strong>네트워크 운영을 위한 비동기 작업이 필요하다.</strong></p>
<h3 id="비동기-작업은-asynctask로도-되지-않나">비동기 작업은 AsyncTask로도 되지 않나?</h3>
<p>맞다.</p>
<p>하지만 <strong>2019년 11월 8일 개발자 Charles Munger에 의해 공식적으로 AsyncTask Deprecated되었습니다.</strong> 즉, 다른 비동기 처리 라이브러리를 써야한다는 것이죠.그렇다면 왜 이런 상황이 되었나 하고 AsyncTask를 자세히 살펴볼 필요가 있다.</p>
<p>AsyncTask는 전체적인 프로세스를 단순화 하지만 <strong>안드로이드의 생명주기를 신경쓰지 않다.</strong> 그렇기 때문에 <strong>액티비티나 프레그먼트가 안드로이드 생명주기에 의해 재생성되거나 파괴되었을 때 마무리 작업에 대한 내용이 보호되지 않는 불편한 점이 있다.</strong></p>
<h3 id="명령형-프로그래밍과-다르다">명령형 프로그래밍과 다르다</h3>
<ul>
<li>명령형 프로그래밍(Imperative programming) – 작성된 코드가 정해진 순서대로 실행됨.</li>
<li>리액티브 프로그래밍(Reactive Programing) – 데이터 흐름을 먼저 정의하고 데이터가 변경되었을 때 연관되는 함수나 메서드가 업데이트 되는 방식.</li>
</ul>
<h3 id="그래서-reactivex가-나왔다">그래서 ReactiveX가 나왔다</h3>
<p>ReactiveX는 비동기 프로그래밍과 Observable 시퀀스를 이용해 이벤트를 처리하기위한 라이브러리</p>
<h3 id="rxkotlin-rxandroid도-같다">RxKotlin, RxAndroid도 같다?</h3>
<p>현재 많은 리액티브 관련 라이브러리가 나와 있는데 대부분 ReactiveX를 사용하기 때문에 RxKotlin, RxJava, RxAndroid, RxSwift 등들은 서로 다른 것이 아니라 하나의 ReactiveX Extensions이라고 보면 된다.</p>
<p>간단히 정리하면 아래와 같다.</p>
<ul>
<li><strong>RxJava: Java(JVM)를 위한 ReactiveX ExtensionsReactive programming(리액티브 프로그래밍) 패러다임을 자바에서 구현</strong>한 프로그래밍 라이브러리</li>
<li><strong>RxKotlin: Kotlin을 위한 ReactiveX Extensions</strong>RxJava 라이브러리를 기반으로 포팅하여 <strong>코틀린을 위한 리액티브 프로그래밍의 특정 부분을 함수형 프로그래밍으로써 구현</strong>한 라이브러리</li>
<li><strong>RxAndroid: Android를 위한 ReactiveX Extensions</strong> RxJava에 최소한의 클래스를 추가하여 <strong>안드로이드 앱에서 리액티브 구성요소를 쉽고 간편하게 사용</strong>하게 만드는 라이브러리</li>
<li><strong>RxSwift: Swift를 위한 ReactiveX Extensions</strong></li>
</ul>
<p>위의 ReactiveX Extensions에는 공통점이 있다.</p>
<ul>
<li>효율적으로 신속하게 비동기 처리를 도와줌</li>
<li>함수형 프로그래밍을 일부 지원함</li>
<li><strong>옵저버패턴(Observer pattern)을 사용함</strong></li>
<li>콜백(Callback)에서 또 콜백을 하는 콜백 지옥에서 벗어날 수 있다.</li>
</ul>
<h3 id="rxjava-리엑티브-자바란">RxJava 리엑티브 자바란?</h3>
<ul>
<li>RxJava 는 자바와 안드로이드를 위한 리엑티브 프로그래밍 구현체</li>
<li>함수형 프로그래밍의 영향을 받았기 때문에 함수 구성을 선호함 ( Ramda 형식 )</li>
<li>전역상태나 부수효과를 피하고 비동기나 이벤트 기반 프로그램을 작성할 때 Stream 방식 ( Ramda Stream ) 으로 생각한다.</li>
<li>RxJava 는 생산자/소비자 콜백을 사용한 옵저버 패턴을 시작으로 구성과 변환, 스케줄링, 스로틀링, 오류 처리, 생명주기 관리를 할 수 있는 수많은 연산자를 제공함</li>
</ul>
<p>RxJava 자바와 안드로이드 뿐만 아니라 서버까지 다루는 오픈소스 라이브러리이다.</p>
<p>리액티브 프로그래밍이란, <strong>데이터나 이벤트 변화와 반응에 초점을 맞춘 프로그래밍</strong>을 뜻하는 일반적인 용어</p>
<p>엄밀히 말하면 FRP( fucntional reative programming ) 은 RxJava와 다르다. FRP는 연속적인 시간의 흐름을 포함하고 RxJava는 시간에 대해 불연속적인 이벤트만 다룬다.</p>
<h3 id="리엑티브-함수형-프로그래밍이란">리엑티브 함수형 프로그래밍이란</h3>
<p>동시성과 병렬성을 해결하는 프로그래밍으로 리액티브나 비동기 요구사항을 명령형 방식으로 만들었을 때 나타나는 콜백 지옥 문제를 해결하는 것이다.</p>
<p>즉 RxJava 를 이용한 리액티브 프로그래밍은 명령형 방식을 이용하지 않고 선언적 접근을 사용한다.</p>
<h3 id="리액티브-프로그래밍이-필요한-순간">리액티브 프로그래밍이 필요한 순간</h3>
<ul>
<li>마우스 움직임이나 클릭, 키보드 타이핑, GPS 신호, 자이로스코프 신호, 터치 이벤트 처리</li>
<li>비동기성을 띠는 네트워크 등 지연 I/O 이벤트 응답</li>
<li>이벤트나 앞서 나온 사용자 이벤트, 애플리케이션에서 발생하는 이벤트나 데이터를 다룰때</li>
</ul>
<h3 id="rxjava-동작방법">RxJava 동작방법</h3>
<p><strong>데이터나 이벤트 스트림을 나타내는 Observable 타입으로 push / reactive 방식을 선호</strong></p>
<p>즉시동작이 아닌 지연실행 가능하고 비동기와 동기 방식 모두 사용가능하고 시간에 따라 많은 이벤트를 다룰 수 있다.</p>
<p>RxJava 가 리액티브이기 위한 핵심은 밀어내기 인데 Observable과 Observer 타입 시그니처가 이를 지원한다.</p>
<p>밀어내기를 지원하기 위해서는 Observable / Observer 쌍을 subscribe로 연결</p>
<pre><code class="language-java">Observer는 구독을 통해 3가지 유형의 이벤트를 받는다.

interface Observer&lt;T&gt;{
        void onNext( T t )
        void onError( Throwalbe t )
        void onComplete()
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[OkHttp]]></title>
            <link>https://velog.io/@ha_jni/OkHttp</link>
            <guid>https://velog.io/@ha_jni/OkHttp</guid>
            <pubDate>Mon, 09 Aug 2021 09:23:51 GMT</pubDate>
            <description><![CDATA[<ul>
<li>OkHttp는 Square에서 제공하는 오픈소스 프로젝트이며 동기, 비동기 방식을 각각 제공해줌으로서 개발자가 선택하여 사용할 수 있다.</li>
<li>OkHttp는 HTTP 및 HTTP/2 통신을 보다 쉽게 할 수 있도록 다양한 기능을 제공해주는 Android 및 Java 용 라이브러리</li>
<li>최신 http통신에 대해 간편히 하고 데이터와 미디어를 교환 하는 방법이며 구성에 도움을 주는 라이브러리</li>
</ul>
<h3 id="get">GET</h3>
<pre><code class="language-java">[GET 동기]
public void get(String requestURL) {
        try {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .addHeader(&quot;x-api-key&quot;, RestTestCommon.API_KEY)
                    .url(requestURL)
                    .build(); //GET Request 

      //동기 처리시 execute함수 사용 
            Response response = client.newCall(request).execute(); 

            //출력 
            String message = response.body().string();
            System.out.println(message);
        } catch (Exception e){
            System.err.println(e.toString());
        }
    }

[GET 비동기]
public void get(String requestURL) {

        try {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .addHeader(&quot;x-api-key&quot;, RestTestCommon.API_KEY)
                    .url(requestURL)
                    .build();

            //비동기 처리 (enqueue 사용)
            client.newCall(request).enqueue(new Callback() {
                //비동기 처리를 위해 Callback 구현 
                @Override
                public void onFailure(Call call, IOException e) {
                    System.out.println(&quot;error + Connect Server Error is &quot; + e.toString());
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    System.out.println(&quot;Response Body is &quot; + response.body().string());
                }
            });

        } catch (Exception e){
            System.err.println(e.toString());
        }
    }</code></pre>
<h3 id="post">POST</h3>
<pre><code class="language-java">[POST 동기]
public void post(String requestURL, String jsonMessage) {
        try{
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .addHeader(&quot;x-api-key&quot;, RestTestCommon.API_KEY)
                    .url(requestURL)
                    .post(RequestBody.create(MediaType.parse(&quot;application/json&quot;), jsonMessage))
                    //POST로 전달할 내용 설정 
                    .build();

      //동기 처리시 execute함수 사용
            Response response = client.newCall(request).execute();  

            //출력
            String message = response.body().string();
            System.out.println(message);

        } catch (Exception e) {
            System.err.println(e.toString());
        }
    }

[POST 비동기]
public void post(String requestURL, String message) {

        try{
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .addHeader(&quot;x-api-key&quot;, RestTestCommon.API_KEY)
                    .url(requestURL)
                    .post(RequestBody.create(MediaType.parse(&quot;application/json&quot;), message))
                    .build();

            //비동기 처리 (enqueue 사용)
            client.newCall(request).enqueue(new Callback() {
                //비동기 처리를 위해 Callback 구현
                @Override
                public void onFailure(Call call, IOException e) {
                    System.out.println(&quot;error + Connect Server Error is &quot; + e.toString());
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    System.out.println(&quot;Response Body is &quot; + response.body().string());
                }
            });

        } catch (Exception e) {
            System.err.println(e.toString());
        }
    }

// 동기 처리를 하고자 한다면 execute를 사용하면 되고 비동기 처리를 원한다면 enqueue를 사용하면 된다.
</code></pre>
<p>HttpURLConnection과 HttpClient에 비해 상당히 직관적이고 사용하기도 편리하다. 동기와 비동기 처리를 아주 쉽게 할 수 있다는 점이 인상적이다</p>
<p><strong>[OkHttp 장점]</strong></p>
<p>1. Interceptor 처리하는 것이 편하다.</p>
<ul>
<li>Application Interceptors : Application과 OKHttp 사이에 Requests, Responses 정보를 intercept하여 추가적으로 처리. (예: Request시 추가적인 비즈니스 로직을 공통적으로 수행해야 되는경우 로그 등)</li>
<li>Network Interceptors : Network와 OkHttp 사이에 Requests, Responses 정보를 intercept하여 추가적으로 처리. (예: Network의 Responses 정보를 보고 retry할지 여부 등)</li>
</ul>
<ol start="2">
<li>기본 설정값 : OKHttp는 강력한 기본값들이 잘 설정되어 있다. (사용자가 관련 내용을 수정할 수 있다)</li>
</ol>
<p>3. Retofit은 OkHttp위에서 돈다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Retrofit]]></title>
            <link>https://velog.io/@ha_jni/Retrofit</link>
            <guid>https://velog.io/@ha_jni/Retrofit</guid>
            <pubDate>Mon, 09 Aug 2021 09:22:51 GMT</pubDate>
            <description><![CDATA[<h3 id="retrofit">Retrofit?</h3>
<p>Retrofit은 REST API로, 서버와 클라이언트간 Http 통신을 위한 인터페이스를 뜻한다.</p>
<p>쉽게 말해, 클라이언트에서 서버로 어떠한 요청을 보내면 서버는 그 요청에 대한 응답을 클라이언트로 보내주게 되는데, 이 일련의 과정들을 쉽게 사용 할 수 있도록 도와주는 역할을 하는 것이 바로 Retrofit이다.</p>
<p>Retrofit은 TypeSafe한 HttpClient라이브러리 이다.</p>
<ul>
<li>Retrofit2는 Square사에서 만든 http통신을 간편하게 만들어주는 라이브러리 입니다.</li>
<li>구글 샘플에서도 네트워크 통신에 대한 예제를 보여줄 때는 retrofit2를 이용하는 것을 보면 비공식적으로 인정했다고 생각합니다.</li>
</ul>
<h3 id="httpclientlibrary-사용이유">HttpClientLibrary 사용이유</h3>
<ol>
<li><p><strong>Http개발의 어려움</strong></p>
<p> 보통 Http를 개발한다면, 아래의 것들을 고려해야 한다.</p>
<ul>
<li>연결</li>
<li>캐싱</li>
<li>실패한 요청의 재시도</li>
<li>스레딩</li>
<li>응답 분석</li>
<li>오류 처리</li>
</ul>
</li>
<li><p><strong>HttpURLConnection</strong></p>
<p> HttpURLConnection은 가장 원시적인 방법의 HttpClient이다.</p>
<p> 장점</p>
<ul>
<li><p>Java.net에 포함된 클래스로 별도의 라이브러리 추가가 필요없다.</p>
</li>
<li><p>자신이 원하는 방식으로 커스텀하여 사용할 수 있다.(단점이기도 하다.)</p>
<p>단점</p>
</li>
<li><p>자유도가 높은 대신, 직접 구현해야하는 것들이 많다.</p>
</li>
</ul>
</li>
</ol>
<p>Retrofit 은 안드로이드 앱에서 필요한 데이터를 서버로부터 가져오고 서버에 데이터를 전송하기 위한 코드를 작성할 때 사용하는 라이브러리이다.</p>
<p>즉, <strong>개발자가 서버와 통신하기 위한 코드를 작성하기 쉽게 라이브러리</strong>로 만들어 놓았기 때문에 사용</p>
<h3 id="retrofit-사용"><strong>Retrofit 사용</strong></h3>
<p>Retrofit 을 사용하기 위해서는 다음의 세 가지 클래스가 필요</p>
<ul>
<li><strong>JSON 형태의 모델 클래스</strong> (kotlin 에서는 data class 를 사용)</li>
<li><strong>HTTP 작업을 정의</strong>하는(onSuccess/onFail) 인터페이스</li>
<li><strong>Retrofit.Builder</strong>를 선언한 클래스 (baseUrl과 Converter등을 선언한다. Interceptor를 추가하여 응답을 가공할수도 있다.)</li>
</ul>
<ol>
<li><p>Gradle에 라이브러리 추가</p>
<p> Retrofit을 사용하기 위해서 먼저, 라이브러리를 Gradle에 추가</p>
<pre><code class="language-java"> build.gradle (Module: app)

 dependencies {
    compile &#39;com.squareup.retrofit2:retrofit:2.4.0
    compile &#39;com.squareup.retrofit2:converter-gson:2.4.0&#39; (optional)
 }</code></pre>
</li>
<li><p>인터넷 사용 권한 추가</p>
<p> 통신을 위해서는 인터넷 사용 권한이 필요</p>
<pre><code class="language-java"> manifests.xml
 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
 &lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
     package=&quot;rebuild.com.retrofit&quot;&gt;

     &lt;!-- 인터넷 사용 권한 --&gt;
     &lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; /&gt;

 &lt;/manifest&gt;</code></pre>
</li>
<li><p>Retrofit 객체 초기화</p>
<pre><code class="language-java"> MainActivity.java
 import retrofit2.Retrofit;

 public class MainActivity extends AppCompatActivity {

    private Retrofit mRetrofit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
             setRetrofitInit();
    }

    privatate void setRetrofitInit() {
       mRetrofit = new Retrofit.Builder()
                  .baseUrl(&lt;Base URL&gt;)
                  .addConverterFactory(GsonConverterFactory.create())
                  .build();
    } 
 }</code></pre>
</li>
<li><p>인터페이스 정의</p>
<p> Http Method(GET, POST, PUT, DELETE 등)와 자원의 정보를 정의 할 인터페이스를 구현</p>
<pre><code class="language-java"> RetrofitAPI.java

 public interface RetrofitAPI {
    @GET(&quot;/movie.json&quot;)
    Call&lt;String&gt; getMovieList();
 }

 //Http Method만을 정의하였으나, 필요에 따라 변수들을 포함하여 정의 하면 됨</code></pre>
</li>
<li><p>통신 요청 및 응답 콜백 구현</p>
<p> 다시 MainActivity로 돌아와 Retrofit 객체와 인터페이스를 연결하고, 영화 리스트 데이터를 요청</p>
<pre><code class="language-java">  MainActivity.java

 public class MainActivity extends AppCompatActivity {
    private Retrofit mRetrofit;
    private RetrofitAPI mRetrofitAPI;
    private Call&lt;String&gt; mCallMovieList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

                 setRetrofitInit();
                 callMovieList();

    }

    privatate void setRetrofitInit() {
       mRetrofit = new Retrofit.Builder()
                  .baseUrl(&lt;Base URL&gt;)
                  .addConverterFactory(GsonConverterFactory.create())
                  .build();

       mRetrofitAPI = mRetrofit.create(RetrofitAPI.class);
    }

    privatate void callMovieList() {
       mCallMoviewList = mRetrofitAPI.getMoviewList();
       mCallMoviewList.enqueue(mRetrofitCallback);
    }

    private Callback&lt;String&gt; mRetrofitCallback = new Callback&lt;String&gt;() {

       @Override
       public void onResponse(Call&lt;String&gt; call, Response&lt;String&gt; response) {
          String result = response.body();
          Log.d(TAG, result)
       }

       @Override
       public void onFailure(Call&lt;String&gt; call, Throwable t) {
          t.printStackTrace();
       }
    }
 }</code></pre>
<p> 생성해두었던 인터페이스를 통해, Http 요청을 보내고 응답을 받을 Callback을 지정.
 이제 통신이 정상적으로 성공할 경우 onResponse()로, 통신에 실패 한 경우에는 onFailure()로 들어오게 된다.</p>
</li>
<li><p>데이터 파싱</p>
<p> 이제 받아온 String 타입의 데이터를 사용하기 쉽도록 바꾸어 보도록 한다.</p>
<p> 우선, 응답 받을 json 데이터에 맞추어 VO(Value Object)를 작성</p>
<pre><code class="language-java"> MovieListVO.java

 public class MovieListVO {
    private String category;
    private ArrayList&lt;Movie&gt; list;

    public String getCategory() {
       return category;
    }

    public void setCategory(String category) {
       this.category = category;
    }

    public ArrayList&lt;Movie&gt; getList() {
       return list;
    }

    public void setList(ArrayList&lt;Movie&gt; list) {
       this.list = list;
    }

    public class Movie {
       private String movie_no;
       private String title;
       private String thumb_url;

       public String getMovie_no() {
          return movie_no;
       }

       public void setMovie_no(String movie_no) {
          this.movie_no = movie_no;
       }

       public String getTitle() {
          return title;
       }

       public void setTitle(String title) {
          this.title = title;
       }

       public String getThumb_url() {
          return thumb_url;
       }

       public void setThumb_url(String thumb_url) {
          this.thumb_url = thumb_url;
       }
    }
 }</code></pre>
<p> 중요한 점은 응답 받을 데이터 구조와 같은 구조이어야 하며 변수 명도 같아야 한다.</p>
<p> 아까 응답 받은 데이터에 적용 시키면, 다음과 같다.</p>
<pre><code class="language-java"> MainActivity.java

 public class MainActivity extends AppCompatActivity {
    private Gson mGson;

    private Callback&lt;String&gt; mRetrofitCallback = new Callback&lt;String&gt;() {

       @Override
       public void onResponse(Call&lt;String&gt; call, Response&lt;String&gt; response) {
          String result = response.body();
          MovieListVO mMovieListVO = (MovieListVO) mGson.fromJson(result, MovieListVO.class)
       }

       @Override
       public void onFailure(Call&lt;String&gt; call, Throwable t) {
          t.printStackTrace();
       }
    }
 }
 //Gson 객체를 초기화 시킨 후, fromJson()을 통해 변환해주시면 된다.
 //  이후에는, 원하시는 모양으로 데이터를 가져다가 사용</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Transition]]></title>
            <link>https://velog.io/@ha_jni/Transition</link>
            <guid>https://velog.io/@ha_jni/Transition</guid>
            <pubDate>Mon, 09 Aug 2021 09:22:09 GMT</pubDate>
            <description><![CDATA[<h3 id="transition">Transition</h3>
<p>트랜지션은 장면이 전환될 때의 애니메이션에 대한 정보를 담고 있는 추상 클래스</p>
<p><strong>Transition 방식은 SDKVersion 21 이상부터 지원</strong>하기 때문에 그 이하의 버전에서는 사용이 불가능</p>
<p><strong>Transitions API</strong></p>
<ul>
<li><strong>Transitions API구글은 액티비티간 화면 전환을 위해 Android 5.0부터 이 API를 제공</strong></li>
<li><strong>좋은 소식은 더 아래 버전에서도 사용할 수 있다는 점. <a href="https://github.com/andkulikov/Transitions-Everywhere">Transitions Everywhere</a>는 안드로이드 Transition API의 백포트입니다.(안드로이드 4.0이상 애니메이션 백포트 지원, 안드로이드 2.2이상 API호환가능)</strong></li>
</ul>
<pre><code class="language-java">dependencies {
    compile &quot;com.andkulikov:transitionseverywhere:1.6.5&quot;
}</code></pre>
<ol>
<li><strong>VISIBLE</strong></li>
</ol>
<p><img src="https://images.velog.io/images/ha_jni/post/74ab8566-df94-4fb1-a727-8e95e11929e1/Visible1.gif" alt="">
    ```java
    TransitionManager.beginDelayedTransition(layoutFrame);</p>
<pre><code>            if(text.getVisibility() == View.VISIBLE){
                text.setVisibility(View.GONE);
            } else {
                text.setVisibility(View.VISIBLE);
            }

```</code></pre><ol start="2">
<li><strong>SLIDE</strong></li>
</ol>
<p><img src="https://images.velog.io/images/ha_jni/post/a048c579-7b74-4597-86af-ef21cf853250/Slide2.gif" alt=""></p>
<pre><code>```java
TransitionManager.beginDelayedTransition(layoutFrame, new Slide(Gravity.RIGHT));

            if(text.getVisibility() == View.VISIBLE){
                text.setVisibility(View.GONE);
            } else {
                text.setVisibility(View.VISIBLE);
            }

```</code></pre><ol start="3">
<li><strong>SCALE</strong></li>
</ol>
<p><img src="https://images.velog.io/images/ha_jni/post/e293f4c3-b925-413b-a4e3-975e0bbc91ab/Scale.gif" alt=""></p>
<pre><code>```java
TransitionSet set = new TransitionSet()
                    .addTransition(new Scale(0.7f))
                    .addTransition(new Fade());

            if(text.getVisibility() == View.VISIBLE){
                set.setInterpolator(new FastOutLinearInInterpolator());
                TransitionManager.beginDelayedTransition(layoutFrame, set);
                text.setVisibility(View.INVISIBLE);
            } else {
                set.setInterpolator(new LinearOutSlowInInterpolator());
                TransitionManager.beginDelayedTransition(layoutFrame, set);
                text.setVisibility(View.VISIBLE);
            }

```</code></pre><ol start="4">
<li><p><strong>RECOLOR</strong>
<img src="https://images.velog.io/images/ha_jni/post/05425115-c525-4a84-b639-3f27aa1ce184/Recolor.gif" alt=""></p>
<pre><code class="language-java"> TransitionManager.beginDelayedTransition(layoutFrame, new Recolor());

             if(button.getCurrentTextColor() == getResources().getColor(R.color.colorPrimary)){
                 button.setTextColor(getResources().getColor(R.color.colorAccent));
                 button.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
             } else {
                 button.setTextColor(getResources().getColor(R.color.colorPrimary));
                 button.setBackgroundColor(getResources().getColor(R.color.colorAccent));
             }</code></pre>
</li>
<li><p><strong>ROTATE</strong></p>
</li>
</ol>
<p><img src="https://images.velog.io/images/ha_jni/post/35cb7b6b-4158-4f79-ae95-78e64cc2e689/Rotate.gif" alt=""></p>
<pre><code>```java
TransitionManager.beginDelayedTransition(layoutFrame, new Rotate());
            if(image.getRotation() == 0){
                image.setRotation(90);
            } else {
                image.setRotation(0);
            }
```</code></pre><ol start="6">
<li><strong>CHANGE_TEXT</strong></li>
</ol>
<p><img src="https://images.velog.io/images/ha_jni/post/d86e882b-c8e1-447a-bd8c-836496902dec/Change_text.gif" alt=""></p>
<pre><code>```java
TransitionManager.beginDelayedTransition(layoutFrame,
                    new ChangeText().setChangeBehavior(ChangeText.CHANGE_BEHAVIOR_OUT_IN));

            if(text.getText().equals(&quot;Hello&quot;)){
                text.setText(&quot;World&quot;);
            } else {
                text.setText(&quot;Hello&quot;);
            }
```</code></pre><ol start="7">
<li><strong>PROGRESS_TRANSITION</strong></li>
</ol>
<p><img src="https://images.velog.io/images/ha_jni/post/edee2b60-e858-487d-80eb-4d241a9a4fb9/Progress_transition.gif" alt=""></p>
<pre><code>```java
private class ProgressTransition extends Transition {

    /**
     * Property is like a helper that contain setter and getter in one place
     */
    private static final Property&lt;ProgressBar, Integer&gt; PROGRESS_PROPERTY = 
        new IntProperty&lt;ProgressBar&gt;() {

        @Override
        public void setValue(ProgressBar progressBar, int value) {
            progressBar.setProgress(value);
        }

        @Override
        public Integer get(ProgressBar progressBar) {
            return progressBar.getProgress();
        }
    };

    /**
      * Internal name of property. Like a intent bundles 
      */
    private static final String PROPNAME_PROGRESS = &quot;ProgressTransition:progress&quot;;

    @Override
    public void captureStartValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    @Override
    public void captureEndValues(TransitionValues transitionValues) {
        captureValues(transitionValues);
    }

    private void captureValues(TransitionValues transitionValues) {
        if (transitionValues.view instanceof ProgressBar) {
            // save current progress in the values map
            ProgressBar progressBar = ((ProgressBar) transitionValues.view);
            transitionValues.values.put(PROPNAME_PROGRESS, progressBar.getProgress());
        }
    }

    @Override
    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, 
            TransitionValues endValues) {
        if (startValues != null &amp;&amp; endValues != null &amp;&amp; endValues.view instanceof ProgressBar) {
            ProgressBar progressBar = (ProgressBar) endValues.view;
            int start = (Integer) startValues.values.get(PROPNAME_PROGRESS);
            int end = (Integer) endValues.values.get(PROPNAME_PROGRESS);
            if (start != end) {
                // first of all we need to apply the start value, because right now
                // the view is have end value
                progressBar.setProgress(start);
                // create animator with our progressBar, property and end value
                return ObjectAnimator.ofInt(progressBar, PROGRESS_PROPERTY, end);
            }
         }
         return null;
    }
}

// ------------------------------------------------------------------------------------

ProgressTransition progressTransition = new ProgressTransition();
            progressTransition.setDuration(1000);
            TransitionManager.beginDelayedTransition(layoutFrame, progressTransition);
            progressBar.setProgress(100);

```</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Animation]]></title>
            <link>https://velog.io/@ha_jni/Animation</link>
            <guid>https://velog.io/@ha_jni/Animation</guid>
            <pubDate>Mon, 09 Aug 2021 09:19:19 GMT</pubDate>
            <description><![CDATA[<h3 id="animation">Animation</h3>
<p>연산을 통해서 Animation효과를 만드는 방법을 말한다.</p>
<p>쉽게 풀어서 얘기하면 view의 위치를 이동, 회전, 확대/축소, 투명도 조절을 할 수 있다.</p>
<ol>
<li><p>투명 애니메이션 (alpha)</p>
<p> alpha값을 설정하여,</p>
<p> 대상의 투명도의 변화를 애니메이션으로 구현</p>
<pre><code class="language-java"> // 투명 상태에서 1초 동안 서서히 모습이 드러나는 애니메이션

 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
 &lt;alpha xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
     android:interpolator=&quot;@android:anim/linear_interpolator&quot;
     android:duration=&quot;1000&quot;
     android:fromAlpha=&quot;0.0&quot;
     android:toAlpha=&quot;1.0&quot;&gt;
 &lt;/alpha&gt;</code></pre>
<p> <strong>uration</strong> : 실행 시간</p>
<p> <strong>fromAlpha</strong> : 시작 투명도 (0 = 완전히 투명)</p>
<p> <strong>toAlpha</strong> : 끝 투명도 (1.0 = 완전히 보임)</p>
<p> Alpha 값은 0 ~ 1.0 사이에서 조절 가능</p>
</li>
<li><p>확장 애니메이션 (scale)</p>
<p> scale값을 설정하여</p>
<p> 대상의 크기를 확대하거나 축소하는 애니메이션을 구현할 수 있다.</p>
<pre><code class="language-java"> // 1초 동안 크기 0에서 2배 크기로 확대되는 애니메이션
 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
 &lt;scale xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
     android:interpolator=&quot;@android:anim/linear_interpolator&quot;
     android:duration=&quot;1000&quot;
     android:pivotX=&quot;50%&quot;
     android:pivotY=&quot;50%&quot;
     android:fromXScale=&quot;0.0&quot;
     android:toXScale=&quot;2.0&quot;
     android:fromYScale=&quot;0.0&quot;
     android:toYScale=&quot;2.0&quot;&gt;
 &lt;/scale&gt;</code></pre>
<p> <strong>duration</strong> : 실행 시간</p>
<p> <strong>pivotX</strong> : X축 값 (50% = X축 중앙)</p>
<p> <strong>pivotY</strong> : Y축 값 (50% = Y축 중앙)</p>
<p> <strong>fromXScale</strong> : 시작 X축 크기 (0.0 = 0)</p>
<p> <strong>toXScale</strong> : 끝 X축 크기 (2.0 = 두 배 크기)</p>
<p> <strong>fromYScale</strong> : 시작 Y축 크기 (0.0 = 0)</p>
<p> <strong>toYScale</strong> : 끝 Y축 크기 (2.0 = 두 배 크기)</p>
</li>
<li><p>이동 애니메이션 (translate)</p>
<p> X, Y Delta값을 설정하여</p>
<p> 대상을 상하좌우로 이동하는 애니메이션을 구현</p>
<pre><code class="language-java"> // 1초 동안 동남쪽으로 이동하는 애니메이션
 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
 &lt;translate xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
     android:interpolator=&quot;@android:anim/linear_interpolator&quot;
     android:duration=&quot;1000&quot;
     android:fromXDelta=&quot;0&quot;
     android:toXDelta=&quot;100%&quot;
     android:fromYDelta=&quot;0&quot;
     android:toYDelta=&quot;200%&quot;&gt;
 &lt;/translate&gt;</code></pre>
<p> <strong>duration</strong> : 실행 시간</p>
<p> <strong>fromXDelta</strong> : 시작 X축 값 (0 = 처음 자리)</p>
<p> <strong>toXDelta</strong> : 끝 X축 값 (100% = 오른쪽으로 100만큼 이동)</p>
<p> <strong>fromYDelta</strong> : 시작 Y축 값 (0 = 처음 자리)</p>
<p> <strong>toYDelta</strong> : 끝 Y축 값 (200% = 아래쪽으로 200만큼 이동)</p>
</li>
<li><p>회전 애니메이션 (rotate)</p>
<p> X, Y pivot 값과, Degree값을 설정하여</p>
<p> 대상을 회전하는 애니메이션을 구현할 수 있다.</p>
<pre><code class="language-java"> // 1초 동안 360도 회전하는 애니메이션
 &lt;rotate xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
     android:interpolator=&quot;@android:anim/linear_interpolator&quot;
     android:duration=&quot;1000&quot;
     android:pivotX=&quot;50%&quot;
     android:pivotY=&quot;50%&quot;
     android:fromDegrees=&quot;0&quot;
     android:toDegrees=&quot;360&quot;&gt;
 &lt;/rotate&gt;</code></pre>
<p> <strong>duration</strong> : 실행 시간</p>
<p> <strong>pivotX</strong> : X축 중심값 (50% = X축 중앙)</p>
<p> <strong>pivotY</strong> : Y축 중심값 (50% = Y축 중앙)</p>
<p> <strong>fromDegrees</strong> : 시작 각도 (0 : 회전 0도)</p>
<p> <strong>toDegrees</strong> : 끝 각도 (360 : 회전 360도)</p>
</li>
<li><p>복합 세트 애니메이션 (set)</p>
<p> <strong><set></strong> 태그를 사용하여 여러 애니메이션을 셋뚜셋뚜로 구현할 수도 있다.</p>
<p> 애니메이션이 동시에 실행된다.</p>
<p> 예상하던 결과와 다를 수 있기 때문에 세심한 작업이 필요</p>
<pre><code class="language-java"> // 1초 동안 투명에서 불투명으로 되면서, 크기가 2배로 커지고,
 // 동남쪽으로 이동하면서, 360도 도는 애니메이션
 &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
 &lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
     android:interpolator=&quot;@android:anim/linear_interpolator&quot;&gt;
     &lt;alpha
         android:duration=&quot;1000&quot;
         android:fromAlpha=&quot;0.0&quot;
         android:toAlpha=&quot;1.0&quot; /&gt;
     &lt;scale
         android:duration=&quot;1000&quot;
         android:pivotX=&quot;50%&quot;
         android:pivotY=&quot;50%&quot;
         android:fromXScale=&quot;0.0&quot;
         android:toXScale=&quot;2.0&quot;
         android:fromYScale=&quot;0.0&quot;
         android:toYScale=&quot;2.0&quot; /&gt;
     &lt;translate
         android:duration=&quot;1000&quot;
         android:fromXDelta=&quot;0&quot;
         android:toXDelta=&quot;100%&quot;
         android:fromYDelta=&quot;0&quot;
         android:toYDelta=&quot;200%&quot;/&gt;
     &lt;rotate
         android:duration=&quot;1000&quot;
         android:pivotX=&quot;50%&quot;
         android:pivotY=&quot;50%&quot;
         android:fromDegrees=&quot;0&quot;
         android:toDegrees=&quot;360&quot;&gt;
     &lt;/rotate&gt;
 &lt;/set&gt;</code></pre>
</li>
<li><p>interpolator</p>
<p> 각 애니메이션은 interpolator 값을 설졍할 수 있다.</p>
<p> interpolator는 한글로 번역하면 더 어려워진다.</p>
<p> 간단히 부속 속성이라고 보면 편하다.</p>
<p> 예를 들어 가장 기본인 linear_interpolator 를 사용하면 애니메이션 등속으로 진행되고 accelerate_interpolator 를 사용하면, 애니메이션 진행에 가속이 붙어서 처음에는 느리다가 빠르게 진행된다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Project structure]]></title>
            <link>https://velog.io/@ha_jni/Project-structure</link>
            <guid>https://velog.io/@ha_jni/Project-structure</guid>
            <pubDate>Mon, 09 Aug 2021 09:18:48 GMT</pubDate>
            <description><![CDATA[<p>Android view에서 보여지는 구조들은 실제 Directory 구조와 다릅니다. 개발편의를 위해서 필요한 폴더들만 보여주고 있다.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/9aedca01-1473-4a0f-8bcd-c6e8311863fa/image.png" alt=""></p>
<p>프로젝트의 모든 빌드 관련 구성 파일을 최상위 Gradle Script 그룹에 표시</p>
<p><strong>Gradle Scripts</strong> : 그룹은 build와 관련된 파일들이 있습니다.</p>
<p><strong>app :</strong> 그룹은 app의 source 등을 포함하고 있고, 하위에 다음과 같은 내용을 보여줍니다.</p>
<p><strong><em>manifest :</em></strong> AndroidManifest.xml 파일을 포함합니다. App의 Spec을 보여준다고 생각하시면 됩니다.</p>
<p><strong><em>java :</em></strong> Java source 및 Junit 파일들을 포함합니다.</p>
<p><strong><em>res :</em></strong> 코드가 아닌 모든 Resource(Image, layout, String 등) 파일들을 포함합니다.</p>
<h3 id="project-view">Project view</h3>
<p>숨겨진 모든 파일을 비롯한 프로젝트의 실제 파일 구조를 보려면 <strong>Project</strong> 창의 상단 드롭다운에서 <strong>Project</strong>를 선택한다.</p>
<p><strong>Project</strong> 뷰를 선택하면 훨씬 많은 파일과 디렉터리를 볼 수 있다.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/bb127051-dcb6-4585-a6d0-bdba5af0fc65/image.png" alt=""></p>
<p><strong><em>build/ :</em></strong> Build output 파일들이 있습니다.</p>
<p><strong><em>libs/ :</em></strong> Private library(비공개 라이브러리)들이 있습니다.</p>
<p><strong><em>src/ :</em></strong> 이 모듈에 대한 모든 code 및 resource 파일들이 있습니다.</p>
<ul>
<li><strong><em>androidTest/ :</em></strong> Android 기기에서 실행되는 instrumentation 테스트에 대한 code가 있습니다. 자세한 내용은 <a href="https://developer.android.com/studio/test/index.html">Android 테스트 문서</a>를 참조하세요.</li>
<li><strong><em>main/ :</em></strong> main source 파일이 있습니다. main source는 Android code 및 resource가 있습니다.<ul>
<li><strong><em>AndroidManifest.xml :</em></strong> 애플리케이션의 특성 및 각 구성 요소를 설명합니다. 자세한 내용은 <a href="https://developer.android.com/guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a> 문서를 참조하세요.</li>
<li><strong><em>java/ :</em></strong> Java source가 있습니다.</li>
<li><strong><em>jni/ :</em></strong> JNI(Java Native Interface)를 사용하는 native code가 있습니다. 자세한 내용은 <a href="https://developer.android.com/ndk/index.html">Android NDK</a> 문서를 참조하세요.</li>
<li><strong><em>gen/ :</em></strong> Android Studio에서 생성하는 Java 파일(예: R.java 파일) 및 AIDL 파일에서 생성되는 인터페이스들이 있습니다.</li>
<li><strong><em>res/ :</em></strong> 애플리케이션 resource(예: drawable, layout file, UI string 등)가 있습니다. 자세한 내용은 <a href="https://developer.android.com/guide/topics/resources/index.html">애플리케이션 리소스</a>를 참조하세요.</li>
<li><strong><em>assets/ :</em></strong> .apk 파일 안에 함께 컴파일되어야 하는 파일들이 있습니다. 이 위치의 파일들은 <a href="https://developer.android.com/reference/android/content/res/AssetManager.html">AssetManager</a> 를 통하여 사용할 수 있습니다.</li>
</ul>
</li>
<li><strong><em>test/ :</em></strong> 호스트 JVM에서 실행되는 로컬 테스트의 코드가 있습니다.</li>
</ul>
<p><strong><em>build.gradle(모듈) :</em></strong> 모듈별 빌드 구성을 정의합니다.</p>
<p><strong><em>build.gradle(프로젝트) :</em></strong> 모든 모듈에 적용되는 빌드 구성을 정의합니다. 이 파일은 프로젝트 내의 모든 모듈의 build에 영향을 주기 때문에 version 관리를 잘 해야 합니다. 자세한 내용은 <a href="https://developer.android.com/studio/build/index.html">빌드 구성</a>을 참조하세요.</p>
<p><strong>Gradle Scripts <em>:</em></strong> 그룹은 build와 관련된 파일들이 있습니다.<strong>app</strong> 그룹은 app의 source 등을 포함하고 있고, 하위에 다음과 같은 내용을 보여줍니다.</p>
<p><strong><em>manifest :</em></strong> AndroidManifest.xml 파일을 포함합니다. App의 Spec을 보여준다고 생각하시면 됩니다.</p>
<p><strong><em>java :</em></strong> Java source 및 Junit 파일들을 포함합니다.</p>
<p><strong><em>res :</em></strong> 코드가 아닌 모든 Resource(Image, layout, String 등) 파일들을 포함합니다.</p>
<h3 id="androidmanifestxml">AndroidManifest.xml</h3>
<p>App source를 볼 때 가장 먼저 확인해야 할 것은 AndroidManifest.xml이다. 이 파일은 App의 모든 설정 및 component들이 정의되어 있다. Device에 App이 설치될 때 Device는 이 파일을 보고 App에 어떤 내용이 있는지 알게 된다. 만약 이 파일에 Activity를 정의하지 않으면 Device가 알지 못해 실행할 수 없게 된다. 이 파일은 manifests 폴더 아래에 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Resources]]></title>
            <link>https://velog.io/@ha_jni/Resources</link>
            <guid>https://velog.io/@ha_jni/Resources</guid>
            <pubDate>Mon, 09 Aug 2021 09:18:02 GMT</pubDate>
            <description><![CDATA[<p>소스코드 뿐만 아니라 다양한 리소스를 활용하여 프로그래밍할 수 있도록, 상당히 유연한 어플리케이션 구조 보유</p>
<p>소스코드 + 다양한 리소스</p>
<ul>
<li>소스코드의 가독성을 높여 유지보수하기가 쉬움.</li>
<li>다양한 언어와 지역에 따라 리소스를 분리하여 관리함으로써 안드로이드 어플리케이션의 현지화(Localization)를 쉽게함.</li>
<li>모바일 기기의 특성에 따라 리소스를 분리하여 관리함으로써 다양한 모바일 기기의 특성에 따라 안드로이드 어플리케이션의 확장 및 적용을 쉽게 함.</li>
</ul>
<h3 id="1-resource-개요">1. Resource 개요</h3>
<p><img src="https://images.velog.io/images/ha_jni/post/64fe5e8b-8a33-4f8f-af05-6ce373163d1b/image.png" alt="">
<strong>소스코드</strong></p>
<ul>
<li>해당 어플리케이션이 동작해서 처리하고자 하는 기능을 가리킨다.</li>
</ul>
<p><strong>리소스</strong></p>
<ul>
<li>해당 어플리케이션이 동작할 때 사용하는 텍스트 문자열, 이미지, 아이콘, 오디오, 동영상 등을 가리킨다.</li>
<li>안드로이드 어플리케이션은 리소스를 좀 더 적극적으로 활용한다.</li>
<li>즉, 레이아웃이나 메뉴, 심지어 간단한 그래픽 도형마저도 소스코드가 아닌 리소스를 통해 구현할 수 있게 한다.</li>
</ul>
<h3 id="안드로이드-어플리케이션의-리소스-관리">안드로이드 어플리케이션의 리소스 관리</h3>
<ul>
<li>안드로이드 어플리케이션에서 리소스는 /res 디렉토리 안에서 관리된다.</li>
<li>각 리소스 종류에 따라 /res 디렉토리 밑에 다양한 하위 디렉토리를 정의하여 관리된다.</li>
</ul>
<h3 id="안드로이드-어플리케이션이-관리하는-리소스-종류">안드로이드 어플리케이션이 관리하는 리소스 종류</h3>
<p><img src="https://images.velog.io/images/ha_jni/post/36fd1214-96f9-4e1c-bff9-22700312ef86/image.png" alt=""></p>
<h3 id="2-기본-resource--정의-및-사용법">2. 기본 Resource  정의 및 사용법</h3>
<blockquote>
<p>문자열 Resource  정의 및 사용방법</p>
</blockquote>
<p><img src="https://images.velog.io/images/ha_jni/post/4372e3d3-8306-4361-883a-1afe4bd903b1/image.png" alt=""></p>
<p>문자열, 문자열 배열, 색상, 크기 같은 단순 리소스 정보들은 모두 <code>/res/values</code> 디렉토리 밑에 XML 파일로 정의 <resource> 루트 요소 밑으로 각각의 리소스를 정의</p>
<ul>
<li>문자열 리소스튼 <code>/res/values</code> 디렉토리 밑에 strings.xml을 통해 관리된다.</li>
<li>이 때 반드시 strings.xml일 필요는 없으며, <resources>를 루트 요소로 정의한 어떤 XML 파일이어도 상관없으나 XML 리소스 파일은 반드시 <code>/res/values</code> 디렉토리에 있어야 한다.</li>
<li>문자열 리소스를 정의하기 위한 XML 요소는 <string> 이다.</li>
</ul>
<p><strong>문자열 이름</strong></p>
<ul>
<li>name 속성 지정</li>
<li>어플리케이션 내에서 전역 변수처럼 사용되므로, 식별자로써 역할을 할 수 있게 고유해야 한다.</li>
</ul>
<p><strong>문자열 값</strong></p>
<ul>
<li><string> 요소의 내용으로 지정</li>
</ul>
<pre><code class="language-java">// 예제
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
    &lt;string name=&quot;app_name&quot;&gt;Hello Android&lt;/string&gt;
    &lt;string name=&quot;action_settings&quot;&gt;Settings&lt;/string&gt;
    &lt;string name=&quot;hello_world&quot;&gt;Hello Android!&lt;/string&gt;
&lt;/resources&gt;</code></pre>
<blockquote>
<p>문자열 배열 Resource</p>
</blockquote>
<p>문자열 배열 리소스는 <code>/res/values/</code> 디렉토리 밑에 arrays.xml을 통해 관리</p>
<p>이 때 반드시 arrays.xml일 필요는 없으며, <resources>를 루트 요소로 정의한 어떤 XML 파일이어도 상관없으나 XML 리소스 파일은 반드시 /res/values 디렉토리에 있어야 한다.</p>
<p>문자열 리소스를 정의하기 위한 XML 요소는 <string-array> 이다.</p>
<p><strong>문자열 배열 이름</strong></p>
<ul>
<li>name 속성 지정</li>
<li>어플리케이션 내에서 전역 변수처럼 사용되므로, 식별자로써 역할을 할 수 있게 고유해야 한다.</li>
</ul>
<p><strong>문자열 배열의 각 문자열 값</strong></p>
<ul>
<li><string-array> 밑에 <item>들로 지정</li>
</ul>
<pre><code class="language-java">// 예시
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
    &lt;string-array name=&quot;company&quot;&gt;
        &lt;item&gt;Samsung&lt;/item&gt;
        &lt;item&gt;Hyundai&lt;/item&gt;
        &lt;item&gt;LG&lt;/item&gt;`
        &lt;item&gt;SK&lt;/item&gt;
        &lt;item&gt;KIA&lt;/item&gt;
    &lt;/string-array&gt;
&lt;/resources&gt;</code></pre>
<blockquote>
<p>색상 Resource</p>
</blockquote>
<p>색상 리소스는 <code>/res/values</code> 디렉토리 밑에 colors.xml을 통해 관리된다.</p>
<p>이 때 반드시 colors.xml일 필요는 없으며, <resources>를 루트 요소로 정의한 어떤 XML 파일이어도 상관없으나 XML 리소스 파일은 반드시 /res/values 디렉토리에 있어야 한다.</p>
<p>색상 리소스를 정의하기 위한 XML 요소는 <color> 이다.</p>
<p><strong>색상 이름</strong></p>
<ul>
<li>name 속성 지정</li>
<li>어플리케이션 내에서 전역 변수처럼 사용되므로, 식별자로써 역할을 할 수 있게 고유해야 한다.</li>
</ul>
<p><strong>색상 값</strong></p>
<ul>
<li><p><color> 요소의 내용으로 지정</p>
</li>
<li><p>비트 수 와 알파(투명도) 여부에 따라서 다음과 같이 네 가지 형태로 정의된다. (각각의 색상을 결정하는 인자 값은 16진수로 정의된다.)</p>
<p>  #RGB               ex&gt; #F00, 12비트 빨강</p>
<p>  #ARGB             ex&gt; #8F00, 12비트 투명도 50% 빨강</p>
<p>  #RRGGBB         ex&gt; #FF0000, 24비트 빨강</p>
<p>  #AARRGGBB     ex&gt; #80FF0000, 24비트 투명도 50% 빨강</p>
<pre><code class="language-java">  // 예시
  &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
  &lt;resources&gt;
     &lt;color name=&quot;my_red_color&quot;&gt;#FF2222&lt;/color&gt;
  &lt;/resources&gt;</code></pre>
</li>
</ul>
<blockquote>
<p>크기 resource</p>
</blockquote>
<p>크기 리소스는 <code>/res/values</code> 디렉토리 밑에 dimens.xml 을 통해 관리된다.</p>
<p>이 때 반드시 dimens.xml일 필요는 없으며, <resources>를 루트 요소로 정의한 어떤 XML 파일이어도 상관없으나 XML 리소스 파일은 반드시 /res/values/ 디렉토리에 있어야 한다.</p>
<p>크기 리소스를 정의하기 위한 XML 요소는 <dimen> 이다.</p>
<p><strong>크기 이름</strong></p>
<ul>
<li>name 속성 지정</li>
<li>어플리케이션 내에서 전역 변수처럼 사용되므로, 식별자로써 역할을 할 수 있게 고유해야 한다.</li>
</ul>
<p><strong>크기 값</strong></p>
<ul>
<li><dimen> 요소의 내용으로 지정</li>
<li>다양한 크기 단위들에 따라 크게 여섯 가지 형태로 정의된다.</li>
</ul>
<p><img src="https://images.velog.io/images/ha_jni/post/b5fd1d7b-e528-428e-a506-b94186b0f41d/image.png" alt=""></p>
<pre><code class="language-java">// 예시
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
   &lt;dimen name=&quot;my_wide_size&quot;&gt;20px&lt;/dimen&gt;
&lt;/resources&gt;</code></pre>
<blockquote>
<p>이미지 Resource</p>
</blockquote>
<p>이미지 파일들은 <code>/res/drawable</code> 디렉토리 밑에서 관리된다.</p>
<p>이미지 파일을 해당 안드로이드 어플리케이션의 리소스에 등록하려면 단순히 <code>/res/drawable</code> 디렉토리에 추가하기만 하면 된다.</p>
<p><img src="https://images.velog.io/images/ha_jni/post/d513b0e6-1282-400d-93c3-7a6d161b0e19/image.png" alt=""></p>
<p><strong>9-patch PNG</strong></p>
<ul>
<li>이미지를 아홉 조각으로 나눠서 해당 이미지를 확대, 축소할 때 각 조각 별로 비례 여부와 방향이 다르도록 설정된 이미지임.</li>
<li>이는 안드로이드 고유의 이미지 형식으로, draw9patch 라는 툴(안드로이드 SDK 내/tools 디렉토리에 존재)을 사용하여 PNG 파일을 9-patch PNG로 변환할 수 있다.</li>
</ul>
<blockquote>
<p>파일 Resource</p>
</blockquote>
<ol>
<li><p><strong>XML 파일 Resource</strong></p>
<p> 개발자가 정의한 임의의 커스텀 XML 파일을 안드로이드 어플리케이션의 리소스로 포함시킬 수 있다.</p>
<p> 이와 같은 XML 파일들은 <code>/res/xml</code> 디렉토리 밑에서 관리된다.</p>
<p> XML 파일을 해당 안드로이드 어플리케이션의 리소스에 등록하려면 단순히 <code>/res/xml</code> 디렉토리에 추가하기만 하면 된다.</p>
</li>
<li><p><strong>기타 원본 파일 Resource</strong></p>
<p> 안드로이드 어플리케이션의 리소스 : mp3, txt 등 각종 원본 파일 <code>/res/raw</code> 디렉토리 밑에서 관리된다.</p>
<p> 원본파일을 해당 안드로이드 어플리케이션의 리소스에 등록하려면 <code>/res/raw/</code> 디렉토리에 추가하기만 하면 된다.</p>
<p> 기타 원본파일 리소스의 용도 : 안드로이드 어플리케이션의 사운드나 배경음악 등을 위한 오디오 파일들을 저장하는 리소스로 사용된다.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript interface]]></title>
            <link>https://velog.io/@ha_jni/Javascript-interface</link>
            <guid>https://velog.io/@ha_jni/Javascript-interface</guid>
            <pubDate>Mon, 09 Aug 2021 09:16:25 GMT</pubDate>
            <description><![CDATA[<p>하이브리드 앱도 앱이기 때문에 웹 페이지로 구성이 되어있지만 앱의 기능을 어느 정도는 사용하게 됩니다. 그렇기 때문에 앱에서는 웹에서 어떠한 신호를 받기 위한 로직이 필요했으며, 그에 따라 생겨난 것이 Javascript를 통해 데이터를 주고받을 수 있도록 한, Android Bridge Interface</p>
<h3 id="1-webview웹뷰-세팅">1. WebView(웹뷰) 세팅</h3>
<pre><code class="language-java">&lt;span style=&quot;font-size: 14pt;&quot;&gt;
/ 웹뷰 위젯 연결
        webView1 = (WebView)findViewById(R.id.webView1);
        // 클리기 새창 안뜨게...
        webView1.setWebViewClient(new WebViewClient());
        // 세부 세팅객체 가져오기
        WebSettings mWebSettings = webView1.getSettings();
        // 자바스크립트 사용 허용
        // 웹뷰 내에서 자바스크립트 실행해 자바스크립트에서 안드로이드 함수
        // 실행시킬려면 필수로 세팅필요
        mWebSettings.setJavaScriptEnabled(true);
        // 안드로이드에서 제공하는 줌 아이콘을 사용할 수 있도록 설정
        mWebSettings.setBuiltInZoomControls(true);
        // 캐시 사용 여부 설정
        mWebSettings.setAppCacheEnabled(false);

        // 로드할 주소를 입력
        webView1.loadUrl(&quot;http://192.168.0.8:8080/&quot;);
&lt;/span&gt;
</code></pre>
<h3 id="2-자바스크립트에서-호출시-수행할-안드로이드-메서드를-작성">2. 자바스크립트에서 호출시 수행할 안드로이드 메서드를 작성</h3>
<p>자바스크립트와 안드로이드를 중간에서 인터페이스 역할을 할 클래스를 작성해서 그 내부에
메서드를 정의</p>
<pre><code class="language-java">// 인터넷 작업 해야함

&lt;!-- manifest에 인터넷 접속 권한 추가 --&gt;
&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; /&gt;</code></pre>
<p>네트워크 작업은 백그라운드 쓰레드로 해야하며, 백그라운드 쓰레드에서는 메인 뷰의 화면 제어를 할 수 없음으로handler에게 대신해달라고 요청을 해야한다.
앞에선 계속 그렇게 해왔는데 이 2가지를 한번에 하는 것이 handler.post(new Runnable(){ run() }) 을 이용한 방식이다. 이런식으로해서 JavascriptInterface 클래스를 만들도록 한다.</p>
<pre><code class="language-java">public class WebViewActivityextends AppCompatActivity {

    WebView webView1;
    Handler handler =new Handler();
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view);

        // 웹뷰 위젯 연결
        webView1 = (WebView)findViewById(R.id.webView1);
        // 클리기 새창 안뜨게...
        webView1.setWebViewClient(new WebViewClient());
        // 세부 세팅객체 가져오기
        WebSettings mWebSettings = webView1.getSettings();
        // 자바스크립트 사용 허용
        // 웹뷰 내에서 자바스크립트 실행해 자바스크립트에서 안드로이드 함수
        // 실행시킬려면 필수로 세팅필요
        mWebSettings.setJavaScriptEnabled(true);
        // 안드로이드에서 제공하는 줌 아이콘을 사용할 수 있도록 설정
        mWebSettings.setBuiltInZoomControls(true);
        // 캐시 사용 여부 설정
        mWebSettings.setAppCacheEnabled(false);

        // 로드할 주소를 입력
        webView1.loadUrl(&quot;http://192.168.0.8:8080/&quot;);

        // 텍스트 뷰 위젯 연결
        textView = (TextView)findViewById(R.id.textView);

    }

    final class JavascriptInterface {
        @android.webkit.JavascriptInterface // 최근에는 이 어노테이션을 붙여줘야 동작하게 되어 있다
        public void callMethodName(final String str){// 반드시 final이어야 한다.
            // 네트워크를 통한 작업임으로 백그라운드 스레드를 써서 작업해야한다.
            // 또한, 백그라운드 스레드는 직접 메인 뷰에 접근해 제어할 수 없음으로
            // 핸들러를 통해서 작업해야하는데
            // 이 때문에 한번에 handler.post()를 통해서 내부에 Runnable을 구현해 작업한다.
            handler.post(new Runnable() {
                @Override
                public void run() {
                    // handle를 통해서 화면에 접근하는 것임으로 가능함
                    textView.setText(&quot;자바스크립트에서 전달받은 문자열을 쓴다 : &quot; + str);
                }
            });
        }
    }

}
</code></pre>
<h3 id="3-만들어준-javascriptinterface-클래스를-웹뷰에-등록">3. 만들어준 JavascriptInterface 클래스를 웹뷰에 등록</h3>
<pre><code class="language-jsx">&lt;span style=&quot;font-size: 14pt;&quot;&gt; 
        webView1.addJavascriptInterface(new JavascriptInterface(),&quot;myJSInterfaceName&quot;);
&lt;/span&gt;</code></pre>
<p>webView1.addJavascriptInterface(new JavascriptInterface(),&quot;myJSInterfaceName&quot;); 에서
두번째 매개변수란에 myJSInterfaceName 처럼 인터페이스 이름을 지정하게 되어 있는데,
이 이름을 이용해서 자바스크립트에서 호출</p>
]]></description>
        </item>
    </channel>
</rss>