<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>디노 블로그</title>
    <link>https://dino-dev.tistory.com/</link>
    <description>블로그 설명</description>
    <language>ko</language>
    <pubDate>Wed, 24 Jun 2026 23:19:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>DinoDev</managingEditor>
    <image>
      <title>디노 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/5602194/attach/939d0d940fb2474ab1920d32607c3047</url>
      <link>https://dino-dev.tistory.com</link>
    </image>
    <item>
      <title>(AI) 아는 건 빼고 인수인계 해줘</title>
      <link>https://dino-dev.tistory.com/148</link>
      <description>&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://dino-dev.tistory.com/146&quot;&gt;내 지식으로 정리해줘&lt;/a&gt; - 공부하다 알게 된 걸 AI한테 시켜서 옵시디언 지식저장소에 키워드 노트로 쌓는 이야기였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 하나는 &lt;a href=&quot;https://dino-dev.tistory.com/147&quot;&gt;인수인계 해줘&lt;/a&gt; - 내가(또는 AI가) 한 작업을 신입한테 가르치듯 하나씩 설명받으면서 능동적으로 학습하는 이야기였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 둘을 한동안 쓰다 보니 어딘가 계속 아쉬웠다. 두 개가 따로 놀고 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;뭐가 아쉬웠나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인수인계를 받다 보면 이런 일이 자꾸 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미 아는 개념인데 AI가 처음부터 친절하게 다 설명한다. &lt;code&gt;@Transactional&lt;/code&gt;이 뭔지, JPA 지연 로딩이 뭔지&amp;hellip; 나 그거 이미 노트로 정리해뒀는데. 듣고 있으면 시간이 아까웠다. 신입한테 0부터 가르치는 톤이라, 정작 내가 궁금한 &quot;이 작업에서 뭘 왜 그렇게 했는지&quot;는 한참 뒤에야 나왔다.&lt;/p&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;a href=&quot;https://dino-dev.tistory.com/146&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;지식저장소&lt;/a&gt;와&amp;nbsp;&lt;a href=&quot;https://dino-dev.tistory.com/147&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;인수인계&lt;/a&gt;가 각자 돌고 있었던 거다. 내가 뭘 아는지는 지식저장소에 다 적혀 있는데, 정작 인수인계는 그걸 모르는 채로 매번 처음부터 다 설명하고 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그래서 스킬을 바꿨다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생각은 단순했다. &lt;b&gt;인수인계 스킬이 내 지식저장소를 먼저 읽게 하면 되잖아?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙도 단순하게 잡았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지식저장소에 노트가 &lt;b&gt;있는&lt;/b&gt; 개념 &amp;rarr; 나 아는 거니까 &quot;이건 이미 정리해두셨으니 넘어가고&quot; 하고 한 줄로 짚고 지나간다. 대신 &lt;b&gt;이번 작업에서 그걸 어떻게 썼는지&lt;/b&gt;에만 집중한다.&lt;/li&gt;
