<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sanggun-park.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 14 Jan 2025 06:10:11 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>sanggun-park.log</title>
            <url>https://velog.velcdn.com/images/plz_no_anr/profile/77a6a0d4-f670-4b27-95a9-2693158e3032/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. sanggun-park.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/plz_no_anr" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Android] Jetpack Compose와 
Stability (작성 중)]]></title>
            <link>https://velog.io/@plz_no_anr/Android-Jetpack-Compose%EC%99%80-Stability</link>
            <guid>https://velog.io/@plz_no_anr/Android-Jetpack-Compose%EC%99%80-Stability</guid>
            <pubDate>Tue, 14 Jan 2025 06:10:11 GMT</pubDate>
            <description><![CDATA[<p><del>이젠 Compose가 아니면 개발하기 힘든 몸이 되</del></p>
<p>Compose는 안드로이드에서 새로운 Ui 개발 패러다임으로 기존 명령형 방식의 XML에서 Kotlin으로 개발 가능한 선언형 개발 방식이다.</p>
<p>처음 Compose를 접했을 땐 기존 XML 방식과 많이 달라서 적응하기 힘들었는데
이젠 Compose가 아니면 개발하는 여간 불편한 게 아니다..</p>
<h2 id="1-compose의-랜더링-단계">1. Compose의 랜더링 단계</h2>
<ul>
<li><strong>구성(Composition)</strong>:    UI 선언 및 트리 생성    UI 트리 구성 (상태와 연결)</li>
<li><strong>레이아웃(Layout)</strong>:    부모-자식 관계로 레이아웃 정의    각 컴포넌트의 위치 및 크기 제약 계산</li>
<li><strong>측정(Measurement)</strong>:    크기 측정    각 컴포넌트의 크기 반환</li>
<li><strong>그리기(Drawing)</strong>:    그래픽 렌더링    UI 요소를 실제 화면에 표시</li>
</ul>
<p>기본적으로 Composable 함수는 위와 같은 단계를 거쳐 화면에 나타난다.</p>
<p>위와 같은 랜더링이 끝난 후 UI 요소를 수정한다고 하면 새로운 업데이트를 적용하기 위해 <strong>구성(Composition)</strong> 단계부터 다시 실행하는데 이 과정을 <strong>재구성(Recomposition)</strong> 이라고 합니다.</p>
<h2 id="2-stability">2. Stability</h2>
<p>Compose에서 안정성(Stability)이 중요한 이유는 </p>
<p>Compose에서 안정적이거나 불안정하다의 추론은 Compose 컴파일러에 의해 이루어 진다. 컴파일러는 Composable 함수에서 사용되는 매개변수의 유형을 검사하고 아래의 기준에 따라 안정성 여부를 분류한다.</p>
<ul>
<li>String을 포함한 기본 유형(Primitive)</li>
<li>(String) -&gt; String과 같은 람다 표현식으로 표현된 함수</li>
<li>모든 public property가 불변이고 (value로 정의된 경우) stable한 속성을 가진 클래스</li>
<li>@Stable, @Immutable과 같은 stability 어노테이션을 사용하여 명시적으로 표기된 클래스</li>
</ul>
<p>예를 들면</p>
<pre><code class="language-kotlin">data class Person( 
    val name: String,
    val age: Int
)</code></pre>
<p>모든 public property가 val이고 Primitive Type이기 때문에 stable하다.</p>
<pre><code class="language-kotlin">data class Person(
    val name: String,
    var age: Int
)</code></pre>
<p>모든 public property가 Primitive Type이긴 하지만 age var이기 때문에 unstable하다.</p>
<h2 id="3-restartable-과-skippable">3. Restartable 과 Skippable</h2>
<h3 id="restartable">Restartable</h3>
<p>입력이나 상태가 변경될 때마다 Compose 런타임이 Composable 함수에 대해 recomposition을 트리거할 수 있다는 것을 의미한다.</p>
<h3 id="skippable">Skippable</h3>
<p>Smart recomposition에 의해 설정된 적절한 조건 하에서 recomposition 을 완전히 건너뛸 수 있다. 따라서 skippable한 Composable 함수는 특정 상황에 따라 recomposition을 건너뛰고 UI 성능을 향상시킬 가능성과 직접적인 연관성이 있다고 볼 수 있다.</p>
<p>skippable이라는 것은 해당 함수가 restartable한 Composable 함수에 대해 recomposition을 건너뛸 수 있음을 의미한다.</p>
<h2 id="4-immutable-collections">4. Immutable Collections</h2>
<p>Compose에서 List는 불안정하다. 그 이유는 아래와 같다.</p>
<pre><code class="language-kotlin">val mutableList = mutableListOf&lt;String&gt;()
val list: List&lt;String&gt; = mutableList</code></pre>
<p>위 코드에서 list는 <code>List</code> 인터페이스로 수정이 불가능 하지만, 구현체는 <code>MutableList</code>로 수정이 가능하다. 이때 Compose 컴파일러는 컴파일 타임에 구현 타입을 추론할 수 없기 때문에 이러한 인스턴스를 불안정한 것으로 처리한다. 이러한 이유로 개발자는 정확한 동작을 보장해야 한다.</p>
<p><a href="https://github.com/Kotlin/kotlinx.collections.immutable">kotlinx.collections.immutable 라이브러리</a>
해당 라이브러리를 사용하면 안정적인 List, Set등의 Collection을 사용할 수 있다.</p>
<hr>
<p>참조
<a href="https://developer.android.com/develop/ui/compose/documentation">Android-Compose</a>
<a href="https://velog.io/@skydoves/compose-stability#immutable-vs-stable">skydoves님 Velog</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Other] Chrome 우클릭 해제 (복사방지)]]></title>
            <link>https://velog.io/@plz_no_anr/Etc-Chrome-%EC%9A%B0%ED%81%B4%EB%A6%AD-%ED%95%B4%EC%A0%9C-%EB%B3%B5%EC%82%AC%EB%B0%A9%EC%A7%80</link>
            <guid>https://velog.io/@plz_no_anr/Etc-Chrome-%EC%9A%B0%ED%81%B4%EB%A6%AD-%ED%95%B4%EC%A0%9C-%EB%B3%B5%EC%82%AC%EB%B0%A9%EC%A7%80</guid>
            <pubDate>Thu, 09 Jan 2025 06:54:25 GMT</pubDate>
            <description><![CDATA[<h3 id="1-f12-개발자-도구">1. F12 (개발자 도구)</h3>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/69d209a4-d774-41ee-a7d0-e18431527344/image.png" alt=""></p>
<h3 id="2-f1설정-또는-톱니바퀴-모양">2. F1(설정) 또는 톱니바퀴 모양</h3>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/28788ccc-e447-4e14-89a3-d330e853553e/image.png" alt=""></p>
<h3 id="3-디버거의-자바스크립트-사용-중지-체크">3. 디버거의 자바스크립트 사용 중지 체크</h3>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/213758d3-3825-419c-b21a-da2b06d16d9c/image.png" alt=""></p>
<h3 id="4-사용-후-체크-해제-">4. 사용 후 체크 해제 !</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Other] Mac 단축키 정리]]></title>
            <link>https://velog.io/@plz_no_anr/Etc-Mac-%EB%8B%A8%EC%B6%95%ED%82%A4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@plz_no_anr/Etc-Mac-%EB%8B%A8%EC%B6%95%ED%82%A4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 09 Jan 2025 06:44:37 GMT</pubDate>
            <description><![CDATA[<p>*<em>Command + Shift + . *</em>: 숨김파일 </p>
<p>*<em>Command + Opt + 방향키 *</em>: Chrome 탭 이동</p>
<p>*<em>Ctrl + 방향키 *</em>: 멀티윈도우 탭 이동</p>
<p>*<em>Command + n *</em>: 새로운 창을 생성</p>
<p>*<em>Command + t *</em>: 새로운 탭을 생성</p>
<p>*<em>Command + w *</em>: 현재 활성화 된 탭을 닫음</p>
<p>*<em>Command + Shift + w *</em>: 현재 활성화 된 탭을 모두 닫고 창도 닫음</p>
<p>*<em>Command + Shift + t *</em>: 직전에 닫은 탭을 열음 </p>
<p>*<em>Command + ` *</em>: 크롬 창 이동</p>
<p>*<em>Command + 1~8 *</em>: 같은 창안에 있는 탭의 번호로 이동</p>
<p>*<em>Command + 9 *</em>: 가장 마지막의 탭으로 이동</p>
<p>*<em>Command + Option + 화살표(좌우) *</em>: 같은 창안에 있는 탭간의 순차적인 이동</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Multi-Threading] Mutex란 무엇인가]]></title>
            <link>https://velog.io/@plz_no_anr/Multi-Threading-Mutex%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@plz_no_anr/Multi-Threading-Mutex%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Wed, 20 Nov 2024 06:32:02 GMT</pubDate>
            <description><![CDATA[<p><del>멀티스레딩은 안드로이드 개발자와 애증의 관계가 아닐까..?</del></p>
<p>안드로이드는 기본적으로 멀티스레딩 환경이기 때문에 스레드 관련 이슈가 많은데 그중 Race Condition이 발생한 상황에서 Mutex로 해결한 예를 코드를 통해 정리해 보려고 한다.</p>
<h2 id="1-경쟁-상태race-condition">1. 경쟁 상태(Race Condition)</h2>
<p>레이스 컨디션은 두 개 이상의 스레드가 동시에 공유 자원에 접근하고, 그 접근 순서에 따라 결과가 달라질 때 발생하는 문제</p>
<p>원인으로는 여러 스레드가 동기화되지 않은 상태에서 동일한 자원(예: 변수, 메모리)에 동시에 읽기 및 쓰기 작업을 수행할 때 발생하고 스레드들이 자원을 조정하지 않고 사용하려고 할 때 데이터 불일치나 예상치 못한 결과가 생길 수 있다.</p>
<h2 id="2-교착-상태deadlock">2. 교착 상태(DeadLock)</h2>
<p>데드락은 두 개 이상의 스레드가 서로 자원의 잠금을 기다리면서 무한 대기 상태에 빠지는 상황을 의미한다. 이로 인해 해당 스레드들은 진행하지 못하고 멈춘 상태</p>
<p>원인으로는 두 개 이상의 스레드가 서로 잠근 자원을 해제하지 않은 상태에서 상대방의 자원을 기다릴 때 발생하고 교착 상태는 일반적으로 잠금을 걸고 해제하는 순서가 일관되지 않을 때 발생한다.</p>
<h2 id="3-임계-영역critical-section">3. 임계 영역(Critical Section)</h2>
<p>멀티스레드 환경에서 여러 스레드가 동시에 접근할 때 문제가 발생할 수 있는 공유 자원에 접근하는 코드 블록을 의미
이 코드 블록은 동시에 하나의 스레드만 접근할 수 있어야 하며, 이를 통해 데이터 무결성과 일관성을 보장할 수 있다.</p>
<ul>
<li><p><strong>공유 자원 접근</strong>: 임계영역은 변수, 데이터 구조, 파일 등 여러 스레드가 동시에 접근하면 문제가 될 수 있는 공유 자원에 접근하거나 수정하는 코드</p>
</li>
<li><p><strong>상호 배제</strong>: 임계영역 내에서는 한 번에 하나의 스레드만 접근할 수 있도록 해야 합니다. 이를 위해 동기화 메커니즘(예: Mutex, synchronized 키워드 등)을 사용하여 스레드들이 동시에 진입하지 못하도록 막는다.</p>
</li>
<li><p><strong>데이터 일관성 보장</strong>: 적절한 동기화가 없으면 레이스 컨디션(race condition)과 같은 문제가 발생할 수 있으며, 이는 공유 자원의 불일치 상태를 초래할 수 있다.</p>
</li>
</ul>
<h4 id="왜-임계영역이-필요한가">왜 임계영역이 필요한가?</h4>
<p>멀티스레드 환경에서는 두 개 이상의 스레드가 동일한 자원에 동시에 접근하여 읽기 및 쓰기 작업을 수행할 수 있다. 이때 동기화되지 않은 코드가 실행되면 다음과 같은 문제가 발생할 수 있다</p>
<ul>
<li><p><strong>레이스 컨디션</strong>: 여러 스레드가 임계영역 내의 작업 순서에 따라 서로 다른 결과가 나올 수 있다.</p>
</li>
<li><p><strong>데이터 불일치</strong>: 공유 자원에 대해 읽기/쓰기 작업이 중복되면 값이 예상대로 유지되지 않을 수 있다.</p>
</li>
</ul>
<h2 id="4-상호-배제mutual-exclusion">4. 상호 배제(Mutual Exclusion)</h2>
<p>멀티스레드 환경에서 공유 자원에 대한 접근을 동기화하여 데이터의 무결성을 보장하는 동기화 메커니즘
<strong>Mutex</strong>는 한 번에 하나의 스레드만 특정 코드 블록(임계 영역)에 진입할 수 있도록 하여 동시에 여러 스레드가 동일한 자원을 수정할 때 발생할 수 있는 문제를 방지한다.</p>
<ul>
<li><p>단일 스레드가 자원을 독점적으로 사용해야 할 때 사용된다.</p>
</li>
<li><p>하나의 스레드가 lock을 하면 다른 스레드는 자원이 unlock될 때까지 대기한다.</p>
</li>
<li><p>재진입 여부에 따라 reentrant mutex(재진입 가능)와 일반 mutex(재진입 불가능)로 나뉜다.</p>
</li>
</ul>
<h2 id="5-세마포어semaphore">5. 세마포어(Semaphore)</h2>
<p><strong>Semaphore</strong>는 카운터로 작동하며, 지정된 수의 스레드가 동시에 자원에 접근할 수 있게 허용한다. 또한 특정 스레드에 의해 소유되지 않으며, 임의의 스레드가 자원을 반환할 수 있다.
특정 개수의 스레드가 자원을 동시에 사용할 수 있도록 제한할 때 사용된다.
특징:</p>
<ul>
<li><p><strong>Counting Semaphore</strong>: 카운터 값이 0보다 큰 경우, 그만큼의 스레드가 동시에 자원을 사용할 수 있다.</p>
</li>
<li><p><strong>Binary Semaphore</strong>: Mutex와 비슷하게 동작하며, 카운터 값이 1인 세마포어</p>
</li>
</ul>
<p><strong>Mutex</strong>와의 차이점으로는 세마포어는 지정된 수(1~n)개의 스레드가 동시에 자원에 접근할 수 있다는 점이 있다.</p>
<h2 id="6-문제-예시와-mutex를-통한-해결">6. 문제 예시와 Mutex를 통한 해결</h2>
<p>우선 Mutex의 구현체를 보면 세마포어를 상속 받지만 permits을 1, acquiredPermits을 lock이면 1 unlock이면 0으로 고정해 놓은 것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/plz_no_anr/post/3c38d7cb-c7db-4cb4-9335-9fdabc299dcd/image.png" alt=""></p>
<p>이제 문제와 해결 방안을 코드로 살펴보면</p>
<pre><code class="language-kotlin">private var result: String? = null

    @Test
    fun main(): Unit = runBlocking {
        launch { thread1() }
        launch { thread2() }
    }

    private fun thread1() {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread1 start&quot;)
        result = &quot;Success&quot;
        println(&quot;$currentThreadName: thread1 end&quot;)
    }

    private fun thread2() {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread2 start&quot;)
        println(&quot;$currentThreadName: 결과는?: $result&quot;)
        println(&quot;$currentThreadName: thread2 end&quot;)
    }</code></pre>
<p>결과</p>
<pre><code class="language-kotlin">Test worker @coroutine#2: thread1 start
Test worker @coroutine#2: thread1 end
Test worker @coroutine#3: thread2 start
Test worker @coroutine#3: 결과는?: Success
Test worker @coroutine#3: thread2 end</code></pre>
<p>위와 같이 다른 스레드지만 딜레이가 없어 먼저 실행된 thread1이 먼저 끝나 thread2에서 result 값을 정상적으로 확인이 가능하다. 하지만</p>
<pre><code class="language-kotlin">@Test
    fun main(): Unit = runBlocking {
        launch { thread1() }
        launch { thread2() }
    }

    private suspend fun thread1() {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread1 start&quot;)
        delay(3000L)
        result = &quot;Success&quot;
        println(&quot;$currentThreadName: thread1 end&quot;)
    }

    private suspend fun thread2() {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread2 start&quot;)
        delay(1000L)
        println(&quot;$currentThreadName: 결과는?: $result&quot;)
        println(&quot;$currentThreadName: thread2 end&quot;)
    }</code></pre>
<p>결과</p>
<pre><code class="language-kotlin">Test worker @coroutine#2: thread1 start
Test worker @coroutine#3: thread2 start
Test worker @coroutine#3: 결과는?: null
Test worker @coroutine#3: thread2 end
Test worker @coroutine#2: thread1 end</code></pre>
<p>위와 같이 thread1는 3초 thread2는 1초가 걸린다면 result에 값이 초기화 되기 전에 읽으려고 하기 때문에 result값이 null인 것을 확인할 수 있다.</p>
<p>즉 공유자원인 result를 여러 스레드에서 사용할 때 동기화 이슈를 해결해야 한다.</p>
<p>여러 해결 방안이 있지만 Mutex를 사용하여 해결해 보면</p>
<pre><code class="language-kotlin">private val mutex = Mutex()
    private var result: String? = null

    @Test
    fun main(): Unit = runBlocking {
        launch { thread1() }
        launch { thread2() }
    }

    private suspend fun thread1() = mutex.withLock {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread1 start&quot;)
        delay(3000L)
        result = &quot;Success&quot;
        println(&quot;$currentThreadName: thread1 end&quot;)
    }

    private suspend fun thread2() = mutex.withLock {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread2 start&quot;)
        delay(1000L)
        println(&quot;$currentThreadName: 결과는?: $result&quot;)
        println(&quot;$currentThreadName: thread2 end&quot;)
    }</code></pre>
<p>결과</p>
<pre><code class="language-kotlin">Test worker @coroutine#2: thread1 start
Test worker @coroutine#2: thread1 end
Test worker @coroutine#3: thread2 start
Test worker @coroutine#3: 결과는?: Success
Test worker @coroutine#3: thread2 end</code></pre>
<p>위와 같이 동기화를 보장해 줄 수 있다.
결과를 살펴보면 thread1이 <code>withLock</code> 함수를 통해 임계 영역으로 들어가면 해당 mutex가 unlock이 되기 전엔 thread2가 해당 mutex를 선점할 수 없기 때문에 동기화가 보장이 된다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/ea82b697-ab8d-452e-83ab-c703b3c0d3bd/image.png" alt=""></p>
<p>위는 <code>withLock</code> 함수의 내부인데 살펴보면 코드 블럭이 실행 되기 전에 lock이 되며 끝나면 unlock이 되는 걸 확인 할 수 있다.</p>
<p>또한 <code>withLock</code> 함수의 owner는 기본적으로 코틀린에서 뮤텍스가 비재진입을 가능하도록 보장해 준다.</p>
<pre><code class="language-kotlin">    private val owner = Any()
    private val mutex = Mutex()
    private var result: String? = null

    @Test
    fun main(): Unit = runBlocking {
        launch { thread1() }
        launch { thread2() }
    }

    private suspend fun thread1() = mutex.withLock(owner) {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread1 start&quot;)
        delay(3000L)
        result = &quot;Success&quot;
        println(&quot;$currentThreadName: thread1 end&quot;)
    }

    private suspend fun thread2() = mutex.withLock(owner) {
        val currentThreadName = Thread.currentThread().name
        println(&quot;$currentThreadName: thread2 start&quot;)
        delay(1000L)
        println(&quot;$currentThreadName: 결과는?: $result&quot;)
        println(&quot;$currentThreadName: thread2 end&quot;)
    }</code></pre>
<p>결과
<img src="https://velog.velcdn.com/images/plz_no_anr/post/d0f8736f-cc0b-4a78-8963-6e3a3935e9bc/image.png" alt=""></p>
<p>위의 코드 결과를 보면 에러가 발생하는 걸 볼 수 있다.
이는 thread1이 owner를 통해 이미 해당 뮤텍스를 소유하고 있는데 thread2가 해당 뮤텍스에 재진입하려고 했기 때문에 위와 같은 에러가 발생한다.</p>
<p>재진입이 가능한 lock을 사용하려면 <code>ReentrantLock</code>(Java의 재진입 락)을 사용하면 된다.</p>
<hr>
<p>멀티 스레딩에서 동기화 이슈는 언제봐도 머리가 아프다...</p>
<h4 id="references">References</h4>
<p><a href="https://ko.wikipedia.org/wiki/%EA%B2%BD%EC%9F%81_%EC%83%81%ED%83%9C">경쟁상태</a>
<a href="https://ko.wikipedia.org/wiki/%EC%84%B8%EB%A7%88%ED%8F%AC%EC%96%B4">세마포어</a>
<a href="https://www.geeksforgeeks.org/difference-between-binary-semaphore-and-mutex/">세마포어-뮤텍스</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[DataStructure] 자료구조의 종류]]></title>
            <link>https://velog.io/@plz_no_anr/DataStructure-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%9D%98-%EC%A2%85%EB%A5%98</link>
            <guid>https://velog.io/@plz_no_anr/DataStructure-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%9D%98-%EC%A2%85%EB%A5%98</guid>
            <pubDate>Mon, 11 Nov 2024 02:14:13 GMT</pubDate>
            <description><![CDATA[<p><del>저기요 여기 배열 안에 사람 있어요</del></p>
<h4 id="자료구조는-왜-필요할까">자료구조는 왜 필요할까?</h4>
<p>자료구조는 데이터를 효율적으로 저장하고 관리하기 위해 사용되는 구조
각 자료구조는 특정한 상황에서 데이터를 다루는 데 최적화되어 있다.</p>
<h2 id="1-기본-자료구조">1. 기본 자료구조</h2>
<ul>
<li><p><strong>배열 (Array)</strong>: 동일한 타입의 요소를 연속된 메모리 공간에 저장하는 자료구조로 고정된 크기를 가지며, 인덱스를 통해 빠르게 접근할 수 있다. 
보통 특정 요소를 빠르게 읽어야 할 때 사용한다.</p>
</li>
<li><p><strong>연결 리스트 (Linked List)</strong>: 요소들이 노드 형태로 존재하며, 각 노드는 다음 노드를 가리키는 포인터를 포함한다. 크기가 가변적이며, 삽입과 삭제가 배열보다 효율적이고 배열과 달리 크기가 가변적이라 메모리 낭비가 적다.</p>
<ul>
<li>단일 연결 리스트: 각 노드가 다음 노드를 가리킴</li>
<li>이중 연결 리스트: 각 노드가 이전 및 다음 노드를 가리킴</li>
<li>원형 연결 리스트: 마지막 노드가 처음 노드를 가리킴</li>
</ul>
</li>
</ul>
<h2 id="2-선형-자료구조">2. 선형 자료구조</h2>
<ul>
<li><p><strong>스택 (Stack)</strong>: LIFO (Last In, First Out) 원칙을 따르는 자료구조
요소의 삽입과 삭제는 스택의 한쪽 끝에서만 일어난다. 
대표적인 연산으로는 push, pop 등이 있다.</p>
</li>
<li><p><strong>큐 (Queue)</strong>: FIFO (First In, First Out) 원칙을 따르는 자료구조 
요소가 한쪽 끝에서 삽입되고 반대쪽 끝에서 제거된다. 
변형된 큐로는 원형 큐, 우선순위 큐 등이 있다. 
<del>*tmi: 리그오브레전드에서 사용하는 단어인 큐도 이와 같은 원리</del></p>
</li>
<li><p><strong>덱 (Deque)</strong>: 양쪽 끝에서 삽입과 삭제가 가능한 자료구조
스택과 큐의 기능을 모두 수행할 수 있다.</p>
</li>
</ul>
<h2 id="3-비선형-자료구조">3. 비선형 자료구조</h2>
<ul>
<li><p><strong>트리 (Tree)</strong>: 계층 구조를 표현하는 자료구조로, 부모-자식 관계를 가진다.</p>
<ul>
<li>이진 트리 (Binary Tree): 각 노드가 최대 두 개의 자식을 가진다.</li>
<li>이진 탐색 트리 (BST): 이진 트리의 일종으로, 왼쪽 자식은 부모보다 작고, 오른쪽 자식은 부모보다 크다  </li>
<li>AVL 트리: 자체 균형 이진 탐색 트리로, 삽입과 삭제 시 균형을 유지한다. <pre><code>- 힙 (Heap): 이진 트리 기반의 자료구조로, 최대값 또는 최소값을 빠르게 찾을 수 있다. 최대 힙(max heap)과 최소 힙(min heap)으로 구분된다.</code></pre></li>
</ul>
</li>
<li><p><strong>그래프 (Graph)</strong>: 정점과 간선의 집합으로, 정점들이 간선으로 연결되어 있는 구조입니다.</p>
<ul>
<li>무방향 그래프: 간선이 양방향</li>
<li>유방향 그래프: 간선이 특정 방향으로만 연결됨</li>
<li>가중치 그래프: 간선에 가중치가 부여된 그래프</li>
</ul>
</li>
</ul>
<h2 id="4-집합-및-사전-자료구조">4. 집합 및 사전 자료구조</h2>
<ul>
<li><p><strong>집합 (Set)</strong>: 중복되지 않는 요소의 집합. 주로 해시 테이블이나 이진 탐색 트리를 사용해 구현된다.</p>
</li>
<li><p><strong>맵/딕셔너리 (Map/Dictionary)</strong>: 키-값 쌍을 저장하는 자료구조로, 키를 통해 값을 효율적으로 검색할 수 있습니다. 코틀린이나 자바의 HashMap, 파이썬의 dict 등이 이에 해당합니다.</p>
</li>
</ul>
<h2 id="5-고급-자료구조">5. 고급 자료구조</h2>
<ul>
<li><p><strong>트라이 (Trie)</strong>: 문자열 검색을 빠르게 수행하기 위한 트리 형태의 자료구조.</p>
</li>
<li><p><strong>세그먼트 트리 (Segment Tree)</strong>: 배열의 특정 구간의 합이나 최댓값을 효율적으로 계산할 수 있는 트리</p>
</li>
<li><p>** B-트리 (B-Tree)**: 자식 노드의 수가 2개 이상일 수 있는 트리로, 데이터베이스나 파일 시스템에서 사용된다.</p>
</li>
<li><p><strong>레드-블랙 트리 (Red-Black Tree)</strong>: 이진 탐색 트리의 일종으로, 균형을 유지하며 삽입과 삭제 연산의 시간복잡도를 𝑂(log⁡𝑛)으로 보장한다.</p>
</li>
</ul>
<h2 id="6-기타-자료구조">6. 기타 자료구조</h2>
<ul>
<li><p><strong>해시 테이블 (Hash Table)</strong>: 해시 함수를 사용해 데이터를 키-값 쌍으로 저장하며, 빠른 검색과 삽입을 제공한다. 
(위에서 본 자바와 코틀린에서 사용하는 HashMap도 Map 인터페이스를 해시 테이블을 사용하여 구현되어 있다.)</p>
</li>
<li><p><strong>비트맵 (Bitmap)</strong>: 비트 배열을 사용해 데이터를 효율적으로 저장하며, 주로 집합이나 블룸 필터 구현에 사용한다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] delegate 패턴이란? feat. 상속]]></title>
            <link>https://velog.io/@plz_no_anr/Kotlin-delegate-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-feat.-%EC%83%81%EC%86%8D</link>
            <guid>https://velog.io/@plz_no_anr/Kotlin-delegate-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80-feat.-%EC%83%81%EC%86%8D</guid>
            <pubDate>Fri, 08 Nov 2024 01:59:06 GMT</pubDate>
            <description><![CDATA[<p><del>내 코드도 클로드한테 위임해</del></p>
<p>코틀린에서 <strong>위임(delegate) 패턴</strong>과 <strong>상속(Inheritance)</strong>은 둘 다 객체의 재사용 및 확장을 위한 방법이지만, 사용하는 방식과 적용 목적에서 차이가 있다.</p>
<h2 id="1-상속inheritance">1. 상속(Inheritance)</h2>
<p>부모 클래스의 기능을 자식 클래스가 상속받아 재사용할 수 있게 한다.
단일 상속만을 지원하므로, 하나의 클래스만 상속할 수 있다.
상속된 클래스는 부모 클래스의 메서드를 오버라이드할 수 있으며, 부모 클래스의 기능을 확장하거나 수정하여 사용할 수 있다.
상속 관계는 IS-A 관계라고 하며, 자식 클래스는 부모 클래스의 일종으로 간주된다.</p>
<h3 id="is-a-관계">Is-a 관계</h3>
<ul>
<li><p>Is-a는 추상화(형식이나 클래스와 같은)들 사이의 포함 관계를 의미하며, 한 클래스 A가 다른 클래스 B의 서브클래스(파생클래스)임을 이야기한다. 다른 말로, 타입 A는 타입 B의 명세(specification)를 암시한다는 점에서 타입 B의 서브타입이라고도 할 수 있다.</p>
</li>
<li><p>Is-a 관계는 타입 또는 클래스 간의 Has-a 관계와는 대조된다. Has-a 및 Is-a 관계들 간의 혼동은 실세계 관계 모델에 대한 설계에 있어 자주 발견되는 에러이다. Is-a 관계는 또한 객체 또는 타입 간의 instance-of 관계와도 대조된다.</p>
</li>
</ul>
<p>특징</p>
<blockquote>
<ul>
<li>부모 클래스의 메서드와 속성을 상속    </li>
</ul>
</blockquote>
<ul>
<li>단일 상속만 가능    </li>
<li>부모 클래스와 강한 결합</li>
<li>객체 간의 관계가 본질적으로 IS-A 관계일 때, 즉 한 객체가 다른 객체의 일종일 때 사용</li>
</ul>
<p>사용 예시</p>
<pre><code class="language-kotlin">open class Developer {
    open fun work() = &quot;develop...&quot;
}

class AndroidDeveloper : Developer() {
    override fun work() = &quot;develop android app...&quot;
}

class AppDeveloper: AndroidDeveloper()</code></pre>
<pre><code class="language-kotlin">val appDeveloper = AppDeveloper()
appDeveloper.work() // &quot;develop android app...&quot;</code></pre>
<p>위와 같이 사용한다면 유연한 설계가 불가능하고 AppDeveloper는 AndroidDeveloper class에 강하게 결합된다.</p>
<h2 id="2-위임delegate-패턴">2. 위임(Delegate) 패턴</h2>
<p>특정 객체에 작업을 위임하여 해당 객체의 기능을 활용할 수 있다.
HAS-A 관계로, 객체는 위임된 클래스의 기능을 가지면서 해당 클래스와 협력하여 작업을 수행한다.
<strong>구성(composition)</strong>을 이용해 객체를 구성하고, 인터페이스나 다른 클래스의 구현을 위임함으로써 다중 상속이 필요한 경우 유용하게 사용할 수 있다.
코틀린에서는 <strong>by 키워드</strong>를 사용해 인터페이스에 대한 위임을 간단히 구현할 수 있다.</p>
<h3 id="has-a-관계">Has-a 관계</h3>
<ul>
<li>Has-a는 구성 관계를 의미하며 한 오브젝트(구성된 객체, 또는 부분/멤버 객체라고도 부릅니다)가 다른 오브젝트(composite type이라고 부릅니다)에 &quot;속한다(belongs to)&quot;를 말합니다. 단순히 말해, has-a 관계는 객체의 멤버 필드라고 불리는 객체를 말하며, Multiple has-a 관계는 소유 계층구조를 형성하기 위해 결합하는 경우를 말합니다.</li>
</ul>
<p>특징</p>
<blockquote>
<ul>
<li>위임 객체의 메서드를 사용</li>
</ul>
</blockquote>
<ul>
<li>여러 위임 객체를 통해 다중 기능 가능</li>
<li>유연하며 교체 가능성 높음</li>
<li>유연한 설계가 필요하거나, 다중 상속이 필요한 상황에서 구성(composition)을 통해 기능을 추가하고자 할 때 유용</li>
</ul>
<p>사용 예시</p>
<pre><code class="language-kotlin">interface Developer {
    fun work(): String
}

class AndroidDeveloper : Developer {
    override fun work() = &quot;develop android app&quot;
}

class AppDeveloper(private val developer: Developer) : Developer by developer</code></pre>
<pre><code class="language-kotlin">val appDeveloper = AppDeveloper(AndroidDeveloper())
appDeveloper.work() // &quot;develop android app&quot;</code></pre>
<p>위와 같이 사용한다면 AppDeveloper는 AndroidDeveloper와 강하게 결합되지 않아 다음과 같이 유연한 사용이 가능하다.</p>
<pre><code class="language-kotlin">class iOSDeveloper: Developer {
    override fun work(): String = &quot;develop ios app&quot;
}</code></pre>
<pre><code class="language-kotlin">val appDeveloper = AppDeveloper(iOSDeveloper())
appDeveloper.work() // &quot;develop ios app&quot;</code></pre>
<p>위의 코드를 보면 AppDeveloper 클래스는 Developer 인터페이스를 구현하지만 AndroidDeveloper나 iOSDeveloper 클래스를 상속하지는 않는다.</p>
<hr>
<h4 id="정리">정리</h4>
<p><strong>상속</strong>의 경우 is 관계가 확실하면 사용하는 것이 좋다.</p>
<ul>
<li>사람은 인간이다.</li>
<li>강아지는 동물이다.</li>
</ul>
<p><strong>구성(composition)</strong>의 경우, 하나의 객체가 다른 객체에 부분적으로 포함 하는 경우 사용할 수 있다.</p>
<ul>
<li>스마트폰은 카메라를 가진다.</li>
<li>자동차는 바퀴를 가진다.</li>
</ul>
<h4 id="references">References</h4>
<p><a href="https://kotlinlang.org/docs/delegation.html">Kotlin-Delegation</a>
<a href="https://en.wikipedia.org/wiki/Is-a">Is-a</a>
<a href="https://en.wikipedia.org/wiki/Has-a">Has-a</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] inline 키워드란? with crossinline]]></title>
            <link>https://velog.io/@plz_no_anr/Kotlin-inline-%ED%82%A4%EC%9B%8C%EB%93%9C%EB%9E%80-with.-crossinline</link>
            <guid>https://velog.io/@plz_no_anr/Kotlin-inline-%ED%82%A4%EC%9B%8C%EB%93%9C%EB%9E%80-with.-crossinline</guid>
            <pubDate>Thu, 07 Nov 2024 01:10:04 GMT</pubDate>
            <description><![CDATA[<p><del>인라인 한번도 안 타본 1인</del></p>
<p><code>inline</code> 키워드는 함수 호출을 <strong>인라이닝(inlining)</strong> 하도록 컴파일러에 지시하는 키워드로, 주로 람다나 고차 함수에서 성능을 최적화할 때 사용된다.</p>
<h3 id="inline-키워드">inline 키워드</h3>
<ol>
<li><p><strong>함수 호출 비용 감소</strong>
<code>inline</code> 키워드가 붙은 함수는 컴파일 시 함수 호출이 인라인되어 함수의 본문이 호출 지점에 직접 삽입된다. 따라서 함수 호출에 따른 오버헤드를 줄일 수 있다.</p>
</li>
<li><p><strong>람다와 고차 함수에서 유용함</strong>
람다 표현식은 객체로 만들어지고, 메모리에 할당되며 함수 호출 시 람다 객체를 통해 호출이 이루어진다.
<code>inline</code> 키워드를 사용하면 람다도 인라인으로 삽입되어 불필요한 객체 생성이 줄어들고 성능이 향상된다.
따라서 고차 함수에 람다를 여러 개 넘기는 경우, inline을 사용해 성능 최적화를 할 수 있다.</p>
</li>
<li><p><strong>Non-local returns</strong>
인라인 함수 내에서 람다를 사용할 때 return을 통해 호출한 지점에서 함수 전체를 종료할 수 있다. 일반적인 람다에서는 return이 로컬 리턴(local return)으로 작동하지만, <code>inline</code> 함수의 경우 함수 자체를 종료하는 <strong>non-local return</strong>이 가능하다.</p>
</li>
</ol>
<pre><code class="language-kotlin">inline fun inlineFunction(value: Int, block: (Int) -&gt; Int): Int {
    return block(value)
}

fun main() {
    val result = inlineFunction(5) { it * it }
    println(result) // 출력: 25
}
</code></pre>
<p><code>inline</code> 함수에 전달되는 람다 중 특정 람다만 인라인하고 싶지 않은 경우, <code>noinline</code> 키워드를 사용할 수 있다.</p>
<pre><code class="language-kotlin">inline fun performOperation(value: Int, block1: (Int) -&gt; Int, noinline block2: (Int) -&gt; Int): Int {
    return block2(block1(value))
}</code></pre>
<p>위처럼 <code>noinline</code> 키워드를 사용하면 block2 람다는 인라인되지 않는다.</p>
<p><code>inline</code> 사용 시 주의할 점으로는 </p>
<ul>
<li><strong>코드 크기 증가</strong>: 인라인 함수는 호출 위치에 본문이 복사되므로, 함수가 많거나 복잡하면 바이너리 크기가 커질 수 있다.</li>
<li><strong>재귀 함수</strong>: 인라인 함수는 자기 자신을 재귀적으로 호출할 수 없다.</li>
</ul>
<h3 id="crossinline-키워드">crossinline 키워드</h3>
<p><code>crossinline</code> 키워드는 inline 함수의 파라미터로 전달된 람다가 비지역(non-local) return을 허용하지 않도록 할 때 사용된다.</p>
<p>일반적으로 inline 함수의 람다에서는 <strong>비지역 return</strong>을 사용할 수 있다.
비지역 return은 람다 내부에서 return을 호출하면, 해당 람다뿐 아니라 inline 함수를 호출한 곳에서 바로 리턴되도록 하는 것이다. 그러나 때로는 비지역 return을 허용하면 문제를 일으킬 수 있는 경우가 있어서, 이를 방지하기 위해 crossinline을 사용한다.</p>
<pre><code class="language-kotlin">inline fun nocrossinlineFunction(block: () -&gt; Unit) {
    println(&quot;Before block&quot;)
    block() // 이곳에서 람다 호출
    println(&quot;After block&quot;)
}

fun main() {
    myFunction {
        println(&quot;Inside block&quot;)
        return // 비지역 return이므로 main 함수로 리턴
    }
    println(&quot;Outside block&quot;) // 호출되지 않는다.
}</code></pre>
<pre><code class="language-kotlin">inline fun crossinlineFunction(crossinline block: () -&gt; Unit) {
    println(&quot;Before block&quot;)
    action() // 비지역 return이 허용되지 않아 에러 발생
    println(&quot;After block&quot;)
}

fun main() {
    myFunction {
        println(&quot;Inside block&quot;)
        // return // 컴파일 오류 발생
    }
    println(&quot;Outside block&quot;)
}</code></pre>
<p>위와 같이 <code>crossinline</code> 사용 시 람다에서 비지역 return을 호출할 수 없게 되어 오류가 발생해 비지역 return을 방지한다.</p>
<p>crossinline은 다음과 같은 경우에 유용합니다.</p>
<ul>
<li><strong>비동기 처리나 콜백 함수로 람다가 전달될 때</strong>: 이런 경우 람다가 다른 스레드나 비동기 작업에 의해 실행될 수 있으므로 비지역 return이 호출되면 예기치 않은 동작을 유발할 수 있다.</li>
<li><strong>제어 흐름을 명확하게 유지하고 싶을 때</strong>: inline 함수에서 코드 흐름이 혼란스럽지 않도록 return을 제한하고 싶을 때 crossinline을 사용한다.
따라서, crossinline을 사용하면 안전한 코드 작성과 더불어, 코드 흐름을 명확하게 제어할 수 있습니다.</li>
</ul>
<hr>
<p>잘 사용한다면 성능 최적화 면에서 좋을 것 같다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Hilt의 @Reusable]]></title>
            <link>https://velog.io/@plz_no_anr/Android-Hilt%EC%9D%98-Reusable</link>
            <guid>https://velog.io/@plz_no_anr/Android-Hilt%EC%9D%98-Reusable</guid>
            <pubDate>Thu, 31 Oct 2024 00:42:10 GMT</pubDate>
            <description><![CDATA[<p>안드로이드에서 많이 사용하는 DI 라이브러리인 Hilt의 @Reusable !!</p>
<p><strong>@Reusable</strong>은 특정 객체를 필요에 따라 재사용할 수 있도록 하는 Dagger의 스코프 어노테이션으로 <strong>객체가 자주 필요하지만, 매번 새로운 인스턴스를 생성하는 것이 비효율적일 때</strong> 유용하다.</p>
<p><strong>@Reusable</strong>로 주입된 객체는 <strong>가능한 경우 캐시에서 가져와 사용하고, 그렇지 않으면 새로 생성</strong>된다.</p>
<h2 id="1-reusable의-특징">1. @Reusable의 특징</h2>
<ul>
<li><p><strong>효율적인 메모리 관리</strong>: @Reusable로 주입된 객체는 Dagger가 관리하는 캐시에서 재사용될 수 있어, 필요할 때마다 새 인스턴스를 생성하는 부담을 줄인다.</p>
</li>
<li><p><strong>조건부 재사용</strong>: Dagger의 내부 캐시에 이전에 생성된 객체가 존재하면 이를 반환하지만, 그렇지 않은 경우 새로운 객체를 생성하여 반환한다.</p>
</li>
<li><p><strong>비교적 짧은 생명주기</strong>: @Reusable은 @Singleton처럼 앱 전체에서 유일한 인스턴스를 보장하지 않는다. 즉, 캐시에 객체가 남아 있는 한 재사용되지만, 필요하지 않으면 언제든지 제거될 수 있다.</p>
</li>
</ul>
<h2 id="2-reusable-사용-예제">2. @Reusable 사용 예제</h2>
<p><strong>@Reusable</strong>은 주로 짧은 생명 주기를 가지는 객체나 빈번하게 호출되지만 매번 인스턴스를 생성할 필요는 없는 경우에 사용된다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Reusable
    fun provideLoggingInterceptor(): HttpLoggingInterceptor {
        return HttpLoggingInterceptor().apply {
            level = HttpLoggingInterceptor.Level.BODY
        }
    }
}</code></pre>
<p>위의 코드는 Okhttp의 LoggingInterceptor를 주입하는 예제인데</p>
<h2 id="3-reusable-vs-singleton">3. @Reusable vs @Singleton</h2>
<ul>
<li><p><strong>@Singleton</strong>: 앱의 생명 주기 동안 객체의 단일 인스턴스를 보장하며, 메모리에 상주한다.</p>
</li>
<li><p><strong>@Reusable</strong>: 객체가 필요할 때마다 캐시에서 가져오거나 새로 생성될 수 있어 메모리 사용량을 절약할 수 있다.</p>
</li>
</ul>
<h2 id="4-그렇다면-언제-reusable을-사용할까">4. 그렇다면 언제 @Reusable을 사용할까?</h2>
<ul>
<li><p>매번 새로운 인스턴스를 생성할 필요는 없지만, 단일 인스턴스를 유지해야 할 필요도 없는 경우</p>
</li>
<li><p>네트워크 요청이나 로깅과 같은 자주 호출되는 객체에서 사용하여, 성능을 최적화할 수 있습니다.</p>
</li>
</ul>
<hr>
<h4 id="이처럼-reusable은-메모리-효율성과-성능을-높이는-데-유용하며-메모리-최적화가-필요한-빈번한-작업에-적합한-스코프이다">이처럼 @Reusable은 메모리 효율성과 성능을 높이는 데 유용하며, 메모리 최적화가 필요한 빈번한 작업에 적합한 스코프이다.</h4>
<h4 id="references">References</h4>
<p><a href="https://dagger.dev/hilt/">Dagger-Hilt</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Hilt의 Provider<T>와 Lazy<T>]]></title>
            <link>https://velog.io/@plz_no_anr/Android-Hilt%EC%9D%98-Provider%EC%99%80-Lazy</link>
            <guid>https://velog.io/@plz_no_anr/Android-Hilt%EC%9D%98-Provider%EC%99%80-Lazy</guid>
            <pubDate>Wed, 30 Oct 2024 06:44:48 GMT</pubDate>
            <description><![CDATA[<p>안드로이드에서 많이 사용하는 DI 라이브러리인 Hilt에서 보이는 <code>Provider&lt;T&gt;</code>와 <code>Lazy&lt;T&gt;</code>의 차이점!</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/5b6bc9c3-e3ea-47c6-be85-36320510945d/image.png" alt="">
<img src="https://velog.velcdn.com/images/plz_no_anr/post/f6523c39-4a4b-4c1d-a6b2-4d1450fb8316/image.png" alt=""></p>
<p>우선 위 두 개는 모두 의존성 객체를 지연 초기화(lazy initialization)하는 방법으로, 필요할 때만 생성하도록 도와준다.
코틀린에서 <code>by lazy</code> 키워드 같이 실제 사용 시 객체를 초기화 하여 주입 시켜주는 방법이다.</p>
<h4 id="그렇다면-둘의-차이점은-뭘까">그렇다면 둘의 차이점은 뭘까?</h4>
<h3 id="1-providert">1. <code>Provider&lt;T&gt;</code></h3>
<p><code>Provider&lt;T&gt;</code>는 호출할 때마다 새로운 객체를 생성한다. 즉, 여러 번 호출할 경우 매번 다른 인스턴스를 얻는다.
이 방법은 매번 새로운 객체가 필요한 경우 유용하다.</p>
<pre><code class="language-kotlin">class MyClass @Inject constructor(
    private val dependencyProvider: Provider&lt;MyDependency&gt;
) {
    fun useDependency() {
        val dependency = dependencyProvider.get() // 매 호출마다 새 인스턴스 반환
    }
}</code></pre>
<h4 id="의존성-객체를-여러-번-생성할-수-있으므로-각-호출에서-별도의-인스턴스가-필요할-때-유용하다">의존성 객체를 여러 번 생성할 수 있으므로 각 호출에서 별도의 인스턴스가 필요할 때 유용하다.</h4>
<h3 id="2-lazyt">2. <code>Lazy&lt;T&gt;</code></h3>
<p><code>Lazy&lt;T&gt;</code>는 객체를 한 번만 생성하고, 이후에는 이미 생성된 인스턴스를 재사용하도록 한다.
Hilt에서 Lazy를 사용하려면 @Inject된 필드나 생성자에서 Lazy<T>로 감싸면 된다.</p>
<pre><code class="language-kotlin">class MyClass @Inject constructor(
    private val lazyDependency: Lazy&lt;MyDependency&gt;
) {
    fun useDependency() {
        val dependency = lazyDependency.get() // 첫 번째 호출 시에만 생성
    }
}</code></pre>
<h4 id="처음-접근할-때만-객체를-생성하므로-불필요한-초기화를-피할-수-있다-이후-재사용되므로-메모리-사용량이-효율적이다">처음 접근할 때만 객체를 생성하므로 불필요한 초기화를 피할 수 있다. 이후 재사용되므로 메모리 사용량이 효율적이다.</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Hilt의 어노테이션]]></title>
            <link>https://velog.io/@plz_no_anr/Android-Hilt%EC%9D%98-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@plz_no_anr/Android-Hilt%EC%9D%98-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98</guid>
            <pubDate>Wed, 30 Oct 2024 06:30:25 GMT</pubDate>
            <description><![CDATA[<p><del>@@@@ 골뱅이</del></p>
<p>안드로이드에서 많이 사용하는 DI 라이브러리인 Hilt에서 자주 사용하는 Annotation 정리 및 사용 예시</p>
<h3 id="1-hiltandroidapp">1. @HiltAndroidApp</h3>
<p>Application 클래스에 붙이며, Hilt가 애플리케이션의 의존성 그래프를 생성할 수 있도록 해준다. 이 어노테이션을 추가하면 Hilt가 애플리케이션의 전반적인 라이프사이클을 관리하게 된다.</p>
<pre><code class="language-kotlin">@HiltAndroidApp
class MyApplication : Application()</code></pre>
<h3 id="2-androidentrypoint">2. @AndroidEntryPoint</h3>
<p>Activity, Fragment, Service, View, BroadcastReceiver 등에 붙이며, Hilt가 해당 클래스에서 의존성 주입을 가능하게 해준다. 이 어노테이션을 붙이면 해당 클래스에서 @Inject를 통해 의존성을 주입받을 수 있다.</p>
<pre><code class="language-kotlin">@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    ...
}</code></pre>
<h3 id="3-inject">3. @Inject</h3>
<p>생성자나 필드에 붙여 Hilt가 해당 클래스 또는 필드의 인스턴스를 주입하도록 한다. 생성자 주입과 필드 주입 모두 가능하며, 일반적으로는 생성자 주입이 권장된다.</p>
<pre><code class="language-kotlin">@Inject lateinit var networkManager: NetworkManager // 필드 주입

class MyViewModel @Inject constructor(
    .... // 생성자 주입
)</code></pre>
<h3 id="4-module">4. @Module</h3>
<p>Hilt 모듈을 정의하기 위해 사용된다. 모듈은 의존성 그래프에 추가할 의존성을 제공하며, Hilt는 @Module이 붙은 클래스를 통해 의존성을 제공한다.
보통 @InstallIn 와 같이 사용한다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)
object LocalDataModule {
    ...
}</code></pre>
<h3 id="5-installin">5. @InstallIn</h3>
<p>모듈 클래스에 @Module과 함께 붙여 사용하는 어노테이션으로, 모듈의 라이프사이클 범위를 지정한다. 예를 들어 @SingletonComponent에 설치하면 애플리케이션 전체에 의존성이 유지된다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)  
object LocalDataModule {
    ...
}</code></pre>
<p>위에서 SingletonComponent로 선언 시 애플리케이션 스코프에 해당 모듈이 생성된다.</p>
<h3 id="6-provides">6. @Provides</h3>
<p>@Module 내부의 메소드에 붙이며, 해당 메소드가 의존성을 제공할 수 있도록 한다. 이 메서드를 통해 특정 타입의 인스턴스를 생성하여 의존성 그래프에 추가할 수 있다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)
object LocalDataModule {
    @Provides
    fun provideLocalData(
        @ApplicationContext context: Context
    ): LocalDataSource {
        return LocalDataSourceImpl(
               context = context
        )
    }
}</code></pre>
<p>위에서 @Provides만 사용 시 사용될 때 마다 인스턴스를 새로 생성한다.</p>
<h3 id="7-singleton">7. @Singleton</h3>
<p>Hilt의 @Provides나 클래스 레벨에 붙여 인스턴스가 싱글톤으로 관리되도록 합니다. @SingletonComponent와 함께 사용하여 애플리케이션 전역에서 인스턴스가 하나만 생성되도록 할 수 있다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)
object LocalDataModule {
    @Provides
    @Singleton
    fun provideLocalData(
        @ApplicationContext context: Context
    ): LocalDataSource {
        return LocalDataSourceImpl(
               context = context
        )
    }
}</code></pre>
<p>@Singleton 사용 시 해당 인스턴스는 객체를 한 번만 생성하고, 이후에는 이미 생성된 인스턴스를 재사용한다.</p>
<h3 id="8-binds">8. @Binds</h3>
<p>@Module 내부에 사용하여 인터페이스를 구현체에 바인딩할 때 사용한다. 인터페이스와 이를 구현하는 클래스 간의 매핑을 정의할 수 있다.</p>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)
interface RepositoryModule {

    @Binds
    fun bindMyRepository(
        impl: DefaultMyRepository // 실제 구현체
    ): MyRepository // 인터페이스

}</code></pre>
<p>위 예시처럼 Repository나 DataSource, 등 인터페이스와 실제 구현체를 연결 해주는 역할을 한다.</p>
<h3 id="9-qualifier">9. @Qualifier</h3>
<p>동일한 타입의 서로 다른 인스턴스를 구분하기 위해 사용된다. 커스텀 Qualifier를 만들어 @Inject 또는 @Provides와 함께 사용할 수 있다.</p>
<pre><code class="language-kotlin">enum class AppDispatcher {
    Default,
    IO,
    Main
}

@Retention(AnnotationRetention.BINARY)
@Qualifier
annotation class Dispatcher(val dispatcher: AppDispatcher)

// 
@Dispatcher(AppDispatcher.IO)
@Provides
fun provideIoDispatcher(): CoroutineDispatcher = Dispatchers.IO

// 사용 예시
class MyRepository @Inject constructor(
    @Dispatcher(AppDispatcher.IO) private val ioDispatcher: CoroutineDispatcher
) {
    ...
}

</code></pre>
<h3 id="10-hiltviewmodel">10. @HiltViewModel</h3>
<p>ViewModel 클래스에 붙이며, @Inject 생성자와 함께 사용하여 ViewModel에 의존성을 주입할 수 있다.
이 어노테이션들은 Hilt의 의존성 주입 시스템을 구성하는 중요한 요소들이며, 주입 위치나 범위에 따라 적절하게 사용하여야 한다.</p>
<pre><code class="language-kotlin">@HiltViewModel
class MainViewModel @Inject constructor(
    private val myRepository: MyRepository
)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] Coroutine의 Dispatcher란 무엇인가]]></title>
            <link>https://velog.io/@plz_no_anr/Kotlin-Coroutine%EC%9D%98-Dispatcher%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@plz_no_anr/Kotlin-Coroutine%EC%9D%98-Dispatcher%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Wed, 30 Oct 2024 01:00:09 GMT</pubDate>
            <description><![CDATA[<p><del>dispatch = 보내다?</del></p>
<p>코틀린으로 비동기 처리 시 이제는 거의 기본이 되어버린 코루틴..
이 코루틴에서 자주 보이는 <strong>Dispatcher</strong>란 무엇인가..?</p>
<p>Dispatcher는 <strong>스레드를 관리하는 추상화 계층</strong>으로, <strong>스레드 풀을 통해 스레드 리소스를 효율적으로 할당</strong>하고, 코루틴이 어떤 스레드나 스레드 풀에서 실행될지를 결정하는 역할을 하고, 실제 스레드는 코루틴의 스케줄러가 관리한다.</p>
<h4 id="즉-개발자가-직접-스레드를-관리할-필요-없이-좀-더-쉽게-비동기-처리를-하도록-도와주는-매개체이다">즉 개발자가 직접 스레드를 관리할 필요 없이 좀 더 쉽게 비동기 처리를 하도록 도와주는 매개체이다.</h4>
<h2 id="dispatcher의-종류">Dispatcher의 종류</h2>
<h3 id="1-dispatchersmain">1. Dispatchers.Main</h3>
<ul>
<li><p>메인(UI) 스레드에서 코루틴을 실행 </p>
</li>
<li><p>주로 UI 관련 작업에 사용되며, 메인 스레드는 리소스가 제한적이므로, 시간이 오래 걸리는 작업을 여기에서 실행하면 앱이 멈추거나 느려질 수 있다.</p>
</li>
</ul>
<h3 id="2-dispatchersio">2. Dispatchers.IO</h3>
<ul>
<li><p>I/O 작업에 최적화된 스레드 풀에서 코루틴을 실행
여러 I/O 작업(파일 읽기/쓰기, 네트워크 요청, 데이터베이스 작업 등)에 적합하며, 필요에 따라 스레드를 많이 생성할 수 있다.</p>
</li>
<li><p>네트워크 요청과 같은 느린 I/O 작업
파일 입출력, 데이터베이스 쿼리 등 비동기적 I/O 작업</p>
</li>
<li><p>스레드 수에 제한이 없어, 많은 동시 I/O 요청을 처리해야 하는 경우 적합하다.</p>
</li>
</ul>
<h3 id="3-dispatchersdefault">3. Dispatchers.Default</h3>
<ul>
<li><p>CPU 집약적 작업에 최적화된 스레드 풀에서 코루틴을 실행
CPU 코어 수에 맞추어 기본적으로 스레드를 생성하므로, 대규모 계산 작업에 적합하다.</p>
</li>
<li><p>복잡한 계산, 예를 들어 배열, 리스트를 처리하거나 무거운 수학 연산을 수행할 때
데이터 처리, 정렬, 필터링 등 CPU 사용량이 높은 작업</p>
</li>
<li><p>일반적으로 CPU 코어 수에 맞추어 스레드 수를 제한하여 사용하기 때문에, 동시에 많은 작업이 발생하지 않도록 CPU 사용량을 관리할 수 있다.</p>
</li>
</ul>
<h3 id="4-dispatchersunconfined">4. Dispatchers.Unconfined</h3>
<ul>
<li><p>특정 스레드에 구속되지 않고 현재 스레드에서 시작하여 실행
코루틴이 처음 시작될 때는 호출한 스레드에서 실행되지만, suspend 함수가 호출되면 재개 시점에 따라 다른 스레드에서 실행될 수도 있다.</p>
</li>
<li><p>테스트 및 간단한 코루틴 작업에서만 사용
코루틴의 스레드 전환이 필요 없고, 특정 디스패처에 의존하지 않는 작업</p>
</li>
<li><p>예상치 못한 스레드에서 실행될 수 있으므로 장기 실행 작업이나 복잡한 작업에는 권장되지 않는다. 특히, UI 업데이트가 필요한 작업에는 적합하지 않다.</p>
</li>
</ul>
<h3 id="5-newsinglethreadcontext와-newfixedthreadpoolcontext">5. newSingleThreadContext와 newFixedThreadPoolContext</h3>
<ul>
<li><p>개발자가 특정한 스레드 풀을 생성하여 코루틴을 실행하도록 하는 방법입니다. 주로 커스텀 스레드가 필요하거나 특정한 스레드에서 작업을 격리해야 할 때 사용합니다.</p>
</li>
<li><p>특정 작업을 고립된 스레드에서 실행해야 하는 경우
고정된 스레드 풀에서 작업을 실행하고자 할 때</p>
</li>
<li><p>newSingleThreadContext는 하나의 스레드만을 사용하는 고립된 작업에 적합합니다. 작업이 끝나면 반드시 close()를 호출해 스레드를 정리해야 합니다.</p>
</li>
</ul>
<h2 id="dispatcher와-스레드-할당의-원리">Dispatcher와 스레드 할당의 원리</h2>
<h4 id="그렇다면-디스패쳐가-직접적으로-스레드를-할당하는가-x">그렇다면 디스패쳐가 직접적으로 스레드를 할당하는가? X</h4>
<p>디스패쳐가 직접적으로 스레드를 할당하지는 않고, 디스패쳐는 코루틴이 <strong>어느 스레드나 스레드 풀에서 실행될지를 결정하는 역할</strong>을 하고, 실제 스레드는 <strong>코루틴의 스케줄러</strong>가 관리한다.</p>
<p>디스패쳐는 <strong>스레드를 관리하는 추상화 계층</strong>으로, <strong>스레드 풀을 통해 스레드 리소스를 효율적으로 할당하도록 돕고</strong> 코루틴이 시작될 때, 디스패쳐는 설정된 정책에 따라 <strong>해당 작업을 수행할 스레드를 선택하거나, 기존 스레드를 재사용하여 스레드 풀을 통해 코루틴을 효율적으로 관리하는 역할</strong>을 한다.</p>
<p>예를 들어:</p>
<p><strong>Dispatchers.IO</strong>는 스레드 풀을 사용하여 여러 I/O 작업을 동시 실행할 수 있도록 스레드를 관리한다. 실제 스레드 풀은 스레드 수를 동적으로 늘리거나 줄일 수 있어, I/O 작업을 필요에 따라 조절합니다.</p>
<p><strong>Dispatchers.Default</strong>는 CPU 집약적인 작업에 최적화된 스레드 풀을 제공하며, 시스템의 CPU 코어 수에 따라 스레드를 할당한다.</p>
<p><strong>Dispatchers.Main</strong>은 메인(UI) 스레드를 가리키지만, 코루틴을 직접 메인 스레드에 할당하지 않고 메인 스레드의 Looper나 이벤트 루프를 사용하여 코루틴을 예약한다.</p>
<h2 id="실제-스레드-할당과의-차이">실제 스레드 할당과의 차이</h2>
<p>디스패쳐 자체가 스레드를 할당하는 것이 아니라, 코루틴의 실행 위치를 지정하고, 스케줄러가 이를 기반으로 스레드 풀에서 스레드를 할당하여 실행한다.</p>
<p>이를 통해 여러 코루틴이 효율적으로 스레드를 공유할 수 있어 리소스 사용의 최적화가 가능한 것이다.</p>
<p>디스패쳐는 단지 코루틴이 특정 스레드 풀에서 실행되도록 지정할 뿐이며, 스케줄러가 이 요청을 받아들여 적절한 스레드를 할당한다.</p>
<hr>
<h4 id="reference">Reference</h4>
<p><a href="https://kotlinlang.org/docs/coroutines-overview.html">kotlin-coroutines</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] 제네릭 in, out과 자바의 와일드카드]]></title>
            <link>https://velog.io/@plz_no_anr/Kotlin-%EC%A0%9C%EB%84%A4%EB%A6%AD-in-out%EA%B3%BC-%EC%9E%90%EB%B0%94-feat.%EC%99%80%EC%9D%BC%EB%93%9C%EC%B9%B4%EB%93%9C</link>
            <guid>https://velog.io/@plz_no_anr/Kotlin-%EC%A0%9C%EB%84%A4%EB%A6%AD-in-out%EA%B3%BC-%EC%9E%90%EB%B0%94-feat.%EC%99%80%EC%9D%BC%EB%93%9C%EC%B9%B4%EB%93%9C</guid>
            <pubDate>Tue, 29 Oct 2024 02:19:45 GMT</pubDate>
            <description><![CDATA[<p><del>Zoom In, Zoom Out</del></p>
<p>코틀린에서 in과 out 키워드는 제네릭 타입 파라미터의 공변성(variance)을 제어하기 위해 사용된다. 
이는 제네릭 클래스나 함수가 상위 타입과 하위 타입 간의 관계를 명확히 하여 타입 안전성을 보장하도록 도와준다.</p>
<p>우선 코틀린의 in, out을 각각 살펴보면</p>
<h2 id="1-코틀린의-제네릭">1. 코틀린의 제네릭</h2>
<h3 id="out-키워드공변성-covariance">out 키워드(공변성, Covariance)</h3>
<p><strong>out</strong> 키워드는 주로 생산자(Producer) 역할의 제네릭 타입에 사용되며, 제네릭 타입이 반환값으로만 사용될 때 적합하다. out 키워드를 사용하면 제네릭 타입이 자신의 하위 타입으로 대체 가능해진다. </p>
<pre><code class="language-kotlin">open class Language
class Kotlin: Language()
class Swift: Language()

class Developer&lt;out T&gt;(private val language: T) {
    fun getLanguage(): T = language
}

@Test
fun developerTest() {
    val kotlinDeveloper: Developer&lt;Kotlin&gt; = Developer(Kotlin())
    val developer: Developer&lt;Language&gt; = kotlinDeveloper 
    // out이 없다면 Type mismatch 발생
    println(developer.getLanguage())
}</code></pre>
<p>위 코드에서 <code>Developer&lt;out T&gt;</code> out 키워드가 없다면 Type mismatch가 발생한다.</p>
<p>out: 꺼내와서(out 시킨다) 읽는다. -&gt; Write은 불가능
부모 클래스에 자식 클래스를 사용가능하게 해준다.</p>
<h3 id="in-키워드반공변성-contravariance">in 키워드(반공변성, Contravariance)</h3>
<p><strong>in</strong> 키워드는 소비자(Consumer) 역할의 제네릭 타입에 사용되며, 제네릭 타입이 입력 파라미터로만 사용될 때 적합하다. in 키워드를 사용하면 제네릭 타입이 자신의 상위 타입으로 대체 가능해진다.</p>
<pre><code class="language-kotlin">open class Language
class Kotlin: Language()
class Swift: Language()

class Developer&lt;in T: Language&gt; {
    fun printLanguage(language: T) {
        println(&quot;this language is $language&quot;)
    }
}

@Test
fun developerTest() {
    val developer: Developer&lt;Language&gt; = Developer()
    val kotlinDeveloper: Developer&lt;Kotlin&gt; = developer 
    // in이 없다면 Type mismatch 발생
    kotlinDeveloper.printLanguage(Kotlin())
}</code></pre>
<h4 id="in-넣어준다in-시킨다-즉-write-할-수-있다---read는-불가능">in: 넣어준다(in 시킨다) 즉, Write 할 수 있다. -&gt; Read는 불가능</h4>
<p>서버 클래스에 슈퍼 클래스를 사용가능하게 해준다.</p>
<h3 id="in과-out이-없는-경우-무공변성-invariance">in과 out이 없는 경우 (무공변성, Invariance)</h3>
<p>제네릭 타입 파라미터에 in 또는 out 키워드를 사용하지 않으면, 해당 제네릭 타입은 <strong>무공변성(invariance)</strong>을 가지며, 명시된 타입만 허용된다. 즉, 하위 타입도 상위 타입으로 대체할 수 없고, 반대도 불가능하다.</p>
<pre><code class="language-kotlin">open class Language
class Kotlin: Language()
class Swift: Language()

class Developer&lt;T&gt;(private var value: T) {
    fun get(): T = value
    fun set(newValue: T) { value = newValue }
}

val kotlinDeveloper: Developer&lt;Kotlin&gt; = Developer(Kotlin())
val developer: Developer&lt;Language&gt; = kotlinDeveloper // Type mismatch. 발생</code></pre>
<h2 id="2-자바의-와일드-카드와-비교">2. 자바의 와일드 카드와 비교</h2>
<p>코틀린의 in과 out 키워드는 자바의 와일드카드(? extends T와 ? super T)와 개념적으로 유사하지만, 언어 차원에서 조금 다르게 작동한다. 코틀린의 in과 out은 제네릭 타입 선언에서 <strong>공변성(variance)</strong>을 지정하기 위한 키워드인 반면, 자바의 와일드카드는 타입 사용 시 변수를 선언하는 데 사용된다.</p>
<h3 id="코틀린-out-vs-자바--extends-t">코틀린 out vs 자바 ? extends T</h3>
<blockquote>
<ul>
<li><strong>코틀린 out</strong>: out은 코틀린의 제네릭 타입에서 공변성(Covariance)을 지정하는 키워드. 이는 해당 타입이 반환만 가능하고, 소비(입력)될 수 없다는 것을 의미한다.</li>
</ul>
</blockquote>
<ul>
<li><strong>자바 ? extends</strong> T: 자바의 ? extends T 와일드카드는 제네릭 타입이 T의 하위 타입만 허용한다는 의미. 주로 읽기 전용으로 사용되며, 안전하게 T의 하위 타입을 반환할 수 있게 한다.</li>
</ul>
<h3 id="코틀린-in-vs-자바--super-t">코틀린 in vs 자바 ? super T</h3>
<blockquote>
<ul>
<li><strong>코틀린 in</strong>: in은 제네릭 타입이 반공변성(Contravariance)을 가지도록 만든다. 즉, 해당 타입은 소비자(Consumer) 역할을 하며, T의 상위 타입을 허용한다. 반공변성으로 지정된 타입은 입력용 파라미터로만 사용할 수 있다.</li>
</ul>
</blockquote>
<ul>
<li><strong>자바 ? super T</strong>: 자바의 ? super T 와일드카드는 제네릭 타입이 T의 상위 타입만 허용하게 만든다. T 타입의 값을 안전하게 소비(입력)할 수 있다.</li>
</ul>
<p>주요 차이점을 정리 하자면</p>
<h4 id="코틀린">코틀린</h4>
<ul>
<li>위치: 제네릭 타입 선언 시</li>
<li>타입 안정성: 컴파일러가 in과 out을 체크</li>
<li>제네릭 타입 정의 시 공변성과 반공변성을 설정하여, 타입 사용 시 명시적인 와일드카드 없이도 타입 안정성을 유지한다.</li>
</ul>
<h4 id="자바">자바</h4>
<ul>
<li>위치: 변수나 메서드 파라미터 선언 시</li>
<li>타입 안정성: 와일드카드 사용 시 안전하게 타입 캐스팅 가능</li>
<li>타입 사용 시점에서 와일드카드를 사용하여, 특정한 제네릭 타입의 상위 또는 하위 타입을 안전하게 사용할 수 있도록 한다.</li>
</ul>
<hr>
<h4 id="간단-요약">간단 요약</h4>
<ul>
<li><strong>out: 공변성(Covariance)
읽기 가능(Read O), 쓰기 불가능(Write X)
부모 클래스에 자식 클래스를 사용 가능</strong></li>
<li><strong>in: 반공변성(Contravariance)
읽기 불가능(Read X), 쓰기 가능(Write O)
자식 클래스에 부모 클래스를 사용 가능</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/3d2e7d5c-f014-4c64-8cff-9fcd9ba190cd/image.png" alt=""></p>
<h4 id="tmi로-kotlin의-list를-보면-out-키워드로-구현되어-있는-것을-볼-수-있다">tmi로 Kotlin의 List를 보면 out 키워드로 구현되어 있는 것을 볼 수 있다.</h4>
<h4 id="reference">Reference</h4>
<p><a href="https://medium.com/mj-studio/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%A0%9C%EB%84%A4%EB%A6%AD-in-out-3b809869610e">medium</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BlockChain] 이더리움은..?]]></title>
            <link>https://velog.io/@plz_no_anr/BlockChain-%EC%9D%B4%EB%8D%94%EB%A6%AC%EC%9B%80%EC%9D%80</link>
            <guid>https://velog.io/@plz_no_anr/BlockChain-%EC%9D%B4%EB%8D%94%EB%A6%AC%EC%9B%80%EC%9D%80</guid>
            <pubDate>Sun, 27 Oct 2024 07:45:19 GMT</pubDate>
            <description><![CDATA[<p><del>이더리움은 진짜 유명한 블록체인임</del></p>
<h4 id="이더리움ethereum이란">이더리움(Ethereum)이란?</h4>
<p> 비탈릭 부테린(Vitalik Buterin)이 비트코인에 사용된 핵심 기술인 블록체인에 화폐 거래 기록뿐 아니라 계약서 등의 추가 정보를 기록할 수 있다는 점에 착안하여 개발한 암호화폐이다.</p>
<h3 id="가상머신ethereum-virtual-machine">가상머신(Ethereum Virtual Machine)</h3>
<p>이더리움 블록체인에서 스마트 계약과 탈중앙화 애플리케이션(DApps)을 실행하기 위한 실행 환경</p>
<blockquote>
<ul>
<li><strong>스마트 계약 실행(Smart Contract Execution)</strong>: EVM은 스마트 계약을 실행하고, 그 결과를 블록체인에 기록하는 역할을 한다</li>
</ul>
</blockquote>
<ul>
<li><strong>독립성(Independence)</strong>: EVM은 플랫폼 독립적이며, 개발자들이 다양한 언어(주로 Solidity)로 작성한 코드를 실행할 수 있음</li>
<li><strong>스택 기반(Stack-based)</strong>: EVM은 스택 기반 아키텍처를 사용하여, 명령어를 스택에 푸시하거나 팝하는 방식으로 작동한다</li>
<li><strong>가상화(Virtualization)</strong>: EVM은 모든 노드에서 동일하게 실행되며, 이를 통해 블록체인의 상태를 유지하고, 분산된 네트워크에서의 일관성을 보장</li>
<li><strong>가스 시스템(Gas System)</strong>: EVM은 트랜잭션 실행 시 가스(gas)라는 개념을 사용하여, 각 연산의 비용을 측정하고 트랜잭션 수수료를 결정하는데, 이는 네트워크의 과부하를 방지하는데 도움을 준다</li>
</ul>
<h3 id="스마트-컨트렉트smart-contract">스마트 컨트렉트(Smart Contract)</h3>
<p>스마트 컨트랙트는 이더리움과 같은 블록체인에서 실행되는 자동화된 계약을 말합니다. 특정 조건이 충족되면 사전에 정의된 규칙에 따라 자동으로 실행되며, 사람의 개입 없이도 계약 조건을 이행할 수 있습니다. 이는 중앙 권한 없이 계약이 이행되는 신뢰 기반 시스템을 제공합니다.</p>
<p>스마트 컨트랙트의 주요 특징</p>
<blockquote>
<ul>
<li><strong>자동 실행(Automated Execution)</strong>: 계약 조건이 만족되면 자동으로 이행된다. 예를 들어, 스마트 컨트랙트가 “A가 B에게 특정 금액을 송금한다”는 조건을 가진 경우, 조건이 충족되면 이 송금이 자동으로 이루어진다.</li>
</ul>
</blockquote>
<ul>
<li><strong>변경 불가능성(Immutability)</strong>: 블록체인 상에 기록된 스마트 컨트랙트는 수정할 수 없다. 한 번 배포된 계약은 모든 노드에서 동일하게 유지되며, 투명성과 신뢰성을 제공한다.</li>
<li><strong>신뢰성(Reliability)</strong>: 중앙 기관 없이도 블록체인의 분산 네트워크에서 동작하기 때문에, 중재자 없이도 계약 이행이 보장된다. 즉, 제3자나 중개인의 역할이 필요 없게 된다.</li>
<li><strong>투명성(Transparency)</strong>: 계약 조건과 실행 상태는 블록체인에 공개되며, 누구나 확인할 수 있다.</li>
</ul>
<h3 id="ercethereum-request-for-comments">ERC(Ethereum Request for Comments)</h3>
<p> ERC는 주로 스마트 계약에서 사용되는 특정한 표준을 정의하는 제안서
 주로 스마트 계약에서 사용되는 표준을 정의하는 제안서입니다. EIP의 한 종류로 볼 수 있으며, 스마트 계약과 관련된 구체적인 기술적 표준을 정의합니다.</p>
<h4 id="erc-20">ERC-20</h4>
<p>ERC-20은 대체 가능한 토큰(Fungible Tokens)을 위한 표준이다.
특징으로는 </p>
<ul>
<li>모든 토큰이 동일한 가치를 가지며, 서로 교환이 가능하다. </li>
<li>같은 종류의 토큰끼리는 서로 교환할 수 있다.</li>
</ul>
<h4 id="erc-721">ERC-721</h4>
<p>ERC-721은 대체 불가능한 토큰(Non-Fungible Tokens, NFT)을 위한 표준입니다. 각 토큰이 고유한 속성을 가지며 서로 교환할 수 없습니다.
특징으로는</p>
<ul>
<li>고유성(Uniqueness): 각 NFT는 고유한 식별자를 가지고 있어, 같은 종류의 토큰이라도 서로 다른 가치를 가질 수 있다.</li>
<li>메타데이터(Metadata): NFT는 추가적인 메타데이터를 포함할 수 있어, 소유자 정보, 생성일, 특성 등을 기록할 수 있다.</li>
</ul>
<h3 id="eip-ethereum-improvement-proposal">EIP (Ethereum Improvement Proposal)</h3>
<p>이더리움 네트워크의 전반적인 개선을 제안하는 문서로 이더리움의 프로토콜을 변경하거나 새로운 기능을 도입할 때 사용된다.
EIP는 네트워크 프로토콜의 변경, 하드 포크, 새로운 기술적 표준 등을 포함하여 이더리움 블록체인 전반에 영향을 미친다.</p>
<h4 id="eip-1559">EIP-1559</h4>
<p>EIP-1559는 이더리움 수수료 구조를 변경하는 제안</p>
<p>번외로
<strong>Wei</strong>: 이더리움의 최소 단위</p>
<p><strong>Gwei</strong>: 기가(10^9)웨이(Wei) 1 Gwei = 1,000,000,000 Wei
**이더리움 트랜잭션 수수료나 스마트 계약 실행에 필요한 비용을 계산할 때 Gwei 단위가 사용된다.</p>
<p><strong>Estimate</strong>: 사용자가 특정 작업을 완료하기 위해 기다려야 할 예상 시간
**일반적으로 현재 네트워크 혼잡도, 사용자가 설정한 가스 가격, 그리고 네트워크의 채굴 난이도 등을 기반으로 계산</p>
<p><strong>Base Fee</strong>: Ethereum 네트워크에서 기본적으로 소모되는 가스 비용, 네트워크 상태에 따라 블록마다 동적으로 조정</p>
<p><strong>Max Priority Fee</strong>: 트랜잭션을 우선 처리하기 위해 사용자 또는 채굴자가 설정할 수 있는 추가 비용</p>
<p><strong>Max Fee Per Gas</strong>: 사용자가 각 가스 단위당 지불할 최대 금액
**트랜잭션을 보낼 때나 스마트 계약과 상호 작용할 때 각 가스 단위당 얼마나 지불할 것인지를 나타내는 &quot;가스 가격&quot;을 설정
MaxFeePerGas = BaseFee + Max Priority Fee</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[BlockChain] 블록체인이란? feat. 비트코인]]></title>
            <link>https://velog.io/@plz_no_anr/BlockChain-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8%EC%9D%B4%EB%9E%80-feat.-%EB%B9%84%ED%8A%B8%EC%BD%94%EC%9D%B8</link>
            <guid>https://velog.io/@plz_no_anr/BlockChain-%EB%B8%94%EB%A1%9D%EC%B2%B4%EC%9D%B8%EC%9D%B4%EB%9E%80-feat.-%EB%B9%84%ED%8A%B8%EC%BD%94%EC%9D%B8</guid>
            <pubDate>Sun, 27 Oct 2024 07:05:27 GMT</pubDate>
            <description><![CDATA[<p><del>비트코인 지금이라도 살까..? 할 때 살걸</del></p>
<h4 id="블록체인blockchain이란">블록체인(BlockChain)이란?</h4>
<p>연결리스트 형태의 블록들이 체인 형태로 구성
각 블록은 거래 정보나 데이터를 포함하고 있으며, 이러한 블록들은 암호화된 방식으로 연결되어 있다.</p>
<blockquote>
<ul>
<li><strong>분산성(Decentralization)</strong>: 데이터가 여러 노드에 분산되어 저장되므로 중앙 집중화된 서버 없이도 운영된다.</li>
</ul>
</blockquote>
<ul>
<li><strong>불변성(Immutability)</strong>: 한 번 기록된 데이터는 변경할 수 없어서, 데이터의 신뢰성을 높인다.</li>
<li><strong>투명성(Transparency)</strong>: 모든 참여자가 동일한 정보를 공유하므로 거래의 투명성이 보장된다.</li>
<li><strong>보안(Security)</strong>: 암호화 기술을 사용하여 데이터를 안전하게 보호한다.</li>
</ul>
<p>핵심은 <strong>탈중앙화(decentralization)</strong></p>
<h4 id="트랜잭션tx">트랜잭션(tx)</h4>
<ul>
<li><p>입력(Inputs):
이전 트랜잭션 ID: 사용자가 보낼 비트코인을 이전에 받은 트랜잭션의 ID
출력 인덱스(Output Index): 이전 트랜잭션의 특정 출력(output)</p>
</li>
<li><p>출력(Outputs):
수신자 주소: 비트코인을 받을 지갑 주소
전송 금액: 수신자에게 보내는 비트코인의 양
트랜잭션 ID: 해당 트랜잭션을 고유하게 식별하는 해시값</p>
</li>
<li><p>서명(ScriptSig): 송신자의 개인 키로 생성된 디지털 서명으로, 트랜잭션의 유효성을 검증하는 데 사용. 이를 통해 송신자가 해당 비트코인을 실제로 소유하고 있다는 것을 증명</p>
</li>
<li><p>타임스탬프: 트랜잭션이 생성된 시간</p>
</li>
<li><p>수수료: 트랜잭션에 포함된 수수료는, 입력과 출력의 차액으로 간접적으로 계산</p>
</li>
</ul>
<h4 id="블록block-여러-tx의-집합-블록은-이전-블록의-hash을-가지고-있음">블록(Block): 여러 tx의 집합, 블록은 이전 블록의 hash을 가지고 있음</h4>
<h4 id="지갑wallet-tx를-만들고-node에-전송-비밀키private-key가-필요">지갑(Wallet): tx를 만들고 node에 전송, 비밀키(Private Key)가 필요</h4>
<h4 id="노드node-데이터베이스를-저장하고-트랜잭션을-검증하며-블록을-생성">노드(Node): 데이터베이스를 저장하고, 트랜잭션을 검증하며, 블록을 생성</h4>
<ul>
<li><p>전체 노드(Full Node): 블록체인의 모든 데이터를 저장하고, 트랜잭션과 블록을 검증
네트워크의 보안을 유지하고, 다른 노드에 정보를 전파합니다.</p>
</li>
<li><p>경량 노드(Light Node): 블록체인의 전체 데이터를 저장하지 않고, 필요한 최소한의 정보만을 저장
빠르고 효율적이며, 주로 모바일 지갑이나 애플리케이션에서 사용됨</p>
</li>
<li><p>마이닝 노드(Mining Node): 새로운 블록을 생성하고, 트랜잭션을 검증하는 노드
작업 증명(Proof of Work)과 같은 합의 알고리즘을 통해 블록을 추가하며, 보상을 받음</p>
</li>
<li><p>프록시 노드(Proxy Node): 다른 노드와의 통신을 중계하여, 네트워크의 효율성을 높임</p>
</li>
</ul>
<h4 id="펜딩-풀pending-pool-아직-블록으로-확인되지-않은-거래들이-임시로-저장되는-공간">펜딩 풀(Pending pool): 아직 블록으로 확인되지 않은 거래들이 임시로 저장되는 공간</h4>
<h4 id="채굴자miner-채굴자-비트코인-생태계를-유지">채굴자(Miner): 채굴자, 비트코인 생태계를 유지</h4>
<h4 id="채굴mining--비트코인은-작업증명proof-of-work-방식">채굴(Mining) : 비트코인은 작업증명(Proof of Work) 방식</h4>
<ul>
<li>복잡한 수학 문제를 해결해야 하며, 이를 통해 네트워크의 안전성과 신뢰성을 확보
문제(해시 연산), 성공시 일정 보상(Reword)을 받음 (코인) </li>
</ul>
<h4 id="반감기halving-암호화폐에서-채굴-보상이-절반으로-줄어드는-이벤트">반감기(Halving): 암호화폐에서 채굴 보상이 절반으로 줄어드는 이벤트</h4>
<hr>
<h4 id="쉽게-정리">쉽게 정리</h4>
<p><strong>블록</strong> -&gt; n개의 거래내역이 담긴 거래내역서</p>
<p><strong>블록체인</strong> -&gt; 위의 블록들이 처음 거래된 때부터의 모든 거래내역을 생성된 순서대로 체인 형태로 엮은 장부</p>
<p><strong>코인</strong> -&gt; 원화(₩)</p>
<p><strong>지갑</strong> -&gt; 말 그대로 코인이 담긴 지갑</p>
<p><strong>노드(마이닝)</strong> -&gt; 은행원들이 개인적으로 차린 3금융권 사무실</p>
<p><strong>채굴자(마이너)</strong> -&gt; 은행원이긴 한데 이제 자격증은 없는..</p>
<p><strong>채굴(마이닝)</strong> -&gt; 은행원이 일을 하는 행위 (합의 알고리즘)</p>
<p><strong>증명(작업 증명)</strong> -&gt; 은행원들이 얼마나 일을 잘하는지 경쟁하는 내기 </p>
<ul>
<li>작업 증명 방식에 그래픽 카드가 필요한 이유는 복잡한 문제 연산을 병렬로 처리가 가능하기 때문</li>
<li>현재는 작업 증명 방식이 전기를 너무 많이 써서 에너지 효율 때문에 다른 증명 방식도 나옴. 지분 증명(Proof of Stake)등</li>
</ul>
<p><strong>리워드</strong> -&gt; 위의 작업 증명에서 승리한 은행원에게 주는 상금</p>
<ul>
<li>채굴자들이 채굴을 하는 이유는 이 리워드가 코인을 주기 때문</li>
</ul>
<p><strong>반감기</strong> -&gt; 상금을 너무 많이 줬네? 응 삭감</p>
<h4 id="블록체인과-일반-은행의-차이점은-일반-은행의-거래-장부는-해당-은행이-단독으로-가지고-있기-때문에-혹시나-해킹-당하면-조작이-가능하지만-블록체인은-채굴자노드들이-각각-장부를-가지고-있기-때문에-한두-개-해킹-당하더라도-조작이-힘듦">블록체인과 일반 은행의 차이점은 일반 은행의 거래 장부는 해당 은행이 단독으로 가지고 있기 때문에 혹시나 해킹 당하면 조작이 가능하지만 블록체인은 채굴자(노드)들이 각각 장부를 가지고 있기 때문에 한두 개 해킹 당하더라도 조작이 힘듦</h4>
<p>은행이 단독으로 장부를 가지고 있는 것 -&gt; <strong>중앙화(centralized)</strong>
블록체인의 여러 채굴자들이 분산하여 장부를 가지고 있는 것 -&gt; <strong>탈 중앙화(decentralized)</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[OOP] 객체 지향 설계의 5가지 원칙 feat. SOLID]]></title>
            <link>https://velog.io/@plz_no_anr/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99-feat.-SOLID</link>
            <guid>https://velog.io/@plz_no_anr/OOP-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99-feat.-SOLID</guid>
            <pubDate>Fri, 18 Oct 2024 02:42:14 GMT</pubDate>
            <description><![CDATA[<p><del>기본기를 탄탄하게</del></p>
<p>우선 <strong>OOP(Object-Oriented Programming, 객체 지향 프로그래밍)이란</strong>, 프로그램을 여러 개의 <strong>객체(object)</strong>로 나누어 이를 상호 작용하게 하여 설계하고 구현하는 프로그래밍 패러다임입니다. 객체는 데이터를 저장하는** 속성(attribute)<strong>과 그 데이터를 처리하는 **메서드(method)</strong>로 구성됩니다. OOP의 핵심 개념은 크게 다음 네 가지로 요약할 수 있습니다.</p>
<blockquote>
<ul>
<li><strong>캡슐화(Encapsulation)</strong>: 객체의 상태(데이터)를 외부에서 직접 접근하지 못하고, 메서드를 통해서만 접근할 수 있게 하는 것. 이를 통해 데이터를 보호하고, 객체의 내부 구현을 숨길 수 있다.</li>
</ul>
</blockquote>
<ul>
<li><strong>상속(Inheritance)</strong>: 기존 클래스(부모 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스)가 물려받아 사용할 수 있게 하는 개념. 이를 통해 코드의 재사용성을 높일 수 있다.</li>
<li><strong>다형성(Polymorphism)</strong>: 같은 이름의 메서드가 서로 다른 클래스에서 다르게 동작할 수 있는 성질을 의미한다. 즉, 하나의 인터페이스로 여러 가지 다른 형태의 객체를 처리할 수 있게 한다.</li>
<li><strong>추상화(Abstraction)</strong>: 복잡한 시스템을 단순하게 표현하여 불필요한 세부사항을 감추고, 중요한 부분만 남기는 개념이다. 이를 통해 프로그램의 복잡성을 줄이고 유지보수성을 높일 수 있다.</li>
</ul>
<h4 id="그렇다면-solid-원칙이란">그렇다면 SOLID 원칙이란?</h4>
<p>SOLID 원칙은 객체 지향 프로그래밍(OOP)에서 소프트웨어 설계를 보다 유연하고 유지보수하기 쉽게 만들기 위한 다섯 가지 주요 설계 원칙이다. 이 원칙들은 소프트웨어 개발 시 <strong>의존성</strong>을 최소화하고, <strong>확장성</strong>과 <strong>재사용성</strong>을 높이는 데 초점을 맞춘다. SOLID는 다음과 같은 다섯 가지 원칙의 첫 글자를 따서 만들어진 용어이다.</p>
<h3 id="1-단일-책임-원칙single-responsibility-principle-srp">1. 단일 책임 원칙(Single Responsibility Principle, SRP)</h3>
<p>클래스는 하나의 책임만 가져야 하며, 클래스가 변경되는 이유는 하나뿐이어야 한다. 즉, 하나의 클래스는 하나의 기능이나 역할만을 수행해야 하며, 그 기능과 관련된 변경 사항만 있을 때 수정되어야 한다.</p>
<h3 id="2-개방-폐쇄-원칙open-closed-principle-ocp">2. 개방-폐쇄 원칙(Open-Closed Principle, OCP)</h3>
<p>소프트웨어 요소는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다. 즉, 새로운 기능을 추가할 때 기존 코드를 수정하는 대신, 기존 클래스나 모듈의 동작을 확장하는 방식으로 변경해야 한다.</p>
<h3 id="3-리스코프-치환-원칙liskov-substitution-principle-lsp">3. 리스코프 치환 원칙(Liskov Substitution Principle, LSP)</h3>
<p>자식 클래스는 언제나 부모 클래스를 대체할 수 있어야 한다. 자식 클래스가 부모 클래스의 기능을 상속받는 경우, 부모 클래스의 동작과 일관되게 동작해야 하며, 이를 위반하면 객체 지향 설계에서의 다형성(polymorphism)이 깨지게 된다.</p>
<h3 id="4-인터페이스-분리-원칙interface-segregation-principle-isp">4. 인터페이스 분리 원칙(Interface Segregation Principle, ISP)</h3>
<p>클라이언트는 자신이 사용하지 않는 인터페이스에 의존하지 않아야 한다. 즉, 큰 인터페이스보다는 더 작은 인터페이스들로 분리해서, 클라이언트가 불필요한 메서드나 의존성을 가지지 않도록 해야 한다.</p>
<h3 id="5-의존성-역전-원칙dependency-inversion-principle-dip">5. 의존성 역전 원칙(Dependency Inversion Principle, DIP)</h3>
<p>고수준 모듈(상위 수준의 정책을 결정하는 모듈)은 저수준 모듈(구체적인 구현 세부사항)에게 의존하지 말아야 하며, 둘 다 추상화된 인터페이스에 의존해야 한다. 이를 통해 상위와 하위 모듈 간의 결합도를 낮추고, 코드의 유연성과 재사용성을 높일 수 있다.</p>
<hr>
<h4 id="결론">결론</h4>
<p>위의 5가지 원칙들이 종합적으로 말하고자 하는 핵심은 <strong>&quot;의존성 최소화, 확장성과 재사용성을 높인다.&quot;</strong> 즉 <strong>추상화</strong>와 <strong>다형성</strong>에 초점을 두고 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] Coroutine의 Channel 알아보기]]></title>
            <link>https://velog.io/@plz_no_anr/Coroutine-Channel</link>
            <guid>https://velog.io/@plz_no_anr/Coroutine-Channel</guid>
            <pubDate>Wed, 20 Mar 2024 08:05:19 GMT</pubDate>
            <description><![CDATA[<p><del>코루틴 네가 내 별이다.</del></p>
<h3 id="channel-이란">Channel 이란?</h3>
<p>데이터를 stream 처럼 전송하기 위한 인터페이스며 구조는 BlockingQueue와 비슷하며, 동일하게 ThreadSafe 한 형태의 구조를 가지고 있습니다.</p>
<p>데이터를 전송하는 SendChannel 과 데이터를 소비하는 ReceiveChannel로 이루어져 있으며 flow와 다른 점은 직접 데이터를 생산하는 것이 아닌 전달의 개념이라는 것이 다른 점입니다.</p>
<p>Channel의 기본 생성자를 살펴보면</p>
<pre><code class="language-kotlin">public fun &lt;E&gt; Channel(
    capacity: Int = RENDEZVOUS,
    onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND,
    onUndeliveredElement: ((E) -&gt; Unit)? = null
): Channel&lt;E&gt;</code></pre>
<p>capacity, onBufferOverflow, onUndeliveredElement 이렇게 세 가지 매개변수를 제공합니다.</p>
<p>기본적으로 전송과 소비는 다음 함수를 사용합니다.</p>
<pre><code class="language-kotlin">public fun trySend(element: E): ChannelResult&lt;Unit&gt;
public fun tryReceive(): ChannelResult&lt;E&gt;

public suspend fun send(element: E)
public suspend fun receive(): E</code></pre>
<p><code>trySend</code> 와 <code>tryReceive</code> 함수는 suspend 하지 않기 때문에 성공과 실패 여부를 ChannelResult 라는 클래스를 통해 리턴해줍니다.
<code>send</code> 와 <code>receive</code>는 suspend 함수이기 때문에 다른 coroutine scope 간에도 전송과 소비가 가능합니다.</p>
<p>우선 기본 생성자로 채널을 생성하여 로그를 찍어보면</p>
<pre><code class="language-kotlin">val channel: Channel&lt;Int&gt; = Channel()
        repeat(10) {
            channel.trySend(it)
        }
        channel.tryReceive().getOrNull().run {
            println(&quot;받은 값 -&gt; $this&quot;)
        }

받은 값 -&gt; null</code></pre>
<p>위의 결과를 보면 분명 send를 했지만 로그에는 null이 찍히는 걸 볼 수 있는데
이는 Channel의 capacity를 알면 쉽게 이해할 수 있다.</p>
<h3 id="capacity란">capacity란?</h3>
<p>일종의 버퍼 개념인데 Channel에선 다음과 같이 capacity를 제공합니다.</p>
<blockquote>
<ul>
<li>RENDEZVOUS : 버퍼가 존재하지 않음</li>
</ul>
</blockquote>
<ul>
<li>CONFLATED : 버퍼가 1이며, 가장 나중에 들어온 데이터만 보장</li>
<li>UNLIMITED : 버퍼가 무한대</li>
<li>BUFFERED : 시스템이 정한 버퍼값(64개) 사용</li>
</ul>
<p>Channel의 기본 생성자를 보면 capacity가 RENDEZVOUS 라 버퍼가 존재하지 않기 때문에 데이터를 저장하지 못해 null로 나오게 됩니다.</p>
<p>그렇다면 각 capacity 별 결과 값을 로그로 찍어보면 다음과 같습니다.</p>
<pre><code class="language-kotlin">val channel: Channel&lt;Int&gt; = Channel(capacity = Channel.CONFLATED)
        repeat(10) {
            channel.trySend(it)
        }
        channel.tryReceive().getOrNull().run {
            println(&quot;받은 값 -&gt; $this&quot;)
        }

받은 값 -&gt; 9</code></pre>
<p>가장 최근 값이 덮어 씌워지기 때문에 9가 찍히게 됩니다.</p>
<pre><code class="language-kotlin">val channel: Channel&lt;Int&gt; = Channel(capacity = Channel.UNLIMITED)
        repeat(10) {
            channel.trySend(it)
        }
        channel.tryReceive().getOrNull().run {
            println(&quot;받은 값 -&gt; $this&quot;)
        }

받은 값 -&gt; 0</code></pre>
<p>Channel의 버퍼에는 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 전부 저장</p>
<pre><code class="language-kotlin">val channel: Channel&lt;Int&gt; = Channel(capacity = 5)
        repeat(10) {
            channel.trySend(it)
        }
        channel.tryReceive().getOrNull().run {
            println(&quot;받은 값 -&gt; $this&quot;)
        }

받은 값 -&gt; 0</code></pre>
<p>Channel의 버퍼에는 0, 1, 2, 3, 4 까지 버퍼 개수만큼 저장</p>
<p>위와 같은 결과를 볼 수 있습니다.</p>
<h3 id="onbufferoverflow란">onBufferOverflow란?</h3>
<p>버퍼가 가득 찼을 때 처리할 방법이고 다음과 같은 방식을 제공합니다.</p>
<blockquote>
<ul>
<li>SUSPEND : 더 이상 데이터를 저장하지 않고 대기</li>
</ul>
</blockquote>
<ul>
<li>DROP_OLDEST : 가장 오래된 데이터를 제거하고 새로운 데이터를 저장 </li>
<li>DROP_LATEST : 가장 최신의 데이터를 제거하고 새로운 데이터를 저장</li>
</ul>
<p>Channel의 기본 생성자를 보면 default 값은 SUSPEND이고 각 DROP_OLDEST는 Queue 방식이고 DROP_LATEST는 Stack 방식이라고 생각하면 쉽습니다.</p>
<h3 id="flow로-변환">flow로 변환</h3>
<p>또한 Channel에는 쉽게 flow로 변환하여 사용할 수 있게 확장 함수를 제공합니다.</p>
<blockquote>
<ul>
<li>receiveAsFlow()</li>
</ul>
</blockquote>
<ul>
<li>consumeAsFlow()</li>
</ul>
<p>두 함수의 차이점으로는 </p>
<h4 id="receiveasflow">receiveAsFlow()</h4>
<ul>
<li>Channel을 Flow로 변환</li>
<li>Channel에서 소비자에게 데이터를 전송</li>
<li>여러 개의 소비자가 같은 Channel에 대해 동시에 구독할 수 있음</li>
<li>소비자가 데이터를 요청할 때마다 Channel에서 데이터를 받아와 Flow로 전달</li>
</ul>
<h4 id="consumeasflow">consumeAsFlow()</h4>
<ul>
<li>Channel의 데이터를 소비하는 Flow를 생성</li>
<li>Channel에서 데이터를 소비하여 소비자에게 전달</li>
<li>여러 개의 소비자가 같은 Channel에 대해 동시에 구독할 수 없음. 즉, 한 번에 하나의 소비자만이 데이터를 소비할 수 있음</li>
<li>Channel을 Flow로 변환하는 것이 아니라, 이미 생성된 Channel의 데이터를 소비하는 Flow를 생성</li>
</ul>
<p>사용 방법은 일반적인 flow와 똑같습니다.</p>
<pre><code class="language-kotlin">val channel: Channel&lt;Int&gt; = Channel(capacity = Channel.UNLIMITED)
        repeat(10) {
            channel.trySend(it)
        }
CoroutineScope(Dispatchers.Default).launch {
        channel.receiveAsFlow().onEach {
             println(&quot;받은 값 -&gt; $it&quot;)
         }.collect()
    }

받은 값 -&gt; 0
받은 값 -&gt; 1
받은 값 -&gt; 2
받은 값 -&gt; 3
받은 값 -&gt; 4
받은 값 -&gt; 5
받은 값 -&gt; 6
받은 값 -&gt; 7
받은 값 -&gt; 8
받은 값 -&gt; 9</code></pre>
<pre><code class="language-kotlin">val channel: Channel&lt;Int&gt; = Channel(capacity = Channel.UNLIMITED)
        repeat(10) {
            channel.trySend(it)
        }
CoroutineScope(Dispatchers.Default).launch {
        channel.consumeAsFlow().onEach {
             println(&quot;받은 값 -&gt; $it&quot;)
         }.collect()
    }

받은 값 -&gt; 0
받은 값 -&gt; 1
받은 값 -&gt; 2
받은 값 -&gt; 3
받은 값 -&gt; 4
받은 값 -&gt; 5
받은 값 -&gt; 6
받은 값 -&gt; 7
받은 값 -&gt; 8
받은 값 -&gt; 9</code></pre>
<hr>
<h4 id="마무리">마무리</h4>
<h4 id="reference">Reference</h4>
<p><a href="https://kotlinlang.org/docs/channels.html">kotlin-channels</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Maven Central에 라이브러리 배포하기]]></title>
            <link>https://velog.io/@plz_no_anr/Android-Maven-%EC%97%90-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@plz_no_anr/Android-Maven-%EC%97%90-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 12 Oct 2023 02:43:06 GMT</pubDate>
            <description><![CDATA[<p><del>내가 보려고 쓰는..</del></p>
<h3 id="1-sonatype-jira-계정-생성-및-로그인">1. Sonatype Jira 계정 생성 및 로그인</h3>
<p><a href="https://issues.sonatype.org/secure/Dashboard.jspa">https://issues.sonatype.org/secure/Dashboard.jspa</a></p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/d16bace2-94a3-43d4-b3a2-fcfa0126bf1f/image.png" alt="">
위의 링크로 이동하여 회원가입을 진행합니다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/8bc12721-4954-4827-8007-576061de552c/image.png" alt=""></p>
<p>이메일, 이름, 유저 아이디, 패스워드를 모두 입력한다. 이 중 유저 아이디는 로그인시 쓰이는 아이디이니 꼭 기억해야 합니다.</p>
<h3 id="2-이슈-생성">2. 이슈 생성</h3>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/e2d43648-245c-40ba-9362-c37909d21776/image.png" alt="">
로그인 후 상단탭의 만들기 버튼을 클릭하면 다음과 같은 화면이 나옵니다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/d28a3c24-bba8-4b1b-9249-0d0df1ecdcb2/image.png" alt="">
프로젝트와 이슈 유형은 위과 같이 설정하고 </p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/f3ba7631-4023-4939-ba7e-56c2e694b3fc/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/e3846dd5-6d33-4aa3-9dd9-c14191a4152b/image.png" alt="">
위와 같이 필수 값을 입력합니다.</p>
<p>Gitbub Repository를 퍼블리싱 한다고 하면
요약 : 프로젝트 이름
Group Id : io.github.(깃허브 유저 이름)
Project URL : Gitbub Repository 주소
SCM url : 깃허브에서 SSH 주소를 복사합니다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/6c32c164-72a8-4d5c-870b-65401c58a286/image.png" alt="">
모두 입력 후 생성하면 위과 같은 화면이 나오는데</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/a711d958-c26c-44e6-98d1-163822b44682/image.png" alt="">
댓글에 자신의 깃허브 주소를 검증하라는 댓글이 달립니다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/a5e47fdb-8d40-4374-88a9-51a6906b89fa/image.png" alt="">
레포지토리 생성 후 인증해 줍니다. (이때 레포지토리는 반드시 Public으로 생성해야 합니다.)</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/902908c0-d60d-4c7e-9665-8436492f45b9/image.png" alt="">
잠시 기다리면 위와 같은 댓글이 달리는데 이러면 준비는 끝입니다.</p>
<h3 id="3-nexus-로그인">3. Nexus 로그인</h3>
<p><a href="https://s01.oss.sonatype.org/#stagingRepositories">https://s01.oss.sonatype.org/#stagingRepositories</a></p>
<p>위 주소로 들어가면 </p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/3921ea63-df83-47be-b0de-380017efc35c/image.png" alt=""></p>
<p>위와 같은 화면이 나오는데 아까 Jira에서 가입한 유저 아이디로 로그인 해 줍니다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/0b559bbf-b25a-434c-ac60-2d493977c2ab/image.png" alt=""></p>
<p>왼쪽 메뉴 중 Staging Profiles를 클릭 후</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/1b3e5715-d1d4-453d-a7af-94a3e7e53b52/image.png" alt=""></p>
<p>자신의 프로필 주소를 클릭하면 주소창에 아래와 같이 생성이 되는데 <a href="https://s01.oss.sonatype.org/#stagingProfiles">https://s01.oss.sonatype.org/#stagingProfiles</a>;
[Profile Id]
여기서 Profile Id만 복사해 둡니다.</p>
<h3 id="4-gpg-key-pair-생성">4. GPG Key Pair 생성</h3>
<p>커맨드 창을 열고 다음과 같이 진행합니다.</p>
<p>gpg key 설치</p>
<pre><code>brew install gpg </code></pre><p>gpg key 생성</p>
<pre><code>gpg --full-gen-key</code></pre><p>1번 RSA and RSA 선택</p>
<pre><code>Please select what kind of key you want:
   (1) RSA and RSA
   (2) DSA and Elgamal
   (3) DSA (sign only)
   (4) RSA (sign only)
   (9) ECC (sign and encrypt) *default*
  (10) ECC (sign only)
  (14) Existing key from card
Your selection? 1</code></pre><p>키 길이는 4096 선택</p>
<pre><code>RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096</code></pre><p>키의 유효기간을 선택하는데 편의상 기간 제한이 없는 0번을 선택 했습니다.</p>
<pre><code>Please specify how long the key should be valid.
         0 = key does not expire
      &lt;n&gt;  = key expires in n days
      &lt;n&gt;w = key expires in n weeks
      &lt;n&gt;m = key expires in n months
      &lt;n&gt;y = key expires in n years
Key is valid for? (0) 0</code></pre><p>0번을 선택하면 확인 메세지가 나오는데 y 입력해 줍니다.</p>
<pre><code>Is this correct? (y/N) y</code></pre><p>마지막으로 다음의 정보를 입력합니다.</p>
<pre><code>Real name: 이름
Email address: 이메일
Comment: 안써도 됩니다.</code></pre><p>변경할 정보가 있으면 변경하고 없다면 O를 입력해서 넘어갑니다.</p>
<pre><code>Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O</code></pre><p>이제 gpg key의 패스워드를 2번 반복해서 입력해 줍니다.
*(패스워드는 퍼블리싱시 꼭 필요하니 미리 저장)</p>
<p>키가 생성 되었으면 퍼블릭키의 마지막 8자리를 복사해 줍니다.</p>
<pre><code>pub   rsa4096 2023-05-09 (SC)
      (퍼블릭키)
uid                      (이름) &lt;(이메일)&gt;
sub   rsa4096 2023-05-09 (E)</code></pre><p>GPG Key를 export합니다. 
이 명령어를 통해 나오는 문자열을 GPG Key로 저장해 줍니다.</p>
<pre><code>gpg --export-secret-keys (퍼블릭키 마지막 8자리) | base64</code></pre><h3 id="4-gradle-설정">4. Gradle 설정</h3>
<p>gradle은 kotlin-dsl을 기준으로 작성되었습니다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/95f6d24a-8376-4a43-b713-d4b0a8401d25/image.png" alt=""></p>
<p>local.properties로 위와 같이 값을 저장 했습니다.</p>
<p>(root) build.gradle.kts</p>
<pre><code class="language-kotlin">plugins {
    id(&quot;io.github.gradle-nexus.publish-plugin&quot;) version nexusPublishVersion
}

val properties = gradleLocalProperties(rootDir)

val userMap = mapOf(
    &quot;ossrhUsername&quot; to properties.getProperty(&quot;ossrhUsername&quot;),
    &quot;ossrhPassword&quot; to properties.getProperty(&quot;ossrhPassword&quot;),
    &quot;sonatypeStagingProfileId&quot; to properties.getProperty(&quot;sonatypeStagingProfileId&quot;),
)

nexusPublishing {
    repositories {
        sonatype {
            stagingProfileId.set(userMap[&quot;sonatypeStagingProfileId&quot;])
            username.set(userMap[&quot;ossrhUsername&quot;])
            password.set(userMap[&quot;ossrhPassword&quot;])
            nexusUrl.set(uri(&quot;https://s01.oss.sonatype.org/service/local/&quot;))
            snapshotRepositoryUrl.set(uri(&quot;https://s01.oss.sonatype.org/content/repositories/snapshots/&quot;))
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/5b2a1bf7-2af8-4b72-a7ff-e4be189f674d/image.png" alt="">
buildSrc를 사용하여 위와 같이 <strong>publish-module-android.gradle.kts</strong>를 생성합니다. (groovy를 사용하거나 buildSrc를 사용하지 않는다면 따로 scripts 폴더를 생성하여 퍼블리싱용 gradle 파일을 생성해도 무관합니다.</p>
<p><strong>buildSrc/build.gradle.kts</strong></p>
<pre><code class="language-kotlin">plugins {
    `kotlin-dsl`
    `kotlin-dsl-precompiled-script-plugins`
}

repositories {
    google()
    mavenCentral()
    gradlePluginPortal()
    mavenLocal()
}

dependencies {
    implementation(&quot;com.android.tools.build:gradle:8.1.1&quot;)
    implementation(&quot;org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.21&quot;)
}</code></pre>
<p><strong>buildSrc/publish-module-android.gradle.kts</strong></p>
<pre><code class="language-kotlin">import com.android.build.gradle.BaseExtension
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties

plugins {
    `maven-publish`
    signing
}

group = &quot;io.github.plz-no-anr&quot; (퍼블리싱 그룹Id) 
version = &quot;1.0.2&quot; (퍼블리싱 버전) 

val androidSourceJar by tasks.registering(Jar::class) {
        archiveClassifier.set(&quot;sources&quot;)
        from(project.extensions.getByType&lt;BaseExtension&gt;().sourceSets.getByName(&quot;main&quot;).java.srcDirs)
    }

(project as ExtensionAware).extensions.configure&lt;LibraryExtension&gt;(&quot;android&quot;) {
    publishing.singleVariant(&quot;release&quot;)
}

afterEvaluate {
    publishing {
        publications {
            create&lt;MavenPublication&gt;(&quot;release&quot;) {
                if (project.plugins.hasPlugin(&quot;com.android.library&quot;)) {
                    from(components[&quot;release&quot;])
                } else {
                    from(components[&quot;java&quot;])
                }
                artifact(androidSourceJar.get())
                groupId = &quot;io.github.plz-no-anr&quot; (퍼블리싱 그룹Id) 
                artifactId = &quot;coma&quot; (퍼블리싱할 라이브러리 이름)
                version = &quot;1.0.2&quot; (퍼블리싱 버전) 

                // Mostly self-explanatory metadata
                pom {
                    name.set(&quot;coma&quot;)
                    description.set(&quot;Compose MVI Android&quot;)
                    url.set(&quot;https://github.com/plz-no-anr/coma&quot;)
                    licenses { // 라이센스가 있다면 추가
                        license {
                            name.set(&quot;MIT license&quot;)
                            url.set(&quot;https://opensource.org/license/mit&quot;)
                        }
                    }
                    developers { 개발자 정보
                        developer {
                            id.set(&quot;plz-no-anr&quot;)
                            name.set(&quot;SanggunPark&quot;)
                            email.set(&quot;psgxxx@naver.com&quot;)
                        }
                    }
                    // Version control info
                    scm { // 깃허브 정보 (나중에 라이브러리 검색시 이동할 링크입니다.)
                        connection.set(&quot;scm:git:github.com/plz-no-anr/coma.git&quot;)
                        developerConnection.set(&quot;scm:git:ssh://github.com/plz-no-anr/coma.git&quot;)
                        url.set(&quot;https://github.com/plz-no-anr/coma/tree/main&quot;)
                    }
                }

            }

        }
    }
}

// local.properties 불러오기
val properties = gradleLocalProperties(rootDir)
val signingMap = mapOf(
    &quot;signing.keyId&quot; to properties.getProperty(&quot;signing.keyId&quot;),
    &quot;signing.password&quot; to properties.getProperty(&quot;signing.password&quot;),
    &quot;signing.key&quot; to properties.getProperty(&quot;signing.key&quot;)
)

signing {
    useInMemoryPgpKeys(
        signingMap[&quot;signing.keyId&quot;],
        signingMap[&quot;signing.key&quot;],
        signingMap[&quot;signing.password&quot;]
    )
    sign(publishing.publications)
}</code></pre>
<p>위와 같이 퍼블리싱할 gradle 정보 셋팅 후 module단의 gradle에 plugin을 추가해 줍니다.</p>
<pre><code class="language-kotlin">plugins {
    androidLibrary
    kotlinAndroid
    id(&quot;publish-module-android&quot;)  // 위에서 만든 퍼블리싱 gradle
}

android {
    namespace = &quot;plznoanr.coma&quot;
    compileSdk = Config.compileSdkVersion

    defaultConfig {
        minSdk = Config.minSdkVersion

        testInstrumentationRunner = &quot;androidx.test.runner.AndroidJUnitRunner&quot;
        consumerProguardFiles(&quot;consumer-rules.pro&quot;)
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile(&quot;proguard-android-optimize.txt&quot;),
                &quot;proguard-rules.pro&quot;
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_11.toString()
    }
}</code></pre>
<p>이때 간혹 릴리즈 옵션에 isMinifyEnabled = true를 사용하여 난독화 시 에러가 발생하는 경우가 있기 때문에 이 옵션은 false로 설정해 주거나 proguard를 통해 에러가 나는 부분을 예외 처리해 줍니다.</p>
<h4 id="이제-준비는-끝났고-task를-이용해-라이브러리를-올려줍니다">이제 준비는 끝났고 Task를 이용해 라이브러리를 올려줍니다.</h4>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/61707c3f-c9ca-47a2-9d9b-ee8171e1743f/image.png" alt="">
위와 같이 publishReleasePublicationToSonatypeRepository를 클릭해 올리거나 Terminal에서 다음과 같이 커맨드를 입력해 올려줍니다.</p>
<pre><code>./gradlew (모듈명):publishReleasePublicationToSonatypeRepository</code></pre><p>빌드가 성공하면 배포 준비는 끝입니다.</p>
<h3 id="5-배포">5. 배포</h3>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/4f851893-5836-4172-8764-51e58cedf534/image.png" alt="">
<img src="https://velog.velcdn.com/images/plz_no_anr/post/b8bd3678-4e1e-4419-bfbc-447ef44883d3/image.png" alt=""></p>
<p><a href="https://s01.oss.sonatype.org/#stagingRepositories">https://s01.oss.sonatype.org/#stagingRepositories</a> 에서 위와 같이 라이브러리가 올라간 것을 확인할 수 있는데 배포할 라이브러리 선택 후 Close - Confirm 을 클릭합니다.</p>
<p>잠시후 Refresh 버튼을 누르면 Release 버튼이 활성화 되는데 누르면 배포가 완료됩니다.</p>
<p><img src="https://velog.velcdn.com/images/plz_no_anr/post/8cb8013e-e4b7-41f9-9b72-b67a7b64371c/image.png" alt=""></p>
<p>배포가 완료된 라이브러리는 <a href="https://central.sonatype.com/">여기</a>서 확인이 가능합니다.</p>
<h3 id="번외">번외</h3>
<p>Github ReadMe에 배포한 라이브러리 뱃지 추가하는 법</p>
<pre><code>[![Maven Central](https://img.shields.io/maven-central/v/그룹아이디.유저아이디/아티팩트-이름.svg)](https://central.sonatype.com/artifact/그룹아이디.유저아이디/아티팩트-이름)</code></pre><p><strong>Ex</strong></p>
<pre><code>[![Maven Central](https://img.shields.io/maven-central/v/io.github.plz-no-anr/coma.svg)](https://central.sonatype.com/artifact/io.github.plz-no-anr/coma)</code></pre><p><a href="https://central.sonatype.com/artifact/io.github.plz-no-anr/coma"><img src="https://img.shields.io/maven-central/v/io.github.plz-no-anr/coma.svg" alt="Maven Central"></a></p>
<hr>
<h4 id="reference">Reference</h4>
<ul>
<li>배포된 <a href="https://central.sonatype.com/artifact/io.github.plz-no-anr/coma">라이브러리</a></li>
<li><a href="https://betterprogramming.pub/how-to-create-and-publish-an-android-library-in-mavencentral-92397df94103">How To Create and Publish an Android Library in MavenCentral</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] lateinit과 by lazy]]></title>
            <link>https://velog.io/@plz_no_anr/Kotlin-lateinit%EA%B3%BC-by-lazy</link>
            <guid>https://velog.io/@plz_no_anr/Kotlin-lateinit%EA%B3%BC-by-lazy</guid>
            <pubDate>Thu, 16 Jun 2022 02:24:13 GMT</pubDate>
            <description><![CDATA[<p><del>늦은 초기화 너 뭔데?</del></p>
<p>자바로 개발을 하다보면 많이 볼 수 있는 에러인 NPE(Null Pointer Exception)
NPE가 발생하면 벌써부터 머리가 어질어질 합니다.
그래서 코틀린에선 NullPointerExeption을 코드상에서 제거하기 위해 Null Safety를 지원합니다.</p>
<p><strong>Null Safety</strong>란 개발자가 코딩중에 실수하지 않도록 타입 시스템이 Null이 가능한지 아닌지 변수 선언시 타입뒤에 ?를 붙혀 Null을 허용하도록 해주는 기능입니다.</p>
<p>그런데 코드 상에서는 null이 될 수도 있지만 실제 동작 중에는 null이 될 소지가 없는 경우가 있습니다.</p>
<p>예를 들어 </p>
<pre><code class="language-kotlin">class MainActivity: AppCompatActivity() {
    var testString: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        testString = &quot;테스트&quot;
    }
}</code></pre>
<p>위와 같이 <code>testString</code>를 전역변수로 선언 후 <code>onCreate()</code>에서 초기화를 시켜주는 코드입니다. 이때 <code>onCreate()</code>에서 값이 항상 초기화되기 때문에 안전한 코드라고 볼 수 있습니다. 그러나 이때도 ?를 표시해야 하는 불편함이 있습니다.</p>
<p>이때 사용하는 키워드가 lateinit과 by lazy입니다. 둘은 비슷하면서 다른 점이 있습니다.</p>
<hr>
<h2 id="1-lateinit">1. lateinit</h2>
<pre><code class="language-kotlin">class MainActivity: AppCompatActivity() {
    lateinit var testString: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        testString = &quot;테스트&quot;
        println(testString)
    }
}</code></pre>
<p>위와 같이 변수 앞에<code>lateinit</code> 키워드를 붙히고 변수의 자료형을 명시해줍니다.
lateinit은 mutable변수에만 사용이 가능하기 때문에 var 타입의 변수에만 사용이 가능합니다.
실행 중 값을 변경할 필요가 있는 경우에 유용합니다.</p>
<h2 id="2-by-lazy">2. by lazy</h2>
<pre><code class="language-kotlin">class MainActivity: AppCompatActivity() {
    val testString: String by lazy {
        &quot;테스트&quot;
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        println(testString)
    }
}</code></pre>
<p><code>by lazy</code>는 해당 변수가 호출되는 시점에 초기화를 하겠다는 의미입니다.
<code>by lazy</code>는 <code>lateinit</code> 과 다르게 immutable변수에만 사용이 가능하기 때문에 val 타입의 변수에만 사용이 가능합니다.</p>
<hr>
<h4 id="마무리">마무리</h4>
<h4 id="자바를-사용하다-코틀린으로-넘어가신-분들은-공감하시겠지만-코틀린-한번-쓰면-다시-자바로-넘어가고-싶지가-않네요">자바를 사용하다 코틀린으로 넘어가신 분들은 공감하시겠지만 코틀린 한번 쓰면 다시 자바로 넘어가고 싶지가 않네요..</h4>
<h4 id="reference">Reference</h4>
<p><a href="https://selfish-developer.com/entry/kotlin-lateinit-lazy-by">kotlin lateinit, lazy by</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] 확장함수와 중위함수 + 꼬리재귀]]></title>
            <link>https://velog.io/@plz_no_anr/Kotlin-%ED%99%95%EC%9E%A5%ED%95%A8%EC%88%98%EC%99%80-%EC%A4%91%EC%9C%84%ED%95%A8%EC%88%98-%EA%BC%AC%EB%A6%AC%EC%9E%AC%EA%B7%80</link>
            <guid>https://velog.io/@plz_no_anr/Kotlin-%ED%99%95%EC%9E%A5%ED%95%A8%EC%88%98%EC%99%80-%EC%A4%91%EC%9C%84%ED%95%A8%EC%88%98-%EA%BC%AC%EB%A6%AC%EC%9E%AC%EA%B7%80</guid>
            <pubDate>Tue, 14 Jun 2022 02:21:04 GMT</pubDate>
            <description><![CDATA[<p><del>확장함수, 중위함수, 꼬리재귀함수 코틀린에선 모두 무료로 제공합니다.</del></p>
<h2 id="1-확장함수">1. 확장함수</h2>
<p>자바나 코틀린에서 지원하는 내장 클래스에는 다양한 함수가 있습니다.</p>
<p>예를 들어 <code>&quot;hello kotlin&quot;.toUpperCase()</code> 라는 코드를 보면 앞의 문자열을 대문자로 변환하는 아주 간단한 코드입니다. </p>
<p>여기서 <code>toUpperCase()</code> 라는 함수는 String 클래스에서 기본적으로 제공하는 메소드죠.
그러나 여기서 자신이 직접 구현한 메소드를 추가하고 싶다면 어떻게 해야 할까요??</p>
<p>코틀린에선 <strong>확장함수</strong>라는 이름으로 이러한 기능을 지원합니다.</p>
<p>예를 들어 이런식으로 사용이 가능합니다.</p>
<pre><code class="language-kotlin">fun String.toConversion(): String {
    var s = &quot;&quot;
    this.indices.forEach {
       s += if (it % 2 == 0) this[it].uppercase() else this[it].lowercase()
    }
    return s
}

println(&quot;Hello Kotlin&quot;.toConversion())
&gt;&gt; HeLlO KoTlIn</code></pre>
<p>위의 함수는 주어진 문자열의 짝수는 대문자 홀수는 소문자로 변환 해주는 String 클래스의 확장함수 입니다.</p>
<h2 id="2-중위함수">2. 중위함수</h2>
<p>중위 표현법은 변수와 변수사이에 함수를 넣어 연산자 처럼 사용하는 것 입니다.</p>
<p>코틀린에서는 메소드 앞에 <code>infix</code> 키워드를 붙혀 사용할 수 있습니다.</p>
<p>예를 들면</p>
<pre><code class="language-kotlin">infix fun Int.add(x: Int) = this + x

println(&quot;result: ${1 add 2}&quot;)
&gt;&gt; result: 3</code></pre>
<p>위와 같은 식으로 자기 자신에 인자로 받은 x를 더해 리턴 할 수 있습니다.</p>
<h2 id="3-꼬리재귀함수">3. 꼬리재귀함수</h2>
<p>코틀린에서 꼬리재귀함수 구현은 메소드에 <code>tailrec</code> 키워드를 붙이면 컴파일러가 꼬리재귀를 루프를 이용한 코드로 변경해 줍니다.</p>
<pre><code class="language-kotlin">tailrec fun recursive(x: Int, y: Int): Int {
    return if (x % 2 == 0) y else recursive(x+1, y+1)
}

println(recursive(1,2))
&gt;&gt; result: 3</code></pre>
<p>위와 같이 <code>recursive()</code> 메소드를 사용해서 재귀함수를 구현할 수 있습니다.</p>
<hr>
<h4 id="코틀린에는-이렇게-많은-기능을-제공하는데-상황에-따라-사용한다면-아주-유용하게-사용이-가능하다고-생각합니다">코틀린에는 이렇게 많은 기능을 제공하는데 상황에 따라 사용한다면 아주 유용하게 사용이 가능하다고 생각합니다.</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Android] Koin을 Dagger-Hilt로 변환하기]]></title>
            <link>https://velog.io/@plz_no_anr/Android-Koin%EC%9D%84-Dagger-Hilt%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@plz_no_anr/Android-Koin%EC%9D%84-Dagger-Hilt%EB%A1%9C-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 31 May 2022 07:23:34 GMT</pubDate>
            <description><![CDATA[<p><del>Koin &lt; Dagger-Hilt ?????</del></p>
<p><a href="https://velog.io/@plz_no_anr/Kotlin%EC%97%90%EC%84%9C-DIKoin-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">Koin사용법</a></p>
<p>안드로이드에는 DI 라이브러리로 Dagger, Koin, Hilt 등을 사용하는데
기존 프로젝트에 Koin을 사용하면서 느낀 건 런타임 에러를 잡기다는 거였다..</p>
<p>그래서 기존의 Koin을 Hilt로 변경하기로 결정! 했습니다.</p>
<p>우선 Koin과 Dagger-Hilt의 장단점을 살펴보면</p>
<h3 id="koin">Koin</h3>
<p>장점 </p>
<ul>
<li>러닝커브가 낮다 (쉽게 배울 수 있음)</li>
<li>Kotlin에 최적화 되어 있음</li>
</ul>
<p>단점</p>
<ul>
<li>런타임에 인스턴스를 동적으로 주입하기 때문에 런타임 퍼포먼스가 떨어짐</li>
<li>런타임 에러가 발생할 수 있음</li>
</ul>
<h3 id="dagger-hilt">Dagger-Hilt</h3>
<p>장점</p>
<ul>
<li>Dagger2를 기반으로 만들어졌기 때문에 기존에 Dagger2를 사용하던 분들이라면 쉽게 적응 가능</li>
<li>컴파일할 때 에러 검출 가능</li>
</ul>
<p>단점</p>
<ul>
<li>컴파일 타임에 주입하기 때문에 컴파일 시간이 오래 걸림</li>
<li>Dagger2에 비교하면 쉽지만 Koin과 비교하면 러닝커브가 높음</li>
</ul>
<p>위에서 살펴본 Koin과 Hilt의 장단점을 종합해 보면 배우기만 한다면 Hilt가 더 좋다고 생각된다.</p>
<hr>
<p>이제 기존의 Koin으로 구현한 코드를 Hilt로 변경해 보면</p>
<h3 id="1-의존성-추가">1. 의존성 추가</h3>
<p>우선 프로젝트와 앱 모듈단에 각각 의존성을 추가 해줍니다.</p>
<h4 id="project">Project</h4>
<pre><code class="language-kotlin">buildscript {
    ext.hilt_version = &#39;2.35&#39;

    dependencies {
        classpath &quot;com.google.dagger:hilt-android-gradle-plugin:$hilt_version&quot;
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}</code></pre>
<h4 id="appmodule">AppModule</h4>
<pre><code class="language-kotlin">plugins {
    id &#39;kotlin-kapt&#39;
    id &#39;dagger.hilt.android.plugin&#39;
}
// Dagger-Hilt
    implementation &quot;com.google.dagger:hilt-android:$hilt_version&quot;
    kapt &quot;com.google.dagger:hilt-android-compiler:$hilt_version&quot;</code></pre>
<h3 id="2-구현">2. 구현</h3>
<h3 id="application">Application</h3>
<h4 id="koin-1">Koin</h4>
<pre><code class="language-kotlin">class LottoApp: Application() {
   override fun onCreate() {
        super.onCreate()
        startKoin {
            androidLogger(Level.NONE)
            androidContext(this@LottoApp)
            modules(listOf(appModule, viewModelModule, repositoryModule))
        }
    }

}</code></pre>
<h4 id="hilt">Hilt</h4>
<pre><code class="language-kotlin">@HiltAndroidApp
class LottoApp: Application(){}</code></pre>
<p>위와 같이 Hilt는 Koin처럼 Applicaiton 클래스에 따로 코드를 작성하지 않고 <code>@HiltAndroidApp</code> 어노테이션만 적어주면 된다.</p>
<h3 id="module">Module</h3>
<h4 id="koin-2">Koin</h4>
<pre><code class="language-kotlin">val appModule = module {
        single {
            Retrofit.Builder()
                .baseUrl(&quot;https://www.dhlottery.co.kr&quot;)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
                .create(LottoAPI::class.java)
        }
        single {
            Room.databaseBuilder(
                androidApplication(),
                AppDatabase::class.java,
                &quot;lotto-app.db&quot;)
                .build()
        }
        single { get&lt;AppDatabase&gt;().LottoDao() }
    }

    val viewModelModule = module {
        viewModel { MainViewModel(get()) }
        viewModel { GeneNumViewModel() }
        viewModel { QRScanViewModel(get()) }
        viewModel { SplashViewModel(get()) }
    }

    val repositoryModule = module {
        single { AppRepository(get(),get()) }
    }</code></pre>
<h4 id="hilt-1">Hilt</h4>
<pre><code class="language-kotlin">@Module
@InstallIn(SingletonComponent::class)
object Modules {
    @Provides
    @Singleton
    fun provideApiService(
    ): LottoAPI {
        return Retrofit.Builder()
            .baseUrl(&quot;https://www.dhlottery.co.kr&quot;)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(LottoAPI::class.java)
    }

    @Provides
    @Singleton
    fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
        return Room.databaseBuilder(
            context,
            AppDatabase::class.java,
            &quot;lotto-app.db&quot;
        )
            .build()
    }

    @Provides
    @Singleton
    fun provideLottoDao(database: AppDatabase): LottoDao {
        return database.LottoDao()
    }

    @Provides
    @Singleton
    fun provideRepository(dao: LottoDao, api: LottoAPI): AppRepository {
        return AppRepository(dao, api)
    }
}</code></pre>
<p>코인처럼 모듈 클래스를 생성하는데 이 부분만 봐도 코인이 더 쉽다는 걸 느낄 수 있다.
Hilt는 클래스 상단에 <code>@Module</code> 어노테이션을 적고 <code>@InstallIn(SingletonComponent::class)</code> 이런식으로 어느 컴포넌트에 적용할 지 정할 수 있습니다.</p>
<p>이제 액티비티와 뷰모델에서의 차이점을 보면</p>
<h3 id="activity">Activity</h3>
<h4 id="koin-3">Koin</h4>
<pre><code class="language-kotlin">class MainActivity : BaseActivity&lt;ActivityMainBinding,MainViewModel&gt;(R.layout.activity_main) {
    override val viewModel: MainViewModel by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }</code></pre>
<h4 id="hilt-2">Hilt</h4>
<pre><code class="language-kotlin">@AndroidEntryPoint
class MainActivity : BaseActivity&lt;ActivityMainBinding,MainViewModel&gt;(R.layout.activity_main) {
    override val viewModel: MainViewModel by viewModels() // ViewModel을 주입

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

    }</code></pre>
<p>Hilt는 액티비티 상단에 <code>@AndroidEntryPoint</code> 어노테이션을 사용하는데 Activity, Fragment, View, Service, BroadcastReceiver 같은 안드로이드 컴포넌트에 사용할 수 있는 어노테이션입니다.</p>
<h3 id="viewmodel">ViewModel</h3>
<h4 id="koin-4">Koin</h4>
<p>Koin은 위에 모듈 클래스에 ViewModel을 선언해 주었기 때문에 따로 뷰모델에 코드를 작성하지 않습니다.</p>
<h4 id="hilt-3">Hilt</h4>
<pre><code class="language-kotlin">@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: AppRepository
): BaseViewModel() {
...
}</code></pre>
<p>Hilt에서는 뷰모델에 위와 같이 구현하는데 <code>@HiltViewModel</code> 어노테이션을 사용하면 <code>ViewModelProvider.Factory</code>를 매번 구현하지 않고 사용할 수 있습니다.
<code>@Inject</code> 어노테이션을 사용하여 뷰모델에서 필요한 repository 인스턴스를 주입 받을 수 있습니다.</p>
<hr>
<h4 id="마무리">마무리</h4>
<h4 id="이렇게-기존에-구현한-koin을-hilt로-변경했는데-확실히-컴파일-타임에-에러를-잡을-수-있어서-좋은-것-같습니다">이렇게 기존에 구현한 Koin을 Hilt로 변경했는데 확실히 컴파일 타임에 에러를 잡을 수 있어서 좋은 것 같습니다.</h4>
<h4 id="하지만-컴파일-시간이-더-오래-걸린다는-거"><del>하지만 컴파일 시간이 더 오래 걸린다는 거...</del></h4>
<p>끝👍</p>
]]></description>
        </item>
    </channel>
</rss>