<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>nodeal dev story</title>
    <link>https://nodealdev.tistory.com/</link>
    <description>한심한 잉여코더. Stay lazy, stay foolish</description>
    <language>ko</language>
    <pubDate>Sat, 30 May 2026 12:25:19 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>nodeal</managingEditor>
    <item>
      <title>안녕, Coroutine</title>
      <link>https://nodealdev.tistory.com/84</link>
      <description>&lt;p&gt;한때 우리 팀의 서버 코드는 거의 모두 &lt;code&gt;suspend&lt;/code&gt; 함수였다. Kotlin을 쓰면서 Coroutine을 쓰지 않을 이유가 있나 싶었고, Spring Data의 &lt;code&gt;CoroutineCrudRepository&lt;/code&gt;, Ktor, kotlinx.coroutines가 만들어 둔 매끈한 API들이 미래를 가리키고 있다고 믿었다. 그런데 지금, 나는 이 글의 제목처럼 작별 인사를 하고 있다.&lt;/p&gt;

&lt;p&gt;이 글은 Coroutine을 비난하기 위한 글이 아니다. 우리가 &lt;em&gt;왜&lt;/em&gt; Coroutine을 도입했는지 다시 짚어보고, 그 동기들이 2026년에도 여전히 유효한지 점검해 본 기록이다.&lt;/p&gt;

&lt;p&gt;몇 가지 한계를 먼저 적어 둔다. 첫째, 이 글은 한 팀의 운영 경험을 정리한 의견이며 벤치마크나 통제된 비교 실험을 담고 있지 않다. 둘째, 우리는 Spring MVC + JDBC + Kotlin이라는 특정 조합 위에서 일했고, WebFlux나 R2DBC를 기본으로 쓰는 조직에는 결론이 그대로 옮겨 가지 않을 수 있다. 셋째, JEP 491이 Java 24(2025-03)에 들어왔는데도 우리가 작별을 결정한 시점은 그보다 한참 뒤다 — Java 25 LTS에서 안정화되고 운영 사례가 쌓이는 시간을 의식적으로 기다렸기 때문이다.&lt;/p&gt;

&lt;h2&gt;우리가 Coroutine으로 풀고 싶었던 것&lt;/h2&gt;

&lt;p&gt;출발점은 단순했다. &lt;strong&gt;OS Thread는 비싸다.&lt;/strong&gt; 동시에 수천 건의 외부 호출을 처리하려고 Thread를 그만큼 만들면 시스템 콜과 메모리에서 비용이 폭증한다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;Array(1000) {
    Thread {
        someBlockingFunction()
    }
} // Thread가 1,000개 생성된다.
    .onEach { it.start() }
    .forEach { it.join() }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Thread Pool로 줄일 수는 있다. 하지만 &lt;code&gt;SynchronousQueue&lt;/code&gt;를 큐로 둔 &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;의 &lt;code&gt;maximumPoolSize&lt;/code&gt;를 1,000으로 잡는 순간, 결국 진짜로 1,000개의 Thread가 만들어진다. &quot;동시성&quot;을 진짜로 원하면 그만큼의 carrier가 필요한 셈이다.&lt;/p&gt;

&lt;p&gt;그래서 우리는 Java 7 시절 등장한 &lt;code&gt;AsynchronousFileChannel&lt;/code&gt; 류의 Non-blocking API로 눈을 돌렸고, 콜백 지옥에 빠졌으며, Coroutine이 그걸 &lt;em&gt;동기 코드처럼&lt;/em&gt; 풀어 준다는 약속에 매료됐다. Kotlin이 언어 차원에서 First-class로 밀어준다는 점도 결정적이었다. Spring Data, Ktor가 모두 그 흐름에 올라탔으니 우리도 늦지 않게 따라가야 한다고 느꼈다.&lt;/p&gt;

&lt;h2&gt;막상 마주한 문제들&lt;/h2&gt;

&lt;h3&gt;러닝 커브, 그리고 잘못된 자신감&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CoroutineContext&lt;/code&gt;, &lt;code&gt;CoroutineScope&lt;/code&gt;, &lt;code&gt;CoroutineDispatcher&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt;. 입문 자료를 읽으면 이해한 것 같지만, 코드 리뷰를 돌리다 보면 — 그리고 솔직히 내가 한 달 전에 쓴 코드를 다시 볼 때도 — 그렇지 않다는 사실이 드러난다. 생각해 보면 전통적인 Spring + JDBC 스택은 개발자가 Thread Pool과 비동기 모델을 직접 들여다보지 않아도 충분히 잘 굴러가도록 설계되어 있었다. 그게 잘못이라는 게 아니라, 오히려 그 잘 만들어진 추상이 우리가 그 아래층을 굳이 알 필요 없게 해 줬다는 뜻이다. 그런 환경 위에 Coroutine이 얹히면, 한 번에 익혀야 할 전제 지식이 갑자기 너무 많아진다. 그런데도 동기식 Blocking I/O가 &quot;원죄&quot;처럼 다뤄지는 분위기 속에서, 이해보다 형식이 앞서는 — 일단 &lt;code&gt;suspend&lt;/code&gt;부터 붙이는 — 코드가 쌓이기 쉽다. 물론 이건 코드 리뷰와 온보딩으로 막아야 하는 문제이지 언어 기능 자체의 죄는 아니다. 다만 막아야 할 면적이 다른 도구에 비해 넓다는 점은 사실이다.&lt;/p&gt;

&lt;h3&gt;예외, 어디서 어떻게 터지는지 모르겠다&lt;/h3&gt;

