<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>HAMA 블로그</title>
    <link>https://hamait.tistory.com/</link>
    <description>관심키워드:  폴리글랏,동시성,분산,에너지IoT, 시계열분석,블럭체인</description>
    <language>ko</language>
    <pubDate>Sun, 17 May 2026 03:53:20 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>[하마] 이승현 (wowlsh93@gmail.com)</managingEditor>
    <image>
      <title>HAMA 블로그</title>
      <url>https://t1.daumcdn.net/cfile/tistory/24414547584670F723</url>
      <link>https://hamait.tistory.com</link>
    </image>
    <item>
      <title>Rust (async/Future) vs Kotlin Coroutines vs Java Virtual Threads 차이 [기초]</title>
      <link>https://hamait.tistory.com/1164</link>
      <description>&lt;pre id=&quot;code_1734676072556&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Rust vs Kotlin vs Java&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rust의 컴파일 타임 vs Kotlin/Java의 런타임&lt;/h3&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;특징&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Rust (async / Future)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&amp;nbsp;Kotlin Coroutines&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&amp;nbsp;Java Virtual Threads&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;비동기 실행 모델&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Future 기반 (poll-driven)&lt;/td&gt;
&lt;td&gt;Suspend 함수 기반 (Continuation)&lt;/td&gt;
&lt;td&gt;사용자 모드 스레드 기반 (Thread API)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;상태 관리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;컴파일 타임&lt;/span&gt;&lt;/b&gt;에 상태 기계로 변환&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;런타임&lt;/span&gt;&lt;/b&gt;에서 상태를 관리&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;런타임&lt;/span&gt;&lt;/b&gt;에서 사용자 모드 스레드 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;최적화&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;컴파일 타임 최적화 (zero-cost abstraction)&lt;/td&gt;
&lt;td&gt;일부 컴파일 타임, 주로 런타임 최적화&lt;/td&gt;
&lt;td&gt;주로 런타임 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;런타임 필요성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;런타임 스케줄러 필요 (Tokio, async-std 등)&lt;/td&gt;
&lt;td&gt;코루틴 디스패처 필요&lt;/td&gt;
&lt;td&gt;Java 런타임 자체에서 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;제어권&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;개발자에 의해 언어&amp;amp;라이브러리 차원의 키워드로 명시적 으로 제어권 반환해야함&amp;nbsp;&lt;/td&gt;
&lt;td&gt;개발자에 의해 언어&amp;amp;라이브러리 차원의 키워드로 &lt;b&gt;명시적으로 제어권 반환&lt;/b&gt; 해야함&amp;nbsp;&lt;/td&gt;
&lt;td&gt;명시적 제어권 반환하는 키워드가 새로 생긴건 없으며, 기존&lt;b&gt; 자바 동기 코드를 사용해도 내부에서 알아서 비동기 처리&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;난이도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;중간&amp;nbsp;&lt;/td&gt;
&lt;td&gt;중간&amp;nbsp;&lt;/td&gt;
&lt;td&gt;쉬움 &amp;amp; 세밀한 제어 및 부모-자식관계 관리등 안됨&amp;nbsp;&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;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Rust의 컴파일 타임&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rust의 &lt;b&gt;비동기 프로그래밍&lt;/b&gt; 모델에서는 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;컴파일 타임&lt;/span&gt;에 많은 작업을 수행&lt;/b&gt;하여 &lt;b&gt;효율적인 비동기 실행&lt;/b&gt;을 지원합니다. Rust의 비동기 실행 모델은 &lt;b&gt;async/await&lt;/b&gt; 키워드와 &lt;b&gt;Future 트레이트&lt;/b&gt;를 기반으로 하며, 다른 언어들에 비해 &lt;b&gt;컴파일 타임 최적화&lt;/b&gt;가 더 강력한 특징을 가지고 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rust의 &lt;b&gt;async와 Future 트레이트&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Rust에서 **async fn**은 **컴파일 타임에 상태 기계(state machine)**로 변환됩니다.&lt;/li&gt;
&lt;li&gt;async fn이 호출되면 &lt;b&gt;미리 생성된 상태 기계 객체&lt;/b&gt;가 반환되며, 이 객체는 Future 트레이트를 구현합니다.&lt;/li&gt;
&lt;li&gt;Future는 &lt;b&gt;poll 기반의 실행 모델&lt;/b&gt;로, 런타임에서 필요한 시점에 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rust의 강점: 컴파일 타임에 많은 것을 결정&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Rust는 &lt;b&gt;async fn을 Future 객체로 변환&lt;/b&gt;하여, 비동기 함수의 모든 상태를 컴파일 타임에 정의합니다.&lt;/li&gt;
&lt;li&gt;이는 런타임 오버헤드를 줄이고, &lt;b&gt;높은 성능과 안전성&lt;/b&gt;을 보장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비동기 코드의 런타임 비용&lt;/b&gt;을 최소화하기 위해 설계되었으며, 이 점에서 Kotlin이나 Java보다 더 낮은 레벨의 최적화를 제공합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 위의 말 가지고는 이해 할 수 가 없습니다.&amp;nbsp; 세상은 좋은 선생님들 덕분에 돌아갑니다. :-)&amp;nbsp;&lt;br /&gt;아래에 직접 구현된 예시가 있습니다. (컴파일 타임에 코루틴이 구현되는 방식 - 예제 코드 솔직히 킹왕짱.)&amp;nbsp;&lt;br /&gt;&lt;a title=&quot;https://github.com/cfsamson/Asynchronous-Programming-in-Rust/tree/main/ch08&quot; href=&quot;https://github.com/cfsamson/Asynchronous-Programming-in-Rust/tree/main/ch08&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/cfsamson/Asynchronous-Programming-in-Rust/tree/main/ch08&amp;nbsp;&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1734673889292&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Asynchronous-Programming-in-Rust/ch07 at main &amp;middot; cfsamson/Asynchronous-Programming-in-Rust&quot; data-og-description=&quot;Asynchronous Programming in Rust, published by Packt - cfsamson/Asynchronous-Programming-in-Rust&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/cfsamson/Asynchronous-Programming-in-Rust/tree/main/ch07&quot; data-og-url=&quot;https://github.com/cfsamson/Asynchronous-Programming-in-Rust/tree/main/ch07&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cP2qPa/hyXOhQkrG1/JhAvRKIWAsFZ1K42iIuQiK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/61No7/hyXOqT2J29/6O3NTmlZyX6GGmLdumEKXk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/cfsamson/Asynchronous-Programming-in-Rust/tree/main/ch07&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/cfsamson/Asynchronous-Programming-in-Rust/tree/main/ch07&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cP2qPa/hyXOhQkrG1/JhAvRKIWAsFZ1K42iIuQiK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/61No7/hyXOqT2J29/6O3NTmlZyX6GGmLdumEKXk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Asynchronous-Programming-in-Rust/ch07 at main &amp;middot; cfsamson/Asynchronous-Programming-in-Rust&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Asynchronous Programming in Rust, published by Packt - cfsamson/Asynchronous-Programming-in-Rust&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심코드만 발췌)&lt;/p&gt;
&lt;pre id=&quot;code_1734922603583&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// coroutine fn async_main() {
//     println!(&quot;Program starting&quot;);
//     let txt = http::Http::get(&quot;/600/HelloAsyncAwait&quot;).wait;
//     println!(&quot;{txt}&quot;);
//     let txt = http::Http::get(&quot;/400/HelloAsyncAwait&quot;).wait;
//     println!(&quot;{txt}&quot;);

// }


// 위의 코드가 아래 코드로 컴파일 시점에 변경 // 

// 설명) 상태를 가진 코루틴 객체가 첫번째 wait만나기 전까지 상태를 저장하고, 자식 비동기 호출하고 
// 이런식으로 함수가 특정 위치부터 이전 상태를 기반으로 재실행됨 


fn async_main() -&amp;gt; impl Future&amp;lt;Output=String&amp;gt; {
    Coroutine0::new()
}

