<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>k_dragon.log</title>
        <link>https://velog.io/</link>
        <description>개발자가 되어보자!</description>
        <lastBuildDate>Fri, 02 Sep 2022 05:49:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>k_dragon.log</title>
            <url>https://images.velog.io/images/k_dragon/profile/b39707e8-0006-4053-9a6b-8ad48a8426f4/AREmoji_20220125_182602.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. k_dragon.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/k_dragon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Kotlin 함수형 프로그래밍 (1)]]></title>
            <link>https://velog.io/@k_dragon/Kotlin-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@k_dragon/Kotlin-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Fri, 02 Sep 2022 05:49:08 GMT</pubDate>
            <description><![CDATA[<p><strong>함수형 프로그램</strong>은 _순수 함수_를 작성하여 프로그램의 부작용을 줄이는 프로그래밍 기법을 말한다.
그리고 함수형 프로그래밍에서는 _람다식_과 _고차 함수_를 사용한다.
<br/></p>
<hr>
<h3 id="순수-함수">순수 함수</h3>
<blockquote>
<ul>
<li>같은 인자에 대하여 항상 같은 값을 반환한다.</li>
</ul>
</blockquote>
<ul>
<li>함수 외부의 어떤 상태도 바꾸지 않는다.   </li>
</ul>
<pre><code class="language-kotlin">fun sum(a : Int, b : Int) : Int{
    return a + b
}</code></pre>
<p>위 함수는 동일한 인자 a, b에 대해서는 항상 같은 값을 반환하며 함수 외부의 어떤 상태도 바꾸지 않기 때문에 순수 함수이다.</p>
<p><em>프로젝트가 커지다보면 부작용을 완벽하게 통제할 수 있는 순수 함수를 만들기는 어렵다. 그래서 평소에 가능한 한 순수 함수에 가깝게 안전한 함수를 구현하려고 노력하는 것이 중요하다.</em>
<br/></p>
<hr>
<h3 id="일급-객체">일급 객체</h3>
<blockquote>
<ul>
<li>일급 객체는 함수의 인자로 전달할 수 있다.</li>
</ul>
</blockquote>
<ul>
<li>일급 객체는 함수의 반환값에 사용할 수 있다.</li>
<li>일급 객체는 변수에 담을 수 있다.</li>
</ul>
<p>함수형 프로그래밍에서는 함수를 일급 객체로 생각한다.
뒤에 나올 <em>람다식</em> 역시 일급 객체의 특징을 가지고 있다.</p>
<p>만약 함수가 일급 객체면 일급 함수라고 부른다. 그리고 일급 함수에 이름이 없는 경우 &#39;람다식 함수&#39; 혹은 &#39;람다식&#39;이라고 부를 수 있다. 즉, 람다식은 일급 객체의 특징을 가진 이름 없는 함수이다.
<br/></p>
<hr>
<h3 id="람다식">람다식</h3>
<pre><code class="language-kotlin">{ x, y -&gt; x + y }</code></pre>
<p>위의 식은 람다식의 예이다.
함수의 이름이 없고 화살표(-&gt;)가 사용되었다.
이 두가지가 람다식임을 알 수 있게 하는 특징이다.<br/>
위에서 일급 객체에 대해 말할 때 람다식에 대해 언급했다.
이름이 없는 일급 함수 == 람다식
위 함수는 보다시피 이름이 없다!</p>
<p>함수형 프로그래밍의 람다식은 다음과 같다.</p>
<blockquote>
</blockquote>
<ul>
<li>다른 함수의 인자로 넘기는 함수</li>
<li>함수의 결괏값으로 반환하는 함수</li>
<li>변수에 저장하는 함수</li>
</ul>
<p>그러나 아직 위의 예시로는 다른 함수의 인자, 결과값이 되는지 알 수 없고 변수에 저장되는지도 알 수 없으니 고차함수를 보면서 천천히 이해해보도록 하자.
<br/></p>
<hr>
<h3 id="고차-함수">고차 함수</h3>
<blockquote>
</blockquote>
<ul>
<li>다른 함수를 인자로 사용하는 함수</li>
<li>다른 함수를 결괏값으로 반환하는 함수</li>
</ul>
<pre><code class="language-kotlin">fun main(){
    println(highFunc({ x, y -&gt; x + y}, 10, 20)
}

fun highFunc(sum: (Int, Int) -&gt; Int, a : Int, b : Int) : Int = sum(a, b)</code></pre>
<p>위 예시를 보면 <code>highFunc()</code>함수는 <code>sum</code>이라는 매개변수가 있다. 그리고 이 <code>sum</code>의 자료형은 람다식 형태이다.
즉, <code>highFunc()</code>함수는 <code>sum</code>이라는 변수에 람다식을 저장하여 람다식 함수를 인자로 받아들일 수 있는 고차 함수인 것이다.</p>
<p>이제 람다식에 대해서도 조금 더 감이 올 것이다.</p>
<hr>
<h3 id="함수형-프로그램의-정의와-특징">함수형 프로그램의 정의와 특징</h3>
<blockquote>
</blockquote>
<ul>
<li>순수 함수를 사용해야 한다.</li>
<li>람다식을 사용할 수 있다.</li>
<li>고차 함수를 사용할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Coroutine basics 번역하기]]></title>
            <link>https://velog.io/@k_dragon/Coroutine-basics-%EB%B2%88%EC%97%AD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@k_dragon/Coroutine-basics-%EB%B2%88%EC%97%AD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 26 Aug 2022 03:27:06 GMT</pubDate>
            <description><![CDATA[<p><em>필자는 영어를 잘 못하기 때문에 사전과 파파고의 도움을 많이 받았으며 번역이 부정확할 수 있습니다.</em></p>
<h2 id="your-first-coroutine">Your first coroutine</h2>
<p>코루틴은 정지할 수 있는 계산의 인스턴스입니다.</p>
<p>코루틴은 한 블록의 코드를 나머지 코드들과 동시에(병행적으로, concurrently) 실행된다는 측면에서 개념적으로 스레드와 비슷합니다    .</p>
<p>하지만 코루틴은 어떤 특정 스레드에도 묶여있지 않습니다.
코루틴은 한 스레드에서 실행을 일시 중단(지연)하고 다른 스레드를 다시 실행합니다.</p>
<p>코루틴은 가벼운 스레드라고 생각될 수 있지만, 실제 사용할 때 매우 다름을 만드는 여러 중요한 차이점들이 있습니다.</p>
<p>아래 코드를 살펴봅시다.</p>
<pre><code>fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println(&quot;World!&quot;) // print after delay
    }
    println(&quot;Hello&quot;) // main coroutine continues while a previous one is delayed
}</code></pre><p>위 코드를 실행시키면 아래와 같은 결과가 나옵니다.</p>
<pre><code>Hello
World!</code></pre><p>이 코드가 어떻게 실행됐는지 살펴봅시다.</p>
<p><code>launch</code>는 코루틴 빌더입니다. 이는 새로운 코루틴을 나머지 코드들과 동시에(병행적으로, concurrently) 시작하며 독립적으로 계속 작동합니다. 이것이 <code>Hello</code>가 먼저 출력된 이유입니다.</p>
<p><code>delay</code>는 특별한 지연(suspending) 함수입니다. 이는 코루틴을 특정 시간동안 지연(중단, suspend)시킵니다. 코루틴을 일시 중단(suspend) 시키는 것은 기존(사용하던) 스레드를 막지(block) 않고 다른 코루틴이 그들의 코드를 위해 기존 스레드를 실행하고 사용할 수 있습니다.</p>
<p><code>runBlocking</code>도 일반적인 메인 함수의 코루틴이 아닌 구역과 <code>runBlocking</code>중괄호 내부의 코루틴 코드들을 이어주는 코루틴 빌더입니다. 이것은 IDE에서 <code>runBlocking</code>을 시작하는 중괄호 바로 다음에 <code>this:CoroutineScope</code>힌트로 강조 표현된다.</p>
<p>만약 당신이 이 코드에서 <code>runBlocking</code>을 지우거나 깜빡한다면 <code>launch</code>를 호출할 때 다음과 같은 에러가 날 것입니다. <code>launch</code>는 CoroutineScope 안에서만 선언될 수 있기 때문입니다.</p>
<pre><code>Unresolved reference: launch</code></pre><p><code>runBlocking</code>의 이름은 <code>runBlocking</code> 내부의 모든 코루틴의 실행이 완료될 때까지 이를 실행하는 스레드(이 경우엔 메인 스레드)가 호출 기간 동안 차단됨을 의미합니다. 애플리케이션의 최상위 수준에서는 이와 같이 사용되는 <code>runBlocking</code>을 종종 볼 수 있지만 실제 코드 내부(아마도 low level의 코드에서?를 의미하는듯 하다)에서는 거의 볼 수 없습니다. 스레드는 (코스트가) 비싼 리소스이며 스레드를 차단(block)하는 것은 비효율적이고 바람직하지 않기 때문입니다.</p>
<h4 id="structured-concurrency-구조화된-동시성">Structured concurrency (구조화된 동시성)</h4>
<p>코루틴은 <strong>structured concurrency</strong>(구조화된 동시성)의 원칙을 따르는데, 이는 코루틴의 생명주기를 지정하는 특정한 CoroutineScope에서만 새로운 코루틴을 시작할 수 있다는 것을 의미한다. 위의 예제는 <code>runBlocking</code>이 해당 스코프(범위?)를 설정한다는 것을 보여주며, 이것이 이전 예제에서 1초의 딜레이 후, &#39;World!&#39;가 출력될 때까지 기다린 다음에서야 종료되는 이유입니다.</p>
<p>실제 앱에서, 당신은 많은 코루틴을 실행할 것 입니다. <strong>structured concurrency</strong>(구조화된 동시성)은 그것들이 분실되고 누출되지 않도록 보장합니다. 모든 자식(하위) 코루틴들이 완료될 때까지 외부 스코프는 완료할 수 없습니다. 또한 <strong>structured concurrency</strong>(구조화된 동시성)은 코드의 모든 오류를 적절하게 알려주고 손실되지 않도록 보장합니다.</p>
<h2 id="extract-function-refactoring">Extract function refactoring</h2>
<p>continue.. 계속 번역하면서 업데이트 할게유</p>
<p><a href="https://kotlinlang.org/docs/coroutines-basics.html">원본 출처</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 화면 크기 구하기 with Kotlin]]></title>
            <link>https://velog.io/@k_dragon/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%ED%99%94%EB%A9%B4-%ED%81%AC%EA%B8%B0-%EA%B5%AC%ED%95%98%EA%B8%B0-with-Kotlin</link>
            <guid>https://velog.io/@k_dragon/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%ED%99%94%EB%A9%B4-%ED%81%AC%EA%B8%B0-%EA%B5%AC%ED%95%98%EA%B8%B0-with-Kotlin</guid>
            <pubDate>Tue, 05 Jul 2022 06:57:32 GMT</pubDate>
            <description><![CDATA[<p>개발을 하다 보면 에뮬레이터 혹은 내 휴대폰의 화면 크기를 구할 일이 가끔 생긴다.</p>
<p>얼마 전, 함께 프로젝트를 하는 디자이너가 내 휴대폰 화면 사이즈를 물어보았다.
이전에 화면의 가로 사이즈 구하는 코드를 사용해본 적이 있었기 때문에 후딱 로그 찍어서 알려주었다.
그리고 디자이너에게 질문이 하나 왔는데..</p>
<blockquote>
<p><em>이거 세로 사이즈 상태 바랑 네비게이션 바 포함된건가요?</em></p>
</blockquote>
<p>어..? 뇌절이 왔다.
생각해보니 이전에는 가로 사이즈만 사용해봐서 측정된 세로 사이즈가 정확히 어디부터 어디까지인지 몰랐다.
그리고 이 과정에서 알게 된 안드로이드의 화면 크기 구하는 방법이다.</p>
<br>

<hr>
<br>

<h2 id="일반적인-안드로이드-화면-크기-구하는-방법">일반적인 안드로이드 화면 크기 구하는 방법</h2>
<br>

<pre><code>val display = this.applicationContext?.resources?.displayMetrics
val deviceWidth = display?.widthPixels
val deviceHeight = display?.heightPixels</code></pre><p>자 deviceWidth와 deviceHeight를 Log 찍어보자
아, 그전에 저렇게 구하면 픽셀 값이 나오니 mdpi 기준 dp값으로 바꾸는 코드를 알아보자.</p>
<pre><code>fun px2dp(px: Int, context: Context) = px / ((context.resources.displayMetrics.densityDpi.toFloat()) / DisplayMetrics.DENSITY_DEFAULT)</code></pre><p>아래 코드와 같은 의미이다.</p>
<pre><code>fun px2dp(px: Int, context: Context): Float {
    return px / ((context.resources.displayMetrics.densityDpi.toFloat()) / DisplayMetrics.DENSITY_DEFAULT)
}</code></pre><p>위에서 구한 deviceWidth와 deviceHeight를 px2dp 함수의 px 매개변수로 넘겨준다.</p>
<pre><code>Log.d(&quot;deviceSize&quot;, &quot;${px2dp(deviceWidth!!, this)}&quot;)
Log.d(&quot;deviceSize&quot;, &quot;${px2dp(deviceHeight!!, this)}&quot;)</code></pre><p>내 휴대폰의 경우 갤럭시S21+이고</p>
<p><strong>px2dp를 적용하기 전 픽셀 값</strong>
<code>width : 1080, height : 2181</code></p>
<p><strong>px2dp함수를 적용한 dp값</strong>
<code>width : 360, height : 727</code></p>
<p><strong>갤럭시S21+의 공식 픽셀 값</strong>
<code>width: 1080, height: 2400</code></p>
<p>어라? 뭔가 이상하지 않은가?
가로는 맞는 것 같은데.. 세로는 내가 측정한 픽셀 값과 공식 픽셀 값이 맞지 않는다.
dp값 또한 애매하게 727이다. 적어도 10의 자리로 떨어지게 만들었을 것 같은데 말이다.
여기서 생각해볼 수 있는 것은 상태 바(status bar)와 네비게이션 바(navigation bar)이다.</p>
<br>

<hr>
<br>

<h2 id="상태-바-네비게이션-바">상태 바, 네비게이션 바</h2>
<br>

<p><strong>상태 바(status bar)</strong>란 휴대폰의 맨 위에 시간, 알림, 블루투스, 와이파이 배터리 등의 정보를 보여주는 영역이다. (아래 사진과 같은~)
<img src="https://velog.velcdn.com/images/k_dragon/post/5ce9b1c4-4eb0-4889-8a57-d1f1a4afbf58/image.jpg">
<br></p>
<p><strong>네비게이션 바(navigation bar)</strong>란 아이폰에는 없지만 안드로이드 기기에 있는 <code>최근 사용 앱, 홈, 뒤로가기</code> 버튼이 있는 최하단 영역이다.</p>
<p>최근에는 갤럭시에서도 스와이프 제스쳐를 지원하면서 네비게이션 바를 없애고 제스쳐를 이용할 수 있다. (점점 사용하는 사람들도 늘어나고 있다.)</p>
<img src="https://velog.velcdn.com/images/k_dragon/post/03186eb3-6676-4325-83a5-674b5542730d/image.jpg" width="250px">
스와이프 제스쳐

<img src="https://velog.velcdn.com/images/k_dragon/post/f189b617-35ad-4879-bc5d-7f5290be7851/image.jpg" width="250px">
네비게이션 바

<br>
<br>

<hr>
<br>

<h2 id="상태-바status-bar-크기-구하기">상태 바(status bar) 크기 구하기</h2>
<br>

<p>다시 화면의 크기를 찾는 내용으로 돌아와보자.
그럼 <code>2400-2181=219</code>는 어디로간걸까</p>
<p>일단 단위가 커서 계산하기 귀찮으니 dp값으로 변환해서 생각하자
2400을 px2dp함수에 넣어 계산하면 800이 나온다.</p>
<p>그럼? 이제 <code>800-727=73</code>이 어디로 갔는지 찾으면 된다.</p>
<p>근데 여기서의 문제는 <code>73</code>에 상태 바와 네비게이션 바가 모두 포함된 것인지 하나만 포함된 것인지를 모르겠다는 것이었다.</p>
<p>그래서 구글링한 결과 상태 바를 구하는 코드는 아래와 같다.</p>
<pre><code>    fun getStatusBarHeight(context: Context){
        var statusbarHeight = 0
        val resourceId: Int = context.resources.getIdentifier(&quot;status_bar_height&quot;, &quot;dimen&quot;, &quot;android&quot;)
        if (resourceId &gt; 0) {
            statusbarHeight = resources.getDimensionPixelSize(resourceId)
            Log.d(&quot;deviceSize&quot;, &quot;status bar : ${px2dp(statusbarHeight, context)}&quot;)
        }
    }</code></pre><p>이러면 로그에 상태 바의 세로 dp값이 나올 것이다.</p>
<p>내 휴대폰은(갤럭시S21+) <code>25</code>가 나왔다.</p>
<p>그럼 <code>727 + 25 &lt; 800</code> 로 아직 부족하다.
여기서 부족한 부분은 네비게이션 바로 추측해볼 수 있다.
그럼  이제 네비게이션 바의 크기를 측정해보자!
<br></p>
<hr>
<br>

<h2 id="네비게이션-바navigation-bar-크기-구하기">네비게이션 바(navigation bar) 크기 구하기</h2>
<br>

<p>네비게이션 바의 세로 길이를 구하는 코드는 아래와 같다.</p>
<pre><code>    fun getnavigationBarHeight(context: Context){
        val resourceId = resources.getIdentifier(&quot;navigation_bar_height&quot;, &quot;dimen&quot;, &quot;android&quot;)
        var navigationbarHeight = 0
        if (resourceId &gt; 0) {
            navigationbarHeight = resources.getDimensionPixelSize(resourceId)
            Log.d(&quot;deviceSize&quot;, &quot;navigation bar : ${px2dp(navigationbarHeight, context)}&quot;)
        }
    }</code></pre><p>상태 바 구하는 코드와 거의 유사하다</p>
<p>내 휴대폰의(갤럭시S21+) 네비게이션 바 세로 dp값은 <code>48</code>이 나왔다.
<br></p>
<hr>
<br>

<h2 id="결론">결론</h2>
<br>

<p><code>727 + 25 + 48 = 800</code>
이제 드디어 숫자가 딱 맞았다.</p>
<p>상태 바와 네비게이션 바를 제외한 실직적인 앱 화면 크기는 <code>727</code>
상태 바와 네비게이션 바를 합친 디바이스의 화면 크기는 <code>800</code>이다.
<br></p>
<blockquote>
<p><em>혹시 그럼 스와이프 제스쳐를 쓰면 어떻게 되죠?</em></p>
</blockquote>
<p>추가로 궁금해서 네비게이션 바를 스와이프 제스쳐로 바꾸고 다시 측정해보았다.
그럼 스와이프 제스쳐의 경우 하단 영역의 길이가 <code>15</code>로 나온다.</p>
<p>스와이프 제스쳐가 차지하는 하단 영역이 없는 줄 알았지만 측정하고 자세히보니 정말 얇은 영역이 있었다.</p>
<p><code>760 + 25 + 15 = 800</code>으로 네비게이션 바를 사용할 때보다 꽤 많은 영역이 늘어난다는 것을 알 수 있다.
<br></p>
<hr>
<br>

<h2 id="주의해야할-점">주의해야할 점</h2>
<br>

<p>방금의 결론에서 우리가 맨 처음 구했던 <code>deviceHeight</code>는
<code>디바이스 크기 - 상태바 - 네비게이션바</code>였다.</p>
<p>하지만 다른 상황이 있다.
 위 경우는 상태 바에 <strong>전면 카메라가 포함되어있는</strong> 경우이다.</p>
<p> 하지만 상태 바에 전면 카메라가 포함되어있지 않은 경우에는 
 <code>deviceHeight</code>에 <code>상태바(status bar)</code>가 포함되지 않는다.</p>
<p> 즉 <code>디바이스 크기 - 네비게이션바</code>라고 생각하면 된다.</p>
<p> 내 휴대폰(갤럭시S21+)은 펀치홀 형식의 전면 카메라를 가지고 있어 카메라가 상태바에 포함된다.</p>
<p> 그러나 내가 안드로이드 스튜디오에서 사용하고 있는 에뮬레이터의 경우 전면 카메라가 없으므로 상태바에 전면 카메라가 포함되어있지 않다.</p>
<p> 해당 에뮬레이터로 크기를 측정해본 결과는 아래와 같다.
 <code>디바이스 크기 : 760</code>
 <code>deviceHeight : 712</code>
 <code>상태바(status bar) : 24</code>
 <code>네비게이션 바(navigation bar) : 48</code></p>
<p> 보다시피 <code>760 - 712 = 48</code>로 네비게이션 바의 크기만큼을 제외한 수치이다.</p>
<p> 다만 전면 카메라가 상태 바에 포함됐을 경우라는 조건은 100% 확실하다고 말하기는 어렵다. 내가 확신하는 나의 추측일뿐이다.</p>
<br>

<hr>
<br>

<h4 id="출처">출처</h4>
<p><a href="https://gdlseed.tistory.com/2">상태바, 네비게이션 바 구하는 방법</a>
 <a href="https://forums.solar2d.com/t/height-of-android-navigation-bar-solved/353073">네비게이션 바 크기 관련</a>
 <a href="https://www.thelec.kr/news/articleView.html?idxno=10354">갤럭시S21+ 공식 크기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android - view binding (안드로이드 - 뷰 결합)]]></title>
            <link>https://velog.io/@k_dragon/Android-view-binding-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EB%B7%B0-%EA%B2%B0%ED%95%A9</link>
            <guid>https://velog.io/@k_dragon/Android-view-binding-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EB%B7%B0-%EA%B2%B0%ED%95%A9</guid>
            <pubDate>Mon, 31 Jan 2022 07:48:58 GMT</pubDate>
            <description><![CDATA[<p>android developers에서 <code>view binding</code>에 대해 읽어보았다.
이전부터 <code>view binding</code>을 사용해오긴 했지만 항상 똑같이 가져다 쓰기만 했지 이해하고 쓰진 않았다. 항상 아래처럼(액티비티라면) 매크로처럼 썼던 것 같다.</p>
<pre><code class="language-kotlin">lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)</code></pre>
<p>이제는 제대로 알고 쓰고 싶어서 한 번 찾아보았다.</p>
<p>모듈에 뷰 바인딩을 사용하도록 설정되면 모듈에 포함된 각 XML 레이아웃 파일의 바인딩 클래스가 생성된다. 각 바인딩 클래스에는 루트 뷰 및 ID가 있는 모든 뷰의 참조가 포함된다.(그래서 id가 없으면 참조되지 않는다)<br/><br/></p>
<p>또한 모든 바인딩 클래스에는 상응하는 레이아웃 파일의 루트 뷰에 관한 직접 참조를 제공하는 <code>getRoot()</code> 메서드가 포함된다.
아래 예에선 <code>ActivityMainBinding</code>클래스의 <code>getRoot()</code> 메서드가 <code>ConstraintLayout</code> 루트 뷰를 반환한다.<br/><br/></p>
<p>&lt;<code>MainActivity</code>의 xml파일&gt;</p>
<pre><code class="language-kotlin">&lt;androidx.constraintlayout ... &gt;
         &lt;TextView android:id=&quot;@+id/tv_login_id&quot; ... /&gt;
        &lt;EditText android:id=&quot;@+id/et_login_id&quot; ... /&gt;
        &lt;ImageView android:id=&quot;@+id/iv_logo&quot; ... /&gt;
        &lt;Button android:id=&quot;@+id/btn_login&quot; ... /&gt;
&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;</code></pre>
<br/>

<hr>
<br/>

<h2 id="액티비티에서-뷰-바인딩-사용">액티비티에서 뷰 바인딩 사용</h2>
<p>액티비티에 사용할 바인딩 클래스 인스턴스를 설정하려면 액티비티의 <code>onCreate()</code> 메서드에서 다음 단계를 따른다.</p>
<ol>
<li><p>생성된 바인딩 클래스에 포함된 정적 <code>inflate()</code> 메서드를 호출하면 액티비티에서 사용할 바인딩 클래스 인스턴스가 생성된다.</p>
</li>
<li><p><code>getRoot()</code> 메서드를 호출하거나 Kotlin 속성 구문을 사용하여 루트 뷰 참조를 가져온다.</p>
</li>
<li><p>루트 뷰를 <code>setContentView()</code>에 전달하여 화면상의 활성 뷰로 만든다.</p>
<pre><code class="language-kotlin"> private lateinit var binding: ActivityMainBinding

 override fun onCreate(savedInstanceState: Bundle) {
     super.onCreate(savedInstanceState)
     binding = ActivityMainBinding.inflate(layoutInflater)
     setContentView(binding.root)
 }</code></pre>
<p><em>뷰 모델을 사용할 때</em></p>
<pre><code class="language-kotlin">binding.tvLoginId.text = viewModel.loginId
binding.button.setOnClickListener { viewModel.loginClicked() }</code></pre>
<br/>

</li>
</ol>
<hr>
<br/>

<h2 id="프래그먼트에서-뷰-바인딩-사용">프래그먼트에서 뷰 바인딩 사용</h2>
<p>프래그먼트에 사용할 바인딩 클래스 인스턴스를 설정하려면 프래그먼트의 <code>onCreateView()</code> 메서드에서 다음 단계를 따른다.</p>
<ol>
<li>생성된 바인딩 클래스에 포함된 정적 <code>inflate()</code> 메서드를 호출하면 프래그먼트에서 사용할 바인딩 클래스 인스턴스가 생성된다.</li>
<li><code>getRoot()</code> 메서드를 호출하거나 Kotlin 속성 구문을 사용하여 루트 뷰 참조를 가져온다.</li>
<li><code>onCreateView()</code> 메서드에서 루트 뷰를 반환하여 화면상의 활성 뷰로 만든다.<br/>
</li>
</ol>
<hr>
<br/>

<h2 id="findviewbyid와의-차이점">findViewById와의 차이점</h2>
<p>뷰 바인딩에는 <code>findViewById</code>를 사용하는 것에 비해 아래와 같은 중요한 장점이 있다.</p>
<ul>
<li>Null Safety: 뷰 바인딩은 뷰의 직접 참조를 생성하므로 유효하지 않은 뷰 ID로 인해 null 포인터 예외가 발생할 위험이 없다. 또한 레이아웃의 일부 구성에만 뷰가 있는 경우 바인딩 클래스에서 참조를 포함하는 필드가 <code>@Nullable</code>로 표시된다.</li>
<li>Type Safety : 각 바인딩 클래스에 있는 필드의 유형이 xml 파일에서 참조하는 뷰와 일치한다. 즉, 클래스 변환 예외가 발생할 위험이 없다.</li>
</ul>
<p>이런 차이점은 레이아웃과 코드 사이의 비호환성으로 인해 런타임이 아닌 컴파일 시간에 빌드가 실패하게 된다는 것을 의미한다.
즉, 컴파일에서 빌드가 실패하므로 우리가 오류를 찾기 더 쉽다.
<br/></p>
<hr>
<br/>

<h2 id="데이터-바인딩과-비교">데이터 바인딩과 비교</h2>
<p>뷰 바인딩과 데이터 바인딩은 모두 뷰를 직접 참조하는데 사용할 수 있는 바인딩 클래스를 생성한다. 하지만 뷰 바인딩은 보다 단순한 사용 사례를 처리하기 위한 것이며 데이터 바인딩에 비해 다음과 같은 이점을 제공한다.</p>
<ul>
<li>더 빠른 컴파일: 뷰 바인딩에는 주석(annotation) 처리가 필요하지 않으므로 컴파일 시간이 더 짧다. (데이터 바인딩에서 @{}를 말하는듯 하다. 그래도 데이터바인딩은 쓰는게 )</li>
<li>사용 편의성: 뷰 바인딩에는 특별히 태그된 xml 레이아웃 파일이 필요하지 않으므로 앱에서 더 신속하게 채택할 수 있다. 모듈에서 뷰 바인딩을 사용 설정하면 모듈의 모든 레이아웃에 뷰 바인딩이 자동으로 적용된다.</li>
</ul>
<p>반대로 뷰 바인딩에는 데이터 바인딩과 비교할 때 아래와 같은 제한사항이 있다.</p>
<ul>
<li>뷰 바인딩은 레이아웃 변수 또는 레이아웃 표현식을 지원하지 않으므로 xml 레이아웃 파일에서 직접 동적 UI콘텐츠를 선언하는데 사용할 수 없습니다. (코틀린 파일에서 TextView, Button과 같은 요소들을 추가할 수 없다? 정도로 이해했음)</li>
<li>뷰 바인딩은 양방향 데이터 바인딩을 지원하지 않는다.(데이터 바인딩은 지원한다.)</li>
</ul>
<p>이렇게 비교를 해봤지만 사실 뷰 바인딩과 데이터 바인딩을 모두 사용하는 것이 가장 좋은 것 같다.
<br/></p>
<hr>
<br/>

<h2 id="추가로-궁금했던-것들">추가로 궁금했던 것들</h2>
<p>항상 inflate(), setContentView() 메서드에 대해 궁금했었다. 매크로처럼 이녀석들을 항상 호출하면서 대체 뭘까하며 생각했었다.
그래서 조금 더 찾아봤다.<br/><br/></p>
<h4 id="inflate">inflate()</h4>
<p>위에 1번에서 말했듯이 inflate() 메서드를 호출하면 액티비티에서 사용할 바인딩 클래스 인스턴스가 생성된다.</p>
<p>즉, inflate()는 xml에 표기된 레이아웃들을 메모리에 객체화시킨다. xml 파일을 객체화해서 코드에서 사용하기 위함이다.
xml 파일은 단순한 디자인 정보로 이 정보를 가지고 실제로 화면에 띄우기 위해서는 xml에 정의된 속성들에 맞게 메모리에 올려야한다.<br/><br/><br/></p>
<h4 id="setcontentview">setContentView()</h4>
<p>setContentView() 함수는 첫 번째 인자로 넘겨주는 xml 레이아웃 리소스 id에 해당하는 파일을 파싱하여 뷰(view)를 생성, 속성을 지정, 상하관계에 맞춰 배치한다.(xml 파일을 객체화시킨다.)
이런 일련의 과정을 전개(inflate)라고 부른다.</p>
<p>그래서 우리는 setContentView() 메서드 밑에서 xml에 있는 UI 요소들을 끌어와 쓸 수 있는 것이다.
바로 메모리에 올라가 객체화 되었기 때문이다.</p>
<p>setContentView() 메서드는 xml 문서를 inflate하기 위해 내부적으로 LayoutInflater 클래스를 참조한다.<br/><br/><br/></p>
<h4 id="inflater">inflater</h4>
<p>xml 파일을 inflate하기 위해서 LayoutInflater 클래스를 제공한다.
LayoutInflater를 생성하는 여러가지 방법이 있다.</p>
<p>(액티비티에서는 아래처럼 쉽게 할 수 있긴 하지만 그래도 자세히 알아봤다.)</p>
<pre><code class="language-kotlin">binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)</code></pre>
<p><br/><br/></p>
<p><strong>context.getSystemService()</strong>
context에서 LayoutInflater를 가져오는 방법</p>
<pre><code class="language-kotlin">val inflater : LayoutInfalter = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)</code></pre>
<p><br/><br/>
<strong>Activity에서 getLayoutInflater</strong>
액티비티에서는 LayoutInflater를 쉽게 얻어올 수 있도록 getLayoutInflater()를 제공한다. 액티비티는 자신 window의 LayoutInflater를 사용한다.</p>
<pre><code class="language-kotlin">val inflater : LayoutInflater = getLayoutInflater()</code></pre>
<p><br/><br/></p>
<p><strong>LayoutInflater.from()</strong>
LayoutInflater에 static으로 정의되어있는 LayoutInflater.from을 통해 LayoutInflater를 만드는 방법이다.내부적으로 context.getSystemService를 호출하고 있으며, 같은 context에서는 같은 객체를 리턴하기 때문에 굳이 멤버 변수로 선언해 놓지 않고 필요할 때마다 호출해서 사용해도 괜찮다.</p>
<pre><code class="language-kotlin">val inflater : LayoutInflater = LayoutInflater.from(context)</code></pre>
<p><br/><br/>
근데.. 그럼 우리가 액티비티에서 사용하던 아래 <code>layoutInflater</code>는 무엇일까?</p>
<pre><code class="language-kotlin">binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)</code></pre>
<br/>