&lt;p&gt;다음 코드의 동작을 한 번에 설명할 수 있을까?&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;suspend fun f(scope: CoroutineScope) {
    val x = scope.async&amp;lt;Int&amp;gt; {
        delay(3.seconds)
        println(&quot;Good morning&quot;)
        1
    }
    val y = scope.async&amp;lt;Int&amp;gt; {
        throw Exception()
    }
    delay(5.seconds)
    println(x.await() + y.await())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;답은 &quot;넘겨받은 &lt;code&gt;scope&lt;/code&gt;가 무엇이냐에 따라 다르다&quot;이다. &lt;code&gt;coroutineScope&lt;/code&gt;라면 &lt;code&gt;y&lt;/code&gt;의 실패가 형제 Job인 &lt;code&gt;x&lt;/code&gt;까지 취소시키지만, &lt;code&gt;supervisorScope&lt;/code&gt;라면 다른 Job을 죽이지 않고 예외는 &lt;code&gt;await()&lt;/code&gt; 시점까지 조용히 묻혀 있다. 호출자는 함수 시그니처만 봐서는 이걸 알 수 없다.&lt;/p&gt;

&lt;p&gt;거기에 &lt;code&gt;CancellationException&lt;/code&gt;이라는 함정이 있다. Job 취소를 위해 의도적으로 던지는 예외인데, 이게 &lt;code&gt;java.util.concurrent.CancellationException&lt;/code&gt;의 typealias이고 그 부모가 &lt;code&gt;IllegalStateException&lt;/code&gt;이다. 즉 누군가 &lt;code&gt;catch (e: IllegalStateException)&lt;/code&gt;로 너무 넓게 잡기만 해도 취소 신호가 삼켜진다. 실무에서 &lt;code&gt;IllegalStateException&lt;/code&gt;을 광범위하게 잡는 코드가 자주 등장하는 건 아니지만, 한 번 사고가 나면 원인을 짚기까지 시간이 오래 걸리는 종류의 버그라서 의심을 들고 다녀야 하는 영역이 늘어난다는 점이 부담이다.&lt;/p&gt;

&lt;h3&gt;&quot;진짜 Non-blocking&quot; 라이브러리는 흔치 않다&lt;/h3&gt;

&lt;p&gt;Coroutine의 효율은 Blocking이 없을 때 성립한다. 그런데 Java 생태계의 상당 부분이 여전히 Blocking이다.&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;&lt;code&gt;synchronized&lt;/code&gt; — Mutex로 대체 가능하지만, 너무 많은 라이브러리가 이미 의존한다.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;ThreadLocal&lt;/code&gt; — &lt;code&gt;synchronized&lt;/code&gt;처럼 어디 숨어 있을지 모른다.&lt;/li&gt;
    &lt;li&gt;&lt;code&gt;ReentrantReadWriteLock&lt;/code&gt; — Coroutine 친화적인 대체재가 마땅치 않다.&lt;/li&gt;
    &lt;li&gt;Guava Cache — 직접적인 대안이 없다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kotlin 공식 답은 &quot;Blocking은 &lt;code&gt;Dispatchers.IO&lt;/code&gt;에 던져라&quot;이다. 그런데 &lt;code&gt;Dispatchers.IO&lt;/code&gt;의 기본 스레드 한도는 &lt;code&gt;max(64, availableProcessors)&lt;/code&gt;, 즉 대부분의 머신에서 64개다. 동시에 처리해야 할 외부 호출이 수백, 수천 건이라면 이 한도는 금세 병목이 된다. 물론 &lt;code&gt;limitedParallelism()&lt;/code&gt;이나 시스템 프로퍼티 &lt;code&gt;kotlinx.coroutines.io.parallelism&lt;/code&gt;으로 조정할 수단은 있다. 정작 어려운 건 &quot;어디서, 얼마나, 왜&quot; 그렇게 조정해야 하는지에 대한 표준 가이드가 없다는 쪽이다. 결국 각 팀이 자기만의 더 큰 디스패처를 만들어 가며 코드베이스 안에 출처가 제각각인 Dispatcher가 늘어 가는 풍경은 우리만의 경험은 아닐 것 같다. 도구가 강제하지 않으니 사람이 강제해야 하는 영역이 그만큼 늘어난다.&lt;/p&gt;

&lt;h3&gt;Thread Pool과 Dispatcher의 미궁&lt;/h3&gt;

&lt;p&gt;적절한 Thread Pool과 함께라면 Non-blocking I/O가 효율적이라는 건 안다. 문제는 라이브러리마다 그 Thread Pool을 다르게 만든다는 사실이다.&lt;/p&gt;

&lt;table&gt;
    &lt;thead&gt;
        &lt;tr&gt;&lt;th&gt;라이브러리&lt;/th&gt;&lt;th&gt;Thread 모델&lt;/th&gt;&lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;&lt;td&gt;OkHttp&lt;/td&gt;&lt;td&gt;Executor를 외부에서 지정 가능&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;aws-sdk-kotlin&lt;/td&gt;&lt;td&gt;HTTP 엔진(OkHttp/CRT)에 따라 다름, 자체 디스패처 보유&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Lettuce&lt;/td&gt;&lt;td&gt;Netty 기반, 고정 크기 이벤트 루프&lt;/td&gt;&lt;/tr&gt;
        &lt;tr&gt;&lt;td&gt;Spring WebClient&lt;/td&gt;&lt;td&gt;Reactor Netty, JVM 시스템 속성으로 조정&lt;/td&gt;&lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&quot;라이브러리를 가져다 쓴다&quot;가 &quot;각 라이브러리의 스레딩 정책을 매번 학습한다&quot;로 변한다. 어느 한 곳에 잘못된 Blocking 호출이 끼면, 어느 풀이 막혀서 장애가 났는지 추적하는 데 한참이 걸린다.&lt;/p&gt;

&lt;h3&gt;스택 트레이스의 상실&lt;/h3&gt;

&lt;p&gt;JVM의 가장 큰 무기 중 하나는 스택 트레이스다. Coroutine은 이걸 부분적으로 잃는다. 호출 지점이 Thread를 건너뛰면서 진입점을 착각하기 쉽고, 디버거를 붙여도 더 이상 사용되지 않을 로컬 변수는 메모리 누수 방지를 위해 일찍 캡처에서 제외되어 보이지 않는다. 라이브 디버깅이 어렵다.&lt;/p&gt;

&lt;h3&gt;포기해야 했던 Spring의 편의&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@Transactional&lt;/code&gt;은 Spring 개발자에게 거의 공기 같은 것이다. Coroutine과는 오랜 시간 잘 맞지 않았다. Spring Framework 6.x에서 suspend 함수의 트랜잭션 컨텍스트 전파가 개선되긴 했지만, 여전히 반응형 트랜잭션 매니저(R2DBC 등) 조합이 전제되는 시나리오가 많고, JDBC + suspend 조합은 &lt;code&gt;TransactionalOperator&lt;/code&gt; 같은 우회 경로를 거쳐야 한다.&lt;/p&gt;

&lt;p&gt;HikariCP는 더 결정적이다. 수천 개의 가벼운 routine이 각자 Connection을 쥔 채 suspend할 수 있다면 풀에는 수천 개의 connection이 필요하다는 뜻인데, 데이터베이스 쪽에서 보면 시작도 못 할 이야기다. 솔직히 짚어 두자. 이건 동시성 모델의 죄라기보다는 RDB Connection이라는 자원 자체가 비싼 데서 오는 문제고, 뒤에서 보겠지만 VirtualThread로 옮겨 가도 같은 형태의 사이징 압력은 그대로 남는다. 다만 코루틴 환경에서는 거기에 더해 ThreadLocal 가정 충돌이라는 별개의 비호환이 얹힌다는 점이 무겁다. 그렇다고 R2DBC를 전사 표준으로 끌고 갈 것인가? 그건 또 다른 큰 결단이다.&lt;/p&gt;

&lt;h2&gt;그럼 대안은&lt;/h2&gt;

&lt;p&gt;탐색 기준은 세 가지였다. &lt;strong&gt;① 동시성 제어가 충분히 잘 되어야 한다. ② Kotlin + Spring MVC와 호환되어야 한다. ③ 앞서 본 문제들을 정말 해결해야 한다.&lt;/strong&gt; 두 번째 조건은 우리 팀의 제약이다. Spring MVC 스택 위에 쌓인 기능과 운영 노하우가 많아 WebFlux로 통째로 이주하는 비용이 정당화되지 않았다. WebFlux + Coroutine이 더 맞는 조직도 분명히 있을 것이고, 이 글의 결론을 거기에까지 일반화할 생각은 없다.&lt;/p&gt;

&lt;h3&gt;CompletableFuture&lt;/h3&gt;

&lt;p&gt;Java 표준이라 의존성이 없다는 장점은 분명하다. 그러나 가독성이 떨어지고, 예외 처리는 무시하기 쉽다. Coroutine을 택했던 두 가지 이유(읽기 쉬운 비동기 코드, 예외 처리의 명료함)를 모두 잃는다. 무엇보다 Blocking 라이브러리 문제는 그대로다.&lt;/p&gt;

&lt;h3&gt;Spring @Async&lt;/h3&gt;

&lt;p&gt;Spring과 잘 어우러지지만, 어노테이션 한 줄로 일어나는 마법은 Spring AOP의 self-invocation 문제를 동반한다. 그리고 함수에 &quot;색깔&quot;이 생긴다는 본질적인 문제는 &lt;code&gt;suspend&lt;/code&gt;와 동일하다. Bob Nystrom의 &lt;a href=&quot;https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/&quot;&gt;What Color is Your Function?&lt;/a&gt;이 지적한 그 문제다.&lt;/p&gt;

&lt;h3&gt;VirtualThread&lt;/h3&gt;

&lt;p&gt;Java 21에서 정식 도입된 경량 스레드. 작업을 표현하는 virtual thread와, 실제 실행을 담당하는 carrier thread가 분리되어 있고, Blocking I/O를 만나면 JVM이 알아서 carrier에서 unmount한다. Coroutine이 Dispatcher 풀에 스레드를 반납하는 것과 본질적으로 같은 원리다.&lt;/p&gt;

&lt;h2&gt;왜 VirtualThread인가&lt;/h2&gt;

&lt;h3&gt;러닝 커브가 거의 없다&lt;/h3&gt;

&lt;p&gt;정확히 말하면 &quot;일상 코드 작성의 러닝 커브가 거의 없다&quot;이다. &quot;Thread를 효율적으로 쓰고 싶다&quot;는 동기만 이해하면, 그 뒤로는 원래 Java/Kotlin 코드를 쓰던 방식 그대로 쓸 수 있다. 운영 관점의 학습 곡선(JFR 이벤트, ThreadLocal 캐싱 함정, 스레드 덤프 가독성, 잔존 핀닝 패턴 등)은 따로 있고, 이건 무시할 수 없다.&lt;/p&gt;

&lt;p&gt;공정하게 두자. 코루틴도 단순 &lt;code&gt;launch&lt;/code&gt;/&lt;code&gt;async&lt;/code&gt;까지의 일상 코드는 어렵지 않다. 어려운 건 그 뒤에 줄지어 등장하는 개념들(&lt;code&gt;CoroutineContext&lt;/code&gt;, &lt;code&gt;Scope&lt;/code&gt;, &lt;code&gt;Dispatcher&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt;의 결합, 예외 전파 분기)이다. 그러니 정확한 비교는 &quot;일상 vs 일상&quot;, &quot;운영/심화 vs 운영/심화&quot; 두 축을 나란히 놓는 쪽이다. 우리 환경에서는 두 축 모두 VirtualThread 쪽이 더 얕은 곡선을 그렸다. 단언이라기보다는 한 팀의 관찰이다. &lt;code&gt;CompletableFuture&lt;/code&gt;는 Thread Pool 이해를 강제하고, &lt;code&gt;@Async&lt;/code&gt;는 &quot;왜 이게 안 되지?&quot;라는 디버깅을 강제한다는 점에서도, VirtualThread는 그런 강요가 적다. Spring Boot에서는 &lt;code&gt;spring.threads.virtual.enabled=true&lt;/code&gt; 한 줄이 전환의 시작이다.&lt;/p&gt;

&lt;h3&gt;예외 전파가 단순해진다&lt;/h3&gt;

&lt;p&gt;적어도 단일 작업 수준에서는 그렇다. &lt;code&gt;CancellationException&lt;/code&gt;이 &lt;code&gt;IllegalStateException&lt;/code&gt;에 묻혀 사라지는 일이 없고, &lt;code&gt;supervisorScope + async&lt;/code&gt;의 지연 예외라는 함정도 없다. VirtualThread는 &lt;code&gt;Thread&lt;/code&gt;를 상속하므로(엄밀히는 &lt;code&gt;BaseVirtualThread&lt;/code&gt;를 거치지만) 기존 &lt;code&gt;interrupt&lt;/code&gt; 기반 취소 모델이 그대로 동작한다. 다만 여러 작업을 묶어 합성할 때 필요한 &lt;code&gt;StructuredTaskScope&lt;/code&gt;는 본문 뒤에서 보듯 아직 Preview이고, 거기까지 들어가면 코루틴의 합성 API와의 비교 우위가 단순하지 않다. &quot;예외 전파가 쉬워진다&quot;의 무게는 거기서 한 번 가벼워진다.&lt;/p&gt;

&lt;h3&gt;Blocking과 화해한다&lt;/h3&gt;

&lt;p&gt;Blocking I/O가 더는 &quot;잘못&quot;이 아니다. JVM이 carrier에서 unmount해 주기 때문에 Blocking 함수를 그대로 써도 처리량이 무너지지 않는다. Java 라이브러리의 내부 구현을 외워야 하는 부담이 사라진다. &lt;code&gt;Dispatchers.IO&lt;/code&gt;와 &lt;code&gt;Dispatchers.Default&lt;/code&gt;의 차이를 매번 의식할 필요도 없다.&lt;/p&gt;

&lt;p&gt;한 가지는 분명히 해 두자. VirtualThread가 함수의 &quot;색깔&quot; 문제를 푼 것은 아니다. 정확히는 색깔을 시그니처에서 보이지 않게 숨긴 것이다. blocking 호출이 platform thread 위에서 일어나면 carrier를 막고, virtual thread 위에서 일어나면 unmount되는 비대칭은 그대로 남아 있다. 단지 호출자에게 그 차이를 강요하지 않을 뿐이다. 그래서 운영자 관점에서는 &quot;어떤 컨텍스트에서 호출되는가&quot;를 여전히 신경 써야 하고, 이 점은 솔직히 인정해야 한다.&lt;/p&gt;

&lt;h3&gt;스택 트레이스가 돌아온다&lt;/h3&gt;

&lt;p&gt;VirtualThread의 스택 트레이스는 Platform Thread의 그것과 동일하다. 디버거에서 로컬 변수도 정상적으로 보인다. 운영 환경에서 사후 분석이 가능한 익숙한 세계로의 복귀다.&lt;/p&gt;

&lt;h3&gt;@Transactional, ThreadLocal이 정상 동작한다&lt;/h3&gt;

&lt;p&gt;VirtualThread도 결국 Thread다. &lt;code&gt;ThreadLocal&lt;/code&gt;이 정상 동작하므로 그 위에 쌓아 올린 Spring의 트랜잭션, 보안 컨텍스트, MDC 같은 인프라가 그대로 쓰인다. 이게 생각보다 굉장히 크다. 우리가 &quot;Coroutine을 위해서&quot; 포기했던 편의의 상당 부분이 자연스럽게 복귀한다.&lt;/p&gt;

&lt;h3&gt;Pinning은 이제 거의 해결됐다&lt;/h3&gt;

&lt;p&gt;VirtualThread 도입 시 가장 큰 걱정거리는 pinning이었다. &lt;code&gt;synchronized&lt;/code&gt; 블록 안에서 blocking I/O가 발생하면 carrier가 묶여 풀에서 빠지는 문제다. 이 중 &lt;code&gt;synchronized&lt;/code&gt; 케이스는 &lt;strong&gt;Java 24의 JEP 491&lt;/strong&gt;(&quot;Synchronize Virtual Threads without Pinning&quot;)에서 해소됐다. 다만 네이티브 호출(JNI/FFM), 클래스 초기화 중 블로킹 같은 잔존 케이스는 남아 있고, JFR의 &lt;code&gt;jdk.VirtualThreadPinned&lt;/code&gt; 이벤트를 모니터링하는 운영 노하우는 이제 새로 쌓아야 한다.&lt;/p&gt;

&lt;p&gt;HikariCP의 사례는 라이브러리 단의 분위기를 보여 주는 좋은 예다. PR #2055는 &lt;code&gt;synchronized&lt;/code&gt;를 &lt;code&gt;ReentrantLock&lt;/code&gt;으로 옮기자는 제안이었지만, 메인테이너의 입장은 &quot;HikariCP의 &lt;code&gt;synchronized&lt;/code&gt; 블록 안에서는 I/O를 하지 않으므로 핀닝 자체가 우리 문제는 아니다&quot;, &quot;&lt;code&gt;ReentrantLock&lt;/code&gt; 인스턴스의 할당과 GC 비용이 부담이다&quot;, &quot;JEP 491이 들어오면 자연히 풀린다&quot;는 쪽이었다. 즉 &quot;정답이 어려워서 멈췄다&quot;보다는 &quot;라이브러리 메인테이너가 변경의 필요성 자체를 받아들이지 않았다&quot;가 더 정직한 묘사다. 그 사이 JEP 491이 들어와 결과적으로 큰 짐을 덜었다.&lt;/p&gt;

&lt;p&gt;그리고 핀닝이 해소됐다고 VirtualThread가 무료는 아니다. &lt;code&gt;ConcurrentBag&lt;/code&gt;의 &lt;code&gt;ThreadLocal&lt;/code&gt; 캐싱은 VirtualThread 규모에서 잘 맞지 않고, 더 넓게는 ThreadLocal을 캐시로 쓰는 라이브러리들이 수만 개의 가상 스레드 환경에서 의도치 않은 메모리 압박을 만들 수 있다. 수천 개가 떠 있는 시점의 thread dump는 사람이 읽기 위해 설계된 출력이 아니다. 즉, VirtualThread도 자기 몫의 운영 학습 곡선이 분명히 있다. 다만 그 곡선이 Coroutine만큼 가파르지 않고, 일상적인 코드 작성 자체에까지 침투하는 종류는 아니라는 게 — 적어도 우리 환경에서는 — 내 판단이다.&lt;/p&gt;

&lt;h2&gt;그럼에도 Coroutine이 나은 영역&lt;/h2&gt;

&lt;p&gt;물론 VirtualThread가 모든 걸 이긴다고는 말 못한다. Coroutine이 여전히 강한 곳이 있다.&lt;/p&gt;

&lt;h3&gt;Flow와 Backpressure&lt;/h3&gt;

&lt;p&gt;전체 데이터를 한 번에 메모리에 올리지 않고 순차 처리하면서, 소비자 속도에 따라 생산자가 조절되는 backpressure가 언어 수준에서 지원된다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;fun streamAll() = channelFlow {
    CampaignTable
        .selectAll()
        // JDBC URL에 useCursorFetch=true 필요
        .fetchSize(1000)
        .forEach { send(it) }
}

suspend fun doConsume() {
    streamAll()
        .map { CampaignRepository.wrapRow(it) }
        .map { it.toValueObject() }
        .chunked(size = 200)
        .buffer(capacity = 20)
        .flatMapMerge(concurrency = 4) { chunk -&gt;
            flow { emit(batch(chunk)) }
        }
        .collect()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;같은 일을 &lt;code&gt;LinkedBlockingDeque&lt;/code&gt; + VirtualThread로 흉내 낼 수는 있지만, 코드 길이와 예외 전파 처리에서 차이가 분명하다.&lt;/p&gt;

&lt;h3&gt;구조화된 동시성&lt;/h3&gt;

&lt;p&gt;&quot;여러 작업 중 하나만 성공해도 나머지는 취소&quot;, &quot;하나 실패하면 전부 취소&quot;, &quot;모두 성공할 때까지 대기&quot; 같은 패턴을 Coroutine은 일찍부터 일급으로 제공해 왔다. JVM 진영에는 &lt;code&gt;StructuredTaskScope&lt;/code&gt;가 있지만 여전히 Preview 단계다. Java 22(JEP 462), Java 23(JEP 480), Java 24(JEP 499)까지 변경 없이 재프리뷰되던 API가 &lt;strong&gt;Java 25의 JEP 505에서 한 차례 크게 바뀌었다.&lt;/strong&gt; 공개 생성자가 정적 팩토리 메서드로 교체되고, &lt;code&gt;ShutdownOnSuccess&lt;/code&gt; / &lt;code&gt;ShutdownOnFailure&lt;/code&gt; 같은 서브클래스 모델이 &lt;code&gt;Joiner&lt;/code&gt; 기반 API로 옮겨졌다. 그리고 2026년 3월 GA된 Java 26의 JEP 525에서도 — 이번엔 작은 손질이지만 — &lt;code&gt;anySuccessfulResultOrThrow()&lt;/code&gt;가 &lt;code&gt;anySuccessfulOrThrow()&lt;/code&gt;로 개명되고, &lt;code&gt;allSuccessfulOrThrow()&lt;/code&gt;의 반환 타입이 스트림에서 리스트로 바뀌고, &lt;code&gt;Joiner.onTimeout()&lt;/code&gt;이 추가됐다. 표준화 전까지는 메이저 릴리스마다 코드를 살짝씩 고쳐 가며 따라가야 한다는 뜻이다.&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;// Java 26(JEP 525) 기준, --enable-preview 필요 — 다음 메이저에서 또 바뀔 가능성이 높다
val joiner = StructuredTaskScope.Joiner
    .anySuccessfulOrThrow&amp;lt;Int&amp;gt;() // Java 25에서는 anySuccessfulResultOrThrow()
val output = StructuredTaskScope.open(joiner).use {
    for (i in 1..3) {
        it.fork(Callable {
            Thread.sleep(i * 1000L)
            return@Callable i
        })
    }
    it.join()
}
println(output) // 1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;이 글의 예제 코드도 시간이 지나면 다시 손봐야 할 가능성이 높다. 그게 &quot;표준화 전까지는 메이저마다 따라가야 한다&quot;의 살아 있는 증거다.&lt;/p&gt;

&lt;h3&gt;Android는 선택지 자체가 다르다&lt;/h3&gt;

&lt;p&gt;엄밀히 말하면 Android는 &quot;Coroutine이 낫다&quot;가 아니라 &quot;사실상 Coroutine뿐&quot;인 자리다. Jetpack Compose의 &lt;code&gt;LaunchedEffect&lt;/code&gt;를 비롯해 현대 Android의 생명주기와 UI 동시성 모델이 Coroutine을 전제한다. Android는 D8 desugaring으로 일부 최신 Java 문법을 컴파일할 수 있지만, ART 런타임이 VirtualThread를 네이티브로 지원하지는 않는다. 서버에서 떠나도 클라이언트에서는 한동안 함께 가야 한다. 그래서 Android 팀이 있는 조직에서 &quot;Coroutine을 완전히 졸업한다&quot;는 표현은 과장이다. 서버 쪽 기본값을 옮긴다는 정도가 정확한 묘사다.&lt;/p&gt;

&lt;h2&gt;안녕, Coroutine&lt;/h2&gt;

&lt;p&gt;Coroutine이 우리에게 가르쳐 준 것은 분명히 크다. 비동기를 동기처럼 쓰는 경험, 구조화된 동시성, Flow의 우아함. 이 글에서 다룬 단점들이 Coroutine의 전부도 아니고, 우리가 Coroutine으로부터 얻은 것들이 작별 후에 사라지는 것도 아니다.&lt;/p&gt;

&lt;p&gt;다만 서버 사이드에서 우리가 &lt;em&gt;일상적으로 마주하는 트레이드오프&lt;/em&gt;를 다시 계산해 보면, 2026년의 답은 더 이상 자동으로 Coroutine이 아니다. JVM이 일찍이 잘하던 일들(스택 트레이스, ThreadLocal, &lt;code&gt;@Transactional&lt;/code&gt;, 익숙한 디버깅 워크플로)을 포기하지 않고도, Thread 효율이라는 원래의 동기를 충족하는 길이 생겼기 때문이다. 그렇다고 답이 자동으로 VirtualThread라는 것도 아니다. Flow의 흐름 처리와 구조화된 취소가 더 자연스러운 자리가 있고, Android는 선택지가 다르며, WebFlux 위에 서 있는 조직은 또 다른 결정에 도달할 것이다.&lt;/p&gt;

&lt;p&gt;그래서 우리는 새 서비스에서는 VirtualThread를 &lt;em&gt;기본 후보&lt;/em&gt;로 두기 시작했다. 무조건이라는 뜻이 아니라, &quot;Kotlin이니까 Coroutine&quot;이라는 디폴트를 한 번 멈춰 세웠다는 의미다.&lt;/p&gt;

&lt;p&gt;이런 결정이 &quot;그럼 Kotlin을 왜 쓰느냐&quot;로 이어지지는 않는다. null safety, data class, 확장 함수, sealed 계층, 표현력 있는 컬렉션 API — Kotlin이 Coroutine을 빼고도 우리에게 주는 것이 많다. 이번 결정은 Kotlin과의 작별이 아니라, Kotlin 안에서 동시성 도구만 바꾸는 일이다.&lt;/p&gt;

&lt;p&gt;안녕, Coroutine. 미워서 보내는 게 아니라, 이제는 매번 검토해 결정해야 할 만큼 선택지가 늘었기 때문이다.&lt;/p&gt;</description>
      <category>Kotlin</category>
      <category>coroutine</category>
      <category>HikariCP</category>
      <category>Java</category>
      <category>jvm</category>
      <category>Kotlin</category>
      <category>Project Loom</category>
      <category>spring boot</category>
      <category>StructuredTaskScope</category>
      <category>virtualthread</category>
      <category>동시성</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/84</guid>
      <comments>https://nodealdev.tistory.com/84#entry84comment</comments>
      <pubDate>Sun, 17 May 2026 02:52:55 +0900</pubDate>
    </item>
    <item>
      <title>ETF 배당소득세 계산기 - 기타 ETF 보유기간과세, 과표기준가 자동계산</title>
      <link>https://nodealdev.tistory.com/83</link>
      <description>&lt;p style=&quot;font-size: 0.9em; color: #888; font-style: italic; margin-bottom: 1.5em;&quot; data-ke-size=&quot;size16&quot;&gt;기타 ETF(국내 상장 해외주식형&amp;middot;채권형&amp;middot;원자재형&amp;middot;레버리지 등)의 보유기간과세 배당소득세를 매수가, 매도가, 과표기준가만 입력하면 자동으로 계산. 과표증분 계산 방식과 예시까지 정리.&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://nodeal.github.io/etf-tax/&quot; title=&quot;ETF 배당소득세 계산기&quot; width=&quot;100%&quot; height=&quot;1080&quot; loading=&quot;lazy&quot; referrerpolicy=&quot;no-referrer-when-downgrade&quot; style=&quot;border:0; max-width:100%;&quot; frameborder=&quot;0&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※ 본 계산기는 삼성자산운용 및 관련 운용사와 무관한 개인 제작 도구. 결과값은 추정치이며 실제 원천징수 금액과 다를 수 있음.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;ETF 배당소득세란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ETF는 한국거래소에 상장되어 있어 주식처럼 거래되지만, 세법상으로는 &lt;b&gt;신탁형 펀드&lt;/b&gt;로 분류됨. 그래서 일반 주식과는 다르게 매매차익이나 분배금에 &lt;b&gt;배당소득세 15.4%(소득세 14% + 지방소득세 1.4%)&lt;/b&gt;가 부과될 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내에 상장된 ETF는 크게 두 가지로 나뉨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내주식형 ETF (예: KODEX 200, TIGER 200)&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;분배금: 15.4% 배당소득세&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기타 ETF (국내 상장 해외주식형&amp;middot;채권형&amp;middot;원자재형&amp;middot;레버리지/인버스 등, 그리고 국내주식형이 &lt;em&gt;아닌&lt;/em&gt; TR&amp;middot;액티브)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매매차익: 15.4% 배당소득세 (보유기간과세)&lt;/li&gt;
&lt;li&gt;분배금: 보유기간과세 방식으로 과세 (아래 주의사항 참고)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 계산기는 매매차익에 세금이 붙는 &lt;b&gt;&quot;기타 ETF&quot;&lt;/b&gt;의 보유기간과세 금액을 추정하기 위한 것. (국내주식형 ETF는 매매차익이 비과세라 계산할 필요 없음.)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보유기간과세 계산 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기타 ETF의 매매차익에 부과되는 배당소득세는 단순히 &quot;매매차익 &amp;times; 15.4%&quot;가 아님. 다음 두 값 중 &lt;b&gt;더 작은 금액&lt;/b&gt;에 15.4%를 곱해서 계산함.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;실제 매매차익&lt;/b&gt; = (매도가 &amp;minus; 매수가) &amp;times; 수량&lt;/li&gt;
&lt;li&gt;&lt;b&gt;과표증분&lt;/b&gt; = (매도일 과표기준가 &amp;minus; 매수일 과표기준가) &amp;times; 수량&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 과세표준 = min(매매차익, 과표증분), 세금 = 과세표준 &amp;times; 15.4%.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단, &lt;strong&gt;매매차익이 음수(손실)이면 과세표준은 0원&lt;/strong&gt;으로 처리되어 보유기간과세는 발생하지 않음. 손실 거래에는 세금이 붙지 않는다는 뜻.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;과표기준가란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과표기준가(과세표준 기준가격)는 ETF 수익 중 과세 대상이 되는 부분만 따로 계산해 둔 기준가격. 시장 거래가격과는 다르며, 매일 자산운용사가 산정해 공시함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증권사 MTS나 운용사 홈페이지에서 종목별로 확인 가능. KODEX는 &lt;a href=&quot;https://www.samsungfund.com/etf/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼성자산운용 ETF 페이지&lt;/a&gt;, TIGER는 &lt;a href=&quot;https://www.tigeretf.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미래에셋 TIGER ETF 페이지&lt;/a&gt;에서 일자별 과세기준가를 엑셀로 받을 수 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매매차익이 났더라도 과표증분이 더 작다면, 그 작은 금액만큼만 세금이 부과되기 때문에 실제 매매차익 기준으로 단순 계산하면 세금이 과대 추정될 수 있음.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;계산 예시&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10,000원에 100주 매수, 11,000원에 100주 매도한 경우.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 매매차익: (11,000 &amp;minus; 10,000) &amp;times; 100 = &lt;b&gt;100,000원&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;매수일 과표기준가: 9,800원 / 매도일 과표기준가: 10,500원&lt;/li&gt;
&lt;li&gt;과표증분: (10,500 &amp;minus; 9,800) &amp;times; 100 = &lt;b&gt;70,000원&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 둘 중 작은 금액인 &lt;b&gt;70,000원&lt;/b&gt;이 과세표준&lt;br /&gt;&amp;rarr; 배당소득세 = 70,000 &amp;times; 15.4% = &lt;b&gt;10,780원&lt;/b&gt;&lt;br /&gt;&amp;rarr; 세후 실현손익 = 100,000 &amp;minus; 10,780 = &lt;b&gt;89,220원&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 과표증분이 매매차익보다 컸다면 매매차익(100,000원)이 과세표준이 됨. 항상 &lt;b&gt;둘 중 작은 쪽&lt;/b&gt;이 기준이라는 점만 기억.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;계산기 사용 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 계산기에 다음 값을 입력 (UI에 표시되는 순서대로).&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;strong&gt;매수 정보&lt;/strong&gt;: 매수 가격(원) &amp;rarr; 매수일 과표기준가 &amp;rarr; 매수 수량&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;매도 정보&lt;/strong&gt;: 매도 가격(원) &amp;rarr; 매도일 과표기준가 &amp;rarr; 매도 수량&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;/li&gt;
&lt;li&gt;과표증분&lt;/li&gt;
&lt;li&gt;과세표준 (둘 중 작은 값)&lt;/li&gt;
&lt;li&gt;배당소득세 (15.4%)&lt;/li&gt;
&lt;li&gt;세후 실현손익&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&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;b&gt;증권사 MTS / HTS&lt;/b&gt;: 종목 상세 화면에서 &quot;과표기준가&quot; 또는 &quot;기준가&quot; 항목 확인&lt;/li&gt;
&lt;li&gt;&lt;b&gt;운용사 홈페이지&lt;/b&gt;: &lt;a href=&quot;https://www.samsungfund.com/etf/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼성자산운용 ETF 페이지&lt;/a&gt;, &lt;a href=&quot;https://www.tigeretf.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미래에셋 TIGER ETF 페이지&lt;/a&gt;, KB자산운용 등 종목 페이지 내 일자별 기준가 공시&lt;/li&gt;
&lt;li&gt;&lt;b&gt;KRX 정보데이터시스템&lt;/b&gt;(data.krx.co.kr): ETF별 과세표준 기준가격 조회&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;대표 종목별 계산 예시&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;KODEX 레버리지 (122630)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;국내 파생형 ETF로 기타 ETF 분류. 매수일 과표기준가 10,125.32원, 매도일 10,141.05원이면 과표증분은 15.73원. 매수 100주를 13,800원에 사서 14,250원에 매도(차익 45,000원)했다면 과세표준은 1,573원이 되어 배당소득세는 약 242원.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TIGER 미국S&amp;amp;P500 (360750)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해외주식형 ETF로 기타 ETF 분류. S&amp;amp;P 500 지수를 기초자산으로 함. NAV에는 기초지수 수익뿐 아니라 원/달러 환율 변동도 반영되므로, 과표증분이 단순 시장 차익과 일치하지 않는 경우가 일반적임.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;TIGER 미국나스닥100 (133690)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NASDAQ 100 지수 추종 해외주식형. 미국 기술주 중심이라 시세 변동이 큰 편이지만 과세 방식은 동일하게 보유기간과세.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;KODEX 200선물인버스2X (252670)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코스피200 선물 지수의 일간 &amp;minus;2배를 추종하는 국내 파생형 인버스. 하락 베팅으로 발생한 차익도 동일하게 15.4% 보유기간과세 대상.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&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;b&gt;추정치&lt;/b&gt;. 실제 원천징수 금액은 증권사 시스템에서 산정되며, 환율&amp;middot;중도 분배&amp;middot;세제 개정 등에 따라 달라질 수 있음.&lt;/li&gt;
&lt;li&gt;분배금 과세는 별도 메커니즘으로 처리됨. 기타 ETF의 분배금도 보유기간과세 방식이 적용되지만, 비교 대상이 되는 과표증분은 &lt;em&gt;분배 시점 NAV 과표와 매수 시점 NAV 과표의 차이&lt;/em&gt;라 매매 차익과는 산식이 다름. 본 계산기는 매매 시점의 차익만 다루며 분배금 세액은 계산하지 않음.&lt;/li&gt;
&lt;li&gt;보유기간과세는 매도 시점에 15.4% 분리과세로 종결됨. 다만 연간 이자&amp;middot;배당 등 금융소득의 합이 2,000만 원을 넘으면 종합과세 대상이 되어 다른 종합소득과 합산해 누진세율(지방세 포함 최고 49.5%)로 다시 계산될 수 있음. &lt;strong&gt;이 누진세율은 본 계산기의 15.4% 결과 위에 자동으로 더해지는 게 아니라 종합소득 신고 시 별도로 산정되는 값&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;해외 거래소에 직접 상장된 ETF&lt;/b&gt;(예: SPY, QQQ 등)는 보유기간과세가 아니라 &lt;b&gt;양도소득세 22%&lt;/b&gt;(연 250만 원 공제) 대상이라 본 계산기와는 무관함.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;연금저축계좌&amp;middot;IRP&amp;middot;ISA&lt;/b&gt; 등 절세계좌에서 보유한 ETF는 과세 시점과 세율이 달라짐.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세제는 자주 바뀌니, 매도 시점의 최신 세법과 증권사 안내를 함께 확인하는 것을 권장.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&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://www.samsungfund.com/etf/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼성자산운용 KODEX&lt;/a&gt; &amp;mdash; 종목별 일자별 과표기준가 공시&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tigeretf.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;미래에셋자산운용 TIGER&lt;/a&gt; &amp;mdash; 종목별 일자별 과표기준가 공시&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://data.krx.co.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;KRX 정보데이터시스템&lt;/a&gt; &amp;mdash; ETF별 과세표준 기준가격 조회&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.krx.co.kr/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;한국거래소&lt;/a&gt; &amp;mdash; ETF 시장 안내 및 세제 정보&lt;/li&gt;
&lt;/ul&gt;
&lt;script type=&quot;application/ld+json&quot;&gt;
{
  &quot;@context&quot;: &quot;https://schema.org&quot;,
  &quot;@type&quot;: &quot;SoftwareApplication&quot;,
  &quot;name&quot;: &quot;ETF 배당소득세 계산기&quot;,
  &quot;description&quot;: &quot;국내 상장 기타 ETF(KODEX 레버리지, TIGER 미국S&amp;P500, TIGER 미국나스닥100, KODEX 200선물인버스2X 등) 매매차익에 부과되는 보유기간과세 배당소득세 15.4%를 매수가·매도가·과표기준가만 입력하면 자동 계산합니다.&quot;,
  &quot;applicationCategory&quot;: &quot;FinanceApplication&quot;,
  &quot;operatingSystem&quot;: &quot;Any&quot;,
  &quot;url&quot;: &quot;https://blog.hcmc.studio/83&quot;,
  &quot;browserRequirements&quot;: &quot;JavaScript&quot;,
  &quot;inLanguage&quot;: &quot;ko-KR&quot;,
  &quot;offers&quot;: { &quot;@type&quot;: &quot;Offer&quot;, &quot;price&quot;: &quot;0&quot;, &quot;priceCurrency&quot;: &quot;KRW&quot; },
  &quot;creator&quot;: { &quot;@type&quot;: &quot;Person&quot;, &quot;name&quot;: &quot;nodealdev&quot; }
}
&lt;/script&gt;</description>
      <category>일상</category>
      <category>ETF</category>
      <category>기타 ETF</category>
      <category>배당소득세</category>
      <category>주식</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/83</guid>
      <comments>https://nodealdev.tistory.com/83#entry83comment</comments>
      <pubDate>Fri, 15 May 2026 18:53:45 +0900</pubDate>
    </item>
    <item>
      <title>Ktor Pipeline, PipelinePhase</title>
      <link>https://nodealdev.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Pipeline을 갖는 component는 Engine, Application, Routing, Route가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 component의 기본 phase는 다음이 있다:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 1054px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;Engine&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;EnginePipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationReceivePipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationSendPipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;ContentEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;TransferEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;Application&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationCallPipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationReceivePipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;AfterTransform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationSendPipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;BodyTransformationCheckPostRender&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;ContentEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;TransferEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;Routing&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationCallPipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationReceivePipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationSendPipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;ContentEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;TransferEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;Route&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationCallPipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Setup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationReceivePipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;ApplicationSendPipeline&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Before&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Transform&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;ContentEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;TransferEncoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;After&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 12.5581%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25.5814%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 61.8605%; height: 17px;&quot;&gt;Engine&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청이 들어왔을 때 실행 순서는 다음과 같다:&lt;/p&gt;
&lt;pre id=&quot;code_1693040743288&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;before: Engine
Setup: Application
Monitoring: Application
Plugins: Application
Call: Application
Setup: Routing &amp;gt; Route
Monitoring: Routing &amp;gt; Route
Plugins: Routing &amp;gt; Route
Call: Routing &amp;gt; Route
// Route body 시작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route body에서 receive를 호출하면 ApplicationReceivePipeline이 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1693041031422&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Before: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
Transform: Engine &amp;gt; (DefaultTransform) &amp;gt; Application &amp;gt; (ContentNegotiation) &amp;gt; Routing &amp;gt; Route
AfterTransform: Application
After: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
// receive 끝&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;respond를 호출하면 ApplicationSendPipeline이 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1693041297046&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Before: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
Transform: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
Render: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
BodyTransformationCheckPostRender: Application
ContentEncoding: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
TransferEncoding: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
After: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route
Engine: Engine &amp;gt; Application &amp;gt; Routing &amp;gt; Route&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ApplicationSendPipeline은 finish를 호출하거나 respond없이 route body가 종료될 때에도 호출된다.&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;Route body가 종료되면 ApplicationCallPipeline.Fallback이 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1693041340941&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Fallback: Routing &amp;gt; Route &amp;gt; Application&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 요청 처리가 모두 완료될 때 EnginePipeline의 나머지 phase가 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1693041446577&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Engine: call &amp;gt; After
// 요청 끝&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kotlin</category>
      <category>2.3.3</category>
      <category>Application</category>
      <category>engine</category>
      <category>Kotlin</category>
      <category>ktor</category>
      <category>phase</category>
      <category>pipeline</category>
      <category>PipelineContext</category>
      <category>route</category>
      <category>Routing</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/82</guid>
      <comments>https://nodealdev.tistory.com/82#entry82comment</comments>
      <pubDate>Sat, 26 Aug 2023 18:21:59 +0900</pubDate>
    </item>
    <item>
      <title>Nullable value class: Long? long?</title>
      <link>https://nodealdev.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;JPA를 이용하거든 그렇지 않든 object의 ID를 Java primitive type으로만 관리하는 것은 여간 귀찮은 일이 아니다. ID를 사용한 조회에서 Long만 여러개 전달하는 상황은 충분한 문서화가 이뤄지지 않은 상황에서 어떤 의도하지 않은 상황으로 이어질지 모른다. 그래서 Kotlin value class를 사용하여 ID를 wrap하여 Java에서는 unwrap된 타입으로 사용되길 기대하고 사용하는데...&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;다음 예시를 보자. JPA Entity에 value class ID를 사용한 모습이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-06 오후 11.40.08.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ouOS6/btsdZ0f6ZsI/JTFrLar4eIsWMRkpbDv0UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ouOS6/btsdZ0f6ZsI/JTFrLar4eIsWMRkpbDv0UK/img.png&quot; data-alt=&quot;FooEntity.kt&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ouOS6/btsdZ0f6ZsI/JTFrLar4eIsWMRkpbDv0UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FouOS6%2FbtsdZ0f6ZsI%2FJTFrLar4eIsWMRkpbDv0UK%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-06 오후 11.40.08.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FooEntity.kt&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까진 아무런 문제가 없다. JpaRepository를 사용하더라도 ID에 unwrap된 type을 넣어준다면 findById까지 완벽히 작동한다. 이때, table `foo`를 참조하는 table `bar`가 있고 이게 optional할 때를 가정하고 BarEntity를 작성해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-06 오후 11.45.20.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mswgm/btsd0uOuhyt/Aa3H64cu7LcvCyQ2KofXl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mswgm/btsd0uOuhyt/Aa3H64cu7LcvCyQ2KofXl1/img.png&quot; data-alt=&quot;BarEntity.kt&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mswgm/btsd0uOuhyt/Aa3H64cu7LcvCyQ2KofXl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmswgm%2Fbtsd0uOuhyt%2FAa3H64cu7LcvCyQ2KofXl1%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-06 오후 11.45.20.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BarEntity.kt&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 표시하듯 FooId가 nullable일 때 Basic 타입 추론을 실패하여 오류로 취급한다. Inspection 수준에서 처리할 수 있도록 해주는 것이다. 여기까지만 보면 적당히 Entity에서는 primitive type으로 사용하면 될 것 같아보인다. 하지만 FooId가 IdWrapper를 상속받는 클래스였다면 어떻게 될까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-06 오후 11.54.50.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brDKyJ/btsd3eEqNLH/X6Rgl2LLwNaTt9ZPqLNY1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brDKyJ/btsd3eEqNLH/X6Rgl2LLwNaTt9ZPqLNY1K/img.png&quot; data-alt=&quot;FooEntity.kt: IdWrapper를 상속받은 FooId&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brDKyJ/btsd3eEqNLH/X6Rgl2LLwNaTt9ZPqLNY1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrDKyJ%2Fbtsd3eEqNLH%2FX6Rgl2LLwNaTt9ZPqLNY1K%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-06 오후 11.54.50.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;FooEntity.kt: IdWrapper를 상속받은 FooId&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.19.46.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dNouoz/btsd0Rv9HBV/MxqApgPqbsvjvfNzrszask/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dNouoz/btsd0Rv9HBV/MxqApgPqbsvjvfNzrszask/img.png&quot; data-alt=&quot;BarEntity.kt: FooId의 오류가 사라졌다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dNouoz/btsd0Rv9HBV/MxqApgPqbsvjvfNzrszask/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdNouoz%2Fbtsd0Rv9HBV%2FMxqApgPqbsvjvfNzrszask%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.19.46.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BarEntity.kt: FooId의 오류가 사라졌다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 문제가 발생한다. 어차피 대부분의 경우에서 IdWrapper같은 interface를 활용하여 정렬같은 extension을 잔뜩 넣어줄 생각으로 구조를 만들었을텐데 FooId가 아무런 interface도 상속받지 않는다는 것이 오히려 어색하다. 하지만 이렇게 다른 interface를 상속받는 것만으로 inspection의 오류는 사라졌다. 정말 오류가 해결되었을까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.21.00.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kWjnl/btsecu0uspb/KZRSdJI08sl7iOC4bdgQKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kWjnl/btsecu0uspb/KZRSdJI08sl7iOC4bdgQKK/img.png&quot; data-alt=&quot;Test.kt: 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kWjnl/btsecu0uspb/KZRSdJI08sl7iOC4bdgQKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkWjnl%2Fbtsecu0uspb%2FKZRSdJI08sl7iOC4bdgQKK%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.21.00.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Test.kt: 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Table `bar`의 insert SQL 2번 parameter가 FooId로 전달된다. 따라서 hibernate가 VARBINARY로 인식하고 적어도 BIGINT는 아닐 것 같은 값을 삽입하려고 시도한다. 왜 이런 오류가 발생할까? 예상할 수 있듯 FooId가 unwrap되지 않은 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.24.21.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZNyot/btsd1i1npE5/ZEJQLwN4Rc9qtbxyAKtUdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZNyot/btsd1i1npE5/ZEJQLwN4Rc9qtbxyAKtUdK/img.png&quot; data-alt=&quot;Decompile된 BarEntity&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZNyot/btsd1i1npE5/ZEJQLwN4Rc9qtbxyAKtUdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZNyot%2Fbtsd1i1npE5%2FZEJQLwN4Rc9qtbxyAKtUdK%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.24.21.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Decompile된 BarEntity&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fooId를 FooId로 가지고 있다. 이 때문에 JPA는 FooId라는 클래스로 인식하고 객체를 통째로 넣는 시도를 하는 것이다. value class는 분명 unwrap되어야 할텐데 Java primitive type에는 null을 사용할 수 없다는 문제에서 기인하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.30.07.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9bSV8/btsd6uzZTmY/Jqu60E2fYxdE9UJanw1iq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9bSV8/btsd6uzZTmY/Jqu60E2fYxdE9UJanw1iq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9bSV8/btsd6uzZTmY/Jqu60E2fYxdE9UJanw1iq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9bSV8%2Fbtsd6uzZTmY%2FJqu60E2fYxdE9UJanw1iq0%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.30.07.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value class는 unboxing 과정에서 primitive, nullable일 때 class boxing이 작동하지 않는 것을 확인할 수 있다. 적어도 Long?은 @Nullable java.lang.Long으로 변환되는 것을 보면 언젠가 해결될 수 있는 문제라고 기대된다. 그럼에도&amp;nbsp;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Kotlin 수준에서 해결되기 전까지는 불편하더라도 다른 방법을 사용해야 할 것 같다.&lt;/span&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;1. Entity에는 value class를 사용하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 모두 지키기는 어려우니 적어도 nullable에는 value class를 적용하지 않는다. 대부분의 경우에서 Entity는 형식을 정의하는 다른 interface를 상속받고 이 interface에서 value class를 사용할 것이다. 그렇다면 interface를 상속받았을 때 부모 타입을 만족하며 작성하는 방법이 필요할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.34.39.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfuguP/btsect8lM82/QcGcEvEfRMRVhHPBEZOMrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfuguP/btsect8lM82/QcGcEvEfRMRVhHPBEZOMrK/img.png&quot; data-alt=&quot;BarEntity.kt: primitiveFooId&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfuguP/btsect8lM82/QcGcEvEfRMRVhHPBEZOMrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfuguP%2Fbtsect8lM82%2FQcGcEvEfRMRVhHPBEZOMrK%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;3840&quot; height=&quot;2160&quot; data-filename=&quot;스크린샷 2023-05-07 오전 12.34.39.png&quot; data-origin-width=&quot;3840&quot; data-origin-height=&quot;2160&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BarEntity.kt: primitiveFooId&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;primitiveFooId로 JPA에 매핑한 다음 직렬화를 막기 위해 Transient annotation을 달았다. 이전처럼 직렬화되면 fooId가 들어갈 것이다.&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. 직접 UserType을 작성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어렵고 험한 길이 될 것이다. JPA는 기본 타입 이외의 형식을 지원하기 위해 UserType을 상속받은 클래스를 사용하여 직접 작성할 수 있도록 한다. 하지만 유일한  concrete type일 때에는 충분히 가능성이 있으나 IdWrapper같은 interface를 사용하여 전체를 매핑할 방법은 없다. 번거롭더라도 각 ID에 대해 등록해준다면 Entity 수준에서 다른 처리없이 값이 대응될 것이다.&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;3. Entity는 그냥 Java로 작성하자.&lt;/p&gt;</description>
      <category>Kotlin</category>
      <category>boxing</category>
      <category>Entity</category>
      <category>inline class</category>
      <category>Java</category>
      <category>JPA</category>
      <category>Kotlin</category>
      <category>unboxing</category>
      <category>value class</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/81</guid>
      <comments>https://nodealdev.tistory.com/81#entry81comment</comments>
      <pubDate>Sun, 7 May 2023 00:41:30 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin/JVM Vector, ArrayList, Mutex 수행 시간</title>
      <link>https://nodealdev.tistory.com/79</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;결과&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;싱글 스레드 환경에서 Vector는 ArrayList 대비 약 6배의 시간이 소요된다.&lt;/li&gt;
&lt;li&gt;멀티 스레드 환경에서 Vector는 synchronized ArrayList 대비 약 1.3배의 시간이 소요된다.&lt;/li&gt;
&lt;li&gt;멀티 스레드 환경에서 synchronized ArrayList은 Mutex.withLock ArrayList 대비 약 2.2배의 시간이 소요된다.&lt;/li&gt;
&lt;li&gt;Vector 대신 ArrayList를 사용하려면 반드시 원자적인 연산만 수행해야 한다.&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java에는 배열을 사용한 List 구현체로 Vector와 ArrayList가 있다.&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;Vector는 모든 add, get, set이 synchronized로 작동하는 점이 ArrayList와 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vector와 ArrayList와 충분한 capacity만 확보되어 있다면 random access와 \(O(1)\)의 시간복잡도로 tail insert가 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vector는 모든 동작에 synchronized 키워드가 사용되어 속도에 영향이 있다고 한다. 대부분의 경우에서 모든 동작이 synchronized할 이유는 없다고 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Vector synchronizes on each individual operation. That's almost never what you want to do.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;&lt;a href=&quot;https://stackoverflow.com/questions/1386275/why-is-java-vector-and-stack-class-considered-obsolete-or-deprecated&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Stack OverFlow: Why&amp;nbsp;is&amp;nbsp;Java&amp;nbsp;Vector&amp;nbsp;(and&amp;nbsp;Stack)&amp;nbsp;class&amp;nbsp;considered&amp;nbsp;obsolete&amp;nbsp;or&amp;nbsp;deprecated?&lt;/a&gt;&lt;/i&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; 예를 들어 더 이상 수정이 발생하지 않는 상황에서 읽기 접근만 존재한다면 각 thread가 모두 비동기적으로 접근하는 편이 훨씬 유리할 것이다. 하지만 지속적으로 내용 수정이 수행되는 상황에서 읽기가 수행되어야 한다면 적절한 동기화가 필요하므로 일일히 synchronized 블럭을 선언하는 것도 번거로울 뿐더러 교착 상태의 위험까지 수반될 수 있다.&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;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;싱글스레드&lt;br /&gt;- N=데이터의 수&lt;br /&gt;- 각 자료구조에 N번 add의 시간을 측정한다.&lt;br /&gt;- N개의 데이터가 삽입되어 있는 각 자료구조에 N번 get의 시간을 측정한다.&lt;/li&gt;
&lt;li&gt;멀티스레드&lt;br /&gt;- N=데이터의 수, M=Thread의 수&lt;br /&gt;- 각 자료구조에 N * M번 add의 시간을 측정한다. 이때, Vector는 직접 add를 호출하고 ArrayList는 synchronized 블럭에서 add를 수행한다.&lt;br /&gt;- N개의 데이터가 삽입되어 있는 각 자료구조에 N * M번 get의 시간을 측정한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;테스트&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N=10,000,000, 단위: ms&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 274px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 19px; text-align: center;&quot;&gt;시행&lt;/td&gt;
&lt;td style=&quot;width: 326.594px; height: 19px; text-align: center;&quot; colspan=&quot;2&quot;&gt;Vector&lt;/td&gt;
&lt;td style=&quot;width: 326.609px; height: 19px; text-align: center;&quot; colspan=&quot;2&quot;&gt;ArrayList&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; text-align: center; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;282&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;199&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;119&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;259&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;198&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;113&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;225&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;183&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;116&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;33&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;253&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;165&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;111&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;251&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;169&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;113&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;254&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;171&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;117&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;263&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;177&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;114&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;246&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;180&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;116&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;34&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;244&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;173&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;109&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;210&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;181&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;122&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;MIN&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;210&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;165&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;109&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;MAX&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;280&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;199&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;122&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;AVERAGE&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;248.7&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;179.6&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;115&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;33.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;MEDIAN&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;252&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;178.5&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;115&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;31.5&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N=100,000,000, 단위: ms&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 257px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 19px; text-align: center;&quot;&gt;시행&lt;/td&gt;
&lt;td style=&quot;width: 326.594px; height: 19px; text-align: center;&quot; colspan=&quot;2&quot;&gt;Vector&lt;/td&gt;
&lt;td style=&quot;width: 326.609px; height: 19px; text-align: center;&quot; colspan=&quot;2&quot;&gt;ArrayList&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 154.797px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;3239&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1853&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1513&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;373&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;3051&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1848&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1454&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;319&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2607&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1838&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1491&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;313&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2671&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1807&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1563&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;299&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2544&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1798&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1575&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;322&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2500&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1831&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1613&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;322&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2512&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1801&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1559&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;291&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2571&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1842&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1573&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;297&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2520&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1757&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1458&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;286&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2504&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1734&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1469&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;298&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;MIN&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2500&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1734&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1454&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;286&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;MAX&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;3239&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1853&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1613&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;373&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;AVERAGE&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2671.9&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1810.9&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1528.8&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;312&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 154.797px; height: 17px; text-align: center;&quot;&gt;MEDIAN&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;2557.5&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1819&lt;/td&gt;
&lt;td style=&quot;width: 154.797px; height: 17px;&quot;&gt;1536&lt;/td&gt;
&lt;td style=&quot;width: 154.812px; height: 17px;&quot;&gt;306&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 먼저 싱글 스레드에서 실행 결과다. 두 자료구조 모두 충분한 capacity를 할당한 후 삽입한다면 데이터 수에 비례한 시간 소요를 보였다(\(=O(N)\)). 예상할 수 있듯 Vector는 모두 synchronized 작동을 수행하므로 싱글 스레드에서 압도적으로 불리한 결과를 보인다.&amp;nbsp; 그렇지만&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Vector에서 임의 접근하는 데에 소요되는 시간이 ArrayList에 삽입하는 시간보다도 오래 걸리는 것을 확인할 수 있다&lt;/span&gt;. ArrayList의 get은 사실상 배열의 get과 call stack의 차이를 제외하고는 같으므로 어디에나 사용할 수 있는 충분히 적은 시간으로 완료되는 것을 알 수 있었다. 어떤 자료구조가 단순히 임의 접근이 가능하다는 사실만으로 시간 복잡도의 계수를 무시하고 '어떤 thread에서나 동기화된다'는 장점만으로 접근하는 것을 시작으로 약 6배의 시간 차이를 받아들이기는 쉽지 않을 것으로 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N=1,000,000, M=8, 단위: ms&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 238px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;시행&lt;/td&gt;
&lt;td style=&quot;width: 28.5714%; height: 17px; text-align: center;&quot; colspan=&quot;2&quot;&gt;Vector&lt;/td&gt;
&lt;td style=&quot;width: 28.5714%; height: 17px; text-align: center;&quot; colspan=&quot;2&quot;&gt;ArrayList(synchronized)&lt;/td&gt;
&lt;td style=&quot;width: 28.5714%; height: 17px; text-align: center;&quot; colspan=&quot;2&quot;&gt;ArrayList(Mutex)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;866&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;888&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;644&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;672&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;279&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;191&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;546&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;701&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;504&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;641&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;228&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;163&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;711&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;408&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;461&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;416&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;249&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;167&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;650&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;624&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;443&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;423&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;228&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;168&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;806&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;677&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;444&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;410&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;236&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;163&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;534&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;711&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;447&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;423&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;228&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;171&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;871&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;625&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;462&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;412&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;241&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;165&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;538&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;626&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;452&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;416&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;232&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;165&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;536&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;793&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;445&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;423&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;237&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;174&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;483&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;513&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;443&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;401&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;230&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;166&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;MIN&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;483&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;408&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;443&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;401&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;228&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;163&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.2857%; text-align: center;&quot;&gt;MAX&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;871&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;888&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;644&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;672&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;279&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%;&quot;&gt;191&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;AVERAGE&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;654.1&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;656.6&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;474.5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;463.7&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;238.8&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;169.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;MEDIAN&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;598&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;651.5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;449.5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;419.5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;234&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;166.5&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N=10,000,000, M=8&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 272px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;시행&lt;/td&gt;
&lt;td style=&quot;width: 28.5714%; height: 17px; text-align: center;&quot; colspan=&quot;2&quot;&gt;Vector&lt;/td&gt;
&lt;td style=&quot;width: 28.5714%; height: 17px; text-align: center;&quot; colspan=&quot;2&quot;&gt;ArrayList(synchronized)&lt;/td&gt;
&lt;td style=&quot;width: 28.5714%; height: 17px; text-align: center;&quot; colspan=&quot;2&quot;&gt;ArrayList(Mutex)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;add&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;get&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;6896&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;8077&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7746&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7468&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3702&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1839&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;4624&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7706&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5525&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7189&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;2920&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1825&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;8887&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7194&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5563&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5694&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;2836&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1856&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;6402&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7752&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5518&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5741&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;2947&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1735&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;6387&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;6541&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5472&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5449&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3344&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1755&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;6&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7250&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5189&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5440&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5916&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3029&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1754&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7599&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;6924&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5530&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5564&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;2946&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1783&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;8754&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7533&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5551&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5174&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3107&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1732&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7342&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7121&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5450&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;4436&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3126&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1716&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;6961&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7551&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5394&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5524&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3122&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1742&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;MIN&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;4624&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5189&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5394&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;4436&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;2836&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1716&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;MAX&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;8887&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;8077&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7743&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7468&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3702&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1856&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;AVERAGE&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7100.2&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7158.8&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5718.6&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5815.2&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3107.9&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1772.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px; text-align: center;&quot;&gt;MEDIAN&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7105.5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;7363.5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5521.5&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;5629&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;3068&lt;/td&gt;
&lt;td style=&quot;width: 14.2857%; height: 17px;&quot;&gt;1754.5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 8개의 thread에서 실행한 결과다. 서로 다른 스레드가 자원을 점유하기 위해 경쟁하고 thread context switching에서 발생하는 오버헤드의 영향으로 싱글 스레드에서보다 훨씬 긴 시간이 소요된다. Vector의 구현 의도대로 싱글 스레드에서의 실행처럼 ArrayList와 큰 차이를 보이지는 않았다. ArrayList 대비 약 1.3배의 차이로 격차가 줄어든 것을 알 수 있지만 1000만개의 데이터는 충분히 가능성이 있는 크기이고, 백엔드 서비스의 응용 프로그램 수준의 캐싱같은 기능이 초 단위의 차이를 보인다면 직접 synchronized 블럭에서 &lt;span&gt;ArrayList 작업을 수행하는 것이 나을 것이다. 또한 get 메소드 호출이 훨씬 빈번하고, 호출이 thread safe함이 보장되지 않아도 되는 경우가 훨씬 많다는 점을 생각하면 결국 ArrayList를 사용할 수 밖에 없다. 주목되는 차이는 Kotlin coroutine에서의 수행 시간이다. Thread와 공정한 비교를 위해 모든 coroutine context를 newSingleThreadContext로 실행했음에도 불구하고 훨씬 적은 시간에 작업이 완료된다. Kotlin에서의 Thread pool 관리로 context swiching 오버헤드가 줄어 생긴 차이로 보이나 정확한 구현을 찾기는 어려웠다.&lt;/span&gt;&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;span&gt;결론&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Vector는 느리다. 모든 호출에서 synchronized(this)를 선언한 것과 같은 효과를 가지므로 원치 않는 상황에서 조차 동기화가 이뤄진다. 따라서 필요에 따른 synchronized 블럭에서의 연산이 가능하다는 점과 모든 연산을 synchronized 블럭에서 수행할 때 조차 ArrayList가 빠르다는 점은 더욱 Vector의 사용을 피하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 그렇다면 Vector는 언제 사용할까? 일일히 synchronized 블럭을 사용하기 귀찮을 때보다는 서로 다른 스레드에서 접근이 명백하고, 내용의 수정과 조회가 순서없이 발생할 수 있을 때라면 고려 대상에 포함할만 하다. 또, synchronized 블럭을 직접 선언하고 critical section에서 작업을 수행하는 것은 완벽히 하나의 작업만 수행할 수 있다면 다행이겠지만, 프로그래머가 작성한 코드에 따라 다른 함수를 호출하거나 다른 변수에 접근할 수도 있다. 그 동작이 동기화를 필요로 한다면 환형 대기 상태로 교착 상태에 빠질 가능성 또한 존재한다. 따라서 ArrayList의 메소드 호출이 또 다른 동기화에 의존적이라면 반드시 피해야 할 것이고, 차라리 Vector를 사용하는 편이 나을 수 있다. 그리고 Java Stack의 사용을 피할 수 없다면 이미 Vector를 사용하고 있는 것이라고도 볼 수 있을 것이다.&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;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JDK: 17.0.5&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM: 17.0.5&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM Option: -Xms4096m -Xmx4096m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin: 1.8.0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin coroutine: 1.6.4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU: Intel Core i9-9880H (8C, 16T)&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;</description>
      <category>Kotlin</category>
      <category>ArrayList</category>
      <category>Java</category>
      <category>Kotlin</category>
      <category>Mutex</category>
      <category>Synchronized</category>
      <category>vector</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/79</guid>
      <comments>https://nodealdev.tistory.com/79#entry79comment</comments>
      <pubDate>Sat, 11 Feb 2023 13:28:22 +0900</pubDate>
    </item>
    <item>
      <title>자연수 합의 마지막 항</title>
      <link>https://nodealdev.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 자연수 \(X\)가 주어졌을 때 이 \(X\)보다 작거나 같고 가장 큰 마지막 자연수의 수열의 원소를 유도해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 1이상의 자연수 \(n\)까지의 자연수의 합 \(S_n\)은 쉽게 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$S_n={{n(n+1)} \over {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;여기서 \(X\)보다 작은 최대 합은 다음을 만족한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$S_n&amp;nbsp;=&amp;nbsp;{{n(n+1)}&amp;nbsp;\over&amp;nbsp;{2}}&amp;nbsp;\leq&amp;nbsp;X$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 최대 합이 \(X\)와 같을 때의 &lt;span&gt;\(n\)을 구한다고 생각하면 부등식이 등식이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$${{n(n+1)} \over {2}} = X$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 식을 \(n\)에 대한 식으로 정리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$n^2 + n = 2X$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$n^2+n+{1 \over 4} = 2X+{1 \over 4}$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$4n^2+4n+1=8X+1$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$(2n+1)^2=8X+1$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$2n+1=\sqrt{8X+1} \ (\because n \geq 1)$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$2n=\sqrt{8X+1} -1$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$n={{\sqrt{8X+1} -1} \over 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;이때 \(n\)은 항상 최대 합을 만족하지 않고, 자연수이므로 최대 합의 마지막 원소 \(n(=a_n)\)은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$n=\left \lfloor {{\sqrt{8X+1} -1} \over 2} \right \rfloor$$&lt;/p&gt;</description>
      <category>일상</category>
      <category>등차수열</category>
      <category>등차수열의 합</category>
      <category>수학</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/78</guid>
      <comments>https://nodealdev.tistory.com/78#entry78comment</comments>
      <pubDate>Thu, 24 Mar 2022 10:36:43 +0900</pubDate>
    </item>
    <item>
      <title>백준 1292: 쉽게 푸는 문제</title>
      <link>https://nodealdev.tistory.com/77</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;solved.ac 실버 5에 랭크되어 있지만 재미있는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어지간히 복잡한 방법을 사용해서 해결하더라도 수열의 길이가 1,000을 넘지 않는다는 점에서 해결이 어려운 문제는 아니다.&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;\(a_n\) = 1 2 2 3 3 3 4 4 4 4 5 5 5 ...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1이 1번, 2가 2번, 3이 3번, 4가 4번 나오는 방법이 반복되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 수열 \(a_n\)의 \(n\)번째 항까지의 합을 \(S(n)\)라고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 입력 A와 B가 주어질 때 A부터 B번째 수까지의 합은 다음과 같이 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$S(B) - S(A) + a_A$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 \(S(n)\)와 A번째 항 \(a_A\)를 상수 시간에 구할 수 있을까?&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, 2, 3, 4, 5 ... 꼴의 등차수열을 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;n번째 항까지의 합은 초등학교 때의 가우스덕분에 상수시간에 해결할 수 있음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$\sum_{i=1}^{n}i={n\times(n+1)\over{2}}$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어떤 정수 n이 주어졌을 때 n에 가장 가깝지만 크지 않은 수열의 합을 이루는 마지막 원소도 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$\left \lfloor {\sqrt{8n + 1} - 1} \over 2 \right \rfloor$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 이용하여 \(a_n\)은 어렵지 않게 구할 수 있게 되었다. 위 식에서 구한 마지막 원소를 \(l\), \(l\)까지의 자연수의 합을 \(s\)라고 할 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$nth(n, l, s) = \begin{cases} l &amp;amp; \text{ if } n = s \\ l + 1 &amp;amp; \text{ if } n \neq s \end{cases}$$&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;또, \(S(n)\)는 이렇게 표현할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$S(n) = \sum_{i=1}^{n}i^2 + (n-s) \times nth(n, l, s)$$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 급수 기호가 나온다면 반복을 떠올릴 수 있지만, 고등학교 이상의 수학을 배웠다면 저 급수 또한 간단한 식으로 바꿀 수 있다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$\sum_{i=1}^{n}i^2 = {n \times (n + 1) \times (2n + 1) \over 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;결국 반복 한 번 돌지 않고 해결할 수 있는 문제였음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 Kotlin으로 작성한 예제이다:&lt;/p&gt;
&lt;pre id=&quot;code_1645444547413&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import kotlin.math.floor
import kotlin.math.sqrt

// 초항이 1이고 각 항이 i의 제곱인 수열의 n번째 항까지의 합
// 1 4 9 16 25 36 49 ...
// ex n = 3) 14
private fun sigmaSquare(n: Int): Int {
    return (n * (n + 1) * (2 * n + 1)) / 6
}

// 초항이 1이고 공차가 1인 등차수열의 n번째 항까지의 합
private fun naturalSum(n: Int): Int {
    return (n * (1 + n)) / 2
}

// 초항이 1이고 공차가 1인 등차수열에서 n이 주어질 때 n보다 작거나 같은 최대 합 sum과 그 수 last를 반환
// ex n = 12) last = 4, sum = 10
private fun maxNaturalSum(n: Int): Pair&amp;lt;Int, Int&amp;gt; {
    val last = ((sqrt((8 * n.toLong() + 1).toDouble()) - 1) / 2).toInt()
    val sum = naturalSum(last)

    return last to sum
}

// 문제에서 주어진 수열에서 n번째 수를 반환
// 1 2 2 3 3 3 4 4 4 4 5 ...
// ex n = 10) 4
//    n = 11) 5
private fun nthNumber(n: Int, last: Int, sum: Int): Int {
    if (sum == n) {
        return last
    } else {
        return last + 1
    }
}

// 문제에서 주어진 수열에서 n번째 항까지의 합을 반환
// 1 2 2 3 3 3 4 4 4 4 5 ...
// ex n = 10) 30
//    n = 11) 35
private fun sumUntil(n: Int, last: Int, sum: Int): Int {
    return sigmaSquare(last) + (n - sum) * (last + 1)
}

fun main() {
    val writer = BufferedWriter(OutputStreamWriter(System.out))
    val reader = BufferedReader(InputStreamReader(System.`in`))
    val (a, b) = reader.readLine().split(&quot; &quot;).map(String::toInt)
    val (lastA, sumA) = maxNaturalSum(a)
    val (lastB, sumB) = maxNaturalSum(b)
    val sA = sumUntil(a, lastA, sumA)
    val sB = sumUntil(b, lastB, sumB)
    val answer = sB - sA + nthNumber(a, lastA, sumA)

    writer.write(&quot;$answer\n&quot;)
    writer.flush()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>간단 문제 풀이</category>
      <category>1292</category>
      <category>O(1)</category>
      <category>백준</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/77</guid>
      <comments>https://nodealdev.tistory.com/77#entry77comment</comments>
      <pubDate>Mon, 21 Feb 2022 20:56:11 +0900</pubDate>
    </item>
    <item>
      <title>백준 1419: 등차수열의 합</title>
      <link>https://nodealdev.tistory.com/76</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 범위의 수들 중 k개의 등차수열의 합으로 표현가능한 수의 수를 구하는 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초항과 공차가 반드시 자연수이므로 각 k에 맞는 최소한의 수가 존재한다.&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;k = 2)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 + 2 = 3이므로, left가 3보다 작더라도 3부터 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초항 n, 공차 d에서 n + n + d = 2n + d를 만족하는 모든 수가 해당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 3이상의 모든 수가 길이가 2인 등차수열의 합이다. (예: 4 = 1 + 3, 5 = 1 + 4 ...)&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;k = 3)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 + 2 + 3 = 6이므로, left가 6보다 작더라도 6부터 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초항 n, 공차 d에서 n + n + d + n + 2d = 3n + 3d를 만족하는 모든 수가 해당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 6이상의 모든 3의 배수가 길이가 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;k = 4)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 + 2 + 3 + 4 = 10이므로, left가 10보다 작더라도 10부터 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초항 n, 공차 d에서 n + n + d + n + 2d + n + 3d = 4n + 6d를 만족하는 모든 수가 해당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수는 10이상의 짝수여야하고, 12는 길이가 4인 등차수열의 합으로 표현할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 10이상의 12가 아닌 모든 짝수가 길이가 4인 등차수열의 합이다.&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;k = 5)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 + 2 + 3 + 4 + 5 = 15이므로, left가 15보다 작더라도 15부터 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초항 n, 공차 d에서 n + n + d + n + 2d + n + 3d + n + 4d = 5n + 10d를 만족하는 모든 수가 해당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 15이상의 모든 5의 배수가 길이가 5인 등차수열의 합이다.&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;문제에서 범위는 최대 10억을 넘지 않으므로, 모두 left(또는 각 k에 맞는 최솟값)부터 right까지 naive한 반복을 돌려도 시간 안에 풀 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1634129609769&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter

fun main() {
    val writer = BufferedWriter(OutputStreamWriter(System.out))
    val reader = BufferedReader(InputStreamReader(System.`in`))
    val left = reader.readLine().toInt()
    val right = reader.readLine().toInt()
    val count = when (reader.readLine().toInt()) {
        2 -&amp;gt; maxOf(right - maxOf(left, 3) + 1, 0)
        3 -&amp;gt; {
            var count = 0
            val min = maxOf(left, 6)
            for (i in min .. right) {
                if (i % 3 == 0) {
                    count++
                }
            }

            count
        }
        4 -&amp;gt; {
            var count = 0
            val min = maxOf(left, 10)
            for (i in min .. right) {
                if (i and 1 == 0 &amp;amp;&amp;amp; i != 12) {
                    count++
                }
            }

            count
        }
        5 -&amp;gt; {
            var count = 0
            val min = maxOf(left, 15)
            for (i in min..right) {
                if (i % 5 == 0) {
                    count++
                }
            }

            count
        }
        else -&amp;gt; throw AssertionError(&quot;k range error&quot;)
    }

    writer.write(&quot;$count\n&quot;)
    writer.flush()
    return
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>간단 문제 풀이</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/76</guid>
      <comments>https://nodealdev.tistory.com/76#entry76comment</comments>
      <pubDate>Wed, 13 Oct 2021 21:53:38 +0900</pubDate>
    </item>
    <item>
      <title>백준 9464: 직사각형 집합</title>
      <link>https://nodealdev.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이게 왜 solved.ac 골드 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;문제의 제한 시간은 2초, 1,000,000보다 작거나 같은 모든 L에 대해 선분의 길이 쌍을 모두 구하고도 남을 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(3, 4), (5, 12), (8, 15) ... 와 같은 쌍을 모두 준비하는 방법을 생각해보자.&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;첫번째 조건은 세 성분을 잘 조합했을 때 '피타고리안 트리플'이 되어야 한다는 것이다. 3, 4가 있다면 다른 한 수는 5가 되어야 한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 조건은 폭보다 높이가 커야한다는 것으로, (3, 4), (4, 3)에서는 (3, 4)만이 유효하다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 조건은 두 수의 최대공약수가 1이어야 한다는 것, 다시 말해 두 수가 서로소여야 한다는 것이다. (3, 4)는 허용해도, (6, 8)은 안 된다는 것이다. 굳이 (6, 8)을 포함하고 (3, 4)를 포기하는 방법도 있지만 최대 직사각형의 수를 구하기 위해서는 항상 작은 것이 이득일 것이다.&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;결국 그렇게 구한 모든 쌍을 w성분과 h성분의 합으로 정렬한 다음, 그리디하게 앞에서부터 L에서 빼주면 된다.&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;EVAL_MAX가 10000L일 때,&lt;/p&gt;
&lt;pre id=&quot;code_1633520312710&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    var a = 1L
    var b = 1L
    val list = ArrayList&amp;lt;Pair&amp;lt;Long, Long&amp;gt;&amp;gt;()
    var sum = 0L
    while (a + b &amp;lt;= EVAL_MAX) {
        val c = sqrt((a * a + b * b).toDouble()).toLong()
        if (c * c == a * a + b * b) {
            if (gcd(a, b) == 1L) {
                list.add(a to b)
                sum += (a + b) * 2
            }
        }

        b++
        if (b == EVAL_MAX - a) {
            a++
            b = a
        }
    }

    list.sortBy { it.first + it.second }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;a, b 모두 1부터 전부 구해봤자 최종적으로 400ms 안팎의 시간밖에 소요되지 않는다는 점만 기억하면 쉽게 풀 수 있을 것이다.&lt;/p&gt;</description>
      <category>간단 문제 풀이</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/75</guid>
      <comments>https://nodealdev.tistory.com/75#entry75comment</comments>
      <pubDate>Wed, 6 Oct 2021 20:39:34 +0900</pubDate>
    </item>
    <item>
      <title>백준 15711: 환상의 짝꿍</title>
      <link>https://nodealdev.tistory.com/74</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;두 수의 합이 다른 두 소수의 합으로 표현할 수 있는가에 대한 문제다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 A와 B의 범위는 1 이상이므로 합은 2, 3, 4, ...로 나올 수 있다.&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;이때 A+B가 2 또는 3이라면)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 작은 소수 2를 뺐을 때 나머지 0 또는 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;A+B가 4 이상의 짝수라면)&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;/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;A+B가 4 이상의 홀수라면)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2로 뺀 A+B-2가 소수라면 두 소수의 합으로 표현할 수 있고, A+B-2가 소수가 아니라면 두 소수의 합으로 표현할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 19는 2+17의 쌍으로 표현할 수 있다. 하지만 23은 2+21에서 21이 합성수이므로 표현할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A+B가 홀수면서, A+B에 홀수의 소수를 뺀 결과가 2가 아닐 때, 반드시 짝수는 소수가 아니므로 두 소수의 합으로 표현할 수 없는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;A+B-2가 소수인지만 판명&lt;/b&gt;하면 되는 문제이므로, 10^12개의 입력 수에서 소수를 판정하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 Miller-Rabin 판별법을 사용하면 간단하게 해결된다. Java, Kotlin을 사용한다면 BigInteger의 isProbablePrime 메소드를 사용해도 좋다.&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;Miller-Rabin 소수 판별은 다른 문제에서도 많이 활용되니 미리 구현해두면 요긴하게 사용할 수 있다.&lt;/p&gt;</description>
      <category>간단 문제 풀이</category>
      <author>nodeal</author>
      <guid isPermaLink="true">https://nodealdev.tistory.com/74</guid>
      <comments>https://nodealdev.tistory.com/74#entry74comment</comments>
      <pubDate>Thu, 30 Sep 2021 21:11:56 +0900</pubDate>
    </item>
  </channel>
</rss>