enum State0 {
    Start,
    Wait1(Box&amp;lt;dyn Future&amp;lt;Output = String&amp;gt;&amp;gt;),
    Wait2(Box&amp;lt;dyn Future&amp;lt;Output = String&amp;gt;&amp;gt;),
    Resolved,
}

struct Coroutine0 {
    state: State0,
}

impl Coroutine0 {
    fn new() -&amp;gt; Self {
        Self { state: State0::Start }
    }
}


impl Future for Coroutine0 {
    type Output = String;

    fn poll(&amp;amp;mut self, waker: &amp;amp;Waker) -&amp;gt; PollState&amp;lt;Self::Output&amp;gt; {
        loop {
        match self.state {
                State0::Start =&amp;gt; {
                    // ---- Code you actually wrote ----
                    println!(&quot;Program starting&quot;);

                    // ---------------------------------
                    let fut1 = Box::new( http::Http::get(&quot;/600/HelloAsyncAwait&quot;));
                    self.state = State0::Wait1(fut1);
                }

                State0::Wait1(ref mut f1) =&amp;gt; {
                    match f1.poll(waker) {
                        PollState::Ready(txt) =&amp;gt; {
                            // ---- Code you actually wrote ----
                            println!(&quot;{txt}&quot;);

                            // ---------------------------------
                            let fut2 = Box::new( http::Http::get(&quot;/400/HelloAsyncAwait&quot;));
                            self.state = State0::Wait2(fut2);
                        }
                        PollState::NotReady =&amp;gt; break PollState::NotReady,
                    }
                }

                State0::Wait2(ref mut f2) =&amp;gt; {
                    match f2.poll(waker) {
                        PollState::Ready(txt) =&amp;gt; {
                            // ---- Code you actually wrote ----
                            println!(&quot;{txt}&quot;);

                            // ---------------------------------
                            self.state = State0::Resolved;
                            break PollState::Ready(String::new());
                        }
                        PollState::NotReady =&amp;gt; break PollState::NotReady,
                    }
                }

                State0::Resolved =&amp;gt; panic!(&quot;Polled a resolved future&quot;)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #5c5c5c; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Kotlin/Java의 런타임&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;코틀린 코루틴의 협력적 스케줄링 (Cooperative Scheduling)&lt;/b&gt;&lt;/h2&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;스레드나 작업이 자발적으로 제어권을 넘겨주는 방식&lt;/b&gt;으로 동작합니다.&lt;/li&gt;
&lt;li&gt;작업 중간에 &lt;b&gt;명시적으로 제어권을 반환&lt;/b&gt;하는 지점 (yield, await, suspend 등)을 코드에 삽입해야 합니다.&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;컨텍스트 스위칭 비용이 매우 낮음&lt;/b&gt;: 협력적 스케줄링은 커널의 간섭 없이 사용자 공간에서 이루어지므로 매우 빠름.&lt;/li&gt;
&lt;li&gt;개발자가 작업 흐름을 더 세밀하게 제어할 수 있음.&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;비협력적인 코드가 있다면 문제가 발생&lt;/b&gt;: 작업이 제어권을 반환하지 않으면, 다른 작업이 실행되지 못하고 시스템이 멈출 수 있음.&lt;/li&gt;
&lt;li&gt;협력적 스케줄링은 &lt;b&gt;블로킹 작업&lt;/b&gt;을 자체적으로 처리하지 못하므로, I/O 작업 등을 비동기로 처리해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;협력적 스케줄러가 관리하는 작업은 사용자 공간에서 실행됩니다.&lt;/li&gt;
&lt;li&gt;각 작업은 명시적으로 &lt;b&gt;제어권을 반환&lt;/b&gt;해야 다른 작업이 실행될 수 있습니다.&lt;/li&gt;
&lt;li&gt;모든 작업은 동일한 스레드에서 실행될 수 있습니다 (멀티스레드 환경도 가능).&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1734674161213&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*
import kotlin.random.Random

suspend fun fetchData(id: Int): String {
    delay(Random.nextLong(100, 500)) // 비동기 대기 (명시적 제어권 반환)
    return &quot;Data for request $id&quot;
}

fun main() = runBlocking {
    val jobs = List(100) { id -&amp;gt; // 100개의 코루틴 생성
        launch {
            val data = fetchData(id)
            println(&quot;Coroutine $id: $data on thread ${Thread.currentThread().name}&quot;)
        }
    }

    jobs.forEach { it.join() } // 모든 작업이 끝날 때까지 대기
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delay() 호출 시 제어권을 반환해 다른 작업이 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Java Virtual Thread - OS 커널 스레드와 분리된 사용자 모드 스레드 (User-mode Threads)&lt;/b&gt;&lt;/h2&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;OS 커널 스레드와 독립적으로 관리&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Java Virtual Threads&lt;/b&gt;와 같이 사용자 공간에서 구현된 가벼운 스레드입니다.&lt;/li&gt;
&lt;li&gt;작업 실행은 OS의 스케줄러가 아닌 &lt;b&gt;사용자 모드 스케줄러&lt;/b&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;OS 커널 스레드의 제한 없이 &lt;b&gt;대량의 사용자 모드 스레드&lt;/b&gt;를 실행할 수 있음.&lt;/li&gt;
&lt;li&gt;스레드 생성과 스위칭이 커널 스레드보다 훨씬 빠름.&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;I/O 작업이나 블로킹 호출은 내부적으로 비동기로 처리되어야 함.&lt;/li&gt;
&lt;li&gt;커널 스레드에 비해 구현이 복잡할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동작 방식&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자 모드 스케줄러는 사용자 모드 스레드를 관리하며, 이 스레드는 필요 시 커널 스레드에서 실행됩니다.&lt;/li&gt;
&lt;li&gt;사용자 모드 스레드가 실행을 중단하면(예: I/O 대기) 커널 스레드가 다른 사용자 모드 스레드를 실행합니다.&lt;/li&gt;
&lt;li&gt;이 과정은 &lt;b&gt;투명하게 이루어지며&lt;/b&gt;, 기존 동기 코드를 그대로 사용할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java의 Virtual Threads는 사용자 모드 스레드입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;**Thread.startVirtualThread**를 통해 가벼운 사용자 모드 스레드를 생성합니다.&lt;/li&gt;
&lt;li&gt;내부적으로 &lt;b&gt;스케줄러가 I/O 대기 및 실행을 관리&lt;/b&gt;하며, OS 스레드가 비효율적으로 차단되지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1734674231018&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.*;
import java.util.Random;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
        Random random = new Random();

        for (int i = 0; i &amp;lt; 100; i++) { // 100개의 Virtual Threads 생성
            int id = i;
            executor.submit(() -&amp;gt; {
                try {
                    Thread.sleep(random.nextInt(400) + 100); // 블로킹 호출
                    System.out.println(&quot;Virtual Thread &quot; + id + &quot;: Data fetched on thread &quot; + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.MINUTES); // 모든 작업이 끝날 때까지 대기
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;협력적 스케줄링과 사용자 모드 스레드의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징협력적 스케줄링사용자 모드 스레드&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;작업 전환 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;작업이 명시적으로 제어권을 반환해야 함&lt;/td&gt;
&lt;td&gt;사용자 모드 스케줄러가 작업 전환을 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스케줄링&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;비선점형 (Non-preemptive)&lt;/td&gt;
&lt;td&gt;선점형 (Preemptive)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;I/O 처리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;비동기 I/O와 결합하여 동작&lt;/td&gt;
&lt;td&gt;내부적으로 비동기 I/O 처리, 블로킹 호출과 호환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스레드 관리&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;단일 OS 스레드에서 실행 가능&lt;/td&gt;
&lt;td&gt;여러 사용자 모드 스레드가 OS 스레드에서 매핑되어 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 예시&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Kotlin 코루틴, Python async/await&lt;/td&gt;
&lt;td&gt;Java Virtual Threads, Go 언어의 goroutine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;성능&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;컨텍스트 스위칭 비용이 낮음&lt;/td&gt;
&lt;td&gt;더 많은 스레드를 처리할 수 있지만 스위칭 비용이 더 높음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;코드 스타일&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;비동기 코드 (async/await, suspend)&lt;/td&gt;
&lt;td&gt;동기 스타일과 비동기 스타일 모두 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Rust Future와 다른 모델 비교&lt;/b&gt;&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Rust Future&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Kotlin Coroutine&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Java Virtual Threads&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Go Goroutine&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스케줄링 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;협력적 스케줄링&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;협력적 스케줄링&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;선점형 스케줄링&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;선점형 스케줄링&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;제어권 반환&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;명시적 (Poll::Pending)&lt;/td&gt;
&lt;td&gt;명시적 (suspend, yield)&lt;/td&gt;
&lt;td&gt;자동&lt;/td&gt;
&lt;td&gt;자동&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;실행 트리거&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Lazy (스케줄러 필요)&lt;/td&gt;
&lt;td&gt;Lazy (런타임 필요)&lt;/td&gt;
&lt;td&gt;Eager (즉시 실행 가능)&lt;/td&gt;
&lt;td&gt;Eager (즉시 실행 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;I/O 처리 방식&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;논블로킹&lt;/td&gt;
&lt;td&gt;논블로킹&lt;/td&gt;
&lt;td&gt;동기 호출도 비동기로 처리 가능&lt;/td&gt;
&lt;td&gt;논블로킹&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;스케줄러 제어 가능성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;개발자가 직접 제어 (poll 호출)&lt;/td&gt;
&lt;td&gt;Dispatchers로 스케줄링 제어&lt;/td&gt;
&lt;td&gt;JVM 스케줄러 자동 관리&lt;/td&gt;
&lt;td&gt;Go의 GMP 스케줄러 자동 관리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;사용 스타일&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;비동기 (async/await, poll)&lt;/td&gt;
&lt;td&gt;비동기 (async/await)&lt;/td&gt;
&lt;td&gt;동기 (Thread API와 동일)&lt;/td&gt;
&lt;td&gt;동기 (go 키워드)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;별책부록1)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Java Virtual Threads&lt;/b&gt;와 &lt;b&gt;Kotlin Coroutines&lt;/b&gt; 모두 결국 &lt;b&gt;&lt;b&gt;JVM 스레드 풀(예: ForkJoinPool)&lt;/b&gt;&lt;/b&gt;&amp;nbsp;위에서 실행됩니다.&lt;br /&gt;둘 모두 OS Thread를 직접 사용 할 때 이루어지는 블로킹 및&amp;nbsp; 컨텍스트 스위칭을 줄여서 동시성 처리 효율을 극대화 하는 방향으로 설계됩니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;Java Virtual Threads 를 사용하면 학습곡선이 낮아집니다. 명시적인 동시성 처리를 안하니깐~&lt;br /&gt;대신 디테일한 조정을 못하게 되겠지요. Go언어의 Goroutine생각하면 Kotlin Coroutine보다 Java Virtual Threads가 훨씬 잘 쓰일듯. (한데.. 코프링이 워낙 좋아서 또.. 몰러)&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;b&gt;좀 편하게 살자 !!!&amp;nbsp; &amp;nbsp; (&lt;u&gt;&lt;b&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/ro-spring-virtual-thread/&quot;&gt;https://tech.kakaopay.com/post/ro-spring-virtual-thread/&lt;/a&gt;&lt;/b&gt;&lt;/u&gt;&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;&lt;b&gt;&lt;u&gt;&lt;b&gt;&lt;a href=&quot;https://tech.kakaopay.com/post/ro-spring-virtual-thread/&quot;&gt;)&lt;/a&gt;&lt;/b&gt;&lt;/u&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;JVM과 OS 스레드 관계&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JVM은 기본적으로 OS 스레드를 기반으로 동작합니다.&lt;/li&gt;
&lt;li&gt;Java에서의 **스레드(Thread)**는 OS 커널 스레드에 직접 매핑됩니다.&lt;/li&gt;
&lt;li&gt;따라서 &lt;b&gt;Java의 스레드 API&lt;/b&gt;를 통해 생성한 스레드는 JVM에서 OS 스레드로 매핑되어 커널 스케줄러에 의해 관리됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;br /&gt;별책부록2)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Kotlin Coroutines에서는 간단히 처리할 수 있지만 Java Virtual Threads로는 더 많은 코드와 복잡한 제어가 필요한 경우의 예제&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;시나리오 1: 부모-자식 관계로 작업 그룹 관리&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;문제&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;100개의 작업을 병렬로 실행하지만, 특정 조건에서 중간에 멈춰야 합니다(예: 하나의 작업이 실패하면 전체 작업을 취소).&lt;/li&gt;
&lt;li&gt;모든 작업이 끝난 후, 결과를 종합해서 처리해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;목표&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업의 그룹 단위 관리 (부모-자식 관계).&lt;/li&gt;
&lt;li&gt;작업 중 하나라도 실패하면 나머지 작업을 취소.&lt;/li&gt;
&lt;li&gt;모든 작업이 성공하면 결과를 합산.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;시나리오 2: 병렬 작업에서 타임아웃과 취소 관리&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;문제&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;100개의 작업을 병렬로 실행하며, 각각 최대 300ms 안에 완료되어야 합니다.&lt;/li&gt;
&lt;li&gt;작업이 실패하거나 타임아웃이 발생하면, 해당 작업을 취소하고 나머지 작업은 계속 진행합니다.&lt;/li&gt;
&lt;li&gt;결과를 모두 모아 성공적으로 완료된 작업만 처리합니다.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Kotlin Coroutines 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin 코루틴은 **구조화된 동시성(Structured Concurrency)**를 제공하므로, 부모-자식 관계를 자연스럽게 관리할 수 있습니다. coroutineScope나 supervisorScope를 사용해 간단히 구현 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1734676043801&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//// 시나리오 1

import kotlinx.coroutines.*
import kotlin.random.Random

suspend fun fetchData(id: Int): Int {
    delay(Random.nextLong(100, 500)) // 비동기 대기
    if (Random.nextBoolean()) throw RuntimeException(&quot;Error in task $id&quot;) // 실패 가능성
    return id * 10
}

fun main() = runBlocking {
    try {
        val results = coroutineScope { // 부모 코루틴 스코프
            (1..100).map { id -&amp;gt;
                async { fetchData(id) } // 자식 코루틴 실행
            }.awaitAll() // 모든 결과를 기다림
        }
        println(&quot;All tasks completed successfully: ${results.sum()}&quot;)
    } catch (e: Exception) {
        println(&quot;Failed: ${e.message}&quot;)
    }
}


//// 시나리오 2


import kotlinx.coroutines.*
import kotlin.random.Random

suspend fun fetchData(id: Int): Int {
    delay(Random.nextLong(100, 500)) // 비동기 대기
    if (Random.nextBoolean()) throw RuntimeException(&quot;Error in task $id&quot;) // 실패 가능성
    return id * 10
}

fun main() = runBlocking {
    val results = (1..100).map { id -&amp;gt;
        async {
            try {
                withTimeout(300) { fetchData(id) } // 300ms 안에 완료되지 않으면 취소
            } catch (e: Exception) {
                null // 실패하거나 타임아웃 발생 시 null 반환
            }
        }
    }.awaitAll()

    val successfulResults = results.filterNotNull()
    println(&quot;Successful results: ${successfulResults.sum()}&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Kotlin 코루틴 동작 방식&lt;/b&gt;:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;**coroutineScope**는 부모 코루틴으로, 자식 코루틴들의 생명 주기를 관리합니다.&lt;/li&gt;
&lt;li&gt;작업 중 하나라도 실패하면, 나머지 작업을 자동으로 취소합니다.&lt;/li&gt;
&lt;li&gt;결과를 awaitAll()로 간단히 모아서 처리합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Java Virtual Threads 구현&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java Virtual Threads는 구조화된 동시성을 기본적으로 제공하지 않으므로, 비슷한 기능을 구현하려면 더 많은 코드와 수작업이 필요합니다&lt;/p&gt;
&lt;pre id=&quot;code_1734676104882&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/// 시나리오 1

import java.util.concurrent.*;
import java.util.*;
import java.util.stream.*;
import java.util.Random;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
        List&amp;lt;Future&amp;lt;Integer&amp;gt;&amp;gt; futures = new ArrayList&amp;lt;&amp;gt;();
        Random random = new Random();

        // 부모 작업: 결과 수집
        try {
            for (int i = 1; i &amp;lt;= 100; i++) {
                final int id = i;
                futures.add(executor.submit(() -&amp;gt; {
                    try {
                        Thread.sleep(random.nextInt(400) + 100); // 블로킹 대기
                        if (random.nextBoolean()) throw new RuntimeException(&quot;Error in task &quot; + id); // 실패 가능성
                        return id * 10;
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }));
            }

            // 결과 처리
            int sum = 0;
            for (Future&amp;lt;Integer&amp;gt; future : futures) {
                sum += future.get(); // 작업 완료 대기 및 결과 수집
            }
            System.out.println(&quot;All tasks completed successfully: &quot; + sum);
        } catch (ExecutionException e) {
            System.out.println(&quot;Failed: &quot; + e.getCause().getMessage());

            // 자식 작업 취소
            for (Future&amp;lt;Integer&amp;gt; future : futures) {
                future.cancel(true);
            }
        } finally {
            executor.shutdown();
            executor.awaitTermination(1, TimeUnit.MINUTES);
        }
    }
}

/// 시나리오 2

import java.util.concurrent.*;
import java.util.*;
import java.util.stream.*;
import java.util.Random;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
        Random random = new Random();
        List&amp;lt;Future&amp;lt;Integer&amp;gt;&amp;gt; futures = new ArrayList&amp;lt;&amp;gt;();

        for (int i = 1; i &amp;lt;= 100; i++) {
            final int id = i;
            futures.add(executor.submit(() -&amp;gt; {
                try {
                    Thread.sleep(random.nextInt(400) + 100); // 블로킹 대기
                    if (random.nextBoolean()) throw new RuntimeException(&quot;Error in task &quot; + id);
                    return id * 10;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }));
        }

        List&amp;lt;Integer&amp;gt; results = new ArrayList&amp;lt;&amp;gt;();
        for (Future&amp;lt;Integer&amp;gt; future : futures) {
            try {
                results.add(future.get(300, TimeUnit.MILLISECONDS)); // 300ms 타임아웃 설정
            } catch (TimeoutException | ExecutionException e) {
                future.cancel(true); // 타임아웃 시 작업 취소
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        System.out.println(&quot;Successful results: &quot; + results.stream().mapToInt(Integer::intValue).sum());
        executor.shutdown();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Java Virtual Threads 동작 방식&lt;/b&gt;:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Virtual Threads는 ExecutorService에서 관리되며, 각 작업은 Future 객체로 반환됩니다.&lt;/li&gt;
&lt;li&gt;작업 실패 시 예외를 처리하려면 추가적인 로직(try-catch와 future.cancel())이 필요합니다.&lt;/li&gt;
&lt;li&gt;구조화된 동시성이 제공되지 않으므로, 부모-자식 관계를 직접 관리해야 합니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>소프트웨어 사색</category>
      <author>[하마] 이승현 (wowlsh93@gmail.com)</author>
      <guid isPermaLink="true">https://hamait.tistory.com/1164</guid>
      <comments>https://hamait.tistory.com/1164#entry1164comment</comments>
      <pubDate>Fri, 20 Dec 2024 14:49:07 +0900</pubDate>
    </item>
    <item>
      <title>고품질 코드 (1) - 기존 코드 건드리지 말기  (feat. 코틀린)</title>
      <link>https://hamait.tistory.com/1156</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;고품질의 코드란 무엇일까?&amp;nbsp; 예외처리가 잘 된 ? 주석이 잘 달린? 성능,메모리 최적화된? 변수명이 좋은?&amp;nbsp;&lt;br /&gt;다 옳은 말이다. 추가적으로 관련 프레임워크/라이브러리의 이해가 잘된 코드, 도메인을 잘 이해한 코드도 물론이고..&lt;br /&gt;&lt;br /&gt;이 글에서 고품질의 코드란 새로운 기능을 구현 할 때 최대한 기존 구현된 코드는 건드리지 않는 구조라고 정하고 &lt;br /&gt;그것에 대한 이야기를 써 본다.&lt;br /&gt;&lt;br /&gt;이 글은 아래에&amp;nbsp; 나오는 지식 기반을 엮어서 설명한다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929; text-align: start;&quot;&gt;Ralph E. Johnson, Martin Fowler &lt;span style=&quot;font-family: 'Nanum Gothic'; background-color: #ffffff; color: #222222; text-align: start;&quot;&gt;et al.&lt;/span&gt; &lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929; text-align: start;&quot;&gt;-&amp;nbsp;&lt;/span&gt; &lt;/span&gt;DI,DIP,IoC(Dependency Injection etc)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;마틴파울러의 리팩토링 - Refactor Conditional To Polymorphism&lt;/li&gt;
&lt;li&gt;로버트 C. 마틴 클린코드 -&amp;nbsp; Data/Object Anti-Symmetry&lt;/li&gt;
&lt;li&gt;버틀란트메이어 객체지향 소프트웨어 설계 - Open/Closed Principle&lt;/li&gt;
&lt;li&gt;Gof의 디자인패턴 - Visitor Pattern (사실 Visitor말고도 수 많은 패턴들이 해당된다)&lt;/li&gt;
&lt;li&gt;더블 디스패칭 - Visitor패턴에서 엘리먼트(데이터)가 하나이고 Visitor (행위자)가 여럿일 경우 비슷&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Duncan McGregor, Nat Pryce의 Java to Kotlin - Open&amp;nbsp;to&amp;nbsp;Sealed&amp;nbsp;Classes&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 나오는 법칙들은 모두 한가지를 가르키는데 변경은 좁은/분리된 범위에서 하게 하자이다.&lt;br /&gt;&lt;br /&gt;먼저 리펙토링(Refactor Conditional To Polymorphism)은 if 문의 중첩으로 코딩된 코드를 다형성으로 처리하여 if 문을 밑으로 더 길게 수정하는 행위보다는 해당 부분은 전혀 건드리지 말고, 다른 파일에 새로운 타입의 구현을 작성하기게 만들자라는 것이고, 그 방식을 다형성의 힘을 이용했다.&amp;nbsp; Data/Object Anti-Symmetry는 다형성을 이용해서 구현을 하게 되면 생기는 문제도 있음을 지적한다. 우리가 개발을 하다보면 동종의 타입들이 늘어나는 경우도 있고, 행위가 늘어나는 경우가 있다.예를들어 Shap(도형) 이라면 draw라는 작동방식은 한정되어 있지만 Circle, Rectange,Star,Square,&lt;span style=&quot;background-color: #fdfdfd; color: #000000; text-align: start;&quot;&gt;Ellipse &lt;/span&gt;등 타입은 계속 늘어날 가능성이 높은 경우가 있고, 반대로 타입은 남자,여자로 거의 정해졌지만 행위는 먹다,자다,놀다,걷다 등으로 무한정 늘어날 가능성이 높은 경우가 있을 것이다. 이때 후자의 경우 다형성 기반으로 코딩을 한다면 행동하나가 늘어 날 때 마다 모든 서브클래스의 파일을 찾아서 수정해 주어야하는 고생을 해야 할 것이라서, 그냥 타입만 구분하고 그것에 강결합되 있는 인터페이스의 동작을 작성하지 말고, 파일하나에 행위 하나를 작성한다면 새로운 행위가 추가되더라도 기존 파일에는 전혀 영향이 없이 새로운 파일을 만들어서 추가하면 될 것이다. 이런 것들은 OCP와도 일맥상충하는데 기존의&amp;nbsp;코드를&amp;nbsp;변경하지&amp;nbsp;않으면서&amp;nbsp;기능을&amp;nbsp;추가할&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;설계가&amp;nbsp;되어야&amp;nbsp;한다는&amp;nbsp;의미입니다.&lt;br /&gt;&lt;br /&gt;이제 말보단 코드로 봅시다. 코틀린의 sealed class를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685966821163&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sealed interface Error

class FileReadError(val file: String): Error
class DatabaseError(val source: String): Error
class RuntimeError : Error&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드가 있다고 합시다. Error라는 인터페이스를 구현하는 타입이 3가지가 있습니다.&lt;br /&gt;만약 Error 라는 타입의 가지고 있는 행위는 log()만 있을 확률이 높고, Error 하위 타입이 늘어날 가능성이 높은 어플리케이션을 님이 만들고 있다면 이것은 다형성을 이용하는게 좋을 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1685967121845&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sealed interface Error {
    fun log()
}

class FileReadError(val file: String): Error {
    override fun log() { println(&quot;Error while reading file $file&quot;) }
}
class DatabaseError(val source: String): Error {
    override fun log() { println(&quot;Error while reading from database $source&quot;) }
}
class RuntimeError : Error {
    override fun log() { println(&quot;Runtime error&quot;) }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 클래스들이 모두 다른 파일에 작성되어 있다면, 기존 파일은 전혀 신경 쓰지 않고 다른 파일 하나 더 만들면 됩니다.&lt;br /&gt;하지만 log 말고 다른 메소드들이 계속 생긴다면? 그 때 마다 다른 파일들 모두 찾아야 겠지요.&lt;br /&gt;&lt;br /&gt;이젠 타입은 정해져있고 log()라는 행위 말고, 다른 행위들이 생긴다고 생각해 봅시다.&lt;/p&gt;
&lt;pre id=&quot;code_1685967218220&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun log(e: Error) = when(e) {
    is FileReadError -&amp;gt; { println(&quot;Error while reading file ${e.file}&quot;) }
    is DatabaseError -&amp;gt; { println(&quot;Error while reading from database ${e.source}&quot;) }
    is RuntimeError -&amp;gt;  { println(&quot;Runtime error&quot;) }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 이런식으로 파일 하나에는 하나의 행위만 들어가기 때문에 , 다른 행위를 위해 다른 파일에서만 추가하면&amp;nbsp; 됩니다.&lt;br /&gt;&lt;br /&gt;이번에는 Visitor 패턴이며&amp;nbsp; 이 패턴 역시 OCP에 관련된 기존 구현을 건드리지 않으면서 행위를 수행합니다.&lt;br /&gt;예를 봅시다.&lt;/p&gt;
&lt;pre id=&quot;code_1686203827524&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Itninerary (
    val id : String,
    val route : Route,
    val accomodations: List&amp;lt;Accomodation&amp;gt; = emptyList()
        ){
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;대략 이런 여행계획서 엔터티가 있다고 합시다. 계획서 안에는 어디를 방문하는지에 관한 Route와 숙소들의 리스트가 있습니다. 해당 여행 계획에 대한 경비를 산출해주는 로직을 만든다고 할 경우 가장 먼저 떠오르는 것은 Route와 Accomodation의 내용을 읽어서 더해주면 될 거 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1686204263859&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CostSummaryCalculator {

    fun calc(itninerary: Itninerary): CostSummary {

        var total : BigDecimal = BigDecimal.ZERO
        
        itninerary.accomodations.forEach {
            total.add(it.cost.amount)
        }

        itninerary.route.paths.forEach{
            total.add(it.cost.amount)
        }
        
        // ...

        return CostSummary(Money.of(total, Currency.WON))
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이런식으로 구현 하게 되면 여행 계획에 무엇인가 행위&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(숙박,교통요금,관광..)&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;가 추가 될 때 마다 이 기존 calc 함수는 변경되야 할 것입니다. 그럼 기존 코드는 안건드리고 새로운 코드만 추가해서 해결 하려면 어떻게 해야 할 까요?&lt;/p&gt;
&lt;pre id=&quot;code_1686204717948&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface CostCalc {
    fun addCostsTo(calc: CostSummaryCalculator);
}
interface ItnieraryItem : CostCalc

data class Path(val cost : Money) :  ItnieraryItem {
    override fun addCostsTo(calc: CostSummaryCalculator) {
        calc.addCost(cost)   
    }
}
class Route :  ItnieraryItem{
    val paths = mutableListOf&amp;lt;Path&amp;gt;()
    override fun addCostsTo(calc: CostSummaryCalculator) {
        paths.forEach{
            it.addCostsTo(calc)
        }
    }

    fun addPath(path: Path) {
        paths.add(path)
    }

}

class Accomodation(val cost : Money) : ItnieraryItem{

    override fun addCostsTo(calc: CostSummaryCalculator) {
        calc.addCost(cost) 
    }

}

class Itninerary  (
    val id : String,
    val items: List&amp;lt;ItnieraryItem&amp;gt; = emptyList()
        ){

    fun addCostTo(calc: CostSummaryCalculator){
        items.forEach {
            it -&amp;gt; it.addCostsTo(calc)
        }
    }
}


data class CostSummary (val cost: Money)

class CostSummaryCalculator {

    val list = mutableListOf&amp;lt;Money&amp;gt;()
    fun addCost(money: Money) {
        list.add(money)
    }

    fun getTotal() : BigDecimal {
        return list.sumOf { it.amount }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이렇게 하게 되면 새로운 여행아이템이 등장해도 해당 아이템에만 addCost 메소드를 추가 구현 해 주면 됩니다. Visitor패턴의 accept 가 addCostTo가 될 것이고, Visitor 는 CostSummaryCalulator이며 visit 에는 Money가 들어가네요.&lt;br /&gt;(Visitor패턴이 모든곳에 좋은 건 아니며 위의 코드도 부실합니다. 설명하고자하는 것에 집중된 단순 예제 일 뿐)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>소프트웨어 사색</category>
      <author>[하마] 이승현 (wowlsh93@gmail.com)</author>
      <guid isPermaLink="true">https://hamait.tistory.com/1156</guid>
      <comments>https://hamait.tistory.com/1156#entry1156comment</comments>
      <pubDate>Mon, 5 Jun 2023 20:45:28 +0900</pubDate>
    </item>
    <item>
      <title>새로운 언어를 공부해야 하는 이유 (feat.코틀린)</title>
      <link>https://hamait.tistory.com/1155</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 언어를 가지고 무엇인가 만들다 보면 그 코드의 쓰임새에 있어서 패턴/정형화가 된다.&lt;br /&gt;그것을 구체적으로 이름 붙히고 규율을 정해 놓으면 사람들이 이해하기 더 쉬워 지는건 인지상정.&lt;br /&gt;&lt;br /&gt;예를들어 고양이과 동물을 다 고양이라고 하고, 호랑이에 대해서는 메우 큰 고양이, 사자에 대해서는 목에 털이 많은 고양이라고 말을 해도 알아먹기야 하겠지만, 고양이과를 사자,호랑이로 명확히 나누어서 소통하면 더 빨리 이해 할 수 있게 될 것고 효율적이게 될것이다. 프로그래밍 언어도 마찬가지이다. &lt;br /&gt;&lt;br /&gt;새로운 언어는 보통 이전 언어/ 경쟁 대상 언어보다 더 키워드가 많아 지게 되는데 예를들어&lt;br /&gt;C++에는 없던 interface 키워드가 자바에서는 생겼는데&amp;nbsp; C++에는 비록 interface가 없더라도 class가 그 역할을 겸하였지만, 자바에서는 Interface라고 키워드를 추가하였기 때문에 자바 개발자들은 인터페이스를 적용하는면에 있어서 더 나아졌다. 즉 처음 자바로 프로그래밍을 공부한 사람은 그냥 그게 너무 당연한 Class와 구분된 기능인 것이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;마찬가지로 보통 자바 개발자들은 class에 대해 보통 한가지를 떠올리겠지만. 자바 class를 많이 사용한 자바구루들은 어떤 정형화 된 패턴들을 발견 할 수 있었을 것이다. 이런식으로 사용되는 클래스 저런 제한이 있는 클래스..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린에서는 신생언어 답게 이런 것들을 세분화 시켜서 키워드를 추가하였다.&amp;nbsp;&lt;br /&gt;그런 덕분에 코틀린을 공부한 사람은 자연스럽게 Class를 더 품격(?) 높게 구분해서 사용 할 수 있는 힘을 갖게 되었다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;data class, value class, sealed class, enum class 등이 바로 이런 것들이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>소프트웨어 사색</category>
      <author>[하마] 이승현 (wowlsh93@gmail.com)</author>
      <guid isPermaLink="true">https://hamait.tistory.com/1155</guid>
      <comments>https://hamait.tistory.com/1155#entry1155comment</comments>
      <pubDate>Mon, 5 Jun 2023 20:23:12 +0900</pubDate>
    </item>
    <item>
      <title>내 언어의 한계가 내 세계의 한계다</title>
      <link>https://hamait.tistory.com/1151</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;best-words-and-phrases-for-marketing-intro.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1IagP/btrK1q6uLBJ/QhvjKHrbEyTKkHRKfBwhTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1IagP/btrK1q6uLBJ/QhvjKHrbEyTKkHRKfBwhTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1IagP/btrK1q6uLBJ/QhvjKHrbEyTKkHRKfBwhTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1IagP%2FbtrK1q6uLBJ%2FQhvjKHrbEyTKkHRKfBwhTk%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;800&quot; height=&quot;585&quot; data-filename=&quot;best-words-and-phrases-for-marketing-intro.png&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 말에 대한 비트겐슈타인의 진정한 함의는 모르겠지만,&amp;nbsp; 문장 자체를 그대로 생각해 보면 소프트웨어 개발과 정말 잘 맞는 듯 하다. &lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이에 따라서 개발자는&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;- 영어를 잘 아는게 중요하다?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;- 글쓰기를 잘 하는게 중요하다?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;- 패턴을 잘 아는게 중요하다?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;개발에 추상화란 매우 중요한 요소이다. 소프트웨어 개발에 있어서 추상화를 하기 위해 사용되는 단어들은 생각보다 어려운 단어들이 아니다. Convert, Proxy, Execute, Provider, Factory, Map, Command, Future, Journal, Match, Invocate, Filter, Channel등 근데 막상 개발 할 때 저런 중학생용 영어를 사용하는데 어려움을 겪는다.&lt;br /&gt;&lt;br /&gt;즉 개발에 있어서 영어를 원어민처럼 하는거와는 상관 없어 보인다. 언어의 한계가 의미하는 바는 단어를 많이 아는지 와는 크게 상관없고, 흔한 그 단어들이 얼마나 나의 머리속에 잘 추상화되어 자리잡혀있는지가 중요해보인다. 물론 다양한 어휘를 적재적소에 잘 사용 했던 사람이 코딩을 함에 있어서 유리할 것이란 것은 자명하다. 소프트웨어 개발은 공학이기도 하지만 문학이기도 하니까...&lt;br /&gt;&lt;br /&gt;글쓰기를 잘 하는 건 그런 추상화를 잘 하게 해주는데 큰 도움을 줄 것이다.&lt;br /&gt;만약 당신이 구체적인 것들을 지칭하는 것에 대해서 공통적인&amp;nbsp; 무엇을 뽑아내야 할 때 사용할 만한 단어를 생각해 내는 것은&amp;nbsp;&lt;br /&gt;먼저 공통된 무엇인가를 뽑아낼 감각과 의지가 있느냐와 동시에 어떻게 그것을 구체화 할 것이냐에 대한 기능적인 지식이 있어야 할 것이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;예를들어 각 OS별로 버튼을 그리는 구체적인 방식은 다를지라도 공통적으로 draw 라든지 paint라는 메소드를 사용 할 것이며, 다양한 블록체인에 접근하기 위한 공통메소드로는 connect 라든지 sendTransaction 따위가 있을 것인데, 그것들의 구체적인 구현체를 선택할 때 사용 할 수 있는 인터페이스의 단어로는 Provider, Factory 등을 사용 할 수 있을 것 같다.&lt;br /&gt;&lt;br /&gt;즉 &lt;br /&gt;Factory.of(&quot;Metamask&quot;) ,&amp;nbsp; Factory.of(&quot;Windows&quot;) 라든지&lt;br /&gt;Provider.of(&quot;Metamask&quot;), Provider.of(&quot;Windows&quot;) 식으로 사용 할 수 있을 거란 말이다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;근데 이런&lt;span&gt; 단어들을&amp;nbsp; 떠올리게 하고 영감을 주는데 있어 도움을 주는 것은 평소에 읽기,쓰기를 많이 해본 인문학적 수련이나&amp;nbsp; 수 많은 오픈소스를 읽는 것이겠지만 &lt;/span&gt;선현들이 만들어 놓은 잘 알려진 패턴을 학습하는게 가장 효율적이지 않을까?&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>소프트웨어 사색</category>
      <author>[하마] 이승현 (wowlsh93@gmail.com)</author>
      <guid isPermaLink="true">https://hamait.tistory.com/1151</guid>
      <comments>https://hamait.tistory.com/1151#entry1151comment</comments>
      <pubDate>Tue, 30 Aug 2022 19:22:03 +0900</pubDate>
    </item>
    <item>
      <title>블록체인 업계에 대해서..</title>
      <link>https://hamait.tistory.com/1150</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blockchain-development-company-in-seoul-south-korea.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dGNjLf/btrCWRymYzr/4TFKzB7pMXe60VnYG47cX0/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dGNjLf/btrCWRymYzr/4TFKzB7pMXe60VnYG47cX0/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dGNjLf/btrCWRymYzr/4TFKzB7pMXe60VnYG47cX0/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdGNjLf%2FbtrCWRymYzr%2F4TFKzB7pMXe60VnYG47cX0%2Fimg.webp&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;1024&quot; height=&quot;512&quot; data-filename=&quot;blockchain-development-company-in-seoul-south-korea.webp&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; (이미지 출처 : &lt;a href=&quot;https://www.bitdeal.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.bitdeal.net/&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;블록체인 업계에 대해서 - 블록체인 전직 혹은 블록체인 대학원/국비학원/각종교육으로 고민중인 분들이 &lt;br /&gt;최근 많이 있는 것 같아서 대략 정리 해봅니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;먼저&amp;nbsp;&lt;br /&gt;&lt;br /&gt;블록체인 업계가 앞으로 어떻게 될 지는 아무도 모릅니다. &lt;br /&gt;다만 블록체인 관련한 업계는 최소 10년이상은 건재 할 거 같습니다.&amp;nbsp;&lt;br /&gt;저는 &quot;블록체인&quot;이 탈중앙 디지털 자산의 기반으로 미래에 핵심적인 인프라로 남기를 희망합니다.&amp;nbsp;&lt;br /&gt;(요 문장에서 만큼은 블록체인==이더리움을 말합니다)&amp;nbsp;&lt;br /&gt;&lt;br /&gt;블록체인은 &quot;스포츠&quot; 와 같습니다. 즉 &quot;스포츠&quot; 전문가란 세상에 없습니다.&amp;nbsp;&lt;br /&gt;블록체인에는 수 많은 한 우물을 파야 하는 분야가 있습니다. 그 한 우물을 파는 것에 대해서 우려가 됩니다.&lt;br /&gt;현재 상황에선 물이 안나올 가능성이 매우 크거든요.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;일단 요약 부터 하자면 (범) 블록체인 업계는 크게 아래와 같은 5가지로써 바라보고 있습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #006dd7;&quot;&gt;0. 블록체인 코어 개발 (극소수TO)&amp;nbsp;&lt;br /&gt;1. 대중들 대상 -&amp;nbsp; 블록체인을 화폐(코인)로 보고 보관/교환/투자/투기 하는 것에 대해 집중합니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #006dd7;&quot;&gt;2. 엔터프라이즈(기업) 대상 - 블록체인을 신뢰성 있는 분산DB로써의 역할에 주목합니다.&lt;br /&gt;3. 블록체인의 힘을 빌릴 수도 있고 아닐 수도 있는 응용분야들 (예: SSI 신원인증, 금융권에서 시도중인 STO)&lt;br /&gt;4. 블록체인이라는 이름을 빌려서 (훔쳐서?) 블록체인 본질과 전혀 다른 일을 하는것에 확장합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #006dd7;&quot;&gt;위의4번은 아래내용에는 없습니다. 좀 민감한 주제라...ㅎㅎ&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;자 이제 아래 부터는 블록체인을 공부한다라는 의미와 업계에 대해서 좀 더 디테일하게 설명을 좀 해 보겠습니다&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;블록체인을 공부한다?&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&quot;블록체인&quot;을 공부한다라는 의미를&amp;nbsp; &quot;구조공학&quot;,&quot;아파트/건설&quot;, &quot;부동산&quot; 을 공부 한다로 대략 비교해보겠습니다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #ee2323;&quot;&gt;블록체인 컴퓨팅 시스템 자체를 공부한다..)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 아파트 건설에 필요한 구성 요소인 구조공학기술 그 자체를 깊이있게 공부하는 것을 원하는 가&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&amp;nbsp; &amp;nbsp;-&amp;gt; 블록체인에서 모두 신뢰성 있게 동일한 데이터를 갖게 하기 위한 합의시스템을&amp;nbsp; 공부한다.&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 아파트 건설에 필요한 다른 구성 요소인 전기배선기술에 집중하려는가&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&amp;nbsp; &amp;nbsp;-&amp;gt; 블록체인에서 각 노드간에 서로를 탐색하고, 신뢰성 있게 서로 연결되는 방법을 공부한다.&amp;nbsp;&lt;br /&gt;&amp;nbsp; &amp;nbsp;-&amp;gt; &lt;span&gt;블록체인에서&lt;span&gt; 암호/보안에 관련된 연구를 한다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 아파트를 짓는 기술 전반을 원하는 가&amp;nbsp; (건설사에 들어가려는가?) (이더리움&amp;nbsp; 오픈소스 기여 및&amp;nbsp; ConsenSys, 클레이튼 등 )&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 각 블록체인 전체의 핵심 아키텍쳐를 공부한다. 이와중에 유니크한 체인을 발명해 본다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(+사이드체인,서브체인등)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;4. 각 아파트 건설사들을 감사/유지/보수를 하기 위한 기반 기술에 집중 한다.&amp;nbsp;&lt;br /&gt;&lt;/span&gt;&amp;nbsp; &amp;nbsp; -&amp;gt; 각 블록체인의 노드들을 관리 하며, 네트워크 개선을 위한 투표에 참여한다. (DSRV등)&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #ee2323;&quot;&gt;블록체인 시스템의 존재 의미를 공부한다.)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. 건축이 인간에게 어떤 의미를 갖는지? 어떻게 활용되야 인간을 위하는 길이 되는지 철학적 탐색..&lt;br /&gt;&amp;nbsp; &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp;-&amp;gt; 블록체인의 진정한 쓰임새, 회계시스템, 탈중앙 시스템의 의미와 로드맵에 대한 고민..(블록체인경제연구소)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #ee2323;&quot;&gt;블록체인을 이용하기 편하게 하기위한 솔루션을 공부한다..)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;6. 아파트를 사고 파는 부동산업에 대해 공부하려는가? 부동산 앱을 만들고 싶은가?&amp;nbsp;&amp;nbsp;&lt;br /&gt;&lt;span&gt;&amp;nbsp; &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 탈중앙에 의한 코인이지만, 사용하기 익숙한 중앙에 의해 통제되는 교환/거래/투자 서비스를 만든다. (&lt;span&gt;빗썸, 업비트 등)&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;&amp;nbsp; &amp;nbsp;-&amp;gt; 탈중앙 교환/거래/투자 서비스를 만든다 (UniSwap등)&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;7. 아파트에 대한 소유권을 편하게 관리하기 위한 앱을 만들고 싶은가? &lt;br /&gt;&amp;nbsp; &lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 다양한 코인들을 관리/보관 할 수 있는 지갑을 만든다. (메타마스크,코인베이스지갑등)&amp;nbsp;&lt;/span&gt;&lt;br /&gt;8. 건축을 아마추어가 하기 편하도록 도와주는 중간 도구들을 만들고 싶은가? &lt;br /&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; color: #000000;&quot;&gt;-&amp;gt; 백엔드 개발자가 &lt;span style=&quot;color: #000000;&quot;&gt;블록체인을 몰라도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;기존 DB(JDBC Driver) 사용하듯이 테이블,인덱스,제약,CRUD,SQL,모니터링을 사용하여 블록체인상에 데이터를 관리하게 하기 위한 미들웨어를 만든다.(오퍼스엠)&amp;nbsp;&lt;br /&gt;&lt;span style=&quot;color: #333333; font-family: 'Noto Serif KR';&quot;&gt;9. 아파트를 만들거나 사는 것은 어렵다. 그냥 빌트인 아파트를 저렴하게 제공해서 몸만 와서 월세로 살게 하자.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 블록체인을 설치하고 관리하는 것은 매우 귀찮은 일이다. 해당 부분을 클라우드에서 BaaS(BlockChain as a Service)로 서비스한다. (kaleido,&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;Alchemy,람다256등&lt;/span&gt;)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #ee2323;&quot;&gt;블록체인을 이용한 응용서비스를 공부한다..)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;10. 아파트에 기반한&amp;nbsp; 대출/적금 해주는 금융에 대해 공부하려는가? &lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;-&amp;gt; 블록체인 네트워크 위에서 스마트컨트랙트를 이용해 SWAP, ICO, Crowdsoursing, DeFi 시스템을 구축한다.(상당수 업체)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;11. 아파트에 기반으로 ...... 을 하려는가? (NFT, DID, 메타버스, Web3, 민간SI등등)&lt;br /&gt;&lt;/span&gt;&amp;nbsp; &amp;nbsp;-&amp;gt;&lt;span&gt;블록체인과 내부의 스마트컨트랙트를 이용해 NFT, DID, 메타버스, Web3, 민간/공공 SI시스템을 구축한다.(&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;오픈씨등 &lt;/span&gt;상당수 업체)&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #ee2323;&quot;&gt;블록체인과 다른 기술을 융합 하기 위해 공부한다..)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;12. 아파트와 병원/생태학을 융합 하길 원하는가?&amp;nbsp; (블록체인을 활용한 정부과제들)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;13. 아파트와 인공지능을 융합 하길 원하는가?&amp;nbsp;&amp;nbsp; (블록체인을 활용한 정부과제들)&lt;br /&gt;14. 블록체인을 이용한 증권의 토큰화 (STO)&lt;br /&gt;15. 블록체인을 이용한 자기 주권 신원 (SSI)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #ee2323;&quot;&gt;블록체인을 악용하여 한탕 하려는가?)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;16. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;다단계, &lt;/span&gt;전세 사기 및 부동산 사기를 공부하려는가?&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;17. 아파트로 투기 하는 방법을 공부하려는가?&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #ee2323;&quot;&gt;기타)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;18. 아파트를 사고 팔 때 법적 문제에 대해 공부하려는가? (변호사들)&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;19. 부동산 언론? 부동산 유튜버?&amp;nbsp; 등등&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;위의 모든 것들이 블록체인업계와 링크되어 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;그럼 학원은 무엇을 가르치는가?&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;학원의 교육과정은 &lt;span&gt;위에서 9번을 하기 위함이며, &lt;/span&gt;보통 아래와 같은 순서로 이루어 지는 거 같습니다.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 블록체인 플랫폼 하나를 선정하여 전반적인 이론들을 가르친다. (주로 이더리움)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 이더리움에 데이터를 넣고 빼는 것을 가르친다. 그 과정에서 이더리움의 내부언어(솔리디티)를 가르친다. &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;with ERC21,ERC721&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 자 이제 이더리움은 DB라고 생각하고, 해당 DB를 사용하기 위한 SDK(Web3j)를 가르친다.&amp;nbsp; |&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. 일반적인 웹프론트엔드, 웹백엔드를 가르친다. (Node.js, Spring, Html, CSS, Javascript, React 등)&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5. 그 동안 배운 기술들을 가지고 솔루션을 개발해본다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;결국 IT소프트웨어 생태계의 보편적 기술인 웹프론트엔드,백엔드가 핵심이며,&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;블록체인을 활용하기 위한 기본적인 기술을 양념으로 첨가 하게 됩니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이 기준으로 공부해야할 양은&amp;nbsp;웹프론트엔드, 웹백엔드가&amp;nbsp;압도적으로 많습니다.&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; font-family: 'Noto Serif KR';&quot;&gt;결국 정해진 교육 개월수가 짧다면 모든게 겉핡기이라 어정쩡한 커리큘럼이 될 수 밖에 없습니다.&lt;br /&gt;블록체인에 진심이라면 웹프론트엔드,벡엔드는 줄이고 솔리디티와 블록체인 서비스 환경에 대해서 많은 조사와 공부에 집중하는게 좋습니다. 이때&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;블록체인에 대한 자기 확신이 필요 합니다.&amp;nbsp; 블록체인에 대한 확신에 가득찬 사람의 이야기를 들으면 도움이 될 수 있습니다. 에르메스가 가치를 갖는 것과 포켓몬 카드가 가치를 갖는 것은 신봉자들이 있기 때문입니다. 블록체인도 그런 신봉자들이 많아 진 다면 가치를 갖게 되는 건 당연한거죠. 요즘 특히 NFT,메타버스,Web3 부분에서 그렇습니다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;블록체인을 공부 할 것인가? 말 것인가?&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. 블록체인 그 자체를 개인적으로 공부하는 것은 매우 좋습니다. 훌륭한 분산시스템 + 컴파일러 + 보안 코드들이 오픈되어 있습니다. 하지만 역시 트레이드 오프입니다. 웹 백엔드, 프론트엔드만 해도 공부해야 할 것은 몇 만 페이지일 겁니다. 정해진 시간에 이걸 더 공부 할 수 있다고 생각한다면, 이 일을 더 잘하고 싶다면 블록체인 코어 공부하는 시간이 좀 아깝습니다. 여담인데 코테 수개월 공부 할 시간에 백엔드,프론트 공부를 하면 매우 많은 기반지식 및 실제 서비스 개발에 필요한 업무 지식을 얻을 수 있을 겁니다. 안타깝게도 블록체인&amp;nbsp; 그 자체를 만드는 곳에 취업해서 그 자체를 개발 하는 일을 하게 될 가능성은 매우 가능성이 낮습니다.&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. 블록체인을 신뢰성 있는 DB로 두고&amp;nbsp; 웹프론트,백엔드를 만들고자 할 경우는 대략적인 이더리움 개념을 공부하면 됩니다. 사실 웹프런트엔드,백엔드에만 집중하는게 좋습니다. 블록체인은 블록체인 회사 입사하고 나서 필요한 부분을 공부해도 됩니다.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. 금융,인증등의 어떤 비지니스 로직을 해당 블록체인 내부언어로 블록체인에 넣는 것을 공부하고자 할 때는 그 부분에 대해서 집중적으로 공부해야합니다. 이것도 TO는 일반 벡엔드,프론트엔드 개발자에 비해 극히 적으며, 앞으로 더 많아질 거라 생각치도 않으며 딱히 개발자로서 메리트가 더 있는것도 아닙니다. 그리고 한방(돈)은 개발자와 무관합니다. ㅎㅎ&amp;nbsp; 커리어에 대한 위험부담만 있을 수도 있습니다. 따라서 블록체인의 분산시스템 혹은 회계시스템등 활용에 관한 매력에 빠지신게 아니라면 그냥 하시던거 하는게 좋지 않을까 합니다. 이 분야는 애정이 있는자들의 놀이터가 되길 바랍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 제 블로그의 스폰서의 글입니다. 실제 강의 내용은 저의 의도와 상의 할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록체인 업계와 개발, 이제 좀 감이 오시나요? 개발은 보면서 배우는 것이 아니라 직접 해봐야 하는거 아시죠? 그래서 이번에 패스트캠퍼스에서 지금 가장 핫한 블록체인 DApp 개발부터 클론코딩까지 싹 다 실습해볼 수 있는 무려 100시간 초격차 강의를 준비했다고 합니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20220919_184023.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;567&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cf8B6d/btrMxEXpTbj/RehDUqRykgbUyptUpFXGDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cf8B6d/btrMxEXpTbj/RehDUqRykgbUyptUpFXGDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cf8B6d/btrMxEXpTbj/RehDUqRykgbUyptUpFXGDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcf8B6d%2FbtrMxEXpTbj%2FRehDUqRykgbUyptUpFXGDK%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;456&quot; height=&quot;457&quot; data-filename=&quot;20220919_184023.png&quot; data-origin-width=&quot;566&quot; data-origin-height=&quot;567&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블록체인 DApp 클론코딩만 배우는 것이 아니라 웹개발 기초부터 탄탄히 쌓을 수 있는 커리큘럼이라고 하네요. &lt;br /&gt;1. 블록체인으로만 채운 100시간 커리큘럼 &lt;br /&gt;2. 한 번 구매하면 평생 소장 가능! &lt;br /&gt;3. 웹개발 기초부터 탄탄히! &lt;br /&gt;4. Solidity, Rust, NFT, P2E &lt;br /&gt;5. DApp 개발과 클론코딩 실습 &lt;br /&gt;6. 40가지 기술스텍 습득 &lt;br /&gt;7. 질의응답 커뮤니티 &lt;br /&gt;8. 기술면접 기출문제집까지! &lt;br /&gt;&lt;br /&gt;관심 있으신 분들은 패스트캠퍼스의 '한 번에 끝내는 블록체인 개발 A to Z' 강의를 통해 블록체인 개발자로 거듭나기 위한 인사이트를 얻고 실무에 활용할 수 있는 수준까지 나아가시면 좋을 것 같습니다!&lt;br /&gt;&lt;br /&gt;▶ 지금 바로 강의 보러 가기 &lt;a href=&quot;https://bit.ly/3xy040z&quot;&gt;https://bit.ly/3xy040z&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20220919_184039.png&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qy7v7/btrMt43CF7H/aGp0qx4gQIHjw7bnzCBJ3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qy7v7/btrMt43CF7H/aGp0qx4gQIHjw7bnzCBJ3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qy7v7/btrMt43CF7H/aGp0qx4gQIHjw7bnzCBJ3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqy7v7%2FbtrMt43CF7H%2FaGp0qx4gQIHjw7bnzCBJ3k%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;628&quot; height=&quot;326&quot; data-filename=&quot;20220919_184039.png&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>블록체인</category>
      <category>블록체인</category>
      <category>블록체인 교육</category>
      <category>블록체인 업계</category>
      <category>블록체인공부</category>
      <category>블록체인업계</category>
      <author>[하마] 이승현 (wowlsh93@gmail.com)</author>
      <guid isPermaLink="true">https://hamait.tistory.com/1150</guid>
      <comments>https://hamait.tistory.com/1150#entry1150comment</comments>
      <pubDate>Mon, 23 May 2022 19:46:24 +0900</pubDate>
    </item>
  </channel>
</rss>