<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Gyun's 개발일지</title>
    <link>https://devlog-wjdrbs96.tistory.com/</link>
    <description>규니의 개발일지</description>
    <language>ko</language>
    <pubDate>Wed, 8 Apr 2026 18:31:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>백엔드 규니</managingEditor>
    <image>
      <title>Gyun's 개발일지</title>
      <url>https://tistory1.daumcdn.net/tistory/3416001/attach/2ed29dabf3534780bdf3b6143ae63658</url>
      <link>https://devlog-wjdrbs96.tistory.com</link>
    </image>
    <item>
      <title>[Coroutine] runBlocking vs coroutineScope 차이는?</title>
      <link>https://devlog-wjdrbs96.tistory.com/452</link>
      <description>&lt;h1&gt;&lt;code&gt;runBlocking, coroutineScope 차이점&lt;/code&gt;&lt;/h1&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;runBlocking&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;runBlocking 내부에서 새로운 코루틴 스코프를 생성&lt;/li&gt;
&lt;li&gt;블록 내부의 모든 코루틴이 끝날 때까지 runBlocking이 호출된 쓰레드는 대기&lt;/li&gt;
&lt;li&gt;주로 main 함수와 같이 비코루틴에서 호출할 때나 테스트 코드에서 코루틴을 사용할 때 일반적인 코드 블록처럼 동작하게 하기 위해 사용됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;coroutineScope&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;suspend 함수 안에서만 호출할 수 있음&lt;/li&gt;
&lt;li&gt;coroutineScope는 새로운 코루틴 스코프를 만들지만 현재 쓰레드를 차단하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;1. Coroutine 신규 생성 여부&lt;/code&gt;&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun main() {
    runBlocking {
        println(&quot;[Main] ${Thread.currentThread().name}&quot;)
        makeCoroutine()
        notMakeCoroutine()
    }
}

fun makeCoroutine() = runBlocking {
    println(&quot;[RunBlocking] Make Coroutine ${Thread.currentThread().name}&quot;)
}

