<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>커브볼의 생존일지</title>
    <link>https://hynwlee.tistory.com/</link>
    <description>개인적인 일상과 회고를 기록하는 블로그입니다.</description>
    <language>ko</language>
    <pubDate>Fri, 12 Jun 2026 09:15:14 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Kirbyyy</managingEditor>
    <image>
      <title>커브볼의 생존일지</title>
      <url>https://tistory1.daumcdn.net/tistory/8345311/attach/4c55187e1b3a49e8a2ccf85e84ffcf4d</url>
      <link>https://hynwlee.tistory.com</link>
    </image>
    <item>
      <title>Closure란 무엇인가?</title>
      <link>https://hynwlee.tistory.com/61</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Closure?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Closure를 처음 들었을 때, 도대체 뭘 뜻하는 것인지 감이 잘 오지 않았다. 이러한 일을 겪을 땐 우선 사전적 의미를 먼저 찾아보곤 한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ9fHB/dJMcaipGzw0/cGRqh5cHSiwPBgQu4OCFK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ9fHB/dJMcaipGzw0/cGRqh5cHSiwPBgQu4OCFK0/img.png&quot; data-alt=&quot;https://dictionary.cambridge.org/dictionary/english/closure&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ9fHB/dJMcaipGzw0/cGRqh5cHSiwPBgQu4OCFK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ9fHB%2FdJMcaipGzw0%2FcGRqh5cHSiwPBgQu4OCFK0%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;466&quot; height=&quot;260&quot; data-origin-width=&quot;611&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://dictionary.cambridge.org/dictionary/english/closure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대강 동작이 중지된, 닫힌 상태를 나타내는 것으로 보인다. 그러고 보니, 수학 시간에 Closure라는 의미를 들어봤던 것 같았다. 이에 관련하여 찾아보니, 다행히도 내 기억이 맞았다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8ETYA/dJMcafGvnxe/SjpJX1k4YW8mzrNO8SmAy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8ETYA/dJMcafGvnxe/SjpJX1k4YW8mzrNO8SmAy1/img.png&quot; data-alt=&quot;https://ko.wikipedia.org/wiki/%ED%8F%90%ED%8F%AC_(%EC%88%98%ED%95%99)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8ETYA/dJMcafGvnxe/SjpJX1k4YW8mzrNO8SmAy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8ETYA%2FdJMcafGvnxe%2FSjpJX1k4YW8mzrNO8SmAy1%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;891&quot; height=&quot;94&quot; data-origin-width=&quot;891&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://ko.wikipedia.org/wiki/%ED%8F%90%ED%8F%AC_(%EC%88%98%ED%95%99)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수학시간에서의 Closure는 다른 집합에 속하지 않을 가능성이 닫혀있다? 라는 의미로 생각했던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;프로그래밍에서는 뭔데?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 이정도 찾아봤으면 대충 뭐하는 친구인지 예상이 가기 마련이었는데, 이번엔 감이 잘 안잡혔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;191&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OYldN/dJMcacJNfCh/51fyawoRxfNZdwzBU3WFZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OYldN/dJMcacJNfCh/51fyawoRxfNZdwzBU3WFZ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OYldN/dJMcacJNfCh/51fyawoRxfNZdwzBU3WFZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOYldN%2FdJMcacJNfCh%2F51fyawoRxfNZdwzBU3WFZ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;192&quot; height=&quot;191&quot; data-origin-width=&quot;192&quot; data-origin-height=&quot;191&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&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czAwyY/dJMcagyBNtg/eFaINyc65BFcO6ToDOGnH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czAwyY/dJMcagyBNtg/eFaINyc65BFcO6ToDOGnH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czAwyY/dJMcagyBNtg/eFaINyc65BFcO6ToDOGnH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczAwyY%2FdJMcagyBNtg%2FeFaINyc65BFcO6ToDOGnH1%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;932&quot; height=&quot;81&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lexical?, Lexical scope? (&lt;s&gt;나에게 매우&lt;/s&gt;)어려운 단어가 나왔지만, 일단 &lt;b&gt;first-class function&lt;/b&gt;를 보니, 일급 함수와 관련된 내용인 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lexical에 대한 힌트도, &lt;b&gt;storing a function together with an enviornment&amp;nbsp;&lt;/b&gt;에서 얻어볼 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로, 요지는 &quot;scope&quot;(범위)다. 즉 어떤 변수의 값을 무엇을 기준으로 결정할 것인가? 에 대한 약속에 대한 내용이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이야기가 조금 산으로 가는 것 같지만, 이런 규칙을 왜 정의해야 했을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;컴퓨터는 바보다...&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 언어에서 &quot;자유 변수&quot;를 사용하기 위해서이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자유 변수는 현재 블록에서 선언되지 않고 사용되는 변수이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777994053823&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun outer() {
    val x = 10

    fun foo() {
        println(x) // x는 자유 변수
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo 함수의 x는 foo 함수 내에서 선언된 것이 아님에도 사용되고 있다. 이렇게 사용하는 것이 너무 당연하게 생각될 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 전역 변수로 선언해둔 것이 아니라면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 타임에는 어떤 함수의 스택 포인터를 알 수 없기 때문에(런타임에 호출 시 할당되므로) 컴파일 할 때, 컴파일러는 x라는 변수의 주소를 알 방법이 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법에 대한 해결책은 다음이 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 쓰지 마셈!! (컴파일 할 때 에러)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;이 경우에는 자유변수를 사용하지 못하므로, 다른 scope에서 어떤 변수를 사용하려면 파라미터로 다 받아야 할 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 모든 변수를 전역 변수화(컴파일할 때 주소를 전부 결정되도록)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;큰일날 것 같긴 하다. 우선 당장 생각나는 문제점은 메모리 낭비, 그리고 멀티 스레드 환경에서의 취약성도 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 선택지 중에는 딱히 만족스러운 선택지는 없는 것 같다. 이러한 상황에서 lexical, dynamic scope을 이해해보자&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;lexical scope&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 lexical scope은 함수가 정의된 위치를 기준으로 자유 변수를 찾는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777996043081&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x = 10

fun printX() {
    // 컴파일 타임에는 wrapper의 x를 모른다. 
    // 전역 변수 x를 캡쳐해간다.
    println(x) 
}

fun wrapper() {
    var x = 20 
    printX()
}

fun main() {
    wrapper() // println(10)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;printX 함수의 변수 x를 중점으로 코드를 살펴보자, lexical scope을 기준으로 삼는다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정의된 위치를 기준으로 자유 변수를 찾는 것이므로,&amp;nbsp; 컴파일러는 전역 변수 x를 자유 변수로 고를 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자유 변수로 고른다, 라는 것은 아마 컴파일 시 해당 함수 스택 내부의 멤버로 추가해 줄 것으로 생각된다. 혹은 힙에 이동시킨다거나.. 이에 대한 세부 구현은 잘 모르겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;dynamic scope&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dynamic scope은 이름에서 예상할 수 있듯이 동적으로, 즉 런타임에 결정한다. 즉 런타임이 함수 호출 스택을 거슬러 올라가며, 자유 변수를 찾는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 동작을 생각해보면 다음과 같을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 1. 우선 해당 함수 내의 스택에서 못찾음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 2. 이전 스택(해당 함수를 호출한 함수의 스택)으로 이동하여 찾음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; 3. 찾을 때까지 반복...&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1777997108272&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x = 10

fun printX() {
    println(x) 
}

fun wrapper() {
    var x = 20 
    printX()
}

fun main() {
    wrapper() 
    // wrapper를 호출한다. 
    // wrapper내에서 printX를 호출한다. 
    // x가 없네? wrapper의 스택으로 이동
    // wrapper에서 x를 찾음!
    // println(20)
}&lt;/code&gt;&lt;/pre&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;b&gt;둘 중에 뭘 사용하는게 좋을까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 어떤 문제가 있었고 이에 대한 해결 방법들을 살펴봤다(안쓰기, 전역변수화 하기, lexical scope, dynamic scope)&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;그런데, 최근 언어들은 전부 lexical scope 방식을 사용하고 있다. 왜 dynamic scope 방식은 사용하지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러저러한 이유를 찾아봤지만, 내 생각에는 다음과 같은 이유가 가장 큰 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dynamic scope 방식을 사용했을 때는 함수의 동작을 예측하기 힘들다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 함수라는 것이 다양한 output을 내놓을 수 있다지만, dynamic scope을 사용하게 되면, &lt;br /&gt;함수의 주변 context에 따라 선정될 수 있는 자유변수가 다를 수 있기 때문에, 호출한 부분의 주변 context에 직접적으로 영향을 받게된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 언어를 사용해본 것은 아니기에 단언할 수는 없지만, 자유 변수를 못 찾을 가능성도 있으므로, 런타임에 프로그램이 터질 수도 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;결국 그래서 Closure는 뭔데?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 먼 길을 온 감이 있어 원래 탐구하고자 한 Closure에 대한 기억이 희미할 것 같다. 하지만, 이제 다시 Closure의 정의를 보면 다르게 보일 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czAwyY/dJMcagyBNtg/eFaINyc65BFcO6ToDOGnH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czAwyY/dJMcagyBNtg/eFaINyc65BFcO6ToDOGnH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czAwyY/dJMcagyBNtg/eFaINyc65BFcO6ToDOGnH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczAwyY%2FdJMcagyBNtg%2FeFaINyc65BFcO6ToDOGnH1%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;932&quot; height=&quot;81&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 Closure는&amp;nbsp; lexical scope 규칙에 의거하여 자유변수를 들고 있는 어떤 함수의 상태, 캡쳐본이다. 다른 말로 해보면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lexical scope는 자유변수를 정하기 위한 규칙이고, Closure는 이를 이용한 결과물이라고 할 수 있을 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kotlin</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/61</guid>
      <comments>https://hynwlee.tistory.com/61#entry61comment</comments>
      <pubDate>Wed, 6 May 2026 01:50:36 +0900</pubDate>
    </item>
    <item>
      <title>[우테코 8기 모바일 안드로이드] 레벨 1 회고</title>
      <link>https://hynwlee.tistory.com/60</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;저번 주에 레벨 1이 끝이 났다.. 이제 내일이면 레벨 2의 첫 날이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 굉장히 허겁지겁 지나간 느낌이 들어, 글쓰기 한번에 전부 내 생각을 정리할 수 있으리라는 생각은 들지 않는다. 생각나는 대로 돌아와서 글을 추가해야겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선은 레벨 1을 마무리하면서, 당장 생각나는 것들을 돌아보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인간은 적응의 동물이다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 6시 반에 기상해야 한다는 사실이 조금 부담이 되기도 했다. 실제로 레벨 1을 시작하고 며칠동안은 아침에 굉장히 피로했다,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 레벨 1을 돌아보면서 아침 일찍 일어나는 것이 크게 부담이 되었다는 생각은 들지 않았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿐만 아니라 차 멀미를 굉장히 많이하는 체질인데, 이 역시 버스를 매일 3시간 정도 타니까 거의 좋아졌다. 때문에 할일이 매우 많은 날에는 버스에서 노트북으로 과제를 하는 경지까지 도달했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;버스에서 안자는 법 아시는 분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 말했지만, 급박할 때의 경우이고, 버스 안에서의 절반 이상을 잠과 함께하는 것 같다. 그렇지만 버스안에서 몰려오는 잠은 뭔가 불가항력적이다는 생각도 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로의 2레벨 기간에는 잠이 몰려오기 전에 급한 과제가 없다면, 내 생각을 러프하게 정리하는 습관이라도 들여보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;나만의 기준을 찾아가자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨을 진행하면서, 아직 부족한 부분이 많다는 것을 느낀다. 특히 미션을 하면서 A안, B안.. 등 여러 갈림길이 있고 이 중 하나를 선택해야 할 때, 어려움을 많이 느꼈던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들이 공감할 부분이라 생각되는데, 장점만 있는 선택지는 거의 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각의 방법마다 장단점이 있었고, 심지어는 단점만 있는 방법들 중에서 골라야 할 때도 있었다. 물론 정해진 답이 없는 문제도 많지만, 어떤 것을 골라야 좋은 선택일지 잘 감이 오지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 고민 때문에, 페어프로그래밍을 할 때, 페어의 의견을 너무 긍정적으로 수용한 감이 없지 않아 있다, 리뷰어들의 피드백도 마찬가지였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;감사하게도 페어들이나 리뷰어들이 제공해준 몇몇 피드백 중에서도 이를 좀 더 개선하면 좋겠다는 피드백들이 있었다. 이를 해결하기 위해서는 나만의 기준을 세워야 겠다는 생각이 계속 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어딘가 아픈 코드 짜보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 최대한 내가 불편함을 느껴야 하겠다는 생각이 들었다. 때문에 &quot;이게 좋다던데? 나도 써볼까?&quot; 를 최대한 지양했다.&amp;nbsp; 그러다 보니, 약간은 극단적으로 결과물이 나왔던 것 같다. 컴포넌트들이 매우 잘게 쪼개진다던가, 모든 State들과 관련 로직들이 들어있는 하나의 거대한 스테이트 홀더가 만들어진다던가, 등등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 보니 리팩터링을 진행하면서, 테스트 코드를 작성하면서, 추가적인 요구사항을 만족시키면서 문제가 발생했고, 그제서야 문제를 인식하고, 해결책을 모색하는 과정에서 나만의 기준과 근거를 잘 세워볼 수 있었다. 물론 이 방법은 얻는 바가 큰 만큼 시간도, 노력도 많이 든다&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 리뷰어들의 리뷰로 채울 수 있었다. 어떤 문제에 있어서 정답을 바로 제시해주는 게 아니라, 어떤 문제가 발생할 수 있겠어요~ 라는 리뷰를 남겨주시어, 이에 대한 문제를 인식하고 이에 대한 해결책을 고민해볼 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로는 어떤 해결책, 솔루션을 선제적으로 제공하는 것보다 &quot;문제인식 -&amp;gt; 해결책 고민&quot;의 과정에서 자연스럽게 찾아가야겠다는 생각이 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 많이 돌아왔는데, 아무튼 나만의 기준을 잘 쌓아 나가야겠다, 레벨 2에서 만날 페어들은 각오하는 것이 좋을 것이다..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbLnzV/dJMcadPhhvf/zD6FQuShwkUsupUE2L3Kck/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbLnzV/dJMcadPhhvf/zD6FQuShwkUsupUE2L3Kck/img.jpg&quot; data-alt=&quot;다소 의역&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbLnzV/dJMcadPhhvf/zD6FQuShwkUsupUE2L3Kck/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbLnzV%2FdJMcadPhhvf%2FzD6FQuShwkUsupUE2L3Kck%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;321&quot; height=&quot;288&quot; data-origin-width=&quot;321&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;다소 의역&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기초는 어렵다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨 1을 지내면서 크루들과 가장 많은 의견을 나눈 부분은 의외로 기본적인 부분이었다고 생각한다. 의외로 기본적인 부분일수록 이유없이 사용하고 있다는 것을 깨달았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모 크루가 &quot;도메인&quot; 에 대한 지식이 무너졌다고 말을 했다. 그순간 나도 &quot;도메인&quot;이라는 것의 범위가 어디서부터 어디까지인지, 그리고 정확히 무엇을 나타내는 것인지 명쾌하게 설명할 수 없다는 것을 깨닫게 되었다...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1001&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CAYAl/dJMcacXblgN/aVBGsiFtTBYfuu97y2Y3G1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CAYAl/dJMcacXblgN/aVBGsiFtTBYfuu97y2Y3G1/img.png&quot; data-alt=&quot;나노 바나나 2 성능 기가막힌데요...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CAYAl/dJMcacXblgN/aVBGsiFtTBYfuu97y2Y3G1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCAYAl%2FdJMcacXblgN%2FaVBGsiFtTBYfuu97y2Y3G1%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;402&quot; height=&quot;393&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;1001&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;나노 바나나 2 성능 기가막힌데요...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 뒤로, 모델과 도메인은 무슨 차이인지 등등 여러 의문들로 이어졌다. 물론 이는 지금도 100퍼센트 명쾌하게 설명할 수 있지는 않다. 맥락에 따라 다르겠지만, 소프트웨어 세상에서의 모델과 도메인을 간단하게 생각해보면. 모델은 말그대로 객체를 모델링 해놓은 것. 도메인은 그러한 모델을 가지고 어디선가? 건네준 데이터들을 처리하는... 정도로 이해하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비슷하게 불변의 사용에 대해서도 무너짐 당했다... 그래도 다시 잘 쌓고 있다~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;회고도 열심히 쓰고, 질문도 열심히 하자!&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레벨 1의 마지막에 레벨로그 인터뷰를 진행했다. 진지한 듯, 편안한 듯 한 분위기에서 면접 같은 느낌으로 진행했는데, 재밌게 참여했다! 하지만 재밌었던 것과는 별개로,&amp;nbsp; 내가 알고 있다고 생각한 것도 사실은 그렇지 않있던 경우가 꽤 많음을 느껴서 아쉬움도 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 어떤 것을 진정 알고 있다고 말할 수 있으려면, 다른 사람들에게 이를 조리있게 설명할 수 있어야 하겠다는 생각이 들었다. 그러니까 내 머릿속에 있는 것을 밖으로 한번은 뱉어봐야 완성되는 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 어떤 방법이 있을까? 다른 사람들 앞에서 설명하기(수업시간에 질문하기, 방과후 수업, 테코톡, 혹은 옆에 지나가는 사람 잡아서 토론하기), 혹은 블로그 글쓰기 등이 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 보니 가장 만만한 것은 글쓰기, 수업시간에 질문하기이다. 앞으로 이 둘 부터 열심히 해봐야겠다는 생각이 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;### 결론&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2레벨의 나, 열심히 하자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5IH7i/dJMcaciBcZt/v9ppSa3BfWb3dqNf15Cpdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5IH7i/dJMcaciBcZt/v9ppSa3BfWb3dqNf15Cpdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5IH7i/dJMcaciBcZt/v9ppSa3BfWb3dqNf15Cpdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5IH7i%2FdJMcaciBcZt%2Fv9ppSa3BfWb3dqNf15Cpdk%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;381&quot; height=&quot;353&quot; data-origin-width=&quot;1246&quot; data-origin-height=&quot;1154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/60</guid>
      <comments>https://hynwlee.tistory.com/60#entry60comment</comments>
      <pubDate>Sun, 26 Apr 2026 18:46:51 +0900</pubDate>
    </item>
    <item>
      <title>[우테코 8기 모바일 안드로이드] [잡소리] 회고 쉬었음 청년, 글쓰기는 코딩이다</title>
      <link>https://hynwlee.tistory.com/56</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;회고 쉬었음 청년&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cr9zbE/dJMcadhpEHC/cHCdMS51IjbfbqM1h03lYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cr9zbE/dJMcadhpEHC/cHCdMS51IjbfbqM1h03lYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cr9zbE/dJMcadhpEHC/cHCdMS51IjbfbqM1h03lYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcr9zbE%2FdJMcadhpEHC%2FcHCdMS51IjbfbqM1h03lYK%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;307&quot; height=&quot;300&quot; data-origin-width=&quot;471&quot; data-origin-height=&quot;461&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;b&gt;&quot;아 이거 안되겠다.&quot;&lt;/b&gt; 라는 생각과 함께 회고를 멈춰버렸다 ㅎㅎ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;역시 내게 병행은 꽤나 힘들다... 평소 가볍게 즐기던 게임들도 우테코 레벨 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;그렇지만, 회고 작성 어려움에 대한 이유와, 나름대로의 해결방법을 찾을 수 있어, 오히려 좋았던 것 같다. 글쓰기, 회고에 대한 내 생각을 간단히 정리해보려고 한다.&lt;/p&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;지금 까지 내 생각: 이왕 하는 김에..&amp;nbsp;&lt;/h3&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;&lt;b&gt;&quot;이왕 한번 할 때, 잘 하는 것이 맞다.&quot;&lt;/b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;라는 생각이 있었다.&amp;nbsp;&lt;/span&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;내가 너무 처음부터 완벽한 글(거의 80퍼 이상의 완성도)을 써야한다. 라는 생각이 있었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 다른 부분에서 이러한 마인드셋이&amp;nbsp; 옳을지도 모른다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 취미인 프라모델 조립에서는, 현재 맡겨진 태스크를 완벽하게 해둘수록 다음 태스크의 능률도 올라가고, 다 맞추었을 때 만족감도 올라간다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습의 경우에도 거의 마찬가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 글쓰기의 경우에서는, 이런 투자 대비 효율이 그렇게 높지 않다는 것을 이번 미션 기간에&amp;nbsp; 많이 느꼈다. 우테코 특성 상, 중간중간 피드백이라던가, 간단한 회고를 작성해야 하는 때가 많았는데.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내 마음에 들 때까지 쓰고 지우고 쓰고 지우고를 반복했던 것 같다. (&lt;s&gt;피드백 작성이 늦을 때마다 내게 잡도리를 제공해준 크루들에게 감사하다..&lt;/s&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;그렇지만, 시간을 많이 투자한다고 당장 내 마음에 드는 글이 나왔던가? 라고 물어보면 거진 9할은 아니었던 것 같다. 심지어 마음에 드는 1할의 글도, 지금 다시 보면 분명 고치고 싶은 부분이 있을 확률이 매우 높다.&lt;/p&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 글을 쓸 때. 완벽하게 쓰기보단, 그 때 내가 들었던 감정, 혹은 생각에 대해 러프하게 기술해 두었다가, 나중에 이를 회고하는 방법이 나와 잘 맞다는 것을 깨닫게 되었다!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재밌게도 지금 문득 생각해보니, 글쓰기도 프로그래밍과 비슷하다는 생각이 든다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍 또한, 처음에 완벽하게 만드려고 하다보면, 어렵다. 하지만, 돌아가는 쓰레기를 만들어두고 이를 리팩토링 하자는 마인드로 임하면, 배울점도 더 많고, 시작하기도 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 역시 관성이 존재하는 것 같다. 이 글을 쓰는 지금도, 쓰다보니, 완벽한 글을 쓰려고 내 무의식이 명령하고 있다.(소제목까지 나눠놓다니..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 이정도로 줄이고, 이제 다른 미션 회고도 작성해보려고 한다~&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/56</guid>
      <comments>https://hynwlee.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 19 Apr 2026 15:14:44 +0900</pubDate>
    </item>
    <item>
      <title>[우테코 8기 모바일 안드로이드] 레벨 1 미션   사용자가 있는 Gemini 웹앱 출시하기 회고</title>
      <link>https://hynwlee.tistory.com/55</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;좋은 문서란 무엇일까?&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오티에서 &quot;좋은 문서는 글이 적게 있다&quot; 라는 이야기를 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 세세하게 작성된 문서, 특히 공식적인 문서에서는 웬만큼 많은 것이 아니면 다다익선이지 않을까? 라는 생각이 있었다. 그러한 관점에서 이는 굉장히 새로운 시각이었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;제미나이 캔버스의 공식 문서 읽어보기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0070d1; text-align: start;&quot;&gt;&lt;a href=&quot;https://gemini.google/kr/overview/canvas/?hl=ko&quot;&gt;https://gemini.google/kr/overview/canvas/?hl=ko&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제미나이 캔버스의 공식 문서가 위에 언급한 &quot;글이 적은 문서&quot;의 예시라고 할 수 있을 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;609&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qEvSo/dJMcacWu1cs/Bu4QQamYzUeRNrYEKLXSIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qEvSo/dJMcacWu1cs/Bu4QQamYzUeRNrYEKLXSIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qEvSo/dJMcacWu1cs/Bu4QQamYzUeRNrYEKLXSIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqEvSo%2FdJMcacWu1cs%2FBu4QQamYzUeRNrYEKLXSIk%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;582&quot; height=&quot;383&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;609&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적인 사용사례 설명 항목이 더 있지만, Canvas 자체에 대한 설명은 이게 다이다. 이러한 문서는 이와 관련된 전문가들이 어떻게 함축할지, 표현할지 고심하여 선정한 단어이기 때문에 자세히 뜯어보는 것이 참 좋다고 하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빠르게 훑었을 때 놓쳤던 것들이 확실히 정독을 하니 달라보였다. 오티 때 이에 대해 나눴던 이야기들 중 몇 개를 소개하면 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 왜 Canvas인가? &lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Canvas라는 이름에 대해 의문을 품어본 적은 없었던 것 같다. 간단하게 생각해봤을 때, 프로그래머가 아닌 사람들에게 좀 더 친숙하게 다가가기 위해서지 않을까? 라는 이야기를 나눴던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;왜 &quot;앱, 게임, 대화형 퀴즈, 웹페이지, 인포그래픽&quot; 에서 굳이 앱, 게임을&amp;nbsp; &quot;앱&quot;으로 묶지 않고 분리했을까?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 부분이 난해하면서도 굉장히 흥미로운 부분이었던 것 같다. 분명 따지면 게임도 앱인데 왜 콕 집어서 게임만 빼놨을지 궁금했다. 내 나름대로 생각해봤을 때는 &quot;사용자가 다르니까?&quot;, &quot;게임에 대한 수요가 크다보니 따로 분리했나?&quot; 등의 생각을 했던 것 같다.&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;color: #333333; text-align: start;&quot;&gt;Canvas가 게임도 잘 생성하는데,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 이를 사용자들에게 명시해주기 위해 이렇게 했을 수도 있을 것 같다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;미션 개요&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 보면 제미나이 캔버스를 이용하여 단순히 웹앱 4개만 만드는 줄 알았으나 다음과 같은 특이사항이 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 일단 내가 쓸 앱을 만들자(나만 쓰더라도 나에게만 유용하면 OK)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 프롬프트 만으로 웹앱 만들기(코드 직접 수정하지 말 것!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 앱 하나는 페어 프롬프팅으로 페어와 함께 진행할 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 4개 앱 중 최소 1개에 Gemini AI 기능을 추가할 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. HTML/CSS/JavaScript 만으로 구현할 것&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;주제 정하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주요 사용자 층을 타겟하는 것이 아닌 우선 나를 위한 앱을 만드려고 계획하니 주제도 잘떠오르고 재미도 잘 붙었던 것 같다! 주제는 정말 금방 정했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유틸 부분은 내가 이전에 진행하려다가 말았던 사진, gif를 아스키 아트로 변환하는 프로그램을 만들어봤다. 이걸 고른 이유는 한창 리눅스를 배울 겸 콘솔 환경에서 개발을 하던 때가 있었는데, 그때 삭막한 콘솔 환경에서 무려 불멍을 할 수 있게 해줬던 프로그램이 생각났다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;401&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GGbV0/dJMcahXPe2Y/iKUqotdl4AgQlWvdU8ykWk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GGbV0/dJMcahXPe2Y/iKUqotdl4AgQlWvdU8ykWk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GGbV0/dJMcahXPe2Y/iKUqotdl4AgQlWvdU8ykWk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/GGbV0/dJMcahXPe2Y/iKUqotdl4AgQlWvdU8ykWk/img.gif&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;467&quot; height=&quot;234&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;401&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 글자들을 이용하여 불을 피워주는데 옆에 띄워놓고 개발하면 꽤 운치있어서 애용했던 기억이 있다. 이런 느낌으로 내가 좋아하는 사진이나 영상, gif를 개발자 감성으로 변환해 사용하면 유용?하겠다는 생각이 들었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blonME/dJMcacI0kKs/MhNvMRCbCbYMdVboBxRhb0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blonME/dJMcacI0kKs/MhNvMRCbCbYMdVboBxRhb0/img.gif&quot; data-alt=&quot;자매품으로 수족관 프로그램도 있다(asciiquarium..)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blonME/dJMcacI0kKs/MhNvMRCbCbYMdVboBxRhb0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/blonME/dJMcacI0kKs/MhNvMRCbCbYMdVboBxRhb0/img.gif&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;464&quot; height=&quot;261&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;자매품으로 수족관 프로그램도 있다(asciiquarium..)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 앱으로는 내가 평소 코딩할 때 오타가 많은 편이라 개발자 코딩 연습 게임을, 학습 앱으로는 코틀린의 null-safety 개념을 실제로 적용해볼 수 있는 퀴즈 게임을 만들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;첫 페어 미션!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우테코에 페어 미션, 특히 페어 프로그래밍이 있다는 것은 알고 있었지만 이렇게 바로 진행할 줄은 몰랐다! 페어는 연극조 내에서 선정되었는데 나는 로미, 아오와 함께 3명이서 페어 미션을 진행하게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 페어가 잘 쓸 수 있을만한 앱을 만들어야 하기 때문에 주제 선정에 좀 더 심혈을 기울이자고 의견을 모았고 실제로 첫 회의시간의 절반정도를 넘게 사용했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 다행인 점은 우리 셋 모두 관심사가 크게 어긋나지 않았다는 점이었다! 그런데 물론 그럼에도 3명이서 사용할만한 앱을 바로 고안해 내는 것은 꽤 어려웠다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고심 끝에 임시로 정했던 주제는 경찰과 도둑, 숨바꼭질 같은 추억놀이를 위한 사람 모으기 앱이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 임시냐면, 과연 이걸 제미나이 캔버스가 과연 만들어줄 수 있을까? 라는 불확실성을 처음에 다들 가지고 있었다. 통상적으로 위 서비스는 멀티유저 서비스이기 때문에 서버가 필요하거나, 적어도 클라우드 환경에 우리가 직접 DB를 만들어서 제미나이 캔버스에게 알려줘야 하지 않을까? 하는 의문이 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이게되네&lt;/b&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;놀랍게도 잘 동작했다. 최종 결과 코드를 봤을 때 알아서 Firebase를 설정하고 관련 API도 잘 사용하는 것 같았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WII49/dJMcacI0uap/WUXth76yG1MLIhFBGEkG50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WII49/dJMcacI0uap/WUXth76yG1MLIhFBGEkG50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WII49/dJMcacI0uap/WUXth76yG1MLIhFBGEkG50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWII49%2FdJMcacI0uap%2FWUXth76yG1MLIhFBGEkG50%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;665&quot; height=&quot;239&quot; data-origin-width=&quot;765&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;물론 내가 파이어베이스에 대해 자세히 공부해 본 적은 없기 때문에 이 방법이 정말로 실 서비스에 적용할 정도인지는 잘 모르겠으나, 아무튼 멀티유저 서비스를 뚝딱 만들어 준 것은 굉장히 놀라웠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;본격적인 페어(트리플?) 프롬프팅!&lt;/b&gt;&lt;/h3&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;1. 프롬프터는 프롬프팅만!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 네비게이터는 프롬프터에게 프롬프팅의 방향을 제시!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 우리는 3명이므로 프롬프터 1 네비게이터 2의 체제로 진행하기로 했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 한번도 경험해보지 못했던 진행 방식이기 때문에 정말 재밌게 진행했던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네비게이터를 할 때는 어떻게 전달해야 프롬프터가 잘, 쉽게 프롬프팅을 할 수 있을 지, 고민하며 수정, 요청 사항을 전달했고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프롬프터를 할 때는 어떻게 프롬프트를 짜야 네비게이터들의 요구사항을 잘 나타내면서, 제미나이 캔버스가 잘 이해할까 고민해볼 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 나 혼자 AI를 사용할 때는 단순히 AI만 고려해서 사용했다면 페어 프롬프팅은 AI 툴 하나를 여러 명이서 사용하다보니, AI 뿐만 아니라 페어까지 고려하면서 사용해야만 했다.&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;이건 여담이지만 우리 조는 네비게이터가 2명이었기 때문에 매 프롬프팅 마다 네비게이터 간에 토론이 발생했는데 덕분에 좋은 결과물이 나왔다. (물론 시간은 좀 더 걸렸다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;그래도 제미나이 캔버스, 마냥 만능은 아니었다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서는 제미나이 캔버스의 장점만 소개한 것 같은데, 제미나이 캔버스를 사용하면서 불편했던 점을 이야기해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 이 친구, 시키지 않은 일 하기를 참 좋아한다. 기능을 마음대로 추가,삭제하고 특히. UI/UX 부분은 거의 일관성 없이 매번 바뀌었다. 어쩐지 기능 구현을 다 완료시킨 후에 UI/UX 개선을 요청하라는 가이드가 괜히 있는게 아니라는 생각이 들었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 사고모드를 사용하지 않았을 때 잔버그들이 많이 발생했다. 앱 실행 중에 크래시가 발생한다거나, 요청한 내용이 제대로 반영되지 않는다거나 등등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;최종 결과물!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유틸리티 앱(아스키 아트 변환기): &lt;a href=&quot;https://gemini.google.com/share/fd3c1637403d&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gemini.google.com/share/fd3c1637403d&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 앱(데브 타자 연습): &lt;a href=&quot;https://gemini.google.com/share/7af5c17ae2f8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gemini.google.com/share/7af5c17ae2f8&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학습 앱(코드 데드 3D)&lt;a href=&quot;https://gemini.google.com/share/0210a668e718&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;: https://gemini.google.com/share/0210a668e718&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페어 앱(추억놀이: 동네모임): &lt;a href=&quot;https://gemini.google.com/share/3aa813ee9016&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://gemini.google.com/share/3aa813ee9016&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;데모데이!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;금요일은 데모데이였다. 우선 만든 앱을 연극조 간에 시연하였는데 다들 재밌는 서비스들을 많이 만들어와서 정말 재밌었다. 연극조 내의 공유가 끝난 후, 간단한 피드백을 주고받고 약간의 수정을 거친 후 다른 마을의 연극조와 합쳐서 다시금 공유 시간을 가졌다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 연극조는 백엔드 마을의 연극조와 공유를 진행했는데, 난 봉구스 크루와 함께 각자의 서비스, 인상깊었던 서비스를 공유했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;봉구스는 특히 자신이 키보드만 사용하여 앱, 컴퓨터를 조작하는 것을 선호한다고 소개하며 자신이 만든 서비스에도 마우스를 이용하여 조작할 수 있도록 해놓았다고 했는데 정말 자신이 사용하기 좋게 잘 만들었다, 라는 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음으로는 페어 중 한 명만 자신이 인상깊었던 서비스를 다시금 소개하는 시간을 가졌는데, 난 특히 백엔드 루디의 앱이 정말 인상깊었다. 그도 그럴 것이, 루디는 제미나이 캔버스를 넘어 아예 유튜브 쇼츠 시청 개수를 추적하여 알림을 보내는 크롬 익스텐션을 만들었다..! 물론 이를 위해 약간의 설정이 필요하긴 했으나, 굉장히 신기했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;마치며...&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우테코 생활을 시작하면서 가장 먼저 마무리한 미션이었다. 미션 시작 전엔 금방 끝날 수 있을 줄 알았으나, 생각보다 AI만을 이용하여 내가 원하는 서비스를 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;그럼에도 페어 프롬프팅이나, 데모데이, 리뷰어 두루의 피드백 등 뜻깊고 즐거운 경험을 많이 할 수 있어서 정말 좋았다! 앞으로의 미션이 정말 기대된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 제미나이 캔버스로 어디까지 만들 수 있을지 한번 테스트 해보고 싶어졌다.&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/55</guid>
      <comments>https://hynwlee.tistory.com/55#entry55comment</comments>
      <pubDate>Sat, 28 Feb 2026 18:22:23 +0900</pubDate>
    </item>
    <item>
      <title>[우테코 8기 모바일 안드로이드] 오리엔테이션 회고</title>
      <link>https://hynwlee.tistory.com/54</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;첫 출근?길&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 첫날은 정말 설렘 반 걱정 반으로 갔던 것 같다. 사실 2시간 정도 걸리기 때문에 이동시간에 독서라도 꾸준히 할 생각으로 책을 준비해뒀었으나.. 역시 멀미 이슈로 못읽었다...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;판교 첫 인상&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이전에 서울에 갈 때 환승목적으로 잠시 들렸던 적은 있었으나 이렇게까지 본격적으로 온 적은 처음이었다. 버스에 내리고서, 육교를 건너 지나가니 건물들에 유명한 기업명들이 적혀있었다! 역시 대단한 동네구나 라는 생각이 들면서도 뭔가 친숙한 기분이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;셔틀버스&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계획은 8:50 셔틀을 타려고 했으나, 아슬아슬하게 못 탄다는 것을 깨달았다! 그래도 9:30 셔틀이 있어서 정말 다행이었다..탑승 장소 옆에있는 버스정류장에서 기다리고 있었는데 한분 두분씩 이쪽으로 오시는 것을 보고 다른 크루분들인가 싶었다. (물론 첫날 + 외향적인 성격이 아니라 말은 못 걸었다ㅎㅎ..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇게 9:30분에 셔틀을 타고 10~15분 정도 이동하여 드디어 우아한 형제들 사옥에 들어갔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;우아한 형제들 사옥 입성~&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10시이전에 도착하면 1층 or 4층 로비에서 대기하고 있으라는 공지사항이 있었기 때문에 우선 1층으로 들어갔더니 다른 크루분들이 많이 앉아계셨다..!&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이왕이면 4층도 구경해볼 겸 4층 로비로 이동했는데 여기는 1층과 정반대로 굉장히 한산했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 한산하고 붐비고가 눈에 들어오진 않을 정도로.. 사옥이 너무 좋다고 생각했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vahpo/dJMcaiI9XbN/3cD5YIXD8HRyLtBkBlBGBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vahpo/dJMcaiI9XbN/3cD5YIXD8HRyLtBkBlBGBk/img.png&quot; data-alt=&quot;4층에는 트랙존과 로비가 있었는데 우선 굉장히 개방감이 느껴지는? 공간이었다!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vahpo/dJMcaiI9XbN/3cD5YIXD8HRyLtBkBlBGBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvahpo%2FdJMcaiI9XbN%2F3cD5YIXD8HRyLtBkBlBGBk%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;591&quot; height=&quot;443&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;4층에는 트랙존과 로비가 있었는데 우선 굉장히 개방감이 느껴지는? 공간이었다!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;본격적인 사옥 구경!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10시 30분에 오리엔테이션 시작이었는데, 20분까지는 사옥을 자유롭게 구경해도 좋다고 하셔서 우테코 교육생들에게 배정된 사옥의 공간을 전부 돌아봤다! 로비 쪽의 시설이나 인테리어도 정말 잘 되어있다고 생각했지만 우테코 공간도 너무 좋았다...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0jqEY/dJMcaioUqUs/qRmJ2C0OxRfgSi8dtteg7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0jqEY/dJMcaioUqUs/qRmJ2C0OxRfgSi8dtteg7k/img.png&quot; data-alt=&quot;통창 + 야무진 인테리어&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0jqEY/dJMcaioUqUs/qRmJ2C0OxRfgSi8dtteg7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0jqEY%2FdJMcaioUqUs%2FqRmJ2C0OxRfgSi8dtteg7k%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;585&quot; height=&quot;439&quot; data-origin-width=&quot;4032&quot; data-origin-height=&quot;3024&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;통창 + 야무진 인테리어&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 프로그래밍을 하면서 느꼈던 점은 주변 환경이 상당히 중요하다는 점이었다,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;나만 들었던 것일수도 있으나, 지금까지 프로그래밍 환경에 대한 사람들의 로망? 을 들었던 적이 많다 이를테면&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;바닷가 모래사장에 앉아서 코딩하고 싶다.&lt;i&gt;(이건 경험자에게 들어봤는데 노트북에 모래 들어간다고 한다..)&lt;/i&gt;&quot;,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;해외여행가서 어느 카페에 앉아서 코딩하면 좋을 것 같다.&quot;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;등의 이야기를 들었었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;조금 이야기가 다른길로 샌 감이 있는데, 결론은 &quot;아, 이 환경? 정말 공부하고 싶은 마음이 드는 환경이다.&quot; 라는 생각이 들었다!!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사옥 구경은 하로 크루와 함께 둘러보고 있었는데, 정말 놀랍게도 주황이 크루가 하로 크루를 알아보고 먼저 인사를 해주셨다! 하로의 벨로그 프로필 사진을 보고 알아봐주셨다고 한다. 나머지 사옥은 주황이, 하로와 함께 스몰톡을 하면서 돌아보고 다시 오리엔테이션 장소로 이동했다!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;오리엔테이션 시작!&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;1615&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3GYbJ/dJMcaaLak4X/NSREBQJVHMPTkAx9OHGPo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3GYbJ/dJMcaaLak4X/NSREBQJVHMPTkAx9OHGPo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3GYbJ/dJMcaaLak4X/NSREBQJVHMPTkAx9OHGPo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3GYbJ%2FdJMcaaLak4X%2FNSREBQJVHMPTkAx9OHGPo1%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;580&quot; height=&quot;773&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2250&quot; data-origin-height=&quot;1615&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;오티 처음에는 우테코의 전반적인 소개가 이루어졌다. 우테코의 시작이지만 여러 유익한 말씀들을 해주셨다. 특히 포비가 크루들에게 반란군이 되라고 말했다, 코치님들 말을 듣지 말라는 것이었다 ㅋㅋㅋ&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;요지는 코치님들의 말씀을 전적으로 신뢰하지 말라는 것이었지만, 이를 재치있게 말씀해주셔서 좋았다,&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;우테코에서 무엇을 기대할 것인가&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사실 이전의 내가 우테코에서 어떤 것을 기대하고 갔는지 생각해보면, 물론 소프트 스킬 학습도 기대하긴 했으나, 결국엔 그러한 소프트 스킬의 취득 목적도 요구사항이나 피드백 등을 좀 더 잘 이해하고 전달하여 &quot;좋은 소프트웨어&quot;를 만드는 것에 귀결되고 있었던 것 같다,&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 오티를 들으면서 이러한 생각은 변화시켜야겠다는 결심을 하게됐다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이전의 우테코는 소프트웨어 장인정신에 초점을 맞추었다고 한다. 하지만 AI가 등장하게 되었고 이로 인해 물론 좋은 코드를 생산하는, 소프트웨어 장인 정신과 같은 &quot;하드 스킬&quot;도 중요하지만 이것만으로는 부족하다고 말씀해 주시며, 소프트 스킬의 중요성을 언급해주셨다, 즉 &amp;nbsp;&quot;함께 일하고 싶은 개발자&quot; 가 되자. 라는 것이었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 글을 쓰는 지금까지 내가 우테코를 거치면서 어떻게 되고 싶은지 곰곰히 생각해 봤는데 역시 나 또한 &quot;함께 일하고 싶은 개발자&quot;가 되고 싶다는 결심을 하게되었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;벌써 미션이요? 두개요?&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사실 소프트 스킬의 중요성에 대한 설명을 들었을 때까지만 해도 &quot;아 오늘 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;정말&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 알차다!&quot; 라고 생각하고 있었는데. 오늘 미션이 두개나 나간다는 말씀을 듣고 놀랐다. 한개는 예상했는데 두개라니..&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다행히 제출기간은 다 널널해서 다행이었다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;첫 번째 미션은 그러지 않기를 바랬지만 예상했던대로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이전 기수분들이 하셨던 연극 미션이었다. 두 번째 미션은 제미나이 캔버스를 이용한 웹앱 출시하기(무려 4개)였다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;우테코 에서의 첫 조~&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;우선 첫 번째 미션인 연극의 조가 거의 바로 배정되었다! 나의 조는 사무엘, 아오, 아키, 로미였는데, 다들 정말 좋은 분들이었다!&amp;nbsp; 첫 날이었음에도 불구하고, 정말 많이 친해졌다!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;마무리하며...&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기대 반, 걱정 반의 마음으로 왔던 하루지만, 걱정이 무색할 정도로 즐겁고 유익한 시간을 보냈다!&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;또, 우테코의 분위기는 내 생각보다 훨씬 더 자유로웠다. 라는 생각이 들었다. 공간도 공간이지만, 크루, 코치, 캡틴이 수평적인 그러한 배움의 환경을 제공해준다는 점에서 위 생각이 들었다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;앞으로도 어떤 배움이 찾아올지는 잘 모르겠지만, 그래도 오티날을 성공적으로 마치면서 앞으로의 우테코 생활이 정말 뜻깊은 나날들이 될 것 같다는 생각이다!&lt;/p&gt;</description>
      <category>우아한테크코스</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/54</guid>
      <comments>https://hynwlee.tistory.com/54#entry54comment</comments>
      <pubDate>Sat, 28 Feb 2026 18:18:47 +0900</pubDate>
    </item>
    <item>
      <title>[우테코 8기 모바일 안드로이드] 레벨 0 4주차 회고</title>
      <link>https://hynwlee.tistory.com/50</link>
      <description>&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;서론&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;어느덧 레벨 0의 마지막 회고를 작성하고 있다는 사실이 잘 실감나지 않는다. 목표를 달성했던 하지 못했건, 레벨 0은 코틀린이라는 언어에 대해 깊게 공부해볼 수 있는 좋은 기회였다고 생각한다. 이제 내일이면 드디어 직접 오프라인에서 우테코에 참여하게 된다는게 떨리기도 하지만 한편으로는 정말 기대되고 재밌겠다라는 생각이 든다. 오늘 회고록에는 What + KPT 회고에 더해 추가적으로 레벨 0에 대한 전체적인 회고, 그리고 우테코 오리엔테이션을 앞둔 내 생각을 정리해보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;What(무엇을 했는가?)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이번 주는 Kotlin in action 2/e의 고차함수, 어노테이션과 리플렉션, DSL(일부), 코루틴(일부)에 대해 학습했다,&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hynwlee.tistory.com/45&quot;&gt;Kotlin in action 2/e: 고차함수: 람다를 파라미터와 반환값으로 사용(1) 고차함수, 스마트 스테핑, 함수 타입의 파라미터, 함수를 반환하는 함수&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hynwlee.tistory.com/46&quot;&gt;Kotlin in action 2/e: 고차함수: 람다를 파라미터와 반환값으로 사용(2) 인라인 함수, 코틀린 표준 라이브러리의 인라이닝&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hynwlee.tistory.com/47&quot;&gt;Kotlin in action 2/e: 어노테이션과 리플렉션(1) 코틀린의 어노테이션&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hynwlee.tistory.com/48&quot;&gt;Kotlin in action 2/e: DSL 만들기(1)&amp;nbsp; DSL이란 무엇인가?&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hynwlee.tistory.com/49&quot;&gt;Kotlin in action 2/e: 코루틴(1) 코루틴이란 무엇인가?&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;추가적으로 이번 주 또한 교재 학습에 좀 더 많은 시간을 투자하기 위해 알고리즘 문제 풀이는 진행하지 않았다.&amp;nbsp; 물론 레벨 0이 끝난 오늘부터는 다시 1일 1알고리즘 포스팅에 도전해보려고 한다!&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Keep(현재 만족하고 있는 부분)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&quot;나의 학습 속도를 고려하여 계획을 세우지 못했다&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이전 주차에서 다른 크루들의 회고들을 보면서 다들 문제를 인식하고 고뇌하는 사고의 흐름을 잘 녹여냈다는 생각이 들었고, 회고의 측면에서도, 혹은 나중에 다시 볼 정리본의 측면에서도 더 좋아보였다. 나도 이러한 사고의 흐름을 기록해 볼까도 했었지만, 지금껏 정리할 내용이 많다는 이유로 약간씩만 기록했던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;때문에 나도 이번 주차 부터는 최대한 내가 기존에 알고 있는 부분에서 한번 바라본 다음에 학습을 진행하고 어떻게 생각이 바뀌게 되었는지에 대해서 기록하는 등 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;이러한 부분을 좀 더 신경쓰면서 회고를 작성해보려고 노력해봤다&lt;/span&gt; 뭔가 좀 더 심도있게 공부한 느낌이 들었다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Problem(개선이 필요하다고 생각하는 부분)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&quot;나의 학습 속도를 고려하여 계획 세우기&quot;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;이번 주도 마찬가지지만 레벨 0을 전체적으로 돌아봤을 때 계획대로 학습을 끝마치지 못했다.. &lt;/span&gt;&lt;span style=&quot;font-size: 16px; letter-spacing: 0px;&quot;&gt;결국 18장까지 교재의&amp;nbsp; 13장까지밖에 학습하지 못했다..&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span&gt;물론 분량이 많았던 것도 사실이긴 하지만, 돌아봤을 때 내 학습 속도를 고려한다면 계획을 조금 수정하여 더 적은 분량을 학습했어야 했던 것 같다. 책을 다 끝내고 싶다는 욕심에 &lt;/span&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;Try(개선 방안)&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;계획을 수립하는데 있어 다양한 메트릭을 사용해보자&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;계획을 세울 때 내 의지만 반영하여 세웠던 것 같다. 열정이 좀 앞섰다 보니 학습 분량을 늘리고 싶었으면 늘렸지, 줄이지는 않았던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;계획에 대해돌아보면, 물론 첫 주는 내 학습 리듬을 평가하기 위해 정확히 계획을 잡을 수는 없으리라 생각한다. 하지만 2~3주차에는 대충 이 책에 대한 내 학습 속도가 느리다는 것을 스스로 알고 있었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이때 학습 분량을 조금 줄여보는 방안도 검토해볼만 했으나 다 끝내고 싶다는 욕심에 줄이지 못했던 것 같다..&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 여러 개인적인 일정도 겹쳐 공부 시간이 줄어든 점도 있었다. 결론적으로 앞으로 계획을 세울 때에는 좀 더 체계적으로 여러 부분들을 고려해야겠다는 생각이 들었다. 특히 다음과 같은 것들!: 발생할 수 있는 이벤트, 학습속도(냉정하게), 내 의지(너무 많이는 말고)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;미완료한 책의 뒷 부분 마무리하기&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;추가적으로 미완료한 책의 뒷 부분은 틈나는대로 학습하여 최대한 빨리 완독해보려고 한다!&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;결론: 레벨 0을 마무리하며..&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;레벨 0이 벌써 끝나다니 사실 실감이 나지 않는다. 아쉬운 부분도 많았지만 그만큼 배울 점도 많았던 레벨 0 이었던 것 같다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;레벨 0 시작 전에 세웠던 최종 목표를 보고 개인적으로 완료했다고 생각하는 정도에 따라 초록(완료) ~ 노랑(애매..) ~빨강(미달성) 으로 점수를 매겨봤다...&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;레벨 0 결과!&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9hXhL/dJMcajgVqeJ/JDbBorC4YFK8TOonTXtqwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9hXhL/dJMcajgVqeJ/JDbBorC4YFK8TOonTXtqwk/img.png&quot; data-alt=&quot;반타작....은 넘었다...!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9hXhL/dJMcajgVqeJ/JDbBorC4YFK8TOonTXtqwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9hXhL%2FdJMcajgVqeJ%2FJDbBorC4YFK8TOonTXtqwk%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;621&quot; height=&quot;329&quot; data-origin-width=&quot;712&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;반타작....은 넘었다...!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;돌아봤을때 사실 런타임 환경에 대해 자세히 뜯어보지 못해 굉장히 아쉬웠다... 원래 계획은 책을 다 보고 남은 공부 시간에 추가적으로 학습할 내용이었지만,,,실상은 책 학습이 끝나면 하루가 끝나던 경우가 대부분이라 학습을 많이 진행하지 못했다고 생각한다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그럼에도 &quot;내 학습 루틴&quot;에 대해 많이 개선했고, 많이 알게 되었다는 생각은 확실히 든다. 특히 학습한 내용을 회고 방식으로 정리하는 것을 습관으로 들일 수 있어 정말 좋았다. 레벨 0의 기간동안 약 40개의 포스팅을 했다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;820&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XjW1f/dJMcac3b6gZ/sy1nGKz5BXOZEqKPpoU941/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XjW1f/dJMcac3b6gZ/sy1nGKz5BXOZEqKPpoU941/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XjW1f/dJMcac3b6gZ/sy1nGKz5BXOZEqKPpoU941/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXjW1f%2FdJMcac3b6gZ%2Fsy1nGKz5BXOZEqKPpoU941%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;513&quot; height=&quot;685&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;820&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 분명 혼자 하는 공부일 것이라고 생각했지만, 다른 크루들의 회고, 학습록을 보고 서로 코멘트도 하며 서로 긍정적인 영향력을 주고 받으면서 학습할 수 있었다. 4주간의 레벨 0을 열심히 달려온 다른 크루들에게 고생했다는 말을 전하고 싶다!&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;내일은 우테코 OT!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;진짜 미라클 모닝?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 판교 근처 자취를 고려하지는 않았다. 경기도에 거주하기도 하고, 광역버스를 이용하면 환승없이 판교역까지 갈 수 있기 때문이다.. 결정적으로 10시부터 수업을 시작한다는 정보를 듣고 꽤나 여유롭게 갈 수 있겠는데? 싶었다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 계산해보니 여유롭게 가려면 적어도 6시 반까지는 일어나야겠다는 결론이 나왔다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 요 몇주동안 8시에 일어나면서도 엄청난 미라클 모닝이라고 생각하고 있었던 나였기에 여기서 한번 더 미라클 모닝을 해야한다는 계산을 하고 나서 꽤 놀랐다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 다른 곳도 아니고 우테코를 가는 것이기 때문이라 생각하고 나니 미라클 모닝마저도 기대가 된다~~~~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;잉여시간 어떻게 하죠..&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 일찍 일어나는 것보다 걱정인건 대강 왕복 3시간을 이동에 써야 한다는 점이다. 그 시간에 공부를 하면 좋겠지만 안타깝게도 약간의 차멀미를 보유 중이라,, 책이나 화면을 보면 멀미가 심해진다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 일단 시도는 해보기 위해 우선 책을 읽어보려고 오늘 부랴부랴 도서관에서 책을 빌려왔다. 처음엔 프리코스 디스코드에서 네오 코치님이 추천해주신 책 중에 읽어보려고 했으나,,, 이게 웬걸 책이 한권도 없었다. 중앙도서관을 가야하나 생각하던 중에 우형 테코블에서 이 포스팅을 발견했다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://techblog.woowahan.com/22211/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://techblog.woowahan.com/22211/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1771851414495&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;우아한형제들 PM들이 추천하는 책(ver.2025) | 우아한형제들 기술블로그&quot; data-og-description=&quot;우아한형제들 PM(Product Manager)들이 추천하는 책들을 기억하시나요? 우아한형제들 PM들은 어떤 책을 읽을까? PM을 준비하고 계신 분들, 또는 PM 업무를 더 잘하고 싶은 분들을 위해 2025년 버전으로 &quot; data-og-host=&quot;techblog.woowahan.com&quot; data-og-source-url=&quot;https://techblog.woowahan.com/22211/&quot; data-og-url=&quot;https://techblog.woowahan.com/22211/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bHGii0/dJMb9gxiinO/BTf9gIK1PnxE2cX5eIWi41/img.png?width=3840&amp;amp;height=766&amp;amp;face=0_0_3840_766&quot;&gt;&lt;a href=&quot;https://techblog.woowahan.com/22211/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://techblog.woowahan.com/22211/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bHGii0/dJMb9gxiinO/BTf9gIK1PnxE2cX5eIWi41/img.png?width=3840&amp;amp;height=766&amp;amp;face=0_0_3840_766');&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;우아한형제들 PM들이 추천하는 책(ver.2025) | 우아한형제들 기술블로그&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;우아한형제들 PM(Product Manager)들이 추천하는 책들을 기억하시나요? 우아한형제들 PM들은 어떤 책을 읽을까? PM을 준비하고 계신 분들, 또는 PM 업무를 더 잘하고 싶은 분들을 위해 2025년 버전으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;techblog.woowahan.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PM 분들이 추천하시는 책이었지만 읽으면 도움이 될 것 같은 책들이 꽤 있었다! 다행히 이 책들은 근처 도서관에 있었다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwj8bb/dJMcagR6dxn/dyoBEbnA6ItcBxDnMoDKs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwj8bb/dJMcagR6dxn/dyoBEbnA6ItcBxDnMoDKs0/img.png&quot; data-alt=&quot;역시 도서관이 최고다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwj8bb/dJMcagR6dxn/dyoBEbnA6ItcBxDnMoDKs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwj8bb%2FdJMcagR6dxn%2FdyoBEbnA6ItcBxDnMoDKs0%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;414&quot; height=&quot;552&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;역시 도서관이 최고다&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;정말 마무리하며..&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;평소보다 긴 회고록을 쓰게 되었는데 읽어주신 분들에게 정말 감사하다! 다들 내일 뵈어요~!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kotlin</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/50</guid>
      <comments>https://hynwlee.tistory.com/50#entry50comment</comments>
      <pubDate>Mon, 23 Feb 2026 22:12:39 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin in action 2/e: 코루틴(1) 코루틴이란 무엇인가?</title>
      <link>https://hynwlee.tistory.com/49</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;동시성 프로그래밍의 필요성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에서는 다음과 같이 설명하며 나 또한 그렇다고 생각한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;현대 프로그램들이 한 번에 한 가지 일만 하는 경우는 드물다...&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 특정 작업이 시작된 후 완료를 기다리지 않고 일단 다음 일을 하는 비동기(Asynchronous)가 필요하다고 할 수 있겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모든 작업을 물리적으로 함께 실행하지 않아도 동시성을 실현할 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;운영체제와 관련하여 생각해본다면 위 문장에 대해 감이 올 것이라고 생각된다. 예를 들어 각 작업을 시분할 하고,&amp;nbsp; 라운드로빈 방식으로 번갈아 실행한다면 싱글 코어 CPU 환경의 사용자 입장에서는 완전히 동시적으로 실행되는 것 처럼 보인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;진짜 물리적으로 동시에 작업을 수행하는 병렬성&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현대에는 대부분의 CPU가 멀티코어이기 때문에 멀티코어 CPU를 이용하면 여러 계산들을 병렬적으로 수행할 수 있으며 이를 병렬성이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린에서의 동시성: 일시 중단 함수와 코루틴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코루틴은 코틀린의 강력한 특징 중 하나라고 책에서는 소개한다. 스레드와 같은 전통적인 방법과 비교했을 때, 코틀린이 훨씬 더 가볍게 동작한다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자바 스레드 vs 코루틴&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JVM에서 병렬, 동시성 프로그래밍을 위한 고전적인 추상화는 스레드를 사용하는 것이다. 물론 코틀린도 이에 호환되기 때문에 자바에서처럼 스레드를 사용할 수 있게 다음과 같은 API를 제공한다고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1771844406763&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlin.concurrent.thread

fun main() {
println(&quot;I'm on ${Thread.currentThread().name}&quot;)
thread {
    println(&quot;And I'm on ${THread.currentThread().name}&quot;)
    }
}&lt;/code&gt;&lt;/pre&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;b&gt;JVM에서 생성하는 스레드는 일반적으로 OS 레벨 스레드이다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 스레드를 직접 사용하게 되면 생성하고 관리하는데 비용을 계속 지불해야하며, 어떤 스레드가 작업이 완료되길 기다리는 동안에는 그냥 블록되기 때문에 자원이 낭비된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 부분은 스레드를 사용하면 어쩔 수 없이 감수해야 하는 부분이라고 생각했다, 그렇기 때문에 코루틴이 이를 어떻게 해결하려고 헀는지 더욱 궁금해졌다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/49</guid>
      <comments>https://hynwlee.tistory.com/49#entry49comment</comments>
      <pubDate>Mon, 23 Feb 2026 20:29:58 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin in action 2/e: DSL 만들기(1) DSL이란 무엇인가?</title>
      <link>https://hynwlee.tistory.com/48</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DSL 이란 무엇인가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Domain-specific_language&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.wikipedia.org/wiki/Domain-specific_language&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSL은 Domain Specific Language 즉 도메인 특화 언어의 약자이다. 사실 이 책을 보기 전에는 DSL에 대해 공부해본 적이 없었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 말 그대로의 뜻으로 생각해봤다. 특정 도메인에 특화된 언어. 간단하게 조사하고 생각해봤을때 객체와 DB를 자동으로 매핑해주는 ORM이 떠올랐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨌던 예시를 들어 이해해보면 프로그래밍 언어에서의 DB 조작을 쉽게 해주는 기능이 사실 원래는 프로그래밍 코드에서 DB등을 조작하려면 여러 API를 조합하거나, 쿼리문을 이용해야 하지만, DSL을 잘 설계하면 이러한 번거로움을 덜 수 있게 해준다. 정도로 이해해봤다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;훌륭한 API란 무엇인가?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 처음에는 DSL과 API가 무슨 관계인지 잘 몰랐다. 하지만 위의 생각을 하고 나니, 분명 둘 간의 어떤 관계가 있긴 한 것 같은데 잘 와닿지는 않았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;막연히 API가 좀 더 저수준이고 DSL이 여러 API를 조합해 사용하기 쉽게 만들어낸? 정도로 와닿았던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 훌륭한 API는 무엇인가 정리해보면 다음과 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 코드를 읽는 독자가 어떤 일이 벌어질지 명확하게 이해할 수 있어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 확실히 중요한 부분이라고 생각한다. 하지만 사용자가 API의 구현을 알지 않고 결과를 명확하게 예측할 수 있게 하려면 어떻게 해야 하는지는 좀 더 생각해봐야할 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 코드에 불필요한 구문이나 번잡한 준비 코드가 가능한 한 적어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;=&amp;gt; 준비 코드가&amp;nbsp; 적은만큼 사용자가 더욱 쉽고 가독성 좋게 사용할 수 있기 떄문에&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;이 부분도 1번의 연장선이라고 생각했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코틀린은 훌륭한 API를 작성하기 알맞다!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에서는 특히 코틀린이 API를 훌륭하게 작성하기에 알맞다, 라고 다음의 이유들과 함께 소개하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확장 함수, 중위 함수 호출, 람다, 연산자 오버로딩..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 또한 이러한 이유들 덕분이라고 정확하게 인지하고 있지는 않았지만, 코틀린 언어가 다른 언어에 비해 자연어처럼 잘 읽힌다, 라고는 생각하고 있었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;DSL은 선언적이다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSL이 선언적이다는 것은 무슨 말일까? 책에서는 범용 프로그래밍 언어는 보통 명령적인 것에 반해 DSL은 선언적이라고 다음과 같은 예시와 함께 설명하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령적 언어는 어떤 연산을 완수하기 위해 필요한 각 단계를 순서대로 정확히 기술해야 하는 반면, 선언적 언어는 원하는 결과만 기술하면 알아서 다 해주는 느낌으로 생각됐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;명령적 언어보다 선언적 언어가 더 효율적인 경우가 자주 있다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 사실 이 부분이 처음에 잘 와닿지 않았다. 지금까지 알아본 특징으로는 명령적 언어가 좀 더 사용하기 번거롭고 선언적 언어가 더 사용하기 쉬운 느낌이었는데 그렇다면 자연히 좀 더 번거로운 만큼 성능적인 측면에서 더 효율적이지 않을까? 라는 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다음과 같은 이유로 위 같은 현상이 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;선언적 언어에서는 실행 엔진이 결과를 얻는 과정을 한꺼번에 최적화 할 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 결국 명령적 언어에서는 각각의 작업에 대해 독립적으로 최적화를 해야한다는 말인데 사실 이것도 한번에 와닿지는 않았다. 명령적 언어도 일정 단위로 묶어서 최적화 엔진에 건네주면 되는 것 아닌가? 라고 생각했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지만 사실 이는 다음과 같은 말이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언적 언어는 구체적인 실행 방안을 정하지 않으므로 세부 실행은 기술하지 않으므로 언어를 해석하는 엔진이 한꺼번에 최적화가 가능하다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 명령적 언어는 세부 실행을 거의 전달해주므로, 최적화에 제한이 있다는 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언적 언어를 해석하는 엔진을 구현하는 난이도가 상당하겠다는 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;선언적 언어의 단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 선언적 언어 즉 DSL에는 이 모든 장점을 넘어서는 큰 단점이 있다고 한다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 범용 언어로 만든 호스트 애플리케이션과 DSL을 함께 조합하기 어렵다는 것이다. DSL은 자체 문법이 있기 때문에 다른 언어의 프로그램 안에 직접 포함시킬 수가 없다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이를 위해서는 DSL 프로그램을 별도의 파일이나 문자열 리터럴로 저장해야 한다고 한다. 사실 이 부분도 한번에 와닿지 않았다. 책에서&amp;nbsp; DSL의 예시로 정규 표현식도 들어줬었는데, 막상 정규 표현식을 사용할 때는 위같은 어려움을 딱히 느껴보지는 못했었다..고 정리하려는 순간 프리코스 때 마주했던 문제가 생각났다&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;바로 정규 표현식을 사용할 때 였는데 처음에는 개행 문자가 아닌 &quot;\n&quot; 문자 자체를 파싱하고 싶었기에 이스케이프 문자를 더해 &quot;\\n&quot;을 정규식에 포함하였으나, 이상하게도 자꾸 개행을 파싱하는 문제에 직면했던 적이 있었다. 알고보니 컴파일러를 거친 후에 Regex 엔진이 이를 보고 정규식 관련 로직을 수행하는 것이기 때문에, 이스케이프 문자를 두번 래핑하거나 &quot;&quot;&quot;\n&quot;&quot;&quot; 등으로 raw string 명시를 해주었어야 했다..&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; DSL을 이해할 수 없기 때문에 Regex 엔진에 이를 넘겨주어야한다는 사실을 깨닫게 되었고, 이는 어딘가에 기본 탑재되어있었기에 내가 위 어려움을 느끼지 못했다는 것을 알 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;내부 DSL이란 무엇인가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 외부 DSL이란 독립적인 문법 구조를 갖는 DSL이다. 방금 언급했던 정규식이 외부 DSL 의 예라고 볼 수 있겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 내부 DSL은 범용 언어 즉 프로그램의 주 언어로 작성할 수 있는 DSL이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;내부 DSL의 예시: 익스포즈드 프레임워크&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린에서는 코틀린으로 작성된 데이터 베이스 프레임워크인 익스포즈드(Exposed) 프레임워크가 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771839157391&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT Country.name, COUNT(Customer.id)
        FROM Country
INNER JOIN Customer
        ON Country.id = Customer.country_id
    GROUP BY Country.name
    ORDER BY COUNT(Customer.id) DESC
    LIMIT 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 SQL을 이용한 구현이다. 내부 DSL인 코틀린의 익스포즈드를 사용하면 동일한 결과를 생성하는 프로그램을 만들 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771839306418&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(Country innerJoin Customer)
    .slice(Country.name, Count(Customer.id)
    .selectAll()
    .groupBy(Country.name)
    .orderBy(Count(Customer.id), order = SortOrder.DESC)
    .limit(1)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 두 예제에는 큰 차이가 있다. 바로 SQL을 이용한 구현은 SQL 쿼리가 돌려주는 결과를 다시 코틀린 객체로 변환해야 하지만, 내부 DSL로 구현한 프로그램에서는 자동으로 코틀린 객체를 반환해준다!&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>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/48</guid>
      <comments>https://hynwlee.tistory.com/48#entry48comment</comments>
      <pubDate>Mon, 23 Feb 2026 03:21:09 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin in action 2/e: 어노테이션과 리플렉션(1) 코틀린의 어노테이션</title>
      <link>https://hynwlee.tistory.com/47</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어노테이션을 적용하면 선언에 표지를 남길 수 있다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린에서는 @과 어노테이션 이름을 선언 앞에 넣어서 어노테이션을 적용할 수 있다. 책의 예제에서도 처음에 등장하지만, 나 또한 이를 보고, JUnit의 @Test가 생각이 났다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771519462604&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlin.test.*
class MyTest {
    @Test
    fun testTrue() {
        assertTrue(1 + 1 == 2)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 함수 선언 위에 @Test 어노테이션을 붙여주게 되면, JUnit 프레임워크에게 이 메서드를 테스트로 호출하라고 지시하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;@Deprecated 어노테이션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상당히 흥미로운 어노테이션이라고 할 수 있겠다. 해당 어노테이션은 선언이 더이상 쓰이지 않게 될 이유를 사용자에게 제공해줄 수 있으며, 새 버전으로 전환할 수 있도록 지원해주고, 컴파일되게 할 것인지 말 것인지 등도 정할 수 있다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771520590995&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Deprecated(&quot;Use removeAt(index) instead.&quot;, ReplaceWith(removeAt(index)&quot;))
fun remove(index: Int) { /* ... */ }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dycxrt/dJMcahKbSzV/fwaZHEC56uNXq2yfc8vpk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dycxrt/dJMcahKbSzV/fwaZHEC56uNXq2yfc8vpk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dycxrt/dJMcahKbSzV/fwaZHEC56uNXq2yfc8vpk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdycxrt%2FdJMcahKbSzV%2FfwaZHEC56uNXq2yfc8vpk1%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;667&quot; height=&quot;168&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 함수에 선이 그어지며 설정해둔 대로 수정 방안도 잘 제공해주는 것을 확인할 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코틀린의 어노테이션의 인자 사용법은 자바와 다르다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 자바의 어노테이션의 사용법은 공부하지 않아 잘 모른다... 때문에 여기서는 자바와 다르다는 사실만 짚고 우선은 코틀린의 어노테이션만 정리해보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스를 어노테이션 인자로 지정할 때 ::class를 클래스 이름 뒤에 넣어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 어노테이션을 인자로 지정할 떄 이름 앞에 @를 붙이면 안된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열을 인자로 지정할 때 대괄호를 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어노테이션 인자를 컴파일 타임에 알 수 있어야 한다. 즉 const val을 붙인 상수여야 하고, 파일 최상위나 object 안에 선언해야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코틀린 소스코드에서 한 선언을 컴파일한 결과가 여러 자바 선언과 대응하는 경우&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린 소스코드에서 한 선언을 컴파일한 결과가 여러 자바 선언과 대응하는 경우 코틀린 선언에 대응하는 각각의 자바 선언에 어노테이션을 붙여야 하는 때가 있다고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면 코틀린에서는 프로퍼티이지만, 자바에서는 게터, 세터 등등 다른 코드와 대응되는 경우라던가, 주 생성자에서 프로퍼티를 선언하는 경우 등이 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771608090572&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Person (val name: String)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코틀린 코드만 작성하더라도, 게터, 파라미터 선언, 주 생성자 등등 여러 자바 요소에 대응된다는 것이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때문에 아래처럼 어노테이션을 사용해서 자바에서 사용될 게터와 세터의 이름도 지정해줄 수 있다&lt;/p&gt;
&lt;pre id=&quot;code_1771608933364&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CertificateManager {
    @get:JvmName(&quot;obtainCertificate&quot;)
    @set:JvmName(&quot;putCertificate&quot;)
    var certificate: String = &quot;-----BEGIN PRIVATE KEY-----&quot;
}&lt;/code&gt;&lt;/pre&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;b&gt;어노테이션으로 JSON 직렬화 제어하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 위의 기능을 위해서 어노테이션을 사용하기에는 뭔가 아쉽다는 생각이 들었다. 다행히 다음에 바로 직렬화를 제어하는데 어노테이션을 사용한다는 설명이 나온다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;직렬화(Serialization)와 역직렬(Deserialization)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬화는 객체를 저장 장치에 저장하거나 네트워크를 통해 전송하기 위해 텍스트나 이진 형식으로 변환하는 것을 말한다. 역직렬화는 당연히 이러한 텍스트, 이진 형식의 값을 다시 객체로 복원하는 직업이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코틀린에서 사용 가능한 객체 &amp;lt;-&amp;gt; JSON 변환 라이브러리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린 팀에서 개발한 kolinx.serialization, 특히 잭슨, 지슨 등 자바 객체를 JSON으로 변환하기 위해 설계된 라이브러리도 완벽하게 호환된다고 한다, 상호 운용성이 이러한 측면에서도 드러나는 것이 대단하다는 생각이 들었다,,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어노테이션 클래스 선언하기&lt;/p&gt;
&lt;pre id=&quot;code_1771643106886&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;annotaion class JsonExclude&lt;/code&gt;&lt;/pre&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;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;언뜻 보면 클래스 선언 특히 데이터 클래스 선언과 비슷해보이기도 한다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어노테이션 클래스는 본문을 정의할 수 없다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 코틀린의 데이터 클래스 본문은 선택이었지만, 어노테이션 클래스는 주 생성자만 정의할 수 있고 본문을 정의할 수 없다. 정말 구조만 정의할 수 있게 되어있는 것으로 보인다. 다음과 같이 사용할 수 있겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771643106887&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;annotaion class JsonName(val name: String)&lt;/code&gt;&lt;/pre&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;b&gt;주 생성자의 모든 파라미터는 불변이어야 한다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 모든 파라미터는 val이어야 한다. 생각해봤을 때, 불변성을 깨면서 메타데이터를 바꾸는 것 보다 차라리 새로운 어노테이션 클래스를 만들도록 의도하는 것이 훨씬 합리적이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;자바 어노테이션 선언 vs 코틀린 어노테이션 선언&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771643106887&quot; class=&quot;angelscript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;public @interface JsonName {
    String value();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 위 코틀린 어노테이션 선언과 동일한 동작을 하는 자바 어노테이션 선언은 위 예제와 같다! 특별히 짚고 넘어가야 할 부분은 value() 메서드인데, 이 메서드를 붙이면 value를 제외한 모든 인자에 이름 붙은 인자 구문을 사용해야만 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 코틀린에서는 일반적인 생성자 구문과 같이 인자의 이름을 선택적으로 명시 가능하다. 하지만 자바에서 선언한 어노테이션을 코틀린에서 적용할 때는 자바에서 사용하던 방법과 동일하게 사용해야 한다. 즉 value 메서드가 붙은 어노테이션을 제외한 모든 인자에 대해 이름 붙은 인자 구문을 사용해야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어노테이션 클래스에 어노테이션 붙이기: 메타 어노테이션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 어노테이션에 어노테이션을 붙인다고 하니 직관적으로 이해가 잘 되지는 않았던 것 같다. 우선 어노테이션중 어노테이션 클래스에 적용할 수 있는 녀석들을 메타 어노테이션이라 부른다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에서는 @Target 메타 어노테이션을 예시로 설명해주는데,&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771643106887&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;@Target(AnnotationTarget.PROPERTY)
annotaion class JsonExclue&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 사용할 수 있다. @Target 어노테이션은 어노테이션을 적용할 수 있는 요소의 유형을 미리 지정한다. 문법을 딱히 사전에 알지 않아도 잘 해석할만 한다 위의 코드는 JsonExclude 어노테이션의 적용 타깃을 지정할 수 있다!&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;blockquote style=&quot;color: #666666; text-align: left;&quot; data-ke-style=&quot;style2&quot;&gt;사실 이를 어떻게 제어하는지는 잘 와닿지 않는다. 이에 대해서는 추후 더 학습하여 정리해보도록 하겠다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어노테이션 파라미터로 클래스 사용&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어노테이션 파라미터에 클래스를 넣는 경우도 있다고 한다. 인터페이스 타입인 프로퍼티에 대한 역직렬화를 제어할 때 어떤 클래스를 사용하여 인터페이스를 구현할지를 지정할 수 있어야 하기 때문이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각해보면 다음과 같은 이유일 것이라 생각된다. 자바, 코틀린에서는 인터페이스에 대해 new 즉 객체를 만들 수 없기 때문에 역직렬화 시 어떤 클래스로 new를 해서 객체로 돌려놓을지에 대한 정보를 전달하는 것으로 생각된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어노테이션 파라미터로 제네릭 클래스 받기&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1771643106887&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;interface ValueSerializer&amp;lt;T&amp;gt; {
    fun toJsonValue(value: T): Any?
    fun fromJsonValue(jsonValue: Any?): T
}&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1771643106888&quot; class=&quot;crystal&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;annotation class CustomSerializer(
    val serializerClass: KClass&amp;lt;out ValueSerializer&amp;lt;*&amp;gt;&amp;gt;
)&lt;/code&gt;&lt;/pre&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;lt;T&amp;gt;를 사용할 줄 알았으나, 어노테이션은 제네릭을 가질 수 없다고 한다. 또한 이 어노테이션이 어떤 타입에 쓰일지 전혀 알 수 없으므로 그러한 관점에서 스타 프로젝션을 사용하는게 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이를 결정하는 것에 대해 개념적으로는 뭔가 이해가 되지만 실제 사용은 할 수 있을지 잘 모르겠다....&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;런타임에 객체 관찰하기: 리플렉션&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 처음에 이를 보고 디버깅이 생각났다, 디버깅도 유사하게 사용자가 직접 객체를 검색하고, 런타임에 조사할 수 있기 때문이었다. 그렇지만 책에서는 직렬화와 관련된 내용으로 이에 대해 설명한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;방금도 어떤 타입이던 받기 위해 스타 프로젝션을 사용했듯이 직렬화 라이브러리는 특정 클래스나 프로퍼티를 위한 것이 아니기 때문에 타입과 관계없이 객체를 다뤄야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 구현을 위해 리플렉션을 사용하는 것으로 생각된다, 코틀린에서는 리플렉션 API를 제공한다고 한다. 주목할 점은 해당 API가 다른 JVM 언어에서 생성한 바이트코드도 충분히 다룰 수 있다는 점이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 당연히 자바의 리플렉션 API에서 코틀린을 컴파일한 바이트코드를 다룰 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Kotlin</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/47</guid>
      <comments>https://hynwlee.tistory.com/47#entry47comment</comments>
      <pubDate>Fri, 20 Feb 2026 02:34:23 +0900</pubDate>
    </item>
    <item>
      <title>Kotlin in action 2/e: 고차함수: 람다를 파라미터와 반환값으로 사용(2) 인라인 함수, 코틀린 표준 라이브러리의 인라이닝</title>
      <link>https://hynwlee.tistory.com/46</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;코틀린에서 람다를 사용하는 구현은 똑같은 코드를 직접 실행하는 것보다 덜 효율적이다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코틀린 컴파일러는 람다는 익명 클래스로 컴파일되어 사용된다. 그러므로 람다식마다 새로운 클래스가 생기고 람다가 변수를 캡처하면 람다 정의가 포함된 코드를 호출하는 시점마다 새로운 객체가 생긴다..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 람다를 사용하는 구현은 똑같은 코드를 직접 실행하는 함수보다 덜 효율적이라고 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인라인 함수를 통해 람다의 부가 비용 없애기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에서는 이러한 문제를 인라인 함수를 통해 부가 비용을 없앨 수 있다고 소개한다. 인라인 함수를 사용하면 반복되는 코드를 뺴내되, 직접 실행될 만큼 효울적인 코드를 컴파일러가 생성하게 할 수 있다고 설명하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 인라인 함수를 사용해야 하면,&amp;nbsp; 람다의 장점은 간결함인데, 인라인 함수를 사용하면 결국 함수를 따로 정의해야 할텐데, 간결함을 챙길 수 없지 않을까? 라는 의문이 들었다. 우선 이런 의문을 가지고 이에 대해 계속 탐구해봤다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인라이닝이 작동하는 방식&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 내가 C++을 배울 때 알고있던 인라인 함수의 작동 방식과 동일했다. 인라인 함수를 사용하게 되면, 함수를 호출하는 부분에서 함수를 호출하는 것이 아니라, 실제 함수 본문을 끼워넣어버리기 때문에 함수 호출 비용을 아낄 수 있다는 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1771416135011&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock

inline fun &amp;lt;T&amp;gt; synchronized(lock: Lock, action:() -&amp;gt; T): T {
    lock.lock()
    try {
        return action()
    }
    finally {
        lock.unlock()
    }
}

fun main() {
    val l = ReentrantLock()
    synchronized(l) {
        //...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 람다 함수를 호출하는 인라인 함수를 사용했을 때, 함수의 본문 뿐 아니라 전달된 람다의 본문도 함께 인라이닝 되기 때문에 원래라면, 익명클래스로 람다를 감싸야했지만, 그 작업이 그냥 단순히 코드 삽입으로 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제 코드를 보고 그제서야 내가 애초에 책의 의도를 잘못 이해했음을 깨달았다. 바로 람다 대신 인라인 함수를 쓰는 것이 아니라, 함수 타입 파라미터를 받는 함수를 인라인 함수로 선언하면, 람다 함수가 가지는 성능적인 이슈가 해결된다는 것이었다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 인라인 함수가 호출되는 시점에서 변수에 저장된 람다의 코드를 알 수 없는 경우 즉 일반적인 람다 호출처럼 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771416981685&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class LockOwner(val lock: Lock) {
    fun runUnderLock(body: () -&amp;gt; Unit) {
        synchronized(lock, body) // 이 위치에서는 body 변수가 어떤 함수인지 알 수 없기에 인라인되지 않는다. 
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;의문이 해결되는 한편 다음 의문이 생겼다, 그러면 함수 타입을 파라미터로 받는 함수는 디폴트 값이 왜 인라인이 아닐까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다행히 그 다음 부분에서 이에 대한 설명이 나왔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인라인 함수의 제약&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;호출 시점에서 변수에 저장된 람다의 구현을 알 수 없는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 언급했듯 인라인 함수가 호출되는 시점에서 변수에 저장된 람다의 코드를 알 수 없는 경우 람다를 인라인 호출할 수 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;함수 타입의 파라미터를 값으로 다루는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드처럼&amp;nbsp; 함수 타입의 변수로 넘기지 않았더라도, 인라인 함수의 함수형 파라미터는 호출 지점에서 코드로 치환되기 때문에 값으로 사용될 수 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1771419212198&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class FunctionStroage {
    var myStoredFunction: ((Int) -&amp;gt; Unit)? = null
    inline fun storeFunction(f: Int) -&amp;gt; Unit) {
        myStoredFunction = f
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;람다를 내부적으로 저장해야하는 경우&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드같은 경우에도 람다가 인라인 될 수 없다. TransformingSequence 클래스의 생성자로 람다를 전달하고 있는데, 이 경우에도 방금과 같은 경우에도 코드로 치환해서는 지원할 수 없기 때문에 익명 클래스 인스턴스로 만들 수 밖에 없다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771419692812&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;fun &amp;lt;T, R&amp;gt; Sequence&amp;lt;T&amp;gt;.map(transform: (T) -&amp;gt; R): Sequence&amp;lt;R&amp;gt; {
    return TransformingSequence(this, transform)
}&lt;/code&gt;&lt;/pre&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;b&gt;인라이닝 불가능한 람다를 noinline으로 선언하여야 한다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 경우는 직접 noinline 변경자를 파라미터 앞에 붙여 인라이닝이 되지 않도록 해주어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1771421011261&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;inline fun foo(inlined: () -&amp;gt; Unit, noinline notInlined: () -&amp;gt; Unit) {
    // ...
}&lt;/code&gt;&lt;/pre&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;b&gt;코틀린의 filter와 map은 람다 호출 비용이 거의 없다&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 또한 처음 람다 호출의 비용에 대해 알고 나서는 그렇다면 코틀린의 표준 라이브러리에서의 람다 호출도 그만한 비용을 지불하던 것이구나, 라고 생각했지만, 책에서 우선 filter, map은 인라인 함수라고 설명하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중간 리스트의 문제&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;data class Person(val name: String, val age: Int)

val people = listOf(Person(&quot;Alice&quot;, 29), Person(&quot;Bob&quot;, 31))

fun main() {
    println(
        people.filter { it.age &amp;gt; 30 } // 여기서 중간 리스트가 생성된다
            .map(Person::name)
    )
    // [Bob]
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서의 filter, map은 인라인되기 때문에 추가 객체나 클래스 생성 없이 즉시 수행된다. 하지만 이는 내부적으로 filter함수의 결과를 저장할 중간 리스트를 만들고 이를 다시 map으로 전달하기 때문에 중간 리스트로 인한 비용 문제가 발생한다(메모리, 리스트를 여러번 순회하게 됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;asSequence, 리스트대신 시퀀스를 사용하여 중간 리스트 사용 멈추기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에서는 이러한 상황에서 시퀀스를 사용하면 이러한 비용을 줄일 수 있다고 설명한다. 시퀀스를 통해 이를 구현하게 되면, 중간 시퀀스가 람다를 필드에 저장하는 객체로 표현되며, 최종 연산은 중간 시퀀스에 있는 람다를 연쇄 호출하기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;하지만 시퀀스 연산에서는 람다가 인라이닝 되지 않는다.&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;람다를 필드에 저장한다는 설명에서 알 수 있듯이 이는 람다를 인라이닝 할 수 없다는 것이기 때문에 오히려, 중간 리스트를 통한 연산으로 리스트를 여러 번 순회하는 비용을 줄이기 위해 다 시퀀스로 만들어버리면, 람다를 익명 클래스 인스턴스로 만들어야 하는 비용이 계속 발생하여 오히려 비용이 더 발생할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;인라인 함수를 언제 사용해야 할까?&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이쯤되면 인라인 함수를 사용 불가한 경우가 많긴 했지만, 이외의 경우에는 인라인 함수를 사용할 만 하지 않을까? 라는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;일반 함수 호출은 JVM이 인라이닝을 지원한다.&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 함수 즉 람다를 인자로 받지 않는 함수는 JVM이 이미 강력하게 인라이닝을 지원한다고 한다. 책에서는 다음과 같이 나와있다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;JVM은 코드 실행을 분석해서 가장 이익이 되는 방향으로 호출을 인라이닝한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;단순히 가장 이익이 되는 방향이라고만 나와있어서 약간 애매한 느낌이 있었다. 나중에 추가적으로 정리해야겠다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;람다를 인자로 받는 함수는 인라이닝 하면 이익이 많다!&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에도 설명했듯이 람다를 인자로 하는 함수는 인라이닝 하면 해당 함수의 호출 비용뿐만 아니라 람다를 표현하는 클래스와 객체를 만들 필요가 없어지기 때문에 이익이 더 극대화 된다고 할 수 있겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현재의 JVM은 람다를 인라이닝 해줄 정도의 고수준은 아니라고 한다. 사실 이는 잘 와닿지 않았다. 람다도 결국 JVM이 바라볼때는 익명 객체의 메서드중 하나일텐데, 그렇다면 다른 일반 함수들과 비슷하지 않나 싶었다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;이에 대해서는 아주 조만간 찾아봐야겠다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;함수 안의 람다에서 return 사용하기&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 당연히 뭔가 람다 바깥의 함수가 리턴될까 싶다가도, 중첩된 반복문에서 break, continue를 수행하면 가장 가까운 루프에 적용되는 것을 생각해보니, 아닐 것도 같았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로는 람다를 인자로 받는 함수가 인라인 함수라면, return이 바깥쪽 함수를 반환시킬 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 나름대로의 이유가 있었는데, 만약 인라이닝 되지 않는 함수라면 이를 어떤 변수, 그러니까 함수보다 스코프가 긴 변수에 대입할 수도 있다. 그렇게 되면, 함수가 반환된 뒤에 나중에 실행될 수도 있기 때문에 이러한 위험성을 차단하기 위해 인라이닝 가능한 변수에만 적용한 것으로 보인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 기본적으로 비로컬 리턴인 인라이닝 되는 함수내 리턴을 레이블을 사용해서 로컬 리턴으로 지정할 수도 있다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;data class Person(val name: String, val age: Int)

val people = listOf(Person(&quot;Alice&quot;, 29), Person(&quot;Bob&quot;, 31))

fun lookForAlice(people: List&amp;lt;Person&amp;gt;) {
    people.forEach label@{
        if (it.name != &quot;Alice&quot;) return@label
        print(&quot;Found Alice!&quot;)
    }
}

fun main() {
    lookForAlice(people)
    // Found Alice!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 레이블 대신 람다를 인자로 받는 인라인 함수의 이름을 return 뒤에 사용해도 된다! 위의 경우에는 forEach를 사용하면 될 것이다.&amp;nbsp;&lt;/p&gt;</description>
      <category>Kotlin</category>
      <author>Kirbyyy</author>
      <guid isPermaLink="true">https://hynwlee.tistory.com/46</guid>
      <comments>https://hynwlee.tistory.com/46#entry46comment</comments>
      <pubDate>Thu, 19 Feb 2026 02:23:00 +0900</pubDate>
    </item>
  </channel>
</rss>