&lt;li&gt;노트가 &lt;b&gt;없는&lt;/b&gt; 개념 &amp;rarr; 모르는 거니까 처음부터 자세히 설명한다. 그리고 설명이 끝나면 &lt;b&gt;&quot;이거 일반적인 개념인데, 노트로 정리해둘까요?&quot;&lt;/b&gt; 하고 물어본다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;인수인계_개선4.png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;108&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vmVyv/dJMcaalkwVj/EJvL6YZslV6ClzdGYX5km0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vmVyv/dJMcaalkwVj/EJvL6YZslV6ClzdGYX5km0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vmVyv/dJMcaalkwVj/EJvL6YZslV6ClzdGYX5km0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvmVyv%2FdJMcaalkwVj%2FEJvL6YZslV6ClzdGYX5km0%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;1708&quot; height=&quot;108&quot; data-filename=&quot;인수인계_개선4.png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;108&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;말 한마디로 스킬 문서를 그 자리에서 고쳐줬다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제로 받아봤다&lt;/h2&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-filename=&quot;인수인계_개선1.png&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMrDoL/dJMcaa6MCOV/fi2M60RtJVKqlIOEx81IkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMrDoL/dJMcaa6MCOV/fi2M60RtJVKqlIOEx81IkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMrDoL/dJMcaa6MCOV/fi2M60RtJVKqlIOEx81IkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMrDoL%2FdJMcaa6MCOV%2Ffi2M60RtJVKqlIOEx81IkK%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;1744&quot; height=&quot;224&quot; data-filename=&quot;인수인계_개선1.png&quot; data-origin-width=&quot;1744&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도입부부터 달랐다. AI가 먼저 내 지식저장소 인덱스를 쭉 읽더니, &quot;이미 노트가 있는 건 아시는 내용이라며 간단히, 없는 건 자세히 설명하고 저장할지 여쭤보겠습니다&quot; 하고 &lt;b&gt;선언하고&lt;/b&gt; 시작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;인수인계_개선2.png&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ypumR/dJMcaicGODk/78qTyphpiVoCz1ad4F1c20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ypumR/dJMcaicGODk/78qTyphpiVoCz1ad4F1c20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ypumR/dJMcaicGODk/78qTyphpiVoCz1ad4F1c20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FypumR%2FdJMcaicGODk%2F78qTyphpiVoCz1ad4F1c20%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;1792&quot; height=&quot;178&quot; data-filename=&quot;인수인계_개선2.png&quot; data-origin-width=&quot;1792&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Spring Layered Architecture&lt;/code&gt;는 내가 이미 노트로 정리해둔 개념이라, &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;1750&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nFJrM/dJMcacXMAfa/ZRKX2qxDT9fY3PcuCLFim0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nFJrM/dJMcacXMAfa/ZRKX2qxDT9fY3PcuCLFim0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nFJrM/dJMcacXMAfa/ZRKX2qxDT9fY3PcuCLFim0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnFJrM%2FdJMcacXMAfa%2FZRKX2qxDT9fY3PcuCLFim0%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;1750&quot; height=&quot;122&quot; data-origin-width=&quot;1750&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;정리해줘&quot; 한마디에, 옵시디언 규칙(폴더 분류, 템플릿, wikilink 연결, 인덱스 갱신)에 맞춰 노트를 알아서 만들어줬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;인수인계_개선3.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mp2vk/dJMcabLpX8e/7FVyWVYRvmAAd3rkO7p1Ik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mp2vk/dJMcabLpX8e/7FVyWVYRvmAAd3rkO7p1Ik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mp2vk/dJMcabLpX8e/7FVyWVYRvmAAd3rkO7p1Ik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmp2vk%2FdJMcabLpX8e%2F7FVyWVYRvmAAd3rkO7p1Ik%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;1670&quot; height=&quot;220&quot; data-filename=&quot;인수인계_개선3.png&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;뭐가 좋아졌나&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 바꾸고 나니 두 가지가 확 좋아졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;첫째, 인수인계가 짧고 밀도 높아졌다.&lt;/b&gt; 아는 건 빠르게 넘기고 모르는 것에만 시간을 쓰니까. 신입한테 다 풀어주는 톤이 아니라, &quot;기초는 아는 동료&quot;한테 핵심만 짚어주는 톤이 됐다. 딱 내가 원하던 거였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;둘째, 인수인계 한 번에 지식저장소가 같이 자란다.&lt;/b&gt; 작업하다 새로 배운 걸 그 자리에서 노트로 떨어뜨리니까, 나중에 따로 정리하는 수고가 사라졌다.&amp;nbsp;&lt;b&gt;지식저장소&lt;/b&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;인수인계&lt;/b&gt;가 드디어 하나로 연결된 거다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인수인계를 받으면 지식저장소가 자라고, 지식저장소가 자라면 다음 인수인계는 더 짧아진다. 알수록 설명이 줄고, 모르는 것만 남는다. 이 루프가 꽤 마음에 든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1년 뒤엔 인수인계가 얼마나 짧아져 있을지, 지식저장소는 얼마나 두꺼워져 있을지 궁금하다.&lt;/p&gt;</description>
      <category>요즘 생각</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/148</guid>
      <comments>https://dino-dev.tistory.com/148#entry148comment</comments>
      <pubDate>Tue, 16 Jun 2026 14:47:22 +0900</pubDate>
    </item>
    <item>
      <title>(AI) 인수인계 해줘</title>
      <link>https://dino-dev.tistory.com/147</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;나는&amp;nbsp;2017년부터&amp;nbsp;7년&amp;nbsp;동안&amp;nbsp;Android&amp;nbsp;개발을&amp;nbsp;했고&amp;nbsp;2024년부터&amp;nbsp;지금까지&amp;nbsp;우아한형제들에&amp;nbsp;재직하면서&amp;nbsp;우아한테크코스의&amp;nbsp;모바일&amp;nbsp;안드로이드&amp;nbsp;코치로&amp;nbsp;교육을&amp;nbsp;하고&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;AI&amp;nbsp;시대가&amp;nbsp;오면서&amp;nbsp;Android&amp;nbsp;개발자,&amp;nbsp;Backend&amp;nbsp;개발자와&amp;nbsp;같이&amp;nbsp;특정&amp;nbsp;플랫폼에서&amp;nbsp;개발하는&amp;nbsp;것보다,&amp;nbsp;AI를&amp;nbsp;적극적으로&amp;nbsp;사용해서&amp;nbsp;바이브코딩으로&amp;nbsp;여러&amp;nbsp;플랫폼&amp;nbsp;위에서&amp;nbsp;개발할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;시대가&amp;nbsp;되고&amp;nbsp;있다고&amp;nbsp;느낀다.&lt;br /&gt;&lt;br /&gt;우아한테크코스&amp;nbsp;모바일&amp;nbsp;안드로이드&amp;nbsp;8기&amp;nbsp;크루(학생)들에게도&amp;nbsp;Android&amp;nbsp;교육을&amp;nbsp;했지만,&amp;nbsp;레벨2&amp;nbsp;후반부에서는&amp;nbsp;AI를&amp;nbsp;사용해서&amp;nbsp;Android가&amp;nbsp;아닌&amp;nbsp;아이폰&amp;nbsp;앱이나&amp;nbsp;플러터,&amp;nbsp;아두이노와&amp;nbsp;같이&amp;nbsp;다른&amp;nbsp;플랫폼을&amp;nbsp;개발하도록&amp;nbsp;교육을&amp;nbsp;하고&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;그러면서&amp;nbsp;나도&amp;nbsp;AI를&amp;nbsp;이용해서&amp;nbsp;다른&amp;nbsp;플랫폼을&amp;nbsp;개발할&amp;nbsp;줄&amp;nbsp;알아야&amp;nbsp;한다고&amp;nbsp;생각해서,&amp;nbsp;LMS+라는&amp;nbsp;교육&amp;nbsp;플랫폼&amp;nbsp;개발을&amp;nbsp;시작했다.&lt;br /&gt;&lt;br /&gt;기존에&amp;nbsp;있는&amp;nbsp;LMS+&amp;nbsp;프로젝트는&amp;nbsp;우리&amp;nbsp;팀의&amp;nbsp;슈퍼&amp;nbsp;백엔드&amp;nbsp;개발자인&amp;nbsp;구구(강현구)와&amp;nbsp;검프(김태정)가&amp;nbsp;백엔드와&amp;nbsp;프론트엔드&amp;nbsp;지식이&amp;nbsp;없어도&amp;nbsp;AI만&amp;nbsp;사용해서&amp;nbsp;제품을&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있도록&amp;nbsp;AI&amp;nbsp;도구를&amp;nbsp;많이&amp;nbsp;마련해뒀다.&amp;nbsp;그래서&amp;nbsp;만들고자&amp;nbsp;하는&amp;nbsp;기능을&amp;nbsp;Jira&amp;nbsp;티켓으로&amp;nbsp;잘&amp;nbsp;정리해두고&amp;nbsp;IntelliJ에서&amp;nbsp;claude&amp;nbsp;code로&amp;nbsp;&quot;Jira&amp;nbsp;이슈&amp;nbsp;번호&amp;nbsp;xxx&amp;nbsp;작업&amp;nbsp;해줘&quot;라고&amp;nbsp;하면&amp;nbsp;알아서&amp;nbsp;브랜치&amp;nbsp;만들고&amp;nbsp;작업&amp;nbsp;범위&amp;nbsp;정하고&amp;nbsp;테스트&amp;nbsp;코드까지&amp;nbsp;작성한다.&amp;nbsp;그리고&amp;nbsp;내가&amp;nbsp;MR(우형은&amp;nbsp;Gitlab을&amp;nbsp;사용)을&amp;nbsp;올려달라고&amp;nbsp;하면&amp;nbsp;MR&amp;nbsp;본문까지&amp;nbsp;작성해주는&amp;nbsp;너무나도&amp;nbsp;편리한&amp;nbsp;기능까지&amp;nbsp;마련되어&amp;nbsp;있다.&lt;br /&gt;&lt;br /&gt;AI가&amp;nbsp;만들어&amp;nbsp;주는&amp;nbsp;것을&amp;nbsp;내가&amp;nbsp;학습하지&amp;nbsp;않고&amp;nbsp;100%&amp;nbsp;맡기면,&amp;nbsp;나중에&amp;nbsp;버그가&amp;nbsp;발생했을&amp;nbsp;때&amp;nbsp;해결할&amp;nbsp;수&amp;nbsp;없다는&amp;nbsp;이야기를&amp;nbsp;수도&amp;nbsp;없이&amp;nbsp;들었다.&amp;nbsp;그런&amp;nbsp;이유로&amp;nbsp;AI를&amp;nbsp;써서&amp;nbsp;제품은&amp;nbsp;만들더라도,&amp;nbsp;내&amp;nbsp;이름으로&amp;nbsp;커밋하는&amp;nbsp;작업만큼은&amp;nbsp;내가&amp;nbsp;제대로&amp;nbsp;알고&amp;nbsp;있어야&amp;nbsp;한다고&amp;nbsp;생각했다.&lt;br /&gt;&lt;br /&gt;백엔드를&amp;nbsp;잘&amp;nbsp;모르는&amp;nbsp;내가&amp;nbsp;어떻게&amp;nbsp;작업한&amp;nbsp;내용을&amp;nbsp;제대로&amp;nbsp;알&amp;nbsp;수&amp;nbsp;있을지&amp;nbsp;고민이었다.&amp;nbsp;그러면서&amp;nbsp;내가&amp;nbsp;안드로이드를&amp;nbsp;처음에&amp;nbsp;어떻게&amp;nbsp;배웠지?&amp;nbsp;하고&amp;nbsp;생각해보니,&amp;nbsp;시니어&amp;nbsp;개발자에게&amp;nbsp;인수인계를&amp;nbsp;받으면서&amp;nbsp;프로젝트에&amp;nbsp;대한&amp;nbsp;이해도&amp;nbsp;하고&amp;nbsp;안드로이드&amp;nbsp;공부도&amp;nbsp;같이&amp;nbsp;할&amp;nbsp;수&amp;nbsp;있었다.&lt;br /&gt;&lt;br /&gt;그래서&amp;nbsp;나는&amp;nbsp;AI에게&amp;nbsp;&quot;너가&amp;nbsp;시니어&amp;nbsp;개발자가&amp;nbsp;되어서&amp;nbsp;이번&amp;nbsp;작업한&amp;nbsp;내용을&amp;nbsp;신입개발자에게&amp;nbsp;알려준다고&amp;nbsp;생각하고&amp;nbsp;하나씩&amp;nbsp;천천히&amp;nbsp;알려주도록&amp;nbsp;하고&amp;nbsp;이것을&amp;nbsp;스킬로&amp;nbsp;만들어줘&quot;라고&amp;nbsp;했다.&amp;nbsp;스킬에는&amp;nbsp;변경한&amp;nbsp;코드,&amp;nbsp;그렇게&amp;nbsp;작성한&amp;nbsp;이유,&amp;nbsp;이해에&amp;nbsp;필요한&amp;nbsp;개념&amp;nbsp;순서로&amp;nbsp;설명하도록&amp;nbsp;정리해뒀다.&amp;nbsp;그리고&amp;nbsp;나는&amp;nbsp;LMS+를&amp;nbsp;AI에게&amp;nbsp;개발시키고,&amp;nbsp;MR을&amp;nbsp;올리기&amp;nbsp;전에&amp;nbsp;꼭&amp;nbsp;&quot;인수인계&amp;nbsp;해줘&quot;라고&amp;nbsp;지시했다.&lt;br /&gt;&lt;br /&gt;그러면&amp;nbsp;AI는&amp;nbsp;나를&amp;nbsp;신입개발자로&amp;nbsp;인식하고&amp;nbsp;시니어&amp;nbsp;개발자의&amp;nbsp;관점에서&amp;nbsp;하나하나&amp;nbsp;작업을&amp;nbsp;친절하게&amp;nbsp;알려줬다.&amp;nbsp;그러면서&amp;nbsp;나는&amp;nbsp;Spring에&amp;nbsp;대한&amp;nbsp;지식도&amp;nbsp;공부하면서&amp;nbsp;LMS+에&amp;nbsp;있는&amp;nbsp;코드들도&amp;nbsp;빠르게&amp;nbsp;학습할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;됐다.&lt;br /&gt;&lt;br /&gt;그렇게&amp;nbsp;나는&amp;nbsp;시니어&amp;nbsp;대신&amp;nbsp;AI에게&amp;nbsp;인수인계를&amp;nbsp;받으며&amp;nbsp;새로운&amp;nbsp;플랫폼을&amp;nbsp;배우고&amp;nbsp;있다.&amp;nbsp;AI&amp;nbsp;시대가&amp;nbsp;됐다고&amp;nbsp;배우지&amp;nbsp;않아도&amp;nbsp;되는&amp;nbsp;게&amp;nbsp;아니라,&amp;nbsp;배우는&amp;nbsp;방법이&amp;nbsp;달라진&amp;nbsp;것뿐이라는&amp;nbsp;걸&amp;nbsp;직접&amp;nbsp;겪어보고&amp;nbsp;나서야&amp;nbsp;알게&amp;nbsp;됐다.&lt;br /&gt;&lt;br /&gt;AI가&amp;nbsp;코드를&amp;nbsp;다&amp;nbsp;짜주는&amp;nbsp;시대여도&amp;nbsp;커밋에&amp;nbsp;남는&amp;nbsp;이름은&amp;nbsp;결국&amp;nbsp;내&amp;nbsp;이름이다.&amp;nbsp;아직&amp;nbsp;백엔드를&amp;nbsp;잘&amp;nbsp;안다고&amp;nbsp;말할&amp;nbsp;수는&amp;nbsp;없지만,&amp;nbsp;&quot;인수인계&amp;nbsp;해줘&quot;&amp;nbsp;덕분에&amp;nbsp;적어도&amp;nbsp;내가&amp;nbsp;올린&amp;nbsp;MR은&amp;nbsp;설명할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;개발자가&amp;nbsp;됐다.&amp;nbsp;다음에는&amp;nbsp;어떤&amp;nbsp;플랫폼을&amp;nbsp;개발하게&amp;nbsp;될지&amp;nbsp;모르겠지만,&amp;nbsp;그때도&amp;nbsp;똑같이&amp;nbsp;AI에게&amp;nbsp;개발을&amp;nbsp;시키고&amp;nbsp;인수인계를&amp;nbsp;받으면서&amp;nbsp;하나씩&amp;nbsp;내&amp;nbsp;것으로&amp;nbsp;만들어&amp;nbsp;갈&amp;nbsp;생각이다.&lt;/p&gt;</description>
      <category>요즘 생각</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/147</guid>
      <comments>https://dino-dev.tistory.com/147#entry147comment</comments>
      <pubDate>Fri, 12 Jun 2026 00:59:03 +0900</pubDate>
    </item>
    <item>
      <title>(AI) 내 지식으로 정리해줘</title>
      <link>https://dino-dev.tistory.com/146</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘&amp;nbsp;내가&amp;nbsp;어떤&amp;nbsp;것을&amp;nbsp;알고,&amp;nbsp;어떤&amp;nbsp;것을&amp;nbsp;모르는지&amp;nbsp;정리해보고&amp;nbsp;싶어졌다.&lt;br /&gt;&lt;br /&gt;그렇다고&amp;nbsp;아는&amp;nbsp;것을&amp;nbsp;머리에서&amp;nbsp;하나하나&amp;nbsp;다&amp;nbsp;꺼내서&amp;nbsp;정리하고&amp;nbsp;싶지는&amp;nbsp;않았다.&amp;nbsp;대신&amp;nbsp;모르는&amp;nbsp;것을&amp;nbsp;마주칠&amp;nbsp;때마다&amp;nbsp;AI와&amp;nbsp;함께&amp;nbsp;정리하면서,&amp;nbsp;나만의&amp;nbsp;지식&amp;nbsp;DB를&amp;nbsp;만들어야겠다고&amp;nbsp;생각했다.&lt;br /&gt;&lt;br /&gt;최근&amp;nbsp;Hermes가&amp;nbsp;OpenClaw를&amp;nbsp;대신해서&amp;nbsp;사용하기&amp;nbsp;좋다는&amp;nbsp;소식을&amp;nbsp;듣고,&amp;nbsp;집에&amp;nbsp;있는&amp;nbsp;맥북에어&amp;nbsp;M5&amp;nbsp;기본형에&amp;nbsp;설치하고&amp;nbsp;Telegram과&amp;nbsp;연결했다.&amp;nbsp;덕분에&amp;nbsp;아이폰이든&amp;nbsp;맥북이든,&amp;nbsp;내가&amp;nbsp;접근할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;모든&amp;nbsp;디바이스에서&amp;nbsp;AI와&amp;nbsp;이야기할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;됐다.&lt;br /&gt;&lt;br /&gt;쓰는&amp;nbsp;방법은&amp;nbsp;간단하다.&amp;nbsp;개발&amp;nbsp;공부하면서&amp;nbsp;모르는&amp;nbsp;게&amp;nbsp;있거나&amp;nbsp;지식으로&amp;nbsp;정리하고&amp;nbsp;싶은&amp;nbsp;게&amp;nbsp;생기면&amp;nbsp;Telegram으로&amp;nbsp;AI와&amp;nbsp;이야기를&amp;nbsp;나눈다.&amp;nbsp;그리고&amp;nbsp;&quot;내&amp;nbsp;지식에&amp;nbsp;정리해줘&quot;라고&amp;nbsp;한마디만&amp;nbsp;하면&amp;nbsp;AI가&amp;nbsp;나의&amp;nbsp;iCloud에&amp;nbsp;정리해준다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HjyNr/dJMcaalhAnG/jRHYUEI5yylWLZ0eN1G5w0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HjyNr/dJMcaalhAnG/jRHYUEI5yylWLZ0eN1G5w0/img.png&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;613&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;49.46&quot; style=&quot;width: 48.885%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HjyNr/dJMcaalhAnG/jRHYUEI5yylWLZ0eN1G5w0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHjyNr%2FdJMcaalhAnG%2FjRHYUEI5yylWLZ0eN1G5w0%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;386&quot; height=&quot;613&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJllao/dJMcab5A0lL/wpgr6aejAxYwmq7kaVf6Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJllao/dJMcab5A0lL/wpgr6aejAxYwmq7kaVf6Ok/img.png&quot; data-origin-width=&quot;397&quot; data-origin-height=&quot;617&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9522%;&quot; data-widthpercent=&quot;50.54&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJllao/dJMcab5A0lL/wpgr6aejAxYwmq7kaVf6Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJllao%2FdJMcab5A0lL%2Fwpgr6aejAxYwmq7kaVf6Ok%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;397&quot; height=&quot;617&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;그리고&amp;nbsp;정리한&amp;nbsp;지식을&amp;nbsp;잊지&amp;nbsp;않도록,&amp;nbsp;매일&amp;nbsp;아침&amp;nbsp;9시마다&amp;nbsp;내&amp;nbsp;지식을&amp;nbsp;기반으로&amp;nbsp;면접&amp;nbsp;문제&amp;nbsp;5개를&amp;nbsp;보내달라고&amp;nbsp;해뒀다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bouxVa/dJMcai4ElY6/YyepKZMKV7ow9QNyl7xW20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bouxVa/dJMcai4ElY6/YyepKZMKV7ow9QNyl7xW20/img.png&quot; data-origin-width=&quot;384&quot; data-origin-height=&quot;578&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.5647%; margin-right: 10px;&quot; data-widthpercent=&quot;52.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bouxVa/dJMcai4ElY6/YyepKZMKV7ow9QNyl7xW20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbouxVa%2FdJMcai4ElY6%2FYyepKZMKV7ow9QNyl7xW20%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;384&quot; height=&quot;578&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnG1Kp/dJMcahdG3LD/wrqubiT87hDW0Cdz80chnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnG1Kp/dJMcahdG3LD/wrqubiT87hDW0Cdz80chnk/img.png&quot; data-origin-width=&quot;363&quot; data-origin-height=&quot;596&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.2726%;&quot; data-widthpercent=&quot;47.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnG1Kp/dJMcahdG3LD/wrqubiT87hDW0Cdz80chnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnG1Kp%2FdJMcahdG3LD%2FwrqubiT87hDW0Cdz80chnk%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;363&quot; height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;이게&amp;nbsp;생각보다&amp;nbsp;효과가&amp;nbsp;좋았다.&amp;nbsp;출근길에&amp;nbsp;AI가&amp;nbsp;보내준&amp;nbsp;면접&amp;nbsp;문제를&amp;nbsp;풀다&amp;nbsp;보면,&amp;nbsp;며칠&amp;nbsp;전에&amp;nbsp;정리했던&amp;nbsp;내용이&amp;nbsp;자연스럽게&amp;nbsp;다시&amp;nbsp;떠오른다.&amp;nbsp;그냥&amp;nbsp;정리만&amp;nbsp;하고&amp;nbsp;끝났을&amp;nbsp;때보다&amp;nbsp;확실히&amp;nbsp;기억에&amp;nbsp;오래&amp;nbsp;남는다.&amp;nbsp;모르는&amp;nbsp;걸&amp;nbsp;정리하고,&amp;nbsp;그걸&amp;nbsp;질문으로&amp;nbsp;다시&amp;nbsp;돌려받는&amp;nbsp;이&amp;nbsp;루프가&amp;nbsp;만들어진&amp;nbsp;뒤로는&amp;nbsp;&quot;이거&amp;nbsp;저번에&amp;nbsp;봤는데&amp;nbsp;뭐였지?&quot;&amp;nbsp;하는&amp;nbsp;순간이&amp;nbsp;눈에&amp;nbsp;띄게&amp;nbsp;줄었다.&lt;br /&gt;&lt;br /&gt;아는&amp;nbsp;것을&amp;nbsp;정리하는&amp;nbsp;게&amp;nbsp;아니라&amp;nbsp;모르는&amp;nbsp;것을&amp;nbsp;쌓아가는&amp;nbsp;방식이라,&amp;nbsp;부담&amp;nbsp;없이&amp;nbsp;계속하게&amp;nbsp;된다.&amp;nbsp;이&amp;nbsp;지식&amp;nbsp;DB가&amp;nbsp;1년&amp;nbsp;뒤에&amp;nbsp;얼마나&amp;nbsp;쌓여&amp;nbsp;있을지&amp;nbsp;기대된다.&lt;/p&gt;</description>
      <category>요즘 생각</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/146</guid>
      <comments>https://dino-dev.tistory.com/146#entry146comment</comments>
      <pubDate>Thu, 11 Jun 2026 23:47:06 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-58 세부 말라파스쿠아 - calanggaman</title>
      <link>https://dino-dev.tistory.com/144</link>
      <description>&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2026.01.03&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 필리핀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 말라파스쿠아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트:&lt;span&gt;&amp;nbsp;&lt;/span&gt;calanggaman&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 요한 다이브(&lt;a href=&quot;https://www.johandive.com/&quot;&gt;https://www.johandive.com/&lt;/a&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;/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;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 48분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 11:37&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 16m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 49분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 12:26&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 60bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Nitrox 32%&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&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: #555555; text-align: start;&quot;&gt;Wet(2.5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&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-filename=&quot;IMG_1767.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfRWjY/dJMcajt4Tt0/ubvMZpuDdaOJyaucFl0mC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfRWjY/dJMcajt4Tt0/ubvMZpuDdaOJyaucFl0mC1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfRWjY/dJMcajt4Tt0/ubvMZpuDdaOJyaucFl0mC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfRWjY%2FdJMcajt4Tt0%2FubvMZpuDdaOJyaucFl0mC1%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1767.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0600.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xCKar/dJMcagEcnF1/hvKheWKDjhxWMlIVr4S2iK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xCKar/dJMcagEcnF1/hvKheWKDjhxWMlIVr4S2iK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xCKar/dJMcagEcnF1/hvKheWKDjhxWMlIVr4S2iK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxCKar%2FdJMcagEcnF1%2FhvKheWKDjhxWMlIVr4S2iK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0600.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0602.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQ6c6r/dJMcacoeUHh/8kJFrRTmBGs5Y1eh5CzxI1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQ6c6r/dJMcacoeUHh/8kJFrRTmBGs5Y1eh5CzxI1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQ6c6r/dJMcacoeUHh/8kJFrRTmBGs5Y1eh5CzxI1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQ6c6r%2FdJMcacoeUHh%2F8kJFrRTmBGs5Y1eh5CzxI1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0602.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0657.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0cT7M/dJMcafSOoH2/BjCWuAoOqP3EMCltZq7nP1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0cT7M/dJMcafSOoH2/BjCWuAoOqP3EMCltZq7nP1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0cT7M/dJMcafSOoH2/BjCWuAoOqP3EMCltZq7nP1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0cT7M%2FdJMcafSOoH2%2FBjCWuAoOqP3EMCltZq7nP1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0657.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0634.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V7cWh/dJMcaiorQTP/ZfPchhaMeUf5zpwgr0NlQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V7cWh/dJMcaiorQTP/ZfPchhaMeUf5zpwgr0NlQ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V7cWh/dJMcaiorQTP/ZfPchhaMeUf5zpwgr0NlQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV7cWh%2FdJMcaiorQTP%2FZfPchhaMeUf5zpwgr0NlQ1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0634.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0630.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbS0Ai/dJMcaiBWBFO/zrNknftz4spsTjx1qYyk91/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbS0Ai/dJMcaiBWBFO/zrNknftz4spsTjx1qYyk91/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbS0Ai/dJMcaiBWBFO/zrNknftz4spsTjx1qYyk91/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbS0Ai%2FdJMcaiBWBFO%2FzrNknftz4spsTjx1qYyk91%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0630.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/J0cXlsDCWYI?si=JvfXZ8wjWvy_xjI7&quot; width=&quot;560&quot; height=&quot;315&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/144</guid>
      <comments>https://dino-dev.tistory.com/144#entry144comment</comments>
      <pubDate>Sat, 3 Jan 2026 22:23:21 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-57 세부 말라파스쿠아 - calanggaman</title>
      <link>https://dino-dev.tistory.com/143</link>
      <description>&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2026.01.03&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 필리핀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 말라파스쿠아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트:&lt;span&gt;&amp;nbsp;&lt;/span&gt;calanggaman&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 요한 다이브(&lt;a href=&quot;https://www.johandive.com/&quot;&gt;https://www.johandive.com/&lt;/a&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;/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;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 53분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 10:05&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 18m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 44분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 10:49&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 60bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Nitrox 32%&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&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: #555555; text-align: start;&quot;&gt;Wet(2.5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1766.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3xNQA/dJMcafZyYqm/s5dXzbqPfM5ItjGudenr11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3xNQA/dJMcafZyYqm/s5dXzbqPfM5ItjGudenr11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3xNQA/dJMcafZyYqm/s5dXzbqPfM5ItjGudenr11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3xNQA%2FdJMcafZyYqm%2Fs5dXzbqPfM5ItjGudenr11%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1766.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0595.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcpL3D/dJMcacBMroP/QosxEGHo1IOWE5lkYLfxm0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcpL3D/dJMcacBMroP/QosxEGHo1IOWE5lkYLfxm0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcpL3D/dJMcacBMroP/QosxEGHo1IOWE5lkYLfxm0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcpL3D%2FdJMcacBMroP%2FQosxEGHo1IOWE5lkYLfxm0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0595.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&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&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/upMAxviQQew?si=PGCsFXp_1HbWy42m&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/143</guid>
      <comments>https://dino-dev.tistory.com/143#entry143comment</comments>
      <pubDate>Sat, 3 Jan 2026 22:19:47 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-56 세부 말라파스쿠아 - calanggaman</title>
      <link>https://dino-dev.tistory.com/142</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2026.01.03&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 필리핀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 말라파스쿠아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: calanggaman&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 요한 다이브(&lt;a href=&quot;https://www.johandive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.johandive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 8:23&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 25m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 49분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 9:12&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 60bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Nitrox 32%&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2.5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1765.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csx2V0/dJMcacV4CiA/4UE7jVAcFv4x8SuIMALoZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csx2V0/dJMcacV4CiA/4UE7jVAcFv4x8SuIMALoZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csx2V0/dJMcacV4CiA/4UE7jVAcFv4x8SuIMALoZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcsx2V0%2FdJMcacV4CiA%2F4UE7jVAcFv4x8SuIMALoZ0%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1765.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0586.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0cbhV/dJMcachtkTN/reANurcHGLG2O8KKkrkGJk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0cbhV/dJMcachtkTN/reANurcHGLG2O8KKkrkGJk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0cbhV/dJMcachtkTN/reANurcHGLG2O8KKkrkGJk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0cbhV%2FdJMcachtkTN%2FreANurcHGLG2O8KKkrkGJk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0586.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/142</guid>
      <comments>https://dino-dev.tistory.com/142#entry142comment</comments>
      <pubDate>Sat, 3 Jan 2026 22:19:12 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-55 세부 말라파스쿠아 - kimud shoal</title>
      <link>https://dino-dev.tistory.com/141</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2026.01.02&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 필리핀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 말라파스쿠아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 요한 다이브(&lt;a href=&quot;https://www.johandive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.johandive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 62분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 12:53&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 18m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 47분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 13:40&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 60bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2.5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파도가 조금 있어서 다른 배들은 먼저 갔고 우리는 남아서 마지막 다이빙을 진행했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바다 속에는 우리만 있어서 그런지 환도상어가 아주 가까이 와서 좋은 영상을 찍을 수 있었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1764.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCxUVt/dJMcadHpTAB/GnR721jskhHONOY2XjuUO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCxUVt/dJMcadHpTAB/GnR721jskhHONOY2XjuUO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCxUVt/dJMcadHpTAB/GnR721jskhHONOY2XjuUO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCxUVt%2FdJMcadHpTAB%2FGnR721jskhHONOY2XjuUO1%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1764.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0564.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lu2JL/dJMcafrJOGN/O8mpniZQUTae4lBdaCKAE1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lu2JL/dJMcafrJOGN/O8mpniZQUTae4lBdaCKAE1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lu2JL/dJMcafrJOGN/O8mpniZQUTae4lBdaCKAE1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLu2JL%2FdJMcafrJOGN%2FO8mpniZQUTae4lBdaCKAE1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0564.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0565.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9H1GR/dJMcac9zcLI/m8VV1L1akJy8R70rCefQQ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9H1GR/dJMcac9zcLI/m8VV1L1akJy8R70rCefQQ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9H1GR/dJMcac9zcLI/m8VV1L1akJy8R70rCefQQ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9H1GR%2FdJMcac9zcLI%2Fm8VV1L1akJy8R70rCefQQ1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0565.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0577.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp7dPf/dJMcaajE60t/2Zs5NG8SCRqPPlZLKypkQk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp7dPf/dJMcaajE60t/2Zs5NG8SCRqPPlZLKypkQk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp7dPf/dJMcaajE60t/2Zs5NG8SCRqPPlZLKypkQk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp7dPf%2FdJMcaajE60t%2F2Zs5NG8SCRqPPlZLKypkQk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0577.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0578.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bppQHR/dJMcacV4CP6/5tjXL6TD8UJFIXgSKru0Pk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bppQHR/dJMcacV4CP6/5tjXL6TD8UJFIXgSKru0Pk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bppQHR/dJMcacV4CP6/5tjXL6TD8UJFIXgSKru0Pk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbppQHR%2FdJMcacV4CP6%2F5tjXL6TD8UJFIXgSKru0Pk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0578.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0579.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rX0Lb/dJMcajgyjkS/A1y5LePxTjhQCxJ3FesetK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rX0Lb/dJMcajgyjkS/A1y5LePxTjhQCxJ3FesetK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rX0Lb/dJMcajgyjkS/A1y5LePxTjhQCxJ3FesetK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrX0Lb%2FdJMcajgyjkS%2FA1y5LePxTjhQCxJ3FesetK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0579.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0580.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba8oCw/dJMcajgyjkm/X5hNtnXK278L8VVUXj3bZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba8oCw/dJMcajgyjkm/X5hNtnXK278L8VVUXj3bZ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba8oCw/dJMcajgyjkm/X5hNtnXK278L8VVUXj3bZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba8oCw%2FdJMcajgyjkm%2FX5hNtnXK278L8VVUXj3bZ1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0580.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0581.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/V2uA6/dJMcabQoKq1/kXWozceiqq0QbLJPYObbM1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/V2uA6/dJMcabQoKq1/kXWozceiqq0QbLJPYObbM1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/V2uA6/dJMcabQoKq1/kXWozceiqq0QbLJPYObbM1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FV2uA6%2FdJMcabQoKq1%2FkXWozceiqq0QbLJPYObbM1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0581.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0582.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sr1u4/dJMcadN9U8C/knvwzoxtW4ijApsMlVZdyK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sr1u4/dJMcadN9U8C/knvwzoxtW4ijApsMlVZdyK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sr1u4/dJMcadN9U8C/knvwzoxtW4ijApsMlVZdyK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsr1u4%2FdJMcadN9U8C%2FknvwzoxtW4ijApsMlVZdyK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0582.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2360&quot; data-origin-height=&quot;1184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/54soE/dJMcafec0Ig/iu6WHajasuH8hbbU0li0Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/54soE/dJMcafec0Ig/iu6WHajasuH8hbbU0li0Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/54soE/dJMcafec0Ig/iu6WHajasuH8hbbU0li0Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F54soE%2FdJMcafec0Ig%2Fiu6WHajasuH8hbbU0li0Y0%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;2360&quot; height=&quot;1184&quot; data-origin-width=&quot;2360&quot; data-origin-height=&quot;1184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/_o3RZDcMr2A?si=d1PHnJX71gZ1mVMy&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/141</guid>
      <comments>https://dino-dev.tistory.com/141#entry141comment</comments>
      <pubDate>Sat, 3 Jan 2026 20:36:44 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-54 세부 말라파스쿠아 - kimud shoal</title>
      <link>https://dino-dev.tistory.com/140</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2026.01.02&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 필리핀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 말라파스쿠아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: kimud&amp;nbsp;shoal&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 요한 다이브(&lt;a href=&quot;https://www.johandive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.johandive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 49분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 11:04&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 23m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 47분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 11:51&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 70bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2.5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&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-filename=&quot;IMG_1763.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p1Go5/dJMcai9Mtph/1z625K0ngfdahzEjYKZuZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p1Go5/dJMcai9Mtph/1z625K0ngfdahzEjYKZuZ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p1Go5/dJMcai9Mtph/1z625K0ngfdahzEjYKZuZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp1Go5%2FdJMcai9Mtph%2F1z625K0ngfdahzEjYKZuZ1%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1763.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0558.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8vqvy/dJMcac2Ojec/efvYEBDy8O2GpCyKVVoiZk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8vqvy/dJMcac2Ojec/efvYEBDy8O2GpCyKVVoiZk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8vqvy/dJMcac2Ojec/efvYEBDy8O2GpCyKVVoiZk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8vqvy%2FdJMcac2Ojec%2FefvYEBDy8O2GpCyKVVoiZk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0558.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0560.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z45EN/dJMcah39tpz/r9b6dOad12uZKegqmxzwf1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z45EN/dJMcah39tpz/r9b6dOad12uZKegqmxzwf1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z45EN/dJMcah39tpz/r9b6dOad12uZKegqmxzwf1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ45EN%2FdJMcah39tpz%2Fr9b6dOad12uZKegqmxzwf1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0560.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;iframe src=&quot;https://www.youtube.com/embed/PaunQVPE6xU?si=3BJwXziv9zilTtW_&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/140</guid>
      <comments>https://dino-dev.tistory.com/140#entry140comment</comments>
      <pubDate>Sat, 3 Jan 2026 20:34:13 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-53 세부 말라파스쿠아 - monad shoal</title>
      <link>https://dino-dev.tistory.com/139</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2026.01.02&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 필리핀&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 말라파스쿠아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: monad&amp;nbsp;shoal&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 요한 다이브(&lt;a href=&quot;https://www.johandive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.johandive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 09:29&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 15m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 46분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 10:15&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 80bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2.5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&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-filename=&quot;IMG_1762.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnW8xo/dJMcai9Mto1/7NKTBLfFCRC6JcdtkOKEs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnW8xo/dJMcai9Mto1/7NKTBLfFCRC6JcdtkOKEs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnW8xo/dJMcai9Mto1/7NKTBLfFCRC6JcdtkOKEs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnW8xo%2FdJMcai9Mto1%2F7NKTBLfFCRC6JcdtkOKEs1%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1762.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0515.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caerBJ/dJMcafyuN3B/WeZi5gOKVyjrsuuNUsHuHK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caerBJ/dJMcafyuN3B/WeZi5gOKVyjrsuuNUsHuHK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caerBJ/dJMcafyuN3B/WeZi5gOKVyjrsuuNUsHuHK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaerBJ%2FdJMcafyuN3B%2FWeZi5gOKVyjrsuuNUsHuHK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0515.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0517.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buTdOE/dJMcaaYgPLb/kI1I1bhTnHDTJ9AYz3Z6u0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buTdOE/dJMcaaYgPLb/kI1I1bhTnHDTJ9AYz3Z6u0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buTdOE/dJMcaaYgPLb/kI1I1bhTnHDTJ9AYz3Z6u0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuTdOE%2FdJMcaaYgPLb%2FkI1I1bhTnHDTJ9AYz3Z6u0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0517.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0520.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgAcjE/dJMcacBMpyX/COPHDRGHmv7C72GKfjrCcK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgAcjE/dJMcacBMpyX/COPHDRGHmv7C72GKfjrCcK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgAcjE/dJMcacBMpyX/COPHDRGHmv7C72GKfjrCcK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgAcjE%2FdJMcacBMpyX%2FCOPHDRGHmv7C72GKfjrCcK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0520.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0524.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Tlv0I/dJMcafyuN3x/9mnBjgoGMaNT3u1ZkwL1qK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Tlv0I/dJMcafyuN3x/9mnBjgoGMaNT3u1ZkwL1qK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Tlv0I/dJMcafyuN3x/9mnBjgoGMaNT3u1ZkwL1qK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTlv0I%2FdJMcafyuN3x%2F9mnBjgoGMaNT3u1ZkwL1qK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0524.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0525.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qOQfG/dJMcaaRu2mG/sIBuMgM6KFGgqapACcPM8K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qOQfG/dJMcaaRu2mG/sIBuMgM6KFGgqapACcPM8K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qOQfG/dJMcaaRu2mG/sIBuMgM6KFGgqapACcPM8K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqOQfG%2FdJMcaaRu2mG%2FsIBuMgM6KFGgqapACcPM8K%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0525.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0526.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctxdzT/dJMcafyuN3u/I6BOgQegHwImHat3GRBJdk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctxdzT/dJMcafyuN3u/I6BOgQegHwImHat3GRBJdk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctxdzT/dJMcafyuN3u/I6BOgQegHwImHat3GRBJdk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctxdzT%2FdJMcafyuN3u%2FI6BOgQegHwImHat3GRBJdk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0526.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&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>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/139</guid>
      <comments>https://dino-dev.tistory.com/139#entry139comment</comments>
      <pubDate>Sat, 3 Jan 2026 20:31:27 +0900</pubDate>
    </item>
    <item>
      <title>(Unity) GetComponent 완벽 정리: 기본부터 최적화까지 (feat. InChildren, InParent)</title>
      <link>https://dino-dev.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 유니티로 게임을 개발하다 보면 가장 많이 치는 코드가 무엇일까요? 아마도 &lt;b&gt;transform.position&lt;/b&gt; 다음으로 &lt;b&gt;GetComponent&lt;/b&gt;가 아닐까 싶습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;&quot;내 오브젝트에 붙은 다른 컴포넌트를 가져오고 싶다!&quot; 혹은 &quot;내 자식 오브젝트의 스크립트를 제어하고 싶다!&quot; 할 때 필수적인 이 함수들.&lt;/p&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 **GetComponent 4대장(기본, InChildren, InParent, plural)**의 정확한 차이점과, &lt;b data-index-in-node=&quot;70&quot; data-path-to-node=&quot;6&quot;&gt;실무에서 꼭 지켜야 할 성능 최적화 꿀팁&lt;/b&gt;까지 정리해 보겠습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;7&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;1. 가장 기본: GetComponent&amp;lt;T&amp;gt;()&lt;/h2&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;가장 흔하게 사용하는 함수입니다. &lt;b data-index-in-node=&quot;19&quot; data-path-to-node=&quot;9&quot;&gt;&quot;현재 스크립트가 붙어 있는 바로 그 GameObject&quot;&lt;/b&gt; 안에서 컴포넌트를 찾습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;탐색 범위:&lt;/b&gt; 내 GameObject 하나.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;특징:&lt;/b&gt; 찾고자 하는 컴포넌트가 없으면 null을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;void Start()
{
    // 내 오브젝트에 붙어있는 Rigidbody 가져오기
    Rigidbody rb = GetComponent&amp;lt;Rigidbody&amp;gt;();
    
    if (rb != null)
    {
        rb.AddForce(Vector3.up * 10);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;12&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size26&quot;&gt;2. 자식들 다 나와: GetComponentInChildren&amp;lt;T&amp;gt;()&lt;/h2&gt;
&lt;p data-path-to-node=&quot;14&quot; data-ke-size=&quot;size16&quot;&gt;나(현재 오브젝트)를 포함하여 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;14&quot;&gt;내 하위(자식, 손자...) 계층 구조&lt;/b&gt;를 뒤져서 컴포넌트를 찾습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;15&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0,0&quot;&gt;탐색 범위:&lt;/b&gt; 나(Self) + 모든 자식(Children) 오브젝트.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,0&quot;&gt;주의점:&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;15,1,1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,1,1,0,0&quot;&gt;나 자신부터 검사합니다.&lt;/b&gt; 내 오브젝트에 해당 컴포넌트가 있다면 자식 껀 무시하고 내 껄 반환합니다.&lt;/li&gt;
&lt;li&gt;깊이 우선 탐색(DFS) 방식으로 찾으며, 하나라도 찾으면 &lt;b data-index-in-node=&quot;33&quot; data-path-to-node=&quot;15,1,1,1,0&quot;&gt;즉시 탐색을 종료&lt;/b&gt;하고 반환합니다.&lt;/li&gt;
&lt;li&gt;비활성화(activeSelf == false)된 오브젝트는 기본적으로 무시합니다 (오버로딩으로 포함시킬 수 있음).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;// 내 자식들(혹은 나) 중에 MeshRenderer가 있으면 하나만 가져와!
MeshRenderer childMesh = GetComponentInChildren&amp;lt;MeshRenderer&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;17&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;18&quot; data-ke-size=&quot;size26&quot;&gt;3. 부모님 계신가요: GetComponentInParent&amp;lt;T&amp;gt;()&lt;/h2&gt;
&lt;p data-path-to-node=&quot;19&quot; data-ke-size=&quot;size16&quot;&gt;이번엔 반대로 &lt;b&gt;위쪽(부모)&lt;/b&gt;으로 올라가며 탐색합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;20&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,0,0&quot;&gt;탐색 범위:&lt;/b&gt; 나(Self) + 부모(Parent) + 부모의 부모...&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;20,1,0&quot;&gt;특징:&lt;/b&gt; 역시 &lt;b data-index-in-node=&quot;7&quot; data-path-to-node=&quot;20,1,0&quot;&gt;나 자신부터 검사&lt;/b&gt;합니다. 주로 UI 버튼에서 상위 캔버스의 특정 스크립트를 찾거나, 캐릭터의 파츠(손, 발)에서 몸통(Main Body) 스크립트를 찾을 때 유용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;// 내 위쪽 계층에 있는 PlayerController 스크립트 찾기
PlayerController player = GetComponentInParent&amp;lt;PlayerController&amp;gt;();
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;22&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;23&quot; data-ke-size=&quot;size26&quot;&gt;4. 싹 다 긁어모아: GetComponents&amp;lt;T&amp;gt;() (뒤에 's' 주의!)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;24&quot; data-ke-size=&quot;size16&quot;&gt;위의 함수들은 조건에 맞는 컴포넌트를 '딱 하나'만 찾아옵니다. 하지만 한 오브젝트에 BoxCollider가 3개 붙어있다면요? 혹은 자식들에 있는 모든 Enemy 스크립트를 관리하고 싶다면요?&lt;/p&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;이때 s가 붙은 함수들을 사용합니다. (GetComponents, GetComponentsInChildren 등)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;26&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;26,0,0&quot;&gt;반환 타입:&lt;/b&gt; 해당 컴포넌트들의 **배열(T[])**을 반환합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;void Start()
{
    // 내 자식들에 있는 모든 'Monster' 스크립트를 가져와서 배열에 담음
    Monster[] monsters = GetComponentsInChildren&amp;lt;Monster&amp;gt;();

    // 반복문으로 전체 제어
    foreach (Monster m in monsters)
    {
        m.Die(); // 전멸 시키기
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;28&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;29&quot; data-ke-size=&quot;size26&quot;&gt;  [중요] 실무 개발자의 성능 최적화 Tip&lt;/h2&gt;
&lt;p data-path-to-node=&quot;30&quot; data-ke-size=&quot;size16&quot;&gt;GetComponent 시리즈는 편리하지만, 내부적으로 GameObject를 뒤지는 연산이 들어가기 때문에 &lt;b data-index-in-node=&quot;60&quot; data-path-to-node=&quot;30&quot;&gt;비용이 꽤 비싼 함수&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;31&quot; data-ke-size=&quot;size23&quot;&gt;1. 절대 Update() 문에 넣지 마세요!&lt;/h3&gt;
&lt;p data-path-to-node=&quot;32&quot; data-ke-size=&quot;size16&quot;&gt;초보자들이 가장 많이 하는 실수입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// ❌ 최악의 예시 (매 프레임마다 찾으러 다님)
void Update() {
    GetComponent&amp;lt;Rigidbody&amp;gt;().velocity = Vector3.zero;
}

// ✅ 좋은 예시 (캐싱 사용)
Rigidbody rb;

void Start() {
    rb = GetComponent&amp;lt;Rigidbody&amp;gt;(); // 시작할 때 한 번만 찾아서 저장
}

void Update() {
    rb.velocity = Vector3.zero; // 저장된 변수 사용
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;34&quot; data-ke-size=&quot;size23&quot;&gt;2. TryGetComponent를 애용하세요 (Unity 2019.2+)&lt;/h3&gt;
&lt;p data-path-to-node=&quot;35&quot; data-ke-size=&quot;size16&quot;&gt;최신 유니티 버전에서는 TryGetComponent 사용을 권장합니다. 컴포넌트를 가져오는 동시에 null 체크까지 안전하게 처리하며, 메모리 할당(GC) 측면에서도 미세하게 더 유리합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;// 이렇게 쓰세요!
if (TryGetComponent&amp;lt;Rigidbody&amp;gt;(out Rigidbody rb))
{
    // rb가 null이 아닐 때만 실행됨
    rb.mass = 10;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;37&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;38&quot; data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-path-to-node=&quot;39&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 유니티 스크립팅의 기초인 GetComponent 패밀리에 대해 알아보았습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;40&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내꺼 찾을 땐: GetComponent&lt;/li&gt;
&lt;li&gt;자식꺼 찾을 땐: GetComponentInChildren&lt;/li&gt;
&lt;li&gt;부모꺼 찾을 땐: GetComponentInParent&lt;/li&gt;
&lt;li&gt;여러 개 찾을 땐: GetComponents&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;41&quot; data-ke-size=&quot;size16&quot;&gt;이 4가지만 상황에 맞춰 잘 쓰고, &lt;b&gt;캐싱(Caching)&lt;/b&gt;만 습관화한다면 여러분의 코드는 훨씬 깔끔하고 빨라질 것입니다.&lt;/p&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/138</guid>
      <comments>https://dino-dev.tistory.com/138#entry138comment</comments>
      <pubDate>Fri, 26 Dec 2025 10:32:25 +0900</pubDate>
    </item>
    <item>
      <title>(Unity) 인스펙터 창을 깔끔하게! [Header] 속성 완벽 가이드</title>
      <link>https://dino-dev.tistory.com/137</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;유니티로 게임을 개발하다 보면 MonoBehaviour 스크립트에 수많은 public 변수나 [SerializeField] 변수들이 쌓이게 됩니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;변수가 5개, 10개를 넘어가기 시작하면 &lt;b data-index-in-node=&quot;23&quot; data-path-to-node=&quot;4&quot;&gt;Inspector(인스펙터)&lt;/b&gt; 창은 그야말로 혼돈의 도가니가 되곤 하죠. 어떤 게 이동 속도인지, 어떤 게 공격력인지 한눈에 들어오지 않아 스크롤을 위아래로 내리며 시간을 낭비한 경험, 다들 있으시죠?&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 코드 한 줄로 여러분의 인스펙터 창을 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;5&quot;&gt;전문가처럼 깔끔하게 정리해 주는 [Header] 속성&lt;/b&gt;에 대해 알아보겠습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;6&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size26&quot;&gt;1. [Header] 속성이란?&lt;/h2&gt;
&lt;p data-path-to-node=&quot;8&quot; data-ke-size=&quot;size16&quot;&gt;[Header]는 유니티 인스펙터 창에서 변수들 위에 굵은 제목(라벨)을 달아주는 속성(Attribute)입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;기능적으로 게임 로직에 아무런 영향을 주지 않지만, 개발자(또는 레벨 디자이너)가 인스펙터 창을 볼 때 &lt;b data-index-in-node=&quot;58&quot; data-path-to-node=&quot;9&quot;&gt;변수들의 카테고리를 시각적으로 구분&lt;/b&gt;해 주는 아주 강력한 도구입니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size23&quot;&gt;사용법&lt;/h3&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;사용법은 매우 간단합니다. 변수 선언부 바로 위에 [Header(&quot;제목&quot;)]만 적어주면 됩니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[Header(&quot;보여질 텍스트&quot;)]
public int myVariable;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;13&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size26&quot;&gt;2. 실제 예제로 비교하기&lt;/h2&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;RPG 게임의 플레이어 스크립트를 작성한다고 가정해 봅시다. 체력, 마나, 이동 속도, 점프력, 공격력, 공격 범위 등 수많은 변수가 섞여 있다면 어떻게 보일까요?&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;16&quot; data-ke-size=&quot;size23&quot;&gt;❌ [Header] 적용 전&lt;/h3&gt;
&lt;p data-path-to-node=&quot;17&quot; data-ke-size=&quot;size16&quot;&gt;변수들이 아무런 구분 없이 나열되어 있어 가독성이 떨어집니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cpp&quot;&gt;&lt;code&gt;using UnityEngine;

public class PlayerStats : MonoBehaviour
{
    public float health = 100f;
    public float mana = 50f;
    public float moveSpeed = 5f;
    public float runMultiplier = 1.5f;
    public float jumpForce = 10f;
    public int attackDamage = 20;
    public float attackRange = 2.5f;
    public float attackSpeed = 1.2f;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;✅ [Header] 적용 후&lt;/h3&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;변수들을 성격에 맞게 그룹화하여 인스펙터에서 훨씬 직관적으로 보입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;using UnityEngine;

public class PlayerStats : MonoBehaviour
{
    [Header(&quot;Base Stats&quot;)] // 기본 스탯 그룹
    public float health = 100f;
    public float mana = 50f;

    [Header(&quot;Movement Settings&quot;)] // 이동 관련 설정 그룹
    public float moveSpeed = 5f;
    public float runMultiplier = 1.5f;
    public float jumpForce = 10f;

    [Header(&quot;Combat Settings&quot;)] // 전투 관련 설정 그룹
    public int attackDamage = 20;
    public float attackRange = 2.5f;
    public float attackSpeed = 1.2f;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;이제 인스펙터 창을 보면 &lt;b data-index-in-node=&quot;14&quot; data-path-to-node=&quot;22&quot;&gt;&quot;Base Stats&quot;&lt;/b&gt;, &lt;b data-index-in-node=&quot;28&quot; data-path-to-node=&quot;22&quot;&gt;&quot;Movement Settings&quot;&lt;/b&gt;, &lt;b&gt;&quot;Combat Settings&quot;&lt;/b&gt;라는 굵은 제목 아래에 변수들이 예쁘게 정렬된 것을 볼 수 있습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;23&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;24&quot; data-ke-size=&quot;size26&quot;&gt;3. 함께 쓰면 좋은 꿀팁 속성들&lt;/h2&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;[Header]만 써도 훌륭하지만, 다음 속성들과 함께 사용하면 인스펙터의 가독성이 200% 상승합니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;26&quot; data-ke-size=&quot;size23&quot;&gt;1) [Space]&lt;/h3&gt;
&lt;p data-path-to-node=&quot;27&quot; data-ke-size=&quot;size16&quot;&gt;[Header]는 제목을 달아주지만, 위쪽 변수와 간격을 벌려주지는 않습니다. 그룹 간에 시원한 여백을 주고 싶다면 [Space]를 사용하세요.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[Space(10)] // 10픽셀만큼 띄우기
[Header(&quot;Movement Settings&quot;)]
public float moveSpeed;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;29&quot; data-ke-size=&quot;size23&quot;&gt;2) [Tooltip]&lt;/h3&gt;
&lt;p data-path-to-node=&quot;30&quot; data-ke-size=&quot;size16&quot;&gt;변수에 마우스를 올렸을 때 설명을 띄워주는 기능입니다. 협업할 때 필수적입니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;[Header(&quot;Combat&quot;)]
[Tooltip(&quot;플레이어의 기본 공격 데미지입니다.&quot;)]
public int damage;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;32&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;33&quot; data-ke-size=&quot;size26&quot;&gt;4. 주의사항&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;34&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,0,0&quot;&gt;따옴표 필수:&lt;/b&gt; [Header(&quot;제목&quot;)] 안에는 반드시 큰따옴표(&quot;&quot;)로 문자열을 감싸야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,1,0&quot;&gt;첫 번째 변수에만:&lt;/b&gt; 그룹의 가장 첫 번째 변수 위에만 적어주면 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;34,2,0&quot;&gt;Private 변수:&lt;/b&gt; private 변수라도 [SerializeField]를 사용해 인스펙터에 노출시킨다면 [Header]를 적용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;35&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;36&quot; data-ke-size=&quot;size26&quot;&gt;마무리&lt;/h2&gt;
&lt;p data-path-to-node=&quot;37&quot; data-ke-size=&quot;size16&quot;&gt;작은 습관이 개발 속도를 바꿉니다. 지금 당장 여러분의 복잡한 스크립트를 열어 [Header] 한 줄을 추가해 보세요. 나중에 이 코드를 다시 볼 미래의 자신과, 함께 일하는 동료들이 여러분에게 고마워할 것입니다!&lt;/p&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/137</guid>
      <comments>https://dino-dev.tistory.com/137#entry137comment</comments>
      <pubDate>Sat, 20 Dec 2025 15:45:10 +0900</pubDate>
    </item>
    <item>
      <title>(Unity) transform.position은 Vector3인데, 왜 Vector2 변수에 그냥 들어갈까?</title>
      <link>https://dino-dev.tistory.com/136</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 유니티로 2D 게임을 개발하다 보면 문득 의아한 점이 생깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 분명 transform.position은 3D 좌표인 Vector3 타입이라고 배웠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 실제 코드를 짤 때는 Vector2 변수에 그냥 대입해도 아무런 에러가 나지 않습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;void Update() {
    // Vector3 타입을 Vector2 변수에 넣는데 에러가 안 난다?
    Vector2 myPos = transform.position; 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;혹시 유니티가 2D 모드라서 알아서 타입을 바꿔준 걸까요? 아닙니다. 여기에는 C# 언어의 아주 편리하면서도 재미있는 비밀이 숨겨져 있습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;오늘은 겉으로 보이는 현상뿐만 아니라, 그 내부에서 일어나는 실제 코드의 동작 원리(암시적 형변환과 연산자 오버로딩)까지 깊이 있게, 하지만 아주 쉽게 파헤쳐 보겠습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;8&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size26&quot;&gt;1. 팩트 체크: Unity는 뼈속까지 3D 엔진이다&lt;/h2&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;가장 먼저 확실히 짚고 넘어가야 할 사실이 있습니다. 여러분이 2D 프로젝트를 생성했더라도, transform.position은 언제나 Vector3 (x, y, z) 타입입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;유니티의 인스펙터 창을 자세히 보세요. 2D 스프라이트라도 Transform 컴포넌트에는 항상 X, Y, Z 세 가지 축이 모두 존재합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;단지 2D 게임에서는 우리가 Z축을 잘 안 쓰거나, 카메라가 평면으로 비춰주고 있을 뿐이죠.&lt;/p&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;그렇다면, 왜 3개짜리 데이터(Vector3)가 2개짜리 그릇(Vector2)에 쏙 들어가는 걸까요?&lt;/p&gt;
&lt;hr data-path-to-node=&quot;13&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size26&quot;&gt;2. 마법의 정체: 암시적 형변환 연산자 (Implicit Operator)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;이 현상은 유니티의 버그가 아니라, C# 문법을 이용해 유니티 개발자들이 미리 만들어둔 변환 규칙 덕분입니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;이를 전문 용어로 연산자 오버로딩(Operator Overloading) 중에서도 암시적 형변환(Implicit Conversion)이라고 부릅니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;우리가 보는 Vector3 구조체의 내부 코드를 뜯어보면(실제로는 컴파일되어 있지만), 대략 아래와 같은 형태의 특수 함수가 숨겨져 있습니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;Vector3 내부의 비밀 코드&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;public struct Vector3
{
    public float x;
    public float y;
    public float z;

    // 핵심: 'implicit operator' 키워드
    // 의미: &quot;누군가 나(Vector3)를 Vector2로 쓰려고 하면, 이 함수를 몰래 실행해.&quot;
    public static implicit operator Vector2(Vector3 v)
    {
        // Vector3의 x, y만 따서 새로운 Vector2를 반환 (z는 버림)
        return new Vector2(v.x, v.y);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;Vector2 내부의 비밀 코드&lt;/h3&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;반대로 Vector2에도 비슷한 코드가 들어있습니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;fortran&quot;&gt;&lt;code&gt;public struct Vector2
{
    public float x;
    public float y;

    // 의미: &quot;누군가 나(Vector2)를 Vector3로 쓰려고 하면, 이 함수를 실행해.&quot;
    public static implicit operator Vector3(Vector2 v)
    {
        // x, y는 그대로 쓰고, z 자리에 0을 채워서 반환
        return new Vector3(v.x, v.y, 0);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;22&quot; data-ke-size=&quot;size16&quot;&gt;즉, 개발자가 일일이 변환 함수를 호출하지 않아도 컴파일러가 알아서 이 타입은 저 타입으로 바꿀 수 있다고 약속되어 있어라고 판단하고 변환 코드를 끼워 넣는 것입니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;23&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;24&quot; data-ke-size=&quot;size26&quot;&gt;3. 컴파일러가 하는 일 (자동 통역)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;여러분이 코드를 작성하고 플레이 버튼을 누르면, 컴파일러는 여러분의 코드를 기계가 이해할 수 있게 번역합니다. 이 과정에서 어떤 일이 일어날까요?&lt;/p&gt;
&lt;p data-path-to-node=&quot;26&quot; data-ke-size=&quot;size16&quot;&gt;작성한 코드&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Vector3 pos3D = new Vector3(10, 20, 5);
Vector2 pos2D = pos3D; // 그냥 대입
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;28&quot; data-ke-size=&quot;size16&quot;&gt;컴파일러가 실제로 변환한 코드&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;Vector3 pos3D = new Vector3(10, 20, 5);
// 컴파일러: &quot;타입이 다르네? 아! implicit operator가 정의되어 있구나. 함수를 호출하자.&quot;
Vector2 pos2D = Vector3.op_Implicit(pos3D); // 변환 함수 자동 호출
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-path-to-node=&quot;30&quot; data-ke-size=&quot;size16&quot;&gt;우리가 (Vector2)라고 명시적으로 형변환을 적지 않아도 되는 이유는, 컴파일러가 이 약속된 규칙을 보고 알아서 처리해주기 때문입니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;31&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;32&quot; data-ke-size=&quot;size26&quot;&gt;4. 왜 명시적(Explicit)이 아니라 암시적(Implicit)일까?&lt;/h2&gt;
&lt;p data-path-to-node=&quot;33&quot; data-ke-size=&quot;size16&quot;&gt;C#에서는 보통 데이터 손실이 일어나는 변환(Vector3에서 z값이 사라지는 상황)은 명시적(Explicit) 키워드를 사용해서, 개발자가 (Vector2)pos3D 처럼 직접 캐스팅을 하도록 강제하는 것이 원칙입니다. 실수를 방지하기 위해서죠.&lt;/p&gt;
&lt;p data-path-to-node=&quot;34&quot; data-ke-size=&quot;size16&quot;&gt;하지만 유니티는 게임 개발의 편의성을 선택했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;35&quot; data-ke-size=&quot;size16&quot;&gt;&quot;2D 게임을 만들 때 transform.position은 무조건 Vector3인데, 이걸 매번 (Vector2)transform.position이라고 캐스팅해서 쓰게 하면 개발자들이 너무 귀찮지 않을까? 그냥 자동으로 되게 해주자!&quot;&lt;/p&gt;
&lt;p data-path-to-node=&quot;36&quot; data-ke-size=&quot;size16&quot;&gt;이러한 유니티 엔지니어들의 배려 덕분에 우리는 코드를 훨씬 간결하게 작성할 수 있게 된 것입니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;37&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;38&quot; data-ke-size=&quot;size26&quot;&gt;5. 주의! 초보자가 자주 겪는 Z축 실종 사건&lt;/h2&gt;
&lt;p data-path-to-node=&quot;39&quot; data-ke-size=&quot;size16&quot;&gt;하지만 이 편리함에는 함정이 있습니다. 바로 Z축 데이터의 손실입니다. 암시적 변환이 일어날 때, Vector2에서 Vector3로 갈 때는 Z가 무조건 0으로 채워진다는 점을 잊으면 안 됩니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;40&quot; data-ke-size=&quot;size23&quot;&gt;버그가 발생하는 코드 예시&lt;/h3&gt;
&lt;p data-path-to-node=&quot;41&quot; data-ke-size=&quot;size16&quot;&gt;내 캐릭터가 현재 (5, 5, -10) 위치에 있어서 카메라에 잘 보인다고 가정해 봅시다. (Z가 -10이라서 카메라 앞에 있음)&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;void Update() {
    // 1. 현재 위치를 Vector2로 가져옵니다. 
    // 여기서 Z값(-10)은 버려집니다! -&amp;gt; (5, 5)
    Vector2 tempPos = transform.position; 
    
    // 2. X 좌표만 살짝 움직입니다.
    tempPos.x += 1f; 

    // 3. 다시 적용합니다. 
    // 이때 Vector2 -&amp;gt; Vector3 변환이 일어나면서 Z는 자동으로 '0'이 됩니다.
    transform.position = tempPos; 
    
    // 결과: 내 캐릭터 위치는 (6, 5, 0)이 되었습니다.
    // 원래 있던 -10이 0으로 바뀌면서, 배경 뒤로 숨거나 카메라에서 사라질 수 있습니다!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-path-to-node=&quot;43&quot; data-ke-size=&quot;size23&quot;&gt;올바른 해결 방법&lt;/h3&gt;
&lt;p data-path-to-node=&quot;44&quot; data-ke-size=&quot;size16&quot;&gt;Z축 값을 유지하면서 X, Y만 바꾸고 싶다면, Vector2 변수에 담았다가 다시 넣는 것보다는 아래처럼 새로운 Vector3를 만드는 것이 가장 안전합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// Z값은 원래 내 Z값을 그대로 유지!
transform.position = new Vector3(newX, newY, transform.position.z);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-path-to-node=&quot;46&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;47&quot; data-ke-size=&quot;size26&quot;&gt;요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;48&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;transform.position은 2D 프로젝트여도 무조건 Vector3 타입입니다.&lt;/li&gt;
&lt;li&gt;Vector2로 그냥 써도 되는 이유는 유니티 내부 코드에 정의된 암시적 형변환(Implicit Operator) 기능 덕분입니다.&lt;/li&gt;
&lt;li&gt;컴파일러가 이 규칙을 보고 자동으로 변환 함수를 연결해 줍니다.&lt;/li&gt;
&lt;li&gt;편리하지만 변환 과정에서 Z값이 0으로 초기화되거나 사라질 수 있으니, 깊이(Depth)가 중요한 게임에서는 주의해야 합니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/136</guid>
      <comments>https://dino-dev.tistory.com/136#entry136comment</comments>
      <pubDate>Sat, 20 Dec 2025 15:11:10 +0900</pubDate>
    </item>
    <item>
      <title>[AI 시대의 생존 전략] 개발자가 집중해야 할 &amp;lsquo;나머지 30%&amp;rsquo;의 가치</title>
      <link>https://dino-dev.tistory.com/135</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;생성형 AI의 등장으로 개발의 패러다임이 바뀌고 있습니다. 코드를 작성하는 속도는 비약적으로 빨라졌고, 누구나 그럴듯한 결과물을 만들어낼 수 있는 시대가 되었습니다. 하지만 이것이 개발자의 종말을 의미할까요? 아닙니다. 오히려 &lt;b data-index-in-node=&quot;127&quot; data-path-to-node=&quot;3&quot;&gt;개발자의 '진짜 역량'이 더욱 중요해지는 시기&lt;/b&gt;가 도래했습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;개발자는 이제 &lt;b data-index-in-node=&quot;8&quot; data-path-to-node=&quot;4&quot;&gt;AI가 잘하는 70%의 영역을 적극적으로 위임하고, 인간만이 할 수 있는 나머지 30%의 핵심 역량에 집중&lt;/b&gt;해야 합니다. 오늘은 시니어와 주니어, 각 연차별로 이 30%를 채우기 위해 어떤 전략을 취해야 하는지 이야기해보려 합니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;5&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;1. 시니어 개발자: 전략가이자 멘토가 되어야 한다&lt;/h3&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;시니어 개발자는 단순히 '코드를 빨리 짜는 사람'이 아닙니다. AI라는 강력한 도구를 손에 쥔 지금, 시니어의 역할은 '나무를 베는 인부'에서 '집을 짓는 건축가'로 확장되어야 합니다.&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size20&quot;&gt;  도메인 지식과 직관을 통한 결정 (Decision Making)&lt;/h4&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;AI는 수많은 나무를 순식간에 베어다 줄 수 있습니다. 하지만 &quot;어떤 나무를 베어야 하는가?&quot;, &quot;이 자재로 어떻게 튼튼한 집을 지을 것인가?&quot;를 결정하는 것은 결국 사람입니다. 시니어 개발자는 오랜 경험에서 축적된 도메인 지식과 문제 발생 가능성을 꿰뚫어 보는 직관(Intuition)을 발휘해야 합니다. AI가 해결할 수 없는 비즈니스의 맥락과 기술적 결정은 여전히 여러분의 몫입니다.&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;10&quot; data-ke-size=&quot;size20&quot;&gt;  '물고기 잡는 법'을 가르치는 멘토링&lt;/h4&gt;
&lt;p data-path-to-node=&quot;11&quot; data-ke-size=&quot;size16&quot;&gt;주니어 개발자가 AI를 통해 코드를 쏟아낼 때, 시니어는 단순히 그 코드를 리뷰하고 실수를 잡아주는 것에 그쳐서는 안 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;검증하는 방법을 전수하세요:&lt;/b&gt; 주니어가 AI의 결과물을 스스로 검토하고 테스트하는 방법을 가르쳐야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;판단력을 길러주세요:&lt;/b&gt; &quot;이 코드가 왜 문제인지&quot;, &quot;더 나은 아키텍처는 무엇인지&quot;에 대한 전략적 사고를 공유해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;앞으로 시니어의 가치는 &lt;b data-index-in-node=&quot;13&quot; data-path-to-node=&quot;13&quot;&gt;판단력, 전략적 사고, 그리고 후배를 육성하는 리더십&lt;/b&gt;에서 증명될 것입니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;14&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;2. 주니어 개발자: '작동하는 코드' 그 이상을 보라&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;AI 어시스턴트 덕분에 코딩과 테스트 작성에 드는 물리적인 시간은 획기적으로 줄었습니다. 하지만 주니어 개발자는 그 줄어든 시간을 '편안함'이 아닌 '깊이'를 더하는 데 써야 합니다.&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size20&quot;&gt;  숲을 보는 눈: 아키텍처와 요구사항&lt;/h4&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;단순 구현보다 중요한 것은 전체 그림을 이해하는 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0,0&quot;&gt;기존 시스템의 이해:&lt;/b&gt; 레거시 아키텍처를 충분히 파악하고, 요구사항을 명확하게 명시할 수 있어야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0&quot;&gt;일관성 유지:&lt;/b&gt; AI가 생성한 코드가 기존 프로젝트의 패턴과 일관성을 유지하는지 검토해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0&quot;&gt;보안과 엣지 케이스:&lt;/b&gt; AI가 놓치기 쉬운 보안 취약점이나 극단적인 상황(Edge Case)을 찾아내는 눈을 길러야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size20&quot;&gt;❓ 'Why'를 질문하는 습관&lt;/h4&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;AI가 작성한 코드를 맹신하지 마세요. 주니어 개발자는 항상 &quot;왜?&quot;라는 질문을 던져야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;22&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;AI는 왜 이 방식을 선택했는가?&quot;&lt;/li&gt;
&lt;li&gt;&quot;이 코드는 줄 단위로 어떤 역할을 하는가?&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-path-to-node=&quot;23&quot; data-ke-size=&quot;size16&quot;&gt;AI에게 코드 설명을 요청하여 기초 지식을 다지고, &lt;b data-index-in-node=&quot;29&quot; data-path-to-node=&quot;23&quot;&gt;AI가 내린 결정이 타당한지 판단하는 사고력&lt;/b&gt;을 길러야 합니다. 최종 결정권자는 AI가 아니라 바로 '나'이기 때문입니다.&lt;/p&gt;
&lt;h4 data-path-to-node=&quot;24&quot; data-ke-size=&quot;size20&quot;&gt; ️ AI 안전망 없는 '야생'의 훈련&lt;/h4&gt;
&lt;p data-path-to-node=&quot;25&quot; data-ke-size=&quot;size16&quot;&gt;아이러니하게도 AI를 잘 쓰기 위해서는 가끔 &lt;b data-index-in-node=&quot;25&quot; data-path-to-node=&quot;25&quot;&gt;AI를 끄는 시간&lt;/b&gt;이 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;26&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;26,0,0&quot;&gt;맨땅에 헤딩하기:&lt;/b&gt; AI 없이 스스로 문제를 해결하고 디버깅하는 연습을 하세요. 이 과정이 없으면 AI가 없을 때 아무것도 할 수 없는 개발자가 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;26,1,0&quot;&gt;철저한 검증:&lt;/b&gt; LLM이 짠 코드는 '잘 돌아갈 것'이라는 믿음 대신, '틀렸을지도 모른다'는 의심을 가져야 합니다. 포괄적인 유닛 테스트와 수동 테스트를 통해 비즈니스 로직을 철저히 검증하는 습관을 들이세요.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;27&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;28&quot; data-ke-size=&quot;size23&quot;&gt;결국 '안목'의 싸움&lt;/h3&gt;
&lt;p data-path-to-node=&quot;29&quot; data-ke-size=&quot;size16&quot;&gt;코드를 작동하게 만드는 것은 이제 누구나 할 수 있는 일이 되었습니다. 하지만 &lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;29&quot;&gt;좋은 코드 구조를 설계하고, 유지보수하기 좋은 스타일을 만들며, 비즈니스 가치를 창출하는 안목&lt;/b&gt;은 AI가 대신해 줄 수 없습니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;30&quot; data-ke-size=&quot;size16&quot;&gt;AI는 훌륭한 러닝메이트입니다. 하지만 그 속도를 제어하고 올바른 방향으로 이끄는 것은 결국 개발자 본인의 &lt;b data-index-in-node=&quot;60&quot; data-path-to-node=&quot;30&quot;&gt;기본기와 통찰력&lt;/b&gt;임을 잊지 마시기 바랍니다.&lt;/p&gt;</description>
      <category>개발/이것저것</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/135</guid>
      <comments>https://dino-dev.tistory.com/135#entry135comment</comments>
      <pubDate>Fri, 19 Dec 2025 14:12:37 +0900</pubDate>
    </item>
    <item>
      <title>바이브 코딩(Vibe Coding)의 핵심 원칙 12가지</title>
      <link>https://dino-dev.tistory.com/134</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;최근 개발 생태계에서 AI는 단순한 도구를 넘어 협업의 파트너로 자리 잡았습니다. 하지만 AI에게 모든 것을 맡기는 '자동 조종' 방식은 때로 예상치 못한 버그와 기술 부채를 남기기도 합니다.&lt;/p&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;진정한 의미의 &lt;b&gt;바이브 코딩(Vibe Coding)&lt;/b&gt;은 감(Vibe)에만 의존하는 것이 아니라, AI와의 명확한 경계를 설정하고 주도권을 유지하는 데서 시작됩니다. 바이브 코딩을 위한 핵심 원칙 12가지와 그에 대한 저의 생각을 공유합니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;5&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;6&quot; data-ke-size=&quot;size23&quot;&gt;1. 명확한 커뮤니케이션: AI는 마법사가 아닌 '주니어'다&lt;/h3&gt;
&lt;p data-path-to-node=&quot;7&quot; data-ke-size=&quot;size16&quot;&gt;AI와의 협업에서 가장 중요한 것은 &lt;b data-index-in-node=&quot;20&quot; data-path-to-node=&quot;7&quot;&gt;명확성&lt;/b&gt;입니다. AI는 우리의 마음을 읽는 것이 아니라, 우리가 제공한 텍스트를 기반으로 동작하기 때문입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0,0&quot;&gt;원하는 내용을 구체적이고 명확하게 전달한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모호한 지시는 모호한 코드를 만듭니다. 맥락(Context)을 충분히 제공할수록 AI의 출력 결과는 우리가 의도한 정답에 가까워집니다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,1,0&quot;&gt;AI를 주니어 개발자처럼 감독한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실력이 좋지만 경험이 부족한 주니어에게 업무를 맡길 때처럼, 명확한 가이드라인을 주고 결과물을 꼼꼼히 살피는 태도가 사고를 방지합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,2,0&quot;&gt;AI에게 생각을 맡기지 않는다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;8,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비즈니스 로직의 본질과 핵심 설계 결정은 인간의 영역입니다. AI는 '구현'의 효율을 높여주는 도구이지 '의사결정'의 주체가 되어서는 안 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;9&quot; data-ke-size=&quot;size23&quot;&gt;2. 엄격한 품질 관리: 신뢰하되 검증하라&lt;/h3&gt;
&lt;p data-path-to-node=&quot;10&quot; data-ke-size=&quot;size16&quot;&gt;AI가 생성한 코드가 당장 에러 없이 작동한다고 해서 '완성'된 것은 아닙니다. 시스템 전체의 안정성을 유지하기 위한 엄격한 기준이 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0,0&quot;&gt;항상 AI의 출력을 원래 의도와 맞는지 검증한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI는 확률적으로 단어를 선택하기 때문에 논리적 비약이 생길 수 있습니다. 검증은 불필요한 의심이 아니라 품질 관리를 위한 필수 과정입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,1,0&quot;&gt;인간이 작성했든 AI가 생성했든 모든 코드는 반드시 리뷰를 거친다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드의 출처보다 중요한 것은 '코드의 품질' 그 자체입니다. 이 원칙은 전체 시스템의 신뢰도를 유지하는 최후의 보루가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,2,0&quot;&gt;이해하지 못하는 코드는 머지하지 않는다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;11,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;'돌아가니까 일단 넣자'는 태도는 관리가 불가능한 기술 부채를 만듭니다. 개발자가 코드에 대한 장악력을 유지해야만 추후 유지보수가 가능합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;12&quot; data-ke-size=&quot;size23&quot;&gt;3. 체계적인 프로세스: 기록과 분리&lt;/h3&gt;
&lt;p data-path-to-node=&quot;13&quot; data-ke-size=&quot;size16&quot;&gt;AI와 함께 개발 속도를 높일 때일수록, 나중에 발생할 문제를 방지하기 위해 흔적을 잘 남겨야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;코드를 생성하기 전에 미리 규칙을 정한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨벤션이나 아키텍처 가이드를 미리 정의하는 것은 AI에게 정확한 '지도'를 쥐여주는 것과 같습니다. 이 과정이 결과물의 일관성을 결정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;AI 변경 사항을 분리할 수 있도록 별도의 커밋을 수행한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;나중에 발생할 수 있는 버그의 원인을 추적하고, AI가 개입한 범위를 명확히 파악하기 위한 매우 실무적이고 효과적인 전략입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,2,0&quot;&gt;문서, 주석, 아키텍처 결정 기록(ADR)을 우선시 한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드는 결과물일 뿐입니다. 왜 이러한 구조를 선택했는지에 대한 논리적 기록은 AI가 대신해 줄 수 없는 인간 개발자의 고유한 자산입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size23&quot;&gt;4. 팀 문화로의 통합: 공유와 지속적인 개선&lt;/h3&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;AI 활용 능력을 개인의 기술로만 두지 않고, 팀 전체의 생산성으로 전환하는 과정이 필요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,0,0&quot;&gt;AI 사용을 개발 관련 소통의 자연스러운 부분으로 생각한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AI를 특별한 치트키가 아니라 코드 리뷰어, 페어 프로그래밍 파트너처럼 팀의 일원으로 수용할 때 협업의 시너지가 발생합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,1,0&quot;&gt;효과적인 프롬프트를 공유하고 재사용한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시행착오를 통해 얻은 효율적인 소통 방식을 팀의 자산으로 만들면, 조직 전체의 작업 효율이 상향 평준화될 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;17,2,0&quot;&gt;정기적으로 성찰하고 이터레이터를 개선한다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;17,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기술 환경은 계속해서 변합니다. 우리가 AI와 협업하는 방식이 여전히 최선인지 주기적으로 돌아보고 개선하는 태도가 진정한 바이브 코딩의 완성입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;18&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-path-to-node=&quot;19&quot; data-ke-size=&quot;size23&quot;&gt;마치며&lt;/h3&gt;
&lt;p data-path-to-node=&quot;20&quot; data-ke-size=&quot;size16&quot;&gt;바이브 코딩은 단순히 편하게 코딩하는 기술이 아니라, &lt;b data-index-in-node=&quot;30&quot; data-path-to-node=&quot;20&quot;&gt;인간 개발자가 더 높은 수준의 설계와 의사결정에 집중할 수 있도록 AI와 리듬을 맞추는 과정&lt;/b&gt;입니다.&lt;/p&gt;</description>
      <category>개발/이것저것</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/134</guid>
      <comments>https://dino-dev.tistory.com/134#entry134comment</comments>
      <pubDate>Thu, 18 Dec 2025 21:34:35 +0900</pubDate>
    </item>
    <item>
      <title>(Unity) 근처 적을 스캔하고 최단 거리 대상 찾기</title>
      <link>https://dino-dev.tistory.com/133</link>
      <description>&lt;p&gt;게임 개발을 하다 보면, 캐릭터가 주변의 적이나 아이템을 자동으로 감지하고 그중에서 가장 가까운 대상을 선택해야 하는 경우가 정말 많습니다.&lt;/p&gt;
&lt;p&gt;유니티 2D 환경에서 &lt;strong&gt;&lt;code&gt;Physics2D.CircleCastAll&lt;/code&gt;&lt;/strong&gt;을 활용하여 주변 목표물을 효과적으로 스캔하고, 가장 가까운 대상을 정확하게 찾아내는 방법에 대한 스크립트를 공유하고 분석해 보겠습니다.&lt;/p&gt;
&lt;h3&gt;  핵심 스캐너 스크립트 살펴보기&lt;/h3&gt;
&lt;p&gt;이 스크립트는 &lt;code&gt;Scanner&lt;/code&gt;라는 이름으로 &lt;code&gt;MonoBehaviour&lt;/code&gt;를 상속받으며, 주변을 원 형태로 스캔하는 역할을 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Scanner : MonoBehaviour
{
    public float scanRange;
    public LayerMask targetLayer;
    public RaycastHit2D[] detectedTargets;
    public Transform nearestTarget;
    // ... (메소드)
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;  변수 해설&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;scanRange&lt;/code&gt;&lt;/strong&gt;: 우리가 주변을 탐색할 원의 반지름(반경)입니다. 인스펙터 창에서 쉽게 조절하며 탐색 범위를 시각적으로 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;targetLayer&lt;/code&gt;&lt;/strong&gt;: 가장 중요한 설정입니다! 적이나 목표물 등 &lt;strong&gt;감지하고자 하는 대상&lt;/strong&gt;이 속한 레이어를 지정합니다. 불필요한 오브젝트(배경, 벽 등)를 걸러내는 필터 역할을 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;nearestTarget&lt;/code&gt;&lt;/strong&gt;: 최종적으로 감지된 대상 중에서 &lt;strong&gt;가장 가까운 대상의 &lt;code&gt;Transform&lt;/code&gt;&lt;/strong&gt;이 여기에 저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;  스캔 작동 원리&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;CircleCastAll&lt;/code&gt;의 마법주변 감지는 물리 업데이트 주기인 &lt;code&gt;FixedUpdate&lt;/code&gt;에서 실행됩니다. 이는 물리 관련 작업의 안정성을 높여줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void FixedUpdate()
{
    detectedTargets = Physics2D.CircleCastAll(
        transform.position, // 기준 위치 (나 자신)
        scanRange,          // 탐색 범위 (반지름)
        Vector2.zero,       // 방향 (고정된 원 형태이므로 zero)
        0f,                 // 거리 (고정된 원 형태이므로 0)
        targetLayer         // 탐색할 레이어 마스크
    );

    nearestTarget = FindNearestTarget();
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;&lt;code&gt;Physics2D.CircleCastAll&lt;/code&gt;을 사용한 이유&lt;/h4&gt;
&lt;p&gt;이 메소드는 특정 지점(&lt;code&gt;transform.position&lt;/code&gt;)에 &lt;strong&gt;반경 &lt;code&gt;scanRange&lt;/code&gt;를 가진 원&lt;/strong&gt;을 만들었을 때, 그 원 안에 겹치는 모든 2D 충돌체 정보를 배열(&lt;code&gt;detectedTargets&lt;/code&gt;)로 반환합니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;direction&lt;/code&gt;과 &lt;code&gt;distance&lt;/code&gt;를 &lt;code&gt;Vector2.zero&lt;/code&gt;와 &lt;code&gt;0f&lt;/code&gt;로 설정함으로써, 우리는 &lt;strong&gt;&amp;quot;현재 위치에 고정된 원 안에 무엇이 있나?&amp;quot;&lt;/strong&gt;를 묻는 &lt;code&gt;OverlapCircleAll&lt;/code&gt;과 거의 동일하게, 하지만 좀 더 유연하게 정보를 얻어낼 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;  최단 거리 탐색 알고리즘&lt;/h3&gt;
&lt;p&gt;가장 가까운 적을 찾아서&lt;code&gt;detectedTargets&lt;/code&gt; 배열에는 스캔 범위 내 모든 적의 정보가 담겨 있습니다.&lt;br&gt;이제 이 중에서 가장 가까운 단 하나의 대상을 골라내야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Transform FindNearestTarget()
{
    float nearestDistance = Mathf.Infinity;
    Transform result = null;

    foreach (RaycastHit2D hit in detectedTargets)
    {
        // 1. 기준점과 현재 감지된 대상 간의 실제 거리 계산
        float distance = Vector2.Distance(transform.position, hit.transform.position);

        // 2. 현재 대상의 거리가 지금까지 발견한 &amp;#39;최단 거리&amp;#39;보다 짧은가?
        if (distance &amp;lt; nearestDistance)
        {
            // 3. 더 가까운 대상을 찾았으므로 값 업데이트
            nearestDistance = distance;
            result = hit.transform;
        }
    }
    return result; // 가장 가까운 대상의 Transform 반환
}&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;
&lt;li&gt;우선, &lt;strong&gt;&lt;code&gt;nearestDistance&lt;/code&gt;를 &lt;code&gt;Mathf.Infinity&lt;/code&gt; (무한대)&lt;/strong&gt;로 초기화합니다. 이렇게 하면 배열의 첫 번째 대상과의 거리가 무조건 최단 거리가 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;detectedTargets&lt;/code&gt; 배열을 &lt;strong&gt;&lt;code&gt;foreach&lt;/code&gt;&lt;/strong&gt; 루프로 돌며 모든 대상을 확인합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Vector2.Distance&lt;/code&gt;를 이용해 실제 거리를 계산하고, 현재 기록된 &lt;code&gt;nearestDistance&lt;/code&gt;와 비교하여 가장 작은 값을 계속 갱신합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;최종적으로 루프가 끝났을 때, &lt;code&gt;result&lt;/code&gt;에 저장된 &lt;code&gt;Transform&lt;/code&gt;이 바로 우리 캐릭터와 가장 가까운 대상이 되는 것입니다!&lt;/p&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/133</guid>
      <comments>https://dino-dev.tistory.com/133#entry133comment</comments>
      <pubDate>Sat, 13 Dec 2025 23:37:47 +0900</pubDate>
    </item>
    <item>
      <title>(Unity) 회전하는 오브젝트 구현 가이드</title>
      <link>https://dino-dev.tistory.com/132</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;유니티에서 플레이어 주변을 회전하는 무기나 오브젝트를 어떻게 구현하는지에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 &lt;b&gt;뱀파이어 서바이버(Vampire Survivors)&lt;/b&gt;와 같은 게임에서 볼 수 있는 시스템을 만드는 기초가 될 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 구현 목표 및 미리 보기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 만들고자 하는 것은 다음과 같은 기능을 가진 스크립트입니다:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;지속적인 회전:&lt;/b&gt; 오브젝트 자체가 특정 속도로 계속 회전합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자식 오브젝트 배치:&lt;/b&gt; 회전하는 오브젝트를 부모로 삼아, 여러 개의 자식 오브젝트(무기)를 원형으로 균등하게 배치합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. C# 스크립트 분석&lt;/h2&gt;
&lt;pre class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;using UnityEngine;

public class Weapon : MonoBehaviour
{
    // [공개 변수] 유니티 에디터에서 설정 가능
    public GameObject prefab; // 회전하는 무기/오브젝트의 프리헵
    public int count;         // 배치할 무기/오브젝트의 개수
    public float speed;       // 오브젝트의 회전 속도

    // 매 프레임마다 호출됨
    void Update()
    {
        // Z축을 기준으로 'speed * Time.deltaTime'만큼 회전합니다.
        // Time.deltaTime을 곱하여 프레임 속도와 상관없이 일정한 속도로 회전하게 만듭니다.
        transform.Rotate(0, 0, speed * Time.deltaTime);
    }

    // 오브젝트들을 배치하고 위치를 업데이트하는 함수
    void Batch()
    {
        for (int i = 0; i &amp;lt; count; i++)
        {
            Transform bullet;
            if (i &amp;lt; transform.childCount)
            {
                bullet = transform.GetChild(i); // 기존에 생성된 자식 오브젝트 재사용 (최적화)
            }
            else
            {
                bullet = Instantiate(prefab).transform; // 새 자식 오브젝트 생성
                bullet.parent = transform; // 현재 오브젝트를 부모로 설정
            }

            // 위치 및 회전 초기화
            // 부모를 기준으로 로컬 위치를 원점(0,0,0)으로, 로컬 회전을 기본값으로 리셋합니다.
            bullet.localPosition = Vector3.zero;
            bullet.localRotation = Quaternion.identity;

            // 회전 및 위치 적용 (원형 배치)

            // 1. 회전 계산: 'count' 개수만큼 360도를 균등하게 나눕니다.
            Vector3 rotVec = Vector3.forward * 360 * i / count;

            // 2. 오브젝트를 계산된 각도만큼 회전시킵니다.
            bullet.Rotate(rotVec);

            // 3. 오브젝트의 '위' 방향(local up)으로 1.5f만큼 이동시킵니다.
            // 이렇게 하면 부모의 중앙에서 'rotVec' 각도로 1.5f 떨어진 위치에 배치됩니다.
            bullet.Translate(bullet.up * 1.5f, Space.World); 
            // 참고: 'Space.World' 대신 'Space.Self'를 사용하면 더 자연스러운 무기 배치를 할 수도 있습니다.
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 스크립트 주요 기능 설명&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;A. 회전 구현 (&lt;code&gt;Update&lt;/code&gt; 함수)&lt;/h3&gt;
&lt;pre class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot;&gt;&lt;code&gt;void Update()
{
    transform.Rotate(0, 0, speed * Time.deltaTime);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;transform.Rotate(x, y, z)&lt;/code&gt;는 현재 오브젝트를 주어진 축을 기준으로 회전시키는 함수입니다.&lt;/li&gt;
&lt;li&gt;우리는 &lt;b&gt;Z축(0, 0, 1)&lt;/b&gt;을 기준으로 &lt;code&gt;speed&lt;/code&gt;에 비례하여 회전시킵니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Time.deltaTime&lt;/code&gt;을 곱해줌으로써 게임이 어떤 프레임 속도로 실행되든 일정한 회전 속도를 유지하게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;B. 자식 오브젝트 배치 (&lt;code&gt;Batch&lt;/code&gt; 함수)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Batch&lt;/code&gt; 함수는 &lt;code&gt;count&lt;/code&gt; 변수에 지정된 개수만큼 자식 오브젝트를 부모 주변에 원형으로 배치합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 오브젝트 생성 및 재사용&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;if (i &amp;lt; transform.childCount)
{
    bullet = transform.GetChild(i); // 기존 자식 오브젝트 재사용
}
else
{
    bullet = Instantiate(prefab).transform; // 새 자식 오브젝트 생성
    bullet.parent = transform; // 현재 오브젝트를 부모로 설정
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;성능 최적화:&lt;/b&gt; 이미 생성된 오브젝트가 있다면 &lt;b&gt;재사용&lt;/b&gt;하고, 부족할 경우에만 새로 &lt;b&gt;생성&lt;/b&gt;합니다. 이는 무기를 업데이트할 때 불필요한 메모리 할당을 줄여줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 원형 위치 계산&lt;/h4&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;// 1. 회전 계산: 'count' 개수만큼 360도를 균등하게 나눕니다.
Vector3 rotVec = Vector3.forward * 360 * i / count;

// 2. 오브젝트를 계산된 각도만큼 회전시킵니다.
bullet.Rotate(rotVec);

// 3. 오브젝트의 '위' 방향(local up)으로 1.5f만큼 이동시킵니다.
bullet.Translate(bullet.up * 1.5f, Space.World); &lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 줄이 핵심입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저 전체 360도를 &lt;code&gt;count&lt;/code&gt; 개수로 나누어 각 오브젝트가 가져야 할 &lt;b&gt;균등한 각도&lt;/b&gt;를 계산합니다.&lt;/li&gt;
&lt;li&gt;오브젝트를 해당 각도만큼 회전시킨 후, &lt;b&gt;오브젝트의 현재 '위' 방향(local up)&lt;/b&gt;으로 특정 거리(예: 1.5f)만큼 이동시킵니다.&lt;/li&gt;
&lt;li&gt;결과적으로, 모든 자식 오브젝트는 부모 오브젝트의 중심에서 1.5f 떨어진 원형 궤도에 배치됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 유니티 적용 방법&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;새 스크립트 생성:&lt;/b&gt; &lt;code&gt;WeaponSample.cs&lt;/code&gt; 이름으로 C# 스크립트를 생성하고 위 코드를 붙여넣습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;부모 오브젝트 준비:&lt;/b&gt; 빈 게임 오브젝트(Empty Object)를 하나 만들고 이름을 &lt;code&gt;WeaponParent&lt;/code&gt; 등으로 지정합니다. 이 오브젝트에 &lt;code&gt;WeaponSample.cs&lt;/code&gt;를 부착합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자식 프리헵 준비:&lt;/b&gt; 회전 무기로 사용할 3D 모델이나 2D 스프라이트를 프리헵으로 만들어 준비합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;변수 설정:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;WeaponParent&lt;/code&gt; 오브젝트를 선택하고, Inspector 창에서 &lt;code&gt;Prefab&lt;/code&gt; 슬롯에 준비한 &lt;b&gt;자식 프리헵&lt;/b&gt;을 드래그하여 넣어줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Count&lt;/code&gt; (예: &lt;b&gt;5&lt;/b&gt;)와 &lt;code&gt;Speed&lt;/code&gt; (예: &lt;b&gt;100&lt;/b&gt;) 값을 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;Batch()&lt;/code&gt; 함수 호출:&lt;/b&gt; 게임 시작 시 또는 무기의 개수가 바뀔 때마다 &lt;b&gt;다른 스크립트&lt;/b&gt;에서 &lt;code&gt;WeaponParent&lt;/code&gt; 오브젝트의 &lt;code&gt;Batch()&lt;/code&gt; 함수를 호출해 주면 원형 배치가 완료됩니다.&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/132</guid>
      <comments>https://dino-dev.tistory.com/132#entry132comment</comments>
      <pubDate>Fri, 12 Dec 2025 18:13:20 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-52 길리 트라왕안 - Meno slope</title>
      <link>https://dino-dev.tistory.com/131</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2025.11.11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 인도네시아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 길리 트라왕안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: Meno slope&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 선샤인 다이브(&lt;a href=&quot;https://www.sunshinedive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.sunshinedive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 61분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 10:56&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 30m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 49분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 11:45&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 40bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 30도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2mm) 반팔 반바지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1048.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b59YBX/dJMcaaDD2gp/NdoNYn4MlhCYK3Tljtmnek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b59YBX/dJMcaaDD2gp/NdoNYn4MlhCYK3Tljtmnek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b59YBX/dJMcaaDD2gp/NdoNYn4MlhCYK3Tljtmnek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb59YBX%2FdJMcaaDD2gp%2FNdoNYn4MlhCYK3Tljtmnek%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1048.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0485.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/extqcH/dJMcaiaBJsI/bk6DChv2HSxGjXjOIaIlCK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/extqcH/dJMcaiaBJsI/bk6DChv2HSxGjXjOIaIlCK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/extqcH/dJMcaiaBJsI/bk6DChv2HSxGjXjOIaIlCK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FextqcH%2FdJMcaiaBJsI%2Fbk6DChv2HSxGjXjOIaIlCK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0485.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0489.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8T6qD/dJMb995N9Vr/b1WwPwNTdLv7kHNEzaL3e1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8T6qD/dJMb995N9Vr/b1WwPwNTdLv7kHNEzaL3e1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8T6qD/dJMb995N9Vr/b1WwPwNTdLv7kHNEzaL3e1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8T6qD%2FdJMb995N9Vr%2Fb1WwPwNTdLv7kHNEzaL3e1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0489.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0490.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/taBDt/dJMcain8Wk8/yLSyXAwkluchFOm4XJpjt0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/taBDt/dJMcain8Wk8/yLSyXAwkluchFOm4XJpjt0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/taBDt/dJMcain8Wk8/yLSyXAwkluchFOm4XJpjt0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtaBDt%2FdJMcain8Wk8%2FyLSyXAwkluchFOm4XJpjt0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0490.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0506.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rmRwl/dJMcaiaBJuK/BOaQKRkHoN2c3LH7WFKfGk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rmRwl/dJMcaiaBJuK/BOaQKRkHoN2c3LH7WFKfGk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rmRwl/dJMcaiaBJuK/BOaQKRkHoN2c3LH7WFKfGk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrmRwl%2FdJMcaiaBJuK%2FBOaQKRkHoN2c3LH7WFKfGk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0506.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/131</guid>
      <comments>https://dino-dev.tistory.com/131#entry131comment</comments>
      <pubDate>Fri, 14 Nov 2025 09:59:35 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-51 길리 트라왕안 - Deep Turbo</title>
      <link>https://dino-dev.tistory.com/130</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2025.11.11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 인도네시아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 길리 트라왕안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: Deep Turbo&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 선샤인 다이브(&lt;a href=&quot;https://www.sunshinedive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.sunshinedive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 09:22&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 28m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 33분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 09:55&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 30bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2mm) 반팔 반바지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1047.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVayXg/dJMcaaDD2br/v2IGbz43CK0Buf3Oi5XrD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVayXg/dJMcaaDD2br/v2IGbz43CK0Buf3Oi5XrD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVayXg/dJMcaaDD2br/v2IGbz43CK0Buf3Oi5XrD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVayXg%2FdJMcaaDD2br%2Fv2IGbz43CK0Buf3Oi5XrD0%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1047.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0460.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfrg83/dJMcabCyix9/mB2Ed7h5Rhr9fVXkHoaZW1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfrg83/dJMcabCyix9/mB2Ed7h5Rhr9fVXkHoaZW1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfrg83/dJMcabCyix9/mB2Ed7h5Rhr9fVXkHoaZW1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbfrg83%2FdJMcabCyix9%2FmB2Ed7h5Rhr9fVXkHoaZW1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0460.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0462.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v5036/dJMcafrpMDM/MExBxyI4ercRqukUnZefVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v5036/dJMcafrpMDM/MExBxyI4ercRqukUnZefVK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v5036/dJMcafrpMDM/MExBxyI4ercRqukUnZefVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv5036%2FdJMcafrpMDM%2FMExBxyI4ercRqukUnZefVK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0462.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0464.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beyCf3/dJMcabo07FN/Rq7VALghfxMC4waxag3Kqk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beyCf3/dJMcabo07FN/Rq7VALghfxMC4waxag3Kqk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beyCf3/dJMcabo07FN/Rq7VALghfxMC4waxag3Kqk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeyCf3%2FdJMcabo07FN%2FRq7VALghfxMC4waxag3Kqk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0464.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0466.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brxWQG/dJMcaaDD2ct/kOb71QS0JZJKWCWeCMudY0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brxWQG/dJMcaaDD2ct/kOb71QS0JZJKWCWeCMudY0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brxWQG/dJMcaaDD2ct/kOb71QS0JZJKWCWeCMudY0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrxWQG%2FdJMcaaDD2ct%2FkOb71QS0JZJKWCWeCMudY0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0466.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0471.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bw0gkK/dJMcacVLrVo/Au25skYKyNwttNg9kIZsK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bw0gkK/dJMcacVLrVo/Au25skYKyNwttNg9kIZsK1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bw0gkK/dJMcacVLrVo/Au25skYKyNwttNg9kIZsK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbw0gkK%2FdJMcacVLrVo%2FAu25skYKyNwttNg9kIZsK1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0471.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0473.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BdKYS/dJMcafrpMEc/JQPSUoSohKRw3eoQOohQJk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BdKYS/dJMcafrpMEc/JQPSUoSohKRw3eoQOohQJk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BdKYS/dJMcafrpMEc/JQPSUoSohKRw3eoQOohQJk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBdKYS%2FdJMcafrpMEc%2FJQPSUoSohKRw3eoQOohQJk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0473.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0477.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cT0gQc/dJMcafrpMEm/udK8s9huyoO1Wmzr3NTO10/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cT0gQc/dJMcafrpMEm/udK8s9huyoO1Wmzr3NTO10/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cT0gQc/dJMcafrpMEm/udK8s9huyoO1Wmzr3NTO10/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcT0gQc%2FdJMcafrpMEm%2FudK8s9huyoO1Wmzr3NTO10%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0477.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/130</guid>
      <comments>https://dino-dev.tistory.com/130#entry130comment</comments>
      <pubDate>Fri, 14 Nov 2025 09:55:29 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-50 길리 트라왕안 - Turtle Heaven</title>
      <link>https://dino-dev.tistory.com/129</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2025.11.11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 인도네시아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 길리 트라왕안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: Turtle Heaven&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 선샤인 다이브(&lt;a href=&quot;https://www.sunshinedive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.sunshinedive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 52분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 10:57&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 23m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 48분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 11:45&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 50bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 30도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2mm) 반팔 반바지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1046.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwhe0n/dJMb995N86T/8W8b5EnjZHWECT0BgizH50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwhe0n/dJMb995N86T/8W8b5EnjZHWECT0BgizH50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwhe0n/dJMb995N86T/8W8b5EnjZHWECT0BgizH50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwhe0n%2FdJMb995N86T%2F8W8b5EnjZHWECT0BgizH50%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1046.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0433.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBP99M/dJMcaaKpzLM/1We3jbQmIO22guV2q8vro1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBP99M/dJMcaaKpzLM/1We3jbQmIO22guV2q8vro1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBP99M/dJMcaaKpzLM/1We3jbQmIO22guV2q8vro1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBP99M%2FdJMcaaKpzLM%2F1We3jbQmIO22guV2q8vro1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0433.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0438.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MGuye/dJMcacg9Sqs/7EYZNrOk0YlNlk9YTw57MK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MGuye/dJMcacg9Sqs/7EYZNrOk0YlNlk9YTw57MK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MGuye/dJMcacg9Sqs/7EYZNrOk0YlNlk9YTw57MK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMGuye%2FdJMcacg9Sqs%2F7EYZNrOk0YlNlk9YTw57MK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0438.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0439.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mHdqO/dJMcabCyhX6/KS6cu1oxSlheGYC0xwdIYK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mHdqO/dJMcabCyhX6/KS6cu1oxSlheGYC0xwdIYK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mHdqO/dJMcabCyhX6/KS6cu1oxSlheGYC0xwdIYK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmHdqO%2FdJMcabCyhX6%2FKS6cu1oxSlheGYC0xwdIYK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0439.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0442.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rQ4C9/dJMcaf51AtR/xTmkK1Y11Y9ODb0MWfD7r1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rQ4C9/dJMcaf51AtR/xTmkK1Y11Y9ODb0MWfD7r1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rQ4C9/dJMcaf51AtR/xTmkK1Y11Y9ODb0MWfD7r1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrQ4C9%2FdJMcaf51AtR%2FxTmkK1Y11Y9ODb0MWfD7r1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0442.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0453.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/00V8X/dJMcafSuqoH/HykbVrsKABKfhGx644Rwq1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/00V8X/dJMcafSuqoH/HykbVrsKABKfhGx644Rwq1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/00V8X/dJMcafSuqoH/HykbVrsKABKfhGx644Rwq1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F00V8X%2FdJMcafSuqoH%2FHykbVrsKABKfhGx644Rwq1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0453.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0455.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dswH4l/dJMcaaKpzOC/CTKyTvIiok9jVYqxgkPx31/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dswH4l/dJMcaaKpzOC/CTKyTvIiok9jVYqxgkPx31/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dswH4l/dJMcaaKpzOC/CTKyTvIiok9jVYqxgkPx31/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdswH4l%2FdJMcaaKpzOC%2FCTKyTvIiok9jVYqxgkPx31%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0455.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/129</guid>
      <comments>https://dino-dev.tistory.com/129#entry129comment</comments>
      <pubDate>Fri, 14 Nov 2025 09:20:00 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-49 길리 트라왕안 - Shark point</title>
      <link>https://dino-dev.tistory.com/128</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2025.11.11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 인도네시아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 길리 트라왕안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: Shark&amp;nbsp;point&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 선샤인 다이브(&lt;a href=&quot;https://www.sunshinedive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.sunshinedive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 09:29&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 27m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 36분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 10:05&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 30bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2mm) 반팔 반바지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_1045.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tUJPD/dJMcabJj0Yx/ENItxZ8rRktbFGFXhZTtf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tUJPD/dJMcabJj0Yx/ENItxZ8rRktbFGFXhZTtf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tUJPD/dJMcabJj0Yx/ENItxZ8rRktbFGFXhZTtf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtUJPD%2FdJMcabJj0Yx%2FENItxZ8rRktbFGFXhZTtf1%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_1045.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0394.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TBRaX/dJMcacg9SiB/lyj5bmbFLJ3ojuwSIsl7hk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TBRaX/dJMcacg9SiB/lyj5bmbFLJ3ojuwSIsl7hk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TBRaX/dJMcacg9SiB/lyj5bmbFLJ3ojuwSIsl7hk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTBRaX%2FdJMcacg9SiB%2Flyj5bmbFLJ3ojuwSIsl7hk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0394.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0404.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmdSp3/dJMcahbHsNx/IXuV9jKpnRzWh2EcLAcdVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmdSp3/dJMcahbHsNx/IXuV9jKpnRzWh2EcLAcdVK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmdSp3/dJMcahbHsNx/IXuV9jKpnRzWh2EcLAcdVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmdSp3%2FdJMcahbHsNx%2FIXuV9jKpnRzWh2EcLAcdVK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0404.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0407.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bI3BiF/dJMcaihnmqc/DK4yO78wXG6TbAIyg1YDVK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bI3BiF/dJMcaihnmqc/DK4yO78wXG6TbAIyg1YDVK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bI3BiF/dJMcaihnmqc/DK4yO78wXG6TbAIyg1YDVK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbI3BiF%2FdJMcaihnmqc%2FDK4yO78wXG6TbAIyg1YDVK%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;4872&quot; height=&quot;5568&quot; data-filename=&quot;GOPR0407.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0413.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHjzur/dJMcabJj0Yr/YmkkhvZuWKPRWnmXoi0RH1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHjzur/dJMcabJj0Yr/YmkkhvZuWKPRWnmXoi0RH1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHjzur/dJMcabJj0Yr/YmkkhvZuWKPRWnmXoi0RH1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHjzur%2FdJMcabJj0Yr%2FYmkkhvZuWKPRWnmXoi0RH1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0413.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/128</guid>
      <comments>https://dino-dev.tistory.com/128#entry128comment</comments>
      <pubDate>Fri, 14 Nov 2025 09:15:04 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-48 길리 트라왕안 - SUN SET</title>
      <link>https://dino-dev.tistory.com/127</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2025.11.11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 인도네시아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 길리 트라왕안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: SUN SET&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 선샤인 다이브(&lt;a href=&quot;https://www.sunshinedive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.sunshinedive.com/&lt;/a&gt;)&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;시야: 좋음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 198분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 14:32&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 22m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 46분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 15:18&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 30bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2mm) 반팔 반바지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0930.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QGu3D/dJMcafStqND/K9kA6gKFwyiSm4YkyHIAmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QGu3D/dJMcafStqND/K9kA6gKFwyiSm4YkyHIAmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QGu3D/dJMcafStqND/K9kA6gKFwyiSm4YkyHIAmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQGu3D%2FdJMcafStqND%2FK9kA6gKFwyiSm4YkyHIAmK%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0930.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0371.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biQSHj/dJMcag4UQpv/duNCsDZYYDZDuLsSVBkvP0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biQSHj/dJMcag4UQpv/duNCsDZYYDZDuLsSVBkvP0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biQSHj/dJMcag4UQpv/duNCsDZYYDZDuLsSVBkvP0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiQSHj%2FdJMcag4UQpv%2FduNCsDZYYDZDuLsSVBkvP0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0371.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0376.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pU6qe/dJMcaacyEAa/Aq7F4ZkKGErkFOMKknLwM1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pU6qe/dJMcaacyEAa/Aq7F4ZkKGErkFOMKknLwM1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pU6qe/dJMcaacyEAa/Aq7F4ZkKGErkFOMKknLwM1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpU6qe%2FdJMcaacyEAa%2FAq7F4ZkKGErkFOMKknLwM1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0376.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/127</guid>
      <comments>https://dino-dev.tistory.com/127#entry127comment</comments>
      <pubDate>Tue, 11 Nov 2025 22:55:08 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-47 길리 트라왕안 - Bounty Wreck</title>
      <link>https://dino-dev.tistory.com/126</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2025.11.11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 인도네시아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 길리 트라왕안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: Bounty&amp;nbsp;Wreck&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 선샤인 다이브(&lt;a href=&quot;https://www.sunshinedive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.sunshinedive.com/&lt;/a&gt;)&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;시야: 좋음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 46분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 10:30&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 22m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 45분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 11:15&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 50bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 30도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2mm) 반팔 반바지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0929.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VQQTl/dJMcacg8SCp/UIu1K9asAT0hQPJoK1Kvu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VQQTl/dJMcacg8SCp/UIu1K9asAT0hQPJoK1Kvu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VQQTl/dJMcacg8SCp/UIu1K9asAT0hQPJoK1Kvu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVQQTl%2FdJMcacg8SCp%2FUIu1K9asAT0hQPJoK1Kvu0%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0929.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0324.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhEYRu/dJMb99Y1uzm/dAZvKg2YKj9iStSvMwzjK1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhEYRu/dJMb99Y1uzm/dAZvKg2YKj9iStSvMwzjK1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhEYRu/dJMb99Y1uzm/dAZvKg2YKj9iStSvMwzjK1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhEYRu%2FdJMb99Y1uzm%2FdAZvKg2YKj9iStSvMwzjK1%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;4872&quot; height=&quot;5568&quot; data-filename=&quot;GOPR0324.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0330.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwDI9m/dJMcadNS8Ob/qnWJrptL78T1wBi6TQoAD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwDI9m/dJMcadNS8Ob/qnWJrptL78T1wBi6TQoAD1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwDI9m/dJMcadNS8Ob/qnWJrptL78T1wBi6TQoAD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwDI9m%2FdJMcadNS8Ob%2FqnWJrptL78T1wBi6TQoAD1%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;4872&quot; height=&quot;5568&quot; data-filename=&quot;GOPR0330.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0336.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpghEL/dJMcahCKNFx/lLzfk4KBM0U3jWk4wwOqy1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpghEL/dJMcahCKNFx/lLzfk4KBM0U3jWk4wwOqy1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpghEL/dJMcahCKNFx/lLzfk4KBM0U3jWk4wwOqy1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpghEL%2FdJMcahCKNFx%2FlLzfk4KBM0U3jWk4wwOqy1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0336.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0344.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YnkPK/dJMb99Y1uzV/QBLYQ09KaKcslkraNVM24k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YnkPK/dJMb99Y1uzV/QBLYQ09KaKcslkraNVM24k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YnkPK/dJMb99Y1uzV/QBLYQ09KaKcslkraNVM24k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYnkPK%2FdJMb99Y1uzV%2FQBLYQ09KaKcslkraNVM24k%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;4872&quot; height=&quot;5568&quot; data-filename=&quot;GOPR0344.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0352.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blYrcu/dJMb995M8Ow/KQLwGV9cIdrKsj17oIWa40/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blYrcu/dJMb995M8Ow/KQLwGV9cIdrKsj17oIWa40/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blYrcu/dJMb995M8Ow/KQLwGV9cIdrKsj17oIWa40/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblYrcu%2FdJMb995M8Ow%2FKQLwGV9cIdrKsj17oIWa40%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;4872&quot; height=&quot;5568&quot; data-filename=&quot;GOPR0352.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0354.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pCQqJ/dJMcagX9jmP/VCf16t9bzC66FI9D69s340/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pCQqJ/dJMcagX9jmP/VCf16t9bzC66FI9D69s340/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pCQqJ/dJMcagX9jmP/VCf16t9bzC66FI9D69s340/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpCQqJ%2FdJMcagX9jmP%2FVCf16t9bzC66FI9D69s340%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;4872&quot; height=&quot;5568&quot; data-filename=&quot;GOPR0354.JPG&quot; data-origin-width=&quot;4872&quot; data-origin-height=&quot;5568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0365.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/M9FQ4/dJMcafEV85k/7OsjYoV4MX3Q1fkvK88cu0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/M9FQ4/dJMcafEV85k/7OsjYoV4MX3Q1fkvK88cu0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/M9FQ4/dJMcafEV85k/7OsjYoV4MX3Q1fkvK88cu0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FM9FQ4%2FdJMcafEV85k%2F7OsjYoV4MX3Q1fkvK88cu0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0365.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/126</guid>
      <comments>https://dino-dev.tistory.com/126#entry126comment</comments>
      <pubDate>Tue, 11 Nov 2025 22:51:31 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-46 길리 트라왕안 - Halik</title>
      <link>https://dino-dev.tistory.com/125</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;날짜: 2025.11.11&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나라: 인도네시아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치: 길리 트라왕안&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 포인트: Halik&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이빙 센터: 선샤인 다이브(&lt;a href=&quot;https://www.sunshinedive.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.sunshinedive.com/&lt;/a&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;/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;h2 data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;휴식 시간: 분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 시간: 09:00&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시작 잔압: 200bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최대 수심: 21m&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다이브 시간: 44분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 시간: 09:44&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종료 잔압: 50bar&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 재질: 알루미늄&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탱크 종류: Air&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수온: 29도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;슈트: &lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(2mm) 반팔 반바지&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0928.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxWeAf/dJMcaap5MLN/F8K45zhLij6Odmfyn7bgi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxWeAf/dJMcaap5MLN/F8K45zhLij6Odmfyn7bgi0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxWeAf/dJMcaap5MLN/F8K45zhLij6Odmfyn7bgi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxWeAf%2FdJMcaap5MLN%2FF8K45zhLij6Odmfyn7bgi0%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0928.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0285.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAUc5W/dJMcaaKozSE/CKfN7o5Hjm5F4TV4zTJ5qK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAUc5W/dJMcaaKozSE/CKfN7o5Hjm5F4TV4zTJ5qK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAUc5W/dJMcaaKozSE/CKfN7o5Hjm5F4TV4zTJ5qK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAUc5W%2FdJMcaaKozSE%2FCKfN7o5Hjm5F4TV4zTJ5qK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0285.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0287.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NjdUQ/dJMcaaKozSA/W7P7gOphIhVZbbOopmMxaK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NjdUQ/dJMcaaKozSA/W7P7gOphIhVZbbOopmMxaK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NjdUQ/dJMcaaKozSA/W7P7gOphIhVZbbOopmMxaK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNjdUQ%2FdJMcaaKozSA%2FW7P7gOphIhVZbbOopmMxaK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0287.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0291.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEhdtf/dJMcahQhY1D/g2Izk8FksjAa08vUHwNkpk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEhdtf/dJMcahQhY1D/g2Izk8FksjAa08vUHwNkpk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEhdtf/dJMcahQhY1D/g2Izk8FksjAa08vUHwNkpk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEhdtf%2FdJMcahQhY1D%2Fg2Izk8FksjAa08vUHwNkpk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0291.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0296.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k8VVn/dJMcahvY9La/3klIrukVKdPuBtNvimNYMk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k8VVn/dJMcahvY9La/3klIrukVKdPuBtNvimNYMk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k8VVn/dJMcahvY9La/3klIrukVKdPuBtNvimNYMk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk8VVn%2FdJMcahvY9La%2F3klIrukVKdPuBtNvimNYMk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0296.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0298.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbbEV6/dJMcahQhY1A/49r6YuZ2gWDyMjaVgACMpk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbbEV6/dJMcahQhY1A/49r6YuZ2gWDyMjaVgACMpk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbbEV6/dJMcahQhY1A/49r6YuZ2gWDyMjaVgACMpk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbbEV6%2FdJMcahQhY1A%2F49r6YuZ2gWDyMjaVgACMpk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0298.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0300.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxvpRk/dJMcahvY9K0/1PH1Ou1pLA9dPt23NOmEzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxvpRk/dJMcahvY9K0/1PH1Ou1pLA9dPt23NOmEzK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxvpRk/dJMcahvY9K0/1PH1Ou1pLA9dPt23NOmEzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxvpRk%2FdJMcahvY9K0%2F1PH1Ou1pLA9dPt23NOmEzK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0300.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0303.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsc8Qr/dJMcagw4TjU/vkKawoyPwH3eUpRDNwEzsK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsc8Qr/dJMcagw4TjU/vkKawoyPwH3eUpRDNwEzsK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsc8Qr/dJMcagw4TjU/vkKawoyPwH3eUpRDNwEzsK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbsc8Qr%2FdJMcagw4TjU%2FvkKawoyPwH3eUpRDNwEzsK%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0303.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0304.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9YwTU/dJMcahvY9K8/8FwWxwGQVDk7KNHJk6hKi1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9YwTU/dJMcahvY9K8/8FwWxwGQVDk7KNHJk6hKi1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9YwTU/dJMcahvY9K8/8FwWxwGQVDk7KNHJk6hKi1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9YwTU%2FdJMcahvY9K8%2F8FwWxwGQVDk7KNHJk6hKi1%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0304.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0305.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT6VEu/dJMcahvY9K2/t66iVYzvWl1xMCEKVSlkZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT6VEu/dJMcahvY9K2/t66iVYzvWl1xMCEKVSlkZ0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT6VEu/dJMcahvY9K2/t66iVYzvWl1xMCEKVSlkZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT6VEu%2FdJMcahvY9K2%2Ft66iVYzvWl1xMCEKVSlkZ0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0305.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0307.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dK6rMs/dJMcahvY9K4/Ur9JDxtr54eLBKwQbIqzz0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dK6rMs/dJMcahvY9K4/Ur9JDxtr54eLBKwQbIqzz0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dK6rMs/dJMcahvY9K4/Ur9JDxtr54eLBKwQbIqzz0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdK6rMs%2FdJMcahvY9K4%2FUr9JDxtr54eLBKwQbIqzz0%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0307.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;GOPR0315.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XXreT/dJMcagw4TjC/ak4zNjUcFCuHEmmsJ9SRgk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XXreT/dJMcagw4TjC/ak4zNjUcFCuHEmmsJ9SRgk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XXreT/dJMcagw4TjC/ak4zNjUcFCuHEmmsJ9SRgk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXXreT%2FdJMcagw4TjC%2Fak4zNjUcFCuHEmmsJ9SRgk%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;5568&quot; height=&quot;4872&quot; data-filename=&quot;GOPR0315.JPG&quot; data-origin-width=&quot;5568&quot; data-origin-height=&quot;4872&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/125</guid>
      <comments>https://dino-dev.tistory.com/125#entry125comment</comments>
      <pubDate>Tue, 11 Nov 2025 22:43:58 +0900</pubDate>
    </item>
    <item>
      <title>Unity 내장 오브젝트 풀(Object Pool) 사용하기</title>
      <link>https://dino-dev.tistory.com/124</link>
      <description>&lt;p data-end=&quot;365&quot; data-start=&quot;204&quot; data-ke-size=&quot;size16&quot;&gt;게임을 만들다 보면 총알, 이펙트, 몬스터처럼 &lt;b&gt;짧은 시간 동안 반복적으로 생성되고 파괴되는 오브젝트&lt;/b&gt;를 자주 다룬다.&lt;br /&gt;이런 경우 단순히 Instantiate()와 Destroy()를 반복하면 &lt;b&gt;성능 저하와 GC(Garbage Collection) 부하&lt;/b&gt;가 발생한다.&lt;/p&gt;
&lt;p data-end=&quot;422&quot; data-start=&quot;367&quot; data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위한 대표적인 패턴이 바로 &lt;b&gt;오브젝트 풀(Object Pool)&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-end=&quot;527&quot; data-start=&quot;424&quot; data-ke-size=&quot;size16&quot;&gt;Unity 2021 버전 이후부터는 UnityEngine.Pool 네임스페이스에&lt;br /&gt;&lt;b&gt;내장 풀 시스템&lt;/b&gt;이 추가되어, 직접 구현하지 않아도 효율적인 풀을 쉽게 사용할 수 있다.&lt;/p&gt;
&lt;hr data-end=&quot;532&quot; data-start=&quot;529&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;563&quot; data-start=&quot;534&quot; data-ke-size=&quot;size26&quot;&gt;  오브젝트 풀(Object Pool) 이란?&lt;/h2&gt;
&lt;blockquote data-end=&quot;608&quot; data-start=&quot;565&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;608&quot; data-start=&quot;567&quot; data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;필요할 때마다 새로 만들지 말고, 미리 만들어둔 오브젝트를 재사용하자.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;691&quot; data-start=&quot;610&quot; data-ke-size=&quot;size16&quot;&gt;오브젝트 풀은 자주 사용되는 오브젝트를 &lt;b&gt;미리 생성&lt;/b&gt;해두고 필요할 때 꺼내서 쓰고, 다 쓰면 &lt;b&gt;비활성화 후 다시 보관&lt;/b&gt;하는 구조다.&lt;/p&gt;
&lt;p data-end=&quot;737&quot; data-start=&quot;693&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어, 슈팅 게임에서 총알을 Instantiate()로 계속 만들면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;799&quot; data-start=&quot;738&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;753&quot; data-start=&quot;738&quot;&gt;생성 시 메모리 할당&lt;/li&gt;
&lt;li data-end=&quot;799&quot; data-start=&quot;754&quot;&gt;파괴 시 GC 수집&lt;br /&gt;이 반복되며 &lt;b&gt;프레임 드랍&lt;/b&gt;이 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;858&quot; data-start=&quot;801&quot; data-ke-size=&quot;size16&quot;&gt;하지만 오브젝트 풀을 사용하면 한 번 만들어진 총알을 재활용하므로 &lt;b&gt;성능이 일정하게 유지&lt;/b&gt;된다.&lt;/p&gt;
&lt;hr data-end=&quot;863&quot; data-start=&quot;860&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;888&quot; data-start=&quot;865&quot; data-ke-size=&quot;size26&quot;&gt;  Unity 내장 풀 시스템 소개&lt;/h2&gt;
&lt;p data-end=&quot;967&quot; data-start=&quot;890&quot; data-ke-size=&quot;size16&quot;&gt;Unity는 UnityEngine.Pool 네임스페이스 아래에 &lt;b&gt;제너릭 기반의 ObjectPool&amp;lt;T&amp;gt; 클래스&lt;/b&gt;를 제공한다.&lt;/p&gt;
&lt;p data-end=&quot;1031&quot; data-start=&quot;969&quot; data-ke-size=&quot;size16&quot;&gt;이 클래스를 사용하면 오브젝트 생성, 회수, 삭제 등의 과정을 &lt;b&gt;콜백 기반으로 손쉽게 관리할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;hr data-end=&quot;1036&quot; data-start=&quot;1033&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1052&quot; data-start=&quot;1038&quot; data-ke-size=&quot;size26&quot;&gt;  기본 사용 예시&lt;/h2&gt;
&lt;p data-end=&quot;1114&quot; data-start=&quot;1054&quot; data-ke-size=&quot;size16&quot;&gt;아래는 ObjectPool&amp;lt;GameObject&amp;gt;를 이용해 &lt;b&gt;Enemy&lt;/b&gt;를 관리하는 간단한 예시다.&lt;/p&gt;
&lt;pre id=&quot;code_1760281584516&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.Pool;

public class EnemySpawner : MonoBehaviour
{
    [SerializeField] private GameObject enemyPrefab;
    private ObjectPool&amp;lt;GameObject&amp;gt; enemyPool;

    [SerializeField] private int initialPoolSize = 10;
    [SerializeField] private int maxPoolSize = 50;

    void Awake()
    {
        // 오브젝트 풀 초기화
        enemyPool = new ObjectPool&amp;lt;GameObject&amp;gt;(
            createFunc: CreateEnemy,
            actionOnGet: OnTakeFromPool,
            actionOnRelease: OnReturnedToPool,
            actionOnDestroy: OnDestroyPoolObject,
            collectionCheck: false,
            defaultCapacity: initialPoolSize,
            maxSize: maxPoolSize
        );
    }

    // Enemy 오브젝트 생성
    private GameObject CreateEnemy()
    {
        var enemy = Instantiate(enemyPrefab);
        enemy.GetComponent&amp;lt;Enemy&amp;gt;().SetPool(enemyPool);
        return enemy;
    }

    // 풀에서 꺼낼 때 호출
    private void OnTakeFromPool(GameObject enemy)
    {
        enemy.SetActive(true);
    }

    // 풀에 반환될 때 호출
    private void OnReturnedToPool(GameObject enemy)
    {
        enemy.SetActive(false);
    }

    // 풀 크기를 초과해 제거될 때 호출
    private void OnDestroyPoolObject(GameObject enemy)
    {
        Destroy(enemy);
    }

    // Enemy 스폰 및 초기화
    public void SpawnEnemy(Vector3 position)
    {
        var enemy = enemyPool.Get();
        enemy.GetComponent&amp;lt;Enemy&amp;gt;().Init(position);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2277&quot; data-start=&quot;2274&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2298&quot; data-start=&quot;2279&quot; data-ke-size=&quot;size26&quot;&gt;  Enemy 스크립트 예시&lt;/h2&gt;
&lt;p data-end=&quot;2331&quot; data-start=&quot;2300&quot; data-ke-size=&quot;size16&quot;&gt;Enemy가 사라질 때 스스로 풀로 돌아가도록 구성한다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1760281606325&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;
using UnityEngine.Pool;

public class Enemy : MonoBehaviour
{
    private ObjectPool&amp;lt;GameObject&amp;gt; pool;
    private int maxHp = 100;
    private int currentHp;

    public void SetPool(ObjectPool&amp;lt;GameObject&amp;gt; objectPool)
    {
        pool = objectPool;
    }

    // 위치와 상태를 초기화하는 함수
    public void Init(Vector3 spawnPosition)
    {
        currentHp = maxHp;
        transform.position = spawnPosition;

        // 추가 초기화: 애니메이션, AI 상태 등
        // Rigidbody 속도 초기화 필요 시 여기서
        var rb = GetComponent&amp;lt;Rigidbody&amp;gt;();
        if (rb != null) rb.velocity = Vector3.zero;
    }

    public void TakeDamage(int damage)
    {
        currentHp -= damage;
        if (currentHp &amp;lt;= 0)
        {
            Die();
        }
    }

    private void Die()
    {
        pool.Release(gameObject);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2737&quot; data-start=&quot;2734&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2755&quot; data-start=&quot;2739&quot; data-ke-size=&quot;size26&quot;&gt;  주요 파라미터 설명&lt;/h2&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3155&quot; data-start=&quot;2757&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;파라미터&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2835&quot; data-start=&quot;2792&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2809&quot; data-start=&quot;2792&quot;&gt;&lt;b&gt;createFunc&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;2835&quot; data-start=&quot;2809&quot; data-col-size=&quot;sm&quot;&gt;오브젝트를 새로 생성할 때 호출되는 함수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2898&quot; data-start=&quot;2836&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2854&quot; data-start=&quot;2836&quot;&gt;&lt;b&gt;actionOnGet&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;2898&quot; data-start=&quot;2854&quot; data-col-size=&quot;sm&quot;&gt;풀에서 오브젝트를 꺼낼 때 호출 (예: SetActive(true))&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;2966&quot; data-start=&quot;2899&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2921&quot; data-start=&quot;2899&quot;&gt;&lt;b&gt;actionOnRelease&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;2966&quot; data-start=&quot;2921&quot; data-col-size=&quot;sm&quot;&gt;오브젝트를 다시 반환할 때 호출 (예: SetActive(false))&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3011&quot; data-start=&quot;2967&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;2989&quot; data-start=&quot;2967&quot;&gt;&lt;b&gt;actionOnDestroy&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;3011&quot; data-start=&quot;2989&quot; data-col-size=&quot;sm&quot;&gt;풀 크기를 초과해 제거될 때 호출&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3066&quot; data-start=&quot;3012&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3034&quot; data-start=&quot;3012&quot;&gt;&lt;b&gt;collectionCheck&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;3066&quot; data-start=&quot;3034&quot; data-col-size=&quot;sm&quot;&gt;중복 반환 감지 여부 (디버깅용, 성능 저하 가능)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3101&quot; data-start=&quot;3067&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3089&quot; data-start=&quot;3067&quot;&gt;&lt;b&gt;defaultCapacity&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;3101&quot; data-start=&quot;3089&quot; data-col-size=&quot;sm&quot;&gt;초기 생성 개수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3155&quot; data-start=&quot;3102&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3116&quot; data-start=&quot;3102&quot;&gt;&lt;b&gt;maxSize&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;3155&quot; data-start=&quot;3116&quot; data-col-size=&quot;sm&quot;&gt;최대 풀 크기 (초과 시 actionOnDestroy 호출)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3160&quot; data-start=&quot;3157&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3170&quot; data-start=&quot;3162&quot; data-ke-size=&quot;size26&quot;&gt;  장점&lt;/h2&gt;
&lt;p data-end=&quot;3227&quot; data-start=&quot;3172&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;직접 구현 불필요&lt;/b&gt;&lt;br /&gt;기존에 직접 Queue나 Stack으로 풀을 만들 필요가 없다.&lt;/p&gt;
&lt;p data-end=&quot;3287&quot; data-start=&quot;3229&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;일관된 인터페이스&lt;/b&gt;&lt;br /&gt;Get(), Release() 메서드만으로 간단히 제어 가능.&lt;/p&gt;
&lt;p data-end=&quot;3356&quot; data-start=&quot;3289&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;성능 최적화&lt;/b&gt;&lt;br /&gt;내부적으로 C++ 레벨에서 관리되어&lt;br /&gt;직접 구현한 C# 풀보다 메모리 관리가 효율적이다.&lt;/p&gt;
&lt;p data-end=&quot;3414&quot; data-start=&quot;3358&quot; data-ke-size=&quot;size16&quot;&gt;✅ &lt;b&gt;자동 메모리 관리&lt;/b&gt;&lt;br /&gt;최대 크기 초과 시 자동으로 파괴(OnDestroy)가 호출된다.&lt;/p&gt;
&lt;hr data-end=&quot;3419&quot; data-start=&quot;3416&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3433&quot; data-start=&quot;3421&quot; data-ke-size=&quot;size26&quot;&gt;⚡ 실무 적용 팁&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;3752&quot; data-start=&quot;3435&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;3532&quot; data-start=&quot;3435&quot;&gt;&lt;b&gt;공통 풀 관리 클래스 작성하기&lt;/b&gt;&lt;br /&gt;여러 종류의 오브젝트를 관리한다면,&lt;br /&gt;풀 초기화를 통합 관리하는 PoolManager 클래스를 두는 것이 좋다.&lt;/li&gt;
&lt;li data-end=&quot;3636&quot; data-start=&quot;3534&quot;&gt;&lt;b&gt;비활성화 시 상태 초기화하기&lt;/b&gt;&lt;br /&gt;오브젝트를 반환할 때(actionOnRelease)&lt;br /&gt;위치, 체력, 속도 등 상태를 초기화하면 다음 재사용 시 안정적이다.&lt;/li&gt;
&lt;li data-end=&quot;3752&quot; data-start=&quot;3638&quot;&gt;&lt;b&gt;maxSize 적정선 정하기&lt;/b&gt;&lt;br /&gt;너무 크게 잡으면 메모리 낭비,&lt;br /&gt;너무 작으면 빈번한 생성/파괴가 다시 발생한다.&lt;br /&gt;플레이 환경에 맞는 균형 잡힌 크기를 실험적으로 정하자.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;3757&quot; data-start=&quot;3754&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3767&quot; data-start=&quot;3759&quot; data-ke-size=&quot;size26&quot;&gt;  정리&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3955&quot; data-start=&quot;3769&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;직접 구현한 풀&lt;/td&gt;
&lt;td&gt;Unity 내장 풀&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3866&quot; data-start=&quot;3843&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3852&quot; data-start=&quot;3843&quot;&gt;구현 난이도&lt;/td&gt;
&lt;td data-end=&quot;3857&quot; data-start=&quot;3852&quot; data-col-size=&quot;sm&quot;&gt;높음&lt;/td&gt;
&lt;td data-end=&quot;3866&quot; data-start=&quot;3857&quot; data-col-size=&quot;sm&quot;&gt;매우 쉬움&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3896&quot; data-start=&quot;3867&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3876&quot; data-start=&quot;3867&quot;&gt;제너릭 지원&lt;/td&gt;
&lt;td data-end=&quot;3887&quot; data-start=&quot;3876&quot; data-col-size=&quot;sm&quot;&gt;직접 구현 필요&lt;/td&gt;
&lt;td data-end=&quot;3896&quot; data-start=&quot;3887&quot; data-col-size=&quot;sm&quot;&gt;기본 지원&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3914&quot; data-start=&quot;3897&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3905&quot; data-start=&quot;3897&quot;&gt;자동 관리&lt;/td&gt;
&lt;td data-end=&quot;3909&quot; data-start=&quot;3905&quot; data-col-size=&quot;sm&quot;&gt;❌&lt;/td&gt;
&lt;td data-end=&quot;3914&quot; data-start=&quot;3909&quot; data-col-size=&quot;sm&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3934&quot; data-start=&quot;3915&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3920&quot; data-start=&quot;3915&quot;&gt;성능&lt;/td&gt;
&lt;td data-end=&quot;3925&quot; data-start=&quot;3920&quot; data-col-size=&quot;sm&quot;&gt;좋음&lt;/td&gt;
&lt;td data-end=&quot;3934&quot; data-start=&quot;3925&quot; data-col-size=&quot;sm&quot;&gt;더 안정적&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3955&quot; data-start=&quot;3935&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3942&quot; data-start=&quot;3935&quot;&gt;유지보수&lt;/td&gt;
&lt;td data-end=&quot;3948&quot; data-start=&quot;3942&quot; data-col-size=&quot;sm&quot;&gt;어려움&lt;/td&gt;
&lt;td data-end=&quot;3955&quot; data-start=&quot;3948&quot; data-col-size=&quot;sm&quot;&gt;용이함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3960&quot; data-start=&quot;3957&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3970&quot; data-start=&quot;3962&quot; data-ke-size=&quot;size26&quot;&gt;  결론&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4200&quot; data-start=&quot;3972&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4032&quot; data-start=&quot;3972&quot;&gt;Unity 2021 이후 버전에서는 &lt;b&gt;내장 오브젝트 풀(ObjectPool)&lt;/b&gt; 을 적극 활용하자.&lt;/li&gt;
&lt;li data-end=&quot;4097&quot; data-start=&quot;4033&quot;&gt;Instantiate() / Destroy()의 반복을 줄이면 &lt;b&gt;프레임 안정성&lt;/b&gt;이 크게 향상된다.&lt;/li&gt;
&lt;li data-end=&quot;4138&quot; data-start=&quot;4098&quot;&gt;적, 총알, 이펙트 등 자주 생성&amp;middot;파괴되는 오브젝트에 필수적이다.&lt;/li&gt;
&lt;li data-end=&quot;4200&quot; data-start=&quot;4139&quot;&gt;ObjectPool&amp;lt;T&amp;gt;는 &lt;b&gt;가독성, 안정성, 성능&lt;/b&gt;을 모두 잡은 Unity의 정답 같은 기능이다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/124</guid>
      <comments>https://dino-dev.tistory.com/124#entry124comment</comments>
      <pubDate>Mon, 13 Oct 2025 00:13:34 +0900</pubDate>
    </item>
    <item>
      <title>Unity에서 tag 비교할 때 CompareTag()를 사용해야 하는 이유</title>
      <link>https://dino-dev.tistory.com/123</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Unity에서 객체의 태그를 비교할 때 보통 아래 두 가지 방법 중 하나를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1760280247151&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (gameObject.tag == &quot;Enemy&quot;) { ... }

또는

if (gameObject.CompareTag(&quot;Enemy&quot;)) { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;417&quot; data-start=&quot;331&quot; data-ke-size=&quot;size16&quot;&gt;두 코드는 같은 결과를 낼 것처럼 보이지만, &lt;b&gt;Unity 공식 문서에서도 CompareTag() 사용을 권장한다.&lt;/b&gt;&lt;br /&gt;그 이유를 하나씩 살펴보자.&lt;/p&gt;
&lt;hr data-end=&quot;422&quot; data-start=&quot;419&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;460&quot; data-start=&quot;424&quot; data-ke-size=&quot;size26&quot;&gt;  1. CompareTag()는 문자열 비교가 아니다&lt;/h2&gt;
&lt;p data-end=&quot;560&quot; data-start=&quot;462&quot; data-ke-size=&quot;size16&quot;&gt;gameObject.tag == &quot;Enemy&quot;는 &lt;b&gt;순수한 문자열 비교 연산&lt;/b&gt;이다.&lt;br /&gt;이 말은 곧 &lt;b&gt;매 프레임마다 문자열을 읽고 비교하는 비용이 발생한다&lt;/b&gt;는 의미다.&lt;/p&gt;
&lt;p data-end=&quot;662&quot; data-start=&quot;562&quot; data-ke-size=&quot;size16&quot;&gt;Unity의 tag는 내부적으로 &lt;b&gt;문자열 해시 ID&lt;/b&gt;로 관리된다.&lt;br /&gt;CompareTag()는 이 해시 값을 직접 비교하므로 &lt;b&gt;문자열 연산 없이 훨씬 빠르게 처리된다.&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-end=&quot;720&quot; data-start=&quot;664&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;720&quot; data-start=&quot;666&quot; data-ke-size=&quot;size16&quot;&gt;✅ 즉, CompareTag()는 &quot;string 비교&quot;가 아니라 &quot;정수 ID 비교&quot;에 가깝다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;725&quot; data-start=&quot;722&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;753&quot; data-start=&quot;727&quot; data-ke-size=&quot;size26&quot;&gt;⚡ 2. 태그가 존재하지 않을 때의 안정성&lt;/h2&gt;
&lt;p data-end=&quot;765&quot; data-start=&quot;755&quot; data-ke-size=&quot;size16&quot;&gt;다음 코드를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1760280266570&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (gameObject.tag == &quot;Enemy&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;924&quot; data-start=&quot;813&quot; data-ke-size=&quot;size16&quot;&gt;이 경우, 만약 해당 GameObject의 태그가 &lt;b&gt;삭제되었거나 존재하지 않는 태그&lt;/b&gt;라면 Unity는 tag 프로퍼티를 접근할 때 &lt;b&gt;예외(UnityException)를 발생시킨다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1011&quot; data-start=&quot;926&quot; data-ke-size=&quot;size16&quot;&gt;반면 CompareTag()는 태그가 존재하지 않더라도 &lt;b&gt;안전하게 false를 반환&lt;/b&gt;한다.&lt;br /&gt;즉, 실행 중에 에러로 게임이 중단되는 일이 없다.&lt;/p&gt;
&lt;blockquote data-end=&quot;1051&quot; data-start=&quot;1013&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1051&quot; data-start=&quot;1015&quot; data-ke-size=&quot;size16&quot;&gt;✅ CompareTag()는 런타임 안전성까지 고려한 함수다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;1056&quot; data-start=&quot;1053&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1077&quot; data-start=&quot;1058&quot; data-ke-size=&quot;size26&quot;&gt;  3. 가독성과 의도 전달&lt;/h2&gt;
&lt;p data-end=&quot;1184&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;CompareTag(&quot;Enemy&quot;)는 &amp;ldquo;이 객체가 특정 태그인지 비교한다&amp;rdquo;는 의도가 명확하다.&lt;br /&gt;문자열 비교보다 &lt;b&gt;의미적으로 분명하고, 코드 리뷰 시에도 한눈에 목적이 드러난다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;1184&quot; data-start=&quot;1079&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1760280302169&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;if (other.CompareTag(&quot;Player&quot;))  // 이 객체가 'Player' 태그인지 확인&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;1263&quot; data-start=&quot;1260&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1282&quot; data-start=&quot;1265&quot; data-ke-size=&quot;size26&quot;&gt;  4. 실제 성능 차이&lt;/h2&gt;
&lt;p data-end=&quot;1382&quot; data-start=&quot;1284&quot; data-ke-size=&quot;size16&quot;&gt;다음과 같은 테스트를 해보면, 수천 번의 비교에서는 눈에 띄는 차이가 없지만&lt;br /&gt;&lt;b&gt;Update() 안에서 매 프레임 여러 객체의 태그를 검사하는 상황&lt;/b&gt;에서는 차이가 커진다.&lt;/p&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1546&quot; data-start=&quot;1384&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;방식&lt;/td&gt;
&lt;td&gt;평균 처리 속도 (상대값)&lt;/td&gt;
&lt;td&gt;예외 발생 가능성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1510&quot; data-start=&quot;1469&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1499&quot; data-start=&quot;1469&quot;&gt;gameObject.tag == &quot;Enemy&quot;&lt;/td&gt;
&lt;td data-end=&quot;1504&quot; data-start=&quot;1499&quot; data-col-size=&quot;sm&quot;&gt;느림&lt;/td&gt;
&lt;td data-end=&quot;1510&quot; data-start=&quot;1504&quot; data-col-size=&quot;sm&quot;&gt;있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1546&quot; data-start=&quot;1511&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1535&quot; data-start=&quot;1511&quot;&gt;CompareTag(&quot;Enemy&quot;)&lt;/td&gt;
&lt;td data-end=&quot;1540&quot; data-start=&quot;1535&quot; data-col-size=&quot;sm&quot;&gt;빠름&lt;/td&gt;
&lt;td data-end=&quot;1546&quot; data-start=&quot;1540&quot; data-col-size=&quot;sm&quot;&gt;없음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;blockquote data-end=&quot;1633&quot; data-start=&quot;1548&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1633&quot; data-start=&quot;1550&quot; data-ke-size=&quot;size16&quot;&gt;⚙️ CompareTag()는 Unity 내부적으로 C++ 레벨에서 최적화되어 있기 때문에&lt;br /&gt;문자열 비교보다 항상 더 효율적이고 안정적이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;1638&quot; data-start=&quot;1635&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1648&quot; data-start=&quot;1640&quot; data-ke-size=&quot;size26&quot;&gt;  결론&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;1907&quot; data-start=&quot;1650&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;항목&lt;/td&gt;
&lt;td&gt;tag == &quot;Enemy&quot;&lt;/td&gt;
&lt;td&gt;CompareTag(&quot;Enemy&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1781&quot; data-start=&quot;1752&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1760&quot; data-start=&quot;1752&quot;&gt;비교 방식&lt;/td&gt;
&lt;td data-end=&quot;1769&quot; data-start=&quot;1760&quot; data-col-size=&quot;sm&quot;&gt;문자열 비교&lt;/td&gt;
&lt;td data-end=&quot;1781&quot; data-start=&quot;1769&quot; data-col-size=&quot;sm&quot;&gt;해시 기반 비교&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1804&quot; data-start=&quot;1782&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1787&quot; data-start=&quot;1782&quot;&gt;성능&lt;/td&gt;
&lt;td data-end=&quot;1798&quot; data-start=&quot;1787&quot; data-col-size=&quot;sm&quot;&gt;상대적으로 느림&lt;/td&gt;
&lt;td data-end=&quot;1804&quot; data-start=&quot;1798&quot; data-col-size=&quot;sm&quot;&gt;빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1846&quot; data-start=&quot;1805&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1813&quot; data-start=&quot;1805&quot;&gt;예외 처리&lt;/td&gt;
&lt;td data-end=&quot;1829&quot; data-start=&quot;1813&quot; data-col-size=&quot;sm&quot;&gt;태그 없을 때 예외 발생&lt;/td&gt;
&lt;td data-end=&quot;1846&quot; data-start=&quot;1829&quot; data-col-size=&quot;sm&quot;&gt;안전하게 false 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1870&quot; data-start=&quot;1847&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1853&quot; data-start=&quot;1847&quot;&gt;가독성&lt;/td&gt;
&lt;td data-end=&quot;1859&quot; data-start=&quot;1853&quot; data-col-size=&quot;sm&quot;&gt;일반적&lt;/td&gt;
&lt;td data-end=&quot;1870&quot; data-start=&quot;1859&quot; data-col-size=&quot;sm&quot;&gt;의도가 명확함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;1907&quot; data-start=&quot;1871&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;1879&quot; data-start=&quot;1871&quot;&gt;권장 여부&lt;/td&gt;
&lt;td data-end=&quot;1883&quot; data-start=&quot;1879&quot; data-col-size=&quot;sm&quot;&gt;❌&lt;/td&gt;
&lt;td data-end=&quot;1907&quot; data-start=&quot;1883&quot; data-col-size=&quot;sm&quot;&gt;✅ &lt;b&gt;Unity 공식 권장 방식&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;1912&quot; data-start=&quot;1909&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1922&quot; data-start=&quot;1914&quot; data-ke-size=&quot;size26&quot;&gt;  요약&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2092&quot; data-start=&quot;1924&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1974&quot; data-start=&quot;1924&quot;&gt;CompareTag()는 문자열 비교가 아니라 &lt;b&gt;내부 해시 비교&lt;/b&gt;로 빠르다.&lt;/li&gt;
&lt;li data-end=&quot;2013&quot; data-start=&quot;1975&quot;&gt;존재하지 않는 태그에 접근해도 &lt;b&gt;예외가 발생하지 않는다.&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;2043&quot; data-start=&quot;2014&quot;&gt;코드 의도가 명확해지고 유지보수성이 높아진다.&lt;/li&gt;
&lt;li data-end=&quot;2092&quot; data-start=&quot;2044&quot;&gt;&lt;b&gt;Unity 공식 문서에서도 항상 CompareTag() 사용을 권장한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2097&quot; data-start=&quot;2094&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2113&quot; data-start=&quot;2099&quot; data-ke-size=&quot;size23&quot;&gt;✍️ 참고 문서&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2221&quot; data-start=&quot;2114&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2221&quot; data-start=&quot;2114&quot;&gt;&lt;a href=&quot;https://docs.unity3d.com/ScriptReference/GameObject.CompareTag.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://docs.unity3d.com/ScriptReference/GameObject.CompareTag.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/123</guid>
      <comments>https://dino-dev.tistory.com/123#entry123comment</comments>
      <pubDate>Sun, 12 Oct 2025 23:46:21 +0900</pubDate>
    </item>
    <item>
      <title>Unity에서 커맨드(Command) 패턴 활용하기</title>
      <link>https://dino-dev.tistory.com/122</link>
      <description>&lt;p data-end=&quot;158&quot; data-start=&quot;126&quot; data-ke-size=&quot;size16&quot;&gt;게임을 만들다 보면 이런 고민을 자주 하게 됩니다  &lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;305&quot; data-start=&quot;160&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;211&quot; data-start=&quot;160&quot;&gt;플레이어가 입력한 행동(이동, 공격, 점프 등)을 &lt;b&gt;기록하거나 되돌리고 싶을 때&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;261&quot; data-start=&quot;212&quot;&gt;&lt;b&gt;AI, UI, 플레이어&lt;/b&gt; 모두 같은 방식으로 명령을 실행하게 만들고 싶을 때&lt;/li&gt;
&lt;li data-end=&quot;305&quot; data-start=&quot;262&quot;&gt;&lt;b&gt;입력 로직&lt;/b&gt;과 &lt;b&gt;실제 행동 로직&lt;/b&gt;을 깔끔하게 분리하고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;354&quot; data-start=&quot;307&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하기 좋은 디자인 패턴이 바로 &lt;b&gt;커맨드(Command) 패턴&lt;/b&gt;입니다.&lt;/p&gt;
&lt;hr data-end=&quot;359&quot; data-start=&quot;356&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;376&quot; data-start=&quot;361&quot; data-ke-size=&quot;size26&quot;&gt;  커맨드 패턴이란?&lt;/h2&gt;
&lt;blockquote data-end=&quot;434&quot; data-start=&quot;378&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;434&quot; data-start=&quot;380&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;요청(행동)을 객체로 캡슐화해서, 실행&amp;middot;취소&amp;middot;재실행 등을 자유롭게 다룰 수 있게 하는 패턴&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;465&quot; data-start=&quot;436&quot; data-ke-size=&quot;size16&quot;&gt;즉, &amp;ldquo;&lt;b&gt;명령을 데이터처럼 다루는 방식&lt;/b&gt;&amp;rdquo;이에요.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;524&quot; data-start=&quot;467&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;484&quot; data-start=&quot;467&quot;&gt;버튼 클릭 &amp;rarr; &amp;ldquo;공격 명령&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;503&quot; data-start=&quot;485&quot;&gt;키보드 입력 &amp;rarr; &amp;ldquo;이동 명령&amp;rdquo;&lt;/li&gt;
&lt;li data-end=&quot;524&quot; data-start=&quot;504&quot;&gt;AI의 의사결정 &amp;rarr; &amp;ldquo;점프 명령&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;566&quot; data-start=&quot;526&quot; data-ke-size=&quot;size16&quot;&gt;이런 행동들을 모두 같은 인터페이스로 실행할 수 있게 만들 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;571&quot; data-start=&quot;568&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;592&quot; data-start=&quot;573&quot; data-ke-size=&quot;size26&quot;&gt;⚙️ 커맨드 패턴의 기본 구조&lt;/h2&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;823&quot; data-start=&quot;594&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;클래스&lt;/td&gt;
&lt;td&gt;역할&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;673&quot; data-start=&quot;626&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;641&quot; data-start=&quot;626&quot;&gt;&lt;b&gt;ICommand&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;673&quot; data-start=&quot;641&quot;&gt;명령의 인터페이스 (Execute, Undo 정의)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;718&quot; data-start=&quot;674&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;696&quot; data-start=&quot;674&quot;&gt;&lt;b&gt;ConcreteCommand&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;718&quot; data-start=&quot;696&quot;&gt;실제 동작을 수행하는 명령 클래스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;766&quot; data-start=&quot;719&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;733&quot; data-start=&quot;719&quot;&gt;&lt;b&gt;Invoker&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;766&quot; data-start=&quot;733&quot; data-col-size=&quot;sm&quot;&gt;명령을 실행하는 주체 (예: InputManager)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;823&quot; data-start=&quot;767&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;782&quot; data-start=&quot;767&quot;&gt;&lt;b&gt;Receiver&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;823&quot; data-start=&quot;782&quot;&gt;실제 행동을 하는 객체 (예: Player, Character 등)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;828&quot; data-start=&quot;825&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;859&quot; data-start=&quot;830&quot; data-ke-size=&quot;size26&quot;&gt;  예제 시나리오 &amp;mdash; &amp;ldquo;캐릭터 이동 및 공격&amp;rdquo;&lt;/h2&gt;
&lt;blockquote data-end=&quot;908&quot; data-start=&quot;861&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;908&quot; data-start=&quot;863&quot; data-ke-size=&quot;size16&quot;&gt;방향키로 캐릭터를 이동하고, Space키로 공격하는 간단한 2D 게임 예제입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;913&quot; data-start=&quot;910&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;939&quot; data-start=&quot;915&quot; data-ke-size=&quot;size23&quot;&gt;  1. ICommand 인터페이스&lt;/h3&gt;
&lt;pre id=&quot;code_1760111015288&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ICommand
{
    void Execute();
    void Undo();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;1070&quot; data-start=&quot;1023&quot; data-ke-size=&quot;size16&quot;&gt;모든 명령은 Execute()로 실행되고, Undo()로 되돌릴 수 있습니다.&lt;/p&gt;
&lt;hr data-end=&quot;1075&quot; data-start=&quot;1072&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1117&quot; data-start=&quot;1077&quot; data-ke-size=&quot;size23&quot;&gt;  2. 실제 명령 클래스들 (Concrete Commands)&lt;/h3&gt;
&lt;pre id=&quot;code_1760111029839&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class MoveCommand : ICommand
{
    private Character _character;
    private Vector3 _direction;

    public MoveCommand(Character character, Vector3 direction)
    {
        _character = character;
        _direction = direction;
    }

    public void Execute()
    {
        _character.Move(_direction);
    }

    public void Undo()
    {
        _character.Move(-_direction);
    }
}

public class AttackCommand : ICommand
{
    private Character _character;

    public AttackCommand(Character character)
    {
        _character = character;
    }

    public void Execute()
    {
        _character.Attack();
    }

    public void Undo()
    {
        Debug.Log(&quot;공격은 되돌릴 수 없습니다.&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;1863&quot; data-start=&quot;1860&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1893&quot; data-start=&quot;1865&quot; data-ke-size=&quot;size23&quot;&gt;  3. 캐릭터 클래스 (Receiver)&lt;/h3&gt;
&lt;pre id=&quot;code_1760111054275&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class Character : MonoBehaviour
{
    public float moveSpeed = 3f;

    public void Move(Vector3 direction)
    {
        transform.position += direction * moveSpeed * Time.deltaTime;
        Debug.Log($&quot;이동: {direction}&quot;);
    }

    public void Attack()
    {
        Debug.Log(&quot;공격!&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;2235&quot; data-start=&quot;2232&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2266&quot; data-start=&quot;2237&quot; data-ke-size=&quot;size23&quot;&gt;  4. 입력 처리 클래스 (Invoker)&lt;/h3&gt;
&lt;pre id=&quot;code_1760111069448&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System.Collections.Generic;
using UnityEngine;

public class InputHandler : MonoBehaviour
{
    [SerializeField] private Character _character;

    private Stack&amp;lt;ICommand&amp;gt; _commandHistory = new Stack&amp;lt;ICommand&amp;gt;();

    private void Update()
    {
        ICommand command = null;

        if (Input.GetKey(KeyCode.W)) command = new MoveCommand(_character, Vector3.up);
        if (Input.GetKey(KeyCode.S)) command = new MoveCommand(_character, Vector3.down);
        if (Input.GetKey(KeyCode.A)) command = new MoveCommand(_character, Vector3.left);
        if (Input.GetKey(KeyCode.D)) command = new MoveCommand(_character, Vector3.right);
        if (Input.GetKeyDown(KeyCode.Space)) command = new AttackCommand(_character);

        if (command != null)
        {
            command.Execute();
            _commandHistory.Push(command);
        }

        if (Input.GetKeyDown(KeyCode.Z)) UndoLastCommand();
    }

    private void UndoLastCommand()
    {
        if (_commandHistory.Count &amp;gt; 0)
        {
            var lastCommand = _commandHistory.Pop();
            lastCommand.Undo();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;3402&quot; data-start=&quot;3399&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3418&quot; data-start=&quot;3404&quot; data-ke-size=&quot;size26&quot;&gt;  실행 흐름 정리&lt;/h2&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3643&quot; data-start=&quot;3420&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;단계&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3480&quot; data-start=&quot;3448&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3452&quot; data-start=&quot;3448&quot;&gt;1&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3480&quot; data-start=&quot;3452&quot;&gt;InputHandler가 키 입력을 감지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3514&quot; data-start=&quot;3481&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3485&quot; data-start=&quot;3481&quot;&gt;2&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3514&quot; data-start=&quot;3485&quot;&gt;해당 입력에 맞는 Command 객체 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3561&quot; data-start=&quot;3515&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3519&quot; data-start=&quot;3515&quot;&gt;3&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3561&quot; data-start=&quot;3519&quot;&gt;Execute()로 명령 실행 (Character 행동 수행)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3602&quot; data-start=&quot;3562&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3566&quot; data-start=&quot;3562&quot;&gt;4&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3602&quot; data-start=&quot;3566&quot;&gt;실행된 명령을 _commandHistory 스택에 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3643&quot; data-start=&quot;3603&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3607&quot; data-start=&quot;3603&quot;&gt;5&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3643&quot; data-start=&quot;3607&quot;&gt;Z 키 입력 시 마지막 명령을 Undo()로 되돌림&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3648&quot; data-start=&quot;3645&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3666&quot; data-start=&quot;3650&quot; data-ke-size=&quot;size26&quot;&gt;  커맨드 패턴의 장점&lt;/h2&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3911&quot; data-start=&quot;3668&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;장점&lt;/td&gt;
&lt;td&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3745&quot; data-start=&quot;3696&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3715&quot; data-start=&quot;3696&quot;&gt;  &lt;b&gt;입력과 행동 분리&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3745&quot; data-start=&quot;3715&quot;&gt;입력 로직을 바꿔도 행동 코드 수정이 필요 없음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3800&quot; data-start=&quot;3746&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3771&quot; data-start=&quot;3746&quot;&gt; ️ &lt;b&gt;되돌리기/재실행 구현 용이&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;3800&quot; data-start=&quot;3771&quot; data-col-size=&quot;sm&quot;&gt;명령을 저장해두면 쉽게 Undo/Redo 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3849&quot; data-start=&quot;3801&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3818&quot; data-start=&quot;3801&quot;&gt;  &lt;b&gt;유연한 확장성&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3849&quot; data-start=&quot;3818&quot;&gt;새로운 명령(예: 점프, 회피)을 쉽게 추가 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3911&quot; data-start=&quot;3850&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3878&quot; data-start=&quot;3850&quot;&gt;  &lt;b&gt;AI, 매크로 등에도 재사용 가능&lt;/b&gt;&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3911&quot; data-start=&quot;3878&quot;&gt;동일한 명령 객체를 AI나 스크립트에서도 재활용 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3916&quot; data-start=&quot;3913&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3929&quot; data-start=&quot;3918&quot; data-ke-size=&quot;size26&quot;&gt;⚠️ 주의할 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4056&quot; data-start=&quot;3931&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4004&quot; data-start=&quot;3931&quot;&gt;너무 작은 동작마다 커맨드로 만들면 &lt;b&gt;불필요한 객체 생성이 많아짐&lt;/b&gt;&lt;br /&gt;&amp;rarr; 주로 &amp;ldquo;의미 있는 행동 단위&amp;rdquo;에만 사용하기&lt;/li&gt;
&lt;li data-end=&quot;4056&quot; data-start=&quot;4005&quot;&gt;명령 저장(Undo)을 많이 사용한다면&lt;br /&gt;&lt;b&gt;스택 메모리 관리&lt;/b&gt;를 신경써야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;4061&quot; data-start=&quot;4058&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4076&quot; data-start=&quot;4063&quot; data-ke-size=&quot;size26&quot;&gt;  확장 아이디어&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;4236&quot; data-start=&quot;4078&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;4134&quot; data-start=&quot;4078&quot;&gt;&lt;b&gt;AI 행동 시스템&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4134&quot; data-start=&quot;4100&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4134&quot; data-start=&quot;4100&quot;&gt;AI가 행동을 Command로 큐에 넣고 순서대로 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4182&quot; data-start=&quot;4135&quot;&gt;&lt;b&gt;플레이어 리플레이 기능&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4182&quot; data-start=&quot;4160&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4182&quot; data-start=&quot;4160&quot;&gt;명령 기록을 파일에 저장 후 재생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;4236&quot; data-start=&quot;4183&quot;&gt;&lt;b&gt;UI 버튼과의 연동&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;4236&quot; data-start=&quot;4206&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;4236&quot; data-start=&quot;4206&quot;&gt;버튼 클릭 시 AttackCommand 실행 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;4241&quot; data-start=&quot;4238&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;4250&quot; data-start=&quot;4243&quot; data-ke-size=&quot;size26&quot;&gt;✅ 정리&lt;/h2&gt;
&lt;blockquote data-end=&quot;4324&quot; data-start=&quot;4252&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;4324&quot; data-start=&quot;4254&quot; data-ke-size=&quot;size16&quot;&gt;커맨드 패턴은 **&amp;ldquo;입력과 행동을 분리&amp;rdquo;**해서&lt;br /&gt;게임의 &lt;b&gt;유연성과 유지보수성&lt;/b&gt;을 높이는 데 매우 유용한 패턴입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;4396&quot; data-start=&quot;4326&quot; data-ke-size=&quot;size16&quot;&gt;Unity에서 이 패턴을 사용하면&lt;br /&gt;입력, 행동, 되돌리기, AI, UI 모두 &lt;b&gt;하나의 구조로 통일&lt;/b&gt;할 수 있습니다.&lt;/p&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/122</guid>
      <comments>https://dino-dev.tistory.com/122#entry122comment</comments>
      <pubDate>Sat, 11 Oct 2025 00:45:27 +0900</pubDate>
    </item>
    <item>
      <title>Unity에서 이벤트버스(Event Bus) 패턴 활용하기</title>
      <link>https://dino-dev.tistory.com/121</link>
      <description>&lt;p data-end=&quot;185&quot; data-start=&quot;133&quot; data-ke-size=&quot;size16&quot;&gt;Unity에서 게임을 개발하다 보면 &lt;b&gt;여러 오브젝트가 서로 상호작용&lt;/b&gt;할 일이 많습니다.&lt;/p&gt;
&lt;p data-end=&quot;195&quot; data-start=&quot;187&quot; data-ke-size=&quot;size16&quot;&gt;예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;288&quot; data-start=&quot;196&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;224&quot; data-start=&quot;196&quot;&gt;플레이어가 공격하면 UI 체력이 줄어야 하고&lt;/li&gt;
&lt;li data-end=&quot;259&quot; data-start=&quot;225&quot;&gt;적이 피해를 받고 사망하면 점수 시스템이 반응해야 하고&lt;/li&gt;
&lt;li data-end=&quot;288&quot; data-start=&quot;260&quot;&gt;사운드 매니저가 효과음을 재생해야 하는 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;388&quot; data-start=&quot;290&quot; data-ke-size=&quot;size16&quot;&gt;이때 &lt;b&gt;직접 참조&lt;/b&gt;(player.OnAttack += ui.UpdateHealth)로 연결하면,오브젝트가 많아질수록 &lt;b&gt;코드가 뒤엉키고 유지보수가 어려워집니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-end=&quot;425&quot; data-start=&quot;390&quot; data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;이벤트버스(Event Bus)&lt;/b&gt; 패턴이 유용합니다.&lt;/p&gt;
&lt;hr data-end=&quot;430&quot; data-start=&quot;427&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;457&quot; data-start=&quot;432&quot; data-ke-size=&quot;size26&quot;&gt;1️⃣ 이벤트버스(Event Bus)란?&lt;/h2&gt;
&lt;blockquote data-end=&quot;518&quot; data-start=&quot;459&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;518&quot; data-start=&quot;461&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Event Bus는 이벤트를 &amp;ldquo;중앙 허브&amp;rdquo;로 모아 관리하고, 필요할 때 전달하는 패턴&lt;/b&gt;입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-end=&quot;524&quot; data-start=&quot;520&quot; data-ke-size=&quot;size16&quot;&gt;즉,&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;592&quot; data-start=&quot;525&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;557&quot; data-start=&quot;525&quot;&gt;이벤트를 &lt;b&gt;발행(Publish)&lt;/b&gt; 하는 오브젝트&lt;/li&gt;
&lt;li data-end=&quot;592&quot; data-start=&quot;558&quot;&gt;이벤트를 &lt;b&gt;구독(Subscribe)&lt;/b&gt; 하는 오브젝트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;649&quot; data-start=&quot;594&quot; data-ke-size=&quot;size16&quot;&gt;이 둘을 직접 연결하지 않고,중앙의 &lt;b&gt;Event Bus&lt;/b&gt;를 통해 통신하게 하는 방식입니다.&lt;/p&gt;
&lt;p data-end=&quot;649&quot; data-start=&quot;594&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-end=&quot;656&quot; data-start=&quot;651&quot; data-ke-size=&quot;size16&quot;&gt;장점:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;750&quot; data-start=&quot;657&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;689&quot; data-start=&quot;657&quot;&gt;오브젝트 간 &lt;b&gt;결합도(Coupling) 최소화&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;717&quot; data-start=&quot;690&quot;&gt;이벤트 추가/삭제가 쉽고, 유지보수가 간단&lt;/li&gt;
&lt;li data-end=&quot;750&quot; data-start=&quot;718&quot;&gt;여러 시스템에서 동일 이벤트를 &lt;b&gt;동시에 처리 가능&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;755&quot; data-start=&quot;752&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;779&quot; data-start=&quot;757&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣ 간단한 이벤트버스 구현 예제&lt;/h2&gt;
&lt;h3 data-end=&quot;800&quot; data-start=&quot;781&quot; data-ke-size=&quot;size23&quot;&gt;  EventBus 클래스&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759880783427&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using System;
using System.Collections.Generic;

public static class EventBus
{
    // 타입별 이벤트 저장
    private static Dictionary&amp;lt;Type, Action&amp;lt;object&amp;gt;&amp;gt; _eventTable = new Dictionary&amp;lt;Type, Action&amp;lt;object&amp;gt;&amp;gt;();

    // 이벤트 구독
    public static void Subscribe&amp;lt;T&amp;gt;(Action&amp;lt;T&amp;gt; listener)
    {
        Type type = typeof(T);
        if (_eventTable.ContainsKey(type))
        {
            _eventTable[type] += (obj) =&amp;gt; listener((T)obj);
        }
        else
        {
            _eventTable[type] = (obj) =&amp;gt; listener((T)obj);
        }
    }

    // 이벤트 구독 해제
    public static void Unsubscribe&amp;lt;T&amp;gt;(Action&amp;lt;T&amp;gt; listener)
    {
        Type type = typeof(T);
        if (_eventTable.ContainsKey(type))
        {
            _eventTable[type] -= (obj) =&amp;gt; listener((T)obj);
        }
    }

    // 이벤트 발행
    public static void Publish&amp;lt;T&amp;gt;(T eventData)
    {
        Type type = typeof(T);
        if (_eventTable.ContainsKey(type))
        {
            _eventTable[type]?.Invoke(eventData);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-end=&quot;1877&quot; data-start=&quot;1812&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;1877&quot; data-start=&quot;1814&quot; data-ke-size=&quot;size16&quot;&gt;  포인트:&lt;br /&gt;T 타입별로 이벤트를 구분해서 발행하고, 구독자가 타입별로 받을 수 있도록 구현했습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;1882&quot; data-start=&quot;1879&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;1901&quot; data-start=&quot;1884&quot; data-ke-size=&quot;size23&quot;&gt;  이벤트 데이터 정의&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759880836028&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class PlayerDamageEvent
{
    public int Damage;
    public PlayerDamageEvent(int damage)
    {
        Damage = damage;
    }
}

public class EnemyDeadEvent
{
    public int Score;
    public EnemyDeadEvent(int score)
    {
        Score = score;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2184&quot; data-start=&quot;2181&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2214&quot; data-start=&quot;2186&quot; data-ke-size=&quot;size23&quot;&gt;  이벤트 발행 예제 (Publisher)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759880900164&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class Player : MonoBehaviour
{
    public void TakeDamage(int damage)
    {
        Debug.Log($&quot;플레이어가 {damage} 데미지 받음&quot;);
        EventBus.Publish(new PlayerDamageEvent(damage));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2447&quot; data-start=&quot;2444&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;2478&quot; data-start=&quot;2449&quot; data-ke-size=&quot;size23&quot;&gt;  이벤트 구독 예제 (Subscriber)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759880910722&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class UIManager : MonoBehaviour
{
    private void OnEnable()
    {
        EventBus.Subscribe&amp;lt;PlayerDamageEvent&amp;gt;(OnPlayerDamage);
    }

    private void OnDisable()
    {
        EventBus.Unsubscribe&amp;lt;PlayerDamageEvent&amp;gt;(OnPlayerDamage);
    }

    private void OnPlayerDamage(PlayerDamageEvent e)
    {
        Debug.Log($&quot;UI 업데이트: 플레이어 체력 감소 {e.Damage}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;2891&quot; data-start=&quot;2888&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2917&quot; data-start=&quot;2893&quot; data-ke-size=&quot;size26&quot;&gt;3️⃣ 이벤트버스 패턴 사용 시 주의점&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-end=&quot;3178&quot; data-start=&quot;2919&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li data-end=&quot;3001&quot; data-start=&quot;2919&quot;&gt;&lt;b&gt;메모리 관리&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3001&quot; data-start=&quot;2938&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;2971&quot; data-start=&quot;2938&quot;&gt;구독 후 반드시 해제(Unsubscribe) 필요&lt;/li&gt;
&lt;li data-end=&quot;3001&quot; data-start=&quot;2975&quot;&gt;안 하면 씬 전환 시 메모리 누수 발생 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3086&quot; data-start=&quot;3003&quot;&gt;&lt;b&gt;디버깅 어려움&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3086&quot; data-start=&quot;3023&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3050&quot; data-start=&quot;3023&quot;&gt;이벤트가 어디서 호출되는지 추적하기 어려움&lt;/li&gt;
&lt;li data-end=&quot;3086&quot; data-start=&quot;3054&quot;&gt;로그를 적극 활용하거나 이벤트 이름/타입을 명확히 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;3178&quot; data-start=&quot;3088&quot;&gt;&lt;b&gt;고빈도 이벤트 주의&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3178&quot; data-start=&quot;3111&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3139&quot; data-start=&quot;3111&quot;&gt;프레임마다 호출되는 이벤트에는 적합하지 않음&lt;/li&gt;
&lt;li data-end=&quot;3178&quot; data-start=&quot;3143&quot;&gt;Update 내에서 매번 Publish 하는 것은 피해야 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;3183&quot; data-start=&quot;3180&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3205&quot; data-start=&quot;3185&quot; data-ke-size=&quot;size26&quot;&gt;4️⃣ 이벤트버스를 활용한 장점&lt;/h2&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3357&quot; data-start=&quot;3207&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;3357&quot; data-start=&quot;3235&quot;&gt;
&lt;tr data-end=&quot;3279&quot; data-start=&quot;3235&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3244&quot; data-start=&quot;3235&quot;&gt;느슨한 결합&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3279&quot; data-start=&quot;3244&quot;&gt;Publisher와 Subscriber가 서로 몰라도 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3317&quot; data-start=&quot;3280&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3287&quot; data-start=&quot;3280&quot;&gt;재사용성&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3317&quot; data-start=&quot;3287&quot;&gt;같은 이벤트를 여러 시스템에서 동시에 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3357&quot; data-start=&quot;3318&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3326&quot; data-start=&quot;3318&quot;&gt;확장 용이&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3357&quot; data-start=&quot;3326&quot;&gt;새 Subscriber만 추가하면 기능 확장 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;3362&quot; data-start=&quot;3359&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3385&quot; data-start=&quot;3364&quot; data-ke-size=&quot;size26&quot;&gt;5️⃣ 실제 게임에서의 활용 예시&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3516&quot; data-start=&quot;3387&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3432&quot; data-start=&quot;3387&quot;&gt;플레이어 공격 &amp;rarr; &lt;b&gt;적 체력 감소 + UI 체력 감소 + 사운드 재생&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3471&quot; data-start=&quot;3433&quot;&gt;적 사망 &amp;rarr; &lt;b&gt;점수 증가 + 아이템 드롭 + 사운드 재생&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3516&quot; data-start=&quot;3472&quot;&gt;게임 종료 &amp;rarr; &lt;b&gt;UI, 점수판, 효과음 등 여러 시스템 동시에 반응&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-end=&quot;3562&quot; data-start=&quot;3518&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-end=&quot;3562&quot; data-start=&quot;3520&quot; data-ke-size=&quot;size16&quot;&gt;  핵심: &lt;b&gt;중앙 이벤트 버스를 통해 각 시스템을 독립적으로 연결&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-end=&quot;3567&quot; data-start=&quot;3564&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3579&quot; data-start=&quot;3569&quot; data-ke-size=&quot;size26&quot;&gt;6️⃣ 마무리&lt;/h2&gt;
&lt;p data-end=&quot;3606&quot; data-start=&quot;3581&quot; data-ke-size=&quot;size16&quot;&gt;Unity에서 이벤트버스 패턴을 사용하면:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3673&quot; data-start=&quot;3607&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3638&quot; data-start=&quot;3607&quot;&gt;&lt;b&gt;복잡한 오브젝트 간 상호작용을 간단히 관리&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;3673&quot; data-start=&quot;3639&quot;&gt;&lt;b&gt;유지보수성 높은 코드 구조&lt;/b&gt;를 만들 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;3781&quot; data-start=&quot;3675&quot; data-ke-size=&quot;size16&quot;&gt;특히 큰 프로젝트나 여러 시스템이 얽힌 게임에서&lt;br /&gt;상태 관리와 UI, 사운드, 점수 시스템 같은 &lt;b&gt;다중 이벤트 처리&lt;/b&gt;가 필요할 때&lt;br /&gt;이벤트버스 패턴은 매우 강력한 도구가 됩니다.  &lt;/p&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/121</guid>
      <comments>https://dino-dev.tistory.com/121#entry121comment</comments>
      <pubDate>Wed, 8 Oct 2025 08:48:55 +0900</pubDate>
    </item>
    <item>
      <title>Unity에서 상태패턴으로 캐릭터 움직임과 공격 구현하기</title>
      <link>https://dino-dev.tistory.com/120</link>
      <description>&lt;p data-end=&quot;236&quot; data-start=&quot;143&quot; data-ke-size=&quot;size16&quot;&gt;유니티에서 캐릭터의 &lt;b&gt;이동, 공격, 피격, 죽음&lt;/b&gt; 같은 행동을 관리할 때&lt;br /&gt;조건문(if, switch)으로 상태를 구분하다 보면 코드가 점점 복잡해집니다.&lt;/p&gt;
&lt;p data-end=&quot;352&quot; data-start=&quot;238&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때 유용한 설계 방법이 바로 &lt;b&gt;상태패턴(State Pattern)&lt;/b&gt; 입니다.&lt;br /&gt;이번 글에서는 상태패턴을 이용해 &lt;b&gt;2D 캐릭터가 움직이고, 공격하고, 맞고, 죽는&lt;/b&gt; 간단한 예제를 만들어봅니다.&lt;/p&gt;
&lt;hr data-end=&quot;357&quot; data-start=&quot;354&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;372&quot; data-start=&quot;359&quot; data-ke-size=&quot;size26&quot;&gt;  상태패턴이란?&lt;/h2&gt;
&lt;p data-end=&quot;438&quot; data-start=&quot;374&quot; data-ke-size=&quot;size16&quot;&gt;상태패턴은 &amp;ldquo;객체의 상태에 따라 행동이 달라지는 것&amp;rdquo;을 &lt;b&gt;각 상태를 클래스로 분리해서 관리하는 방법&lt;/b&gt;이에요.&lt;/p&gt;
&lt;p data-end=&quot;453&quot; data-start=&quot;440&quot; data-ke-size=&quot;size16&quot;&gt;보통 이렇게 바뀌죠  &lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;635&quot; data-start=&quot;455&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Before (조건문 기반)&lt;/td&gt;
&lt;td&gt;After (상태패턴 기반)&lt;span&gt; &lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;572&quot; data-start=&quot;541&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;555&quot; data-start=&quot;541&quot;&gt;if문이 점점 길어짐&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;572&quot; data-start=&quot;555&quot;&gt;각 상태별 클래스로 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;610&quot; data-start=&quot;573&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;592&quot; data-start=&quot;573&quot;&gt;상태 전환 시 코드 수정 많음&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;610&quot; data-start=&quot;592&quot;&gt;상태 클래스만 수정하면 됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;635&quot; data-start=&quot;611&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;621&quot; data-start=&quot;611&quot;&gt;테스트 어려움&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;635&quot; data-start=&quot;621&quot;&gt;독립적 테스트 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;640&quot; data-start=&quot;637&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;655&quot; data-start=&quot;642&quot; data-ke-size=&quot;size26&quot;&gt;  예제 시나리오&lt;/h2&gt;
&lt;p data-end=&quot;686&quot; data-start=&quot;657&quot; data-ke-size=&quot;size16&quot;&gt;이번 예제에서 캐릭터는 다음 5가지 상태를 가집니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;상태설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;831&quot; data-start=&quot;688&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;831&quot; data-start=&quot;716&quot;&gt;
&lt;tr data-end=&quot;738&quot; data-start=&quot;716&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;723&quot; data-start=&quot;716&quot;&gt;Idle&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;738&quot; data-start=&quot;723&quot;&gt;가만히 서 있는 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;758&quot; data-start=&quot;739&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;746&quot; data-start=&quot;739&quot;&gt;Walk&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;758&quot; data-start=&quot;746&quot;&gt;이동 중인 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;779&quot; data-start=&quot;759&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;768&quot; data-start=&quot;759&quot;&gt;Attack&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;779&quot; data-start=&quot;768&quot;&gt;공격 중 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;804&quot; data-start=&quot;780&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;786&quot; data-start=&quot;780&quot;&gt;Hit&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;804&quot; data-start=&quot;786&quot;&gt;피격되어 잠시 멈추는 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;831&quot; data-start=&quot;805&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;812&quot; data-start=&quot;805&quot;&gt;Dead&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;831&quot; data-start=&quot;812&quot;&gt;체력이 0이 되어 죽은 상태&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;836&quot; data-start=&quot;833&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;851&quot; data-start=&quot;838&quot; data-ke-size=&quot;size26&quot;&gt;  프로젝트 구조&lt;/h2&gt;
&lt;pre id=&quot;code_1759824331467&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Scripts/
 ├── Character.cs
 ├── ICharacterState.cs
 ├── IdleState.cs
 ├── WalkState.cs
 ├── AttackState.cs
 ├── HitState.cs
 └── DeadState.cs&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;hr data-end=&quot;1007&quot; data-start=&quot;1004&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1029&quot; data-start=&quot;1009&quot; data-ke-size=&quot;size26&quot;&gt;  1. 상태 인터페이스 정의&lt;/h2&gt;
&lt;p data-end=&quot;1056&quot; data-start=&quot;1031&quot; data-ke-size=&quot;size16&quot;&gt;모든 상태가 공통으로 가져야 하는 구조입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1759824344856&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface ICharacterState
{
    void OnEnter();
    void OnExit();
    void Update();
}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-end=&quot;1219&quot; data-start=&quot;1168&quot; data-ke-size=&quot;size16&quot;&gt;각 상태는 &amp;ldquo;들어올 때&amp;rdquo;, &amp;ldquo;나갈 때&amp;rdquo;, &amp;ldquo;프레임마다 실행할 때&amp;rdquo; 세 가지 함수를 가집니다.&lt;/p&gt;
&lt;hr data-end=&quot;1224&quot; data-start=&quot;1221&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;1252&quot; data-start=&quot;1226&quot; data-ke-size=&quot;size26&quot;&gt;  2. Character 클래스 만들기&lt;/h2&gt;
&lt;p data-end=&quot;1292&quot; data-start=&quot;1254&quot; data-ke-size=&quot;size16&quot;&gt;캐릭터는 현재 상태를 가지고 있으며, 입력을 받아서 상태를 바꿉니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1759824360943&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class Character : MonoBehaviour
{
    private ICharacterState _currentState;

    // 상태 객체
    private IdleState _idleState;
    private WalkState _walkState;
    private AttackState _attackState;
    private HitState _hitState;
    private DeadState _deadState;

    // 이동 관련
    [SerializeField] private float _moveSpeed = 3f;
    private Vector2 _moveDir;

    // 체력 및 공격
    [SerializeField] private int _health = 100;
    private bool _canAttack = true;
    private float _attackCooldown = 1f;

    // 컴포넌트
    private Animator _animator;
    private SpriteRenderer _renderer;

    private void Awake()
    {
        _animator = GetComponent&amp;lt;Animator&amp;gt;();
        _renderer = GetComponent&amp;lt;SpriteRenderer&amp;gt;();

        _idleState = new IdleState(this);
        _walkState = new WalkState(this);
        _attackState = new AttackState(this);
        _hitState = new HitState(this);
        _deadState = new DeadState(this);
    }

    private void Start()
    {
        ChangeState(_idleState);
    }

    private void Update()
    {
        if (_currentState == _deadState)
            return;

        HandleInput();
        _currentState.Update();
    }

    private void HandleInput()
    {
        // 공격
        if (Input.GetKeyDown(KeyCode.Space) &amp;amp;&amp;amp; _canAttack)
        {
            ChangeState(_attackState);
            return;
        }

        // 이동
        float x = Input.GetAxisRaw(&quot;Horizontal&quot;);
        float y = Input.GetAxisRaw(&quot;Vertical&quot;);
        _moveDir = new Vector2(x, y).normalized;

        if (_moveDir != Vector2.zero)
            ChangeState(_walkState);
        else
            ChangeState(_idleState);
    }

    public void Move(Vector2 dir)
    {
        transform.Translate(dir * _moveSpeed * Time.deltaTime);
        if (dir.x != 0)
            _renderer.flipX = dir.x &amp;lt; 0;
    }

    public void ChangeState(ICharacterState newState)
    {
        if (_currentState == newState)
            return;

        _currentState?.OnExit();
        _currentState = newState;
        _currentState.OnEnter();
    }

    public void TakeDamage(int damage)
    {
        if (_currentState == _deadState)
            return;

        _health -= damage;
        if (_health &amp;lt;= 0)
            ChangeState(_deadState);
        else
            ChangeState(_hitState);
    }

    public void SetAttackCooldown()
    {
        _canAttack = false;
        Invoke(nameof(ResetAttackCooldown), _attackCooldown);
    }

    private void ResetAttackCooldown() =&amp;gt; _canAttack = true;

    public Vector2 GetMoveDir() =&amp;gt; _moveDir;
    public Animator Animator =&amp;gt; _animator;
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;3929&quot; data-start=&quot;3926&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3952&quot; data-start=&quot;3931&quot; data-ke-size=&quot;size26&quot;&gt;  3. 각 상태 클래스 만들기&lt;/h2&gt;
&lt;h3 data-end=&quot;3970&quot; data-start=&quot;3954&quot; data-ke-size=&quot;size23&quot;&gt;  IdleState&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759824468283&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class IdleState : ICharacterState
{
    private readonly Character _character;

    public IdleState(Character character)
    {
        _character = character;
    }

    public void OnEnter()
    {
        Debug.Log(&quot;대기 상태 진입&quot;);
        _character.Animator.Play(&quot;Idle&quot;);
    }

    public void OnExit() { }

    public void Update() { }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;4357&quot; data-start=&quot;4354&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4375&quot; data-start=&quot;4359&quot; data-ke-size=&quot;size23&quot;&gt;  WalkState&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759824475241&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class WalkState : ICharacterState
{
    private readonly Character _character;

    public WalkState(Character character)
    {
        _character = character;
    }

    public void OnEnter()
    {
        Debug.Log(&quot;걷기 상태 진입&quot;);
        _character.Animator.Play(&quot;Walk&quot;);
    }

    public void OnExit() { }

    public void Update()
    {
        Vector2 moveDir = _character.GetMoveDir();
        _character.Move(moveDir);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;4855&quot; data-start=&quot;4852&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;4887&quot; data-start=&quot;4857&quot; data-ke-size=&quot;size23&quot;&gt;  AttackState (공격 쿨타임 포함)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759824484493&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class AttackState : ICharacterState
{
    private readonly Character _character;
    private float _attackDuration = 0.5f;
    private float _timer;

    public AttackState(Character character)
    {
        _character = character;
    }

    public void OnEnter()
    {
        Debug.Log(&quot;공격 상태 진입&quot;);
        _character.Animator.Play(&quot;Attack&quot;);
        _character.SetAttackCooldown();
        _timer = _attackDuration;
    }

    public void OnExit() { }

    public void Update()
    {
        _timer -= Time.deltaTime;
        if (_timer &amp;lt;= 0)
        {
            _character.ChangeState(new IdleState(_character));
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;5572&quot; data-start=&quot;5569&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;5596&quot; data-start=&quot;5574&quot; data-ke-size=&quot;size23&quot;&gt;⚡ HitState (피격 상태)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759824523559&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class HitState : ICharacterState
{
    private readonly Character _character;
    private float _hitDuration = 0.3f;
    private float _timer;

    public HitState(Character character)
    {
        _character = character;
    }

    public void OnEnter()
    {
        Debug.Log(&quot;피격 상태 진입&quot;);
        _character.Animator.Play(&quot;Hit&quot;);
        _timer = _hitDuration;
    }

    public void OnExit() { }

    public void Update()
    {
        _timer -= Time.deltaTime;
        if (_timer &amp;lt;= 0)
        {
            _character.ChangeState(new IdleState(_character));
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;6226&quot; data-start=&quot;6223&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-end=&quot;6251&quot; data-start=&quot;6228&quot; data-ke-size=&quot;size23&quot;&gt;⚫ DeadState (죽음 상태)&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1759824532409&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class DeadState : ICharacterState
{
    private readonly Character _character;

    public DeadState(Character character)
    {
        _character = character;
    }

    public void OnEnter()
    {
        Debug.Log(&quot;죽음 상태 진입&quot;);
        _character.Animator.Play(&quot;Dead&quot;);
    }

    public void OnExit() { }

    public void Update() { }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-end=&quot;6993&quot; data-start=&quot;6990&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7009&quot; data-start=&quot;6995&quot; data-ke-size=&quot;size26&quot;&gt;  상태패턴의 장점&lt;/h2&gt;
&lt;p data-end=&quot;7092&quot; data-start=&quot;7011&quot; data-ke-size=&quot;size16&quot;&gt;✅ 각 상태가 독립적이라 수정이 쉽습니다.&lt;br /&gt;✅ if문 없이 상태 전환이 깔끔합니다.&lt;br /&gt;✅ 상태 추가(예: 점프, 스킬 등)가 간단합니다.&lt;/p&gt;
&lt;hr data-end=&quot;7268&quot; data-start=&quot;7265&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;7278&quot; data-start=&quot;7270&quot; data-ke-size=&quot;size26&quot;&gt;✨ 마무리&lt;/h2&gt;
&lt;p data-end=&quot;7398&quot; data-start=&quot;7280&quot; data-ke-size=&quot;size16&quot;&gt;상태패턴은 &lt;b&gt;복잡한 행동을 가진 캐릭터&lt;/b&gt;를 관리할 때 정말 강력합니다.&lt;br /&gt;특히 유니티 같은 프레임 기반 구조에서는 &amp;ldquo;현재 상태가 어떤 로직을 실행할지&amp;rdquo;를 분리해두면 디버깅도, 확장도 훨씬 쉬워집니다.&lt;/p&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/120</guid>
      <comments>https://dino-dev.tistory.com/120#entry120comment</comments>
      <pubDate>Tue, 7 Oct 2025 17:16:43 +0900</pubDate>
    </item>
    <item>
      <title>Unity에서 추상 클래스 기반 Singleton 패턴 구현하기</title>
      <link>https://dino-dev.tistory.com/119</link>
      <description>&lt;p data-end=&quot;265&quot; data-start=&quot;150&quot; data-ke-size=&quot;size16&quot;&gt;게임을 만들다 보면, 전역에서 접근 가능한 매니저 객체가 필요합니다.&lt;br /&gt;예를 들어 GameManager, AudioManager, UIManager처럼 하나만 존재해야 하는 클래스들 말이죠.&lt;/p&gt;
&lt;p data-end=&quot;384&quot; data-start=&quot;267&quot; data-ke-size=&quot;size16&quot;&gt;이럴 때 사용하는 대표적인 디자인 패턴이 바로 &lt;b&gt;Singleton(싱글톤)&lt;/b&gt; 입니다.&lt;br /&gt;이번에는 일반적인 싱글톤이 아니라, &lt;b&gt;추상 클래스를 기반으로 공통 로직을 재사용할 수 있는 구조&lt;/b&gt;로 만들어봅시다.&lt;/p&gt;
&lt;hr data-end=&quot;389&quot; data-start=&quot;386&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;405&quot; data-start=&quot;391&quot; data-ke-size=&quot;size26&quot;&gt;1️⃣ 기본 아이디어&lt;/h2&gt;
&lt;p data-end=&quot;520&quot; data-start=&quot;407&quot; data-ke-size=&quot;size16&quot;&gt;Unity에서는 MonoBehaviour 기반의 싱글톤을 만들 때,&lt;br /&gt;다른 클래스들이 동일한 패턴을 반복해서 작성하는 경우가 많습니다.&lt;br /&gt;예를 들어 이런 형태를 여러 매니저가 각각 구현하곤 하죠&lt;/p&gt;
&lt;pre id=&quot;code_1759751784825&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class GameManager : MonoBehaviour
{
    public static GameManager Instance;

    void Awake()
    {
        if (Instance == null)
            Instance = this;
        else
            Destroy(gameObject);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;846&quot; data-start=&quot;757&quot; data-ke-size=&quot;size16&quot;&gt;이런 중복을 줄이기 위해 &lt;b&gt;제네릭 추상 클래스 Singleton&amp;lt;T&amp;gt;&lt;/b&gt;를 만들면,&lt;br /&gt;모든 매니저가 상속만으로 동일한 싱글톤 동작을 자동으로 가지게 됩니다.&lt;/p&gt;
&lt;hr data-end=&quot;851&quot; data-start=&quot;848&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;879&quot; data-start=&quot;853&quot; data-ke-size=&quot;size26&quot;&gt;2️⃣ Singleton 추상 클래스 구현&lt;/h2&gt;
&lt;pre id=&quot;code_1759751822550&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public abstract class Singleton&amp;lt;T&amp;gt; : MonoBehaviour where T : MonoBehaviour
{
    private static T _instance;
    public static T Instance
    {
        get
        {
            if (_instance == null)
            {
                // 씬에 존재하는 인스턴스 찾기
                _instance = FindFirstObjectByType&amp;lt;T&amp;gt;();

                // 없으면 자동 생성
                if (_instance == null)
                {
                    var obj = new GameObject(typeof(T).Name);
                    _instance = obj.AddComponent&amp;lt;T&amp;gt;();
                }
            }
            return _instance;
        }
    }

    protected virtual void Awake()
    {
        if (_instance == null)
        {
            _instance = this as T;
            DontDestroyOnLoad(gameObject); // 씬 이동 시에도 유지
        }
        else if (_instance != this)
        {
            Destroy(gameObject); // 중복 생성 방지
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-end=&quot;1807&quot; data-start=&quot;1794&quot; data-ke-size=&quot;size23&quot;&gt;  핵심 포인트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;2028&quot; data-start=&quot;1808&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;1875&quot; data-start=&quot;1808&quot;&gt;where T : MonoBehaviour : 제네릭 타입 T가 MonoBehaviour여야 한다는 제약 조건&lt;/li&gt;
&lt;li data-end=&quot;1916&quot; data-start=&quot;1876&quot;&gt;FindObjectOfType&amp;lt;T&amp;gt;() : 씬 내 존재 여부 확인&lt;/li&gt;
&lt;li data-end=&quot;1954&quot; data-start=&quot;1917&quot;&gt;DontDestroyOnLoad() : 씬 전환 시에도 유지&lt;/li&gt;
&lt;li data-end=&quot;2028&quot; data-start=&quot;1955&quot;&gt;protected virtual void Awake() : 상속받는 클래스가 Awake()를 오버라이드할 수 있도록 허용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2033&quot; data-start=&quot;2030&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2059&quot; data-start=&quot;2035&quot; data-ke-size=&quot;size26&quot;&gt;3️⃣ GameManager에 적용하기&lt;/h2&gt;
&lt;p data-end=&quot;2109&quot; data-start=&quot;2061&quot; data-ke-size=&quot;size16&quot;&gt;이제 GameManager를 Singleton&amp;lt;T&amp;gt;를 상속받아 구현하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1759751886705&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;using UnityEngine;

public class GameManager : Singleton&amp;lt;GameManager&amp;gt;
{
    public int score = 0;

    protected override void Awake()
    {
        base.Awake(); // 반드시 호출해야 함
        Debug.Log(&quot;GameManager Awake&quot;);
    }

    public void AddScore(int value)
    {
        score += value;
        Debug.Log($&quot;현재 점수: {score}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2549&quot; data-start=&quot;2462&quot; data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &lt;b&gt;GameManager는 자동으로 싱글톤 동작을 가지게 됩니다.&lt;/b&gt;&lt;br /&gt;씬에 하나만 존재하도록 보장되고, 필요하면 자동 생성까지 이루어집니다.&lt;/p&gt;
&lt;hr data-end=&quot;2554&quot; data-start=&quot;2551&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2577&quot; data-start=&quot;2556&quot; data-ke-size=&quot;size26&quot;&gt;4️⃣ 다른 스크립트에서 사용하기&lt;/h2&gt;
&lt;pre id=&quot;code_1759751907245&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class Player : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag(&quot;Coin&quot;))
        {
            GameManager.Instance.AddScore(10);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-end=&quot;2878&quot; data-start=&quot;2791&quot; data-ke-size=&quot;size16&quot;&gt;GameManager.Instance로 전역 접근이 가능하죠.&lt;br /&gt;씬에 GameManager가 없어도 자동 생성되므로, 따로 등록할 필요가 없습니다.&lt;/p&gt;
&lt;hr data-end=&quot;2883&quot; data-start=&quot;2880&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;2901&quot; data-start=&quot;2885&quot; data-ke-size=&quot;size26&quot;&gt;5️⃣ 확장 가능한 구조&lt;/h2&gt;
&lt;p data-end=&quot;3018&quot; data-start=&quot;2903&quot; data-ke-size=&quot;size16&quot;&gt;이 추상 클래스 기반 구조의 장점은, AudioManager, UIManager, SpawnManager 등 다양한 매니저 클래스가 모두 같은 로직을 상속받아 일관성 있게 동작한다는 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1759751936466&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public class AudioManager : Singleton&amp;lt;AudioManager&amp;gt;
{
    public void PlayBGM(string name)
    {
        Debug.Log($&quot;BGM 재생: {name}&quot;);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-end=&quot;3186&quot; data-start=&quot;3183&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3199&quot; data-start=&quot;3188&quot; data-ke-size=&quot;size26&quot;&gt;⚠️ 주의할 점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-end=&quot;3354&quot; data-start=&quot;3200&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;3254&quot; data-start=&quot;3200&quot;&gt;MonoBehaviour 기반 싱글톤이므로, &lt;b&gt;Unity 씬 안에서만 유효&lt;/b&gt;합니다.&lt;/li&gt;
&lt;li data-end=&quot;3302&quot; data-start=&quot;3255&quot;&gt;base.Awake()를 호출하지 않으면 인스턴스 초기화가 되지 않습니다.&lt;/li&gt;
&lt;li data-end=&quot;3354&quot; data-start=&quot;3303&quot;&gt;멀티스레드 환경이나 순수 C# 유틸 클래스에는 일반 C# 싱글톤을 사용하는 게 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;3359&quot; data-start=&quot;3356&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-end=&quot;3368&quot; data-start=&quot;3361&quot; data-ke-size=&quot;size26&quot;&gt;✅ 정리&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;항목설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;3566&quot; data-start=&quot;3370&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;3566&quot; data-start=&quot;3398&quot;&gt;
&lt;tr data-end=&quot;3432&quot; data-start=&quot;3398&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3403&quot; data-start=&quot;3398&quot;&gt;패턴&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3432&quot; data-start=&quot;3403&quot;&gt;Singleton (제네릭 기반 추상 클래스)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3466&quot; data-start=&quot;3433&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3438&quot; data-start=&quot;3433&quot;&gt;장점&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3466&quot; data-start=&quot;3438&quot;&gt;중복 제거, 일관된 싱글톤 관리, 자동 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3516&quot; data-start=&quot;3467&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3474&quot; data-start=&quot;3467&quot;&gt;사용 예&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3516&quot; data-start=&quot;3474&quot;&gt;GameManager, AudioManager, UIManager 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;3566&quot; data-start=&quot;3517&quot;&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3523&quot; data-start=&quot;3517&quot;&gt;주의점&lt;/td&gt;
&lt;td data-col-size=&quot;sm&quot; data-end=&quot;3566&quot; data-start=&quot;3523&quot;&gt;반드시 base.Awake() 호출, MonoBehaviour 한정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>개발/Unity</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/119</guid>
      <comments>https://dino-dev.tistory.com/119#entry119comment</comments>
      <pubDate>Mon, 6 Oct 2025 20:59:44 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Compose phase - 어떻게 화면을 그릴까?</title>
      <link>https://dino-dev.tistory.com/118</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;Jetpack Compose는 선언형 UI 툴킷입니다.&quot; 우리는 이 말을 정말 많이 듣습니다. 단지 상태(State)를 선언하면 UI가 마법처럼 그려진다고 하죠. 그런데 그 '마법' 뒤에서는 어떤 일이 일어나고 있을까요?  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose가 UI를 화면에 표시하기까지의 과정을 이해하는 것은 단순히 지식을 넘어, 성능을 최적화하고 복잡한 UI 문제를 해결하는 데 매우 중요합니다. 오늘은 Compose의 핵심 동작 원리인 &lt;b&gt;3단계 렌더링 파이프라인(Composition, Layout, Drawing)에&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;1280&quot; data-origin-height=&quot;140&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0DKeq/btsPWp3hdyt/rHwriiXYbeGmBRcaA7SfX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0DKeq/btsPWp3hdyt/rHwriiXYbeGmBRcaA7SfX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0DKeq/btsPWp3hdyt/rHwriiXYbeGmBRcaA7SfX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0DKeq%2FbtsPWp3hdyt%2FrHwriiXYbeGmBRcaA7SfX0%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;1280&quot; height=&quot;140&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;140&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1단계: Composition - 무엇을 보여줄까?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;composition-ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byK82Y/btsPXlsM8KA/erd0NB0sY9FF6QRcz5iJ10/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byK82Y/btsPXlsM8KA/erd0NB0sY9FF6QRcz5iJ10/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byK82Y/btsPXlsM8KA/erd0NB0sY9FF6QRcz5iJ10/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/byK82Y/btsPXlsM8KA/erd0NB0sY9FF6QRcz5iJ10/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;800&quot; height=&quot;450&quot; data-filename=&quot;composition-ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;어떤 UI를 보여줄지 결정하는 단계&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 먼저 Compose는 &lt;b&gt;Composition&lt;/b&gt; 단계를 실행합니다. 이 단계에서 Compose 런타임은 우리가 작성한 @Composable 함수들을 호출합니다. 그리고 그 결과로 UI에 대한 &lt;b&gt;'설계도'를&lt;/b&gt; 만듭니다. 이 설계도는 단순한 그림이 아니라, UI의 구조와 내용을 담고 있는 데이터 트리(UI Tree)입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;최초 컴포지션 (Initial composition):&lt;/b&gt; 앱이 처음 실행될 때, Compose는 모든 관련 컴포저블 함수를 실행하여 전체 UI 트리를 구축합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;리컴포지션 (Recomposition):&lt;/b&gt; 앱의 상태가 변경되면, Compose는 똑똑하게 &lt;b&gt;해당 상태와 관련된 컴포저블 함수만 다시 호출&lt;/b&gt;하여 UI 트리의 필요한 부분만 업데이트합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심:&lt;/b&gt; 이 단계는 오직 '무엇을' 보여줄지에만 관심이 있습니다. 각 UI 요소가 화면 어디에, 어떤 크기로 그려질지는 전혀 신경 쓰지 않습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre id=&quot;code_1755514769608&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun Greeting(
    name: String
) {
    // 1. Composition 단계에서 이 Text 함수가 호출됩니다.
    // 2. &quot;Hello $name!&quot; 이라는 내용을 가진 UI 노드가
    //    UI 트리에 생성됩니다.
    Text(
        text = &quot;Hello $name!&quot;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2단계: Layout - 어디에 배치할까?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;layout-ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9Um1e/btsPUFsKt5O/bXj3YxuntW7RT4LQm1xyWk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9Um1e/btsPUFsKt5O/bXj3YxuntW7RT4LQm1xyWk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9Um1e/btsPUFsKt5O/bXj3YxuntW7RT4LQm1xyWk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/9Um1e/btsPUFsKt5O/bXj3YxuntW7RT4LQm1xyWk/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;800&quot; height=&quot;450&quot; data-filename=&quot;layout-ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;UI 요소의 크기와 위치를 결정하는 단계&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Composition 단계에서 '무엇을' 보여줄지 결정했다면, &lt;b&gt;Layout&lt;/b&gt; 단계에서는 이것들을 '어디에, 얼마나 크게' 배치할지 계산합니다. 이 과정은 모든 UI 노드에 대해 측정과 배치를 수행하며, 2개의 단계(step)로 진행됩니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;측정 (Measurement):&lt;/b&gt; 부모 노드가 자식 노드에게 &quot;이런 제약 조건 안에서 얼마나 크기가 필요하니?&quot;라고 묻습니다. 자식은 자신의 크기를 측정하여 부모에게 보고합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;배치 (Placement):&lt;/b&gt; 부모는 자식들의 크기 정보를 바탕으로, 각 자식을 자신의 좌표계 내의 정확한 x, y 위치에 배치합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심:&lt;/b&gt; Modifier.size(), Modifier.padding() 등 레이아웃과 관련된 모든 수정자는 바로 이 단계에서 작동합니다. 부모가 주는 제약 조건(Constraints)과 자식 스스로의 요구사항(Intrinsic size)이 결합하여 최종 크기와 위치가 결정됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre id=&quot;code_1755514803226&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun UserProfile(
    name: String
) {
    // 1. Row가 Text에게 &quot;얼마나 필요해?&quot;라고 묻습니다.
    // 2. Text는 &quot;Hello $name!&quot;을 표시하는데 필요한 너비와 높이를 계산해 응답합니다.
    // 3. Row는 Text를 자신의 시작점(x=0, y=0)에 배치합니다.
    Row(
        modifier = Modifier.padding(
            all = 16.dp
        )
    ) {
        Text(
            text = &quot;Hello $name!&quot;
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3단계: Drawing - 어떻게 그릴까?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;drawing-ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMWmqN/btsPXzxyGSp/wXizYXXIK2ufDUQgLaWHR1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMWmqN/btsPXzxyGSp/wXizYXXIK2ufDUQgLaWHR1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMWmqN/btsPXzxyGSp/wXizYXXIK2ufDUQgLaWHR1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dMWmqN/btsPXzxyGSp/wXizYXXIK2ufDUQgLaWHR1/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;800&quot; height=&quot;450&quot; data-filename=&quot;drawing-ezgif.com-video-to-gif-converter.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;450&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;&lt;b&gt;&quot;화면에 실제로 픽셀을 그리는 단계&quot;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 모든 UI 요소의 종류, 크기, 위치를 알게 되었습니다. 마지막 &lt;b&gt;Drawing&lt;/b&gt; 단계에서는 이 정보를 이용해 화면의 캔버스(Canvas)에 실제로 픽셀을 그립니다. 각 요소는 Modifier.drawBehind나 Modifier.border 같은 수정자를 통해 자신을 어떻게 그려야 하는지에 대한 지침을 가집니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심:&lt;/b&gt; Compose는 이 단계에서도 매우 효율적입니다. 만약 어떤 요소의 색상이나 내용이 아니라 위치만 변경되었다면, Compose는 해당 요소의 픽셀 정보를 다시 그릴 필요 없이 기존의 그래픽 데이터를 그대로 이동시켜 사용하기도 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;⚠️ 주의: 리컴포지션 루프를 피하세요 (Recomposition Loop)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;리컴포지션 루프&lt;/b&gt;란 이름 그대로 컴포저블이 스스로를 끊임없이 재호출하며 멈추지 않는 상태를 말합니다. 앱은 아무런 반응 없이 얼어붙고, CPU 사용량은 치솟게 되죠.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 현상은 &lt;b&gt;상태를 읽는(read) 단계에서 그 상태를 다시 쓰는(write) 경우&lt;/b&gt;에 발생합니다. 특히 Composition 단계뿐만 아니라 &lt;b&gt;Layout 단계에서 상태를 변경&lt;/b&gt;할 때 미묘한 루프가 발생하기 쉽습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;❌ 잘못된 예시: 형제 컴포저블의 크기로 레이아웃 변경 시도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드는 이미지의 실제 높이를 측정하여 그 아래에 있는 텍스트의 상단 패딩을 동적으로 지정하려고 합니다. 하지만 이 구조는 즉시 리컴포지션 루프를 유발합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1755515496953&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun BadExample() {
    Box {
        var imageHeightPx by remember { mutableStateOf(0) }

        Image(
            painter = painterResource(R.drawable.rectangle),
            contentDescription = &quot;I'm above the text&quot;,
            modifier = Modifier
                .fillMaxWidth()
                .onSizeChanged { size -&amp;gt;
                    // ❌ 나쁨: Layout 단계에서 측정한 값을 State에 기록합니다.
                    imageHeightPx = size.height
                }
        )
        Text(
            text = &quot;I'm below the image&quot;,
            modifier = Modifier.padding(
                // 이 Text는 imageHeightPx 상태를 읽고 있습니다.
                // 위 Image에서 이 값이 변경되면 Text가 리컴포지션되고,
                // 전체 프로세스가 다시 시작되어 루프가 발생합니다.
                top = with(LocalDensity.current) { imageHeightPx.toDp() }
            )
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 올바른 해결책: 목적에 맞는 레이아웃 사용하기 (Column)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 아래에 텍스트를 배치하는 것이 목적이라면, 복잡하게 크기를 측정하고 상태를 전달할 필요가 전혀 없습니다. 단순히 자식들을 &lt;b&gt;수직으로 배치해 주는&lt;/b&gt; &lt;b&gt;Column&lt;/b&gt; 컴포저블을 사용하면 모든 문제가 해결됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Column은 내부적으로 자식들을 하나씩 측정하고 그 순서대로 아래에 배치하는 로직을 모두 처리해 줍니다. 이것이 바로 Compose의 선언적인 힘입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1755515711000&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun GoodExample() {
    // ✅ 좋음: Column은 자식들을 수직으로 차례대로 배치합니다.
    // 복잡한 측정이나 상태 전달 없이 원하는 UI를 명확하게 선언할 수 있습니다.
    Column {
        Image(
            painter = painterResource(R.drawable.rectangle),
            contentDescription = &quot;I'm above the text&quot;,
            modifier = Modifier.fillMaxWidth()
        )

        Text(
            text = &quot;I'm below the image&quot;
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 더 복잡한 layout을 그려야한다면 ConstraintLayout을 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ConstraintLayout을 사용하면 한 요소의 아래(bottom)에 다른 요소의 위(top)를 연결하는 식으로 관계를 직접 선언할 수 있어, 리컴포지션 루프 없이 원하는 UI를 구성할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 보는 Compose 화면은 아래와 같은 과정을 거쳐 완성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상태 변경(State Change) &amp;rarr; Composition (무엇을?) &amp;rarr; Layout (어디에?) &amp;rarr; Drawing (어떻게?) &amp;rarr; 화면 표시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 단계는 항상 같은 순서로 진행되며, 하나의 프레임을 완성하는 파이프라인처럼 작동합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;왜 이것을 알아야 할까요?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;성능 최적화:&lt;/b&gt; 무거운 계산을 컴포저블 함수 안에 직접 넣으면, 리컴포지션이 일어날 때마다 해당 계산이 반복되어 버벅거림을 유발할 수 있습니다. 이는 &lt;b&gt;Composition&lt;/b&gt; 단계에 부담을 주는 행위입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;커스텀 레이아웃:&lt;/b&gt; Layout 컴포저블을 사용하여 자신만의 커스텀 레이아웃을 만들려면, 측정(Measure)과 배치(Place)가 어떻게 동작하는지 반드시 이해해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;디버깅:&lt;/b&gt; UI가 예상치 못한 위치에 그려지거나 크기를 차지한다면, 그것은 &lt;b&gt;Layout&lt;/b&gt; 단계의 제약 조건(Constraints)과 관련이 있을 가능성이 높습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 Compose의 '마법'이 조금은 더 명확하게 보이시나요? 이 세 가지 단계를 기억한다면, 여러분은 더 효율적이고 정교한 UI를 자신 있게 만들어 나갈 수 있을 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/118</guid>
      <comments>https://dino-dev.tistory.com/118#entry118comment</comments>
      <pubDate>Mon, 18 Aug 2025 20:16:22 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Compose Side-Effect</title>
      <link>https://dino-dev.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack Compose의 세계에서 컴포저블(Composable) 함수는 &lt;b&gt;'순수함수(Pure Function)'&lt;/b&gt; 와 같습니다. 동일한 상태(State)가 주어지면 항상 동일한 UI를 그려내는 &lt;b&gt;'설계도'&lt;/b&gt; 역할을 하죠. 이 설계도는 상태가 변경될 때마다, 때로는 초당 수십 번씩 다시 그려질 수 있습니다. 이 과정을 &lt;b&gt;리컴포지션(Recomposition)&lt;/b&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;만약 설계도를 그릴 때마다 스낵바를 띄우거나, 네트워크 요청을 보내거나, 데이터베이스에 데이터를 저장한다면 어떻게 될까요?   앱은 순식간에 통제 불능 상태에 빠지고, 수많은 버그와 성능 저하를 일으킬 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 현실의 앱은 '바깥 세상'과 소통해야만 합니다. 이처럼 컴포저블의 범위를 벗어나 앱의 상태를 변경하거나 외부와 통신하는 모든 작업을 &lt;b&gt;사이드 이펙트(Side Effect, 부수 효과)&lt;/b&gt; 라고 합니다. Compose는 이 위험하고도 필수적인 작업을 안전하게 처리할 수 있도록, 마치 '특별 허가를 받은 통로'와 같은 강력한 API들을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 이 '안전한 탈출구', 사이드 이펙트 핸들러들을 하나하나 깊이 있게 파헤쳐 보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LaunchedEffect: 컴포지션 시점의 비동기 작업 실행기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LaunchedEffect는 컴포저블이 처음 화면에 나타나거나(Composition), 특정 &lt;b&gt;key&lt;/b&gt; 값이 변경되었을 때 &lt;b&gt;코루틴&lt;/b&gt;을 실행하는 가장 기본적이고 널리 사용되는 사이드 이펙트 핸들러입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?&lt;/b&gt;  &lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;화면이 처음 보일 때 초기 데이터를 로딩해야 할 때&lt;/li&gt;
&lt;li&gt;특정 상태가 true가 되었을 때 스낵바를 표시하거나 다른 화면으로 이동하고 싶을 때&lt;/li&gt;
&lt;li&gt;ViewModel의 StateFlow나 Channel 같은 이벤트를 구독하고 특정 UI 액션을 수행해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;key의 비밀: 재시작의 열쇠&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;key가 변경되면:&lt;/b&gt; 진행 중이던 기존 코루틴은 **취소(Cancel)**되고, 새로운 key와 함께 내부 코드가 처음부터 &lt;b&gt;재시작&lt;/b&gt;됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;key가 변경되지 않으면:&lt;/b&gt; 리컴포지션이 일어나도 코루틴은 재시작되지 않고 계속 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;key1 = Unit 또는 key1 = true:&lt;/b&gt; key 값이 절대 변하지 않으므로, 컴포저블이 처음 생성될 때 딱 한 번만 실행되고 화면에서 사라질 때까지 유지됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755438481087&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 에러 상태에 따라 스낵바 보여주기

@Composable
fun MyScreen(
    viewModel: MyViewModel = hiltViewModel()
) {
    val uiState by viewModel.uiState.collectAsState()
    val scaffoldState = rememberScaffoldState()

    LaunchedEffect(key1 = uiState.errorMessage) {
        uiState.errorMessage?.let { message -&amp;gt;
            scaffoldState.snackbarHostState.showSnackbar(message)
            viewModel.consumeErrorMessage()
        }
    }
    
    Scaffold(scaffoldState = scaffoldState) { /* ... */ }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;rememberCoroutineScope: 사용자 액션에 응답하는 비동기 작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 버튼을 클릭하는 등, 리컴포지션과 상관없는 &lt;b&gt;이벤트 콜백&lt;/b&gt; 내에서 코루틴을 실행하고 싶을 때 사용합니다. remember를 통해 컴포저블의 생명주기에 연결된 CoroutineScope를 얻어올 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?&lt;/b&gt;  &lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버튼 클릭 시 네트워크 요청을 보내고 로딩 인디케이터를 표시해야 할 때&lt;/li&gt;
&lt;li&gt;사용자가 목록을 아래로 당겨 새로고침할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755438531303&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 버튼 클릭 시 프로필 업데이트

@Composable
fun ProfileEditScreen(viewModel: ProfileViewModel) {
    val scope = rememberCoroutineScope()
    var isLoading by remember { mutableStateOf(false) }

    Button(
        onClick = {
            scope.launch {
                isLoading = true
                try {
                    viewModel.updateProfile()
                } finally {
                    isLoading = false
                }
            }
        },
        enabled = !isLoading
    ) { /* ... */ }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DisposableEffect: 리소스 생성과 정리가 쌍으로 필요할 때&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포저블이 화면에 보이는 동안 리스너를 등록하고, 화면에서 사라질 때 해제해야 하는 경우처럼 &lt;b&gt;진입(Setup)과 퇴장(Clean-up)&lt;/b&gt; 시점의 작업이 명확하게 쌍을 이룰 때 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?&lt;/b&gt;  &lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LifecycleObserver를 등록하고 해제할 때&lt;/li&gt;
&lt;li&gt;BroadcastReceiver를 등록하고 해제할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755438616839&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 화면의 생명주기 이벤트 감지

@Composable
fun AnalyticsLogger(lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current) {
    DisposableEffect(key1 = lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event -&amp;gt;
            if (event == Lifecycle.Event.ON_RESUME) logScreenView(&quot;enter&quot;)
        }
        lifecycleOwner.lifecycle.addObserver(observer)

        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;rememberUpdatedState: 최신 값을 참조해야 하는 장기 실행 작업&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LaunchedEffect(Unit)처럼 단 한 번만 실행되는 코루틴 내부에서, &lt;b&gt;재시작 없이&lt;/b&gt; 컴포저블의 최신 상태나 람다를 참조해야 할 때 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?&lt;/b&gt; ⏱️&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일정 시간 후 사라지는 화면에서, 사라지기 직전에 최신 상태를 기반으로 액션을 취해야 할 때&lt;/li&gt;
&lt;li&gt;계속 실행되는 이펙트 안에서 부모로부터 받은 람다(onClick, onEvent)를 호출해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755438788492&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 최신 람다를 호출하는 타임아웃

@Composable
fun LandingScreen(onTimeout: () -&amp;gt; Unit) {
    val updatedOnTimeout by rememberUpdatedState(onTimeout)
    
    LaunchedEffect(Unit) {
        delay(3000L)
        updatedOnTimeout()
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SideEffect: Compose 상태를 외부와 공유하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 리컴포지션이 &lt;b&gt;성공적으로 완료될 때마다&lt;/b&gt; 코드를 실행하고 싶을 때 사용합니다. Compose가 관리하지 않는 외부 객체나 라이브러리와 Compose의 상태를 공유하는 용도로 주로 사용됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?&lt;/b&gt;  &lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 상태를 외부 분석(Analytics) 라이브러리에 전달할 때&lt;/li&gt;
&lt;li&gt;외부 콜백을 통해 UI 상태를 외부에 알릴 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755438834676&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 분석 라이브러리에 현재 상태 전달

@Composable
fun UserProfileScreen(user: User) {
    // user 정보가 바뀔 때마다 리컴포지션이 성공적으로 끝나면 호출됩니다.
    SideEffect {
        Analytics.setUserProperty(&quot;userName&quot;, user.name)
        Analytics.setUserProperty(&quot;userTier&quot;, user.tier)
    }

    Text(&quot;사용자: ${user.name}&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;produceState: 외부 스트림을 Compose State로 변환&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flow, LiveData, RxJava 등 외부의 비동기적인 데이터 스트림을 Compose가 즉시 사용할 수 있는 State로 변환해줍니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?&lt;/b&gt;  &lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Flow나 다른 데이터 스트림을 구독하여 UI에 표시해야 할 때&lt;/li&gt;
&lt;li&gt;네트워크나 데이터베이스로부터 오는 데이터 스트림을 State로 만들고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755438905760&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Flow를 State로 변환하기

@Composable
fun &amp;lt;T&amp;gt; Flow&amp;lt;T&amp;gt;.collectAsStateWithLifecycle(
    initialValue: T
): State&amp;lt;T&amp;gt; {
    // 위치 정보 Flow를 구독하여 State&amp;lt;Location&amp;gt;으로 변환합니다.
    return produceState(initialValue = initialValue) {
        // 이 블록은 코루틴 스코프입니다.
        // Flow가 새로운 값을 방출(emit)하면 `value`가 업데이트됩니다.
        this@collectAsStateWithLifecycle.collect {
            value = it
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;snapshotFlow: Compose State를 외부 Flow로 변환&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;produceState의 정반대 역할을 합니다. Compose의 State 객체를 &lt;b&gt;Kotlin Flow&lt;/b&gt; 로 변환하여, debounce, map, filter 등 풍부한 Flow 연산자를 활용할 수 있게 해줍니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?  &lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자가 텍스트 입력을 멈췄을 때만 검색 API를 호출하고 싶을 때 (debounce)&lt;/li&gt;
&lt;li&gt;스크롤 위치(LazyListState) 같은 Compose 상태의 변화를 ViewModel에서 감지하고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755438978405&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 입력이 멈추면 자동 검색

@Composable
fun SearchScreen(
    viewModel: SearchViewModel
) {
    var text by remember { mutableStateOf(&quot;&quot;) }

    TextField(
        value = text,
        onValueChange = { text = it }
    )

    // text State가 변경될 때마다 Flow가 새로운 값을 방출합니다.
    LaunchedEffect(
        key1 = Unit
    ) {
        snapshotFlow { text }
            .debounce(500) // 500ms 동안 입력이 없으면
            .filter { it.isNotEmpty() } // 비어있지 않으면
            .collect { query -&amp;gt;
                viewModel.search(query) // 검색 실행
            }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;derivedStateOf: 불필요한 리컴포지션을 막는 계산된 State&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;derivedStateOf는 하나 이상의 State 객체를 기반으로 새로운 State를 계산하여 만듭니다. 핵심은, &lt;b&gt;내부 State가 바뀔 때마다 리컴포지션을 유발하는 것이 아니라, 계산된 최종 결과가 바뀔 때만 리컴포지션을 유발한다는 것&lt;/b&gt;입니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;언제 사용할까요?&lt;/b&gt; ✨&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 상태에 따라 UI의 가시성이 결정되는데, 이 계산이 자주 일어날 때&lt;/li&gt;
&lt;li&gt;LazyListState의 스크롤 위치처럼 매우 빈번하게 변경되는 상태를 기반으로 다른 UI를 제어할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1755439017087&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 스크롤 위치에 따라 '맨 위로' 버튼 표시

@Composable
fun MyList() {
    val listState = rememberLazyListState()

    // firstVisibleItemIndex는 스크롤할 때마다 계속 바뀝니다.
    // 하지만 &quot;맨 위로 가기&quot; 버튼의 가시성은 (index &amp;gt; 0)의 결과, 즉 true/false가
    // 바뀔 때만 변경되면 됩니다. derivedStateOf가 바로 이 역할을 합니다.
    val showButton by remember {
        derivedStateOf {
            listState.firstVisibleItemIndex &amp;gt; 0
        }
    }

    Box {
        LazyColumn(
            state = listState
        ) { 
            // ... list content ...
        }

        AnimatedVisibility(
            visible = showButton,
            modifier = Modifier.align(Alignment.BottomCenter)
        ) {
            Button(
                onClick = { /* ... */ }
            ) {
                Text(text = &quot;맨 위로&quot;)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 우리는 Compose가 제공하는 거의 모든 종류의 사이드 이펙트 핸들러를 깊이 있게 탐험했습니다. 각 핸들러는 저마다의 명확한 역할과 목적을 가지고 있으며, 이들을 올바르게 사용하는 것이 안정적이고 성능 좋은 Compose 앱을 만드는 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/117</guid>
      <comments>https://dino-dev.tistory.com/117#entry117comment</comments>
      <pubDate>Sun, 17 Aug 2025 22:58:00 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Lifecycle - Composition의 탄생부터 소멸까지</title>
      <link>https://dino-dev.tistory.com/116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack Compose를 사용하다 보면 우리는 상태가 바뀌면 UI가 알아서 업데이트된다는 마법을 경험합니다. 이 마법의 비밀은 Compose가 컴포저블의 &lt;b&gt;생명주기(Lifecycle)&lt;/b&gt;를 어떻게 관리하는지에 숨어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 컴포저블이 컴포지션(Composition)에 들어와서 사라지기까지, 그 여정을 함께 따라가 보겠습니다. 이 글을 이해하면 왜 remember를 써야 하고, 언제 LaunchedEffect를 사용해야 하는지 명확한 그림을 그릴 수 있게 될 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Composition?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Composition은 &lt;b&gt;Compose가 컴포저블 함수들을 실행하여 UI를 설명하는 트리 구조를 만드는 과정&lt;/b&gt;입니다. 이 트리는 UI의 '설계도'와 같습니다. 이 설계도가 만들어지고, 변경되고, 사라지는 것이 바로 컴포저블의 생명주기입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Composable의 생명주기&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bT6heJ/btsPL0bKxYN/qCeyrGQE5zszczMPMqUqBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bT6heJ/btsPL0bKxYN/qCeyrGQE5zszczMPMqUqBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bT6heJ/btsPL0bKxYN/qCeyrGQE5zszczMPMqUqBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbT6heJ%2FbtsPL0bKxYN%2FqCeyrGQE5zszczMPMqUqBK%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;1191&quot; height=&quot;512&quot; data-origin-width=&quot;1191&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포저블의 생명주기는 컴포지션 안에서 세 가지 주요 이벤트를 겪습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;컴포지션에 진입 (Entering the Composition)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;언제?&lt;/b&gt; Compose가 컴포저블 함수를 처음 호출하여 UI 설계도에 추가할 때입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;무슨 일이?&lt;/b&gt; 해당 컴포저블과 그 자식들이 처음으로 화면에 그려지기 위한 모든 단계를 거칩니다. (Composition -&amp;gt; Layout -&amp;gt; Drawing)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심:&lt;/b&gt; 이 시점에 초기 데이터를 불러오거나 애니메이션을 시작하는 등의 '일회성' 작업을 수행할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;재구성 (Recomposition)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;언제?&lt;/b&gt; 컴포저블이 관찰하는 State가 변경되었을 때입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;무슨 일이?&lt;/b&gt; Compose는 &lt;b&gt;변경된 State를 사용하는 컴포저블만&lt;/b&gt; 다시 호출하여 UI 설계도를 업데이트합니다. 이때 Compose는 똑똑하게 변경이 필요한 부분만 다시 그리므로 매우 효율적입니다. 재구성은 매우 빈번하게, 심지어 애니메이션의 매 프레임마다 발생할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심:&lt;/b&gt; 컴포저블 함수는 재구성을 염두에 두고 항상 &lt;b&gt;멱등성(idempotent)&lt;/b&gt;을 가지도록, 즉 여러 번 호출되어도 부작용(Side-effect) 없이 동일한 결과를 내도록 작성해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;컴포지션에서 제거 (Leaving the Composition)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;언제?&lt;/b&gt; 더 이상 UI 설계도에 해당 컴포저블이 포함되지 않을 때입니다. if 문의 조건이 false가 되거나 다른 화면으로 이동하는 경우가 해당됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;무슨 일이?&lt;/b&gt; 컴포저블은 폐기(disposed)되며, 관련된 모든 리소스가 정리됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;핵심:&lt;/b&gt; 이 시점에 사용 중이던 리스너를 해제하거나 관찰을 중단하는 등, 메모리 누수를 방지하기 위한 '뒷정리' 작업을 수행해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;생명주기에 맞게 동작하는 코드 작성하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이 생명주기 이벤트에 맞춰 어떻게 코드를 실행할 수 있을까요?&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;remember&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;remember는 재구성이 일어나도 값을 잃어버리지 않도록 &lt;b&gt;메모리(기억)&lt;/b&gt;를 제공하는 마법 같은 함수입니다. remember가 없다면 재구성 시 모든 변수가 초기화되어 상태를 저장할 수 없습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754640586825&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// remember가 없다면, 재구성될 때마다 count는 0으로 초기화됩니다.
var count by remember { mutableIntStateOf(0) }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Side-Effect&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포저블의 생명주기 이벤트 시점에 특정 작업을 실행하려면 Side-Effect 핸들러를 사용해야 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;LaunchedEffect&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LaunchedEffect는 key가 변경되지 않는 한 컴포지션에 진입할 때 딱 한 번만 실행됩니다. 데이터를 불러오거나, 초기 설정을 하기에 완벽합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754640641649&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LaunchedEffect(Unit) { // key로 Unit을 주면 한 번만 실행됨
    viewModel.loadData()
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DisposableEffect&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DisposableEffect는 컴포지션에서 제거될 때 onDispose 블록이 호출되어 뒷정리를 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754640690880&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DisposableEffect(Unit) {
    // ... 리스너 등록 등 자원을 사용하는 작업 ...
    onDispose {
        // ... 리스너 해제 등 뒷정리 작업 ...
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose의 생명주기는 전통적인 안드로이드 생명주기보다 훨씬 단순하고 예측 가능합니다. 단순하게 진입, 재구성, 제거 라는 3가지 이벤트를 기억하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 remember를 통해 재구성에도 살아남는 상태를 만들고, LaunchedEffect와 DisposableEffect 같은 핸들러를 통해 각 생명주기 시점에 필요한 작업을 안전하게 실행하는 것. 이것이 바로 Compose 생명주기를 이해하고 활용하는 핵심입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/116</guid>
      <comments>https://dino-dev.tistory.com/116#entry116comment</comments>
      <pubDate>Fri, 8 Aug 2025 17:13:03 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Stateful vs Stateless</title>
      <link>https://dino-dev.tistory.com/115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack Compose로 UI를 만드는 것은 레고(Lego) 블록으로 멋진 작품을 조립하는 것과 같습니다. Text, Button, Image 같은 기본 블록들을 가지고 우리는 무엇이든 만들 수 있죠.&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;Stateful&lt;/b&gt;과 &lt;b&gt;Stateless&lt;/b&gt;의 세계로 떠나보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일회용 부품?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신입 개발자 '제이'는 오늘 팀의 칭찬을 한 몸에 받았습니다. '클릭하면 하트가 채워지는' 좋아요 버튼을 완벽하게 만들어냈기 때문이죠. 스스로 상태를 관리하는 이 똑똑한 버튼 덕분에 코드는 아주 간결했습니다.&lt;/p&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;b&gt;일회용 부품&lt;/b&gt;을 만들고 있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 멋지게 동작하던 코드가 재사용성이라는 벽에 부딪히는 순간, 우리는 더 나은 설계에 대해 고민하게 됩니다. &lt;b&gt;Stateful&lt;/b&gt;과 &lt;b&gt;Stateless&lt;/b&gt; 컴포넌트 설계법을 통해 다시는 이런 곤경에 빠지지 않는 방법을 알아봅시다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Stateful&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제이가 처음 만든 '좋아요 버튼' 컴포넌트는 &lt;b&gt;자신의 상태를 remember를 통해 직접 만들고 관리&lt;/b&gt;하는 구조였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754536476176&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun StatefulLikeButton(modifier: Modifier = Modifier) {
    // '좋아요' 여부(isLiked)를 스스로 기억하고 관리한다.
    var isLiked by remember { mutableStateOf(false) }

    IconButton(
        onClick = { isLiked = !isLiked },
        modifier = modifier
    ) {
        Icon(
            imageVector = if (isLiked) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
            contentDescription = &quot;좋아요&quot;
        )
    }
}&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;h2 data-ke-size=&quot;size26&quot;&gt;Stateless&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제이는 선임 개발자의 조언에 따라 자신의 버튼에서 '상태'와 'UI'를 분리하기로 결심합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;바보 컴포넌트 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;바보 컴포넌트&lt;/b&gt;를 만들라니 조금 이상하죠? 컴포넌트 스스로는 아무것도 할 수 없고 외부의 도움으로 동작하도록 만드는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, 상태를 전혀 갖지 않고 오직 외부에서 시키는 대로 그리기만 하는 &lt;b&gt;Stateless&lt;/b&gt; 컴포넌트를 만듭니다. 필요한 모든 데이터는 파라미터로 전달받고, 클릭 이벤트는 람다 함수를 통해 외부에 알리기만 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754536657848&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun StatelessLikeButton(
    isLiked: Boolean,          // 1. UI를 그리는 데 필요한 데이터를 외부에서 받는다.
    onLikeClick: () -&amp;gt; Unit,   // 2. 클릭 이벤트를 외부로 전달할 통로만 만든다.
    modifier: Modifier = Modifier
) {
    IconButton(
        onClick = onLikeClick,
        modifier = modifier
    ) { // 3. 클릭 시 전달받은 람다를 호출
        Icon(
            imageVector = if (isLiked) Icons.Filled.Favorite else Icons.Filled.FavoriteBorder,
            contentDescription = &quot;좋아요&quot;
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;상태를 부모 컴포넌트에게 위임하기(State Hoisting)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 '바보'가 된 버튼에게 상태를 전달하고 제어할 '똑똑한' 부모가 필요합니다. 부모 컴포저블을 만들고, 상태 관리를 그곳으로 &lt;b&gt;끌어올립니다(Hoisting)&lt;/b&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1754536763464&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun ArticleScreen() {
    // 1. 상태(isUserLiked)를 부모 컴포넌트가 직접 소유하고 관리한다.
    var isUserLiked by remember { mutableStateOf(false) }

    Column {
        Text(&quot;Compose 설계의 모든 것&quot;)
        // ... (본문 내용)

        // 2. Stateless 자식에게 상태와 이벤트 핸들러를 전달한다.
        StatelessLikeButton(
            isLiked = isUserLiked,          // 상태는 아래로 내려주고 (State Down)
            onLikeClick = {                 // 이벤트는 위로 올려받는다 (Events Up)
                isUserLiked = !isUserLiked
            }
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조를 통해 데이터의 흐름은 &lt;b&gt;'위에서 아래로(State Down)'&lt;/b&gt;, 이벤트의 흐름은 **'아래에서 위로(Events Up)'**라는 명확한 단방향으로 정리되었습니다. 이것이 바로 &lt;b&gt;상태 호이스팅&lt;/b&gt; 패턴의 핵심입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;더 고도화 하면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전까지는 좋아요 여부와 클릭했을 때 동작만 파라미터로 전달 받았는데 아이콘 이미지도 받는다면 재사용성이 극대화 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754537273292&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun IconToggleButton(
    toggled: Boolean,
    onToggle: () -&amp;gt; Unit,
    toggledIcon: ImageVector,
    untoggledIcon: ImageVector,
    modifier: Modifier = Modifier,
    contentDescription: String? = null,
) {
    IconButton(
        onClick = onToggle,
        modifier = modifier
    ) {
        Icon(
            imageVector = if (toggled) toggledIcon else untoggledIcon,
            contentDescription = contentDescription
        )
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 더이상 토글 관련된 기능을 가진 버튼 UI를 매번 만들지 않고 재사용해서 사용할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754537480909&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 북마크 기능을 위한 부모 컴포저블
@Composable
fun BookmarkFeature() {
    // '북마크' 상태를 관리한다
    var isBookmarked by remember { mutableStateOf(false) }

    // 똑같은 IconToggleButton을 '북마크' 기능으로 재사용!
    IconToggleButton(
        toggled = isBookmarked,
        onToggle = {
            isBookmarked = !isBookmarked
            // server.saveToBookmark(...)  &amp;lt;-- 북마크 관련 로직 호출
        },
        toggledIcon = Icons.Filled.Bookmark,      // '북마크 On' 아이콘 전달
        untoggledIcon = Icons.Filled.BookmarkBorder, // '북마크 Off' 아이콘 전달
        contentDescription = &quot;북마크&quot;
    )
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;쉬운 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 IconToggleButton을 아주 쉽게 테스트할 수 있습니다. 이제 IconToggleButton이 정말 재사용 가능한지, 그리고 &lt;b&gt;올바르게 동작하는지&lt;/b&gt; 검증하고 싶습니다. 그는 단순히 모양만 테스트하는 것을 넘어, &quot;버튼을 클릭했을 때, 우리가 전달한 onToggle 람수 함수가 정말로 실행되는가?&quot;를 확인하는 테스트를 작성합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754537855364&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
fun `IconToggleButton을_클릭하면_onToggle_람다가_호출된다`() {
    // 1. Given (준비): 람다가 호출되었는지 확인할 추적용 Boolean 변수를 만든다.
    var isToggled = false

    composeTestRule.setContent {
        IconToggleButton(
            toggled = false,
            // 2. When (행동): 버튼이 클릭되면, 추적용 변수의 값을 true로 변경하는 람다를 전달한다.
            onToggle = { isToggled = true },
            toggledIcon = Icons.Filled.Favorite,
            untoggledIcon = Icons.Filled.FavoriteBorder,
            contentDescription = &quot;좋아요 버튼&quot;
        )
    }

    // `contentDescription`으로 버튼을 찾아 클릭 동작을 수행한다.
    composeTestRule.onNodeWithContentDescription(&quot;좋아요 버튼&quot;).performClick()

    // 3. Then (검증): 람다가 호출되어 isToggled 변수의 값이 true로 바뀌었는지 확인한다.
    assertTrue(&quot;onToggle 람다가 호출되지 않았습니다.&quot;, isToggled)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 하나의 상태를 가진 컴포넌트가 어떻게 의도치 않게 &lt;b&gt;'일회용 부품'&lt;/b&gt;이 될 수 있는지, 그리고 간단한 설계 원칙의 적용만으로 어떻게 재사용 가능한 컴포넌트로 진화할 수 있는지 구체적인 여정을 함께했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;Stateful&lt;/b&gt; 컴포넌트와 &lt;b&gt;Stateless&lt;/b&gt; 컴포넌트가 어떻게 다른지 명확히 이해하고, &lt;b&gt;'상태 호이스팅'&lt;/b&gt;이라는 다리를 통해 이 둘을 의도적으로 분리하는 것이었습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Stateful 컴포넌트는 스스로 모든 것을 처리해 편리해 보이지만, 특정 기능과 단단히 결합되어 재사용이 어렵다는 명확한 한계를 가집니다.&lt;/li&gt;
&lt;li&gt;Stateless 컴포넌트는 상태를 직접 관리하지 않아 '바보'처럼 보일지라도, 바로 그 특징 때문에 어떤 상황에서든 재사용할 수 있는 놀라운 유연성을 갖게 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 설계된 Stateless 컴포넌트는 여러분의 가장 든든한 자산이 되어, 개발 속도를 높이고 버그는 줄여줄 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/115</guid>
      <comments>https://dino-dev.tistory.com/115#entry115comment</comments>
      <pubDate>Thu, 7 Aug 2025 12:45:17 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Compose UI Test</title>
      <link>https://dino-dev.tistory.com/114</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이제 멋진 화면을 만들고, 목록을 표시하고, 화면을 이동하는 방법까지 알게 되었습니다. 하지만 우리가 만든 코드가 앞으로도 계속 올바르게 동작할 것이라고 어떻게 보장할 수 있을까요? 디자인을 조금 바꾸거나 로직을 리팩터링 했을 때, 앱의 다른 기능이 고장 나지는 않았는지 어떻게 확인할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 눈으로 모든 화면을 클릭해보는 것은 지치고 실수하기 쉽습니다. 이럴 때 필요한 것이 바로 &lt;b&gt;자동화된 UI 테스트&lt;/b&gt;입니다. 오늘은 Jetpack Compose로 UI 테스트 코드를 작성하는 기본 방법을 알아보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;UI 테스트, 왜 귀찮은 일을 추가하면서까지 해야 할까요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느 화창한 오후, PM에게서 간단한 요청을 받습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&quot;결제 화면의 '구매하기' 버튼 색깔을 좀 더 눈에 띠는 파란색으로 바꿔주세요. 그리고 문구도 '결제하기'로 수정해 주세요.&quot;&lt;/span&gt;&lt;/p&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;'이 정도면 5분도 안 걸리지!'&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 자신만만하게 해당 Composable을 찾아 Modifier.background(Color.Blue)와 Text(&quot;결제하기&quot;)로 수정합니다. @Preview로 보니 완벽합니다. 코드를 커밋하고, 빌드하고, 배포까지 일사천리로 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그리고 다음 날 아침, 재앙이 시작됩니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고객센터에 불이 나고, 팀 메신저는 폭발합니다. 이유는 단 하나.&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;나는 가슴이 철렁 내려앉은 당신은 코드를 파헤치기 시작합니다. 원인은 황당했습니다. 버튼 스타일을 수정하면서, 실수로 clickable Modifier를 감싸고 있던 다른 Modifier 안으로 옮겨버렸고, 이로 인해 클릭 이벤트가 제대로 전달되지 않았던 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색깔과 문구만 바꿨을 뿐인데, 앱의 가장 중요한 기능인 '결제'가 막혀버렸습니다. 회사는 금전적 손실을 입었고, 사용자들은 분노했으며, 나는 동료들에게 사과하며 급하게 핫픽스를 배포해야 합니다. 그날 하루는 온전히 이 문제 해결에 소모됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;만약 UI 테스트가 있었다면?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 시나리오를 다시 돌려보겠습니다. '결제하기' 버튼을 수정한 후, 습관처럼 미리 작성해 둔 UI 테스트를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;테스트 시나리오: &quot;결제 버튼을 클릭하면, 결제 확인 화면으로 이동해야 한다.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1754364305988&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Test
fun `결제_버튼을_클릭하면_확인화면으로_이동한다`() {
    // 1. Given: 결제 화면을 띄운다.
    // 2. When: '결제하기' 버튼을 찾아 클릭한다.
    // 3. Then: '결제 확인'이라는 텍스트가 화면에 나타나는지 검증한다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이런 간단한 UI 테스트가 있었다면 회사는 금전적 손실을 입지 않았을 것이고, 사용자들은 분노하지 않았을 것이고, 동료들에게 사과하러 다니는 나는 없었을 것이고 핫픽스를 하지도 않았을 것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;UI 테스트는 '보험'이자 '안전망'입니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 UI 테스트를 하는 이유는 단순히 버그를 찾기 위함만이 아닙니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;자신감을 얻기 위해:&lt;/b&gt; &quot;이 코드를 고쳐도 다른 곳이 망가지지 않을 거야&quot;라는 확신을 줍니다. 레거시 코드나 복잡한 로직을 두려움 없이 개선할 수 있는 자유를 줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;시간을 아끼기 위해:&lt;/b&gt; 매번 모든 기능을 손으로 테스트하는 끔찍한 반복 작업을 자동화된 로봇에게 맡기는 것입니다. 테스트 코드 작성에 1시간을 투자하면, 미래의 수십, 수백 시간의 수동 테스트와 버그 수정 시간을 아낄 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;팀과 평화롭게 지내기 위해:&lt;/b&gt; 내가 수정한 코드가 동료의 코드를 망가뜨리는 일을 방지하고, 반대로 동료의 수정으로부터 내 코드를 보호합니다. 서로를 비난하는 대신, 테스트 코드가 조용히 문제를 알려줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UI 테스트는 귀찮은 추가 업무가 아닙니다.&lt;/b&gt; 프로 개발자로서 나의 결과물을 책임지고, 미래의 나에게 닥칠 재앙을 막아주는 가장 든든하고 현명한 &lt;b&gt;안전장치&lt;/b&gt;인 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;테스트 환경 설정하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose UI 테스트를 작성하려면 먼저 필요한 라이브러리를 추가해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app/build.gradle.kts 파일의 dependencies 블록에 다음을 추가하세요. (프로젝트를 처음 생성할 때 이미 추가되어 있을 수 있습니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1754365514946&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// UI 테스트를 위한 라이브러리 (androidTestImplementation)
androidTestImplementation(&quot;androidx.compose.ui:ui-test-junit4:1.8.3&quot;) // 버전을 확인하세요

// 디버그 빌드에서만 테스트 매니페스트를 사용하기 위함
debugImplementation(&quot;androidx.compose.ui:ui-test-manifest:1.8.3&quot;)&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;아마도 최신 프로젝트에서는 이렇게 직접 version을 명시하지 않고 bom 형태를 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.android.com/develop/ui/compose/bom&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.android.com/develop/ui/compose/bom&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1754365870412&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;재료명세서 사용 &amp;nbsp;|&amp;nbsp; Jetpack Compose &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;이 페이지는 Cloud Translation API를 통해 번역되었습니다. 재료명세서 사용 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose 재료명세서 (BOM)를 사용하면 BOM&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/develop/ui/compose/bom&quot; data-og-url=&quot;https://developer.android.com/develop/ui/compose/bom?hl=ko&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ZcWZW/hyZvn8fQYy/IMt0yTftuk53ZSOazdpMqK/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676&quot;&gt;&lt;a href=&quot;https://developer.android.com/develop/ui/compose/bom&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/develop/ui/compose/bom&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ZcWZW/hyZvn8fQYy/IMt0yTftuk53ZSOazdpMqK/img.png?width=1201&amp;amp;height=676&amp;amp;face=0_0_1201_676');&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;재료명세서 사용 &amp;nbsp;|&amp;nbsp; Jetpack Compose &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 페이지는 Cloud Translation API를 통해 번역되었습니다. 재료명세서 사용 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose 재료명세서 (BOM)를 사용하면 BOM&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 코드는 app/src/androidTest/java/ 디렉터리 안에 작성합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compose 테스트의 3가지 핵심 요소&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose 테스트는 대부분 아래 3단계를 따릅니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;찾기: 테스트하려는 UI 요소(노드)를 화면에서 찾습니다.&lt;/li&gt;
&lt;li&gt;검증: 해당 노드가 원하는 상태(예: 화면에 보이는지, 텍스트가 맞는지)인지 확인합니다.&lt;/li&gt;
&lt;li&gt;행동: 해당 노드에 특정 동작(예: 클릭, 텍스트 입력)을 수행합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정을 코드로 구현하는 방법을 알아봅시다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;테스트 룰(Rule)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 Compose 테스트의 시작점입니다. 테스트 환경을 설정하고 Composable을 렌더링 하는 역할을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754370283517&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import androidx.compose.ui.test.junit4.createComposeRule
import org.junit.Rule

class MyComposeTest {
    @get:Rule
    val composeTestRule = createComposeRule()
    // ... 테스트 코드
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요소 찾기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;composeTestRule을 통해 다양한 조건으로 노드를 찾을 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;onNodeWithText(): 특정 텍스트를 가진 노드를 찾습니다.&lt;/li&gt;
&lt;li&gt;onNodeWithContentDescription(): Image나 IconButton의 contentDescription으로 찾습니다.&lt;/li&gt;
&lt;li&gt;onNodeWithTag(): 테스트를 위한 전용 태그(testTag)로 노드를 찾는 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1754370348259&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Composable 코드에 testTag 추가하기
Text(
    text = &quot;Hello&quot;,
    modifier = Modifier.testTag(&quot;GreetingText&quot;)
)

// 테스트 코드에서 testTag로 찾기
composeTestRule.onNodeWithTag(&quot;GreetingText&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검증과 행동&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드를 찾았다면, 이제 그 노드가 올바른지 확인하고 동작을 시킬 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;주요 검증 함수 (Assertions)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;assertIsDisplayed(): 화면에 보이는지 확인합니다.&lt;/li&gt;
&lt;li&gt;assertExists(): 화면에 보이지 않더라도 노드 트리에 존재하는지 확인합니다.&lt;/li&gt;
&lt;li&gt;assertIsEnabled() / assertIsNotEnabled(): 활성화/비활성화 상태인지 확인합니다.&lt;/li&gt;
&lt;li&gt;assertTextEquals(&quot;...&quot;): 텍스트 내용이 일치하는지 확인합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;주요 행동 함수 (Actions)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;performClick(): 클릭합니다.&lt;/li&gt;
&lt;li&gt;performTextInput(&quot;...&quot;): 텍스트를 입력합니다.&lt;/li&gt;
&lt;li&gt;performScrollTo(): 특정 위치로 스크롤합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1754370513561&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createComposeRule
import org.junit.Rule
import org.junit.Test

class CounterAppTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun counter_app_test() {
        // 1. Given (준비): 테스트할 Composable을 화면에 렌더링합니다.
        composeTestRule.setContent {
            CounterApp()
        }

        // 2. When (행동): '더하기' 버튼을 찾아 클릭합니다.
        composeTestRule.onNodeWithTag(&quot;AddButton&quot;)
            .performClick()
        composeTestRule.onNodeWithTag(&quot;AddButton&quot;)
            .performClick()

        // 3. Then (검증): 카운터 텍스트가 '2'로 변경되었는지 확인합니다.
        composeTestRule.onNodeWithTag(&quot;CounterText&quot;)
            .assertTextEquals(&quot;클릭 횟수: 2&quot;)
    }

    @Composable
    fun CounterApp() {
        var count by remember { mutableIntStateOf(0) }
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Text(
                text = &quot;클릭 횟수: $count&quot;,
                modifier = Modifier.testTag(&quot;CounterText&quot;)
            )
            Button(
                onClick = { count++ },
                modifier = Modifier.testTag(&quot;AddButton&quot;)
            ) {
                Text(&quot;더하기&quot;)
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 테스트는 CounterApp을 실행하고, '더하기' 버튼을 두 번 누른 뒤, 텍스트가 &quot;클릭 횟수: 2&quot;로 올바르게 표시되는지 자동으로 검증해 줍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 블로그를 통해서 Compose UI Test의 기본을 알아봤습니다. 앞으로 테스트 코드 작성을 습관화한다면 버그 걱정 없이 훨씬 더 안정적이고 품질 좋은 앱을 만들 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/114</guid>
      <comments>https://dino-dev.tistory.com/114#entry114comment</comments>
      <pubDate>Tue, 5 Aug 2025 14:10:06 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) LazyColumn</title>
      <link>https://dino-dev.tistory.com/113</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;연락처&amp;nbsp;목록,&amp;nbsp;SNS&amp;nbsp;피드,&amp;nbsp;상품&amp;nbsp;리스트,&amp;nbsp;설정&amp;nbsp;화면까지...&amp;nbsp;앱을&amp;nbsp;만들다&amp;nbsp;보면&amp;nbsp;'목록'을&amp;nbsp;표시해야&amp;nbsp;하는&amp;nbsp;경우는&amp;nbsp;셀&amp;nbsp;수&amp;nbsp;없이&amp;nbsp;많습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose를&amp;nbsp;처음&amp;nbsp;배울&amp;nbsp;때&amp;nbsp;Column&amp;nbsp;안에&amp;nbsp;for문을&amp;nbsp;돌려&amp;nbsp;UI를&amp;nbsp;추가하는&amp;nbsp;방법을&amp;nbsp;떠올릴&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;하지만&amp;nbsp;아이템이&amp;nbsp;10개,&amp;nbsp;20개를&amp;nbsp;넘어&amp;nbsp;100개,&amp;nbsp;1000개가&amp;nbsp;된다면&amp;nbsp;어떻게&amp;nbsp;될까요?&amp;nbsp;앱은&amp;nbsp;심하게&amp;nbsp;버벅이다가&amp;nbsp;결국&amp;nbsp;비정상&amp;nbsp;종료될지도&amp;nbsp;모릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 Compose에서 목록을 효과적으로 보여주기 위한 방법에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;왜 Column이 아닌 LazyColumn?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 둘의 차이를 이해하는 것이 가장 중요합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Column&lt;/b&gt;: Column 안에 있는 &lt;b&gt;모든 자식 Composable을 한 번에&lt;/b&gt; 메모리에 올리고 화면에 그립니다. 1000개의 아이템이 있다면 1000개 전부를 렌더링하려고 시도하죠. 이는 엄청난 메모리 낭비와 성능 저하를 일으킵니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;LazyColumn&lt;/b&gt;: 이름의 'Lazy(게으른)'에서 알 수 있듯, &lt;b&gt;지금 당장 화면에 보이는 아이템만&lt;/b&gt; 렌더링합니다. 사용자가 스크롤을 내리면, 화면 밖으로 사라지는 아이템은 메모리에서 재활용하고 새로 나타날 아이템만 그립니다. 이는 RecyclerView의 동작 원리와 같습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Column은 1000페이지짜리 책을 한 번에 모두 인쇄하는 것과 같고, LazyColumn은 웹 브라우저가 화면에 보이는 부분만 로딩하는 것과 같습니다. 당연히 후자가 훨씬 효율적이죠!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LazyColumn 기본 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LazyColumn의 사용법은 매우 직관적입니다. LazyColumn의 컨텐츠 블록 안에서 items라는 특별한 함수를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754363250740&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable

@Composable
fun SimpleList() {
    LazyColumn {
        // 100개의 아이템을 가진 리스트
        items(100) { index -&amp;gt;
            Text(text = &quot;아이템 #$index&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 복잡한 예제를 살펴 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754363279797&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 데이터 클래스 정의
data class User(val id: Int, val name: String)

// 2. 아이템 하나의 UI를 담당할 Composable
@Composable
fun UserCard(user: User) {
    Row(modifier = Modifier.padding(16.dp)) {
        Image(
            painter = painterResource(id = R.drawable.ic_profile),
            contentDescription = &quot;Profile Picture&quot;
        )
        Spacer(modifier = Modifier.width(8.dp))
        Text(text = user.name)
    }
}

// 3. LazyColumn과 연결
@Composable
fun UserList(users: List&amp;lt;User&amp;gt;) {
    LazyColumn {
        items(items = users) { user -&amp;gt;
            UserCard(user = user)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 앱에서는 보통 데이터 클래스의 리스트를 다루게 됩니다. 각 아이템의 UI를 별도의 Composable 함수로 분리하는 것이 좋은 습관입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;성능 최적화 하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LazyColumn에서는 Compose의 성능 최적화를 위해서 key를 제공하고 있습니다. 이 key를 이용한다면 더욱 더 매끄러운 앱을 만들 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754363370222&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;items(
    items = users,
    // 각 아이템에 고유하고 안정적인 키를 제공합니다.
    key = { user -&amp;gt; user.id }
) { user -&amp;gt;
    UserCard(user = user)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;key를 왜 사용해야 할까요?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트의 데이터가 변경(추가, 삭제, 순서 변경)될 때, Compose는 어떤 아이템이 어떤 것인지 구별해야 합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;key가 없으면:&lt;/b&gt; Compose는 아이템의 &lt;b&gt;위치&lt;/b&gt;를 기준으로 판단합니다. 리스트 맨 앞에 아이템이 추가되면, Compose는 모든 아이템이 한 칸씩 밀렸다고 생각하고 전부 다시 그리는 비효율적인 작업을 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;key가 있으면:&lt;/b&gt; Compose는 user.id와 같은 고유한 key를 보고 아이템을 식별합니다. 리스트가 바뀌어도 &quot;아, id가 5인 아이템은 그대로 있구나. 다시 그릴 필요 없겠네.&quot;라고 판단하여 &lt;b&gt;꼭 필요한 부분만 업데이트&lt;/b&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;key 사용의 장점:&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;성능 향상:&lt;/b&gt; 불필요한 재구성(Recomposition)을 방지합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;상태 유지:&lt;/b&gt; 아이템이 가진 내부 상태(remember로 관리되는)가 유지됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애니메이션 최적화:&lt;/b&gt; 아이템 추가/삭제 시 애니메이션이 더 자연스러워집니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LazyColumn의 items에는 항상 고유하고 안정적인 key를 제공하는 습관을 들이세요. 보통 데이터의 id를 사용합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/113</guid>
      <comments>https://dino-dev.tistory.com/113#entry113comment</comments>
      <pubDate>Tue, 5 Aug 2025 12:12:15 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Modifier</title>
      <link>https://dino-dev.tistory.com/112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack Compose를 사용하다 보면 거의 모든 Composable 함수 끝에 modifier = Modifier... 코드가 따라붙는 것을 보셨을 겁니다. 처음에는 그저 &quot;UI를 꾸미는 건가?&quot; 싶지만, 사실 Modifier는 그 이상의 역할을 하는 Compose의 핵심 요소입니다.&lt;br /&gt;이번 내용에서 Modifier가 무엇인지, 어떻게 작동하는지, 그리고 왜 중요한지에 대해 깊이 파고들어 보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Modifier란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifier는 이름 그대로 Composable을 &lt;b&gt;수정(Modify)&lt;/b&gt;하는 역할을 합니다. Composable의 크기, 모양, 여백, 배경색을 바꾸는 것부터 클릭, 스크롤, 드래그와 같은 사용자 입력을 처리하는 것까지, UI 요소에 대한 거의 모든 '부가 속성'을 설정하는 데 사용됩니다.&lt;br /&gt;&lt;br /&gt;Modifier가&amp;nbsp;없다면&amp;nbsp;Text는&amp;nbsp;그저&amp;nbsp;글자일&amp;nbsp;뿐이고,&amp;nbsp;Box는&amp;nbsp;투명한&amp;nbsp;사각형일&amp;nbsp;뿐입니다.&amp;nbsp;Modifier를&amp;nbsp;통해&amp;nbsp;비로소&amp;nbsp;우리가&amp;nbsp;원하는&amp;nbsp;모습과&amp;nbsp;동작을&amp;nbsp;갖춘&amp;nbsp;UI&amp;nbsp;요소로&amp;nbsp;거듭나는&amp;nbsp;것이죠.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Modifier 사용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifier의 가장 큰 특징은 여러 수정을 &lt;b&gt;체인(Chain) 형태&lt;/b&gt;로 이어 붙일 수 있다는 점입니다. 마치 레고 블록을 조립하듯, 필요한 기능들을 순서대로 연결하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754304923992&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Composable
fun ModifierExample() {
    Text(
        text = &quot;Hello, Modifier!&quot;,
        modifier = Modifier
            .padding(16.dp) // 1. 바깥쪽에 16dp 여백을 준다.
            .background(Color.Yellow) // 2. 노란색 배경을 칠한다.
            .fillMaxWidth() // 3. 너비를 꽉 채운다.
            .clickable { /* 텍스트 클릭 시 동작 */ } // 4. 클릭 이벤트를 추가한다.
    )
}&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;1126&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RhOmO/btsPFhFejeC/ukgovrrJzqlc7uLmN41n9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RhOmO/btsPFhFejeC/ukgovrrJzqlc7uLmN41n9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RhOmO/btsPFhFejeC/ukgovrrJzqlc7uLmN41n9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRhOmO%2FbtsPFhFejeC%2FukgovrrJzqlc7uLmN41n9K%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;1126&quot; height=&quot;276&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 Modifier는 점(.)을 이용해 코드를 읽는 것만으로 어떤 속성들이 적용되었는지 명확하게 파악할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Modifier에서 순서가 중요합니다.&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifier를 사용할 때 반드시 기억해야 할 단 하나의 규칙이 있다면, 바로 &lt;b&gt;순서가 중요하다&lt;/b&gt;는 것입니다. 각 Modifier는 자신보다 먼저 선언된 Modifier가 적용된 결과물 위에 적용됩니다.&lt;br /&gt;&lt;br /&gt;아래&amp;nbsp;두&amp;nbsp;코드의&amp;nbsp;결과를&amp;nbsp;비교해&amp;nbsp;볼까요?&lt;/p&gt;
&lt;pre id=&quot;code_1754305094292&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Box(
    modifier = Modifier
        .padding(20.dp) // 1. 먼저 20dp의 여백(투명)을 만든다.
        .background(Color.Blue) // 2. 그 결과물에 파란 배경을 칠한다.
        .size(100.dp)
)&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;446&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brxCTm/btsPGpCssJa/2l2pgCW7ZywaWlKZck2ao0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brxCTm/btsPGpCssJa/2l2pgCW7ZywaWlKZck2ao0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brxCTm/btsPGpCssJa/2l2pgCW7ZywaWlKZck2ao0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrxCTm%2FbtsPGpCssJa%2F2l2pgCW7ZywaWlKZck2ao0%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;446&quot; height=&quot;480&quot; data-origin-width=&quot;446&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1754305160055&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Box(
    modifier = Modifier
        .background(Color.Blue) // 1. 먼저 파란 배경을 칠한다.
        .padding(20.dp) // 2. 그 결과물 안쪽에 20dp 여백을 만든다.
        .size(100.dp)
)&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;462&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ8gYc/btsPGpCss5q/K4Jjhzsx8ufuasJTBRm4h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ8gYc/btsPGpCss5q/K4Jjhzsx8ufuasJTBRm4h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ8gYc/btsPGpCss5q/K4Jjhzsx8ufuasJTBRm4h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ8gYc%2FbtsPGpCss5q%2FK4Jjhzsx8ufuasJTBRm4h1%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;462&quot; height=&quot;506&quot; data-origin-width=&quot;462&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 &lt;b&gt;순서를 어떻게 정하느냐에 따라 UI가 완전히 달라질 수 있으므로&lt;/b&gt; 항상 적용 순서를 염두에 두어야 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그 외 Modifier 함수들&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifier는 정말 종류가 많지만, 개발할 때 특히 자주 사용하는 것들은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;크기와 관련된 Modifier
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;size(size: Dp): 가로, 세로 크기를 지정합니다.&lt;/li&gt;
&lt;li&gt;fillMaxWidth(), fillMaxHeight(), fillMaxSize(): 가능한 최대 너비, 높이, 또는 전체 크기를 채웁니다.&lt;/li&gt;
&lt;li&gt;width(width: Dp), height(height: Dp): 너비나 높이만 개별적으로 지정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여백과 관련된 Modifier
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;padding(all: Dp): 모든 방향에 동일한 내부 여백을 줍니다.&lt;/li&gt;
&lt;li&gt;padding(horizontal: Dp, vertical: Dp): 수평, 수직 여백을 개별적으로 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모양과 관련된 Modifier
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;background(color: Color): 배경색을 칠합니다.&lt;/li&gt;
&lt;li&gt;border(width: Dp, color: Color): 테두리를 그립니다.&lt;/li&gt;
&lt;li&gt;clip(shape: Shape): Composable을 특정 모양(예: CircleShape)으로 잘라냅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;동작과 관련된 Modifier
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;clickable { ... }: 클릭 이벤트를 처리합니다.&lt;/li&gt;
&lt;li&gt;scrollable(...): 스크롤 동작을 가능하게 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;레이아웃 가중치 (Row/Column 전용)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;weight(weight: Float): Row나 Column 내에서 차지하는 공간의 비율을 설정합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifier는 Jetpack Compose에서 레이아웃을 만들고, 스타일을 입히고, 생명을 불어넣는 가장 기본적이면서도 강력한 도구입니다. 처음에는 순서가 헷갈릴 수 있지만, 몇 번만 직접 코드를 작성하며 순서를 바꿔보면 그 원리를 금방 터득할 수 있을 겁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/112</guid>
      <comments>https://dino-dev.tistory.com/112#entry112comment</comments>
      <pubDate>Mon, 4 Aug 2025 20:02:40 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) 상태(state)</title>
      <link>https://dino-dev.tistory.com/111</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 지금까지 Text, Button, Image, Column, Row 등을 이용해서 화면에 정적인 UI를 그리는 법에 대해 소개했습니다. 하지만 앱은 사용자와 상호작용하며 UI가 계속 변화하게 됩니다. Compose에서는 상태(state)라는 핵심 개념을 이용해서 이것을 가능하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;상태(state)란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서 상태는 &lt;b&gt;시간이 지남에 따라 변할 수 있는 모든 값&lt;/b&gt;을 의미합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;체크박스의 체크 여부(boolean)&lt;/li&gt;
&lt;li&gt;사용자가 입력한 텍스트(String)&lt;/li&gt;
&lt;li&gt;버튼을 누른 횟수(Int)&lt;/li&gt;
&lt;li&gt;서버에서 받아온 데이터 목록(List &amp;lt;Data&amp;gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 모든 것이 상태가 될 수 있습니다. Compose에서는 상태가 변경되는 것을 감지하면 해당 상태를 사용하는 UI 부분을 자동으로 다시 그려줍니다. 이 과정을 &lt;b&gt;Recomposition(재구성)&lt;/b&gt;이라고 부릅니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compose는 상태가 변경되는 것을 어떻게 감지할까?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서는 mutableStateOf와 remember라는 두 가지 핵심 함수를 사용해서 변경을 감지합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutableStateOf(): Compose가 변화를 추적할 수 있는 특별한 상태 객체를 만드는 함수&lt;/li&gt;
&lt;li&gt;remember { }: Compose가 재구성될 때 값이 초기화되지 않고 기억되도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1754300994179&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment

@Composable
fun CounterApp() {
    // 1. mutableStateOf로 '상태'를 만들고, remember로 기억합니다.
    // 'by' 키워드를 사용하면 .value 없이 값을 바로 쓸 수 있어 편리합니다.
    var count by remember { mutableStateOf(0) }

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(text = &quot;버튼을 누른 횟수: $count&quot;)

        Button(onClick = {
            // 2. 버튼을 누르면 상태(count)의 값이 변경됩니다.
            count++
        }) {
            Text(text = &quot;클릭하세요!&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif-1b1e2a3e287aaf.webp&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1778&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doxKyI/btsPHGqiLWc/kwifinnMkQdDIREvl3KqQ1/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doxKyI/btsPHGqiLWc/kwifinnMkQdDIREvl3KqQ1/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doxKyI/btsPHGqiLWc/kwifinnMkQdDIREvl3KqQ1/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoxKyI%2FbtsPHGqiLWc%2FkwifinnMkQdDIREvl3KqQ1%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;379&quot; height=&quot;842&quot; data-filename=&quot;ezgif-1b1e2a3e287aaf.webp&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;1778&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var&amp;nbsp;count&amp;nbsp;by&amp;nbsp;remember&amp;nbsp;{&amp;nbsp;mutableStateOf(0)&amp;nbsp;}:&amp;nbsp;count라는&amp;nbsp;이름의&amp;nbsp;상태&amp;nbsp;변수를&amp;nbsp;선언하고&amp;nbsp;초기값을&amp;nbsp;0으로&amp;nbsp;설정했습니다.&amp;nbsp;remember로&amp;nbsp;감싸주었기&amp;nbsp;때문에,&amp;nbsp;count&amp;nbsp;값이&amp;nbsp;1,&amp;nbsp;2,&amp;nbsp;3으로&amp;nbsp;변해도&amp;nbsp;화면이&amp;nbsp;재구성될&amp;nbsp;때&amp;nbsp;다시&amp;nbsp;0으로&amp;nbsp;돌아가지&amp;nbsp;않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count++:&amp;nbsp;버튼이&amp;nbsp;클릭되면&amp;nbsp;count&amp;nbsp;상태의&amp;nbsp;값이&amp;nbsp;1&amp;nbsp;증가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose는&amp;nbsp;count가&amp;nbsp;변경된&amp;nbsp;것을&amp;nbsp;감지하고,&amp;nbsp;count를&amp;nbsp;사용하고&amp;nbsp;있는&amp;nbsp;Text(text&amp;nbsp;=&amp;nbsp;&quot;...&quot;)&amp;nbsp;부분을&amp;nbsp;자동으로&amp;nbsp;다시&amp;nbsp;그립니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;원시타입용 상태 홀더&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아마도 mutableStateOf(0)를 사용했다면 &lt;b&gt;Prefer mutableIntStateOf instead of mutableStateOf&lt;/b&gt; 워닝을 만나게 될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서는 Int, Long, Float, Double에 대한 원시타입용 상태 홀더를 제공하고 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutableIntStateOf(초기값):&amp;nbsp;Int&amp;nbsp;타입을&amp;nbsp;위한&amp;nbsp;상태&lt;/li&gt;
&lt;li&gt;mutableLongStateOf(초기값):&amp;nbsp;Long&amp;nbsp;타입을&amp;nbsp;위한&amp;nbsp;상태&lt;/li&gt;
&lt;li&gt;mutableFloatStateOf(초기값): Float 타입을 위한 상태&lt;/li&gt;
&lt;li&gt;mutableDoubleStateOf(초기값):&amp;nbsp;Double&amp;nbsp;타입을&amp;nbsp;위한&amp;nbsp;상태&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1754301810323&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 일반적인 방법
val countGeneric by remember { mutableStateOf(0) }

// 2. 원시 타입 특화 방법
val countPrimitive by remember { mutableIntStateOf(0) }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 코드의 사용법은 완전히 동일하지만, 내부적으로는 중요한 차이가 있습니다. 바로 &lt;b&gt;박싱(Boxing)&lt;/b&gt;의 유무입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;박싱(Boxing)이란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java와&amp;nbsp;Kotlin의&amp;nbsp;세계에서&amp;nbsp;Int,&amp;nbsp;Float&amp;nbsp;같은&amp;nbsp;원시&amp;nbsp;타입은&amp;nbsp;객체(Object)가&amp;nbsp;아닙니다.&amp;nbsp;하지만&amp;nbsp;mutableStateOf&amp;lt;T&amp;gt;와&amp;nbsp;같은&amp;nbsp;제네릭(Generic)&amp;nbsp;함수는&amp;nbsp;모든&amp;nbsp;타입을&amp;nbsp;다루기&amp;nbsp;위해&amp;nbsp;내부적으로&amp;nbsp;객체(Any?)로&amp;nbsp;값을&amp;nbsp;다룹니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때,&amp;nbsp;원시&amp;nbsp;타입인&amp;nbsp;Int를&amp;nbsp;객체&amp;nbsp;타입인&amp;nbsp;Integer로&amp;nbsp;포장하는&amp;nbsp;과정이&amp;nbsp;발생하는데,&amp;nbsp;이것을&amp;nbsp;박싱이라고&amp;nbsp;합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;mutableStateOf(0):&amp;nbsp;Int&amp;nbsp;값&amp;nbsp;0이&amp;nbsp;Integer&amp;nbsp;객체로&amp;nbsp;박싱되어&amp;nbsp;저장됩니다.&lt;/li&gt;
&lt;li&gt;mutableIntStateOf(0):&amp;nbsp;Int&amp;nbsp;값&amp;nbsp;0이&amp;nbsp;박싱&amp;nbsp;과정&amp;nbsp;없이&amp;nbsp;원시&amp;nbsp;타입&amp;nbsp;그대로&amp;nbsp;저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;박싱은 아주 작은 메모리 할당과 처리 비용을 유발합니다. 앱의 상태가 몇 개 안 되고 가끔 업데이트된다면 이 차이는 무시해도 좋을 만큼 미미합니다.&lt;br /&gt;하지만 상태 변경이 매우 잦은 UI(예: 복잡한 애니메이션, 실시간으로 변하는 그래프, 스크롤에 따라 계속 계산되는 값)에서는 이런 작은 오버헤드가 쌓여 앱의 버벅거림(Jank)을 유발할 수 있습니다.&lt;br /&gt;mutableIntStateOf와&amp;nbsp;같은&amp;nbsp;특화된&amp;nbsp;상태&amp;nbsp;홀더는&amp;nbsp;이&amp;nbsp;박싱을&amp;nbsp;피함으로써&amp;nbsp;불필요한&amp;nbsp;메모리&amp;nbsp;할당을&amp;nbsp;줄여&amp;nbsp;성능을&amp;nbsp;극대화합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/111</guid>
      <comments>https://dino-dev.tistory.com/111#entry111comment</comments>
      <pubDate>Mon, 4 Aug 2025 19:06:28 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) @Preview</title>
      <link>https://dino-dev.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Android 개발을 하다 보면 작은 UI 수정 하나를 확인하기 위해서 적으면 몇 초에서 많으면 몇십 분씩 빌드를 기다리는 일이 너무 번거롭고 지겹습니다. Jetpack Compose는 이런 것을 해결하기 위해 @Preview 어노테이션을 제공합니다. @Preview는 앱을 빌드해서 확인하지 않고 Android Studio에서 Compose UI가 어떻게 보일지 즉시 렌더링 해주는 기능입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Preview 사용법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Preview의 사용법은 놀라울 정도로 간단합니다. 그저 @Composeable 함수 위에 @Preview를 추가하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754295038546&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview

// UI를 구성하는 Composable 함수
@Composable
fun Greeting(name: String) {
    Text(text = &quot;Hello, $name!&quot;)
}

// 위 Composable을 미리보기 위한 또 다른 Composable 함수
@Preview(showBackground = true) // Preview를 위한 어노테이션 추가
@Composable
fun GreetingPreview() {
    // 앱의 테마를 적용하면 더 정확한 미리보기가 가능합니다.
    MyApplicationTheme {
        Greeting(&quot;Android with Preview&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 @Preview를 추가하는 순간 Android Studio의 Split View에 Greeting Compose UI가 렌더링 된 모습을 볼 수 있습니다. 코드를 수정하면 잠시 후 Preview 화면도 자동으로 업데이트됩니다. 만약 업데이트가 되지 않는다면 빌드를 하면 됩니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;@Preview에 showBackground = true를 넣으면 Preview 화면에 배경을 추가해줘서 한눈에 보기 좋습니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;208&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wdx9c/btsPE2uJZU2/4pLkC4VvowoXR6UNIV4lqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wdx9c/btsPE2uJZU2/4pLkC4VvowoXR6UNIV4lqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wdx9c/btsPE2uJZU2/4pLkC4VvowoXR6UNIV4lqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwdx9c%2FbtsPE2uJZU2%2F4pLkC4VvowoXR6UNIV4lqk%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;622&quot; height=&quot;208&quot; data-origin-width=&quot;622&quot; data-origin-height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;@Preview 파라미터&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Preview에는 다양한 파라미터를 사용해서 Preview 화면을 더욱 다채롭게 만들 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;name&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;name을 사용하면 Preview에 이름을 지정할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754295433885&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Preview(name = &quot;기본 인사말&quot;)
@Composable
fun GreetingPreview() { ... }

@Preview(name = &quot;긴 이름일 경우&quot;)
@Composable
fun LongNameGreetingPreview() {
    Greeting(&quot;Android Jetpack Compose is Awesome&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;uiMode&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱에서 다크모드를 지원한다면 Preview에서 바로 다크모드 화면을 확인할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754295516232&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import android.content.res.Configuration

@Preview(name = &quot;라이트 모드&quot;)
@Composable
fun LightModePreview() { ... }

@Preview(name = &quot;다크 모드&quot;, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun DarkModePreview() { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;device&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Preview에서 다양한 형태의 device를 지원합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754295699181&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반적인 스마트폰
@Preview(device = Devices.PHONE)
@Composable
fun PhonePreview() { ... }

// 태블릿
@Preview(device = Devices.TABLET)
@Composable
fun TabletPreview() { ... }

// 폴드
@Preview(device = Devices.FOLDABLE)
@Composable
fun FoldablePreview() { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;locale&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다국어 테스트도 Preview에서 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754295858615&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Preview(name = &quot;한국어&quot;, locale = &quot;ko&quot;)
@Composable
fun KoreanPreview() { ... }

@Preview(name = &quot;영어 (미국)&quot;, locale = &quot;en-US&quot;)
@Composable
fun EnglishPreview() { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;커스텀 @Preview 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 @Preview를 별칭을 지어서 만들 수도 있고 여러개의 @Preview를 묶어서 만들 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1754295999621&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 여러 Preview 설정을 담은 커스텀 어노테이션 정의
@Preview(name = &quot;라이트 모드&quot;, showBackground = true)
@Preview(name = &quot;다크 모드&quot;, uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true)
annotation class ThemePreviews

// 2. 간편하게 사용하기
@ThemePreviews
@Composable
fun MyComponentPreview() {
    MyApplicationTheme {
        Greeting(&quot;Android with Preview&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 @ThemePreviews 어노테이션 하나만 붙이면 라이트/다크 모드 미리 보기가 한 번에 생성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Preview는&amp;nbsp;단순히&amp;nbsp;코드를&amp;nbsp;시각화하는&amp;nbsp;것을&amp;nbsp;넘어,&amp;nbsp;우리의&amp;nbsp;개발&amp;nbsp;사이클을&amp;nbsp;획기적으로&amp;nbsp;단축시키고&amp;nbsp;다양한&amp;nbsp;환경에서의&amp;nbsp;UI&amp;nbsp;품질을&amp;nbsp;보장하는&amp;nbsp;필수적인&amp;nbsp;도구입니다.&amp;nbsp;오늘&amp;nbsp;배운&amp;nbsp;팁들을&amp;nbsp;적극적으로&amp;nbsp;활용하여,&amp;nbsp;지루한&amp;nbsp;빌드&amp;nbsp;시간&amp;nbsp;대신&amp;nbsp;창의적인&amp;nbsp;UI&amp;nbsp;개발에&amp;nbsp;더&amp;nbsp;많은&amp;nbsp;시간을&amp;nbsp;쏟아보세요!&lt;br /&gt;&lt;br /&gt;이제&amp;nbsp;@Preview와&amp;nbsp;함께&amp;nbsp;더&amp;nbsp;빠르고&amp;nbsp;즐거운&amp;nbsp;Compose&amp;nbsp;개발을&amp;nbsp;시작할&amp;nbsp;시간입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/110</guid>
      <comments>https://dino-dev.tistory.com/110#entry110comment</comments>
      <pubDate>Mon, 4 Aug 2025 17:27:28 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Row, Column</title>
      <link>https://dino-dev.tistory.com/109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 이번 글에서는 Jetpack Compose의 가장 기본적이면서도 정말 많이 사용되는 컴포넌트인 &lt;b&gt;Row&lt;/b&gt;와 &lt;b&gt;Column&lt;/b&gt;에 대해 알아보겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 블로그 글에서 Text와 Button과 Image에 대해서 알아보았는데 이 컴포넌트들을 배치하는 방법에 대해서는 다루지 않았습니다. 또한 XML을 사용해서 UI 개발을 하던 개발자가 Compose를 처음 접한다면 &amp;ldquo;뷰(View)에서 LinearLayout을 세로/가로로 쓰던 걸 Compose에서는 뭘로 하지?&amp;rdquo;라는 생각을 할 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이때 등장하는 게 Row와 Column입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Row와 Column이란?&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Row&lt;/b&gt;: 자식 컴포넌트들을 &lt;b&gt;가로 방향(수평)으로&lt;/b&gt; 배치&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Column&lt;/b&gt;: 자식 컴포넌트들을 &lt;b&gt;세로 방향(수직)으로&lt;/b&gt; 배치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뷰(View) 시절의 LinearLayout과 개념적으로 비슷하지만, 훨씬 가볍고 선언적으로 작성할 수 있어서 더 직관적이에요.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;기본 사용법&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단한 예제를 먼저 볼까요?&lt;/p&gt;
&lt;pre class=&quot;excel&quot;&gt;&lt;code&gt;@Composable
fun SimpleRowAndColumn() {
    Column {
        Text(&quot;첫 번째 줄&quot;)
        Text(&quot;두 번째 줄&quot;)
        Text(&quot;세 번째 줄&quot;)
    }

    Row {
        Text(&quot;왼쪽&quot;)
        Text(&quot;가운데&quot;)
        Text(&quot;오른쪽&quot;)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Column은 UI를 위에서 아래로 배치하고 Row는 UI를 왼쪽에서 오른쪽으로 배치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 내가 수직으로 나열하고 싶을 때는 Column을 사용하고 수평으로 나열하고 싶다면 Row를 사용하면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Modifier로 스타일 조정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Row와 Column은 Modifier를 통해 크기, 배경, 패딩 등 다양한 스타일을 쉽게 줄 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;isbl&quot;&gt;&lt;code&gt;Column(
    modifier = Modifier
        .fillMaxWidth()
        .background(Color.LightGray)
        .padding(16.dp)
) {
    Text(&quot;첫 번째 줄&quot;)
    Text(&quot;두 번째 줄&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Arrangement와 Alignment&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조금 더 고급 설정으로는 자식 컴포넌트들의 위치를 조절할 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;horizontalArrangement&lt;/b&gt;: Row에서 수평 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;verticalArrangement&lt;/b&gt;: Column에서 수직 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;verticalAlignment&lt;/b&gt;: Row에서 수직 정렬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;horizontalAlignment&lt;/b&gt;: Column에서 수평 정렬&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 가운데 정렬을 하고 싶다면 아래와 같이 사용하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.Center
) {
    Text(&quot;가운데에 있는 텍스트&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Row와 Column 같이 활용하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 앱에서는 Row와 Column을 적절히 섞어서 다양한 화면을 만듭니다. 예를 들어, 제목과 설명을 세로로 배치하면서, 오른쪽 끝에 버튼을 두고 싶다면 이렇게 작성할 수 있어요.&lt;/p&gt;
&lt;pre class=&quot;mathematica&quot;&gt;&lt;code&gt;Row(
    modifier = Modifier.fillMaxWidth(),
    verticalAlignment = Alignment.CenterVertically,
    horizontalArrangement = Arrangement.SpaceBetween
) {
    Column {
        Text(&quot;제목&quot;, fontWeight = FontWeight.Bold)
        Text(&quot;설명 텍스트&quot;)
    }
    Button(onClick = { /* TODO */ }) {
        Text(&quot;버튼&quot;)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Row와 Column은 Compose에서 정말 자주 쓰이는 기본 레이아웃 컴포넌트입니다. 처음에는 간단하게 시작하더라도, 다양한 Modifier와 Arrangement, Alignment를 조합하면서 원하는 UI를 만들 수 있어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/109</guid>
      <comments>https://dino-dev.tistory.com/109#entry109comment</comments>
      <pubDate>Wed, 30 Jul 2025 15:56:11 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Image</title>
      <link>https://dino-dev.tistory.com/108</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack Compose에서 &lt;b&gt;이미지를 화면에 표시할 때&lt;/b&gt; 사용하는 Image 컴포넌트에 대해 소개합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱을 만들 때 사진, 그림, 아이콘 등을 보여주는 건 거의 필수 기능인데요, Compose에서는 이 과정을 어떻게 처리할 수 있을지 알아보겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Image 컴포넌트란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Image는 단순히 말해 &lt;b&gt;화면에 비트맵, 벡터, URL로 불러온 이미지 등을 보여주는 UI 컴포넌트입니다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서는 painter라는 개념을 이용해 이미지를 그리는데, 로컬 리소스뿐만 아니라 네트워크 이미지도 손쉽게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;Image(
    painter = painterResource(id = R.drawable.sample_image),
    contentDescription = &quot;샘플 이미지&quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;크기와 스타일 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Modifier를 이용하면 크기나 스타일을 쉽게 변경할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;Image(
    painter = painterResource(id = R.drawable.sample_image),
    contentDescription = &quot;샘플 이미지&quot;,
    modifier = Modifier
        .size(120.dp)
        .clip(CircleShape)
        .border(2.dp, Color.Gray, CircleShape)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;네트워크 이미지 불러오기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 리소스뿐만 아니라 URL로 이미지를 불러오기 위해서는 Coil과 같은 Image Loader 라이브러리를 사용해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;lisp&quot;&gt;&lt;code&gt;AsyncImage(
    model = &quot;&amp;lt;https://example.com/image.jpg&amp;gt;&quot;,
    contentDescription = &quot;네트워크 이미지&quot;,
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/coil-kt/coil&quot;&gt;https://github.com/coil-kt/coil&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;contentScale로 이미지 맞추기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 영역에 어떻게 채울지 결정하는 속성은 contentScale입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML에서 ImageView의 scaleType과 동일한 속성입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ContentScale.Crop: 영역을 꽉 채우도록 잘라서 보여주기&lt;/li&gt;
&lt;li&gt;ContentScale.Fit: 이미지 전체를 맞추되 비율 유지&lt;/li&gt;
&lt;li&gt;ContentScale.FillBounds: 비율 무시하고 영역에 딱 맞추기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;Image(
    painter = painterResource(id = R.drawable.sample_image),
    contentDescription = null,
    contentScale = ContentScale.Crop,
    modifier = Modifier.size(150.dp)
)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/108</guid>
      <comments>https://dino-dev.tistory.com/108#entry108comment</comments>
      <pubDate>Tue, 29 Jul 2025 14:43:41 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Button</title>
      <link>https://dino-dev.tistory.com/107</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 Jetpack Compose의 가장 기본적이면서도 사용자와 상호작용 할 수 있는 UI 컴포넌트인 Button에 대해 이야기해보려고 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Button 컴포넌트란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서 버튼은 Button() 함수를 사용해 구현합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 화면에 간단한 버튼 하나를 만들고 싶다면 이렇게 작성할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;var count by remember { mutableStateOf(0) }

Button(onClick = { count++ }) {
    Text(&quot;클릭 수: $count&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onClick 파라미터에 클릭 시 동작을 넣고, 버튼 안에는 Text를 사용해 표시할 글자를 넣습니다. XML 시절보다 훨씬 간단하고 직관적입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;버튼에 색상과 모양 추가하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose의 버튼은 다양한 속성을 통해 쉽게 스타일링할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;hsp&quot;&gt;&lt;code&gt;Button(
    onClick = { /* 클릭 시 처리 */ },
    colors = ButtonDefaults.buttonColors(
        containerColor = Color(0xFF6200EE), // 배경 색
        contentColor = Color.White // 글자 색
    ),
    shape = RoundedCornerShape(32.dp) // 모서리를 둥글게
) {
    Text(&quot;스타일 버튼&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;버튼 크기 조절하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼의 크기도 쉽게 설정할 수 있어요.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Button(
    onClick = { /* 클릭 시 처리 */ },
    modifier = Modifier
        .width(200.dp)
        .height(50.dp)
) {
    Text(&quot;큰 버튼&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;아이콘과 함께 사용하기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼에 아이콘을 추가해 사용자 경험을 높일 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;Button(onClick = { /* 클릭 시 처리 */ }) {
    Icon(
        imageVector = Icons.Default.Favorite,
        contentDescription = &quot;Favorite&quot;,
        modifier = Modifier.size(18.dp)
    )
    Spacer(modifier = Modifier.width(8.dp))
    Text(&quot;좋아요&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Modifier.clickable&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 사용하지 않고 Text에 Modifier.clickable을 사용하면 사용자의 클릭 이벤트를 받을 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;nix&quot;&gt;&lt;code&gt;Text(
    text = &quot;여기를 눌러보세요&quot;,
    modifier = Modifier.clickable { 
        println(&quot;Text 클릭됨!&quot;) 
    },
    color = Color.Blue,
    fontSize = 16.sp
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/107</guid>
      <comments>https://dino-dev.tistory.com/107#entry107comment</comments>
      <pubDate>Tue, 29 Jul 2025 11:38:26 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Text</title>
      <link>https://dino-dev.tistory.com/106</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 이번 글에서는 Jetpack Compose를 공부하시는 분들이 가장 먼저 만나게 되는 컴포넌트 중 하나인 &lt;span&gt;Text&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;Text 컴포넌트란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Text&lt;/span&gt; 컴포넌트는 화면에 글자를 보여주는 가장 기본적인 컴포넌트입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XML 시대에는 &lt;span&gt;&amp;lt;TextView&amp;gt;&lt;/span&gt;를 썼지만, Compose에서는 &lt;span&gt;Text()&lt;/span&gt; 함수를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용법도 매우 직관적입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1753687922055&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Text(text = &quot;안녕하세요, Compose!&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 한 줄만 작성해도 화면에 &lt;span&gt;&quot;안녕하세요, Compose!&quot;&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;Text의&amp;nbsp;다양한&amp;nbsp;속성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose의 &lt;span&gt;Text&lt;/span&gt;는 단순히 문자열만 출력하는 용도가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 가지 속성을 조합해 글자 스타일을 쉽게 바꿀 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1753688144103&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Text(
    text = &quot;안녕하세요, Compose!&quot;,
    color = Color.Blue, // 색상 blue
    fontSize = 20.sp, // 크기 20sp
    fontWeight = FontWeight.Bold // 볼드체
)&lt;/code&gt;&lt;/pre&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;이런 스타일을 더욱더 심화로 커스텀하기 위해서는 TextStyle을 활용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1753688202728&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Text(
    text = &quot;Compose로 스타일링!&quot;,
    style = TextStyle(
        fontSize = 18.sp,
        fontWeight = FontWeight.Medium,
        letterSpacing = 1.sp
    )
)&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;pre id=&quot;code_1753688237130&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Text(
    text = &quot;이 문장은 꽤 길어서 여러 줄에 걸쳐 보여질 거예요.&quot;,
    maxLines = 2,
    overflow = TextOverflow.Ellipsis
)&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;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 Compose Text를 사용하면 정말 간단하게 글자를 보여줄 수 있고, 여러 형태의 스타일도 쉽게 추가할 수 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/106</guid>
      <comments>https://dino-dev.tistory.com/106#entry106comment</comments>
      <pubDate>Mon, 28 Jul 2025 16:38:19 +0900</pubDate>
    </item>
    <item>
      <title>(Git) git alias로 빠르게 사용자 정보 변경하기</title>
      <link>https://dino-dev.tistory.com/105</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발을 하다 보면 같은 컴퓨터에서 여러 계정 정보로 git commit을 해야하는 경우가 있습니다. 예를 들면 회사컴퓨터에서 회사와 개인 프로젝트를 오가며 git user.name과 git user.email을 바꿔야 하는 상황이 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 복잡하게 변경하는 것 보다 git alias를 활용해서 한 줄 명령어로 빠르고 간단하게 전환할 수 있는 방법이 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;alias란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;alias는 git 명령어를 짧은 이름으로 별칭을 만들어 빠르게 실행할 수 있도록 도와주는 기능입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들면, git status를 git st 처럼 줄여 쓸 수도 있고, 복잡한 명령어도 하나의 단축어로 만들 수 있습니다. 이번 블로그에서는 복잡한 명령어를 하나의 단축어를 만드는 것을 소개합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;alias 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 git 전역 설정 파일인 ~/.gitconfig에 alias를 추가합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시처럼 회사용과 개인용을 변경하는 alias를 추가합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1753683913764&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[alias]
	set-work = &quot;!git config --global user.name dino &amp;amp;&amp;amp; git config --global user.email dino@company.com&quot;
	set-personal = &quot;!git config --global user.name sjjeong &amp;amp;&amp;amp; git config --global user.email sjjeong1225@gmail.com&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 복사해서 사용하시고 user.name 뒤에 있는 것과 user.email 뒤에 있는 것만 본인 것에 맞게 변경하시면 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;alias 사용하기&lt;/h3&gt;
&lt;pre id=&quot;code_1753683974493&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git set-work
git set-personal&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 실행해보고 cat ~/.gitconfig를 보면 [user] 영역이 내가 설정한 것으로 잘 변경된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;마무리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git alias를 이용하면 user.name과 user.email 변경하는 것 외에 평소 귀찮았던 설정 변경을 훨씬 빠르게 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 환경에 맞게 alias를 커스터마이즈해서 더 효율적인 개발 환경을 만들어보세요!&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Git</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/105</guid>
      <comments>https://dino-dev.tistory.com/105#entry105comment</comments>
      <pubDate>Mon, 28 Jul 2025 15:27:45 +0900</pubDate>
    </item>
    <item>
      <title>(Compose) Compose 소개</title>
      <link>https://dino-dev.tistory.com/104</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안드로이드 앱을 만들 때 &lt;b&gt;UI를 어떻게 설계하고 구현할까?&lt;/b&gt; 가 가장 큰 고민입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에는 TextView와 Button, ImageView와 같은 View를 XML에 정의하고, Kotlin에서 가져와 속성을 바꾸고 액션을 지정하며 화면을 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제는 더 쉽고, 현대적인 방법인 Compose를 사용해서 UI를 구현할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack Compose는 Google이 공식적으로 제공하는 선언형 UI 툴킷으로 최근 안드로이드 개발 표준으로 자리 잡고 있는 기술입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose는 &lt;b&gt;화면의 상태(state)에 따라 UI를 선언적으로 그려주는 프레임워크&lt;/b&gt; 라고 할 수 있습니다. 기존 View는 XML에 UI를 정의하고 Kotlin에서 findViewById()로 뷰를 가져와서 속성을 변경하고 액션을 지정했다면 Compose는 원하는 UI의 상태를 선언하기만 하면 Compose가 알아서 그려주는 형태입니다. 다시 말해서 UI를 어떻게 그릴 것인지 고민하는 게 아니라 무엇을 그릴 지를 고민하는 형태가 됐습니다.&lt;/p&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를 만들어 봅시다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Button(onClick = { count++ }) {
        Text(&quot;Clicked $count times&quot;)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서는 이렇게 Button과 Text를 선언하고 이것들이 어떻게 보일지 묘사하면 됩니다. count가 변경되면 UI도 알아서 업데이트됩니다. count가 변경되면서 UI를 직접 업데이트할 필요가 없습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compose는 어떻게 쓸까요?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose를 사용하기 위해 2가지를 알아야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;첫 번째로 @Composable 함수입니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;@Composable 함수는 UI를 그리기 위한 가장 작은 단위입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환값이 없는 형태의 함수에 @Composable을 붙이면 UI를 그리기 위한 형태가 됩니다. 그리고 함수 안에 Text, Button, Column과 같은 컴포넌트를 사용해 화면을 구성합니다.&lt;/p&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;@Composable
fun Greeting(name: String) {
    Text(text = &quot;Hello $name!&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;두 번째로 상태 관리입니다.&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose는 화면의 상태가 바뀔 때마다 UI를 새로 그리도록 설계되어 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;remember나 mutableStateOf 같은 것을 사용해야 합니다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;var text by remember { mutableStateOf(&quot;Hello&quot;) }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Compose의 장점은 무엇일까요?&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Compose는 XML 없이 Kotlin 코드만으로 UI를 작성할 수 있습니다.&lt;/li&gt;
&lt;li&gt;상태만 관리하면 UI가 자동으로 업데이트돼서 수동으로 업데이트했던 것보다 버그를 줄일 수 있습니다.&lt;/li&gt;
&lt;li&gt;XML과 Kotlin을 같이 관리하지 않고 Kotlin만 관리하면 되기 때문에 코드가 간결해지고 가독성이 높아집니다.&lt;/li&gt;
&lt;li&gt;@Composable 함수 단위로 UI를 구성할 수 있어 재사용성이 높습니다.&lt;/li&gt;
&lt;li&gt;@Preview를 사용해서 앱을 실행하지 않고 UI를 빠르게 확인할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마지막으로&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Jetpack Compose는 단순히 새로운 UI 도구가 아니라, &lt;b&gt;안드로이드 개발 방식 자체를 바꾸는 도구&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 낯설고 코드 스타일도 다르지만, 한 번 익숙해지면 빠른 개발 속도와 유지보수 편리함 때문에 절대 예전으로 돌아가기 어렵다는 말이 나올 정도로 강력합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/Compose</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/104</guid>
      <comments>https://dino-dev.tistory.com/104#entry104comment</comments>
      <pubDate>Thu, 24 Jul 2025 14:46:38 +0900</pubDate>
    </item>
    <item>
      <title>(Coroutine) 나는 Coroutine 동작 순서를 얼마나 잘 아는가?</title>
      <link>https://dino-dev.tistory.com/103</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;여러분들은 CoroutineRaceGuesser Game을 아시나요?&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;741&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tCgZr/btsOST6z09L/u1Y2IJkPVe8K2xWZ4rwiqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tCgZr/btsOST6z09L/u1Y2IJkPVe8K2xWZ4rwiqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tCgZr/btsOST6z09L/u1Y2IJkPVe8K2xWZ4rwiqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtCgZr%2FbtsOST6z09L%2Fu1Y2IJkPVe8K2xWZ4rwiqk%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;862&quot; height=&quot;741&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;741&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;만약 처음 보셨다면 여러분들은 이 블로그 글을 만난 것이 정말로 행운입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐면 저도 이것을 알기 전에는 제가 Kotlin Coroutine 동작 순서에 대해 잘 아는줄 알았거든요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇일 전에 제가 운영하는 스터디의 안드로이드 카카오 단체톡방에 흥미로운 링크가 공유되었습니다. 그것은 바로 Coroutine 동작 순서 퀴즈 였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 Coroutine 스터디를 5기째 운영하고 있어서 쉽게 문제를 풀 것이라 생각했는데 맘처럼 쉽게 풀리지 않아서 아직 많이 부족하고 잘 모르는구나 하고 느꼈습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이 코드는 어떤 순서로 동작하나요?&lt;/h2&gt;
&lt;pre id=&quot;code_1750992416811&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;suspend fun main() = coroutineScope {
    delay(2000)
    val value1 = async {
        delay(1000)
        println(&quot;B&quot;)
        delay(1000)
        &quot;A&quot;
    }
    println(&quot;D&quot;)
    println(value1.await())
    delay(1000)
}&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;A, B, D는 몇초의 시간을 기다리며 어떤 순서로 출력이 될까요??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답을 메모장에 작성하고 더보기를 눌러서 확인해보세요.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2초 delay&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;D&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1초 delay&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1초 delay&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1초 delay&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(done)&lt;/p&gt;
&lt;/div&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;생각했던 동작 순서와 일치하나요??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제가 쉬웠나요? 어려웠나요? 아니면 재미있고 흥미로웠나요??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 더 풀어보고 싶다면 아래 링크로 들어가서 도전해보세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marcinmoskala.com/CoroutinesRaceGuesser/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://marcinmoskala.com/CoroutinesRaceGuesser/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1750992673939&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Coroutines Guesser Game&quot; data-og-description=&quot;&quot; data-og-host=&quot;marcinmoskala.com&quot; data-og-source-url=&quot;https://marcinmoskala.com/CoroutinesRaceGuesser/&quot; data-og-url=&quot;https://marcinmoskala.com/CoroutinesRaceGuesser/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://marcinmoskala.com/CoroutinesRaceGuesser/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://marcinmoskala.com/CoroutinesRaceGuesser/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Coroutines Guesser Game&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;marcinmoskala.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코루틴 공부를 제대로 하고 싶다면?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 또한 코루틴을 제대로 알지 못하고 사용했는데 조세영님이 쓰신 코틀린 코루틴의 정석을 읽고, 이 책으로 코루틴 스터디를 5기 째 운영하며 많이 성장 했습니다. 만약 코루틴을 제대로 알고 사용하고 싶다면 이 책을 강추합니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;1200&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m5JwY/btsOTtsePH5/wHkvGBKtjQKVBt45N9I7G1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m5JwY/btsOTtsePH5/wHkvGBKtjQKVBt45N9I7G1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m5JwY/btsOTtsePH5/wHkvGBKtjQKVBt45N9I7G1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm5JwY%2FbtsOTtsePH5%2FwHkvGBKtjQKVBt45N9I7G1%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;959&quot; height=&quot;1200&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;1200&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;인프런에 온라인 강의도 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inf.run/S9Do1&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inf.run/S9Do1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1753063416161&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;코틀린 코루틴 완전 정복| 조세영 - 인프런 강의&quot; data-og-description=&quot;현재 평점 4.8점 수강생 524명인 강의를 만나보세요. 『코틀린 코루틴의 정석』 저자의 코틀린 코루틴 강의입니다. 이 강의에서는 코루틴에 대한 지식을 기초부터 핵심까지 다룹니다. 코루틴을 &quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/S9Do1&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hgIEE/hyZnkLjjPI/4kQ2jDmakAwdAoHfQ7zkt1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/geFNv/hyZnbOmrat/RLIc6WBkLkAXeqhqIcKHKk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/dAe15g/hyZnj6IION/yFiIufXCU73HiNCm1LFdm1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781&quot;&gt;&lt;a href=&quot;https://inf.run/S9Do1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/S9Do1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hgIEE/hyZnkLjjPI/4kQ2jDmakAwdAoHfQ7zkt1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/geFNv/hyZnbOmrat/RLIc6WBkLkAXeqhqIcKHKk/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/dAe15g/hyZnj6IION/yFiIufXCU73HiNCm1LFdm1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781');&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;코틀린 코루틴 완전 정복| 조세영 - 인프런 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현재 평점 4.8점 수강생 524명인 강의를 만나보세요. 『코틀린 코루틴의 정석』 저자의 코틀린 코루틴 강의입니다. 이 강의에서는 코루틴에 대한 지식을 기초부터 핵심까지 다룹니다. 코루틴을&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[본 게시물은 파트너스 활동의 일환으로 소정의 수수료를 받을 수 있습니다.]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;그 외에 다른 것들?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kotlin Coroutine 외에 Compose나 Collection 관련 게임도 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marcinmoskala.com/ModifierOrderGuesser/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://marcinmoskala.com/ModifierOrderGuesser/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1750992720512&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;ModifierOrderGuesser&quot; data-og-description=&quot;&quot; data-og-host=&quot;marcinmoskala.com&quot; data-og-source-url=&quot;https://marcinmoskala.com/ModifierOrderGuesser/&quot; data-og-url=&quot;https://marcinmoskala.com/ModifierOrderGuesser/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://marcinmoskala.com/ModifierOrderGuesser/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://marcinmoskala.com/ModifierOrderGuesser/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;ModifierOrderGuesser&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;marcinmoskala.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://marcinmoskala.com/CollectionProcessingGuesser/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://marcinmoskala.com/CollectionProcessingGuesser/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1750992727926&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CollectionProcessingGuesser&quot; data-og-description=&quot;&quot; data-og-host=&quot;marcinmoskala.com&quot; data-og-source-url=&quot;https://marcinmoskala.com/CollectionProcessingGuesser/&quot; data-og-url=&quot;https://marcinmoskala.com/CollectionProcessingGuesser/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://marcinmoskala.com/CollectionProcessingGuesser/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://marcinmoskala.com/CollectionProcessingGuesser/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;CollectionProcessingGuesser&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;marcinmoskala.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그 글에 대해 궁금한 점이 있다면 아래 카카오톡 오픈채팅에 들어와서 질문해주세요&lt;/p&gt;
&lt;figure id=&quot;og_1690530990560&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Android Kotlin Compose QnA&quot; data-og-description=&quot;&quot; data-og-host=&quot;open.kakao.com&quot; data-og-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268&quot;&gt;&lt;a href=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://open.kakao.com/o/gAJ9Dmnf&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dl9WJN/hyTtiLGRN2/mK63VONwvIrzAFD5mro5K1/img.png?width=800&amp;amp;height=400&amp;amp;face=112_196_184_268');&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;Android Kotlin Compose QnA&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;open.kakao.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개발/코틀린</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/103</guid>
      <comments>https://dino-dev.tistory.com/103#entry103comment</comments>
      <pubDate>Fri, 27 Jun 2025 11:52:11 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-45 오키나와 6 산칭</title>
      <link>https://dino-dev.tistory.com/102</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;날짜: 2025.06.15&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;/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;a href=&quot;http://hongstardive.com/&quot;&gt;http://hongstardive.com/&lt;/a&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;조류: 약함&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;휴식 시간: 113분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 시간: 13:31&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 잔압: 190bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대 수심: 22m&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다이브 시간: 42분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 시간: 14:13&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 잔압: 60bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&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;탱크 종류: Air&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;수온: 28도&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;슈트:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0109.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KlbgW/btsODS6Wkaa/ikxeNKa64ZsD3KogxzyIO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KlbgW/btsODS6Wkaa/ikxeNKa64ZsD3KogxzyIO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KlbgW/btsODS6Wkaa/ikxeNKa64ZsD3KogxzyIO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKlbgW%2FbtsODS6Wkaa%2FikxeNKa64ZsD3KogxzyIO0%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0109.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;

&lt;iframe src=&quot;https://www.youtube.com/embed/mFXRC40DPbA?si=NgGrGsLF1yIgWscu&quot; width=&quot;560&quot; height=&quot;315&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/102</guid>
      <comments>https://dino-dev.tistory.com/102#entry102comment</comments>
      <pubDate>Tue, 17 Jun 2025 12:10:02 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-44 오키나와 5 카메키치</title>
      <link>https://dino-dev.tistory.com/101</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;날짜: 2025.06.15&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;/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;a href=&quot;http://hongstardive.com/&quot;&gt;http://hongstardive.com/&lt;/a&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;조류: 약함&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;휴식 시간: 56분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 시간: 10:48&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 잔압: 190bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대 수심: 22m&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다이브 시간: 50분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 시간: 11:38&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 잔압: 40bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&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;탱크 종류: Air&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;수온: 27도&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;슈트:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0108.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EdBpP/btsOEd3IyIB/y4vQhe8VyIczs4MbhxUid0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EdBpP/btsOEd3IyIB/y4vQhe8VyIczs4MbhxUid0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EdBpP/btsOEd3IyIB/y4vQhe8VyIczs4MbhxUid0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEdBpP%2FbtsOEd3IyIB%2Fy4vQhe8VyIczs4MbhxUid0%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0108.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/101</guid>
      <comments>https://dino-dev.tistory.com/101#entry101comment</comments>
      <pubDate>Tue, 17 Jun 2025 12:08:59 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-43 오키나와 4 크루키타</title>
      <link>https://dino-dev.tistory.com/100</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;날짜: 2025.06.15&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;/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;a href=&quot;http://hongstardive.com/&quot;&gt;http://hongstardive.com/&lt;/a&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;조류: 약함&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&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;시작 시간: 9:16&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 잔압: 190bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대 수심: 22m&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다이브 시간: 36분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 시간: 9:52&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 잔압: 20bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&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;탱크 종류: Air&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;수온: 26도&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;슈트:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0107.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8O1uG/btsOCfPulTI/HiUUyhTKk5rb8Okyhk64Pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8O1uG/btsOCfPulTI/HiUUyhTKk5rb8Okyhk64Pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8O1uG/btsOCfPulTI/HiUUyhTKk5rb8Okyhk64Pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8O1uG%2FbtsOCfPulTI%2FHiUUyhTKk5rb8Okyhk64Pk%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0107.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/100</guid>
      <comments>https://dino-dev.tistory.com/100#entry100comment</comments>
      <pubDate>Tue, 17 Jun 2025 12:06:39 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-42 오키나와 3 나간누키타</title>
      <link>https://dino-dev.tistory.com/99</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;날짜: 2025.06.14&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;/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;a href=&quot;http://hongstardive.com/&quot;&gt;http://hongstardive.com/&lt;/a&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;조류: 약함&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;휴식 시간: 90분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 시간: 13:25&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 잔압: 190bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대 수심: 12m&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다이브 시간: 44분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 시간: 14:09&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 잔압: 60bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&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;탱크 종류: Air&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;수온: 26도&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;슈트:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(5mm)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0106.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgsAOB/btsODLNqFgL/UsHjHGLxmLkW4rp4E3S96K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgsAOB/btsODLNqFgL/UsHjHGLxmLkW4rp4E3S96K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgsAOB/btsODLNqFgL/UsHjHGLxmLkW4rp4E3S96K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgsAOB%2FbtsODLNqFgL%2FUsHjHGLxmLkW4rp4E3S96K%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0106.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/99</guid>
      <comments>https://dino-dev.tistory.com/99#entry99comment</comments>
      <pubDate>Tue, 17 Jun 2025 12:05:07 +0900</pubDate>
    </item>
    <item>
      <title>다이빙로그-41 오키나와 2 쿠에후키타</title>
      <link>https://dino-dev.tistory.com/98</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;날짜: 2025.06.14&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;/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;a href=&quot;http://hongstardive.com/&quot;&gt;http://hongstardive.com/&lt;/a&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;조류: 약함&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;다이빙&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;휴식 시간: 53분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 시간: 11:05&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;시작 잔압: 190bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;최대 수심: 10m&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다이브 시간: 50분&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 시간: 11:55&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;종료 잔압: 60bar&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;탱크 크기: 11L&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;탱크 종류: Air&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;수온: 26도&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;장비&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;웨이트: 6&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;kg(Belt-6kg)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;슈트:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;Wet(5mm)&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_0105.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baTh7o/btsOEQNVuGi/bwuTg3fCSEXXEk6tVJ3hR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baTh7o/btsOEQNVuGi/bwuTg3fCSEXXEk6tVJ3hR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baTh7o/btsOEQNVuGi/bwuTg3fCSEXXEk6tVJ3hR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaTh7o%2FbtsOEQNVuGi%2FbwuTg3fCSEXXEk6tVJ3hR0%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;1206&quot; height=&quot;2622&quot; data-filename=&quot;IMG_0105.PNG&quot; data-origin-width=&quot;1206&quot; data-origin-height=&quot;2622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>취미/스쿠버다이빙</category>
      <author>DinoDev</author>
      <guid isPermaLink="true">https://dino-dev.tistory.com/98</guid>
      <comments>https://dino-dev.tistory.com/98#entry98comment</comments>
      <pubDate>Tue, 17 Jun 2025 12:03:02 +0900</pubDate>
    </item>
  </channel>
</rss>