suspend fun notMakeCoroutine() = coroutineScope {
    println(&quot;[CoroutineScope] Make Coroutine ${Thread.currentThread().name}&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;[Main] main @coroutine#1
[RunBlocking] Make Coroutine main @coroutine#2
[CoroutineScope] Make Coroutine main @coroutine#1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 &lt;code&gt;runBlocking {}&lt;/code&gt; 통해서 생성한 메소드는 코루틴을 새로 생성한 것을 볼 수 있고, &lt;code&gt;coroutineScope {}&lt;/code&gt;을 통해서 생성한 메소드는 코루틴을 생성하지 않은 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;2. 스레드 블로킹 여부&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;runBlocking {}은 현재 스레드를 블로킹하지만 coroutineScope {}은 suspend function 으로서 현재 스레드를 블로킹 하지 않는다는 차이가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드를 Blocking 하는지 안하는지가 가장 큰 차이라고 할 수 있는데, 처음에는 이 부분이 가장 잘 이해가 안되었습니다. 그래서 쓰레드와 코루틴에 대해 간단하게 먼저 정리해보려 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로세스가 있어야만 스레드가 있을 수 있고, 스레드는 프로세스를 바꿀 수 없다.&lt;/li&gt;
&lt;li&gt;코루틴의 코드가 실행되려면 스레드가 있어야 한다.&lt;/li&gt;
&lt;li&gt;코루틴이 중단되었다가 재개될 때 다른 스레드에 배정될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드와 코루틴을 아주 간단하게 말하면 위와 같은 특징이 존재합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/015b4fe8-db6c-402c-9c8d-cb9aa1fd364d&quot; alt=&quot;스크린샷 2024-11-09 오전 12 23 41&quot; width=&quot;1078&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 코루틴은 여러 쓰레드에서 실행될 수 있다는 것을 표현하려고 대략적으로 그려보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 여기서 쓰레드가 Blocking 되면 코루틴은 어떻게 될까요?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/497593b6-2ffc-40f7-a42f-4a4dea1a049a&quot; alt=&quot;스크린샷 2024-11-09 오전 12 32 01&quot; width=&quot;1372&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰레드가 멈추게 되었을 때 Blocking 되어 있으면 멈춰있는 시간 동안 다른 코루틴이 해당 쓰레드를 사용할 수 없게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 Non-Blocking 이라면 쓰레드가 멈춰 있더라도 다른 코루틴이 해당 쓰레드를 사용하여 동작할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;fun main() {
    runBlocking {
        blockingExample()
        nonBlockingExample()
    }
}

suspend fun blockingExample() = coroutineScope {
    repeat(2) { index -&amp;gt;
        launch {
            println(&quot;Blocking coroutine $index started on ${Thread.currentThread().name}&quot;)
            Thread.sleep(1000)  // Thread Block
            println(&quot;Blocking coroutine $index finished on ${Thread.currentThread().name}&quot;)
        }
    }
}

suspend fun nonBlockingExample() = coroutineScope {
    repeat(2) { index -&amp;gt;
        launch {
            println(&quot;Non-blocking coroutine $index started on ${Thread.currentThread().name}&quot;)
            delay(1000) // Thread Non-Block
            println(&quot;Non-blocking coroutine $index finished on ${Thread.currentThread().name}&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;Blocking coroutine 0 started on main @coroutine#2
Blocking coroutine 0 finished on main @coroutine#2
Blocking coroutine 1 started on main @coroutine#3
Blocking coroutine 1 finished on main @coroutine#3
Non-blocking coroutine 0 started on main @coroutine#4
Non-blocking coroutine 1 started on main @coroutine#5
Non-blocking coroutine 0 finished on main @coroutine#4
Non-blocking coroutine 1 finished on main @coroutine#5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 보면 쓰레드 Block을 위해 &lt;code&gt;Thread.sleep&lt;/code&gt;을 사용하였고, 쓰레드 Non-Block을 위해서 &lt;code&gt;suspend delay&lt;/code&gt;를 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과를 보면 Blocking 코드는 쓰레드가 Block 되기 때문에 순차 실행되는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 Non-Blocking 코드는 쓰레드가 블락되지 않기 때문에 delay 통해서 코루틴이 멈춰있는 동안 새로운 코루틴이 생성되어 &lt;code&gt;Non-Blocking start&lt;/code&gt;를 출력하는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드까지 보면 쓰레드가 Block, Non-Block이 어떤 차이가 있는지 알 수 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;code&gt;runBlocking vs coroutineScope&lt;/code&gt;을 사용해서 하나의 예시를 더 확인 해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;예제 코드&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;private val context = Executors.newFixedThreadPool(2).asCoroutineDispatcher()

fun main() {
    runBlocking {
        blockingExample()
        println(&quot;***************************************************&quot;)
        nonBlockingExample()
    }
}

suspend fun blockingExample() = runBlocking {
    repeat(5) { index -&amp;gt;
        launch(context) {
            runBlocking {
                println(&quot;[A] Blocking coroutine $index started on ${Thread.currentThread().name}&quot;)
                delay(1000)
                println(&quot;[B] Blocking coroutine $index finished on ${Thread.currentThread().name}&quot;)
            }
        }
    }
}

suspend fun nonBlockingExample() = runBlocking {
    repeat(5) { index -&amp;gt;
        launch(context) {
            coroutineScope {
                println(&quot;[A] Non-blocking coroutine $index started on ${Thread.currentThread().name}&quot;)
                delay(1000)
                println(&quot;[B] Non-blocking coroutine $index finished on ${Thread.currentThread().name}&quot;)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;[A] Blocking coroutine 0 started on pool-1-thread-1 @coroutine#5
[A] Blocking coroutine 1 started on pool-1-thread-2 @coroutine#9
[B] Blocking coroutine 0 finished on pool-1-thread-1 @coroutine#5
[A] Blocking coroutine 2 started on pool-1-thread-1 @coroutine#10
[B] Blocking coroutine 1 finished on pool-1-thread-2 @coroutine#9
[A] Blocking coroutine 3 started on pool-1-thread-2 @coroutine#11
[B] Blocking coroutine 2 finished on pool-1-thread-1 @coroutine#10
[B] Blocking coroutine 3 finished on pool-1-thread-2 @coroutine#11
[A] Blocking coroutine 4 started on pool-1-thread-1 @coroutine#12
[B] Blocking coroutine 4 finished on pool-1-thread-1 @coroutine#12
***************************************************
[A] Non-blocking coroutine 1 started on pool-1-thread-1 @coroutine#15
[A] Non-blocking coroutine 0 started on pool-1-thread-2 @coroutine#14
[A] Non-blocking coroutine 2 started on pool-1-thread-2 @coroutine#16
[A] Non-blocking coroutine 3 started on pool-1-thread-1 @coroutine#17
[A] Non-blocking coroutine 4 started on pool-1-thread-2 @coroutine#18
[B] Non-blocking coroutine 1 finished on pool-1-thread-1 @coroutine#15
[B] Non-blocking coroutine 0 finished on pool-1-thread-2 @coroutine#14
[B] Non-blocking coroutine 2 finished on pool-1-thread-1 @coroutine#16
[B] Non-blocking coroutine 3 finished on pool-1-thread-2 @coroutine#17
[B] Non-blocking coroutine 4 finished on pool-1-thread-1 @coroutine#18&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 2개의 쓰레드에서 Blocking(runBlocking), Non-Blocking(coroutineScope) 으로 동작하도록 코드를 작성한 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/user-attachments/assets/ea9c58ec-17c9-4923-af2a-e947034a85dc&quot; alt=&quot;스크린샷 2024-11-09 오전 2 27 59&quot; width=&quot;1246&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Blocking 코드의 결과를 보면 위의 그림처럼 표현할 수 있습니다. runBlocking 안에서 &lt;code&gt;delay(1000)&lt;/code&gt;를 통해서 코루틴이 중단 될 것인데요. &lt;code&gt;coroutine #1&lt;/code&gt;에서 start, finish를 모두 출력하고 작업이 완료 되어야 다른 코루틴이 해당 쓰레드를 사용할 수 있도록 Block 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 해당 쓰레드는 중단되는 동안 다른 코루틴의 작업을 처리할 수 없다는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2024-11-09 오전 2.50.51.png&quot; data-origin-width=&quot;2478&quot; data-origin-height=&quot;1290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/omBTz/btsKDR4cc8a/C2zSF1zl45mtMjvOmKpCQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/omBTz/btsKDR4cc8a/C2zSF1zl45mtMjvOmKpCQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/omBTz/btsKDR4cc8a/C2zSF1zl45mtMjvOmKpCQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FomBTz%2FbtsKDR4cc8a%2FC2zSF1zl45mtMjvOmKpCQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2478&quot; height=&quot;1290&quot; data-filename=&quot;스크린샷 2024-11-09 오전 2.50.51.png&quot; data-origin-width=&quot;2478&quot; data-origin-height=&quot;1290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 Non-Blocking 코드의 결과를 보면 coroutineScope 안에서 &lt;code&gt;delay(1000)&lt;/code&gt;를 통해서 코루틴이 중단 될 때 Thread가 Block 되지 않고 다른 코루틴이 해당 쓰레드에서 실행되고 있는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Reference&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kghworks.tistory.com/205&quot;&gt;https://kghworks.tistory.com/205&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html&quot;&gt;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html&quot;&gt;https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kotlinlang.org/docs/coroutines-basics.html#scope-builder&quot;&gt;https://kotlinlang.org/docs/coroutines-basics.html#scope-builder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://myungpyo.medium.com/reading-coroutine-official-guide-thoroughly-part-0-20176d431e9d&quot;&gt;https://myungpyo.medium.com/reading-coroutine-official-guide-thoroughly-part-0-20176d431e9d&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://myungpyo.medium.com/reading-coroutine-official-guide-thoroughly-part-1-98f6e792bd5b&quot;&gt;https://myungpyo.medium.com/reading-coroutine-official-guide-thoroughly-part-1-98f6e792bd5b&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Reactive/Coroutine</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/452</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/452#entry452comment</comments>
      <pubDate>Sat, 9 Nov 2024 02:46:13 +0900</pubDate>
    </item>
    <item>
      <title>[Cassandra] Cassandra truncate 이후 snapshot 제거</title>
      <link>https://devlog-wjdrbs96.tistory.com/448</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Cassandra truncate 이후 snaptshot 제거&lt;/code&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;truncate&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;TRUNCATE test_keyspsace.user_activity;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라에서도 &lt;code&gt;truncate&lt;/code&gt; 명령어로 테이블의 데이터를 지울 수 있다. 하지만 데이터를 다시 복구해야 할 수도 있기 때문에 스냅샷이라는 데이터를 남겨놓는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, truncate 한다고 해서 바로 Disk 사용량이 줄어들지 않고 스냅샷을 제거해야 서버의 Disk 사용량이 줄어들게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;snapshot 제거&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;./nodetool clearsnapshot {keyspace} --all
ex) ./nodetool clearsnapshot test_keyspsace --all&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cassandra TRUNCATE 작업 후에 위의 명령어로 SNAPSHOT 제거도 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 알게된 것은 스냅샷 제거는 테이블 단위로 제거하는 줄 알았는데 keyspace 단위로 제거해야 하는 것이었다. 그리고 노드 하나 들어가서 스냅샷 제거하면 알아서 스냅샷이 다 제거될 줄 알았는데, 노드별로 다 들어가서 직접 삭제해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 생각해보면, 카산드라는 노드별로 토큰의 범위를 가지고 그 범위에 있는 데이터를 가지고 있기 때문에.. 당연한거 같다. (한번에 모든 노드 스냅샷 날리는 명령어가 있는지 모르겠다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 4대의 노드를 쓰고 있었는데 4대 노드에 각각 스냅샷 제거 명령어를 실행 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Reference&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datastax.com/en/cql-oss/3.x/cql/cql_reference/cqlTruncate.html&quot;&gt;https://docs.datastax.com/en/cql-oss/3.x/cql/cql_reference/cqlTruncate.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Cassandra</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/448</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/448#entry448comment</comments>
      <pubDate>Wed, 10 Apr 2024 21:57:57 +0900</pubDate>
    </item>
    <item>
      <title>[MySQL] MySQL 인덱스 컨디션 푸시다운이란?</title>
      <link>https://devlog-wjdrbs96.tistory.com/447</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;MySQL 인덱스 컨디션 푸시다운 이란?&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 5.6 버전부터는 인덱스 컨디션 푸시다운(&lt;code&gt;Index Condition Pushdown&lt;/code&gt;) 이라는 기능이 도입 되었는데요. 어떤 내용인지 알아보겠습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;인덱스 컨디션 푸시 다운&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;secondary index에만 사용됩니다.&lt;/li&gt;
&lt;li&gt;전체 row 읽기의 수를 줄여 I/O 작업을 줄이는 것.&lt;/li&gt;
&lt;li&gt;인덱스를 범위 제한 조건으로 사용하지 못하는 쿼리에 한해서 발생하는 것 같음 (&lt;code&gt;LIKE %name%&lt;/code&gt;, &lt;code&gt;like &amp;gt; 10&lt;/code&gt; 같이 인덱스를 이용하지 못하는 범위 검색을 의미하는 것 같음)&lt;/li&gt;
&lt;li&gt;InnoDB Clustered index의 경우, 전체 레코드가 InnoDB 버퍼에 존재하기 때문에, 인덱스 컨디션 푸시다운을 사용해도 I/O가 감소하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;MySQL SQL 수행 절차&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/bd4a6fce-87d2-4e4d-93c5-6fed1c1749b8&quot; alt=&quot;스크린샷 2024-04-10 오전 12 51 30&quot; width=&quot;780&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;MySQL 엔진&lt;/code&gt;: 스토리지 엔진에서 받은 데이터 가공 처리 역할 (쿼리의 최적화된 실행을 위한 옵티마이저가 중심)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;스토리지 엔진&lt;/code&gt;: 실제 데이터를 디스크 스토리지에 저장하거나 디스크 스토리지로부터 데이터를 읽어오는 부분 담당 (대표적으로 인덱스를 비교하는 담당)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;인덱스 푸시다운 예제&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 푸시다운은 쿼리의 실행 계획 extra 컬럼에서 &lt;code&gt;Using index condition&lt;/code&gt;로 표시되는데요. 어떤 상황에서 발생하는지 확인 해보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SET optimizer_switch='index_condition_pushdown=on';
ALTER TABLE people ADD INDEX idx_last_name_address (zipcode, lastname, address)&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SELECT * FROM people
  WHERE zipcode='95054'
  AND lastname LIKE '%sal'
  AND address LIKE '%Main Street%';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 인덱스 컨디션 푸시다운이 없을 때 위의 쿼리가 실행된다면 어떻게 실행 될까요?&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;code&gt;zipcode=95054&lt;/code&gt;에 해당하는 값들을 스토리지 엔진을 통해서 읽어옴 (&lt;code&gt;idx_last_name_address&lt;/code&gt; 인덱스를 통해서 데이터 파일에 접근하여 데이터를 읽어옴)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lastname LIKE '%sal'&lt;/code&gt;에 해당하는 조건을 MySQL 엔진에서 필터링 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;last_name이 &lt;code&gt;Marsja&lt;/code&gt;, &lt;code&gt;Masaki&lt;/code&gt; 같이 굳이 테이블을 읽지 않아도 되는 데이터도 데이터 파일에 접근하여 불필요한 Disk I/O 발생&lt;/li&gt;
&lt;li&gt;&lt;code&gt;만약에 10만건 읽고 필터링 되어서 1건 남았다면 99,999건의 불필요한 Disk I/O 발생&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/5e857cd2-01eb-4381-804d-418593faeb50&quot; alt=&quot;스크린샷 2024-04-10 오전 1 04 28&quot; width=&quot;975&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 2번 과정을 보면 비효율적이라는게 보이는데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비효율적인 이유가 무엇이냐면 이미 1번 과정(&lt;code&gt;zipcode=95054&lt;/code&gt;)에서 인덱스 파일을 읽어서 데이터 파일에 접근하여 데이터를 가져온 것을 볼 수 있습니다. (스토리지 엔진에서 인덱스 사용하여 디스크 파일에 접근하여 데이터를 MySQL 엔진으로 반환했음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 엔진에서는 굳이 3건 모두 디스크 파일에 접근하지 않더라도 스토리지 엔진에서 반환한 값을 보면 &lt;code&gt;lastname LIKE '%sal'&lt;/code&gt; 해당하는 결과가 1건이라는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 1건만 디스크에 접근해서 가져오면 되고 굳이 필터링 되어 버려진 값들에 대해서 불필요하게 Disk 접근을 할 필요가 없다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;하지만 MySQL 5.5 버전까지는 인덱스를 범위 제한 조건으로 사용하지 못하는 lastname 조건은 MySQL 엔진이 스토리지 엔진으로 아예 전달하지 못한다고 합니다.&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;MySQL 엔진 (핸들러 API 사용)-&amp;gt; 스토리지 엔진 -&amp;gt; 디스크 파일&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 MySQL 엔진에서 lastname 조건 처럼 인덱스를 사용하지 못하는 범위 검색의 경우 핸들러 API에서 스토리지 엔진으로 넘겨주지 않기 때문에 스토리지 엔진의 입장에서는 모두 디스크 파일에 접근하여 I/O를 발생시킬 수 밖에 없었던 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/1e2b2dc3-f763-4f0a-bd69-01eb9b50ea37&quot; alt=&quot;스크린샷 2024-04-10 오전 1 18 44&quot; width=&quot;1066&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 MySQL 5.6 버전부터는 인덱스를 사용하지 못하는 범위 검색이라 하더라도 모두 같이 스토리지 엔진으로 전달할 수 있게 핸들러 API가 개선되어, 위처럼 스토리지 엔진에서 MySQL 엔진으로 부터 받은 필터링 조건으로 불필요한 Disk I/O를 줄이는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핸들러 API: MySQL 엔진의 쿼리 실행기에서 데이터를 쓰거나 읽어야 할 때는 각 스토리지 엔진에 쓰기 또는 읽기를 요청 API&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;인덱스 컨디션 푸시 다운 on/off&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;SET optimizer_switch='index_condition_pushdown=off';
SET optimizer_switch='index_condition_pushdown=on';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어로 인덱스 푸시 다운 설정을 on/off 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;정리하며&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL 아키텍쳐에서 MySQL 엔진, 스토리지 엔진의 역할 등등 전체적인 개념에 대해서 학습해보면 좋을 거 같습니다.&lt;/li&gt;
&lt;li&gt;데이터 수가 많아짐에 따라 DB에서 랜덤 Disk I/O에 대한 관리는 아주 중요한 것 같습니다.&lt;/li&gt;
&lt;li&gt;요즘 대부분의 MySQL 버전에서는 &lt;code&gt;인덱스 컨디션 푸시다운&lt;/code&gt;이 디폴트로 활성화 되어 있겠지만 데이터 수가 수억건 같이 많은 곳에서 쿼리 실행을 해보면 쿼리를 했을 때 디스크 I/O를 줄이는 것이 쿼리 성능에 얼마나 도움되는지 경험할 수 있을 거 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Reference&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;Real MySQL - 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/index-condition-pushdown-optimization.html&quot;&gt;https://dev.mysql.com/doc/refman/8.0/en/index-condition-pushdown-optimization.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jojoldu.tistory.com/474&quot;&gt;https://jojoldu.tistory.com/474&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Database</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/447</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/447#entry447comment</comments>
      <pubDate>Wed, 10 Apr 2024 01:42:24 +0900</pubDate>
    </item>
    <item>
      <title>[Cassandra] Cassandra Consistency Level 이란?</title>
      <link>https://devlog-wjdrbs96.tistory.com/444</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Cassandra Consistency Level 알아보자&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 카산드라 Write, Read 각각 &lt;code&gt;Consistency Level&lt;/code&gt;에 대해서 어떤 특징이 있는지 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Casssandra CAP 특징&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/58ac2c32-09cd-4c4b-ad69-cba5b0b966d7&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라는 &lt;a href=&quot;https://en.wikipedia.org/wiki/CAP_theorem&quot;&gt;CAP&lt;/a&gt; 3가지 특징 중에 &lt;code&gt;AP&lt;/code&gt; 시스템으로 &lt;code&gt;높은 가용성&lt;/code&gt;과 &lt;code&gt;파티션 허용 오차&lt;/code&gt;를 제공합니다. 상황에 따라 &lt;code&gt;CP&lt;/code&gt;시스템으로 동작하도록 설정할 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 카산드라에서는 &lt;code&gt;Wrtie&lt;/code&gt;, &lt;code&gt;Read&lt;/code&gt;의 &lt;code&gt;Consitency Level&lt;/code&gt;을 어떤 값으로 설정하냐에 따라서 &lt;code&gt;AP&lt;/code&gt; 시스템이 될 수도 있고, &lt;code&gt;CP&lt;/code&gt; 시스템이 될 수도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Consistency Level 이란?&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일관성 수준은 &lt;code&gt;코디네이터(coordinator) 노드&lt;/code&gt;가 &lt;code&gt;non-lightweight transaction&lt;/code&gt;을 성공적으로 처리하기 위해 복제본에서 응답해야 하는 노드 수를 결정하는 옵션입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 일관성 레벨은 클라이언트에게 데이터를 응답하기 전에 읽기 요청에 응답해야 하는 복제본 수를 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 일관성 레벨은 쓰기 요청이 카산드라에 성공적으로 반영되어 클라이언트에게 응답되기 전에 얼마나 많은 복제본이 쓰기 요청에 응답해야 하는지를 지정합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Write Consistency Level&lt;/code&gt;&lt;/h3&gt;
&lt;table style=&quot;height: 466px;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;th style=&quot;height: 19px;&quot;&gt;Level&lt;/th&gt;
&lt;th style=&quot;height: 19px;&quot;&gt;Description&lt;/th&gt;
&lt;th style=&quot;height: 19px;&quot;&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;ALL&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;클러스터의 모든 복제 노드들의 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;가장 높은 일관성을 제공하지만, 가장 낮은 가용성을 제공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 57px;&quot;&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;EACH_QUORUM&lt;/td&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;각 datacenter에서 QUORUM 만큼의 복제 노드들의 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;특정 dc가 다운되면 QUORUM수를 만족하지 못해 쓰기 동작이 실패할 것이므로 각 datacenter를 같은 수준의 일관성으로 유지하고 싶을 때 사용할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 76px;&quot;&gt;
&lt;td style=&quot;height: 76px;&quot;&gt;QUORUM&lt;/td&gt;
&lt;td style=&quot;height: 76px;&quot;&gt;모든 datacenter에서 QUORUM 만큼의 복제 노드들의 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 76px;&quot;&gt;EACH_QUORUM의 경우 각 dc를 기준으로 하므로 dc1에서 1개, dc2에서 1개의 노드가 다운되었을 경우에만 정상 동작할 것이고 QUORUM은 dc1에서는 0개, dc2에서 2개의 노드가 다운되어도 정상 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 57px;&quot;&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;LOCAL_QUORUM&lt;/td&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;coordinator node가 존재하는 같은 datacenter에서 QUORUM 만큼의 복제 노드들의 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;같은 dc 안에서 동작하기 때문에 dc간의 통신 지연을 방지할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;ONE&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;적어도 1개의 replica 노드에 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;같은 dc 안에서 동작하기 때문에 dc간의 통신 지연을 방지할 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;TWO&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;적어도 2개의 replica 노드에 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 38px;&quot;&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;THREE&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;적어도 3개의 replica 노드에 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 38px;&quot;&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 57px;&quot;&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;LOCAL_ONE&lt;/td&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;local datacenter에서 적어도 하나의 replica 노드에 commit log와 memtable에 쓰기 동작이 완료되어야 하는 level&lt;/td&gt;
&lt;td style=&quot;height: 57px;&quot;&gt;multiple datacenter에서 보통 ONE level이 적절하지만, 운영 중이지 않은 데이터 센터의 노드가 운영 중인 데이터 센터의 노드에 자동으로 연결되지 않도록 함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 48px;&quot;&gt;
&lt;td style=&quot;height: 48px;&quot;&gt;ANY&lt;/td&gt;
&lt;td style=&quot;height: 48px;&quot;&gt;모든 replica 노드들이 다운되어도 쓰기 동작이 가능한 level&lt;/td&gt;
&lt;td style=&quot;height: 48px;&quot;&gt;쓰기가 성공한 데이터는 복제본 노드들이 정상 동작할 때까지 읽을 수 없고, 가장 높은 가용성을 보장하지만 일관성은 가장 낮다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Read Consistency Level&lt;/code&gt;&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ALL&lt;/td&gt;
&lt;td&gt;모든 복제본이 응답을 확인한 후 클라이언트에게 응답한다. 즉, 복제본이 응답하지 않으면 읽기 작업이 실패한다.&lt;/td&gt;
&lt;td&gt;가장 높은 일관성을 제공하지만, 가장 낮은 가용성을 제공한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EACH_QUORUM&lt;/td&gt;
&lt;td&gt;읽기에서 지원되지 않음&lt;/td&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QUORUM&lt;/td&gt;
&lt;td&gt;모든 데이터 센터의 복제본 쿼럼이 응답한 후 레코드를 반환한다.&lt;/td&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LOCAL_QUORUM&lt;/td&gt;
&lt;td&gt;coordinator node가 존재하는 같은 datacenter에서 QUORUM 만큼의 복제 노드들의 응답을 확인한 후 클라이언트에게 응답한다.&lt;/td&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ONE&lt;/td&gt;
&lt;td&gt;가장 가까운 1개의 replica 노드가 응답하는 경우 값을 반환하는 level&lt;/td&gt;
&lt;td&gt;오래된 데이터를 읽어도 큰 문제가 없는 경우 가장 높은 가용성을 제공하는 level이다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TWO&lt;/td&gt;
&lt;td&gt;가장 가까운 2개의 replica 노드가 응답하는 경우 값을 반환하는 level&lt;/td&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;THREE&lt;/td&gt;
&lt;td&gt;가장 가까운 3개의 replica 노드가 응답하는 경우 값을 반환하는 level&lt;/td&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LOCAL_ONE&lt;/td&gt;
&lt;td&gt;local datacenter에서 가장 가까운 하나의 replica 노드가 응답하는 경우 값을 반환하는 level&lt;/td&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SERIAL&lt;/td&gt;
&lt;td&gt;모든 datacenter에서 QUORUM 만큼의 replica 노드에서 커밋 되지 않은 상태의 데이터를 읽을 수 있는 level&lt;/td&gt;
&lt;td&gt;경량 트랜잭션을 실행하여 컬럼에 쓴 후에 컬럼의 최신 값을 읽으려면 SERIAL 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LOCAL_SERIAL&lt;/td&gt;
&lt;td&gt;SERIAL과 동일하나 local dc로 제한한다.&lt;/td&gt;
&lt;td&gt;---&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;How QUORUM is calculated&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;quorum = (sum_of_replication_factors / 2) + 1&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) replication factors = 3인 경우 QUORUM = 2가 되므로 1개의 노드가 다운되어도 정상 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;sum_of_replication_factors = datacenter1_RF + datacenter2_RF + . . . + datacentern_RF&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 데이터 센터가 2개 이상일 때 replication factors = 3인 경우 QUORUM = 4가 되므로 2개 노드가 다운되어도 정상 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 그림을 통해서 &lt;code&gt;Write Consistency Level&lt;/code&gt;에 대해서 조금만 더 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Pictures Write Consistency Level&lt;/code&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;CL = ALL&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/9c2f4c8c-0fdf-4d26-9dcc-5d9d28f14354&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일관성 레벨 &lt;code&gt;ALL&lt;/code&gt;은 모든 복제본에서 응답해야 클라이언트에서 응답을 할 수 있기 때문에 카산드라에서 가장 높은 일관성을 제공하지만, 하나의 복제본이라도 실패한다면 클라이언트 응답에 실패하기 때문에 가용성은 낮다는 단점이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라의 큰 장점은 가용성이 높다는 것인데 일관성 레벨 &lt;code&gt;ALL&lt;/code&gt;을 사용하면 가용성과 성능을 낮추기 때문에 카산드라를 제대로 사용한다 할 수 없을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가용성이 아닌 높은 일관성을 유지하는 것이 필요하다면 카산드라 보다는 RDB를 사용하는 것이 더 좋은 선택일 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;CL = EACH_QUORUM&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/5b3d91d3-38df-4e09-8d45-51887e2f054d&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 복제본 노드에서 응답해야 하는 것은 아니기 때문에 &lt;code&gt;ALL&lt;/code&gt; 보다는 가용성이 좀 더 높다고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;EACH_QUORUM&lt;/code&gt;는 데이터 센터마다의 동일한 일관성 유지를 목표로 하는데, 이 뜻은 &lt;code&gt;QUORUM = 2&lt;/code&gt; 이기 때문에 각 데이터 센터에서 최소 2개 노드에서 응답해야 성공할 수 있다는 것을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;CL = QUORUM&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/f865c5ac-e81d-4fab-830d-460ea6d362b8&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;QUORUM&lt;/code&gt;도 &lt;code&gt;EACH_QUORUM&lt;/code&gt;가 동일하지만, 하나 다른 점이 존재합니다. &lt;code&gt;EACH_QUORUM&lt;/code&gt;의 경우 &lt;code&gt;QUORUM = 2&lt;/code&gt; 일 때 데이터 센터 각각에서 최소 2개의 노드씩 응답을 해야 성공할 수 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;code&gt;QUORUM&lt;/code&gt; 레벨은 &lt;code&gt;QUORUM = 2&lt;/code&gt; 일 때 모든 데이터 센터 노드에서 최소 2개 이상만 응답하면 된다는 특징이 있습니다. (&lt;code&gt;QUORUM 일 때는 datacenter 2에서 replication factor = 1&lt;/code&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;CL = LOCAL_QUORUM&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/65c4387d-66aa-4576-880c-e9f8eeec1348&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;LOCAL_QUORUM&lt;/code&gt; 레벨은 같은 데이터 센터 복제본 노드에서만 응답하면 클라이언트에게 성공으로 응답할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 요청이 현재 데이터 센터의 경계를 벗어나지 않기 때문에 지연 시간이 줄어든다는 장점이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;CL = ONE, TWO, THREE&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 일관성 레벨은 숫자에 맞게만 복제본 노드에서 응답을 받으면 되는데, 복제본 수에 따라서 일관성이 높아질 수도 낮아질 수도 있다는 특징이 있습니다. 예를들어, &lt;code&gt;Replication Factor = 3&lt;/code&gt;, &lt;code&gt;CL = TWO&lt;/code&gt; 경우로 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/a9184926-019d-4b15-9b2f-646f07404173&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 복제 노드가 3개라면, 3개 중에 2개만 응답하면 되기 때문에 일관성이 높은 편이라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/a353447e-a84b-4e8e-aeb0-1576871a8f7e&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위처럼 복제본 수가 늘어난다면 5개 복제 노드 중에 2개만 응답 받으면 되기 때문에 일관성이 조금 떨어질 수 있다는 특징이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;CL = LOCAL_ONE&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/79343d33-e2af-40cc-8700-539fafb06f71&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LOCAL_QUORM 처럼 일관성 검사는 로컬 데이터 센터에서만 적용됩니다. 하나의 복제본에 대해서만 일관성 체크를 하면 되기 때문에 가용성 높이고, 일관성을 낮추게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SNS 리액션 수 같이 당장 일관성을 유지하는 것이 중요하지 않은 데이터에 낮은 일관성 수준을 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;CL = ANY&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/dd158c79-33d9-4fd1-a994-2bf58ddb8968&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ANY 레벨은 복제본의 응답을 필요로 하지 않습니다. 또한 카산드라에 데이터를 저장하지 않고도 성공으로 응답할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복제 노드가 다운되어 데이터를 처리하지 못한 경우 코디네이터 노드는 힌트(hint)를 저장합니다. 힌트(hint)가 만료 시간은 3시간이며, 이는 해당 시간 내에 복제 노드가 다시 정상 상태로 돌아오지 않으면 데이터가 손실됨을 의미합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일관성 레벨 ANY는 카산드라에서 가장 높은 가용성과 가장 낮은 일관성을 제공합니다. 데이터 저장에 대한 보장이 없으므로 데이터 손실을 초래할 수 있으므로 운영 환경에서는 권장되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Referenece&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.x/cassandra/dml/dmlAboutDataConsistency.html&quot;&gt;https://docs.datastax.com/en/cassandra-oss/3.x/cassandra/dml/dmlAboutDataConsistency.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html&quot;&gt;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/dml/dmlConfigConsistency.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pythian.com/blog/cassandra-consistency-level-guide&quot;&gt;https://www.pythian.com/blog/cassandra-consistency-level-guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://pompitzz.github.io/blog/Cassandra/Cassandra_Consistency_Level.html#write-consistency-levels&quot;&gt;https://pompitzz.github.io/blog/Cassandra/Cassandra_Consistency_Level.html#write-consistency-levels&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Cassandra</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/444</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/444#entry444comment</comments>
      <pubDate>Mon, 12 Feb 2024 01:49:33 +0900</pubDate>
    </item>
    <item>
      <title>[Cassandra] Cassandra Partition에 대해 알아보자</title>
      <link>https://devlog-wjdrbs96.tistory.com/443</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Cassandra Partition에 대해 알아보자&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성과 확장성을 위해 설계된 분산형 NoSQL 데이터베이스 시스템인 Cassandra에서 파티셔닝은 중요한 개념입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cassandra는 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;데이터를 더 작은 파티션으로 나누어 클러스터에 분산하는 과정인 파티셔닝을 통해서&lt;/span&gt;&amp;nbsp;여러 노드에 걸쳐 데이터를 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라를 사용할 때 성능을 최적화하고 데이터 분산을 균일하게 하기 위해서는 파티셔닝을 제대로 이해하고 구성하는 것은 아주 중요하고 필수적입니다. (안그러면 카산드라 사용하는 이유 및 효율성이 매우 떨어지는..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적인 내용을 들어가기 전에 가볍게 Cassandra &lt;code&gt;Partition Key&lt;/code&gt;, &lt;code&gt;Clustering Key&lt;/code&gt;, &lt;code&gt;Primary Key&lt;/code&gt;에 대해서 살짝만 알아보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/97618dea-c6d9-4ac5-a92e-7a2a5bf26d38&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Primary Key = Partition Key + [Clustering Columns]&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림을 보면 Cassandra Partition Key, Clustering Key, Primary Key에 대해서 잘 나타내고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Sensor&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;를 Partition Key로 사용하여 노드별로 데이터를 분산시키고, 노드 안에서 파티션 키가 같다면 &lt;code&gt;timestamp&lt;/code&gt;를 사용해서 데이터를 정렬하고 있는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cassandra는 DDL 쿼리에서 Partition Key, Clustering Key, Primary Key를 명시할 수 있는데 이 부분은 해당 글에서 설명하지 않겠습니다. 좀 더 알고 싶다면 &lt;a href=&quot;https://www.baeldung.com/cassandra-keys&quot;&gt;여기&lt;/a&gt; 글을 참고하면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림을 보면 Partition Key 별로 서로 다른 노드에 저장되고 있는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라 읽기 쓰기 작업은 Partition 단위로 진행되는데, &lt;code&gt;토큰(tokens) (a long value out of range -2^63 to +2^63 -1)&lt;/code&gt;을 통해서 데이터를 분산 하여 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Casssandra Token 이란?&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/25595181-2c0c-44c6-817e-199b9893cc25&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라 토큰의 범위는 &lt;code&gt;-2^63 to +2^63 -1&lt;/code&gt; 입니다. 즉, 노드(서버)별로 토큰의 할당 범위를 가지게 됩니다. (노드가 추가되거나 제거되면 토큰 재할당(데이터 재분배) 과정이 필요함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션은 주어진 파티션 키를 토큰으로 변환하기 위해 파티셔너를 사용하고, 카산드라는 파티션 키를 사용하여 노드에 데이터를 분산하여 저장하고 데이터를 읽어올 때 어떤 노드에서 데이터를 찾을지 결정합니다. (즉, 파티션 키가 같다면 같은 노드에 저장됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 Cassandra Partitioner는 &lt;code&gt;Murmur3Partitioner(default)&lt;/code&gt;, &lt;code&gt;RandomPartitioner&lt;/code&gt;, &lt;code&gt;ByteOrderedPartitioner&lt;/code&gt; 3가지를 제공하는데 자세한 것은 &lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/architecture/archPartitionerAbout.html&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 말만 들으면 무슨 말인지 이해가 완벽하게 안될 수 있는데요. 좀 더 구체적인 예시를 들어보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;age&lt;/th&gt;
&lt;th&gt;car&lt;/th&gt;
&lt;th&gt;gender&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jim&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;camaro&lt;/td&gt;
&lt;td&gt;M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;carol&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;bmw&lt;/td&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;johnny&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;suzy&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 partitionKey가 name인 카산드라 테이블에 데이터가 있다고 가정하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;partition key&lt;/th&gt;
&lt;th&gt;murmur3 hash value (token)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;jim&lt;/td&gt;
&lt;td&gt;-2245462676723223822&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;carol&lt;/td&gt;
&lt;td&gt;7723358927203680754&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;johnny&lt;/td&gt;
&lt;td&gt;-6723372854036780875&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;suzy&lt;/td&gt;
&lt;td&gt;1168604627387940318&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 키 &lt;code&gt;name&lt;/code&gt;을&amp;nbsp; &lt;code&gt;murmur3&lt;/code&gt; 파티셔너를 사용하여 해싱하면 위의 값처럼 토큰이 만들어지게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/1043b459-3006-450d-852e-47fa2e79d72d&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 위처럼 노드별로 토큰 할당 범위를 가지고 있어서 데이터가 분산되어 저장되는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;node&lt;/th&gt;
&lt;th&gt;start range&lt;/th&gt;
&lt;th&gt;end range&lt;/th&gt;
&lt;th&gt;partition key&lt;/th&gt;
&lt;th&gt;hash value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;-9223372036854775808&lt;/td&gt;
&lt;td&gt;-4611686018427387904&lt;/td&gt;
&lt;td&gt;johnny&lt;/td&gt;
&lt;td&gt;-6723372854036780875&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;-4611686018427387903&lt;/td&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;jim&lt;/td&gt;
&lt;td&gt;-2245462676723223822&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;4611686018427387903&lt;/td&gt;
&lt;td&gt;suzy&lt;/td&gt;
&lt;td&gt;1168604627387940318&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;4611686018427387904&lt;/td&gt;
&lt;td&gt;9223372036854775807&lt;/td&gt;
&lt;td&gt;carol&lt;/td&gt;
&lt;td&gt;7723358927203680754&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시를 보면 카산드라 토큰과 파티셔닝을 통하여 데이터가 분산되어 저장되는 방식에 대해서 잘 이해할 수 있을 것이라 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/6920ce7a-fbec-40ae-a0ef-38d0e154ad60&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라 토큰과 데이터 분산되는 방식에 대해 한눈에 보기 좋은 그림이 있어서 이것도 같이 참고하면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;카산드라 Partition 설계할 때 알아야 할 점&lt;/code&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;파티션 사이즈&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라 실질적인 Partition Size는 20억 셀이라고 하는데 이렇게 큰 것은 비효율적이라 적절한 파티션 수를 가지는 것이 필요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 중요한 것은 카산드라의 최대 파티션 크기는 100MB 이하여야 하는데, 이상적으로는 10MB 이하여야 합니다. 10MB가 넘어가면 데이터를 읽어올 때 성능 저하가 생길 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;파티션 데이터 불균형&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 키를 잘못 설계하면 특정 파티션으로 데이터가 많이 쏠려서 파티션마다의 데이터가 불균형하게 분배되어 저장될 수 있습니다. 예시를 통해서 좀 더 자세히 알아보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;CREATE TABLE server_logs(
   log_hour timestamp,
   log_level text,
   message text,
   server text,
   PRIMARY KEY (server)
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 &lt;code&gt;server&lt;/code&gt; 컬럼 하나만 파티션 키를 지정했다면 어떻게 될까요? 서버의 로그는 시간이 지나면 계속 쌓이게 되는데 이러면 파티션 사이즈도 무한히 증가할 수 있다는 것을 뜻합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;CREATE TABLE server_logs(
   log_hour timestamp,
   log_level text,
   message text,
   server text,
   PRIMARY KEY ((log_hour, server))
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;code&gt;log_hour&lt;/code&gt;, &lt;code&gt;server&lt;/code&gt;를 하나의 파티션 키로 지정 했다면 어떻게 될까요? 파티션은 시간 단위로 나뉘어 지게 될테고 파티션 사이즈의 무한한 증가를 막을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;partition skew&lt;/code&gt;는 파티션에 할당된 데이터가 다른 파티션에 비해 더 많고 시간이 지남에 따라 파티션이 무한히 증가하는 조건을 의미하는데 이를 조심해서 설계해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;CREATE TABLE server_logs(
   log_hour timestamp,
   log_level text,
   message text,
   server text,
   slot int
   PRIMARY KEY ((server, slot))
)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면 특정 컬럼을 추가하여, 특정 개수가 되면 파티션을 분배할 수 있도록 &lt;code&gt;slot&lt;/code&gt; 이라는 개념을 추가하여 파티션을 분배할 수도 있고.. 등등 다양한 방법이 있을 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Replication Factor&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스나 Kafka 처럼 데이터를 처리하고 다루는 곳에서 복제본은 내결함성(&lt;code&gt;fault tolerance&lt;/code&gt;) 및 고가용성(&lt;code&gt;high availability&lt;/code&gt;)을 제공하기 때문에, &lt;code&gt;Replcation Factor&lt;/code&gt; 개념은 매우 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라에서도 데이터에 대한 Replication Factor를 설정할 수 있으며, 이는 클러스터 전체에 각 파티션의 복제본 수를 몇개 가지고 있을지 결정하는 옵션입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://github.com/wjdrbs96/Today-I-Learn/assets/45676906/5508d68d-1b80-4ba5-8fbd-bd2235c79997&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 &lt;code&gt;Replication Factor = 3&lt;/code&gt; 이라면 위처럼 3개의 노드에 데이터를 복제해서 저장하게 됩니다. 만약에 해당 파티션을 담당하는 메인 노드가 다운되게 되면 복제되어 있는 노드에서 해당 파티션의 데이터를 응답하게 됩니다. 그리고 &lt;code&gt;카산드라는 백그라운드에서 데이터를 비동기적으로 복제합니다.&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라에서 복제 전략에는 아래와 같이 2가지가 존재합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;SimpleStrategy&lt;/code&gt;: 하나의 데이터 센터와 하나의 rack 을 사용하고 있을 때 사용. 두 개 이상의 데이터 센터를 사용한다면 &lt;code&gt;NetworkTopologyStrategy&lt;/code&gt; 전략을 사용해야 함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NetworkTopologyStrategy&lt;/code&gt;: 향후 카산드라 확장이 필요할 때 여러 데이터 센터로 확장하기가 훨씬 쉽기 때문에 대부분의 구축에 권장되는 옵션&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;데이터 복제&lt;/code&gt;에 관한 자세한 것은 &lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/architecture/archDataDistributeReplication.html&quot;&gt;여기&lt;/a&gt;를 참고하면 좋을 것 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;카산드라 클러스터에 미치는 파티션의 영향&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 파티션의 크기는 10MB 이하를 유지하는 것이 이상적이라고 하였는데요. 이는 직관적으로? 단순하게? 생각해보아도, 하나의 파티션 점점 커진다면 해당 노드에서 데이터를 가져오는데 빠르게 효율적으로 가져오기 힘들어질 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 각 파티션에 저장된 데이터의 크기를 제어하는 것은 클러스터 전체에 데이터를 균등하게 분산하고 I/O 성능을 높이기 위해 필수적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;읽기 성능&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Cassandra는 Cache, Index, Index summaries 등등 디스크의 SStables 파일 내에서 파티션을 찾아서 데이터를 가져오는 내부적인 동작 과정이 있는데, 큰 파티션은 이러한 데이터 구조를 유지하는데 비효율적이고 성능 저하를 초래합니다. 이는 다른 글에서 다시 정리해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Memory Usage&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 크기는 JVM Heap 크기 및 카산드라 Garbage Collection 메커니즘에 직접적인 영향을 미칩니다. 파티션 사이즈가 크다면 Garbage Collection이 비효율적입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라의 내부 동작 및 메모리 사용에 대해 좀 더 자세하 알게 되면 더 자세히 이해가 될 것 같습니다. (&lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/operations/opsTuneJVM.html&quot;&gt;Link&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Casssandra Repair&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라 &lt;code&gt;repair&lt;/code&gt;는 데이터를 일관성 있게 만들 수 있는 것입니다. (자세한 것은 &lt;a href=&quot;https://cassandra.apache.org/doc/stable/cassandra/operating/repair.html&quot;&gt;여기&lt;/a&gt;를 참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;repair&lt;/code&gt;는 노드간에 데이터 일관성이 깨졌을 때, 데이터를 스캔하여 다른 노드의 데이터 복제본과 비교한 후 데이텉 싱크를 맞추는 작업이라고 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 파티션 크기가 크다면 &lt;code&gt;repair&lt;/code&gt;를 통해서 데이터를 복구 및 싱크를 맞추는 작업이 부담이 될 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Tombstone Eviction&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라는 데이터 삭제 할 때 바로 삭제하지 않고 &lt;code&gt;tombstone&lt;/code&gt; 표시를 남겨놓고, 나중에 삭제를 진행합니다. 이는 &lt;code&gt;Compaction&lt;/code&gt; 전략이 적절하게 동작하지 않는다면 큰 파티션이 &lt;code&gt;tombstone&lt;/code&gt; 제거할 때 어려움이 있을 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Tombstone&lt;/code&gt;이 무엇인지는 &lt;a href=&quot;https://www.instaclustr.com/support/documentation/cassandra/using-cassandra/managing-tombstones-in-cassandra/&quot;&gt;여기&lt;/a&gt;와 &lt;a href=&quot;https://docs.datastax.com/en/dse/5.1/docs/architecture/database-internals/architecture-tombstones.html&quot;&gt;여기&lt;/a&gt; 글을 참고하면 좋을 것 같고 다른 글에서 &lt;code&gt;Compaction&lt;/code&gt; 등등 개념들을 다루어 보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;마무리 하며&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라는 알면 알수록 재밌으면서 어렵고.. 많은 것을 알게되는 것 같습니다. 카산드라 Partition에 대해 가볍게 정리하려 했는데, 파티션과 연결되어 있는 여러가지 개념 및 용어들이 많이 나온거 같습니다. 앞으로 카산드라 용어 및 내부 동작들에 대해서 앞으로 하나씩 계속 정리해보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Referenece&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/architecture/archDataDistributeReplication.html&quot;&gt;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/architecture/archDataDistributeReplication.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.x/cassandra/architecture/archDataDistributeHashing.html&quot;&gt;https://docs.datastax.com/en/cassandra-oss/3.x/cassandra/architecture/archDataDistributeHashing.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/cassandra-replication-partitioning&quot;&gt;https://www.baeldung.com/cassandra-replication-partitioning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@sadigrzazada20/partitioning-in-apache-cassandra-9eda05439a52&quot;&gt;https://medium.com/@sadigrzazada20/partitioning-in-apache-cassandra-9eda05439a52&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sunitc.dev/2021/07/25/partitions-in-cassandra/&quot;&gt;https://sunitc.dev/2021/07/25/partitions-in-cassandra/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.instaclustr.com/blog/cassandra-data-partitioning/&quot;&gt;https://www.instaclustr.com/blog/cassandra-data-partitioning/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.baeldung.com/cassandra-keys&quot;&gt;https://www.baeldung.com/cassandra-keys&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datastax.com/en/dse/5.1/docs/architecture/database-internals/architecture-tombstones.html&quot;&gt;https://docs.datastax.com/en/dse/5.1/docs/architecture/database-internals/architecture-tombstones.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.instaclustr.com/support/documentation/cassandra/using-cassandra/managing-tombstones-in-cassandra/&quot;&gt;https://www.instaclustr.com/support/documentation/cassandra/using-cassandra/managing-tombstones-in-cassandra/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cassandra.apache.org/doc/stable/cassandra/operating/repair.html&quot;&gt;https://cassandra.apache.org/doc/stable/cassandra/operating/repair.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/operations/opsTuneJVM.html&quot;&gt;https://docs.datastax.com/en/cassandra-oss/3.0/cassandra/operations/opsTuneJVM.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Cassandra</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/443</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/443#entry443comment</comments>
      <pubDate>Mon, 5 Feb 2024 01:23:05 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] Kafka를 처음 공부할 때 보면 좋은 내용들</title>
      <link>https://devlog-wjdrbs96.tistory.com/442</link>
      <description>&lt;h1&gt;&lt;code&gt;Kafka 처음 사용할 때 알면 좋은 것들&lt;/code&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 제가 Kafka를 사용하면서 겪은 경험보다는 &lt;code&gt;Kafka를 공부하면서 처음 사용할 때 알면 좋은 것들의 이론&lt;/code&gt;에 대해 정리한 글입니다. 참고한 곳은 맨 아래에 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;Kafka 기본&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;Cluster, Broker란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Lag 란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Topic, Partition 이란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;레코드란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;컨트롤러란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;코디네이터란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;데이터 삭제&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Replication Factor란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;리더 팔로워란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;ISR 이란?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Producer&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;Producer 주요 옵션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;ack=all과 브로커의 min.insync.replicas 옵션의 관계&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Consumer&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;Consumer 대표 옵션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;컨슈머 그룹이란?&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;컨슈머 리밸런스 특징&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;컨슈머 하트비트란?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;컨슈머 그룹 특징&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;토픽의 파티션에는 하나의 컨슈머만 연결 가능하다&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a&gt;커밋과 오프셋이란?&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;자동 커밋&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;수동 커밋&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;동기 오프셋 커밋&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;비동기 오프셋 커밋&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Zookeeper란?&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;Controller election&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Configuration Of Topics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Access control lists&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;Membership of the cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;컨슈머 오프셋&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a&gt;필수 카프카 명령어&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a&gt;토픽 생성하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;토픽 제거하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;토픽 리스트 확인&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;토픽 상세보기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;토픽의 파티션 수 변경&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;컨슈머 그룹 리스트 확인&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a&gt;컨슈머 상태와 오프셋 확인&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;카프카 기본&lt;/code&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Cluster, Broker란?&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211151988-bbd39323-3010-4e20-9417-9021045d50b4.png&quot; alt=&quot;스크린샷 2023-01-07 오후 10 02 47&quot; width=&quot;487&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka Broker가 모여서 Cluster를 이룬다고 할 수 있다.(쉽게 말하면 Broker는 하나의 서버라고 할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Lag란?&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211139263-ccb27719-6b14-42d0-9fea-b41283461794.png&quot; alt=&quot;스크린샷 2023-01-07 오후 4 25 41&quot; width=&quot;1023&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Producer가 데이터를 넣는 속도가 Consumer가 데이터를 소비하는 속도보다 빠르다면 컨슈머가 마지막으로 읽은 offset과 Producer가 마지막으로 넣은 offset의 차이가 발생한다. 이 차이를 &lt;b&gt;&lt;code&gt;Consumer Lag&lt;/code&gt;&lt;/b&gt; 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka에서 Lag 값을 통해 Producer, Consumer의 상태를 유추할 수 있다. 즉, Lag이 높다면 Consumer에 문제가 있다는 뜻일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Topic, Partition 이란?&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;532&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCVmZq/btrVEpC5gYS/cQIhYRhoEL8HkeL4k1IFG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCVmZq/btrVEpC5gYS/cQIhYRhoEL8HkeL4k1IFG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCVmZq/btrVEpC5gYS/cQIhYRhoEL8HkeL4k1IFG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCVmZq%2FbtrVEpC5gYS%2FcQIhYRhoEL8HkeL4k1IFG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;410&quot; height=&quot;532&quot; data-origin-width=&quot;828&quot; data-origin-height=&quot;532&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka는 여러 개의 Topic을 가질 수 있다.&lt;/li&gt;
&lt;li&gt;Topic 안에 여러 개의 파티션을 가질 수 있다.(즉, 파티션이란 토픽을 분할한 것이라 할 수 있다.)&lt;/li&gt;
&lt;li&gt;Partition은 처음 생성한 이후로는 추가할 수만 있고, 줄일 수는 없다는 특징이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;레코드란(Record)?&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파티션 안에 프로듀서가 보낸 데이터가 존재하는데 이것을 &lt;b&gt;&lt;code&gt;레코드(Record)&lt;/code&gt;&lt;/b&gt; 라고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;레코드는 &lt;b&gt;&lt;code&gt;타임스탬프&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;메세지 키&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;메세지 값&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;오프셋&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;헤더&lt;/code&gt;&lt;/b&gt;로 구성되어 있고, 프로듀서가 생성한 레코드가 브로커로 전송되면 오프셋과 타임스탬프가 지정되어 저장된다.&lt;/li&gt;
&lt;li&gt;브로커에 한번 적재된 레코드는 수정할 수 없고 &lt;b&gt;&lt;code&gt;로그 리텐션 기간 또는 용량&lt;/code&gt;&lt;/b&gt;에 따라서만 삭제된다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;b&gt;메세지 키는 메세지 값을 순서대로 처리하거나 메세지 값의 종류를 나타내기 위해 사용한다.&lt;/b&gt;&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;&lt;b&gt;메세지 키를 사용하면 프로듀서가 토픽에 레코드를 전송할 때 메세키 키의 해시값을 토대로 파티션을 지정한다.&lt;/b&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;즉, 메세지 키를 지정하면 동일한 파티션에 들어가게 된다. 다만, 어떤 파티션에 지정될지는 모르고 파티션의 개수가 변경되면 메세지 키와 파티션 매칭이 달라지게 되므로 주의해야 한다.&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211156184-a4cd08d5-e7e2-42b4-a551-ca19c45c6786.png&quot; alt=&quot;스크린샷 2023-01-07 오후 11 38 58&quot; width=&quot;497&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;메세지 키와 메세지 값은 직렬화되어 브로커로 전송되기 때문에 컨슈머가 이용할 때는 직렬화한 형태와 동일한 형태로 역직렬화를 해야 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211156393-140ad193-5879-4d75-8b3e-eb8fc23b3ce4.png&quot; alt=&quot;스크린샷 2023-01-07 오후 11 43 58&quot; width=&quot;670&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211156366-487c7263-8880-4ea3-9ee5-2a2b62961434.png&quot; alt=&quot;스크린샷 2023-01-07 오후 11 43 22&quot; width=&quot;845&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;컨트롤러(Contoller)&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka 클러스터의 다수 브로커 중 한 대가 컨트롤러 역할을 한다. 컨트롤러는 다른 브로커들의 상태를 체크하고 브로커가 클러스터에서 빠지는 경우 해당 브로커에 존재하는 &lt;b&gt;&lt;code&gt;리더 파티션을 재분배&lt;/code&gt;&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;코디네이터(coodinator)&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터의 다수 브로커 중 한 대는 코디네이터 역할을 수행한다. 코데네이터는 컨슈머 그룹의 상태를 체크하고 파티션을 컨슈머와 매칭하도록 분배하는 역할을 한다. 이렇게 파티션을 컨슈머로 재할당하는 과정을 &lt;b&gt;&lt;code&gt;리밸런스&lt;/code&gt;&lt;/b&gt; 라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;데이터 삭제&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카는 다른 메세징 플롯팸과 다르게 컨슈머가 데이터를 가져가더라도 토픽의 데이터는 삭제되지 않는다. 컨슈머, 프로듀서가 데이터 삭제 요청을 할 수 없고 요청 브로커만이 데이터를 삭제할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 삭제는 파일 단위로 이루어지는데 기본 값은 1GB 이다. 1GB 용량에 도달하면 데이터를 삭제하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/installation/configuration/topic-configs.html#retention-bytes&quot;&gt;retention.bytes&lt;/a&gt;, &lt;a href=&quot;https://docs.confluent.io/platform/current/installation/configuration/topic-configs.html#retention-ms&quot;&gt;retention.ms&lt;/a&gt; 옵션을 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Replication Factor란?&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브로커에게 &lt;code&gt;리플리케이션 팩터 수 만큼 토픽안의 파티션들을 복제&lt;/code&gt;하도록 하는 설정 값이다.&lt;/li&gt;
&lt;li&gt;복제본이 생기기 때문에 리더와 팔로워 개념이 존재하고, 가장 중요한 것은 모든 읽기와 쓰기가 리더를 통해서만 일어난다는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;리더, 팔로워란?&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211140249-e31556b5-ebf6-443b-a455-e3d08ab82940.png&quot; alt=&quot;스크린샷 2023-01-07 오후 4 53 19&quot; width=&quot;816&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리더는 모든 데이터의 읽기 쓰기 작업을 처리히고, 팔로워는 리더를 주기적으로 보면서 자신에게 없는 데이터를 리더로부터 주기적으로 가져오는 방법으로 리플리케이션을 유지한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;ISR 이란?&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로듀서가 리더에게 메세지를 발행하면 팔로워가 해당 메세지를 복제한다. 그런데 팔로워에 문제가 생겨서 데이터 복제가 제대로 되지 않은 상황에서 리더가 문제가 생기면 &lt;b&gt;&lt;code&gt;데이터 정합성&lt;/code&gt;&lt;/b&gt;에 문제가 생긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka 에서는 이러한 현상을 막기 위해 &lt;b&gt;&lt;code&gt;ISR&lt;/code&gt;&lt;/b&gt; 이라는 개념이 존재한다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211140369-36f84961-662a-4c93-b636-80bd4109db57.png&quot; alt=&quot;스크린샷 2023-01-07 오후 4 57 14&quot; width=&quot;786&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Producer가 리더에게 메세지를 보내면 팔로워가 리더로부터 메세지를 복제하게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211140503-da38b674-9e78-4cb6-a936-3641256951ab.png&quot; alt=&quot;스크린샷 2023-01-07 오후 5 01 35&quot; width=&quot;849&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Broker 2 팔로워는 복제를 했지만, &lt;code&gt;Broker 3 팔로워에는 문제가 생겨 복제를 하지 못했다.&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211140935-8450b400-bdd9-402c-a119-7b7f2a8d587c.png&quot; alt=&quot;스크린샷 2023-01-07 오후 5 14 18&quot; width=&quot;805&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메세지를 받은 후에 리더 브로커는 Producer 에게 &lt;code&gt;ack&lt;/code&gt;를 응답으로 돌려준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211140553-6bf78afc-a3c3-42f2-a258-87cd281a7800.png&quot; alt=&quot;스크린샷 2023-01-07 오후 5 03 11&quot; width=&quot;810&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/installation/configuration/broker-configs.html#replica-lag-time-max-ms&quot;&gt;replica.lag.time.max.ms&lt;/a&gt; 값 만큼 확인 요청이 오지 않는다면, 문제가 있는 팔로워를 &lt;code&gt;ISR&lt;/code&gt; 그룹에서 제거한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Producer&lt;/code&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Producer 주요 옵션&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#producerconfigs_bootstrap.servers&quot;&gt;bootstrap.servers&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카프카 클러스터에 처음 연결을 하기 위한 호스트와 포트 정보로 구성된 리스트 정보를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#producerconfigs_acks&quot;&gt;acks&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로듀서가 카프카 토픽의 리더에게 메세지를 보낸 후 요청을 완료하기 전 ack(승인) 수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;ack=0&lt;/code&gt;&lt;/b&gt;: 프로듀서는 서버로부터 어떠한 ack도 기다리지 않는다. ack 요청을 기다리지 않기 때문에 매우 빠르게 메세지를 보낼 수 있지만 메세지가 손실될 가능성이 높다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;b&gt;ack=1&lt;/b&gt;&lt;/code&gt;: 리더는 데이터를 기록하지만, 모든 팔로워는 확인하지 않기 때문에 메세지 손실이 발생할 수도 있다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;b&gt;ack=all&lt;/b&gt;&lt;/code&gt;: 리더는 ISR의 팔로워로부터 데이터에 대한 ack를 기다리기 때문에, 팔로워가 있는 한 데이터 무손실에 대해 강력하게 보장할 수 있다. 완벽하게 사용하기 위해서는 &lt;b&gt;&lt;code&gt;Broker 설정&lt;/code&gt;&lt;/b&gt;도 같이 조정해야 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;b&gt;기본 값: all&lt;/b&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;ack=all과 브로커의 min.insync.replicas 옵션의 관계&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#brokerconfigs_min.insync.replicas&quot;&gt;min.insync.replicas&lt;/a&gt;: 성공적인 것으로 간주되는 메세지에 쓰기를 승인해야 하는 &lt;b&gt;&lt;code&gt;최소 복제본 수&lt;/code&gt;&lt;/b&gt;를 지정하는 옵션이다. 즉, &lt;b&gt;&lt;code&gt;최소 리플리케이션을 유지해야 하는 수&lt;/code&gt;&lt;/b&gt;라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;ack=all, min.insync.replicas=1&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211141530-4f964427-8d8d-4760-92af-73af12bddd36.png&quot; alt=&quot;스크린샷 2023-01-07 오후 5 32 48&quot; width=&quot;610&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;min.insync.replicas&lt;/code&gt;&lt;/b&gt; 옵션이 &lt;b&gt;&lt;code&gt;1&lt;/code&gt;&lt;/b&gt; 이기 때문에, 프로듀서가 리더에게 메세지를 보냈을 때 리더는 최소 1개의 브로커에게만 메세지를 잘 받았는지 확인하면 된다. 즉, 리더 자신 하나가 잘 받았기 때문에 바로 ack를 응답한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;ack=all, min.insync.replicas=2&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211141989-3d3b4e54-491d-4259-a167-67fab09ab38e.png&quot; alt=&quot;스크린샷 2023-01-07 오후 5 43 03&quot; width=&quot;809&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로듀서는 메세지를 리더에게 보낸다.&lt;/li&gt;
&lt;li&gt;리더는 메세지를 받은 후에 저장하고, 팔로워는 해당 메세지를 가져와 저장한다.&lt;/li&gt;
&lt;li&gt;리더는 팔로워에게 메세지가 잘 복제되었는지 확인한다. &lt;b&gt;&lt;code&gt;min.insync.replicas&lt;/code&gt;&lt;/b&gt; 옵션이 &lt;b&gt;&lt;code&gt;2&lt;/code&gt;&lt;/b&gt;이기 때문에 &lt;b&gt;&lt;code&gt;리더 1개&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;팔로워 1개&lt;/code&gt;&lt;/b&gt;만 확인한다.&lt;/li&gt;
&lt;li&gt;리더는 acks 응답을 프로듀서에게 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;ack=all, min.insync.replicas=3&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211142238-ef4c58a3-d488-41f5-9ebb-4663d9643feb.png&quot; alt=&quot;스크린샷 2023-01-07 오후 5 49 21&quot; width=&quot;837&quot; /&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프로듀서는 메세지를 리더에게 보낸다.&lt;/li&gt;
&lt;li&gt;리더는 메세지를 받은 후에 저장하고, 팔로워는 해당 메세지를 가져와 저장한다.&lt;/li&gt;
&lt;li&gt;리더는 팔로워에게 메세지가 잘 복제되었는지 확인한다. &lt;b&gt;&lt;code&gt;min.insync.replicas&lt;/code&gt;&lt;/b&gt; 옵션이 &lt;b&gt;&lt;code&gt;3&lt;/code&gt;&lt;/b&gt;이기 때문에 &lt;b&gt;&lt;code&gt;리더 1개&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;팔로워 2개&lt;/code&gt;&lt;/b&gt;에 대해서 확인한다.&lt;/li&gt;
&lt;li&gt;리더는 acks 응답을 프로듀서에게 보낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;ack=all, min.insync.replicas=2 설정을 권장하는 이유&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.conduktor.io/kafka/kafka-topic-configuration-min-insync-replicas&quot;&gt;Kafka 문서&lt;/a&gt;를 확인해보면 &lt;b&gt;&lt;code&gt;ack=all 일 때, min.insync.replicas=2&lt;/code&gt;&lt;/b&gt; 설정을 권장하고 있다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211142618-ef14ff83-a7c2-42c6-bd80-b5d8c6c45a97.png&quot; alt=&quot;스크린샷 2023-01-07 오후 5 49 21&quot; width=&quot;537&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&lt;b&gt;min.insync.replicas&lt;/b&gt;&lt;/code&gt; 옵션이 &lt;b&gt;&lt;code&gt;2&lt;/code&gt;&lt;/b&gt; 라면 위와 같이 하나의 브로커가 문제가 생겨도 클러스터 전체 장애로 이어지지 않는다. 하지만 &lt;b&gt;&lt;code&gt;min.insync.replicas&lt;/code&gt;&lt;/b&gt; 옵션을 &lt;b&gt;&lt;code&gt;3&lt;/code&gt;&lt;/b&gt;을 사용했을 때는 하나의 브로커에 문제가 생겨도 클러스터 전체 장애로 이어지게 된다. 그렇기 때문에 &lt;b&gt;&lt;code&gt;ack=all&lt;/code&gt;&lt;/b&gt;를 사용할 때 브로커 설정 &lt;b&gt;&lt;code&gt;min.insync.replicas=2&lt;/code&gt;&lt;/b&gt; 옵션 사용을 권장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;acks와 관련된 자세한 것은 &lt;a href=&quot;https://www.conduktor.io/kafka/kafka-topic-configuration-min-insync-replicas&quot;&gt;여기&lt;/a&gt;에서 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Consumer&lt;/code&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Consumer 대표 옵션&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#connectconfigs_group.id&quot;&gt;group.id&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨슈머가 속한 컨슈머 그룹을 식별하는 식별자이다. group id는 매우 중요하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_enable.auto.commit&quot;&gt;enable.auto.commit&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;백그라운드로 주기적으로 오프셋을 커밋한다.&lt;/li&gt;
&lt;li&gt;기본 값: &lt;b&gt;&lt;code&gt;true&lt;/code&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_auto.offset.reset&quot;&gt;auto.offset.reset&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Kafka에서 초기 오프셋이 없거나 현재 오프셋이 더 이상 존재하지 않은 경우(데이터가 삭제)에 다음 옵션으로 리셋한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;earliest&lt;/code&gt;&lt;/b&gt;: 가장 초기의 오프셋값으로 설정한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;latest&lt;/code&gt;&lt;/b&gt;: 가장 마지막의 오프셋값으로 설정한다.(기본 값)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;none&lt;/code&gt;&lt;/b&gt;: 이전 오프셋값을 찾지 못하면 에러를 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_max.poll.records&quot;&gt;max.poll.records&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 호출 poll()에 대한 최대 레코드 수를 조정한다.&lt;/li&gt;
&lt;li&gt;기본 값은 500 이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_max.poll.interval.ms&quot;&gt;max.poll.interval.ms&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 옵션 시간 만큼 컨슈머 그룹에서 컨슈머가 살아 있지만 poll() 메소드를 호출하지 않을 때, 장애라고 판단하여 컨슈머 그룹에서 제외한 후 다른 컨슈머가 해당 파티션에서 메세지를 가져가게 한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;기본 값: 5분&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_auto.commit.interval.ms&quot;&gt;auto.commit.interval.ms&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주기적으로 오프셋을 커밋하는 시간&lt;/li&gt;
&lt;li&gt;&lt;code&gt;기본 값: 5초&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;컨슈머 그룹이란?&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 토픽에 여러 컨슈머 그룹이 동시에 메세지를 가져올 수 있다.&lt;/li&gt;
&lt;li&gt;동일한 컨슈머 그룹 내 컨슈머가 추가되면 위와 같이 파티션 소유권이 바뀌게 되고, 이렇게 소유권이 이동하는 것을 &lt;code&gt;리밸런스&lt;/code&gt; 라고 한다.&lt;/li&gt;
&lt;li&gt;컨슈머 그룹의 리밸런스를 통해 컨슈머 그룹에는 컨슈머를 쉽고 안전하게 추가할 수 있고 제거할 수도 있어 높은 가용성과 확장성을 확보할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/clients/consumer.html#concepts&quot;&gt;Consumer Group Document&lt;/a&gt;, &lt;a href=&quot;https://www.popit.kr/kafka-consumer-group/&quot;&gt;읽어보기 좋은 글 참고하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;컨슈머 리밸런스 특징&lt;/code&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리밸런스가 발생하면 컨슈머 그룹 전체가 일시적으로 메세지를 가져올 수 없다는 특징이 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨슈머 그룹 내에서 리밸런스가 일어나면 토픽의 각 파티션마다 하나의 컨슈머가 연결된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;컨슈머의 하트비트&lt;/code&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨슈머가 컨슈머 그룹 안에서 멤버로 유지하고 할당된 파티션의 소유권을 유지하는 방법은 &lt;code&gt;하트비트&lt;/code&gt;를 보내는 것이다. 즉, 컨슈머가 일정한 주기로 하트비트를 보낸다는 사실은 해당 파티션의 메세지를 잘 처리하고 있다는 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;하트비트는 컨슈머가 poll 할 때와 가져간 메세지의 오프셋을 커밋할 때 보내게 된다.&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 컨슈머가 오랫동안 하트비트를 보내지 않으면 세션은 타임아웃되고 해당 컨슈머가 다운되었다고 판단하여 리밸런스가 시작된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;컨슈머 그룹 특징&lt;/code&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 컨슈머 그룹들이 하나의 토픽에서 메세지를 가져갈 수 있는 이유는 컨슈머 그룹마다 각자의 오프셋을 별도로 관리하기 때문이다.&lt;/li&gt;
&lt;li&gt;즉, 하나의 토픽에 두 개의 컨슈머 그룹뿐만 아니라 더 많은 컨슈머 그룹이 연결되어도 다른 컨슈머 그룹에게 영향 없이 메세지를 가져갈 수 있다는 특징이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;토픽의 파티션에는 하나의 컨슈머만 연결 가능하다.&lt;/code&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메세지 처리량을 올리기 위해서 파티션 수는 늘리지 않고 컨슈머 수만 늘리는 것은 의미가 없다. 토픽의 파티션에는 하나의 컨슈머만 연결이 가능하기 때문에 컨슈머를 추가해도 메세지를 소비할 수 없기 때문이다.&lt;/li&gt;
&lt;li&gt;하나의 파티션에 하나의 컨슈머만 붙을 수 있는 이유는 &lt;code&gt;각각의 파티션에 대해서는 메세지 순서를 보장하기&lt;/code&gt; 위해서다.&lt;/li&gt;
&lt;li&gt;즉, 컨슈머를 늘리고 싶으면 토픽의 파티션 수를 먼저 늘려야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;커밋과 오프셋이란?&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;카프카에서는 각 파티션마다 메세지가 저장되는 위치를 &lt;code&gt;오프셋(offset)&lt;/code&gt;이라고 부르고, 오프셋은 파티션 내에서 유일하고 순사적으로 증가하는 숫자(64비트 정수) 형태로 되어 있다.&lt;/li&gt;
&lt;li&gt;각 파티션에 대해 현재 위치(오프셋)를 업데이트하는 동작을 &lt;code&gt;커밋&lt;/code&gt;한다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;자동 커밋&lt;/code&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_enable.auto.commit&quot;&gt;enable.auto.commit&lt;/a&gt;를 &lt;b&gt;&lt;code&gt;true&lt;/code&gt;&lt;/b&gt;로 설정하면 컨슈머는 &lt;code&gt;&lt;b&gt;poll()&lt;/b&gt;&lt;/code&gt;을 호출할 때 가장 마지막 오프셋을 자등으로 커밋한다. (비명시 오프셋 커밋이라고 할 수 있다.)&lt;/li&gt;
&lt;li&gt;커밋 주기는 5초가 기본 값이며, &lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_auto.commit.interval.ms&quot;&gt;auto.commit.interval.ms&lt;/a&gt; 옵션을 통해 조정이 가능하다.&lt;/li&gt;
&lt;li&gt;커밋 주기 5초가 지나기 전, 3초가 지났을 때 컨슈머 리밸런스가 일어난다면 메세지 중복 처리가 될 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) A, B 메세지를 소비한 후에 3초가 지난 시기에 리밸런스가 일어났다면, A, B 메세지는 소비가 되었는데 커밋이 되지 않은 상황이다. 즉, 리밸런스 후에 컨슈머가 다시 A,b 메세지를 소비하여 메세지가 중복 처리될 수 있다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&lt;b&gt;데이터 중복이나 유실을 허용하지 않는 서비스라면 자동 커밋을 사용해서는 안 된다.&lt;/b&gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;수동 커밋&lt;/code&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/#consumerconfigs_enable.auto.commit&quot;&gt;enable.auto.commit&lt;/a&gt;를 &lt;b&gt;&lt;code&gt;false&lt;/code&gt;&lt;/b&gt;로 설정하여 가장 마지막 오프셋을 수동으로 커밋한다.(명시적인 오프셋 커밋이라고 할 수 있다.)&lt;/li&gt;
&lt;li&gt;수동 커밋의 경우에도 컨슈머가 메세지를 읽어온 후에 DB에 반영한 후에 커밋할 때 메세지 중복이 발생할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ex) 메세지들을 데이터베이스에 저장하는 도중에 실패하게 된다면, 마지막 커밋된 오프셋부터 메세지를 다시 가져오기 때문에 일부 메세지들은 데이터베이스에 중복으로 저장될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;동기 오프셋 커밋&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;poll() 메소드가 호출된 이후에 &lt;b&gt;&lt;code&gt;commitSync()&lt;/code&gt;&lt;/b&gt; 메소드를 호출하여 오프셋 커밋을 명시적으로 수행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동기 커밋의 경우 브로커로부터 컨슈머 오프셋 커밋이 완료되었음을 받기까지 컨슈머는 데이터를 더 처리하지 않고 기다리기 때문에 자동 커밋이나 비동기 오프셋 커밋보다 동일 시간당 데이터 처리량이 적다는 특징이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/090/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html#commitSync(java.util.Map)&quot;&gt;commitSync&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;비동기 오프셋 커밋&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 오프셋 커밋은 &lt;b&gt;&lt;code&gt;commitAsync()&lt;/code&gt;&lt;/b&gt; 메소드를 호출하여 사용할 수 있다. 비동기 오프셋 커밋도 마찬가지로 가장 마지막 레코드를 기준으로 오프셋 커밋을 한다. &lt;b&gt;&lt;code&gt;다만, 비동기 오프셋은 커밋이 완료될 때까지 응답을 기다리지 않는다는 특징&lt;/code&gt;&lt;/b&gt;이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/090/javadoc/org/apache/kafka/clients/consumer/KafkaConsumer.html#commitAsync(org.apache.kafka.clients.consumer.OffsetCommitCallback)&quot;&gt;commitAsync&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Zookeeper란?&lt;/code&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Controller election&lt;/code&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리터 선출 시 주키퍼의 메타데이터 정보를 참조한다.&lt;/li&gt;
&lt;li&gt;주키퍼는 현재의 컨트롤러가 장애가 나면 새로운 컨트롤러가 선정되는 것을 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Configuration Of Topics&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 토픽에 대한 파티션 수, 모든 복제본 위치, 모든 토픽에 대한 구성 재정의 목록 및 모든 토픽에 대한 설정 재정의 목록 및 선호되는 리더 노드 등 정보를 가지고 있는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Access control lists&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACLs는 사용자 역활과 관련된 토픽에 대해 읽기와 쓰기 권한 종류를 결정하고, ACLs 정보는 주키퍼가 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Membership of the cluster&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Zookeeper는 클러스터의 일부인 모든 브로커의 목록을 유지 및 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/211159164-446fbadb-f7ab-465e-8429-7d2f53a4eaef.png&quot; alt=&quot;스크린샷 2023-01-08 오전 12 50 59&quot; width=&quot;1698&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 Zookeeper 내용은 &lt;a href=&quot;https://zookeeper.apache.org/doc/current/zookeeperUseCases.html&quot;&gt;Zookeeper Document&lt;/a&gt;를 보고 적어본 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 Zookeeper가 브로커를 관리, 리더 선출, 메타 데이터 관리를 한다는 것은 알았는데 약간의 역할이 더 있고 내부적인 동작들이 더 있는 것 같다.(아직은 Zookeeper에 대한 공부가 더 필요하다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;컨슈머 오프셋&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0.9 이전 버전의 컨슈머는 오프셋 정보를 주키퍼에 저장했지만 성능 등의 문제로 0.9 이후 버전의 컨슈머에서는 카프카 내에 별도로 내부에서 사용하논 &lt;code&gt;토픽(_consumer_offsets)&lt;/code&gt;을 만들고 그 토픽에 오프셋 정보를 저장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;필수 카프카 명령어&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/bin/kafka-topics.sh \
    --bootstrap-server localhost:9092 \
    --replication-factor 1 \
    --partitions 1 \
    --topic test_topic \
    --create &lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토픽 생성하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;/bin/kafka-topics.sh \
    --bootstrap-server localhost:9092 \
    --topic my_topic \
    --delete&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토픽 제거하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/bin/kafka-topics.sh \
    --bootstrap-server localhost:9092 \
    --list&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토픽 리스트 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/bin/kafka-topics.sh \
    --bootstrap-server localhost:9092 \
    --topic test_topic \
    --describe&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토픽 상세보기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/bin/kafka-topics.sh \
    --bootstrap-server localhost:9092 \
    --alter --topic test_topic --partition 2&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토픽의 파티션 수 변경&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;/bin/kafka-consumer-groups.sh \
    --bootstrap-server localhost:9092 \
    --list&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨슈머 그룹 리스트 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;/bin/kafka-consumer-groups.sh \
    --bootstrap-server localhost:9092 \
    --group test_topic --describe&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨슈머 상태와 오프셋 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;code&gt;Kafka Manager&lt;/code&gt;&lt;/b&gt; 라는 GUI 툴을 사용하면 어느정도는 CLI를 사용하지 않고 해결할 수 있다. 하지만 특정한(?) 상황에서 내부 정보를 확인할 때는 CLI를 사용하는 상황도 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka CLI에 대한 좀 더 자세한 것은 &lt;a href=&quot;https://akageun.github.io/2020/05/07/kafka-cli.html&quot;&gt;여기&lt;/a&gt;와 &lt;a href=&quot;http://www.yes24.com/Product/Goods/59789254&quot;&gt;카프카, 데이터 플랫폼의 최강자 6장&lt;/a&gt;을 참고하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;Reference&lt;/code&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/59789254&quot;&gt;카프카, 데이터 플랫폼의 최강자&lt;/a&gt; - &lt;code&gt;3, 4, 5, 6장 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/99122569&quot;&gt;아파치 카프카 애플리케이션 프로그래밍 with 자바&lt;/a&gt; - &lt;code&gt;3, 4장 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.openlogic.com/blog/using-kafka-zookeeper&quot;&gt;https://www.openlogic.com/blog/using-kafka-zookeeper&lt;/a&gt; - &lt;code&gt;Zookeeper 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zookeeper.apache.org/doc/current/zookeeperUseCases.html&quot;&gt;https://zookeeper.apache.org/doc/current/zookeeperUseCases.html&lt;/a&gt; - &lt;code&gt;Zookeeper 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.conduktor.io/kafka/kafka-topic-configuration-min-insync-replicas&quot;&gt;https://www.conduktor.io/kafka/kafka-topic-configuration-min-insync-replicas&lt;/a&gt; - &lt;code&gt;ack=all, min.insync.replicas=2 내용 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kafka.apache.org/documentation/&quot;&gt;https://kafka.apache.org/documentation/&lt;/a&gt; - &lt;code&gt;Kafka 설정 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/installation/configuration/topic-configs.html&quot;&gt;https://docs.confluent.io/platform/current/installation/configuration/topic-configs.html&lt;/a&gt; - &lt;code&gt;Topic 설정들 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.confluent.io/platform/current/installation/configuration/broker-configs.html&quot;&gt;https://docs.confluent.io/platform/current/installation/configuration/broker-configs.html&lt;/a&gt; - &lt;code&gt;Broker 옵션들 참고&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/62326946/kafka-min-insync-replicas-interpretation&quot;&gt;https://stackoverflow.com/questions/62326946/kafka-min-insync-replicas-interpretation&lt;/a&gt; - &lt;code&gt;ack=all, min.insync.replicas 내용 참고&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/Kafka</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/442</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/442#entry442comment</comments>
      <pubDate>Sun, 8 Jan 2023 00:57:30 +0900</pubDate>
    </item>
    <item>
      <title>2022년 0년차 개발자의 회고</title>
      <link>https://devlog-wjdrbs96.tistory.com/441</link>
      <description>&lt;h1&gt;&lt;code&gt;2022년 0년차 개발자의 회고&lt;/code&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 2022년 0년차 개발자가 된 한 해의 회고를 진행해보려 한다. 회고의 제목은 &lt;b&gt;&lt;code&gt;0년차 개발자의 회고&lt;/code&gt;&lt;/b&gt; 이지만, 내용은 내가 한 해 동안 어떤 일들이 있었는지 &lt;a href=&quot;https://devlog-wjdrbs96.tistory.com/430&quot;&gt;2021년 회고&lt;/a&gt;와 비교해보고, 기록하면서 회고해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;대학교 9학기&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년이 끝났을 때만 해도 드디어 나도 대학교를 졸업하는 줄 알았다. 하지만 2022년 1월 초에 0.5점이 부족해서 졸업할 수 없다는 학교의 전화를 받았다.(130 학점을 채워야 졸업인데 129.5 학점을 채웠다.) 계절학기 수강신청도 다 끝났기 때문에 0.5학점을 채우기 위해서는 무조건 9학기를 다녀야 하는 상황이었다. 졸업 사정 확인을 제대로 못한 나의 책임이 100% 였기에 누구도 탓할 수 없었고 너무나 속상했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;바보도 이런 바보가 없었고&lt;/b&gt; 9학기를 다니고 무사히 2022년 8월에 졸업을 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;네이버 웹툰 인턴&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네이버 웹툰에서 2021년 12월 부터 인턴을 진행했다. 인턴 기간동안 &lt;a href=&quot;https://brand.webtoonscorp.com/ko&quot;&gt;웹툰 브랜드 가이드 사이트&lt;/a&gt;를 개발하였는데, 이는 단순히 과제가 아니라 실제 서비스를 개발하여 배포하는 것이었다. 인턴으로서 웹툰 브랜드 가이드 사이트를 만들어 서비스를 배포하였다는 것도 뿌듯하였고, 하나의 서비스가 외부로 나가기 위한 전체 프로세스를 경험할 수 있던 것도 너무나 좋았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 팀에서 만난 멘토님이 나의 첫 멘토님이자 스승님 같은 존재인데 너무나 멋있는 분이었고 배울 점이 많은 분이었다. 이런 분을 첫 멘토님으로 만나서 너무나 기쁘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;정규직 전환&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 진행했던 인턴은 &lt;b&gt;&lt;code&gt;체험형 인턴&lt;/code&gt;&lt;/b&gt; 이었다. 그럼에도 12월에 인턴을 시작할 때는 &lt;b&gt;&lt;code&gt;정규직 전환&lt;/code&gt;&lt;/b&gt;에 대한 조금의 기대감과 열심히 해보고자 하는 열정을 가지고 시작했다. 하지만 인턴을 진행하면서 1월이 되었을 쯤엔 자신감이 점점 떨어졌고, 체험형 인턴이다 보니 &lt;code&gt;정규직 전환&lt;/code&gt;은 정말 안되는 것인가 생각을 하면서 전환에 대한 마음을 접기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 2월이 되고 인턴이 거의 끝나갈 무렵 정규직 전환 면접을 제안해주셨다. 이 때는 나에 대한 평가가 좋지 않을 것이라 생각했는데, 예상과는 다르게 전환 면접 제안을 주셔서 너무나 기분이 좋았고 감사드리고 싶다. (나중에 들은 바로는 인턴을 하면서 내가 하나를 되게 깊게 팠던 부분이 있었는데 그 부분에 대해서 좋게 봐주셨다고 했다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 인턴에서 정규직으로 전환이 되어 &lt;b&gt;현재는 네이버 웹툰에서 일을 하고 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;운동&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3년 반 정도를 운동과 담을 쌓고 살고 있었다. 하지만 올해 여름 부터는 &lt;b&gt;&lt;code&gt;헬스&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;자전거&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;농구&lt;/code&gt;&lt;/b&gt; 운동을 하기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 헬스를 시작하게 된 이유는 회사 인턴을 하면서는 살이 3kg 정도 빠졌지만, 인턴이 끝나고 정규직이 전환된 이후로 3개월 만에 7kg 정도 찐 것 같다. (빠진건 지방만 빠진게 아닌데 찐건 지방만 찐 느낌)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 더 이상 운동을 안하면 안될 것 같아서 6월 쯤에 헬스장에 가서 PT를 등록했다. 확실히 혼자 운동을 할 때는 진짜 운동이 너무 하기 싫었는데, PT를 하면서 강제로 운동 하게 되다 보니 점점 운동에 흥미가 느껴지기 시작했다. 그렇게 운동을 하면서 9월 정도가 되었을 때 한달에 2kg 감량을 목표로 하는 가벼운 다이어트를 시작 했다. 매일 운동을 열심히 하다 보니 현재는 6~7kg 정도 감량 했고 앞으로 좀 더 빼보려 하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 원래 운동은 밤에 했지만, 요즘은 매일 아침 6시 운동을 하고 있다. 처음에는 아침 운동이 가능할까? 싶었는데 계속 하다 보니 그러려니 적응은 된다.(하지만 아침에 눈 뜰 때마다 아직도 쉽지 않다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째는 자전거인데 친구가 같이 자전거 타고 한강(선유도)을 갔다오자 해서 자전거를 타고 다녀왔다. 왕복 50 ~ 60km 정도 되는 거리인데 이정도 거리의 자전거를 처음 타보다 보니 너무나 힘들었다. 하지만 다녀왔을 때는 나름 뿌듯했기에 한번 더 다녀와서 총 2번을 다녀왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 번째로 농구도 몇년만에 친구들과 하게 되었는데 옛날 생각나면서 되게 재밌었다. 내년에도 꾸준히 해야겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 활동적인 운동들을 많이 할 수 있었고 &lt;b&gt;내년에도 운동을 절대 빼먹지 않고 꾸준히 할 예정이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;스포츠 경기&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wMzAg/btrU01qjh7y/dQ8baVY8GRTJ8hIQZBgb21/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wMzAg/btrU01qjh7y/dQ8baVY8GRTJ8hIQZBgb21/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot; data-filename=&quot;14.jpeg&quot; width=&quot;250&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wMzAg/btrU01qjh7y/dQ8baVY8GRTJ8hIQZBgb21/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwMzAg%2FbtrU01qjh7y%2FdQ8baVY8GRTJ8hIQZBgb21%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1080&quot; height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zke7A/btrU3bseEDk/zCOKsrODvlOkS0O0ip3yvK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zke7A/btrU3bseEDk/zCOKsrODvlOkS0O0ip3yvK/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot; data-filename=&quot;13.jpeg&quot; width=&quot;336&quot; height=&quot;448&quot; style=&quot;width: 32.5581%; margin-right: 10px;&quot; data-widthpercent=&quot;33.33&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zke7A/btrU3bseEDk/zCOKsrODvlOkS0O0ip3yvK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZke7A%2FbtrU3bseEDk%2FzCOKsrODvlOkS0O0ip3yvK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1080&quot; height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyxo8f/btrU14GRV1t/zxwDrgkkz45CkVUfZQzBv1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyxo8f/btrU14GRV1t/zxwDrgkkz45CkVUfZQzBv1/img.jpg&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot; data-filename=&quot;12.jpeg&quot; width=&quot;392&quot; height=&quot;523&quot; style=&quot;width: 32.5581%;&quot; data-widthpercent=&quot;33.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyxo8f/btrU14GRV1t/zxwDrgkkz45CkVUfZQzBv1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcyxo8f%2FbtrU14GRV1t%2FzxwDrgkkz45CkVUfZQzBv1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1080&quot; height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 운이 너무나 좋게도 &lt;b&gt;&lt;code&gt;한국 vs 브라질&lt;/code&gt;&lt;/b&gt; 축구 경기를 직관할 수 있었다. 축구장은 정말 어렸을 때 가본거 같은데 브라질이랑 하는 경기를 직접가서 볼 수 있어서 너무나 재밌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 몇년만에 야구장도 가게 되었는데 오랜만에 가니까 분위기가 너무 좋고 재밌었다.(내가 응원하는 팀은 언제 잘해질까?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 농구장도 몇년만에 &lt;b&gt;&lt;code&gt;한국 vs 필리핀&lt;/code&gt;&lt;/b&gt; 경기가 있어서 보러가게 되었는데 정말 재밌었고 농구 직관하러 몇번 더 가고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;여행&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/210138150-6a1ef6c3-54df-47dc-8fac-395867e8f8d5.jpeg&quot; alt=&quot;스크린샷 2022-12-31 오후 10 02 21&quot; width=&quot;308&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 부산, 제주도, 안동 여행을 다녀왔다. 부산은 내 기억으론 올해 처음 가봤는데 좋은 기억의 여행이었다. 특히나 안동 여행을 갔을 때는 등산을 했는데, 정말 오랜만에 등산하기도 하고 산의 경사가 만만치 않아서 힘들었지만 되게 재밌고 뿌듯한 등산이어서 기억에 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;매쉬업 12기&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해도 매쉬업 12기 스프링 팀으로 활동했었다. 스프링 팀을 하면서 올해도 좋은 사람들을 많이 만났고 재밌게 활동할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇보다 12기에서 &lt;a href=&quot;https://github.com/mash-up-kr/marryting_backend_spring&quot;&gt;결혼식 소개팅 매리팅&lt;/a&gt; 프로젝트를 했었는데 우리팀이 2등을 했는데, 프로젝트 기획, 팀원들이 재밌었고 좋은 사람들이어서 기억에 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;1784&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/210138418-3c1e0e5f-aa35-481d-bb93-274898c6fefe.jpeg&quot; alt=&quot;스크린샷 2022-12-31 오후 10 02 21&quot; width=&quot;308&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹툰은 회사가 판교에 있는데, 회사 행사가 있어서 1784로 출근할 기회가 있었다. 1784를 직접 가보니 시설도 너무 좋고 좋은 경험이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;워크샵&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/210138664-61da3d1d-ff06-4562-963d-ee6c18d72d22.png&quot; alt=&quot;스크린샷 2022-12-31 오후 10 02 21&quot; width=&quot;408&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 춘천으로 주니어 워크샵을 가게 되었는데 내부 시설도 좋았고 주변 풍경도 이쁘고 회사 사람들과 좋은 추억을 남길 수 있는 좋았던 기억이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;발표&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 학교와 회사에서 발표할 기회가 있었다. 학교에서는 같은 과 학생들에게 나의 취업 경험을 얘기하는 자리였다. 이렇게 취업에 성공해서 나의 이야기를 학교에서 발표했다는게 신기하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 한 발표는 테크톡 같은 느낌의 발표이다. 주제는 꼭 기술 주제가 아니어도 되고 원하는 주제로 발표해도 된다. 나는 내가 신입으로서 느낀 첫 프로젝트 경험기로 발표했는데, 나름 회사의 많은 사람들이 보는 앞에서 발표하다 보니 부담되었다. 하지만 끝나고 나면 역시나 뿌듯하고 재밌는 경험이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;사이드 프로젝트&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해는 주로 회사 업무에 집중하고 업무에 관련된 공부를 하느라 정신이 없었다. 그럼에도 틈틈히 사이트 프로젝트를 하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 대해 간략하게 설명하면 하나의 명함을 공유하는 앱이고, 하나의 빵 선택을 도와주는 앱이라 할 수 있다. 어쩌다보니 2개의 사이드 프로젝트를 참여하고 있다. (역시 일 벌리기 장인인가?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 일이 항상 1순위다 보니 사이드 프로젝트에 집중을 못할 때도 많을 거 같아서 참여하는 것에 대해 고민이 많았는데 더 열심히 공부해보려 참여하게 되었다. 내년에도 열심히 프로젝트를 하다 보면 더 나를 성장할 수 있게 하지 않을까? 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;회사 업무의 시작&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 제일 할 말도 많고 올 한해의 가장 많은 비중을 차지하고 있는게 &lt;b&gt;&lt;code&gt;회사 업무&lt;/code&gt;&lt;/b&gt;다 보니 회고의 가장 마지막에 넣었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3월에 인턴에서 정규직으로 전환되었을 때는 자신감이 많이 생겼다. 하지만 정규직으로 들어온 후에 일을 시작했을 때는 자신감이 엄청나게 떨어졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발하기&amp;nbsp;위해서는&amp;nbsp;프로젝트&amp;nbsp;도메인에&amp;nbsp;대해&amp;nbsp;파악해야&amp;nbsp;했는데&amp;nbsp;모르는&amp;nbsp;것이&amp;nbsp;너무나&amp;nbsp;많았고,&amp;nbsp;단순하게&amp;nbsp;파악할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;구조가&amp;nbsp;아니어서&amp;nbsp;스스로&amp;nbsp;해결하지&amp;nbsp;못하고&amp;nbsp;자꾸&amp;nbsp;질문하고&amp;nbsp;주변&amp;nbsp;사람들에게&amp;nbsp;의존하게&amp;nbsp;되었다.&lt;br /&gt;&lt;br /&gt;좀만&amp;nbsp;더&amp;nbsp;침착했으면&amp;nbsp;스스로&amp;nbsp;해결할&amp;nbsp;수&amp;nbsp;있었던&amp;nbsp;부분도&amp;nbsp;질문을&amp;nbsp;통해서&amp;nbsp;해결하고,&amp;nbsp;간단한&amp;nbsp;문제여도&amp;nbsp;시간이&amp;nbsp;오래&amp;nbsp;걸리다&amp;nbsp;보니&amp;nbsp;나의&amp;nbsp;개발&amp;nbsp;능력을&amp;nbsp;의심하게&amp;nbsp;되기도&amp;nbsp;했다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;역시나&amp;nbsp;공부해야&amp;nbsp;할&amp;nbsp;건&amp;nbsp;너무나&amp;nbsp;많고&amp;nbsp;부족한게&amp;nbsp;너무나&amp;nbsp;많다는&amp;nbsp;걸&amp;nbsp;다시&amp;nbsp;한번&amp;nbsp;느낄&amp;nbsp;수&amp;nbsp;있었다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;새로운 기술 스택&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사 업무를 하면서 사용했던 기술들에 대해서 간략하게 정리해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Kotlin, Coroutine&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin으로 개발을 하고 싶다는 생각이 있었다. 그런데 올해 신규 프로젝트를 시작했는데 여기서는 &lt;b&gt;&lt;code&gt;Java&lt;/code&gt;&lt;/b&gt;가 아닌 &lt;b&gt;&lt;code&gt;Kotlin&lt;/code&gt;&lt;/b&gt;으로 개발하고 있어서 아주 좋다. 그리고 &lt;b&gt;&lt;code&gt;Coroutine&lt;/code&gt;&lt;/b&gt;도 사용하게 되었는데, 아직 잘 모르기 때문에 조금씩 공부해보고 있고 팀에서 사용하고 있는 것들을 보고 있는데 아직은 많이 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 신규 프로젝트에서 사용하면서 학습하고 있기 때문에 내년에는 많이 공부할 내용이 될 것 같고, 내년 회고에서는 이 기술들에 대해서 자신감 가질 수 있도록 노력해야 겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;ElasticSearch, Filebeat, Logstash, Kibana&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 ElasticSearch를 사용해서 Admin에서 사용할 검색 기능을 개발한 적이 있었다. 이 때 ElasticSearch에 대해서 처음 공부하여 개발해봤는데, 이 때 ElasticSearch가 어떻게 동작하는지 공부하면서 실제 적용해볼 수 있어서 아주 재밌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Filebeat도 사용해보고 ELK 라고 하는 &lt;b&gt;ElasticSearch, Logstash, Kibana&lt;/b&gt; 사용해서 모니터링을 구축해볼 수 있었고, 이러한 기술들이 동작하는 방식들에 대해서 경험해볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Kafka&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀은 Kafka Platform을 운영하다 보니 Kafka를 많이 사용하는 팀이다. Kafka는 공부해본 적도 없고 사용해본 적도 없기 때문에 되게 어렵고 미지의 세계였는데 Kafka 프로젝트를 참여하게 되었다. Kafka 프로젝트를 하면서 &lt;b&gt;&lt;code&gt;Lag&lt;/code&gt;, &lt;code&gt;Topic&lt;/code&gt;, &lt;code&gt;Partition&lt;/code&gt;, &lt;code&gt;Consumer Group&lt;/code&gt;&lt;/b&gt; 등등 Kafka 용어, 개념을 알게 되었고, Kafka 운영 노하우를 경험할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 아직도 Kafka를 깊게 아는 것은 아니기 때문에 내년에 더 많은 공부가 필요하고 할 예정이다. 그리고 내가 올해 제일 많이 참여했던 프로젝트는 우리 팀에서 운영하고 있는 Kafka 프로젝트이다. 제일 많은 &lt;b&gt;도메인 지식을 알게된 프로젝트&lt;/b&gt;라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Spring&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring에 대해서는 나름 어느정도는 알고 있다고 생각했는데 팀에서 프로젝트 하다 보니 Spring 조차 알고 있는게 부족하다는 것을 느꼈고, 기본기에 대한 중요성도 많이 느꼈고 더 공부할 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 팀에서는 멀티 모듈을 사용하는데 멀티 모듈에 대해서도 많이 익숙해지고 배울 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Cassandra, Redis&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에 들어오기 전에는 &lt;b&gt;&lt;code&gt;RDB&lt;/code&gt;&lt;/b&gt;만을 사용해봤다. 하지만 우리 팀은 &lt;b&gt;&lt;code&gt;RDB&lt;/code&gt;&lt;/b&gt; 보다는 &lt;b&gt;&lt;code&gt;NoSQL&lt;/code&gt;&lt;/b&gt;을 메인으로 사용하다 보니 &lt;b&gt;&lt;code&gt;NoSQL&lt;/code&gt;&lt;/b&gt;에 대한 학습이 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 사용한 DB는 Cassandra 인데, 아직 공부한지도, 사용한지도 얼마 되지 않아서 어색하고 어렵다. 앞으로 정말 많이 사용하게 될 예정인지라 많은 공부가 필요한 기술이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Vue&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀에서 Admin을 개발할 때 Vue를 사용하기 때문에 Vue를 공부하고 사용하면서 개발하였다. 그리고 단순히 Admin 개발 용도로 Vue를 학습하고 사용하기엔 너무나 괜찮다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Admin 개발을 하면서 느낀 것은 항상 백엔드만 개발했기 때문에 프론트엔드 경험이 정말 부족했다.  팀에서 Vue로 Admin 개발을 하다 보니 이제 어느정도의 어드민을 만들 수 있게 된 것 같다. 그리고&amp;nbsp;&lt;b&gt;백엔드 개발자여도 어드민을 개발할 정도의 프론트엔드 개발 능력은 있어야 한다고 생각한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;Git&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에 들어오기 전에 &lt;b&gt;&lt;code&gt;Git&lt;/code&gt;&lt;/b&gt;을 사용할 때는 &lt;b&gt;&lt;code&gt;Rebase&lt;/code&gt;&lt;/b&gt;, &lt;b&gt;&lt;code&gt;Cherry-pick&lt;/code&gt;&lt;/b&gt; 같은 것들을 들어보기만 하고 사용해보지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 우리 팀 같은 경우는 Git을 사용할 때 &lt;code&gt;Rebase&lt;/code&gt;를 사용하는데, 이런 방식들이 내가 사용했던 방식보다 되게 복잡해서 처음에는 되게 어려웠다. 하지만 프로젝트를 하면서 계속 사용하다 보니&amp;nbsp;&lt;b&gt;이제는 Rebase에 대해 익숙해질 수 있었고 Cherry-pick도 사용해보면서 Git에 대해 조금 더 잘 알게 되었다.&lt;/b&gt; (이전에는 Git을 막 쓰다가 이제 좀 생각하면서 쓰는 느낌이랄까?)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;우리 팀에서 느낀 점&lt;/code&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;커뮤니케이션, 프로젝트 도메인 파악&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 일하면서 &lt;b&gt;실무에서 역량을 발휘하려면 개발 실력도 중요하지만 커뮤니케이션과 도메인 지식을 빠르게 습득하는 것, 그리고 다른 사람이 작성한 코드를 읽고 이해하는 능력&lt;/b&gt;이 굉장히 중요하다는 것을 느꼈다.&lt;br /&gt;&lt;br /&gt;커뮤니케이션은&amp;nbsp;일을&amp;nbsp;하다보면&amp;nbsp;어떤식으로&amp;nbsp;해야&amp;nbsp;하는지&amp;nbsp;점점&amp;nbsp;감을&amp;nbsp;찾고&amp;nbsp;잘하게&amp;nbsp;되는&amp;nbsp;것&amp;nbsp;같다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;하지만 도메인 지식을 파악하는 것은 나에게 쉽지 않았다. 처음에는 당연히 프로젝트에 대해서 어느정도 설명해주고 프로젝트에 관련된 문서를 보면서 개념을 익혀 나가지만, 팀원들이 옆에서 하나하나 알려주지 않는다.(&lt;b&gt;이건 어떤 회사를 가나 똑같을 것이다.&lt;/b&gt;)&lt;br /&gt;&lt;br /&gt;즉,&amp;nbsp;다른&amp;nbsp;사람이&amp;nbsp;작성한&amp;nbsp;코드를&amp;nbsp;이해하면서&amp;nbsp;프로젝트&amp;nbsp;도메인에&amp;nbsp;대해&amp;nbsp;스스로&amp;nbsp;파악할&amp;nbsp;수&amp;nbsp;있어야&amp;nbsp;한다.&amp;nbsp;지금&amp;nbsp;생각해보면&amp;nbsp;입사&amp;nbsp;초반에&amp;nbsp;프로젝트&amp;nbsp;도메인을&amp;nbsp;파악할&amp;nbsp;때&amp;nbsp;스스로&amp;nbsp;파악할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;부분과&amp;nbsp;질문을&amp;nbsp;해야&amp;nbsp;할&amp;nbsp;부분을&amp;nbsp;잘&amp;nbsp;나눴어야&amp;nbsp;했는데&amp;nbsp;질문을&amp;nbsp;엄청나게&amp;nbsp;했던&amp;nbsp;것&amp;nbsp;같다.&lt;br /&gt;&lt;br /&gt;질문이라는게 엄청 좋은 것이지만, 나에게 질문은 너무 다른 사람에게 의존하게 만드는 것 같다. 스스로 찾아보고 해결할 수 있는 능력을 더 기르고 싶은데 자꾸 주변에 물어보고 해결하고자 한다. 이 부분은 매년 스스로 좀 고치고 싶어하는 부분인데 잘 고쳐지지 않는 것 같다.&lt;br /&gt;&lt;br /&gt;내년에는&amp;nbsp;꼭&amp;nbsp;주변에&amp;nbsp;사수님이나&amp;nbsp;시니어&amp;nbsp;분이&amp;nbsp;있어야&amp;nbsp;일을&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있는게&amp;nbsp;아니라&amp;nbsp;&lt;b&gt;스스로&amp;nbsp;문제를&amp;nbsp;해결하고&amp;nbsp;팀에&amp;nbsp;많은&amp;nbsp;기여할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;사람이&amp;nbsp;되고&amp;nbsp;싶다.&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;성장할 수 있는 환경&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀에 주니어 개발자들이 많이 있는 편이다. &lt;b&gt;입사 초반에는 이러한 환경이 내가 성장하기에 좋은 환경일까?&lt;/b&gt; 라는 생각을 많이 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 주니어 개발자여도 너무나 잘하는 사람들이 많기 때문에 배울점도 많았고 나에게 좋은 자극을 주었다. 특히 어떤 주니어 개발자분은 말도 안되게 잘하는 분도 있어서 배울점이 정말 많았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀에 주니어가 많고 적은게 중요한게 아니라 내가 더 학습하면 노력하고 어떤 환경에서도 충분히 성장할 수 있다는 것을 느끼고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 팀에 있는 시니어 분들에게도 배울 점이 정말 많았다. 이것이 시니어인가? 하는 느낌을 많이 받을 수 있었다. 특히나 나와 같이 많이 일했던 사수님이 계신데 질문도 정말 잘 받아주셨고 나에게 많은 가르침을 주셔서 내가 더 성장할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀은 &lt;b&gt;성능 테스트&lt;/b&gt;를 정말 많이 하는 팀이다. 성능이 중요하고 성능을 개선하기 위해서 노력하기 때문에 성능 테스트를 많이 한다. 나 또한 성능 테스트를 많이 경험해볼 수 있었는데 성능 테스트할 때 어떤 것들을 봐야 하는지, 병목 지점을 찾아서 어떻게 개선해나가는지 배울 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 팀의 이름은 &lt;b&gt;&lt;code&gt;Dataflow&lt;/code&gt;&lt;/b&gt; 이다. 말 그대로 &lt;b&gt;대용량 데이터 처리에 특화된 팀&lt;/b&gt;이라 할 수 있다. 팀에서 대용량 데이터를 처리할 때 어떻게 해야 빠르게 할 수 있고, 효율적으로 할 수 있는지에 대해 많이 배울 수 있었다. 앞으로 정말 많이 배우면서 성장할 수 있는 환경이라는 것을 점점 느끼고 있어서 내년이 기대된다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;다양한 의견 제시&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일을 할 때 내가 이 일을 왜 하는지, 어떤 것을 하는지 명확하게 알고 해야 하는데, 그냥 일을 하게 될 때가 있었다. 그러다 보니 나의 의견 제시를 많이 안하게 되고 그냥 시키는대로 하게 될 때가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 프로젝트 도메인에 대한 지식이 깊어지고, 내가 이 일을 왜 하는지, 어떤 것을 하고 있는지 명확하게 알다보면 다양한 의견 제시를 할 수 있을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 수동적으로 일하는 것이 아니라 능동적으로 일하는 것이 회사에서 일할 때 많이 중요하다는 것을 느낄 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;code&gt;문서화&lt;/code&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 문서 작성하는 것에는 잘한다고 생각하는데 팀 내에서도 문서에 대해서는 꽤나 기여할 수 있었던 것 같다. 하지만 초반에는 무조건 문서가 많으면 좋다고 생각했는데, 문서도 결국 유지보수의 대상이기 때문에 관리가 잘 되지 않으면 오히려 독이 될 수 있음을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마지막으로&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 잘하는 개발자가 무엇인지 종종 생각하곤 한다. 여러 개발 경험이 있고, 여러 기술을 사용한 경험이 있으면 잘하는 개발자 일까?, 한 분야에 깊게 알고 있으면 잘 하는 개발자 일까?&lt;br /&gt;&lt;br /&gt;아니면&amp;nbsp;빠르게&amp;nbsp;도메인&amp;nbsp;지식을&amp;nbsp;파악하고&amp;nbsp;실무에&amp;nbsp;투입될&amp;nbsp;수&amp;nbsp;있는게&amp;nbsp;잘하는&amp;nbsp;개발자일까?&amp;nbsp;아니면&amp;nbsp;개발을&amp;nbsp;빠르게&amp;nbsp;하는&amp;nbsp;사람?,&amp;nbsp;코드를&amp;nbsp;객체지향&amp;nbsp;적으로&amp;nbsp;잘&amp;nbsp;짜는&amp;nbsp;사람?,&amp;nbsp;테스트&amp;nbsp;코드를&amp;nbsp;잘&amp;nbsp;짜는&amp;nbsp;사람?&lt;br /&gt;&lt;br /&gt;이렇게&amp;nbsp;하나씩&amp;nbsp;나열하다&amp;nbsp;보면&amp;nbsp;끝도&amp;nbsp;없을&amp;nbsp;것이다.&amp;nbsp;이러한&amp;nbsp;항목들을&amp;nbsp;N&amp;nbsp;각형을&amp;nbsp;나타내면&amp;nbsp;사람들은&amp;nbsp;자신의&amp;nbsp;잘하는&amp;nbsp;부분,&amp;nbsp;부족한&amp;nbsp;부분들이&amp;nbsp;나타날&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;잘하는&amp;nbsp;개발자가&amp;nbsp;무엇인지는&amp;nbsp;사람마다&amp;nbsp;생각이&amp;nbsp;다르기에&amp;nbsp;정답은&amp;nbsp;없지만,&amp;nbsp;잘하는&amp;nbsp;개발자라면&amp;nbsp;N&amp;nbsp;각형에서&amp;nbsp;뾰족한&amp;nbsp;부분이&amp;nbsp;많을&amp;nbsp;거라고&amp;nbsp;생각한다.(내가&amp;nbsp;생각하는&amp;nbsp;잘하는&amp;nbsp;개발자는&amp;nbsp;아직&amp;nbsp;잘&amp;nbsp;모르겠다.)&lt;br /&gt;&lt;br /&gt;그런데&amp;nbsp;내가&amp;nbsp;개발자로서&amp;nbsp;가지고&amp;nbsp;있는&amp;nbsp;능력을&amp;nbsp;N&amp;nbsp;각형으로&amp;nbsp;표현해본다면&amp;nbsp;부족한&amp;nbsp;부분이&amp;nbsp;너무나&amp;nbsp;많다.&amp;nbsp;내년에는&amp;nbsp;나의&amp;nbsp;부족한&amp;nbsp;부분을&amp;nbsp;보완하도록&amp;nbsp;노력할&amp;nbsp;예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;2023년 목표&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올해 목표는 아래 두가지만 정해보려 하려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;깊이 있는 학습&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팀에서 여러 가지 기술을 사용해봤지만 아직 한 기술에 대한 깊이가 부족하다고 생각하고 나만의 강점을 가질 수 있도록 깊이있게 기술을 학습해보자. 뿐만 아니라 회사에서 일하다 보니 기본기에 대한 공부도 많이 필요하다고 느꼈다. (CS 지식이라던지, Java, Spring, 새로운 지식 등등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 테스트 코드는 매번 안짜는 버릇이 들다 보니까 나에게 엄청나게 취약한 부분이 되었다. 올해는 테스트 코드를 작성하는 습관을 기르자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내년에는 올해보다 좀 더 깊이있는 공부를 꾸준히 하면서 성장하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;새로운 기술 겁내지 않기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 생각하는 잘하는 개발자라고 생각하는 것 중에 하나는 &lt;b&gt;처음 사용해보는 기술&lt;/b&gt;, &lt;b&gt;새로운 기술&lt;/b&gt;에 대해서 금방 적응하고 이해할 수 있는 능력이다.&lt;br /&gt;&lt;br /&gt;하지만 나는 &lt;b&gt;새로운 기술&lt;/b&gt;에 대해 적응하고 이해하는 능력이 높다고 생각하지 않는다. 간단한 이유를 말하면 일단 &lt;b&gt;내가 할 수 있을까?&lt;/b&gt;란 겁을 먹기 때문인 것 같다.&lt;br /&gt;&lt;br /&gt;물론&amp;nbsp;올해도&amp;nbsp;새로운&amp;nbsp;기술들을&amp;nbsp;많이&amp;nbsp;사용하고&amp;nbsp;적응해왔지만,&amp;nbsp;앞으로도&amp;nbsp;새로운&amp;nbsp;기술을&amp;nbsp;공부하고&amp;nbsp;사용해야&amp;nbsp;할&amp;nbsp;상황은&amp;nbsp;너무나&amp;nbsp;많을&amp;nbsp;것이다.&lt;br /&gt;&lt;br /&gt;새로운 기술을 익히는 방법은 &lt;b&gt;공식 문서 보기&lt;/b&gt;, &lt;b&gt;인강 보기&lt;/b&gt;, &lt;b&gt;책 보기&lt;/b&gt;, &lt;b&gt;여러 참고 자료 보기&lt;/b&gt; 등등 여러가지가 있을 것인데, 내년에는 &lt;b&gt;새로운 기술&lt;/b&gt;을 적응하는 자기만의 노하우를 만들고 침착하게 공부해서 내 것으로 만들 수 있도록 노력하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;운동&lt;/code&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운동을 했을 때 운동을 하기 전보다 확실히 몸과 마음이 달라지는게 많이 느껴진다. 올해는 어디 다치지 않고 운동을 꾸준히 할 수 있었으면 좋겠다. 내년에도 매일 5시 30분에 일어나서 6시 운동을 할 예정이다.&lt;/p&gt;</description>
      <category>Retrospect</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/441</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/441#entry441comment</comments>
      <pubDate>Sun, 1 Jan 2023 01:42:09 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] Multi Module에서 implementation으로 참조 못하는 에러 해결하기</title>
      <link>https://devlog-wjdrbs96.tistory.com/438</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Multi Module JPA Cannot Access Error 해결하기&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 &lt;code&gt;Multi-Module&lt;/code&gt;을 사용했을 때 반드시 발생하는 에러는 아니고 필자처럼 사용했을 때 에러가 발생할 수 있는 상황이다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/204126357-0128511c-39c2-4c18-a936-6fa90d1d0029.png&quot; alt=&quot;스크린샷 2022-11-27 오후 5 40 05&quot; /&gt;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;Cannot access 'org.springframework.data.repository.Repository' which is a supertype of 'org.springframework.data.repository.CrudRepository'. Check your module classpath for missing or conflicting dependencies&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi Module로 진행할 때 JPA에서 제공해주는 &lt;code&gt;findById&lt;/code&gt; 메소드를 사용하려는데 위와 같은 에러가 발생했다. 또 에러가 왜 발생하는 것일까.. 하고 관련 내용을 찾아봤는데 나의 상황에 맞는거는 딱히 찾을 수 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 좀 더 고민해보니 &lt;code&gt;Multi Module Gradle&lt;/code&gt; 설정 문제라는 것을 깨달았고, 내가 생각한 이유가 맞았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/204126756-37debd5f-2b23-43af-9adc-5b287d5d03e2.png&quot; alt=&quot;스크린샷 2022-11-27 오후 5 52 06&quot; width=&quot;595&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 상황은 각 모듈이 다른 모듈 or 의존성을 참조할 때 &lt;code&gt;implementation&lt;/code&gt;을 사용해서 참조하고 있었다.(&lt;code&gt;spring data jpa&lt;/code&gt; 의존성은 전체 &lt;code&gt;build.gradle&lt;/code&gt;이 아닌 &lt;code&gt;Domain build.gradle&lt;/code&gt;에 넣어놓은 상황이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 왜 이런 상황이 발생하는지 알기 위해서는 gradle의 &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;implementation&lt;/code&gt; 차이가 무엇인지 알아야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;gradle implementation vs api 차이&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/204126960-b77b2c1e-1197-440f-9347-f0acf76079e3.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api를 사용하게 되면 직접, 간접 의존하고 있는 모듈까지 rebuild(recompile)이 되어야 한다. 즉, C 모듈을 수정했는데 B 모듈 뿐만 아니라 A 모듈까지 rebuild(recomplie) 되어야 하는 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 implementation은 직접 참조하고 있는 모듈만 rebuild(recomplie) 하면 된다는 특징이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;위의 에러는 왜 발생한 것일까?&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;api, implementation 또 하나의 차이는 &lt;code&gt;Module API 노출&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/204127087-69bab9dd-f8e7-44c7-8260-330b831aec51.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/204127100-758b9e92-4791-4990-bcfa-31cb96a83879.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림을 보면 알 수 있지만 implementation을 사용하면 간접 참조하고 있는 모듈은 참조할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들면, C 모듈을 직접 참조하고 있는 모듈은 B이고, 간접 참조하고 있는 것은 A인데 A에서 C 모듈을 코드를 사용할 수 없다는 뜻이다. 이러한 이유 때문에 위와 같은 에러가 발생한 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A : API 모듈&lt;/li&gt;
&lt;li&gt;B : Domain 모듈&lt;/li&gt;
&lt;li&gt;C : Spring Data JPA 의존성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 상황에서 &lt;code&gt;A -&amp;gt; B -&amp;gt; C&lt;/code&gt; 참조할 때 모두 &lt;code&gt;implementation&lt;/code&gt;을 사용했기 때문에 API 모듈에서 JPA를 참조할 수 없다는 에러가 발생하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것을 해결하려면 Domain -&amp;gt; JPA 의존성을 참조할 때 &lt;code&gt;implementation&lt;/code&gt;이 아닌 &lt;code&gt;api&lt;/code&gt;를 사용해서 참조하면 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Reference&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gradle.org/7.0/userguide/java_library_plugin.html#sec:java_library_configurations_graph&quot;&gt;https://docs.gradle.org/7.0/userguide/java_library_plugin.html#sec:java_library_configurations_graph&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/44493378/whats-the-difference-between-implementation-api-and-compile-in-gradle&quot;&gt;https://stackoverflow.com/questions/44493378/whats-the-difference-between-implementation-api-and-compile-in-gradle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Server/Spring</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/438</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/438#entry438comment</comments>
      <pubDate>Sun, 27 Nov 2022 18:06:55 +0900</pubDate>
    </item>
    <item>
      <title>[Kafka] acks=all 일 때 min.insync.replicas=2 설정을 권장하는 이유</title>
      <link>https://devlog-wjdrbs96.tistory.com/436</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;acks=all 일 때 min.insync.replicas=2로 설정해야 하는 이유는 무엇일까?&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;acks=all&lt;/code&gt; : 리더는 ISR의 팔로워로부터 데이터에 대한 ack를 기다리고, 하나의 팔로워가 있는 한 데이터는 손실되지 않으며 데이터 무손실에 대해 가장 강력하게 보장&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ISR&lt;/code&gt;: In Sync Replica의 약어로 현재 리플리케이션이 되고 있는 리플리케이션 그룹(replication group)을 의미&lt;/li&gt;
&lt;li&gt;&lt;code&gt;min.insync.replicas&lt;/code&gt;: 최소 리플리케이션 팩터를 지정하는 옵션&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Replication Factor&lt;/code&gt; 는 토픽의 파티션의 복제본을 몇 개를 생성할 지에 대한 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 내용에 들어가기 전에 카프카 용어에 대해 간략하게 정리하면 위와 같습니다. 여기서 이번 글에서는 &lt;code&gt;min.insync.replicas&lt;/code&gt; 옵션에 대해서 알아볼 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;아파치 카프카 문서에서는 손실 없는 메세지 전송을 위한 조건으로 프로듀서는 acks=all, 브로커의 min.insync.replicas=2, Topic의 Replication Factor는 3&lt;/code&gt;으로 권장하고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;min.insync.replicas&lt;/code&gt; 옵션 값을 올리면 복제본이 올라가니까 손실 없는 메세지 전송을 위해 &lt;code&gt;min.insync.replicas=3&lt;/code&gt;으로 설정해야 하는 것이 아닌가라고 생각할 수 있는데요. 아파치 카프카에서는 왜 &lt;code&gt;min.insync.replicas=2&lt;/code&gt;로 설정을 권장하는 것인지에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;min.insync.replicas=3으로 설정했을 때 동작방식&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/195646636-4a2c58ef-8940-4192-be50-0d771a40212f.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로듀서가 &lt;code&gt;acks=all&lt;/code&gt; 옵션으로 리더에게 메세지를 보냅니다.&lt;/li&gt;
&lt;li&gt;리더는 메세지를 받은 후에 저장합니다. 브로커 1에 있는 팔로워는 변경된 사항이나 새로 받은 메세지가 없는지를 리더로부터 주기적으로 확인하면서, 새로운 메세지가 전송된 것을 확인하면 자신도 리더로부터 메세지를 가져와서 저장합니다.&lt;/li&gt;
&lt;li&gt;리더는 &lt;code&gt;min.insync.replicas&lt;/code&gt;가 3으로 설정되어 있기 때문에 &lt;code&gt;acks&lt;/code&gt;를 보내기 전 최소 3개의 복제를 유지하는지 확인해야 합니다.&lt;/li&gt;
&lt;li&gt;리더는 프로듀서가 전송한 메세지에 대해 acks를 프로듀서에게 보냅니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림은 &lt;code&gt;min.insync.replicas=2&lt;/code&gt;로 설정되어 있을 때지만 그림처럼 &lt;code&gt;acks=all&lt;/code&gt;로 되어 있기 때문에 &lt;code&gt;acks&lt;/code&gt;를 보내기 전에 &lt;code&gt;min.insync.replicas&lt;/code&gt; 값만큼 리플리케이션을 확인한 후에 acks를 응답으로 보내게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 보아도 &lt;code&gt;min.insync.replicas&lt;/code&gt;를 3으로 올리는 것이 더 좋아보이는데 그러면 왜 3이 아닌 2로 권장하는 것일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 시나리오를 생각해보겠습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로듀서는 &lt;code&gt;acks=all&lt;/code&gt; 옵션으로 메세지를 전송합니다.&lt;/li&gt;
&lt;li&gt;브로커 중 팔로워가 위치하고 있는 브로커 하나를 강제로 종료합니다.&lt;/li&gt;
&lt;li&gt;카프카 서버의 로그를 확인합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팔로워 중에 하나인 브로커를 강제 종료하고 에러 로그를 확인해보면 &lt;code&gt;Replication Factor가 부족하다&lt;/code&gt;는 내용의 메세지가 나타나게 될 것인데요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/195647144-c7049206-c4ea-4e87-95d2-d68e16b37080.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 이러한 로그들이 발생하는지 알아보면 우리는 &lt;code&gt;acks=all, min.insync.replicas=3&lt;/code&gt;으로 설정했습니다. 이렇게 설정한 경우 프로듀서가 토픽의 리더에게 메세지를 전송하게 되면, 리더 + 팔로워 + 팔로워 이렇게 3곳에서 모두 메세지를 받아야만 리더는 프로듀서에게 메세지를 잘 받았다는 &lt;code&gt;확인(ack)&lt;/code&gt;을 보낼 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여기서 팔로워 중에 하나인 브로커 하나에 문제가 발생했으니 ISR에는 리더와 팔로워 하나만 남아 있습니다. 결국 옵션으로 설정한 조건을 충족시킬 수 없는 상황이 발생했기 때문에 위와 같은 에러 로그가 발생하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카는 브로커 하나가 다운되더라도 크리티컬한 장애 상황없이 서비스를 잘 처리할 수 있도록 구성되어 있는데, 만약 &lt;code&gt;acks=all + min.insync.replicas=3&lt;/code&gt;으로 설정하게 되면 브로커 하나만 다운되더라도 카프카 메세지를 보낼 수 없는 클러스터 전체 장애와 비슷한 상황이 발생하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 그림 같이 &lt;code&gt;min.insync.replicas&lt;/code&gt; 값이 2이고 브로커가 1대에만 문제가 생겼다면 동작하는데 지장을 주진 않지만, &lt;code&gt;min.insync.replicas=3&lt;/code&gt;으로 했다면 문제가 생기게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;이러한 이유로 카프카에서는 손실없는 메세지 전송을 위해 프로듀서의 acks=all로 사용하는 경우 브로커의 min.insync.replicas=2로 설정하고 토픽의 리플리케이션 팩터는 3으로 설정하기를 권장하고 있습니다.&lt;/code&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Reference&lt;/code&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/62326946/kafka-min-insync-replicas-interpretation&quot;&gt;https://stackoverflow.com/questions/62326946/kafka-min-insync-replicas-interpretation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.yes24.com/Product/Goods/59789254&quot;&gt;카프카 데이터 플랫폼의 최강자 4장&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DevOps/Kafka</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/436</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/436#entry436comment</comments>
      <pubDate>Fri, 14 Oct 2022 01:05:56 +0900</pubDate>
    </item>
    <item>
      <title>[Spring] 멀티 모듈에서 모듈별 yml 파일 관리하는 법</title>
      <link>https://devlog-wjdrbs96.tistory.com/435</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;Multi module에서 yml 파일 관리하는 법&lt;/code&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;code&gt;Multi-Module&lt;/code&gt;을 사용할 때 모듈 별 &lt;code&gt;yml&lt;/code&gt; 파일 관리하는 법에 대해서 정리해보려 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;api 모듈
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;application.yml&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;domain 모듈
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;application.yml&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 위와 같이 2개의 모듈이 있을 때, 각 모듈마다 &lt;code&gt;application.yml&lt;/code&gt;을 가지고 있을 것입니다. 그런데 지금까지 &lt;code&gt;Multi-Module&lt;/code&gt;로 프로젝트를 할 때는 모듈별로 &lt;code&gt;yml&lt;/code&gt; 파일을 분리하지 않고 &lt;code&gt;api&lt;/code&gt; 모듈 하나에 모든 설정들을 다 넣었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yml이 잘 분리가 되었다면 api 모듈의 yml에서는 api와 관련된 설정(ex: Swagger)들이 있을 것이고 domain 모듈의 yml에는 대표적으로 DB 접근 정보 및 JPA 설정들이 존재할 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이제 모듈 별 yml 분리하는 방법에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;code&gt;모듈별 yml 분리하기&lt;/code&gt;&lt;/h2&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@SpringBootApplication
class NadaServerApplication

fun main(args: Array&amp;lt;String&amp;gt;) {
    System.setProperty(&quot;spring.config.name&quot;, &quot;application,application-domain&quot;)
    runApplication&amp;lt;NadaServerApplication&amp;gt;(*args)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;@SpringBootApplication
public class NadaServerApplication {
    public static void main(String[] args) {
        System.setProperty(&quot;spring.config.name&quot;, &quot;application,application-domain&quot;);
        SpringApplication.run(NadaServerApplication.class, args);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 &lt;code&gt;main&lt;/code&gt; 클래스에 &lt;code&gt;System.setProperty()&lt;/code&gt; 하나만 추가하면 됩니다. &lt;code&gt;System.setProperty&lt;/code&gt;는 환경변수를 등록하는 것인데요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;application&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;application-domain&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수 Value에 이라고 되어 있는데 위처럼 적으면 &lt;code&gt;application.**&lt;/code&gt;, &lt;code&gt;application-domain.**&lt;/code&gt; 파일들을 읽어와서 사용하겠다는 뜻입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://user-images.githubusercontent.com/45676906/193611127-83695921-81f9-4ddc-8a9a-0c8470e90ddb.png&quot; alt=&quot;스크린샷 2022-10-04 오전 12 04 43&quot; width=&quot;363&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;code&gt;core&lt;/code&gt; 모듈에 존재하는 &lt;code&gt;application-domain.yml&lt;/code&gt;을 읽어올 수 있게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주의할 점은 &lt;code&gt;Value&lt;/code&gt;에 적는 이름은 &lt;code&gt;yml&lt;/code&gt; 파일의 이름이어야 하고, &lt;code&gt;yml&lt;/code&gt; 파일의 이름이 겹치면 안됩니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;System.setProperty(&quot;spring.config.name&quot;, &quot;application,api&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 적었다면 &lt;code&gt;api.yml&lt;/code&gt;, &lt;code&gt;api-local.yml&lt;/code&gt;로 사용할 수도 있고, &lt;code&gt;application.yml&lt;/code&gt;, &lt;code&gt;application-local.yml&lt;/code&gt; 같은 이름으로도 사용할 수 있는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 모듈별로 모두 &lt;code&gt;application.yml&lt;/code&gt;의 이름을 사용하게 되면 가까운 yml을 읽어서 사용하기 때문에 &lt;code&gt;domain&lt;/code&gt; 모듈의 &lt;code&gt;yml&lt;/code&gt;은 사용하지 못하게 됩니다.&lt;/p&gt;</description>
      <category>Server/Spring</category>
      <author>백엔드 규니</author>
      <guid isPermaLink="true">https://devlog-wjdrbs96.tistory.com/435</guid>
      <comments>https://devlog-wjdrbs96.tistory.com/435#entry435comment</comments>
      <pubDate>Tue, 4 Oct 2022 00:18:18 +0900</pubDate>
    </item>
  </channel>
</rss>