<p><code>layoutInflater</code>에 <code>ctrl + 커서</code>를 해보면 아래와 같이 나온다.</p>
<pre><code class="language-kotlin">Activity public LayoutInflater getLayoutInflater()</code></pre>
<p>즉 우리가 위에서 봤던 <code>getLayoutInflater()</code>이다.
<br/></p>
<hr>
<br/>

<p>참고 출처
<a href="https://developer.android.com/topic/libraries/view-binding?hl=ko">https://developer.android.com/topic/libraries/view-binding?hl=ko</a>
<a href="https://developer.android.com/reference/android/view/LayoutInflater">https://developer.android.com/reference/android/view/LayoutInflater</a>
<a href="https://soo0100.tistory.com/1017">https://soo0100.tistory.com/1017</a>
<a href="https://lktprogrammer.tistory.com/156">https://lktprogrammer.tistory.com/156</a>
<a href="https://velog.io/@appletorch/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Inflate%EC%9D%B4%EB%9E%80">https://velog.io/@appletorch/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-Inflate%EC%9D%B4%EB%9E%80</a>
<a href="https://medium.com/vingle-tech-blog/android-layoutinflater-b6e44c265408">https://medium.com/vingle-tech-blog/android-layoutinflater-b6e44c265408</a>
<a href="https://blog.naver.com/pistolcaffe/221285539895">https://blog.naver.com/pistolcaffe/221285539895</a></p>
]]></description>
        </item>
    </channel>
</rss>