<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Gatsby Starter Blog RSS Feed]]></title><description><![CDATA[I share concise notes on what I learn while developing.]]></description><link>https://songwoody.github.io</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 19 Jan 2026 23:14:23 GMT</lastBuildDate><item><title><![CDATA[GraphQL 기본 개념 살펴보기]]></title><description><![CDATA[들어가며 Gatsby로 블로그를 운영하며 GraphQL을 가볍게 접해본 후, 그 독특한 방식이 호기심이 자극되어서 깊이 있게 공부를 시작했습니다. 쿼리를 통해 필요한 데이터만 골라 가져오는 방식이 과연 REST API…]]></description><link>https://songwoody.github.io/blog/graphql/graphql-basic-concepts-overview/</link><guid isPermaLink="false">https://songwoody.github.io/blog/graphql/graphql-basic-concepts-overview/</guid><pubDate>Fri, 09 Jan 2026 15:53:01 GMT</pubDate><content:encoded>&lt;h1 id=&quot;들어가며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%93%A4%EC%96%B4%EA%B0%80%EB%A9%B0&quot; aria-label=&quot;들어가며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;들어가며&lt;/h1&gt;
&lt;p&gt;Gatsby로 블로그를 운영하며 GraphQL을 가볍게 접해본 후, 그 독특한 방식이 호기심이 자극되어서 깊이 있게 공부를 시작했습니다. &lt;strong&gt;쿼리를 통해 필요한 데이터만 골라 가져오는 방식&lt;/strong&gt;이 과연 REST API보다 어떤 점이 좋을지 궁금했고, 공부할수록 GraphQL은 활용성이 높은 기술이라는 것을 느꼈습니다. 이번 포스트에서는 GraphQL의 핵심 개념을 살펴보겠습니다&lt;/p&gt;
&lt;h1 id=&quot;왜-graphql-이-탄생했을까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-graphql-%EC%9D%B4-%ED%83%84%EC%83%9D%ED%96%88%EC%9D%84%EA%B9%8C&quot; aria-label=&quot;왜 graphql 이 탄생했을까 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 GraphQL 이 탄생했을까?&lt;/h1&gt;
&lt;p&gt;서론에서 언급했듯이, GraphQL은 기존 &lt;strong&gt;REST API의 구조적 한계를 극복하기 위해 탄생&lt;/strong&gt;했습니다. 서비스가 복잡해질수록 REST 방식은 데이터를 주고받는 과정에서 비효율성이 커지는데, 대표적으로 두 가지 문제가 발생합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;오버페칭(Over-fetching)&lt;/strong&gt;: &lt;u&gt;필요한 것보다 더 많이 받음&lt;/u&gt;, 클라이언트에서 사용자의 &lt;code class=&quot;language-text&quot;&gt;이름&lt;/code&gt;만 필요한 상황임에도 불구하고, 서버의 엔드포인트에서 사용자의 &lt;code class=&quot;language-text&quot;&gt;주소&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;연락처&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;가입일&lt;/code&gt; 등 불필요한 정보까지 모두 내려받는 현상입니다. 이는 네트워크 대역폭을 낭비하고 모바일 환경에서 성능 저하의 원인이 됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;언더페칭(Under-fetching)&lt;/strong&gt;: &lt;u&gt;필요한 것보다 적게 받아 여러 번 요청함&lt;/u&gt;, 하나의 화면을 구성하기 위해 &lt;strong&gt;여러 개의 엔드포인트를 호출&lt;/strong&gt;해야 하는 상황입니다. 예를 들어 게시글 내용과 작성자 정보, 댓글 목록을 가져오기 위해 각각 &lt;code class=&quot;language-text&quot;&gt;/posts&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;/users&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;/comments&lt;/code&gt;로 세 번의 요청을 보내야 합니다. 이 과정에서 언더페칭과 더불어 각 API 응답마다 원치 않는 데이터까지 섞여 들어오는 오버페칭이 동시에 발생하기도 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;br&gt;
&lt;p&gt;&lt;strong&gt;페이스북의 고민에서 부터 시작&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;오버페칭, 언더페칭 문제는 서비스 규모가 커질수록 치명적이었습니다.&lt;br&gt;
2012년 당시, 페이스북은 모바일 앱으로의 전환 과정에서 성능 문제에 직면했습니다. 뉴스피드처럼 복잡하고 다양한 데이터가 얽혀 있는 화면을 구현할 때, REST API로는 &lt;u&gt;&lt;strong&gt;수많은 네트워크 요청&lt;/strong&gt;&lt;/u&gt;과 &lt;u&gt;&lt;strong&gt;데이터 낭비&lt;/strong&gt;&lt;/u&gt;를 감당하기에는 한계가 있었습니다.&lt;/p&gt;
&lt;p&gt;그러하여 페이스북 개발팀은 &lt;strong&gt;&quot;클라이언트가 직접 필요한 데이터의 구조를 정의하고, 단 한 번의 요청으로 원하는 데이터만 받을 수 없을까?&quot;&lt;/strong&gt; 라는 질문을 던졌고, 그 해답으로 GraphQL이 탄생하였습니다.&lt;/p&gt;
&lt;h1 id=&quot;핵심-개념-알아보기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC-%EA%B0%9C%EB%85%90-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0&quot; aria-label=&quot;핵심 개념 알아보기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심 개념 알아보기&lt;/h1&gt;
&lt;p&gt;추상적인 개념을 알아보기 전에 어떻게 동작하는지 예를 먼저 들어보겠습니다.&lt;br&gt;
GraphQL 은 클라이언트가 내가 필요한 데이터가 무엇인지 요청하면 서버에서 알려주면 서버는 그에 맞는 데이터를 돌려주는 방식이며, 이때 요청은 쿼리를 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;블로그 포스트의 제목과 작성자 정보 요청(Query)&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property-query&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt;
        &lt;span class=&quot;token object&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;서버의 응답&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;post&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;그래프큐엘 시작하기&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;우디&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;예시와 같이 클라이언트가 작성한 모양 그대로 서버의 응답이 JSON 형태로 돌아옵니다.&lt;br&gt;
이러한 형태가 가능하려면 클라이언트는 서버가 &lt;em&gt;&quot;post 라는 데이터를 줄 수 있다&quot;&lt;/em&gt;, &lt;em&gt;&quot;id 를 입력하면 해당 id 에 대한 post 정보만 준다.&quot;&lt;/em&gt;, &lt;em&gt;&quot;post 에는 title, author 정보를 줄 수 있다.&quot;&lt;/em&gt; 등에 대한 정보를 알고 있다는 건데요.&lt;br&gt;
이것을 가능하게 하는 것이 GraphQL 서버의 자기소개서와 같은 &lt;strong&gt;스키마와 타입&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;h2 id=&quot;핵심1-스키마와-타입the-schema--types&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC1-%EC%8A%A4%ED%82%A4%EB%A7%88%EC%99%80-%ED%83%80%EC%9E%85the-schema--types&quot; aria-label=&quot;핵심1 스키마와 타입the schema  types permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심1. 스키마와 타입(The Schema &amp;#x26; Types)&lt;/h2&gt;
&lt;p&gt;스키마(Schema)는 &lt;strong&gt;GrahpQL 의 설계도&lt;/strong&gt;이며 타입(Type)으로 구성되어 입니다.&lt;/p&gt;
&lt;p&gt;먼저 타입부터 살펴보면&lt;/p&gt;
&lt;h3 id=&quot;타입type&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%83%80%EC%9E%85type&quot; aria-label=&quot;타입type permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;타입(Type)&lt;/h3&gt;
&lt;p&gt;타입에는 세 가지 종류가 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;스칼라 타입(Scalar Type)&lt;/strong&gt;: 가장 기본적인 데이터 조각입니다. 아래가 기본적으로 제공해주는 GrpahQL 스칼라 타입입니다.
&lt;ul&gt;
&lt;li&gt;String: 글자 (예: &quot;우디&quot;)&lt;/li&gt;
&lt;li&gt;Int: 정수 (예: 25)&lt;/li&gt;
&lt;li&gt;Float: 실수 (예: 4.5)&lt;/li&gt;
&lt;li&gt;Boolean: 참/거짓 (예: true)&lt;/li&gt;
&lt;li&gt;ID: 고유 식별자 (객체를 식별할 때 쓰는 특수한 값)&lt;/li&gt;
&lt;li&gt;커스텀 스칼라(Custom Scalar): 사용자가 직접 생성한 스칼라 타입(날짜(Date)처럼 기본 타입 외에 특별히 정의한 타입)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;객체 타입(Object Type)&lt;/strong&gt;: 스칼라 타입들을 모아서 만든 &apos;복합 데이터&apos;입니다. 우리가 실제 서비스에서 다루는 **사용자(User)**나 &lt;strong&gt;포스트(Post)&lt;/strong&gt; 같은 데이터 입니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;열거형 타입(Enum types)&lt;/strong&gt;: 미리 정의된 특정 값들의 집합 중 하나를 선택하도록 강제하는 특별한 타입입니다. 정해진 옵션 이외의 값은 허용하지 않을 때 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 포스트의 상태를 정의하는 열거형 타입&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PostStatus&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;DRAFT&lt;/span&gt;      &lt;span class=&quot;token comment&quot;&gt;# 초안&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;PUBLISHED&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 발행됨&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;HIDDEN&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;# 숨김&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;# 스칼라 타입&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 스칼라 타입&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;# 다른 객체 타입과의 관계&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PostStatus&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 열거형 타입을 필드로 사용&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;느낌표(!)를 통한 널 허용/비허용&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;타입 뒤에 붙는 느낌표(!)는 &lt;strong&gt;&quot;이 값은 절대 비어있을 수 없다(Null이 될 수 없다)&quot;&lt;/strong&gt; 는 약속입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;String: 이름이 없을 수도 있음 (Null 허용)&lt;/li&gt;
&lt;li&gt;String!: 이름이 무조건 있어야 함 (Null 비허용)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;# ID는 무조건 있어야 합니다.&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# 제목 없는 포스트는 존재할 수 없습니다.&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 본문은 비어있을 수도 있습니다.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 느낌표 하나로 데이터의 필수 여부를 명확하게 정할 수 있어, 스키마 레벨에서 데이터의 &lt;strong&gt;무결성을 강제&lt;/strong&gt;하고 클라이언트의 &lt;strong&gt;런타임 에러를 사전&lt;/strong&gt;에 방지합니다.&lt;/p&gt;
&lt;h3 id=&quot;스키마schema&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%82%A4%EB%A7%88schema&quot; aria-label=&quot;스키마schema permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스키마(Schema)&lt;/h3&gt;
&lt;p&gt;스키마는 위에서 만든 타입들을 한데 모으고, 클라이언트가 &lt;strong&gt;어떤 행동(읽기, 쓰기 등)&lt;/strong&gt; 을 할 수 있는지 정의한 최종 문서입니다.&lt;br&gt;
스키마를 보면 이 서비스의 모든 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;실제 스키마 파일은 보통 이런 식으로 구성됩니다. 마치 메뉴판의 &apos;전체 카테고리&apos;를 정해주는 것과 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 1. 데이터 타입 정의&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 2. 할 수 있는 행동(Query, Mutation 등) 정의&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# &quot;id를 주면 Post 하나를 줄게&quot; 라는 약속&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Mutation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# &quot;제목을 주면 새로운 포스트를 만들고 그 결과를 보여줄게&quot; 라는 약속&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;createPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;요약하자면 스키마는 &lt;strong&gt;&quot;어떤 타입의 데이터를(Type), 어떤 방식으로(Query/Mutation) 주고받을 것인가&quot;&lt;/strong&gt; 에 대한 서버와 클라이언트의 표준이라고 할 수 있습니다.&lt;br&gt;
마치 REST API 에서 API 명세서(API Specification)를 정의한 것과 같습니다.&lt;/p&gt;
&lt;h3 id=&quot;스키마타입-작성의-장점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8A%A4%ED%82%A4%EB%A7%88%ED%83%80%EC%9E%85-%EC%9E%91%EC%84%B1%EC%9D%98-%EC%9E%A5%EC%A0%90&quot; aria-label=&quot;스키마타입 작성의 장점 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;스키마&amp;#x26;타입 작성의 장점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;안정성&lt;/strong&gt;: 정의되지 않은 엉뚱한 데이터를 요청하면 서버가 실행되기도 전에 &quot;그건 없는 데이터야&quot;라고 알려줄 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;협업 효율&lt;/strong&gt;: 프론트엔드 개발자는 스키마만 보고도 서버 개발자에게 물어볼 필요 없이 어떤 데이터를 받아올지 알 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이제 스키마와 타입이 준비되었습니다. 그럼 이 스키마를 보고 실제로 데이터를 요청하는 방법인 **쿼리(Query)**에 대해 알아보겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;핵심2-쿼리query---데이터-읽기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC2-%EC%BF%BC%EB%A6%ACquery---%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9D%BD%EA%B8%B0&quot; aria-label=&quot;핵심2 쿼리query   데이터 읽기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심2. 쿼리(Query) - 데이터 읽기&lt;/h2&gt;
&lt;p&gt;쿼리는 클라이언트가 서버에 데이터를 요청하는 방식입니다. REST API의 GET 요청과 비슷하지만, 훨씬 더 효율적입니다.&lt;/p&gt;
&lt;p&gt;REST API는 서버가 정해준 데이터를 통째로 받아야 했지만, GraphQL 쿼리는 &lt;strong&gt;내가 필요한 필드(Field)&lt;/strong&gt; 만 적어서 보냅니다.&lt;br&gt;
만약 블로그 포스트 &lt;code class=&quot;language-text&quot;&gt;id:1&lt;/code&gt; 의 제목, 작성자 이름을 가져오고 싶으면&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;REST API: /posts/1 호출 시 제목, 본문, 작성일, 댓글 등 모든 데이터를 다 줍니다. (Overfetching)&lt;/li&gt;
&lt;li&gt;GraphQL: &quot;제목이랑 작성자 이름만 줘!&quot;라고 요청하면 딱 그 데이터만 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# &quot;포스트 1번의 제목(title)과 작성자 이름(name)만 필요해&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property-query&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt;
        &lt;span class=&quot;token object&quot;&gt;author&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위와 같이 스키마만 알면 어떤 데이터를 가져올지는 &lt;strong&gt;클라이언트에게 주도권(Client-driven)&apos;&lt;/strong&gt; 이 있습니다.&lt;/p&gt;
&lt;br&gt;
&lt;h3 id=&quot;프래그먼트fragment---쿼리의-재사용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8fragment---%EC%BF%BC%EB%A6%AC%EC%9D%98-%EC%9E%AC%EC%82%AC%EC%9A%A9&quot; aria-label=&quot;프래그먼트fragment   쿼리의 재사용 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프래그먼트(Fragment) - 쿼리의 재사용&lt;/h3&gt;
&lt;p&gt;쿼리를 작성하다 보면 여러 곳에서 공통된 필드들을 반복해서 적어야 할 때가 있습니다. 이때 &lt;strong&gt;프래그먼트&lt;/strong&gt;를 사용하면 &lt;b&gt;&lt;u&gt;중복되는 필드 세트를 하나로 묶어 재사용&lt;/u&gt;&lt;/b&gt;할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 공통 필드를 프래그먼트로 정의&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fragment&lt;/span&gt; &lt;span class=&quot;token fragment function&quot;&gt;PostDetails&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 쿼리에서 사용&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;token property-query&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token fragment function&quot;&gt;PostDetails&lt;/span&gt; 
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;마치 레고 블록을 미리 조립해두고 필요할 때마다 끼워 쓰는 것과 같아 코드가 훨씬 간결해집니다.&lt;/p&gt;
&lt;h2 id=&quot;핵심3-뮤테이션mutation---데이터-수정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC3-%EB%AE%A4%ED%85%8C%EC%9D%B4%EC%85%98mutation---%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%88%98%EC%A0%95&quot; aria-label=&quot;핵심3 뮤테이션mutation   데이터 수정 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심3. 뮤테이션(Mutation) - 데이터 수정&lt;/h2&gt;
&lt;p&gt;뮤테이션은 REST API의 POST, PUT, DELETE를 하나로 합쳐놓은 것이라고 생각하면 쉽습니다.&lt;br&gt;
쿼리가 데이터를 가져오는 &apos;읽기&apos; 전용이라면, **뮤테이션(Mutation)**은 서버의 데이터를 변경하는 &apos;쓰기&apos; 전용 통로입니다. 이름 그대로 데이터에 &apos;변화(Mutation)&apos;를 주는 작업이죠.&lt;/p&gt;
&lt;h3 id=&quot;cud생성-수정-삭제-작업-처리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#cud%EC%83%9D%EC%84%B1-%EC%88%98%EC%A0%95-%EC%82%AD%EC%A0%9C-%EC%9E%91%EC%97%85-%EC%B2%98%EB%A6%AC&quot; aria-label=&quot;cud생성 수정 삭제 작업 처리 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;CUD(생성, 수정, 삭제) 작업 처리&lt;/h3&gt;
&lt;p&gt;뮤테이션을 사용하면 새로운 포스트를 만들거나(Create), 기존 내용을 고치거나(Update), 마음에 안 드는 데이터를 지우는(Delete) 작업을 할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 새로운 포스트를 작성하는 Mutation 예시&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;mutation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property-query property-mutation&quot;&gt;createPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;뮤테이션 배우기&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;생각보다 쉬워요&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;title&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;createdAt&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;위 코드는 createPost 로 title, content를 인자로 전달하고, 작업 성공 시 id, title, createdAt 필드를 받도록 요청하는 예시입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;createPost&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;101&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;뮤테이션 배우기&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;createdAt&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2026-01-15T10:00:00Z&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;인풋-타입input-type---뮤테이션의-인자를-편하게&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%B8%ED%92%8B-%ED%83%80%EC%9E%85input-type---%EB%AE%A4%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%98-%EC%9D%B8%EC%9E%90%EB%A5%BC-%ED%8E%B8%ED%95%98%EA%B2%8C&quot; aria-label=&quot;인풋 타입input type   뮤테이션의 인자를 편하게 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;인풋 타입(input type) - 뮤테이션의 인자를 편하게&lt;/h3&gt;
&lt;p&gt;뮤테이션을 사용할 때 보낼 데이터가 많아지면(예: 제목, 본문, 카테고리, 태그 등) 인자가 너무 길어져서 가독성이 떨어집니다. 이때 관련된 인자들을 하나로 묶어주는 것이 바로 input 타입입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;깔끔한 코드: 여러 개의 인자를 하나하나 나열하지 않고, &apos;가방&apos;에 담아 한 번에 전달하는 것과 같습니다.&lt;/li&gt;
&lt;li&gt;재사용성: 데이터를 생성할 때와 수정할 때 똑같은 input 구조를 재사용할 수 있어 편리합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# 1. input 타입 정의&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;input&lt;/span&gt; &lt;span class=&quot;token atom-input class-name&quot;&gt;CreatePostInput&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;
  &lt;span class=&quot;token attr-name&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token scalar&quot;&gt;String&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# 2. 뮤테이션에서 사용&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;mutation&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property-query property-mutation&quot;&gt;createPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;안녕&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;반가워&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;일반-타입type-vs-인풋-타입input&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9D%BC%EB%B0%98-%ED%83%80%EC%9E%85type-vs-%EC%9D%B8%ED%92%8B-%ED%83%80%EC%9E%85input&quot; aria-label=&quot;일반 타입type vs 인풋 타입input permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;일반 타입(Type) vs 인풋 타입(Input)&lt;/h3&gt;
&lt;p&gt;겉모습은 비슷하지만 용도가 엄격히 구분됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;객체 타입 (Object Type): 서버가 클라이언트에게 데이터를 보낼 때 사용하는 &apos;출력용&apos; 규격입니다.&lt;/li&gt;
&lt;li&gt;인풋 타입 (Input Type): 클라이언트가 서버에 데이터를 보낼 때 사용하는 &apos;입력용&apos; 규격입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;참고&lt;/strong&gt;: GraphQL에서는 보안과 구조상의 이유로 일반 객체 타입을 뮤테이션의 인자로 직접 사용할 수 없게 설계되어 있습니다. 그래서 꼭 input 키워드를 사용해야 합니다.&lt;/p&gt;
&lt;h2 id=&quot;핵심-4-서브스크립션-subscription---실시간-데이터&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC-4-%EC%84%9C%EB%B8%8C%EC%8A%A4%ED%81%AC%EB%A6%BD%EC%85%98-subscription---%EC%8B%A4%EC%8B%9C%EA%B0%84-%EB%8D%B0%EC%9D%B4%ED%84%B0&quot; aria-label=&quot;핵심 4 서브스크립션 subscription   실시간 데이터 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심 4: 서브스크립션 (Subscription) - 실시간 데이터&lt;/h2&gt;
&lt;p&gt;쿼리가 &apos;질문과 답변&apos;이라면, 서브스크립션은 **&apos;구독과 알림&apos;**입니다. 클라이언트가 특정 이벤트가 발생했을 때 알려달라고 서버에 요청해두면, 서버가 실시간으로 데이터를 밀어주는(Push) 방식입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 웹소켓(WebSocket) 기반의 실시간 통신&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;일반적인 HTTP 요청은 클라이언트가 먼저 말을 걸어야 응답을 받을 수 있지만, 서브스크립션은 웹소켓(WebSocket) 프로토콜을 사용합니다. 한 번 연결을 맺어두면 서버와 클라이언트가 계속 연결된 상태를 유지하며 데이터를 주고받을 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 어떻게 사용하나요?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;주로 새로운 댓글이 달렸거나, 채팅 메시지가 왔을 때처럼 &apos;특정 조건이 충족되는 순간&apos; 데이터를 전송합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;graphql&quot;&gt;&lt;pre class=&quot;language-graphql&quot;&gt;&lt;code class=&quot;language-graphql&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# &quot;누군가 댓글을 달면(commentAdded), 그 댓글의 본문과 작성자를 실시간으로 보내줘!&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;subscription&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property-query&quot;&gt;commentAdded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;postId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;101&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;author&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;3. 주요 특징&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;이벤트 중심(Event-driven)&lt;/strong&gt;: 서버에서 특정 데이터의 변화(주로 뮤테이션 발생)가 감지될 때 동작합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지속성&lt;/strong&gt;: 연결이 끊어지기 전까지는 클라이언트가 별도의 추가 요청을 보내지 않아도 최신 데이터를 계속 받아볼 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;활용 사례&lt;/strong&gt;: 실시간 채팅, 주식 시황 알림, 배달 위치 추적 등 실시간성이 중요한 기능에 필수적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;핵심5-리졸버resolver---데이터-채우기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC5-%EB%A6%AC%EC%A1%B8%EB%B2%84resolver---%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B1%84%EC%9A%B0%EA%B8%B0&quot; aria-label=&quot;핵심5 리졸버resolver   데이터 채우기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심5. 리졸버(Resolver) - 데이터 채우기&lt;/h2&gt;
&lt;p&gt;스키마가 데이터의 생김새를 정의한 &apos;설계도&apos;라면, 그 설계도에 맞춰 실제로 데이터를 채워넣는 함수가 바로 리졸버입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 리졸버란?&lt;/strong&gt;&lt;br&gt;
클라이언트로부터 쿼리가 들어왔을 때, 해당 필드에 어떤 값을 돌려줄지 결정하는 실행 함수입니다. 스키마에 정의된 모든 필드는 사실 각각의 리졸버 함수를 가지고 있습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;스키마는 &apos;무엇(What)&apos;을 줄지 약속하고, 리졸버는 그 데이터를 &apos;어떻게(How)&apos; 가져올지 담당합니다.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;2. 데이터의 소스를 가리지 않는 유연성&lt;/strong&gt;
리졸버의 가장 강력한 특징은 데이터가 어디에 있든 상관없다는 점입니다. 하나의 쿼리 안에서도 여러 리졸버가 각각 다른 곳에서 데이터를 긁어와 합칠 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;DB&lt;/strong&gt;: 사용자 정보는 MySQL에서 가져오고,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;REST API&lt;/strong&gt;: 포스트 정보는 기존 REST API에서 가져오고,&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Third-party&lt;/strong&gt;: 날씨 정보는 외부 기상청 API에서 가져와서 하나로 묶어줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 동작 예시&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 리졸버 함수의 예 (JavaScript 스타일)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resolvers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// &quot;post 쿼리가 들어오면 DB에서 해당 id를 찾아줘!&quot;&lt;/span&gt;
        &lt;span class=&quot;token function-variable function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Posts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findOne&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;4. 리졸버가 주는 이점&lt;/strong&gt;&lt;br&gt;
클라이언트는 뒤에 DB가 있는지, 다른 API가 있는지 알 필요가 없습니다. 오직 GraphQL 엔드포인트 하나에만 물어보면 리졸버들이 각자의 위치에서 데이터를 모아 완벽한 하나의 응답을 만들어주기 때문입니다.&lt;/p&gt;
&lt;h1 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h1&gt;
&lt;p&gt;이로써, GraphQL 의 핵심 개념들에 대해서 살펴봤습니다.&lt;br&gt;
간단하게 필요한 요소들만 설명하였는데요. &lt;strong&gt;그래프 이론&lt;/strong&gt;을 알고 GraphQL 을 배우면 이해하는데 더욱더 도움이 되지만, 그렇게 되면 포스팅을 하나 더 올려야되는 수준이라 설명드리지는 못 하였습니다.&lt;br&gt;
앞으로 GraphQL을 계속해서 사용해야되는 상황이라면 찾아서 공부해 보시는 것을 추천드립니다.&lt;/p&gt;
&lt;p&gt;다음 포스팅 주제는 시간이 되면 Client(Android,), Server(Spring Boot) 를 사용하여 실습하는 예제를 올려보도록 하겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;graphql의-장단점-정리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#graphql%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90-%EC%A0%95%EB%A6%AC&quot; aria-label=&quot;graphql의 장단점 정리 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;GraphQL의 장단점 정리&lt;/h2&gt;
&lt;p&gt;마지막으로 간단하게 &lt;u&gt;GraphQL의 장단점&lt;/u&gt;을 정리해 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;장점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;생산성 향상: 클라이언트가 필요한 데이터를 직접 정의하므로, API 변경을 위해 백엔드 개발자를 기다리는 시간이 획기적으로 줄어듭니다.&lt;/li&gt;
&lt;li&gt;자기 문서화(Self-documenting): 스키마 자체가 곧 완벽한 API 명세서입니다. 별도의 문서 도구 없이도 인트로스펙션 기능을 통해 실시간으로 API 구조를 확인할 수 있습니다.&lt;/li&gt;
&lt;li&gt;프론트-백엔드 의존성 분리: 서버는 데이터 소스 제공에 집중하고, 클라이언트는 화면 구성에 집중하는 진정한 의미의 관심사 분리가 가능해집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;단점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;캐싱의 복잡성: REST API는 URL 단위로 캐싱하기 쉽지만, GraphQL은 요청마다 본문(Body)이 다르기 때문에 HTTP 캐싱을 그대로 쓰기 어렵습니다. (Apollo 등 별도 라이브러리의 도움 필요)&lt;/li&gt;
&lt;li&gt;파일 업로드: 표준 사양에 파일 업로드가 포함되어 있지 않아, 별도의 멀티파트 요청 처리가 필요합니다.&lt;/li&gt;
&lt;li&gt;초기 학습 곡선: 쿼리 언어뿐만 아니라 스키마 설계, 리졸버 구현 등 기존 REST 방식과는 다른 사고방식이 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;다루지-않은-내용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A4%EB%A3%A8%EC%A7%80-%EC%95%8A%EC%9D%80-%EB%82%B4%EC%9A%A9&quot; aria-label=&quot;다루지 않은 내용 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;다루지 않은 내용&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;인트로스펙션(introspection): 서버의 스키마, 타입을 조회하는 기능, Playground 에서 인스로스펙션 기능을 통해 스키마 정보를 실시간으로 확인하며 쿼리를 작성&lt;/li&gt;
&lt;li&gt;추상 구문 트리(AST): 쿼리가 들어오면 서버는 &apos;추상 구문 트리(AST)&apos;로 파싱하여 분석하고, 그 결과에 따라 적절한 리졸버 함수를 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;br&gt;
&lt;br&gt;
&lt;p&gt;감사합니다.😊&lt;/p&gt;</content:encoded></item><item><title><![CDATA[::compose:: Navigation3 핵심개념 살펴보기]]></title><description><![CDATA[최근 Navigation 3 의 안정화 버전이 출시되었습니다. 기존 Navigation 2에서 느꼈던 복잡함과 다중 패널 구현의 한계가 크게 개선되었는데요. 직접 사용해 보니 백스택(Backstack…]]></description><link>https://songwoody.github.io/blog/compose/06-compose-jetpack-compose-navigation3-overview/</link><guid isPermaLink="false">https://songwoody.github.io/blog/compose/06-compose-jetpack-compose-navigation3-overview/</guid><pubDate>Tue, 16 Dec 2025 13:02:55 GMT</pubDate><content:encoded>&lt;p&gt;최근 &lt;strong&gt;Navigation 3&lt;/strong&gt; 의 안정화 버전이 출시되었습니다. 기존 Navigation 2에서 느꼈던 &lt;strong&gt;복잡함&lt;/strong&gt;과 &lt;strong&gt;다중 패널 구현의 한계&lt;/strong&gt;가 크게 개선되었는데요. 직접 사용해 보니 백스택(Backstack)을 개발자가 직접 제어할 수 있게 되어, 훨씬 직관적이고 간결하다는 인상을 받았습니다.&lt;/p&gt;
&lt;p&gt;이번 포스팅에서는 Navigation 3의 핵심 개념을 가볍게 정리해 보겠습니다.&lt;/p&gt;
&lt;h1 id=&quot;jetpack-navigation-3-무엇이-달라졌고-왜-필요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jetpack-navigation-3-%EB%AC%B4%EC%97%87%EC%9D%B4-%EB%8B%AC%EB%9D%BC%EC%A1%8C%EA%B3%A0-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;jetpack navigation 3 무엇이 달라졌고 왜 필요한가 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jetpack Navigation 3, 무엇이 달라졌고 왜 필요한가?&lt;/h1&gt;
&lt;p&gt;기존 &lt;code class=&quot;language-text&quot;&gt;Navigation 2&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;NavGraph&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;NavController&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Destination&lt;/code&gt; 등의 요소를 통해 &lt;strong&gt;백스택을 간접적으로 관리&lt;/strong&gt; 하며 화면 이동을 처리했습니다. 이러한 구조는 본래 전통적인 액티비티(Activity)와 프래그먼트(Fragment) 환경을 지원하기 위해 설계된 것으로 알고 있습니다.&lt;/p&gt;
&lt;p&gt;하지만 이 과정에서 안드로이드 프레임워크와의 결합도가 높아졌고, 현대적인 선언형 UI(Compose) 환경에서는 구현 복잡도가 불필요하게 증가하는 원인이 되었습니다. Navigation 3는 바로 이러한 한계를 극복하고, 컴포즈 환경에 맞게 더 선언적이고 유연한 구조를 제공하기 위해 등장했습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;핵심 개선 사항&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;명확한 백스택(Back Stack) 제어 및 투명성&lt;/strong&gt;: Nav2는 &lt;code class=&quot;language-text&quot;&gt;navigate()&lt;/code&gt;나 &lt;code class=&quot;language-text&quot;&gt;popBackStack()&lt;/code&gt; 같은 &lt;strong&gt;명령&lt;/strong&gt; 을 내려 화면을 컨트롤 하였습니다. Nav3 에서는 백스택을 상태(State)로 취급하여 개발자가 내부를 투명하게 들여다보고 조작할 수 있게 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;유연한 다중 패널 및 폴더블 디바이스 대응&lt;/strong&gt;: Nav2는 기본적으로 &apos;하나의 화면&apos;을 다른 &apos;하나의 화면&apos;으로 교체하는 방식에 최적화되어 있습니다. 태블릿이나 폴더블처럼 여러 화면을 동시에 보여주거나, 특정 영역만 동적으로 교체해야 하는 복잡한 UI를 구현하기에는 유연성이 부족했습니다. Nav3 에서는 &lt;code class=&quot;language-text&quot;&gt;SceneStragegy&lt;/code&gt;를 통해 다중 패널 환경의 화면 처리를 훨씬 유연하게 다룰 수 있도록 돕습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;정리하자면, Navigation 3는 불투명했던 구조에서 벗어나 개발자가 백스택의 상태를 명확하게 읽고 제어할 수 있는 &lt;strong&gt;투명하고, 유연하며, 확장 가능한 새로운 아키텍처&lt;/strong&gt;를 제공합니다.&lt;/p&gt;
&lt;p&gt;이어서 Navigation 3의 핵심적인 구성 요소에 대해 자세히 살펴보겠습니다&lt;/p&gt;
&lt;h1 id=&quot;nav3의-핵심-변경-사항-백-스택&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nav3%EC%9D%98-%ED%95%B5%EC%8B%AC-%EB%B3%80%EA%B2%BD-%EC%82%AC%ED%95%AD-%EB%B0%B1-%EC%8A%A4%ED%83%9D&quot; aria-label=&quot;nav3의 핵심 변경 사항 백 스택 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nav3의 핵심 변경 사항: 백 스택&lt;/h1&gt;
&lt;p&gt;Nav3 이해하기 위한 첫 번째 &lt;strong&gt;&apos;백 스택&apos;&lt;/strong&gt; 에 대해서 알아보겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;백-스택back-stack-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B1-%EC%8A%A4%ED%83%9Dback-stack-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;백 스택back stack 이란 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&apos;백 스택(Back Stack)&apos; 이란?&lt;/h2&gt;
&lt;p&gt;백 스택은 사용자가 앱 내에서 이동한 화면들을 보관하는 보관소입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;화면 이동: 새로운 화면으로 이동하면 스택의 가장 위에 정보가 쌓입니다.&lt;/li&gt;
&lt;li&gt;뒤로 가기: 뒤로 가기 버튼이나 제스처를 사용하면 최상단의 화면을 제거(Pop)하고 바로 아래에 있던 이전 화면을 보여줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a02e974d95bd5c17f63b1630c74dbd30/0c3d0/back-stack.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 24.050632911392405%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAAsTAAALEwEAmpwYAAABDUlEQVR42lWQW0+DQBCF+f9/p6Z9NfrWxtgmKhRsxVKuheWygFCBz2WNRifZnIdvz8ycMcIwIEkSgiBASomu6VtEluO/B1zClMS/kGWCn5qmiTRNOZ/PxHGM7/u0bYth2w6Hw5FZoyjSH/u+102jImBbrtmKNbtyQ1LGMELXd3rqvMR+b//6i6LAsKxnHMdS4AXXfdOTLMvi5J4w3Sce23s2+S0P9R1udMT3Zm7ieZ5W2za13zSfECLDaNollVzQfizIhMn1OjKOg9YotOjaFbJc0jVLFfHIMMxxBxXvU51qp32VvKFuVpQqgZEXatXylbywqaoLf48opVDMVs/RWtf5P15VifbNfO7TNJIvJKV1gTrKeKAAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;back-stack&quot;
        title=&quot;&quot;
        src=&quot;/static/a02e974d95bd5c17f63b1630c74dbd30/f058b/back-stack.png&quot;
        srcset=&quot;/static/a02e974d95bd5c17f63b1630c74dbd30/c26ae/back-stack.png 158w,
/static/a02e974d95bd5c17f63b1630c74dbd30/6bdcf/back-stack.png 315w,
/static/a02e974d95bd5c17f63b1630c74dbd30/f058b/back-stack.png 630w,
/static/a02e974d95bd5c17f63b1630c74dbd30/40601/back-stack.png 945w,
/static/a02e974d95bd5c17f63b1630c74dbd30/78612/back-stack.png 1260w,
/static/a02e974d95bd5c17f63b1630c74dbd30/0c3d0/back-stack.png 1414w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;백 스택에는 실제 컨텐츠를 담을 수도 있고 콘텐츠에 필요한 정보만 담을 수도 있으며, 화면 이동에 필요한 Key만 담을 수도 있으나, &lt;strong&gt;Navigation3 에서는 Key&lt;/strong&gt; 만을 담는 방식으로 선언형 UI에 환경에 더 잘 맞게 설계되었습니다.&lt;/p&gt;
&lt;p&gt;아래 코드는 기본적인 백 스택 구현 코드입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; ListScreen
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DetailScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyApp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// back stack 생성&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; backstack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; mutableStateListOf&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ScreenA&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// NavDisplay를 사용한 백스택으로 화면 그리기&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ..생략.. &lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 화면 이동(Push)&lt;/span&gt;
    backStack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ScreenB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;myId&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 뒤로 가기(Pop)&lt;/span&gt;
    backStack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeLastOrNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;nav3-에서-콘텐츠를-관리하는-법-naventry&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#nav3-%EC%97%90%EC%84%9C-%EC%BD%98%ED%85%90%EC%B8%A0%EB%A5%BC-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B2%95-naventry&quot; aria-label=&quot;nav3 에서 콘텐츠를 관리하는 법 naventry permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Nav3 에서 콘텐츠를 관리하는 법: NavEntry&lt;/h1&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;NavEntry&lt;/code&gt;는 &lt;strong&gt;키&lt;/strong&gt;와 해당 Key가 나타내는 &lt;strong&gt;콘텐츠&lt;/strong&gt;를 &lt;strong&gt;유지&lt;/strong&gt;하고 &lt;strong&gt;저장&lt;/strong&gt;하는 합니다..&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NavEntry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; T&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    contentKey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Any&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    metadata&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Map&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    content&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt; 와 &lt;code class=&quot;language-text&quot;&gt;content&lt;/code&gt; 는 필수입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;key&lt;/code&gt;: Back Stack에서 사용하는 Key와 동일하며, 특정 엔트리를 식별하는 고유 값입니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;contentKey&lt;/code&gt;: 콘텐츠의 고유 식별자입니다. 주로 화면 전환(Transition) 애니메이션을 적용하거나, 동일한 Key 내에서 UI 구성을 구분 및 캐싱하는 기준점으로 사용됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;metadata&lt;/code&gt;: 화면과 관련된 부가 정보(예: 페이지 타이틀, 특정 UI 요소 노출 여부 등)를 담는 Map입니다. 네비게이션 로직 외부에서 화면의 상태를 참조할 때 유용합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;content&lt;/code&gt;: 실제로 화면에 그려질 @Composable UI 블록입니다. 매개변수로 전달받은 key(T)를 활용하여 화면을 구성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;NavEntry&lt;/code&gt; 를 만드는 방식을 두 가지가 있습니다.&lt;br&gt;
하나는 직접 &lt;code class=&quot;language-text&quot;&gt;when&lt;/code&gt; 문을 사용하여 key에 따라 분기처리하는 방법과 &lt;code class=&quot;language-text&quot;&gt;entryProvider&lt;/code&gt; DSL 을 사용하는 방법입니다.&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;entryProvider&lt;/code&gt; DSL 를 사용하는 방법만 간단하게 살펴봅시다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;entryProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entryProvider &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    entry&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ListScreen&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;List Screen&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    entry&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;DetailScreen&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        metadata &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;extraDataKey&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;extraDataValue&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; key &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Target ID: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;화면을-보여주는-창-navdisplay&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%94%EB%A9%B4%EC%9D%84-%EB%B3%B4%EC%97%AC%EC%A3%BC%EB%8A%94-%EC%B0%BD-navdisplay&quot; aria-label=&quot;화면을 보여주는 창 navdisplay permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;화면을 보여주는 창: NavDisplay&lt;/h1&gt;
&lt;p&gt;마지막으로 &lt;code class=&quot;language-text&quot;&gt;NavDisplay&lt;/code&gt; 에 대해서 살펴보겠습니다.&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;NavEntry&lt;/code&gt;가 &apos;무엇을 보여줄 것인가&apos;에 대한 상태 데이터라면, &lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;NavDisplay&lt;/code&gt;는 그 상태를 &apos;어떻게 화면에 그릴 것인가&apos;를 결정&lt;/strong&gt;하는 역할입니다.&lt;br&gt;
백 스택의 상태를 관찰하고, 현재 최상단에 있는 NavEntry를 실제 UI로 렌더링하는 역할을 합니다.&lt;/p&gt;
&lt;p&gt;아래는 &lt;strong&gt;백 스택&lt;/strong&gt;, &lt;strong&gt;NavEntry&lt;/strong&gt;, &lt;strong&gt;NavDisplay&lt;/strong&gt; 관계를 잘 보여주는 공식문서의 data flow 입니다.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b1189e9dcae707847cc4e1f04f38aeb6/d8817/nav3-data-flow.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 81.64556962025317%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAQCAYAAAAWGF8bAAAACXBIWXMAAAsTAAALEwEAmpwYAAACtUlEQVR42oWU6U/bQBDF+/9/hbYcUtUicQWqVuoBgRxNCZScxEAgjhMCCeQCJ45N65DY/nXsgKjUUiw9z+6O/ObN212/4ImneQEHaYNCps9BSkcrOthDOHQhZvwi1rOI6haRrkHCdrh0J9+98DyPR0wWHQf2IrA0d83a2xuW5jt8XR5z0YbFO4eXjRYva3VBg2ntjFf6gPjonvBf6sZjyO/borDH7rdL8ukbsgmDShfS/XM4XaS2M8NZYoafygK96meSQ2dC2O12qdfrtFotOp2uoM1YqikZkyOlRiKep3Cgouy3KAthtqdhHX+lVoijpTfoFL/TVjfZGw4nhJZloSgKqqoGMIw++o1JZrfH8WGdnW+KEFYo7LfRrn3CKv2TGJViipNsjKaa46ocZf+B0H+5rht46Lq+iR6jO79lE6VQJR7Nks+VONhrUupIy3qZ3tGWzCOkoh/QcgmapTBJ237aQ39zztQ7SscGudQlp0c66qFF71assAdUroqcNIqU26qMTzi9rlIauU8TPjKDZVpBxHtcHsmuDcQqRzozJfJH+mlCX6bANAdB9O7P1Hg0on5xjjkwApqLWg1NvG82m88r9Cl8BQ8CH1QMRaF5r9BX6jc7dpxnCG/FsH6fQaOBZ4iagSg1BAMTt9fHbndwdB1T8n7O+3OX/1ImlYlEsBcWqExPo01NUXv9KkB9doar+blgvTI9RWN2Tq7UkhyL/H8U+ldlawsvFMIJrTNOLOHk3tGPz6LHZrD33gTz0eaK5N/D6gpkMv8h9P3Y3oa1NVj/gJcO8avyhUpykePoGy4za9xVP8lfIiT5jxBalROffUbhxgYsL8NKiPGPVc61LIfZJJmdMMVCmlY5KUWl1ZV1aXkRUqlnNsWvGA6Ll1HY3ZKWNuWaCDLh+yhIbE/ymzIulwPC33PBoKCQnP9GAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;back-stack&quot;
        title=&quot;&quot;
        src=&quot;/static/b1189e9dcae707847cc4e1f04f38aeb6/f058b/nav3-data-flow.png&quot;
        srcset=&quot;/static/b1189e9dcae707847cc4e1f04f38aeb6/c26ae/nav3-data-flow.png 158w,
/static/b1189e9dcae707847cc4e1f04f38aeb6/6bdcf/nav3-data-flow.png 315w,
/static/b1189e9dcae707847cc4e1f04f38aeb6/f058b/nav3-data-flow.png 630w,
/static/b1189e9dcae707847cc4e1f04f38aeb6/40601/nav3-data-flow.png 945w,
/static/b1189e9dcae707847cc4e1f04f38aeb6/d8817/nav3-data-flow.png 1238w&quot;
        sizes=&quot;(max-width: 630px) 100vw, 630px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;코드로 간단히 살펴보면 아래와 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NavDisplay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    backstack&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... 생략 ... ,&lt;/span&gt;
    entryProvider&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; NavEntry&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;backstack&lt;/code&gt;: 현재 앱에 쌓여 있는 화면 키(Key)들의 리스트입니다. NavDisplay는 이 리스트의 변화를 감지하여 화면을 갱신합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;entryProvider&lt;/code&gt;: 주어진 key(T)를 바탕으로 그에 맞는 NavEntry를 생성하거나 찾아주는 로직입니다. &quot;어떤 키에 어떤 화면을 보여줄지&quot;를 연결하는 다리 역할을 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;기타&lt;/strong&gt;: 뒤로가기 처리를 위한 onBack, 전체 화면 이동 애니메이션을 처리하기 위한 transitionSpec 등의 파라미터가 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이제 핵심 개념인 &lt;strong&gt;백 스택&lt;/strong&gt;, &lt;strong&gt;NavEntry&lt;/strong&gt;, &lt;strong&gt;NavDisplay&lt;/strong&gt; 는 모두 정리되었습니다.&lt;br&gt;
그러나 이것만으로는 안드로이드에서 &lt;strong&gt;&apos;구성 변경&apos;&lt;/strong&gt; 및 &lt;strong&gt;&apos;프로세트 종료&apos;&lt;/strong&gt; 와 같은 상황에서 백스택을 유지할 수 없습니다.&lt;br&gt;
다음으로 상태 저장을 위한 필요한 요소들을 살펴봅니다.&lt;/p&gt;
&lt;h1 id=&quot;화면-상태-저장관리-navkey-인터페이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%99%94%EB%A9%B4-%EC%83%81%ED%83%9C-%EC%A0%80%EC%9E%A5%EA%B4%80%EB%A6%AC-navkey-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;화면 상태 저장관리 navkey 인터페이스 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;화면 상태 저장/관리: NavKey 인터페이스&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;&apos;구성 변경(화면 회전)&apos;&lt;/strong&gt; 및 &lt;strong&gt;&apos;프로세트 종료(메모리 부족에 의한 종료)&apos;&lt;/strong&gt; 에서 앱에 다시 진입하여 액티비티가 다시 그려지게 되면 액티비티가 파괴되고 다시 생성됩니다. 그 과정에서 Composable 또한 파괴되어 백 스택이 초기화 됩니다.&lt;br&gt;
이러한 상황을 대처하기 위해 Nav3 에서는 &lt;strong&gt;저장 가능한&lt;/strong&gt; 백 스택을 제공하는는 &lt;code class=&quot;language-text&quot;&gt;NavKey&lt;/code&gt; 인터페이스와 &lt;code class=&quot;language-text&quot;&gt;rememberNavBackStack&lt;/code&gt; 를 사용합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberNavBackStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;vararg&lt;/span&gt; elements&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NavKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NavBackStack&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;NavKey&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;

&lt;span class=&quot;token annotation builtin&quot;&gt;@Serializable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; NavBackStack&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NavKey&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; MutableList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; StateObject&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberNavBackStack&lt;/code&gt; 는 내부적으로 rememberSaveable 을 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberNavBackStack&lt;/code&gt; 가 올바르게 작동하려면 백 스택의 각 키가 특정 요구사항을 준수해야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;NavKey 인터페이스&lt;/strong&gt;: 백 스택의 모든 키는 NavKey 인터페이스를 구현해야 합니다. 이는 키를 저장할 수 있음을 라이브러리에 알리는 마커 인터페이스 역할을 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;@Serializable&lt;/strong&gt;: NavKey를 구현하는 것 외에도 키 클래스와 객체는 @Serializable 주석으로 표시해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Serializable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; ScreenA &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NavKey

&lt;span class=&quot;token annotation builtin&quot;&gt;@Serializable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; ScreenB &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NavKey

&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NavBackStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; backStack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberNavBackStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ScreenA&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;백스택 자체를 ViewModel 에 저장하는 대안이 있으나 따로 다루지는 않겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;저장-범위-지정하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%80%EC%9E%A5-%EB%B2%94%EC%9C%84-%EC%A7%80%EC%A0%95%ED%95%98%EA%B8%B0&quot; aria-label=&quot;저장 범위 지정하기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;저장 범위 지정하기&lt;/h1&gt;
&lt;h2 id=&quot;remembersavable의-수명주기를-entry에-맞추기필수&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#remembersavable%EC%9D%98-%EC%88%98%EB%AA%85%EC%A3%BC%EA%B8%B0%EB%A5%BC-entry%EC%97%90-%EB%A7%9E%EC%B6%94%EA%B8%B0%ED%95%84%EC%88%98&quot; aria-label=&quot;remembersavable의 수명주기를 entry에 맞추기필수 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;rememberSavable의 수명주기를 entry에 맞추기(필수)&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberSaveableStateHolderNavEntryDecorator&lt;/code&gt;를 사용하여 &lt;strong&gt;상태 저장의 주인(Owner)&lt;/strong&gt; 을 &lt;strong&gt;해당 화면(Entry)&lt;/strong&gt; 으로 지정할 수 있습니다.&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;rememberSaveableStateHolderNavEntryDecorator&lt;/code&gt;를 사용하지 않으면 모든 데이터가 &lt;code class=&quot;language-text&quot;&gt;Activity&lt;/code&gt;에 저장되어 화면을 뒤로 가기로 나갔다 들어와도 옛날 데이터가 남아있을 수 있습니다. 반면, 이를 사용하면 데이터가 &lt;strong&gt;해당 화면(Entry)&lt;/strong&gt; 이 죽을 때 같이 깔끔하게 사라집니다.&lt;/p&gt;
&lt;p&gt;아래 예시는 &lt;code class=&quot;language-text&quot;&gt;NavDisplay&lt;/code&gt; 에 &lt;code class=&quot;language-text&quot;&gt;entryDecorators&lt;/code&gt; 로 &lt;code class=&quot;language-text&quot;&gt;rememberSaveableStateHolderNavEntryDecorator&lt;/code&gt; 를 사용하지 않았을 경우의 문제입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AppNavigation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; backStack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberNavBackStack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;HomeKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;NavDisplay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        backStack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; backStack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// entryDecorators = emptyList() // 기본값&lt;/span&gt;
        entryProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entryProvider &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            entry&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;HomeKey&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;HomeScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onSearch &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; backStack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SearchKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            entry&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;SearchKey&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;SearchScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SearchScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 이 값은 Activity가 살아있는 한 계속 저장됨&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 화면을 나갔다(Back) 다시 들어와도 이전 입력값이 남아있을 위험이 있음&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; query &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; rememberSaveable &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mutableStateOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token function&quot;&gt;TextField&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onValueChange &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; query &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; it &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;viewmodel의-수명주기를-entry에-맞추기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#viewmodel%EC%9D%98-%EC%88%98%EB%AA%85%EC%A3%BC%EA%B8%B0%EB%A5%BC-entry%EC%97%90-%EB%A7%9E%EC%B6%94%EA%B8%B0&quot; aria-label=&quot;viewmodel의 수명주기를 entry에 맞추기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;viewModel의 수명주기를 entry에 맞추기&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberSaveable&lt;/code&gt; 과 마찬가지로 &lt;code class=&quot;language-text&quot;&gt;ViewModel&lt;/code&gt; 을 사용한다면 &lt;code class=&quot;language-text&quot;&gt;rememberViewModelStoreNavEntryDecorator&lt;/code&gt; 를  &lt;code class=&quot;language-text&quot;&gt;entryDecorators&lt;/code&gt; 에 포함해야 합니다.
이 데코레이터는 각 네비게이션 엔트리(NavBackStackEntry)가 &lt;strong&gt;독립적인 ViewModelStore를 소유&lt;/strong&gt;할 수 있도록 설정합니다. 이를 통해 ViewModel의 생명주기를 특정 화면(Entry)의 수명주기에 종속시킬 수 있으며, 화면이 백스택에서 완전히 제거될 때 관련 ViewModel 및 리소스가 올바르게 해제되도록 보장합니다.&lt;/p&gt;
&lt;h2 id=&quot;설정-예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A4%EC%A0%95-%EC%98%88%EC%8B%9C&quot; aria-label=&quot;설정 예시 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;설정 예시&lt;/h2&gt;
&lt;p&gt;아래와 같이 NavDisplay에 데코레이터들을 설정하여 사용할 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token function&quot;&gt;NavDisplay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    entryDecorators &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 각 화면 관리 및 상태 저장을 위한 기본 데코레이터 추가&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;rememberSaveableStateHolderNavEntryDecorator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 개별 엔트리 단위의 ViewModel 생명주기 관리를 위한 데코레이터 추가&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;rememberViewModelStoreNavEntryDecorator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    backStack &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; backStack&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    entryProvider &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entryProvider &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h1&gt;
&lt;p&gt;지금까지 Android Navigation 3의 핵심 개념과 상태 관리 메커니즘을 가볍게 살펴보았습니다.&lt;/p&gt;
&lt;p&gt;Navigation 2 를 사용해봤던 터라, Nav3가 더욱더 직관적으로 느껴졌습니다.
특히 Kotlin Serialization을 활용한 타입 안정성과 멀티플랫폼, 다중 패널(Multi-pane) 환경까지 고려한 설계가 컴포즈에 정말 잘 녹여낸것 같아 인상 깊게 다가왔습니다.
이번 글에서 미처 다루지 못한 &lt;strong&gt;멀티 모듈 프로젝트에서의 탐색 전략&lt;/strong&gt;, &lt;strong&gt;화면 전환 애니메이션 처리&lt;/strong&gt;, 그리고 &lt;strong&gt;커스텀 데코레이터(Custom Decorator)&lt;/strong&gt; 를 활용한 기능 확장 등은 공식 문서를 참고해 보시길 추천드립니다.&lt;/p&gt;
&lt;h1 id=&quot;참고-자료&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0-%EC%9E%90%EB%A3%8C&quot; aria-label=&quot;참고 자료 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고 자료&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.android.com/guide/navigation/navigation-3&quot;&gt;Jetpack Navigation 3 공식 문서&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Jetpack DataStore: Android 데이터 저장을 위한 최신 솔루션]]></title><description><![CDATA[왜 DataStore인가? (SharedPreferences의 한계) 오랜 시간 동안 Android에서 간단한 키-값(Key-Value…]]></description><link>https://songwoody.github.io/blog/android-etc/jetpack-datastore/</link><guid isPermaLink="false">https://songwoody.github.io/blog/android-etc/jetpack-datastore/</guid><pubDate>Wed, 03 Dec 2025 09:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;왜-datastore인가-sharedpreferences의-한계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%99%9C-datastore%EC%9D%B8%EA%B0%80-sharedpreferences%EC%9D%98-%ED%95%9C%EA%B3%84&quot; aria-label=&quot;왜 datastore인가 sharedpreferences의 한계 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;왜 DataStore인가? (SharedPreferences의 한계)&lt;/h2&gt;
&lt;p&gt;오랜 시간 동안 Android에서 간단한 키-값(Key-Value) 형태의 데이터를 저장하는 데에는 &lt;code class=&quot;language-text&quot;&gt;SharedPreferences&lt;/code&gt;가 주로 사용되어 왔습니다. 하지만 &lt;code class=&quot;language-text&quot;&gt;SharedPreferences&lt;/code&gt;는 몇 가지 중대한 한계를 가지고 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;UI 스레드에서 안전하지 않음&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;apply()&lt;/code&gt;는 비동기적으로 디스크에 쓰기를 하지만, &lt;code class=&quot;language-text&quot;&gt;commit()&lt;/code&gt;은 UI 스레드를 블로킹할 수 있습니다. &lt;code class=&quot;language-text&quot;&gt;get*()&lt;/code&gt; 메서드 또한 메모리에 로드된 데이터를 읽어오는 것이므로, 초기 로딩 시 디스크 I/O가 발생하여 UI 스레드를 블로킹할 위험이 있습니다. 이는 ANR(Application Not Responding)을 유발할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;동기 API&lt;/strong&gt;: 모든 작업이 동기적으로 이루어지므로 메인 스레드에서 호출 시 성능 저하를 야기합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;런타임 오류 가능성&lt;/strong&gt;: 저장 및 조회 시 파싱 오류 등의 런타임 오류에 대한 보호 메커니즘이 부족합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;트랜잭션 미지원&lt;/strong&gt;: 여러 데이터를 한 번에 업데이트할 때, 부분적인 성공/실패가 발생할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이러한 문제점들을 해결하기 위해 Google은 Jetpack DataStore를 도입했습니다.&lt;/p&gt;
&lt;h2 id=&quot;jetpack-datastore란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jetpack-datastore%EB%9E%80&quot; aria-label=&quot;jetpack datastore란 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jetpack DataStore란?&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Preferences DataStore&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proto DataStore&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;preferences-datastore-사용법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#preferences-datastore-%EC%82%AC%EC%9A%A9%EB%B2%95&quot; aria-label=&quot;preferences datastore 사용법 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Preferences DataStore 사용법&lt;/h2&gt;
&lt;p&gt;가장 먼저 접하게 될 Preferences DataStore의 기본적인 사용법을 살펴보겠습니다.&lt;/p&gt;
&lt;h3 id=&quot;1-의존성-추가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%B6%94%EA%B0%80&quot; aria-label=&quot;1 의존성 추가 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 의존성 추가&lt;/h3&gt;
&lt;h3 id=&quot;2-datastore-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-datastore-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;2 datastore 생성 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. DataStore 생성&lt;/h3&gt;
&lt;h3 id=&quot;3-데이터-쓰기-저장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%93%B0%EA%B8%B0-%EC%A0%80%EC%9E%A5&quot; aria-label=&quot;3 데이터 쓰기 저장 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 데이터 쓰기 (저장)&lt;/h3&gt;
&lt;h3 id=&quot;4-데이터-읽기-조회&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%9D%BD%EA%B8%B0-%EC%A1%B0%ED%9A%8C&quot; aria-label=&quot;4 데이터 읽기 조회 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 데이터 읽기 (조회)&lt;/h3&gt;
&lt;h2 id=&quot;proto-datastore-추후-다룰-예정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#proto-datastore-%EC%B6%94%ED%9B%84-%EB%8B%A4%EB%A3%B0-%EC%98%88%EC%A0%95&quot; aria-label=&quot;proto datastore 추후 다룰 예정 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Proto DataStore (추후 다룰 예정)&lt;/h2&gt;
&lt;h2 id=&quot;결론&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B2%B0%EB%A1%A0&quot; aria-label=&quot;결론 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;결론&lt;/h2&gt;</content:encoded></item><item><title><![CDATA[서명키 유출이 부른 비극: 쿠팡 해킹 사건으로 알아보는 JWT]]></title><description><![CDATA[들어가며 tbd: 배경 및 타임라인, 사건 개요: 이번 쿠팡 해킹 사태의 주요 경과(정보 유출 인지 시점, 수사 과정 등)를 간략히 정리.
tbd: 국회 청문회 등에서 밝혀진 핵심 발언('퇴사한 관리자가 서명키를 가지고 있었다', '인증에 JWT…]]></description><link>https://songwoody.github.io/blog/login-system/jwt/</link><guid isPermaLink="false">https://songwoody.github.io/blog/login-system/jwt/</guid><pubDate>Tue, 02 Dec 2025 13:12:30 GMT</pubDate><content:encoded>&lt;h1 id=&quot;들어가며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%93%A4%EC%96%B4%EA%B0%80%EB%A9%B0&quot; aria-label=&quot;들어가며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;들어가며&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;tbd: 배경 및 타임라인, 사건 개요: 이번 쿠팡 해킹 사태의 주요 경과(정보 유출 인지 시점, 수사 과정 등)를 간략히 정리.
tbd: 국회 청문회 등에서 밝혀진 핵심 발언(&apos;퇴사한 관리자가 서명키를 가지고 있었다&apos;, &apos;인증에 JWT를 사용하고 있었다&apos;)을 제시하고, 이 발언들이 의미하는 기술적 위험성을 제기
글의 목표 제시: 이 사건을 통해 **JWT의 서명키(Secret Key)**가 무엇이며, 서명키 유출이 어떻게 대규모 정보 탈취로 이어질 수 있었는지 그 원리를 철저히 파헤쳐봅니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;jwt란-무엇이며-왜-서명키가-중요한가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jwt%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EC%99%9C-%EC%84%9C%EB%AA%85%ED%82%A4%EA%B0%80-%EC%A4%91%EC%9A%94%ED%95%9C%EA%B0%80&quot; aria-label=&quot;jwt란 무엇이며 왜 서명키가 중요한가 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JWT란 무엇이며, 왜 서명키가 중요한가?&lt;/h1&gt;
&lt;h2 id=&quot;jwt의-정의-및-필요성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jwt%EC%9D%98-%EC%A0%95%EC%9D%98-%EB%B0%8F-%ED%95%84%EC%9A%94%EC%84%B1&quot; aria-label=&quot;jwt의 정의 및 필요성 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JWT의 정의 및 필요성&lt;/h2&gt;
&lt;p&gt;tbd: JWT가 등장하게 된 배경(Scale-out, Stateless)을 간략히 설명&lt;/p&gt;
&lt;h2 id=&quot;토큰-기반-인증-vs-세션-기반-인증-관리-주체-차이&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%86%A0%ED%81%B0-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D-vs-%EC%84%B8%EC%85%98-%EA%B8%B0%EB%B0%98-%EC%9D%B8%EC%A6%9D-%EA%B4%80%EB%A6%AC-%EC%A3%BC%EC%B2%B4-%EC%B0%A8%EC%9D%B4&quot; aria-label=&quot;토큰 기반 인증 vs 세션 기반 인증 관리 주체 차이 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;토큰 기반 인증 vs 세션 기반 인증 (관리 주체 차이)&lt;/h2&gt;
&lt;p&gt;tbd: 세션: 서버가 상태(State)를 직접 관리(DB, 메모리).&lt;/p&gt;
&lt;h2 id=&quot;jwt-의-3단-구조&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jwt-%EC%9D%98-3%EB%8B%A8-%EA%B5%AC%EC%A1%B0&quot; aria-label=&quot;jwt 의 3단 구조 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;JWT 의 3단 구조&lt;/h2&gt;
&lt;p&gt;tbd: Header.Payload: 이 부분은 인코딩되었을 뿐, 누구나 쉽게 디코딩하여 내용을 볼 수 있음을 강조
tbd: Signature: 토큰이 위변조되지 않았음을 보장하는 유일한 장치이며, &lt;strong&gt;서명키&apos;&lt;/strong&gt; 를 통해서만 생성/검증됨을 강조.
tbd: JWT 토큰: 클라이언트가 토큰을 소지하며, 서버는 상태를 관리하지 않고 토큰의 유효성만 검증하는 차이점을 명확히 설명합니다. (🔑 이 &apos;검증&apos;에 서명키가 쓰임을 예고)&lt;/p&gt;
&lt;h2 id=&quot;사건의-핵심-원리-서명signature과-검증-과정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%82%AC%EA%B1%B4%EC%9D%98-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EC%84%9C%EB%AA%85signature%EA%B3%BC-%EA%B2%80%EC%A6%9D-%EA%B3%BC%EC%A0%95&quot; aria-label=&quot;사건의 핵심 원리 서명signature과 검증 과정 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;사건의 핵심 원리: 서명(Signature)과 검증 과정&lt;/h2&gt;
&lt;p&gt;tbd: 서명이 Header, Payload 그리고 **서버만 아는 서명키(Secret Key)**를 특정 해싱 알고리즘(예: HMACSHA256)을 통해 생성되는 과정을 도식화와 함께 설명
tbd:
서버가 토큰을 받음.
Header와 Payload를 추출.
서버가 보관하고 있는 서명키를 이용해 토큰의 Header, Payload로 **새로운 서명(시그니처)**을 직접 생성.
새로 만든 서명과 토큰에 담겨있던 서명을 비교하여 일치하면 토큰을 신뢰.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;쿠팡 사태가 가능했던 이유: &apos;퇴사한 관리자가 서버와 동일한 서명키를 가지고 있었기 때문에, 서버가 완벽하게 유효하다고 판단하는 위조 토큰을 만들 수 있었다.&apos;고 결론&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;직접-실습으로-이해하는-jwt-인증-플로우&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%81%EC%A0%91-%EC%8B%A4%EC%8A%B5%EC%9C%BC%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EB%8A%94-jwt-%EC%9D%B8%EC%A6%9D-%ED%94%8C%EB%A1%9C%EC%9A%B0&quot; aria-label=&quot;직접 실습으로 이해하는 jwt 인증 플로우 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;직접 실습으로 이해하는 JWT 인증 플로우&lt;/h1&gt;
&lt;p&gt;tbd: 직접 Spring Boot (JWT 라이브러리 활용) 를 만들어서 실습해보기, 클라이언트는 안드로이드로 할지 고민..&lt;/p&gt;
&lt;h1 id=&quot;마무리-서명키-관리와-jwt-보안-강화-대책&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EB%AC%B4%EB%A6%AC-%EC%84%9C%EB%AA%85%ED%82%A4-%EA%B4%80%EB%A6%AC%EC%99%80-jwt-%EB%B3%B4%EC%95%88-%EA%B0%95%ED%99%94-%EB%8C%80%EC%B1%85&quot; aria-label=&quot;마무리 서명키 관리와 jwt 보안 강화 대책 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마무리: 서명키 관리와 JWT 보안 강화 대책&lt;/h1&gt;
&lt;p&gt;tbd: JWT의 Payload 암호화보다 서명키 관리가 훨씬 중요함을 다시 한번 강조
tbd: 핵심 보안 유의점
서명키 보안: 서명키는 절대 외부에 노출되어서는 안 되며, 정기적인 키 교체(Rotation)가 필수적임. (Ex. 퇴사 시점의 키 무효화)&lt;/p&gt;
&lt;p&gt;내부자 통제: 핵심 서명키에 접근 가능한 인원을 최소화하고, 접근 기록을 엄격히 관리해야 함.&lt;/p&gt;
&lt;p&gt;JWT 활용 시 주의점:
민감 정보는 Payload에 넣지 않기 (Payload는 인코딩일 뿐 암호화가 아님).
탈취 위험을 줄이기 위해 토큰의 유효 기간(Expiry)을 짧게 설정하고, Refresh Token 활용 방안을 소개.
Blacklist 기능을 구현하여 유출/악용된 토큰을 강제로 무효화하는 방법 (Stateful하게 관리하는 트레이드오프 언급).&lt;/p&gt;</content:encoded></item><item><title><![CDATA[::compose:: Navigation2 간단하게 살펴보기]]></title><description><![CDATA[Compose Navigation2 안드로이드 애플리케이션을 개발할 때 화면 이동(Navigation) 은 사용자 경험의 핵심 요소입니다. Compose 에서 화면 이동(Navigation)을 구현할 때, 사용자가 직접 화면에 대한 상태(State…]]></description><link>https://songwoody.github.io/blog/compose/05-compose-navigation2-basic/</link><guid isPermaLink="false">https://songwoody.github.io/blog/compose/05-compose-navigation2-basic/</guid><pubDate>Wed, 26 Nov 2025 11:46:12 GMT</pubDate><content:encoded>&lt;h1 id=&quot;compose-navigation2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compose-navigation2&quot; aria-label=&quot;compose navigation2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compose Navigation2&lt;/h1&gt;
&lt;p&gt;안드로이드 애플리케이션을 개발할 때 &lt;strong&gt;화면 이동(Navigation)&lt;/strong&gt; 은 사용자 경험의 핵심 요소입니다.&lt;br&gt;
Compose 에서 화면 이동(Navigation)을 구현할 때, 사용자가 직접 화면에 대한 상태(State) 를 정의하고 이 상태 값을 변경하여 화면을 노출 시켜줄 수도 있지만, 이처럼 직접 구현하게 되면 &lt;strong&gt;상태 저장&lt;/strong&gt;, &lt;strong&gt;생명주기 관리&lt;/strong&gt;, &lt;strong&gt;백 스택 관리&lt;/strong&gt; 등 고려해야 할 점이 복잡하게 늘어납니다. (viewModel 을 사용할 경우 &lt;code class=&quot;language-text&quot;&gt;ViewModelStoerOwner&lt;/code&gt; 의 구현 등)&lt;br&gt;
그러므로 Compose 에서는 네비게이션을 쉽게 사용하기 위해 공식 &lt;code class=&quot;language-text&quot;&gt;navigation-compose&lt;/code&gt; 라이브러리를 사용합니다.&lt;br&gt;
지금 부터 Comopse Navigation 의 기본적인 개념과 간단한 예시를 통해 라이브러리를 이해해 보도록 하겠습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;본 내용은 Navigation2 에 대한 간략한 개념/사용법을 소개하며, Navigation3 는 추후 포스트에서 Navigation2 와 비교하면서 다루도록 하겠습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;compose-navigation-의-핵심-가치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compose-navigation-%EC%9D%98-%ED%95%B5%EC%8B%AC-%EA%B0%80%EC%B9%98&quot; aria-label=&quot;compose navigation 의 핵심 가치 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compose Navigation 의 핵심 가치&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;1. Type-safe (타입 안전성)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;가장 큰 장점 중 하나입니다. 기존에는 화면 간 데이터(인자)를 전달할 때 String이나 Bundle을 사용하여 타입 오류가 발생하기 쉬웠습니다. 하지만 Compose Navigation은 Route 정의 시 인자의 타입을 명시할 수 있게 되어, 잘못된 타입의 데이터를 전달하려는 시도를 컴파일 시점에 잡아낼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 상태 주도 (State-driven)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Compose의 철학을 그대로 따릅니다. 화면 이동을 명령(Imperative)하는 것이 아니라, 내비게이션 상태(State)가 변경되면 그에 따라 화면(UI)이 자동으로 갱신됩니다. 이는 내비게이션 로직을 더욱 직관적이고 예측 가능하게 만듭니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 단일 활동 아키텍처 (Single-Activity Architecture)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Compose 앱은 모든 화면을 단일 Activity 내의 여러 Composable로 구성하는 것이 권장됩니다. Compose Navigation은 이 단일 Activity 내에서 여러 화면 컴포저블 간의 전환을 효율적으로 관리하여, 앱의 복잡성을 줄이고 성능을 개선하는 데 기여합니다.&lt;/p&gt;
&lt;h1 id=&quot;library-settings&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#library-settings&quot; aria-label=&quot;library settings permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Library Settings&lt;/h1&gt;
&lt;p&gt;Navigation 2 사용&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;dependencies &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; nav_version &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2.9.6&quot;&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;implementation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;androidx.navigation:navigation-compose:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;nav_version&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;compose-navigation-핵심-요소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compose-navigation-%ED%95%B5%EC%8B%AC-%EC%9A%94%EC%86%8C&quot; aria-label=&quot;compose navigation 핵심 요소 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compose Navigation 핵심 요소&lt;/h1&gt;
&lt;p&gt;이해를 위해 먼저 예제 코드를 확인해 보겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// HomeScreen 컴포저블&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;HomeScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    onNavigateToDetail&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;itemId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    modifier&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Modifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Modifier
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        modifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; modifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fillMaxSize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        horizontalAlignment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Alignment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CenterHorizontally&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        verticalArrangement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Arrangement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Center
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Home Screen&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; style &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MaterialTheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;typography&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headlineLarge&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Spacer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Modifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// 버튼 클릭 이벤트 발생 -&gt; 콜백 함수 호출 (상위 컴포저블에 네비게이션 요청)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; itemId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;456&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;onNavigateToDetail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;itemId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Go to Detail (ID: 456)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// DetailScreen 컴포저블&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DetailScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;itemId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... DetailScreen 내용 ...&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Received Item ID: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;itemId&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;
&lt;span class=&quot;token comment&quot;&gt;// Route: Home 화면 Route (인자가 없으면 빈 클래스)&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Serializable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; HomeRoute

&lt;span class=&quot;token comment&quot;&gt;// Route: Detail 화면 Route (인자는 클래스의 속성으로 정의)&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Serializable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DetailRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; itemId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// NavHost를 포함하는 메인 컴포저블&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AppNavigationTypeSafe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ⬅️ 핵심 요소 1: NavController 생성 및 상태 저장 (네비게이션 엔진)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; navController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberNavController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// ⬅️ 핵심 요소 2: 요소 2: NavHost - 네비게이션 영역 및 목적지(Route) 등록 컨테이너&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;NavHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        navController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; navController&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
        startDestination &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; HomeRoute
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Home 화면 등록&lt;/span&gt;
        composable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;HomeRoute&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;HomeScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// ⬅️ onNavigateToDetail 콜백 함수 정의&lt;/span&gt;
                onNavigateToDetail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; itemId &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// 콜백이 호출되면, navController를 사용하여 실제 이동 실행&lt;/span&gt;
                    navController&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;navigate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DetailRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;itemId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; itemId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Detail 화면 등록 (DetailRoute 인자를 toRoute&amp;lt;T&gt;()로 타입 안전하게 추출)&lt;/span&gt;
        composable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;DetailRoute&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; backStackEntry &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; route &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; backStackEntry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;toRoute&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;DetailRoute&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;DetailScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;itemId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; route&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;itemId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;위 코드를 실무에서 쓰기에도 충분하겠지만 조금더 현대적인 네비게이션 구성을 확인하려면 &lt;a href=&quot;%22https://github.com/android/nowinandroid%22&quot;&gt;Now in Android&lt;/a&gt; 코드를 참고해 보시는길 추천드립니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;핵심요소1-navcontroller-네비게이션-컨트롤러&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC%EC%9A%94%EC%86%8C1-navcontroller-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC&quot; aria-label=&quot;핵심요소1 navcontroller 네비게이션 컨트롤러 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심요소1. NavController (네비게이션 컨트롤러)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;NavController&lt;/strong&gt; 는 네비게이션을 실제로 제어하고 관리하는 핵심 객체입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;역할&lt;/strong&gt;: 화면 전환(Navigation), 뒤로 가기(Pop), Back Stack(백 스택) 관리 등 모든 네비게이션 작업을 처리하는 네비게이션 엔진입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코드 내 위치&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; navController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberNavController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;코드 내 역할:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rememberNavController()를 통해 생성되어 컴포저블의 생명주기 동안 상태를 유지합니다.&lt;/li&gt;
&lt;li&gt;HomeScreen의 콜백 함수 내에서 실제로 화면을 이동시키는 명령을 수행합니다: &lt;code class=&quot;language-text&quot;&gt;navController.navigate(DetailRoute(itemId = itemId))&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;핵심요소2-route-경로목적지-식별자&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC%EC%9A%94%EC%86%8C2-route-%EA%B2%BD%EB%A1%9C%EB%AA%A9%EC%A0%81%EC%A7%80-%EC%8B%9D%EB%B3%84%EC%9E%90&quot; aria-label=&quot;핵심요소2 route 경로목적지 식별자 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심요소2. Route (경로/목적지 식별자)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Route&lt;/strong&gt; 는 네비게이션이 도달할 &lt;strong&gt;특정 화면(Destination)&lt;/strong&gt; 을 유일하게 식별하는 경로입니다. 전통적으로는 문자열을 사용했지만, 제공된 코드에서는 제가 선호하는 타입 안전성을 위해 &lt;code class=&quot;language-text&quot;&gt;@Serializable&lt;/code&gt; 클래스를 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;역할&lt;/strong&gt;: 특정 목적지를 가리키는 &lt;strong&gt;주소&lt;/strong&gt;의 역할을 합니다. 인자가 필요할 경우, 해당 인자를 Route 객체의 속성으로 포함합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코드 내 위치&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Serializable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; HomeRoute

&lt;span class=&quot;token annotation builtin&quot;&gt;@Serializable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DetailRoute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; itemId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;코드 내 역할&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;HomeRoute&lt;/code&gt;는 인자가 필요 없는 홈 화면의 주소를 나타냅니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;DetailRoute(val itemId: Int)&lt;/code&gt;는 &lt;code class=&quot;language-text&quot;&gt;itemId라는&lt;/code&gt; 인자를 포함하는 상세 화면의 주소를 나타냅니다. 네비게이션 시 이 객체를 전달하고 (&lt;code class=&quot;language-text&quot;&gt;navController.navigate(DetailRoute(...))&lt;/code&gt;), 목적지에서 다시 추출하여 사용합니다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;핵심요소3-navhost-네비게이션-호스트&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC%EC%9A%94%EC%86%8C3-navhost-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98-%ED%98%B8%EC%8A%A4%ED%8A%B8&quot; aria-label=&quot;핵심요소3 navhost 네비게이션 호스트 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심요소3. NavHost (네비게이션 호스트)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;NavHost&lt;/strong&gt; 는 네비게이션이 일어나는 영역을 정의하며, 현재의 &lt;code class=&quot;language-text&quot;&gt;Route&lt;/code&gt;에 해당하는 &lt;strong&gt;실제 UI(Composable)&lt;/strong&gt; 를 표시하는 컨테이너입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;역할&lt;/strong&gt;: 네비게이션 그래프(&lt;code class=&quot;language-text&quot;&gt;NavGraph&lt;/code&gt;)와 &lt;strong&gt;NavController&lt;/strong&gt; 를 연결하고, 현재 상태에 따라 적절한 &lt;code class=&quot;language-text&quot;&gt;NavDestination&lt;/code&gt;의 컴포저블을 렌더링합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코드 내 위치&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token function&quot;&gt;NavHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    navController &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; navController&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    startDestination &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; HomeRoute &lt;span class=&quot;token comment&quot;&gt;// 시작 지점&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... 목적지 등록 ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;코드 내 역할&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;navController&lt;/code&gt;와 연결되어 네비게이션 이벤트를 받습니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;startDestination&lt;/code&gt;으로 앱이 시작될 때 처음 보여줄 화면을 지정합니다.&lt;/li&gt;
&lt;li&gt;빌더(&lt;code class=&quot;language-text&quot;&gt;NavGraphBuilder.() -&gt; Unit&lt;/code&gt;)의 &lt;code class=&quot;language-text&quot;&gt;composable&amp;lt;T&gt;&lt;/code&gt; 함수를 통해 목적지들을 등록합니다. (navController.createGraph(...) 통해 그래프 객체를 만들어서 전달주는 방법도 가능합니다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;핵심요소4-navdestination-네비게이션-목적지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC%EC%9A%94%EC%86%8C4-navdestination-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98-%EB%AA%A9%EC%A0%81%EC%A7%80&quot; aria-label=&quot;핵심요소4 navdestination 네비게이션 목적지 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심요소4. NavDestination (네비게이션 목적지)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;NavDestination&lt;/strong&gt; 은 네비게이션을 통해 도달할 수 있는 하나의 독립된 화면 단위를 의미하며, 일반적으로 &lt;strong&gt;특정 Composable&lt;/strong&gt; 을 감싸고 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;역할&lt;/strong&gt;: 네비게이션 그래프 내의 노드(Node)입니다. &lt;code class=&quot;language-text&quot;&gt;composable&amp;lt;T&gt;&lt;/code&gt; 블록 하나가 하나의 NavDestination을 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코드 내 위치&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;composable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;HomeRoute&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;token function&quot;&gt;HomeScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;token comment&quot;&gt;// 그리고&lt;/span&gt;
composable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;DetailRoute&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;token function&quot;&gt;DetailScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;코드 내 역할&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;각 &lt;code class=&quot;language-text&quot;&gt;composable&amp;lt;T&gt;&lt;/code&gt; 블록은 &lt;code class=&quot;language-text&quot;&gt;HomeRoute&lt;/code&gt;와 &lt;code class=&quot;language-text&quot;&gt;DetailRoute&lt;/code&gt;라는 Route에 매핑되는 &lt;strong&gt;개별적인 목적지&lt;/strong&gt; 를 정의합니다.&lt;/li&gt;
&lt;li&gt;이 목적지 내부에 &lt;code class=&quot;language-text&quot;&gt;HomeScreen&lt;/code&gt;과 &lt;code class=&quot;language-text&quot;&gt;DetailScreen&lt;/code&gt; 컴포저블이 위치합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;핵심요소5-navgraph-네비게이션-그래프&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC%EC%9A%94%EC%86%8C5-navgraph-%EB%84%A4%EB%B9%84%EA%B2%8C%EC%9D%B4%EC%85%98-%EA%B7%B8%EB%9E%98%ED%94%84&quot; aria-label=&quot;핵심요소5 navgraph 네비게이션 그래프 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심요소5. NavGraph (네비게이션 그래프)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;NavGraph&lt;/strong&gt; 는 앱 내의 모든 NavDestination&lt;strong&gt;들을 모아 연결해&lt;/strong&gt; 놓은 집합체로, &lt;strong&gt;네비게이션의 구조&lt;/strong&gt;를 정의합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;역할&lt;/strong&gt;:&lt;br&gt;
앱의 모든 화면(Destination)과 그 화면들 간의 이동 가능한 경로(Action)를 정의하는 &lt;strong&gt;지도 또는 청사진&lt;/strong&gt; 입니다. &lt;code class=&quot;language-text&quot;&gt;NavHost&lt;/code&gt; 내의 블록 자체가 하나의 그래프를 구성합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;코드 내 위치&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token function&quot;&gt;NavHost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 이 블록 전체가 NavGraph를 정의합니다.&lt;/span&gt;
    composable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;HomeRoute&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    composable&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;DetailRoute&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;코드 내 역할&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NavHost의 블록({}) 내에 등록된 모든 &lt;code class=&quot;language-text&quot;&gt;composable&lt;/code&gt; 목적지들을 포함하여 &lt;strong&gt;&quot;이 앱에서는 HomeRoute와 DetailRoute로 이동할 수 있다&quot;&lt;/strong&gt; 는 전체 구조를 NavController에게 제공합니다.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;startDestination = HomeRoute&lt;/code&gt;는 이 그래프의 시작점을 명시합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h1&gt;
&lt;p&gt;기본적인 Compose Navigation2 라이브러리의 사용방법을 간단하게 알아봤습니다.&lt;br&gt;
Navigation3가 나온 시점에서 Navigation2 를 자세히 알아보기 보다는 기본적인 구성요소와 사용법만을 알아보고 다음 포스트에서 Navigation3 의 소개와 Navigation2와 비교했을 때의 장점을 알아보도록 하겠습니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Parcelize Parcelable 을 편하게 사용하기]]></title><description><![CDATA[Parcelize: Android 직렬화의 불편함을 해소 Parcelable은 안드로이드에서 객체를 효율적으로 직렬화하는 방법입니다. Serializable이 리플렉션을 사용해 런타임 오버헤드가 발생하는 것과 달리, Parcelable…]]></description><link>https://songwoody.github.io/blog/android-etc/parcelize/</link><guid isPermaLink="false">https://songwoody.github.io/blog/android-etc/parcelize/</guid><pubDate>Mon, 24 Nov 2025 11:12:12 GMT</pubDate><content:encoded>&lt;h1 id=&quot;parcelize-android-직렬화의-불편함을-해소&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parcelize-android-%EC%A7%81%EB%A0%AC%ED%99%94%EC%9D%98-%EB%B6%88%ED%8E%B8%ED%95%A8%EC%9D%84-%ED%95%B4%EC%86%8C&quot; aria-label=&quot;parcelize android 직렬화의 불편함을 해소 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Parcelize: Android 직렬화의 불편함을 해소&lt;/h1&gt;
&lt;p&gt;Parcelable은 안드로이드에서 객체를 효율적으로 직렬화하는 방법입니다.&lt;br&gt;
Serializable이 리플렉션을 사용해 런타임 오버헤드가 발생하는 것과 달리, Parcelable은 &lt;strong&gt;컴파일 시점(Compile Time)&lt;/strong&gt; 에 코드를 변환하기 때문에 빠르고 런타임 오버헤드가 없습니다.&lt;/p&gt;
&lt;p&gt;그러나 Parcelable을 사용하려면 개발자가 직렬화를 위한 보일러플레이트(Boilerplate) 코드를 직접 작성해야 하는 불편함이 따랐습니다.&lt;/p&gt;
&lt;p&gt;이를 해결하기 위해 &lt;strong&gt;Parcelize 플러그인&lt;/strong&gt;이 등장했습니다.&lt;/p&gt;
&lt;p&gt;Parcelize는 바이트코드로 변환되는 과정에 개입하여 @Parcelize 애너테이션 하나만 추가해도 필요한 모든 Parcelable 코드를 자동으로 생성해 줍니다. 이제 개발자는 수동으로 코드를 작성할 필요가 없습니다.&lt;/p&gt;
&lt;p&gt;이 포스트에서는 &lt;code class=&quot;language-text&quot;&gt;@Parcelize&lt;/code&gt;의 기본적인 사용법과, 개발자가 직접 수정할 수 없는 외부 Class를 직렬화하는 @TypeParceler 사용법을 알아보겠습니다.&lt;/p&gt;
&lt;h1 id=&quot;프로젝트-설정&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%84%A4%EC%A0%95&quot; aria-label=&quot;프로젝트 설정 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;프로젝트 설정&lt;/h1&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kts&quot;&gt;&lt;pre class=&quot;language-kts&quot;&gt;&lt;code class=&quot;language-kts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// gradle(:app)&lt;/span&gt;
plugins &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;token function&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;kotlin-parcelize&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kts&quot;&gt;&lt;pre class=&quot;language-kts&quot;&gt;&lt;code class=&quot;language-kts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// version catalog&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// libs.versions.toml&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;plugins&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
kotlin&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;parcelize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.jetbrains.kotlin.plugin.parcelize&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// gradle(:app) &lt;/span&gt;
plugins &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;libs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;kotlin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parcelize&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;기본-사용법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95&quot; aria-label=&quot;기본 사용법 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기본 사용법&lt;/h1&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; kotlinx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parcelize&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Parcelize

&lt;span class=&quot;token annotation builtin&quot;&gt;@Parcelize&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; age&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parcelable&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;지원-타입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A7%80%EC%9B%90-%ED%83%80%EC%9E%85&quot; aria-label=&quot;지원 타입 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;지원 타입&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Primitive Types&lt;/strong&gt; (and their boxed versions)
&lt;ul&gt;
&lt;li&gt;Int, Boolean, Long 등 (java.lang.Integer, java.lang.Boolean 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Objects&lt;/strong&gt; 과 &lt;strong&gt;enums&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;String, CharSequence&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exception&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;S&lt;strong&gt;ize, SizeF, Bundle, IBinder, IInterface, FileDescriptor&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SparseArray, SparseIntArray, SparseLongArray, SparseBooleanArray&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;모든 Serializable (Date 포함), Parcelable 구현체&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지원 되는 모든 타입에 대한 Collections&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;List&lt;/strong&gt; (내부적으로 ArrayList 로 처리하여 직렬화),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set&lt;/strong&gt; (내부적으로 LinkedHashSet 으로 처리하여 직렬화),&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Map&lt;/strong&gt; (내부적으로 LinkedHashMap 으로 처리하여 직렬화)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지원 되는 모든 타입에 대한 Arrays&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;지원 되는 모든 타입에 대한 Nullable&lt;/strong&gt; (Int?, List&lt;String&gt;?)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;custom-parcele&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-parcele&quot; aria-label=&quot;custom parcele permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom Parcele&lt;/h1&gt;
&lt;p&gt;Custom Parcele 은 &lt;code class=&quot;language-text&quot;&gt;Pacelable&lt;/code&gt; 인터페이스를 구현할 수 없는 타입을 Parcelable 객체에 포함시켜야할 때 사용됩니다.&lt;/p&gt;
&lt;h4 id=&quot;custom-parceler-사용-케이스&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#custom-parceler-%EC%82%AC%EC%9A%A9-%EC%BC%80%EC%9D%B4%EC%8A%A4&quot; aria-label=&quot;custom parceler 사용 케이스 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Custom Parceler 사용 케이스&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;외부/표준 라이브러리 타입 직렬화&lt;/strong&gt;: 개발자가 수정할 수 없는 클레스(java.util.UUID 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;커스텀 로직 사용&lt;/strong&gt;: 객체의 내부 상태를 그대로 직렬화하는 대신, 특정 필드만 직렬화하거나, 직렬화 과정에서 데이터를 압축 또는 변환하는 등의 커스텀 로직이 필요할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;parceler-사용법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parceler-%EC%82%AC%EC%9A%A9%EB%B2%95&quot; aria-label=&quot;parceler 사용법 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Parceler 사용법&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Custom Parceler&lt;/strong&gt;는 &lt;code class=&quot;language-text&quot;&gt;kotlinx.parcelize.Parceler&amp;lt;T&gt;&lt;/code&gt; 인터페이스를 구현하여 정의합니다.&lt;br&gt;
여기서 T는 직렬화하려는 타입입니다.&lt;/p&gt;
&lt;h4 id=&quot;parceler-인터페이스-정의&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#parceler-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EC%A0%95%EC%9D%98&quot; aria-label=&quot;parceler 인터페이스 정의 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Parceler 인터페이스 정의&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;create(parcel: Parcel)&lt;/strong&gt;: T (역직렬화, 읽기): Parcel에서 데이터를 읽어와 객체 T의 인스턴스를 생성하고 반환합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;write(input: T, parcel: Parcel, flags: Int) (직렬화, 쓰기)&lt;/strong&gt;: 객체 T의 인스턴스(input)를 Parcel에 기록합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 외부 데이터 예시&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ExternalData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; data1&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; data2&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Parceler 인터페이스 구현&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; ExternalParceler &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parceler&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ExternalData&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 1. [쓰기 / 직렬화]: ExternalData 객체를 Parcel에 기록&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; ExternalData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        parcel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parcel&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        flags&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// data1을 기록&lt;/span&gt;
        parcel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// data2를 기록 (순서 중요!)&lt;/span&gt;
        parcel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 2. [읽기 / 역직렬화]: Parcel에서 데이터를 읽어 ExternalData 객체 생성&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parcel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parcel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ExternalData &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 쓴 순서와 정확히 일치하게 읽어야 합니다.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; data1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parcel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; data2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; parcel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ExternalData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;@TypeParceler&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;@WriteWith&lt;/code&gt; 사용하여 Parcelable 클래스에 외부 클래스 추가&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// @TypeParceler 을 Class에 적용&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Parcelize&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@TypeParceler&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ExternalData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ExternalParceler&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ExternalData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parcelable

&lt;span class=&quot;token comment&quot;&gt;// @TypeParceler 을 프로퍼티에 적용&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Parcelize&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@TypeParceler&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ExternalData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ExternalParceler&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ExternalData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parcelable


&lt;span class=&quot;token comment&quot;&gt;// @WriteWith 을 사용하여 파라미터에 적용&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Parcelize&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token annotation builtin&quot;&gt;@WriteWith&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ExternalParceler&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; ExternalData&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parcelable

&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;ignoredonparcel-직렬화가-필요없는-프로퍼티-무시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#ignoredonparcel-%EC%A7%81%EB%A0%AC%ED%99%94%EA%B0%80-%ED%95%84%EC%9A%94%EC%97%86%EB%8A%94-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EB%AC%B4%EC%8B%9C&quot; aria-label=&quot;ignoredonparcel 직렬화가 필요없는 프로퍼티 무시 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;@IgnoredOnParcel: 직렬화가 필요없는 프로퍼티 무시&lt;/h1&gt;
&lt;p&gt;@IgnoredOnParcel 은 Parcelable 객체 내의 특정 프로퍼티를 직렬화 및 역질렬화 과정에서 제외하고 싶을 때 사용합니다.&lt;br&gt;
보통 클래스 필드, 위임(by) 나 Callback 같은 경우 Warning 으로 표시해 줍니다.&lt;/p&gt;
&lt;p&gt;사용 케이스&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;계산된 값일 경우&lt;/strong&gt;: 클래스의 다른 필드를 기반으로 런타임에 계산되는 프로퍼티인 경우 (firname+lastname = fullname)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;일시적인 상태&lt;/strong&gt;: 유지될 필요가 없는 임시 상태(예: UI 상태, 캐시된 값 등)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;직렬화 불가능 객체&lt;/strong&gt;: Parceler로도 처리할 수 없는 복잡한 객체나 &lt;strong&gt;메모리 참조(예: 콜백 리스너, Context 등)&lt;/strong&gt; 를 포함해야 할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Parcelize&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; lastName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@IgnoredOnParcel&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ignoredValue&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 직렬화 제외가 필요한 값 제외&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Parcelable &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// (IgnoredOnParcel 을 사용하지 않아도 제외 됨)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 계산된 값: 런타임에 계산되므로 Parcel에 기록할 필요가 없습니다.&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@IgnoredOnParcel&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; fullName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;firstName&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;lastName&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// (IgnoredOnParcel 을 사용하지 않아도 제외 됨)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 임시 상태 / 직렬화 불가 객체&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// @IgnoredOnParcel 어노테이션을 사용하여 이 필드를 Parcelable 로직에서 제외합니다.&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@delegate:IgnoredOnParcel&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 프로퍼티 위임 시 사용&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; tempCache&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Map&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; lazy &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;token comment&quot;&gt;// 무거운 초기화 로직...&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;mutableMapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// (IgnoredOnParcel 을 사용하지 않아도 제외 됨)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 콜백 리스너: 보통 Parcelable로 전달할 수 없으므로 무시합니다.&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@IgnoredOnParcel&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; listener&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;마무리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EB%AC%B4%EB%A6%AC&quot; aria-label=&quot;마무리 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마무리&lt;/h1&gt;
&lt;p&gt;더 자세한 내용을 알고 싶으면 &lt;a href=&quot;https://developer.android.com/kotlin/parcelize&quot;&gt;공식 문서&lt;/a&gt; 참고하시면 됩니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[::compose:: 컴포즈 단계 이해하기]]></title><description><![CDATA[Jetpack Compose 단계 사용자한테 시각적인 UI가 보일 때까지 Jetpack Compose 는 3개의 주요 단계를 거치게 됩니다. 이는 컴포즈가 컴포저블 함수를 실행해서 UI 트리를 만드는 컴포지션 단계, UI…]]></description><link>https://songwoody.github.io/blog/compose/04-compose-phases-understanding/</link><guid isPermaLink="false">https://songwoody.github.io/blog/compose/04-compose-phases-understanding/</guid><pubDate>Mon, 17 Nov 2025 13:11:50 GMT</pubDate><content:encoded>&lt;h1 id=&quot;jetpack-compose-단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#jetpack-compose-%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;jetpack compose 단계 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Jetpack Compose 단계&lt;/h1&gt;
&lt;p&gt;사용자한테 시각적인 UI가 보일 때까지 Jetpack Compose 는 3개의 주요 단계를 거치게 됩니다.&lt;br&gt;
이는&lt;br&gt;
컴포즈가 컴포저블 함수를 실행해서 UI 트리를 만드는 &lt;strong&gt;컴포지션&lt;/strong&gt; 단계,&lt;br&gt;
UI 트리를 읽고 각 노드의 크기를 &lt;em&gt;&lt;strong&gt;측정&lt;/strong&gt;&lt;/em&gt; 하고 &lt;em&gt;&lt;strong&gt;배치&lt;/strong&gt;&lt;/em&gt; 하는 단계인 &lt;strong&gt;레이아웃&lt;/strong&gt; 단계,&lt;br&gt;
배치된 UI를 기기 화면에 그리는 단계인 &lt;strong&gt;그리기(Drawing)&lt;/strong&gt; 단계가 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/4210a11634c15fa4a3d8a4c829babb03/compose_phasesl.svg&quot; alt=&quot;compose_phases&quot;&gt;&lt;/p&gt;
&lt;p&gt;이러단 단계는 일반적으로 순서대로 진행하여 프레임을 생성합니다.
하지만 BoxWithConstraints, LazyColumn, LazyRow은 예외로, 하위 요소의 컴포지션이 상위 요소의 레이아웃 단계에 따라 달라집니다.(이거는 나중에 분석해 보기로 합니다.)&lt;/p&gt;
&lt;p&gt;또한 컴포즈는 성능을 최적화하기 위해서 동일 데이터(State)가 변경될 때에는 이러한 단계를 최대한 스킵한다.&lt;/p&gt;
&lt;h1 id=&quot;단계-과정-이해하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8B%A8%EA%B3%84-%EA%B3%BC%EC%A0%95-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; aria-label=&quot;단계 과정 이해하기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;단계 과정 이해하기&lt;/h1&gt;
&lt;h2 id=&quot;1-컴포지션composition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98composition&quot; aria-label=&quot;1 컴포지션composition permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 컴포지션(Composition)&lt;/h2&gt;
&lt;p&gt;앞선 포스트 &quot;Compose 수명주기&quot; 에서도 여러번 설명한 &lt;strong&gt;컴포지션 단계&lt;/strong&gt; 입니다.&lt;br&gt;
Compose 런타임이 컴포저블 함수를 실행하고 &lt;strong&gt;UI 트리를 출력&lt;/strong&gt; 합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(조금 자세히 찾아보니 컴포지션은 &lt;strong&gt;SlotTable&lt;/strong&gt; 이라는 내부 구조에 UI 생성 로직과 데이터를 저장하며, 이 로직이 실행되어 &lt;strong&gt;LayoutNode&lt;/strong&gt; 라는 실제 렌더링 및 레이아웃을 위한 UI 트리를 구성한다고 하는데... 나중에 자세히 알아보는걸로..)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;/b718201101eb96d62180caac60eda1cd/compose_ui_tree.svg&quot; alt=&quot;compose_ui_tree&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;2-레이아웃layout&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83layout&quot; aria-label=&quot;2 레이아웃layout permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 레이아웃(Layout)&lt;/h2&gt;
&lt;p&gt;앞선 컴포지션 단계에서 생성된 UI 트리를 바탕으로 &lt;strong&gt;측정(measure)&lt;/strong&gt;, &lt;strong&gt;배치(layout)&lt;/strong&gt; 순서로 진행됩니다.&lt;br&gt;
레이아웃 단계는 루트를 시작으로 3단계 알고리즘을 순회 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;하위 요소 측정(Measure children)&lt;/strong&gt;: 노드의 하위 요소가 있을 경우 하위 요소를 측정합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;자체 크기 결정(Decide own size)&lt;/strong&gt;: 측정 값을 기반으로 노드가 자체 크기를 결정합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;하위 요소 배치(Place Children)&lt;/strong&gt;: 각 하위 노드는 노드의 자체 위치를 기준으로 배치됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 단계가 끝나면 각 레이아웃 노드는 자체 크기(너비/높이), 위치(x/y좌표) 를 가지게 됩니다.&lt;/p&gt;
&lt;p&gt;아래의 UI 트리를 예를 들어서 설명해보면&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/7fadac1cc524c2df55af501c4a81f4f4/layout_phase_tree_exam2.svg&quot; alt=&quot;layout_phase_tree&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;측정-및-크기-결정-단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B8%A1%EC%A0%95-%EB%B0%8F-%ED%81%AC%EA%B8%B0-%EA%B2%B0%EC%A0%95-%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;측정 및 크기 결정 단계 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;측정 및 크기 결정 단계&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1단계&lt;/strong&gt;: Row가 하위 요소를 측정합니다.
&lt;ul&gt;
&lt;li&gt;Row는 자신의 하위 요소인 Image와 Column을 측정합니다. 이때 Row는 자신의 제약 조건을 각각의 하위 요소에 전달합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2단계&lt;/strong&gt;: Image가 측정됩니다.
&lt;ul&gt;
&lt;li&gt;Image는 하위 요소가 없으므로 전달받은 제약 조건 내에서 자체 크기를 결정합니다.&lt;/li&gt;
&lt;li&gt;결정된 크기(너비와 높이)를 Row에 다시 보고합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3단계&lt;/strong&gt;: Column이 측정됩니다.
&lt;ul&gt;
&lt;li&gt;Column은 다음에 측정됩니다. Column은 자신의 하위 요소 (Text1, Text2)를 측정해야만 자신의 크기를 결정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;Column은 자신이 Row로부터 받은 제약 조건을 조정하여 하위 요소 (Text1, Text2)에 전달합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;4단계&lt;/strong&gt;: Text1과 Text2가 측정됩니다.
&lt;ul&gt;
&lt;li&gt;Text1이 먼저 측정됩니다. 하위 요소가 없으므로 전달받은 제약 조건 내에서 자체 크기를 결정하고 Column에 다시 보고합니다.&lt;/li&gt;
&lt;li&gt;Text2도 마찬가지로 측정되고 Column에 크기를 보고합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;5단계&lt;/strong&gt;: Column이 자체 크기를 결정합니다.
&lt;ul&gt;
&lt;li&gt;Column은 하위 요소 (Text1, Text2)로부터 보고받은 크기를 사용하여 자체 크기를 결정합니다.
&lt;ul&gt;
&lt;li&gt;너비: 하위 요소 중 가장 큰 너비를 자신의 너비로 사용합니다.&lt;/li&gt;
&lt;li&gt;높이: 하위 요소 높이의 합을 자신의 높이로 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;결정된 최종 크기를 Row에 다시 보고합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;6단계&lt;/strong&gt;: Row가 자체 크기를 결정합니다.
&lt;ul&gt;
&lt;li&gt;Row는 하위 요소 (Image, Column)로부터 보고받은 크기를 사용하여 자체 크기를 결정합니다.
&lt;ul&gt;
&lt;li&gt;너비: 하위 요소 너위의 합을 자신의 너비로 사용합니다.&lt;/li&gt;
&lt;li&gt;높이: 하위 요소 중 가장 큰 높이를 자신의 높이로 사용합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;배치-단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%B0%EC%B9%98-%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;배치 단계 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;배치 단계&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;7단계&lt;/strong&gt;: Column이 하위 요소를 배치합니다.
&lt;ul&gt;
&lt;li&gt;Column은 Text1과 Text2를 자체에 상대적으로 세로로 서로 아래에 배치합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;8단계&lt;/strong&gt;: Row가 하위 요소를 배치합니다.
&lt;ul&gt;
&lt;li&gt;Row는 Image와 Column을 자체에 상대적으로 가로로 나란히 배치합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 하위 요소의 최종 너비/높이는 Modifier.height(), Modifier.width(), Modifier.padding(), 그리고 상위 요소가 전달한 제약 조건 등 모든 &lt;strong&gt;수정자(Modifier)&lt;/strong&gt; 의 영향을 받아 결정됩니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;3-그리기drawing&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EA%B7%B8%EB%A6%AC%EA%B8%B0drawing&quot; aria-label=&quot;3 그리기drawing permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 그리기(Drawing)&lt;/h2&gt;
&lt;p&gt;그리기 단계는 UI 트리가 다시 위에서 아래로 순회하면서 각 노드가 차례로 그려지게 됩니다.&lt;/p&gt;
&lt;p&gt;아까의 레이아웃 단계의 예로 들면&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Row 의 배경색과 같은 컨텐츠를 그립니다.&lt;/li&gt;
&lt;li&gt;Image 가 자체적으로 그려집니다.&lt;/li&gt;
&lt;li&gt;Coloumn 이 자체적으로 그려집니다.&lt;/li&gt;
&lt;li&gt;Text1, Text2 가 각각 자체적으로 그려집니다.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[::compose:: Side-effect(부수효과) 개념]]></title><description><![CDATA[Side-effect 소개 Side-effect 는 Composable 함수가 자신의 주요 임무(UI…]]></description><link>https://songwoody.github.io/blog/compose/03-compose-side-effects/</link><guid isPermaLink="false">https://songwoody.github.io/blog/compose/03-compose-side-effects/</guid><pubDate>Wed, 12 Nov 2025 22:55:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;side-effect-소개&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#side-effect-%EC%86%8C%EA%B0%9C&quot; aria-label=&quot;side effect 소개 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Side-effect 소개&lt;/h1&gt;
&lt;p&gt;Side-effect 는 Composable 함수가 자신의 주요 임무(UI를 계산하는 일) 외에 외부 세계에 영향을 미치는 모든 행위 또는 변경 사항을 의미합니다.&lt;br&gt;
예를 들어&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;네트워크 요청으로 서버 데이터 업데이트&lt;/li&gt;
&lt;li&gt;SharedPreferences 값 변경&lt;/li&gt;
&lt;li&gt;File 조작, Log 기록, ViewModel의 내부데이터 변경 등등&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compose 에서는 Compasable 함수 내에서 이러한 Side-Effect 가 없고 순수한 함수의 형태로 사용하는 것이 좋습니다.  그러나 실제 개발을 하다보면 Side-Effect 가 필요한 상황이 많고 이럴 때에는 Jetpack Compose 에서 제공하는 Side-Effect API 를 사용해서 처리합니다.
Side-Effect API 는 컴포저블의 생명주기를 인식하여 관리된 환경을 제공합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;  참고  
  &quot;컴포저블 함수는 최대한 순수함수의 형태로 유지하는게 좋고  
  Side-Effect API 도 남용하지 않는 것이 좋습니다.&quot;  
  이 말은 나중에 설명하게될 컴포즈의 중요한 개념인  
  State Hoisting(상태 끌어올리기) 와도 이어지는 내용입니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;side-effect-api-와-사용사례&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#side-effect-api-%EC%99%80-%EC%82%AC%EC%9A%A9%EC%82%AC%EB%A1%80&quot; aria-label=&quot;side effect api 와 사용사례 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Side-effect API 와 사용사례&lt;/h1&gt;
&lt;p&gt;많이 사용하는 Side-effect API 를 소개하고 이해를 위한 예제를 같이 보겠습니다.&lt;/p&gt;
&lt;h2 id=&quot;launchedeffect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#launchedeffect&quot; aria-label=&quot;launchedeffect permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;LaunchedEffect&lt;/h2&gt;
&lt;p&gt;LaunchedEffect는 컴포저블이 생성될 때 매개변수로 전달된 코루틴 블록이 호출되며, key 가 바뀌지 않는 이상 컴포저블 생명주기 동안 한번만 실행됩니다.(즉, key 가 바뀌지 않는 이상 리컴포지션에 반응하지 않음)&lt;br&gt;
&lt;em&gt;&lt;strong&gt;key 가 변경&lt;/strong&gt;&lt;/em&gt; 되면 기존 코루틴은 &lt;code class=&quot;language-text&quot;&gt;취소&lt;/code&gt;되고 &lt;code class=&quot;language-text&quot;&gt;다시 실행&lt;/code&gt;됩니다.&lt;br&gt;
&lt;em&gt;&lt;strong&gt;컴포지션이 종료&lt;/strong&gt;&lt;/em&gt; 되면 코루틴은 &lt;code class=&quot;language-text&quot;&gt;취소&lt;/code&gt; 됩니다.&lt;br&gt;
주로 화면이 그려졌을 때 초기화 작업(예: 네트워크 통신으로 데이터 불러오기) 에 사용됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;LaunchedEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key1&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Any&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; block&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; CoroutineScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h5 id=&quot;launched-effect-예제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#launched-effect-%EC%98%88%EC%A0%9C&quot; aria-label=&quot;launched effect 예제 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Launched Effect 예제&lt;/h5&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyComposable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onIncrement&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mutableStateOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Loading...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;LaunchedEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EffectTest&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;LaunchedEffect 시작: 사용자 이름 가져오는 중...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchUserName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Column &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!, count: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            modifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Modifier
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onIncrement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Click Me&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 카운트 증가 (리컴포지션 유발)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchUserName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1초 지연 시뮬레이션&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Jame&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Json&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dean&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;예제를 실행해보면 MyComposable 이 처음 호출될 때에만 LaunchedEffect 가 호출되고 이후 &quot;Click Me&quot; 버튼을 클릭해서 리컴포지션을 유발해도 LaunchedEffect 가 호출되지 않는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h5 id=&quot;launched-effect-취소-예제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#launched-effect-%EC%B7%A8%EC%86%8C-%EC%98%88%EC%A0%9C&quot; aria-label=&quot;launched effect 취소 예제 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Launched Effect 취소 예제&lt;/h5&gt;
&lt;p&gt;이제 Luanched Effect 의 코루틴이 key 값에 의해 취소되는 과정을 보겠습니다.&lt;br&gt;
테스트를 위해 key 에 Unit 이 아닌 count 값을 줘서 count 가 바뀔때 마다 Launced Effect 의 코루틴이 취소되고 다시 실행되도록 하고 try-catch 에서 CancellationException 로 코루틴 취소를 로그로 확인하겠습니다.&lt;br&gt;
편의를 위해 fetchUserName 의 딜레이를 5초로 늘려주겠습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MyComposable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onIncrement&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mutableStateOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Loading...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;LaunchedEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EffectTest&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Key &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: LaunchedEffect 시작: 사용자 이름 가져오는 중...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchUserName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CancellationException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EffectTest&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Key &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: 이전 코루틴이 취소되었습니다!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EffectTest&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;--- LaunchedEffect 종료 (Key: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;) ---&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Column &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;!, count: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            modifier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Modifier
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onIncrement&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Click Me&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 카운트 증가 (리컴포지션 유발)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchUserName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5초 지연 시뮬레이션&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Jame&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Json&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dean&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;D  Key 0: LaunchedEffect 시작: 사용자 이름 가져오는 중...
W  Key 0: 이전 코루틴이 취소되었습니다!
D  --- LaunchedEffect 종료 (Key: 0) ---
D  Key 1: LaunchedEffect 시작: 사용자 이름 가져오는 중...
D  --- LaunchedEffect 종료 (Key: 1) ---&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Count 0 일 때 버튼을 한번더 누르게되면 key 값이 바뀌게 되어 이전 코루틴은 취소되고 Luanched Effact 가 다시 호출되는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;remembercoroutinescope&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#remembercoroutinescope&quot; aria-label=&quot;remembercoroutinescope permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;rememberCoroutineScope&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberCoroutineScope&lt;/code&gt;는 컴포즈 환경에서 CoroutineScope 객체를 생성하고 기억(remember)하는 함수입니다.&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;Button&lt;/code&gt; 의 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; 과 같은 일반 콜백에서 &lt;code class=&quot;language-text&quot;&gt;suspend&lt;/code&gt; 함수를 호출하려면 코루틴 스코프를 사용해야 되는데,&lt;br&gt;
이때 코루틴 스코프를 새로 만들지 않고 rememberCoroutineScope 사용해서 한번만 생성하여 안전하게 처리할 수 있습니다.&lt;/p&gt;
&lt;p&gt;핵심 역할&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scope 생성&lt;/strong&gt;: 컴포저블 내에서 코루틴을 실행할 수 있는 범위를 제공합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;생명 주기 연결&lt;/strong&gt;: 이 Scope는 해당 컴포저블의 컴포지션 생명 주기에 연결됩니다. 즉, 컴포저블이 화면에서 사라지면(Dispose), 이 Scope와 그 안에서 실행 중이던 모든 코루틴이 자동으로 취소됩니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;명령형 실행&lt;/strong&gt;: 주로 onClick이나 onValueChange와 같은 사용자 이벤트 콜백 내에서 launch를 호출하여 코루틴을 시작할 때 사용됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id=&quot;remembercoroutinescope-예제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#remembercoroutinescope-%EC%98%88%EC%A0%9C&quot; aria-label=&quot;remembercoroutinescope 예제 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;rememberCoroutineScope 예제&lt;/h5&gt;
&lt;p&gt;스낵바를 문제를 통해 확인해 보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Scaffold&lt;/code&gt;의 &lt;code class=&quot;language-text&quot;&gt;snackbarHostState&lt;/code&gt;를 사용하여 스낵바를 표시하려면 &lt;code class=&quot;language-text&quot;&gt;suspend&lt;/code&gt; 함수인 &lt;code class=&quot;language-text&quot;&gt;showSnackbar()&lt;/code&gt;를 호출해야 합니다. 하지만 &lt;code class=&quot;language-text&quot;&gt;Button&lt;/code&gt;의 &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; 블록은 일반 함수이므로 &lt;code class=&quot;language-text&quot;&gt;suspend&lt;/code&gt; 함수를 직접 호출할 수 없습니다.&lt;/p&gt;
&lt;p&gt;🚫 &lt;em&gt;&lt;strong&gt;Case 1&lt;/strong&gt;&lt;/em&gt;: &lt;code class=&quot;language-text&quot;&gt;onClick&lt;/code&gt; 에서 suspend 함수 호출 (컴파일 오류)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// [문제점] onClick은 suspend 함수를 직접 호출할 수 없음&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;BadSnackbarExample&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snackbarHostState&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SnackbarHostState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// 🚨 컴파일 오류 발생: suspend 함수는 코루틴 내에서만 호출 가능&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// snackbarHostState.showSnackbar(&quot;메시지&quot;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;스낵바 표시&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 문제를 해결하기 위해 깔끔하지 않은(그리고 틀린) 방법은 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;🚫 &lt;em&gt;&lt;strong&gt;Case 2&lt;/strong&gt;&lt;/em&gt;: 깔끔하지 않은 해결책 (GlobalScope 사용)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;BadSnackbarExample&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snackbarHostState&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SnackbarHostState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// 🚨 문제점 1: GlobalScope 사용은 지양됨 (앱 전체 생명주기에 연결되어 취소 관리가 어려움)&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// 🚨 문제점 2: 해당 컴포저블이 사라져도 작업이 계속 실행될 수 있음&lt;/span&gt;
            GlobalScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
                snackbarHostState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showSnackbar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;메시지&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;스낵바 표시&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;✅ &lt;em&gt;&lt;strong&gt;Case 3&lt;/strong&gt;&lt;/em&gt;: rememberCoroutineScope 사용 (깔끔한 코드)&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberCoroutineScope&lt;/code&gt;를 사용하여 컴포저블의 생명 주기에 맞는 Scope를 만들고, 이 Scope를 사용하여 onClick 이벤트 내에서 안전하게 코루틴을 시작합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ Case 2: rememberCoroutineScope 사용 (권장되는 방법)&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GoodSnackbarExample&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snackbarHostState&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; SnackbarHostState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ✨ 장점 1: 컴포저블의 생명주기에 연결된 Scope를 안전하게 기억 (remember)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; scope &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberCoroutineScope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 

    &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// ✨ 장점 2: 해당 Scope 내에서 코루틴을 시작하여 suspend 함수 호출&lt;/span&gt;
            scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
                &lt;span class=&quot;token comment&quot;&gt;// ✨ 장점 3: 컴포저블이 사라지면 이 코루틴도 자동 취소됨&lt;/span&gt;
                snackbarHostState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;showSnackbar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                    message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;메시지가 표시되었습니다!&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    actionLabel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;닫기&quot;&lt;/span&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;스낵바 표시&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;rememberupdatedstate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#rememberupdatedstate&quot; aria-label=&quot;rememberupdatedstate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;rememberUpdatedState&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberUpdatedState&lt;/code&gt; 는 오래 지속되는 Side-Effect API 를 사용할 때 항상 최신의 상태나, 콜백을 참조할 수 있도록 해주는 함수입니다.&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;LaunchedEffec&lt;/code&gt; 나 &lt;code class=&quot;language-text&quot;&gt;DisposableEffect&lt;/code&gt; 를 사용해 오랜 시간 대기 후 전달받은 State 를 읽게 되면 이 상태 값은 콜백 블록이 실행되었을 때 캡처된 상태를 가져오게 됩니다.&lt;br&gt;
만약 이게 아닌 최신의 상태값을 가져와야할 경우 사용하는 Effect가 rememberUpdatedState 입니다.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;rememberUpdatedState&lt;/code&gt;는 예를 들어서 설명하는게 이해가 쉬운데,&lt;br&gt;
컴포져블이 생성된 후 5초 뒤 최신 상태값을 로그로 출력해야 되는 시나리로를 가정해 봅시다.&lt;/p&gt;
&lt;p&gt;🚫 &lt;strong&gt;Case 1&lt;/strong&gt;: &lt;code class=&quot;language-text&quot;&gt;rememberUpdatedState&lt;/code&gt; 미사용 (오래된 콜백 참조 문제)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ⚠️ Stale State 문제가 발생하는 Composable 함수&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;BadStateContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onClick&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// LaunchedEffect의 Key가 Unit이므로, 컴포저블이 화면에 있는 동안 단 한 번만 실행됨 (재시작되지 않음)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;LaunchedEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 1. LaunchedEffect가 처음 실행될 때의 count 값만 캡처하여 기억함&lt;/span&gt;
        Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Remember&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5초 타이머 시작! 초기 count: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5초 동안 비동기 대기&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 2. 5초 후 호출 시점: &lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 외부에서 Button 클릭으로 인해 count가 1, 2, 3 등으로 증가해도, &lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 이 블록 안의 count는 LaunchedEffect가 시작될 때 캡처한 &apos;오래된 초기값&apos;($count)만 유지하고 출력합니다.&lt;/span&gt;
        Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Remember&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5초 후 LaunchedEffect 내부의 count: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ❌ Stale State(오래된 상태) 출력!&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Column &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 이 Text는 리컴포지션될 때마다 최신 count 값을 즉시 반영하여 출력함&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;현재 화면에 표시되는 최신 count: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Click Me (count 증가)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;✅ 올바른 처리: rememberUpdatedState 사용&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✨ LaunchedEffect 내부에서 최신 상태를 참조하는 Composable 함수&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CorrectStateContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onClick&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// 💡 해결책: &apos;count&apos; 값을 rememberUpdatedState로 래핑하여 최신 값을 기억합니다.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// &apos;latestCount&apos;는 컴포저블이 리컴포즈될 때마다 자동으로 최신 count 값으로 업데이트됩니다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; latestCount &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberUpdatedState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Key = Unit: 타이머는 절대 재시작되지 않고 5초 동안 실행을 유지합니다.&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;LaunchedEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
        Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Remember&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5초 타이머 시작! (재시작 없음)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;token function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 5초 동안 비동기 대기&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// 1. 5초 후 호출 시점: &lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// LaunchedEffect는 &apos;latestCount&apos;를 참조합니다. &lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// latestCount는 불변 객체이지만, 내부적으로는 가장 최신 업데이트된 count 값을 가지고 있습니다.&lt;/span&gt;
        Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Remember&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;5초 후 LaunchedEffect 내부의 최신 count: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;latestCount&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ✅ 최신 상태(Latest State) 출력!&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    Column &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 화면 표시는 여전히 최신 count 값을 즉시 반영&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;현재 화면에 표시되는 최신 count: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;count&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
        &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Click Me (count 증가)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;예시에서는 매개변수로 &lt;strong&gt;상태 값(data)&lt;/strong&gt; 을 전달받는 경우를 다뤘지만, 이는 콜백 함수를 매개변수로 전달받을 때도 동일하게 적용됩니다.
&lt;strong&gt;콜백&lt;/strong&gt;을 5초 후에 실행해야 한다고 하면&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;rememberUpdatedState&lt;/code&gt; 를 사용하지 않으면 5초 사이에 리컴포지션이 호출되어 콜백이 변경 되더라도 &lt;strong&gt;최신 콜백이 아닌 이전에 캡쳐된 콜백이 호출되는 문제&lt;/strong&gt; 를 겪을 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;disposableeffect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#disposableeffect&quot; aria-label=&quot;disposableeffect permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;DisposableEffect&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DisposableEffect&lt;/code&gt; 은 Composable이 Composition에 진입하거나 (초기화) 키가 변경될 때 호출됩니다.&lt;br&gt;
DisposableEffect(key1, key2, ...) 형태로 사용하며, 이는 내부적으로 &lt;strong&gt;DisposableEffectScope&lt;/strong&gt;를 확장하는 람다 함수를 인수로 받습니다. (DisposableEffectScope는 &lt;strong&gt;코루틴 스코프&lt;/strong&gt; 가 아님)&lt;br&gt;
&lt;code class=&quot;language-text&quot;&gt;DisposableEffectScope&lt;/code&gt; 는 onDispose { ... } 블록 구현을 강제합니다. 이 블록은 Composable이 Composition에서 제거될 때 (화면 전환, 컴포넌트 폐기 등) 또는 key 값이 변경되어 Effect가 재실행될 때 호출되어 리소스를 정리합니다.&lt;/p&gt;
&lt;p&gt;공식 문서 예제가 가장 이해가 좋은 예인것 같아서 주석만 수정해서 확인해 보면,&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;HomeScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    lifecycleOwner&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LifecycleOwner &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; LocalLifecycleOwner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    onStart&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;Unit&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &apos;시작됨(started)&apos; 분석 이벤트를 전송&lt;/span&gt;
    onStop&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;Unit &lt;span class=&quot;token comment&quot;&gt;//&apos;종료됨(stopped)&apos; 분석 이벤트를 전송&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 새로운 람다가 제공될 때 현재 람다를 안전하게 업데이트합니다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; currentOnStart &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberUpdatedState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onStart&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; currentOnStop &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberUpdatedState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onStop&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// `lifecycleOwner`가 변경되면, 이 effect를 폐기하고 재설정합니다&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;DisposableEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lifecycleOwner&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 분석 이벤트 전송을 위해 저장된 콜백을 트리거하는 옵저버를 생성합니다.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; LifecycleEventObserver &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; Lifecycle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ON_START&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                currentOnStart&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; Lifecycle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ON_STOP&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                currentOnStop&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        lifecycleOwner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lifecycle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;observer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; observer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 이 effect를 호출한 컴포저블이 사라지게 되면 옵저버를 제거합니다.&lt;/span&gt;
        onDispose &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            lifecycleOwner&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lifecycle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeObserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;observer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;DisposableEffect(lifecycleOwner)를 사용하면, 해당 HomeScreen Composable이 화면에 나타나고 사라지는 시점을 Activity의 생명주기에 정확히 맞추어 정리(dispose) 및 초기화할 수 있습니다.&lt;br&gt;
예를 들어, HomeScreen이 NavHost를 통해 네비게이션으로 진입하고 나갈 때, 이 이벤트 추적이 정확히 시작되고 중지되어야 합니다.&lt;/p&gt;
&lt;p&gt;만약, 모든 화면에서 이벤트 처리를 해야하는데 &lt;code class=&quot;language-text&quot;&gt;DisposableEffect&lt;/code&gt; 가 없다면?&lt;br&gt;
화면(Composable)별 이벤트를 처리하기 위해 결국 &lt;strong&gt;Activity의 onStart()&lt;/strong&gt; 에 의존하게 되며, 이는 코드를 복잡하게 만듭니다.&lt;/p&gt;
&lt;h2 id=&quot;sideeffect&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sideeffect&quot; aria-label=&quot;sideeffect permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SideEffect&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SideEffect&lt;/code&gt;는 컴포저블이 컴포지션/리컴포지션이 발생할 때 마다 호출됩니다.&lt;br&gt;
Compose의 상태를 외부와 공유할 때 사용됩니다. 만약, 특정 화면에서 사용자가 계정 전환을 해서 userType 이라는 FirebaseAnalytics 객체의 속성값이 변경되어야 한다고 가정해보겠습니다. FirebaseAnalytics 객체는 생성된 상태에서 리컴포즈 시 userType 속성만 변경해야합니다.&lt;br&gt;
이럴 때 사용하는 것이 &lt;code class=&quot;language-text&quot;&gt;SideEffect&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;코드 예시를 보면&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberFirebaseAnalytics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; User&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FirebaseAnalytics &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// remember 로 FirebaseAnalytics 는 한번만 생성&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; analytics&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FirebaseAnalytics &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;FirebaseAnalytics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// user 변경에 의해 컴포지션이 발생할 때마다,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 현재 User의 userType으로 FirebaseAnalytics를 업데이트 합니다. &lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 이로써 향후 모든 분석 이벤트에 해당 메타데이터가 첨부되도록 보장합니다.&lt;/span&gt;
    SideEffect &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        analytics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setUserProperty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userType&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; analytics
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;producestate&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#producestate&quot; aria-label=&quot;producestate permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;produceState&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;produceState&lt;/code&gt; 는 비동기 소스에서 발생하는 데이터를 Compose 의 &lt;code class=&quot;language-text&quot;&gt;State&lt;/code&gt;로 변환하고 관리하는데 사용됩니다.
특징으로는&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;초기 값 제공: Composition에 진입할 때 즉시 사용할 수 있는 초기값&lt;/li&gt;
&lt;li&gt;코루신 생행: &lt;code class=&quot;language-text&quot;&gt;LaunchedEffect&lt;/code&gt; 처럼 코루틴 스코프를 제공하여 비동기 작업을 처리할 수 있습니다.&lt;/li&gt;
&lt;li&gt;값 업데이트: 코루틴 내부에서 &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; 속성을 통해 값을 업데이트하면, 이 상태를 사용하는 컴포저블이 리컴포즈 됩니다.&lt;/li&gt;
&lt;li&gt;자동정리: 컴포저블이 Composition 에서 제거되거나 키가 변경되면, 내부 코루틴이 자동으로 취소됩니다.(&lt;code class=&quot;language-text&quot;&gt;LuanchedEffect&lt;/code&gt; 와 유사)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Flow, LiveData, Rxjava, Listener 등을 사용한 외부 구독 기반 생태를 컴포지션으로 변환할 때 주로 사용합니다.&lt;/p&gt;
&lt;p&gt;공식문서 예제는 Image 로드 초기값 &lt;code class=&quot;language-text&quot;&gt;Result.Loading&lt;/code&gt;을 주고 로드 성공/실패 시 상태를 업데이트해 줍니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadNetworkImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    url&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    imageRepository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ImageRepository &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ImageRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; State&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Result&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Image&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Result.Loading을 초기값으로 갖는 State&amp;lt;T&gt;를 생성합니다.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 만약 `url` 또는 `imageRepository`가 변경되면, 실행 중이던 생산자(producer)는 취소되고&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 새로운 입력값(url, imageRepository)으로 다시 시작됩니다.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; produceState&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Result&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Image&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; imageRepository&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// produceState 내부: LaunchedEffect와 동일하게 코루틴 환경이 제공됩니다.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 코루틴 내에서 정지 함수(suspend calls)를 호출할 수 있습니다.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; imageRepository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// State 값 업데이트: 에러(Error) 또는 성공(Success) 결과로 State를 갱신합니다.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 💡 이 &apos;value =&apos; 업데이트는 이 State를 읽고 있는 모든 Composable에서&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//    자동으로 Recomposition을 트리거합니다.&lt;/span&gt;
        value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Error
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;image&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;추가로 위치값을 계속해서 가져와야될 때 사용할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;가상 위치 매니저 (외부 시스템)&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 외부 시스템: 콜백을 통해 위치를 업데이트하는 가상의 매니저&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; latitude&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Double&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; longitude&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Double&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; LocationListener &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onLocationUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;location&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Location&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; LocationManager &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; listener&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LocationListener&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;l&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LocationListener&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        listener &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; l
        &lt;span class=&quot;token comment&quot;&gt;// 💡 최초 위치를 즉시 제공한다고 가정&lt;/span&gt;
        l&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onLocationUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;37.5665&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;126.9780&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 서울 시청&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unregisterListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        listener &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 외부에서 임의로 위치를 변경시키는 함수 (실제로는 GPS 갱신으로 발생)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;simulateLocationChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newLocation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Location&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        listener&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onLocationUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newLocation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;produceState를 사용한 위치 구독 함수&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observeCurrentLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;manager&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LocationManager&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initialLocation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Location&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; State&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Location&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// [1] produceState 시작: 초기 값(initialLocation)을 설정하고 코루틴 스코프 제공&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;produceState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initialValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; initialLocation&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; manager&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// [2] 콜백 리스너 정의: LocationManager가 위치를 업데이트하면 produceState의 &apos;value&apos;를 갱신&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; listener &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; LocationListener &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onLocationUpdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;location&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Location&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// 💡 이 코드를 통해 Compose State 값이 업데이트되고 Recomposition이 발생합니다.&lt;/span&gt;
                value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; location 
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// [3] 리스너 등록 (Effect 시작)&lt;/span&gt;
        manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;registerListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;listener&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// [4] onDispose 블록: Composable이 제거되거나 &apos;manager&apos; 키가 변경될 때 정리 작업 실행&lt;/span&gt;
        awaitDispose &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unregisterListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 리스너 해제&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Composable에서 활용&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;LocationDisplayScreen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; locationManager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;LocationManager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// 초기 위치를 &apos;0.0, 0.0&apos;으로 설정하고, observeCurrentLocation을 통해 실제 위치를 구독&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; locationState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;observeCurrentLocation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        manager &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; locationManager&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        initialLocation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; location &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; locationState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token comment&quot;&gt;// State의 현재 값 참조&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// UI 로직 (State 값이 변경될 때마다 자동 업데이트됨)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Modifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;현재 위치 정보&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; style &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MaterialTheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;typography&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;h6&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;위도 (Latitude): &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;latitude&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;경도 (Longitude): &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;longitude&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;Spacer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Modifier&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// 위치 변경 시뮬레이션 버튼&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; newLat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;latitude &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.001&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; newLon &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;longitude &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.001&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// 💡 외부 매니저의 상태를 변경하여, 콜백을 통해 Compose 상태가 갱신되도록 유도&lt;/span&gt;
            locationManager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;simulateLocationChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;newLat&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newLon&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;위치 시뮬레이션 갱신&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;produceState&lt;/code&gt; 는 실무에서 활용 범위가 넓으므로 유용하게 사용할 수 있습니다.&lt;/p&gt;
&lt;h2 id=&quot;derivedstateof&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#derivedstateof&quot; aria-label=&quot;derivedstateof permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;derivedStateOf&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;derivedStateOf&lt;/code&gt; 는 상태가 너무 자주 바뀌어서 리컴포지션이 자주 불리는 문제가 있을 경우 사용됩니다.&lt;br&gt;
스크롤 위치와 같은 상태는 자주 변경되는 항목이지만 내가 필요로 하는건 특정 임계치 이상일 때에만 UI를 변경 시켜줘야하는 경우가 있을 때 일단적인 State 는 너무 자주 바뀌어서 리컴포지션이 여러번 호출되어 오버헤드가 발생하니, 이럴 때에는 derivedStateOf 를 사용해서 특정 임계치 일때만 방응 하도록 할 수 있습니다.(Flow를 사용해봤다면 &lt;code class=&quot;language-text&quot;&gt;distinctUntilChanged()&lt;/code&gt; 와 유사하다고 생각하시면 됩니다.)&lt;br&gt;
단, &lt;code class=&quot;language-text&quot;&gt;derivedStateOf&lt;/code&gt; 는 공식 문서에서도 강조하길 비용이 많이드는 작업이므로 꼭 불필요한 리컴포지션 방지를 위해서만 사용해야지, A상태와 B상태를 합쳐서 새로운 상태를 만들때와 같은 의도와 다른 사용은 피해야합니다.(안티 패턴)&lt;/p&gt;
&lt;h3 id=&quot;올바른-사용-예시&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%98%AC%EB%B0%94%EB%A5%B8-%EC%82%AC%EC%9A%A9-%EC%98%88%EC%8B%9C&quot; aria-label=&quot;올바른 사용 예시 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;올바른 사용 예시&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MessageList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;messages&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Message&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// MessageList 함수는 메시지 목록을 매개변수로 받습니다.&lt;/span&gt;
    Box &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; listState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberLazyListState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// LazyColumn의 스크롤 상태를 기억합니다.&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;LazyColumn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; listState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// ... 메시지 아이템들 ...&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token comment&quot;&gt;// 첫 번째 보이는 항목이 첫 번째 항목(index 0)을 지났을 때 버튼을 표시합니다.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// 불필요한 리컴포지션을 최소화하기 위해, &lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// remembered derived state 를 사용합니다.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; showButton &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
            &lt;span class=&quot;token comment&quot;&gt;// derivedStateOf를 사용하여 불필요한 리컴포지션을 최소화합니다.&lt;/span&gt;
            derivedStateOf &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; 
                listState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstVisibleItemIndex &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 첫 번째 보이는 항목의 인덱스가 0보다 클 때 (즉, 목록이 스크롤되어 상단이 안 보일 때)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token function&quot;&gt;AnimatedVisibility&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;visible &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; showButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;ScrollToTopButton&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;맨 위로 스크롤&quot; 버튼 표시&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;derivedStateOf는 내부 상태(listState.firstVisibleItemIndex &gt; 0)의 결과가 실제로 변경될 때만 showButton이라는 State 객체를 업데이트하도록 보장합니다. (true-&gt;false, flase-&gt;true 일 때 리컴포지션)&lt;/p&gt;
&lt;h3 id=&quot;잘못된-사용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%98%EB%AA%BB%EB%90%9C-%EC%82%AC%EC%9A%A9&quot; aria-label=&quot;잘못된 사용 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;잘못된 사용&lt;/h3&gt;
&lt;p&gt;두 개의 Compose 상태를 결합해서 사용하는 잘못된 경우&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ⚠️ 사용하지 금지. derivedStateOf의 잘못된 사용 예시입니다.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; firstName &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mutableStateOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; lastName &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mutableStateOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 

&lt;span class=&quot;token comment&quot;&gt;// ❌ 나쁜 사용: derivedStateOf를 사용하여 불필요하게 fullName을 계산합니다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// (firstName이나 lastName이 변경될 때마다 fullNameBad의 &apos;State 객체&apos;가 업데이트되고 리컴포즈를 유발합니다.)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; fullNameBad &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; derivedStateOf &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;firstName&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;lastName&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; 

&lt;span class=&quot;token comment&quot;&gt;// ✅ 올바른 사용: 이 방법이 가장 단순하고 효율적입니다.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// (firstName이나 lastName이 변경되면 이 Composable이 리컴포즈될 때 fullNameCorrect가 자동으로 최신 값을 계산합니다.)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; fullNameCorrect &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;firstName&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;lastName&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;fullNameBad의 문제점:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;firstName이나 lastName 중 하나가 변경되면, 컴포저블 함수 전체가 리컴포즈됩니다.&lt;/li&gt;
&lt;li&gt;이 리컴포지션 과정에서 fullNameBad의 **내부 블록(&quot;$firstName $lastName&quot;)**이 어차피 다시 실행되어 새로운 전체 이름이 계산됩니다.&lt;/li&gt;
&lt;li&gt;derivedStateOf는 이렇게 이미 리컴포지션되는 상황에서, 계산된 결과를 또 다른 State 객체로 한 번 더 감싸서 관리하므로, &lt;strong&gt;불필요한 오버헤드&lt;/strong&gt; 만 추가하게 됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;snapshotflow&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#snapshotflow&quot; aria-label=&quot;snapshotflow permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;snapshotFlow&lt;/h1&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;snapshotFlow&lt;/code&gt; 는 Compose State 를 Flow 로 바꿔줍니다.(Compose State → Flow)&lt;br&gt;
Flow 의 강력한 연산자(예: debounce, filter, map)를 사용하여 코루틴 환경에서 처리하기 위해 사용합니다.
collect 연산자를 사용하여 로그를 전달하는 등 작업을 처리할 때 주로 사용합니다.&lt;br&gt;
단, collect 에서 상태를 변경하는 행위는 안티 패턴으로 &lt;strong&gt;양방향 상태 순환(Circular State Dependency)&lt;/strong&gt; 를 유발해 버그(무한 리컴포지션, 성능 저하)를 발생 시킬 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; listState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rememberLazyListState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;LazyColumn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; listState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;LaunchedEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;listState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    snapshotFlow &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; listState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;firstVisibleItemIndex &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; index &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;distinctUntilChanged&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; it &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// 이벤트 전송&lt;/span&gt;
            MyAnalyticsService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendScrolledPastFirstItemEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;sideeffects-api-사용시-주의-사항&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#sideeffects-api-%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%A3%BC%EC%9D%98-%EC%82%AC%ED%95%AD&quot; aria-label=&quot;sideeffects api 사용시 주의 사항 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;SideEffects API 사용시 주의 사항&lt;/h1&gt;
&lt;p&gt;LaunchedEffect, produceStateof, DisposableEffect 와 같은 SdieEffects는 키를 가지고 효과를 재시작할 수 있다.&lt;/p&gt;
&lt;p&gt;이 API 들은 아래와 같은 형태를 취합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token function&quot;&gt;EffectName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;restartIfThisKeyChanges&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; orThisKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; orThisKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; block &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 때 key 를 설정해주는 것에 따라 동작이 미묘하게 달라져 문제가 발생할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;필요한 것보다 적은 효과를 앱에 버그가 발생할 수 있습니다.
&lt;ul&gt;
&lt;li&gt;사용자 ID(userId)가 변경되었지만 key에 누락되어, Effect는 이전 사용자 ID로 네트워크 요청을 계속 보내는 경우&lt;/li&gt;
&lt;li&gt;viewLifecycleOwner 같은 인수가 key에서 빠지면, 화면이 재생성될 때마다 이전 리스너가 해제되지 않은 상태에서 새로운 리스너가 계속 추가되어 메모리 누수나 이벤트 중복 발생합니다.&lt;/li&gt;
&lt;li&gt;분석 로그를 기록하는 SideEffect에서 현재 화면의 카테고리(currentCategory)가 변경되었는데도 key에 없어, 오래된 카테고리 이름으로 로그가 기록됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;필요한 것보다 많은 효과를 다시 시작하면 비효율적일 수 있습니다.
&lt;ul&gt;
&lt;li&gt;화면에 진입할 때 한 번만 로드하면 되는 사용자 프로필 데이터가 있지만, LaunchedEffect의 key에 &lt;strong&gt;자주 변경되는 UI 상태&lt;/strong&gt; 같은 것을 포함하는 경우.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;마무리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EB%AC%B4%EB%A6%AC&quot; aria-label=&quot;마무리 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마무리&lt;/h1&gt;
&lt;p&gt;이렇게 공식문서에 나와있는 API 들을 정리해 봤습니다.&lt;br&gt;
이 API들을 이해하는 것은 Compose에서 &lt;strong&gt;비동기 작업&lt;/strong&gt;, &lt;strong&gt;외부 시스템과의 통합&lt;/strong&gt;, &lt;strong&gt;생명 주기 관리&lt;/strong&gt;, 그리고 &lt;strong&gt;불필요한 리컴포지션 방지&lt;/strong&gt;라는 네 가지 주요 과제를 해결하는 데 필수적입니다.&lt;br&gt;
자세히 정리하려고 조금 길어진 감이 있는데.. 요약하는 포스트도 하나 올려서 빠르게 볼수 있게 정리해 보겠습니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[::compose:: UI 아키텍쳐, 수명주기 이해하기]]></title><description><![CDATA[Compose 수명주기 이해하기 참고: https://developer.android.com/develop/ui/compose/lifecycle?hl=ko 핵심 내용 컴포저블의 수명 주기 3단계 호출 사이트 (Call Site…]]></description><link>https://songwoody.github.io/blog/compose/02-compose-lifecycle/</link><guid isPermaLink="false">https://songwoody.github.io/blog/compose/02-compose-lifecycle/</guid><pubDate>Fri, 24 Oct 2025 06:12:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;compose-수명주기-이해하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compose-%EC%88%98%EB%AA%85%EC%A3%BC%EA%B8%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0&quot; aria-label=&quot;compose 수명주기 이해하기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compose 수명주기 이해하기&lt;/h1&gt;
&lt;p&gt;참고: &lt;a href=&quot;https://developer.android.com/develop/ui/compose/lifecycle?hl=ko&quot;&gt;https://developer.android.com/develop/ui/compose/lifecycle?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;핵심-내용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%ED%95%B5%EC%8B%AC-%EB%82%B4%EC%9A%A9&quot; aria-label=&quot;핵심 내용 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;핵심 내용&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;컴포저블의 수명 주기 3단계&lt;/li&gt;
&lt;li&gt;호출 사이트 (Call Site)를 통한 컴포저블 식별&lt;/li&gt;
&lt;li&gt;컴포저블 인스턴스 식별 및 리컴포지션 최적화 원리&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;0-수명주기-개요&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#0-%EC%88%98%EB%AA%85%EC%A3%BC%EA%B8%B0-%EA%B0%9C%EC%9A%94&quot; aria-label=&quot;0 수명주기 개요 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;0. 수명주기 개요&lt;/h2&gt;
&lt;h6 id=&quot;컴포지션composition-이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98composition-%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;컴포지션composition 이란 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;컴포지션(Composition) 이란?&lt;/h6&gt;
&lt;p&gt;컴포지션은 &lt;strong&gt;앱의 UI를 설명하는 트리 구조&lt;/strong&gt; 이고, 컴포지션(=UI 트리)는 컴포저블(Composable)을 실행하여 생성됩니다.&lt;br&gt;
Jetpack Compose 가 초기 컴포지션 시 UI를 설명하기 위해 컴포저블을 추적(tracking)합니다.&lt;br&gt;
그러고 앱의 상태가 변경되면 Jetpack Compose는 리컴포지션(Recomposition)을 예약합니다.&lt;/p&gt;
&lt;h6 id=&quot;리컴포지션recomposition이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A6%AC%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98recomposition%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;리컴포지션recomposition이란 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;리컴포지션(Recomposition)이란?&lt;/h6&gt;
&lt;p&gt;상태(State) 변경에 따라 변경될 수 있는 컴포저블을 다시 실행한 다음, 모든 변경 사항을 반영하기 위해 Composition을 업데이트는 행위 입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;컴포지션은 초기 컴포지션을 통해서만 생성될 수 있으며, 리컴포지션을 통해서만 업데이트될 수 있습니다. 컴포지션을 수정하는 유일한 방법은 재구성입니다.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;일단 개념만 간단하게 알아놓으면 되고 나중에 최적화를 고민하게 되면 Compose 단계(Composition-Layout-Drawing), 스냅샷에 대해서 배우게 될 텐데 그때 더 자세히 다루도록 합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;1-컴포저블의-수명-주기-3단계&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%BB%B4%ED%8F%AC%EC%A0%80%EB%B8%94%EC%9D%98-%EC%88%98%EB%AA%85-%EC%A3%BC%EA%B8%B0-3%EB%8B%A8%EA%B3%84&quot; aria-label=&quot;1 컴포저블의 수명 주기 3단계 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 컴포저블의 수명 주기 3단계&lt;/h2&gt;
&lt;p&gt;컴포저블은 보통 Activity, View 등과 다르게 단순하게 &lt;strong&gt;세 가지의 수명 주기&lt;/strong&gt; 만을 가집니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;컴포지션 시간(Enter the Compositon): 컴포저블이 처음 호출되어 UI 트리에 배치됩니다.&lt;/li&gt;
&lt;li&gt;0회 이상 재구성(Recompose zero or more times): 상태 변경에 따라 컴포저블이 0번 또는 그 이상 다시 실행됩니다.&lt;/li&gt;
&lt;li&gt;컴포지션 종료(Exit the Compotion): 컴포저블이 더 이상 필요하지 않게 되어 UI 트리에 제거됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;컴포저블이 수명 주기가 더 복잡한 외부 리소스를 관리하거나 이와 상호작용해야 하는 경우 부작용(Side-effect)를 사용해야 합니다.&lt;/p&gt;
&lt;h2 id=&quot;2-호출-사이트-call-site를-통한-컴포저블-식별&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%ED%98%B8%EC%B6%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8-call-site%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%BB%B4%ED%8F%AC%EC%A0%80%EB%B8%94-%EC%8B%9D%EB%B3%84&quot; aria-label=&quot;2 호출 사이트 call site를 통한 컴포저블 식별 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 호출 사이트 (Call Site)를 통한 컴포저블 식별&lt;/h2&gt;
&lt;p&gt;그렇다면 컴포저블의 두 번째 수명 주기 단계인 &lt;strong&gt;&apos;0회 이상 재구성(Recompose)&apos;&lt;/strong&gt; 에서 Compose가 어떤 인스턴스를 업데이트해야 할지, 혹은 건너뛰어야 할지 어떻게 알 수 있을까요?&lt;br&gt;
이것을 구분하기 위해서 Compose는 각 컴포저블 호출 시 &lt;strong&gt;고유한 식별자&lt;/strong&gt; 를 부여합니다.&lt;br&gt;
이러한 식별자를 호출 사이트(Call site) 라고 합니다.&lt;br&gt;
호출 사이트는 컴포저블이 호출되는 &lt;strong&gt;소스 코드의 정확한 위치&lt;/strong&gt; 를 의미합니다.&lt;br&gt;
Compose 컴파일러는 이 위치를 기준으로 컴포지션 내에서 컴포저블의 특정 인스턴스를 식별합니다.&lt;/p&gt;
&lt;h2 id=&quot;3-컴포저블-인스턴스-식별-및-리컴포지션-최적화-원리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EC%BB%B4%ED%8F%AC%EC%A0%80%EB%B8%94-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%8B%9D%EB%B3%84-%EB%B0%8F-%EB%A6%AC%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98-%EC%B5%9C%EC%A0%81%ED%99%94-%EC%9B%90%EB%A6%AC&quot; aria-label=&quot;3 컴포저블 인스턴스 식별 및 리컴포지션 최적화 원리 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 컴포저블 인스턴스 식별 및 리컴포지션 최적화 원리&lt;/h2&gt;
&lt;p&gt;설명했듯이, Compose는 컴포지션 내 각 컴포저블 인스턴스를 호출 사이트를 통해 고유하게 식별합니다.&lt;br&gt;
이 식별 메커니즘은 단순히 UI를 생성하는 것을 넘어, 리컴포지션의 효율성을 극대화하는 핵심 원리가 됩니다.&lt;/p&gt;
&lt;h3 id=&quot;31-인스턴스-유지와-재사용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#31-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EC%9C%A0%EC%A7%80%EC%99%80-%EC%9E%AC%EC%82%AC%EC%9A%A9&quot; aria-label=&quot;31 인스턴스 유지와 재사용 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.1. 인스턴스 유지와 재사용&lt;/h3&gt;
&lt;p&gt;Compose는 UI 상태가 변경되어 리컴포지션이 발생할 때, 전체 UI를 처음부터 다시 그리지 않습니다. 대신, 변경이 필요한 특정 컴포저블만 선별적으로 재실행합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;호출 사이트의 역할: 상태가 변경되어 부모 컴포저블이 재실행되더라도, 자식 컴포저블의 호출 사이트가 동일하게 유지된다면, Compose는 이전에 생성된 동일한 인스턴스를 식별하고 재사용합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;인스턴스의 변경: 만약 리컴포지션 시 조건문($if/else)이나 흐름 변경으로 인해 특정 컴포저블의 호출 사이트 자체가 사라지거나 새로운 위치에 호출된다면, Compose는 이전 인스턴스를 &lt;strong&gt;제거(Exit the Composition)&lt;/strong&gt; 하고 새 인스턴스를 생성합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;32-리컴포지션-건너뛰기skipping의-조건&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#32-%EB%A6%AC%EC%BB%B4%ED%8F%AC%EC%A7%80%EC%85%98-%EA%B1%B4%EB%84%88%EB%9B%B0%EA%B8%B0skipping%EC%9D%98-%EC%A1%B0%EA%B1%B4&quot; aria-label=&quot;32 리컴포지션 건너뛰기skipping의 조건 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.2. 리컴포지션 건너뛰기(Skipping)의 조건&lt;/h3&gt;
&lt;p&gt;인스턴스를 유지하는 것만으로는 충분하지 않습니다.&lt;br&gt;
Compose는 성능을 위해 해당 인스턴스 실행 자체를 건너뛰어(Skip) 실행 비용을 0으로 만들려고 시도합니다.&lt;/p&gt;
&lt;p&gt;Compose가 컴포저블의 재실행을 건너뛰는 조건은 매우 명확합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;호출 사이트가 유지되어야 합니다. (인스턴스가 재사용될 수 있어야 함)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;해당 컴포저블이 받는 &lt;strong&gt;모든 입력(매개변수)&lt;/strong&gt; 이 &lt;strong&gt;안정적(Stable)&lt;/strong&gt; 이어야 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;입력 매개변수들의 값이 이전 호출과 비교했을 때 변경되지 않았어야 합니다. (비교는 주로 equals() 메서드를 사용합니다.)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt; 📝 참고: 안정적인 유형(Stable Type)이란?  
 컴포즈에게 &quot;이 데이터는 한 번 생성되면 내용이 바뀌지 않거나,  
 만약 내용이 바뀌면 시스템에 확실히 알려줄 수 있다&quot;고 약속하는 유형입니다.  
 원시 타입(Int, String 등), 불변(Immutable) 클래스,  
 그리고 MutableState&amp;lt;T&gt; 등이 안정적인 유형으로 간주됩니다.  
 안정적이지 않은(Unstable) 타입은 값이 바뀌지 않았더라도  
 리컴포지션을 건너뛸 수 없게 만듭니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;33-key-컴포저블-동일-호출-사이트에서의-식별&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#33-key-%EC%BB%B4%ED%8F%AC%EC%A0%80%EB%B8%94-%EB%8F%99%EC%9D%BC-%ED%98%B8%EC%B6%9C-%EC%82%AC%EC%9D%B4%ED%8A%B8%EC%97%90%EC%84%9C%EC%9D%98-%EC%8B%9D%EB%B3%84&quot; aria-label=&quot;33 key 컴포저블 동일 호출 사이트에서의 식별 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3.3. Key 컴포저블: 동일 호출 사이트에서의 식별&lt;/h3&gt;
&lt;p&gt;일반적으로 호출 사이트가 다르면 인스턴스도 다르게 식별되지만,
&lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; 루프나 목록처럼 하나의 호출 사이트 내에서 동일한 컴포저블이 여러 번 반복되어 호출되는 경우가 있습니다.&lt;/p&gt;
&lt;p&gt;이때는 &lt;strong&gt;호출 순서(Execution Order)&lt;/strong&gt; 가 인스턴스 식별에 사용됩니다.
하지만 목록에서 항목을 추가/삭제/재정렬하면 호출 순서가 바뀌게 되고,
이는 불필요한 리컴포지션이나 내부 상태가 꼬이는 버그를 유발할 수 있습니다.&lt;/p&gt;
&lt;p&gt;아래 코드 예제를 보면&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; test &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remember &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mutableStateListOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;가&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;나&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;다&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Woody&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Click Me&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;라&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// test.add(4) 일 때는 4에 대한 컴포저블 인스턴스만 생성됨&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Click Me&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;NumberText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Initial composition with text: 가
Initial composition with text: 나
Initial composition with text: 다
Click Me
Recomposition with text: 라
Recomposition with text: 가
Recomposition with text: 나
Initial composition with text: 다&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Index 0 에 &quot;라&quot; 를 추가했을 때&lt;br&gt;
4 번째 컴포저블에 &quot;다&quot; 라는 상태를 가진 컴포저블 인스턴스가 다시 생성되는 것을 볼 수 있습니다.
기존 &quot;가&quot;, &quot;나&quot;, &quot;다&quot; 에 대해 recompose 가 호출되고
상태가 &quot;라&quot;, &quot;가&quot;, &quot;나&quot; 로 변경 되었습니다.&lt;/p&gt;
&lt;p&gt;이러한 동작은 개발자는 첫 번째 컴포저블이 생성되기를 기대하지만 기대와 다르게 동작합니다.&lt;br&gt;
이를 해결하기 위해서는 key 를 이용해서 추가적인 정보를 알려줘야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;NumberText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Initial composition with text: 가
Initial composition with text: 나
Initial composition with text: 다
Click Me
Initial composition with text: 라&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이렇게 key 를 이용해서 컴포저블 인스턴스에 호츨 사이트 외에 추가적인 식별자(id) 를 주게되면 기대한 바와같이 추가된 &quot;라&quot; 에 대해서만 컴포저블 인스턴스가 생성됩니다.&lt;/p&gt;
&lt;p&gt;직접 한번 돌려보면서 이해해 보기를 바랍니다.&lt;/p&gt;
&lt;h2 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h2&gt;
&lt;p&gt;위에 제시된 for 루프와 key() 컴포저블의 사용 예시는 &apos;컴포저블 인스턴스 식별&apos; 메커니즘을 명확히 설명하기 위한 목적입니다.&lt;br&gt;
실제 대규모 리스트 UI를 개발할 때는 성능이 최적화된 &lt;strong&gt;LazyColumn&lt;/strong&gt;이나 &lt;strong&gt;LazyRow&lt;/strong&gt;를 사용해야 합니다.&lt;br&gt;
Lazy Composable 역시 내부적으로 동일한 key 메커니즘을 활용하여 인스턴스를 효율적으로 관리하므로,&lt;br&gt;
이 원리를 이해하여 Lazy List의 성능 최적화 방법을 이해하는데 도움이 되기를 바랍니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[::compose:: 컴포즈의 이해]]></title><description><![CDATA[Compose 이해 참고: https://developer.android.com/develop/ui/compose/mental-model?hl=ko 들어가며 과거 프로젝트에서 Jetpack Compose…]]></description><link>https://songwoody.github.io/blog/compose/01-compose-understanding/</link><guid isPermaLink="false">https://songwoody.github.io/blog/compose/01-compose-understanding/</guid><pubDate>Mon, 20 Oct 2025 21:14:03 GMT</pubDate><content:encoded>&lt;h1 id=&quot;compose-이해&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compose-%EC%9D%B4%ED%95%B4&quot; aria-label=&quot;compose 이해 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compose 이해&lt;/h1&gt;
&lt;p&gt;참고: &lt;a href=&quot;https://developer.android.com/develop/ui/compose/mental-model?hl=ko&quot;&gt;https://developer.android.com/develop/ui/compose/mental-model?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;들어가며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%93%A4%EC%96%B4%EA%B0%80%EB%A9%B0&quot; aria-label=&quot;들어가며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;들어가며&lt;/h3&gt;
&lt;p&gt;과거 프로젝트에서 Jetpack Compose를 활용한 경험이 있지만,
현 프로젝트 환경 제약으로 인해 잠시 멀리하게 되었습니다.&lt;br&gt;
다시금 Compose를 더욱 깊이 있게 이해하고 사용하기 위해,
이번 기회에 공식 문서 기반으로 핵심 개념을 재정립하는 학습 내용을 공유하고자 합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Compose는&lt;/strong&gt; 선언형 프로그래밍 패러다임을 안드로이드 UI 개발에 도입한 프레임워크입니다.
기존 XML 기반의 명령형 UI 방식이 가졌던 상태 관리 및 유지보수의 복잡성을 해소하고자 등장했으며,
개발자가 &apos;어떻게&apos; 가 아닌 &apos;무엇을&apos; 보여줄지에 집중하게 하여 생산성을 높이는 것이 핵심 목표입니다&lt;/p&gt;
&lt;h3 id=&quot;선언형-uideclarative-ui-란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A0%EC%96%B8%ED%98%95-uideclarative-ui-%EB%9E%80&quot; aria-label=&quot;선언형 uideclarative ui 란 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;선언형 UI(Declarative UI) 란?&lt;/h3&gt;
&lt;h4 id=&quot;명령형-ui의-이해-기존-방식&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%AA%85%EB%A0%B9%ED%98%95-ui%EC%9D%98-%EC%9D%B4%ED%95%B4-%EA%B8%B0%EC%A1%B4-%EB%B0%A9%EC%8B%9D&quot; aria-label=&quot;명령형 ui의 이해 기존 방식 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;명령형 UI의 이해 (기존 방식)&lt;/h4&gt;
&lt;p&gt;명령형 UI (Imperative UI) 방식에서는, 앱의 &lt;strong&gt;상태(데이터)&lt;/strong&gt; 가 사용자 상호작용 등의 이유로 변경될 때마다 개발자가 직접 UI를 업데이트해야 했습니다.&lt;/p&gt;
&lt;p&gt;이는 다음과 같은 메소드 호출을 통해 이루어졌습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;textView.setText(String)&lt;/li&gt;
&lt;li&gt;container.addChild(View)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이러한 메소드들은 뷰 자체의 상태를 변경하며, 개발자는 &quot;어떻게(How)&quot; UI 요소를 조작하여 화면을 갱신할지 상세하게 명령해야 했습니다.&lt;/p&gt;
&lt;h4 id=&quot;상태state의-정의-핵심-키워드&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%81%ED%83%9Cstate%EC%9D%98-%EC%A0%95%EC%9D%98-%ED%95%B5%EC%8B%AC-%ED%82%A4%EC%9B%8C%EB%93%9C&quot; aria-label=&quot;상태state의 정의 핵심 키워드 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;상태(State)의 정의 (핵심 키워드)&lt;/h4&gt;
&lt;p&gt;여기서 말하는 &lt;strong&gt;상태(State)&lt;/strong&gt; 는 선언형 UI를 이해하는 데 있어 가장 중요한 키워드입니다.&lt;br&gt;
상태란 시간이 지남에 따라 변할 수 있는 값을 의미하며, 이는 곧 앱의 UI에 표시되어야 하는 데이터를 나타냅니다.&lt;/p&gt;
&lt;p&gt;예시:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;사용자가 입력한 텍스트&lt;/li&gt;
&lt;li&gt;버튼의 활성화 여부 (true/false)&lt;/li&gt;
&lt;li&gt;리스트에 포함된 아이템 목록&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;선언형-ui의-등장과-차이점&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%84%A0%EC%96%B8%ED%98%95-ui%EC%9D%98-%EB%93%B1%EC%9E%A5%EA%B3%BC-%EC%B0%A8%EC%9D%B4%EC%A0%90&quot; aria-label=&quot;선언형 ui의 등장과 차이점 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;선언형 UI의 등장과 차이점&lt;/h4&gt;
&lt;p&gt;기존의 명령형 UI에서는 상태가 변경될 때마다 개발자가 직접 UI 업데이트 코드를 작성해야 하는 부담이 있었습니다.&lt;br&gt;
그러나 선언형 UI에서는 개발자가 직접 뷰를 조작하지 않습니다.&lt;br&gt;
대신, 특정 상태(데이터)가 주어졌을 때 화면이 &quot;어떤 모습&quot;이어야 하는지를 선언합니다.&lt;br&gt;
선언형 UI 환경(예: Compose, React)에서는 상태가 변경되면 자동으로 UI가 다시 그려지므로, 개발자는 &quot;어떤&quot; UI를 보여줄지에만 집중할 수 있어 개발의 복잡도가 크게 줄어들고 생산성이 향상됩니다.&lt;/p&gt;
&lt;h3 id=&quot;간단한-기본-예제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B0%84%EB%8B%A8%ED%95%9C-%EA%B8%B0%EB%B3%B8-%EC%98%88%EC%A0%9C&quot; aria-label=&quot;간단한 기본 예제 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;간단한 기본 예제&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Greeting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Stirng&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;이 예제는 화면에 name 이라는 매개변수를 받아서 Text 를 UI에 노출시킵니다.&lt;/p&gt;
&lt;p&gt;주목할 점은&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;@Composable&lt;/code&gt; 어노테이션을 지정합니다. 모든 컴포저블 함수는 이 주석이 있어합니다.&lt;/li&gt;
&lt;li&gt;매견변수를 받을 수 있다.&lt;/li&gt;
&lt;li&gt;Text 컴포저블 함수를 호출하여 텍스트를 노출 시킨다.&lt;/li&gt;
&lt;li&gt;함수는 아무것도 반환하지 않는다.&lt;/li&gt;
&lt;li&gt;이 함수는 빠르고 &lt;strong&gt;멱등성&lt;/strong&gt;을 가지며 부작용이 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;멱등성(Idempotence):&lt;/strong&gt;&lt;br&gt;
시스템, 연산 또는 함수가 &lt;strong&gt;여러 번 적용되어도 한 번 적용한 것과 같은 결과&lt;/strong&gt;를 내는 속성(property)을 의미합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;동적-콘텐츠-dynamic-content&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%8F%99%EC%A0%81-%EC%BD%98%ED%85%90%EC%B8%A0-dynamic-content&quot; aria-label=&quot;동적 콘텐츠 dynamic content permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;동적 콘텐츠 (&lt;strong&gt;Dynamic content)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;컴포즈는 XML 대신 코틀린을 사용하므로 코틀린 코드처럼 동적으로 동작할 수 있다.(if, for, when 등을 사용하여 컨텐츠 구성)&lt;/p&gt;
&lt;h3 id=&quot;재구성-recomposition&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%9E%AC%EA%B5%AC%EC%84%B1-recomposition&quot; aria-label=&quot;재구성 recomposition permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;재구성 (&lt;strong&gt;Recomposition)&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;컴포저블의 가장 핵심적인 개념으로&lt;br&gt;
재구성(Recomposition)이란 &lt;strong&gt;데이터(입력)가 변경될 때&lt;/strong&gt; 업데이트된 UI를 생성하기 위해&lt;br&gt;
컴포저블 함수가 &lt;strong&gt;다시 실행&lt;/strong&gt;되는 프로세스를 말합니다.&lt;/p&gt;
&lt;p&gt;먼저 명령형 UI 모델에의 UI 업데이트 방식을 살펴보면,
&lt;strong&gt;명령형 UI 모델&lt;/strong&gt;은 위젯을 변경하기 위해서 &lt;strong&gt;setter를 호출&lt;/strong&gt;합니다.&lt;br&gt;
하지만 컴포저블에서는 새 데이터를 사용하여 컴포저블 함수를 다시 호출합니다.&lt;br&gt;
이렇게 되면 함수가 재구성(recomposition)되면서 Compose 프레임워크는 필요한 컴포저블을 다시 그려줍니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ClickCounter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clicks&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onClick&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; Unit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;Button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;onClick &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; onClick&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;I&apos;ve been clicked &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;clicks&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; times&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;버튼이 클릭 될 때마다 clicks 값이 증가하며 clicks 을 사용하고 잇는 Text 컴포저블이 재구성 됩니다.&lt;/p&gt;
&lt;p&gt;재구성은 매우 효율적으로 동작합니다.&lt;br&gt;
새 입력 값에 따라 실제로 변경된 데이터에 의존하는 컴포저블 함수나 람다만 선택적으로 다시 실행하고,&lt;br&gt;
변경되지 않은 부분은 건너뛰어(Skip) 불필요한 연산을 방지합니다.&lt;/p&gt;
&lt;p&gt;재구성은 언제든지 &apos;건너뛸 수 있는&apos; 특성이 있으므로 컴포저블 함수의 실행 여부를 예측하기 어렵기 때문에,&lt;br&gt;
컴포저블 함수 내에서 **부작용(Side Effect)**을 직접적으로 발생시키면 사용자가 예측할 수 없는 동작을 경험할 수 있습니다.&lt;br&gt;
그렇다면 재구성 중에 피해야 할 **부작용(Side Effect)**이란 정확히 무엇일까요?&lt;br&gt;
부작용은 컴포저블 함수가 자신의 범위를 벗어나 앱의 나머지 부분, 혹은 외부 환경에까지 영향을 미치는&lt;br&gt;
모든 변경 사항을 의미합니다.&lt;br&gt;
(예: &quot;네트워크 호출, 데이터베이스 쓰기, 전역 변수 변경, 외부 상태 업데이트 등이 대표적인 부작용입니다.&quot;)&lt;/p&gt;
&lt;p&gt;공식 문서에는 아래와 같은 행위를 하면 안 된다고 나와 있는데 번역된 내용으로 보면 이해가 잘 안 되어서 추가 설명해 보겠습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;공유 객체의 속성에 쓰기 (영문: Writing to a property of a shared object)&lt;br&gt;
-&gt; 싱글톤 객체, 전역 변수 등 Compose가 그 변경을 추적하지 못하는 외부 객체의 속성을 직접 변경하지 말라.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ViewModel&lt;/code&gt;에서 식별 가능한 요소 업데이트 (영문: Updating an observable in ViewModel)&lt;br&gt;
-&gt; @Composable 함수의 &apos;렌더링 단계&apos;**에서 ViewModel이 노출하는 상태(StateFlow, LiveData, State)를 직접 변경하지 말라.&lt;/li&gt;
&lt;li&gt;공유 환경설정 업데이트 (영문: Updating shared preferences)&lt;br&gt;
-&gt; SharedPreferences 쓰기와 같이 비용이 많이 드는 파일 I/O 작업이나, DB 및 Network I/O와 같은 비동기 작업을 @Composable 함수의 렌더링 단계에서 직접 실행하지 말라.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;간단하게 코드로 보면&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Composable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;BadComposable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ViewModel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 1. 공유 객체 속성에 직접 쓰기 (UI 불일치 위험)&lt;/span&gt;
    MySingleton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 2. ViewModel의 상태를 렌더링 단계에서 직접 업데이트 (예측 불가능한 반복 실행 위험)&lt;/span&gt;
    viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;updateRenderCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 

    &lt;span class=&quot;token comment&quot;&gt;// 3. 비용이 큰 I/O 작업(네트워크/DB/SharedPreferences)을 렌더링 단계에서 직접 호출 (성능 저하 위험)&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// 💡 참고: 이 함수가 동기적으로 실행된다고 가정 (실제로는 비동기 처리가 필요)&lt;/span&gt;
    NetworkClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendEventLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 

    &lt;span class=&quot;token function&quot;&gt;Text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;test &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;I am bad! &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;MySingleton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;그래서 이런 작업이 필요하면 어떻게 하면 될까요?&lt;/p&gt;
&lt;p&gt;위에서 언급된 세 가지 행위(공유 객체 쓰기, I/O 작업, ViewModel의 업데이트)는&lt;br&gt;
@Composable 함수의 **&apos;렌더링 단계&apos;**에서 실행될 때만 위험합니다.&lt;/p&gt;
&lt;p&gt;실제 앱에서는 DB 저장, API 호출 등 부작용이 필수적입니다.&lt;br&gt;
Compose는 이러한 부작용을 안전하게 처리하기 위해 &lt;strong&gt;전용 API&lt;/strong&gt;를 제공합니다.&lt;/p&gt;
&lt;p&gt;핵심 해결책:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;사용자 상호작용에 의한 상태 변경은 $\text{Button}$의 onClick 또는 $\text{TextField}$의 &lt;strong&gt;onValueChange&lt;/strong&gt;와 같은 이벤트 핸들러 스코프에서 ViewModel 함수를 호출하여 처리합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;생명주기나 특정 상태 변화에 반응해야 하는 DB 접근, API 호출, Listener 등록/해제와 같은 복잡하고 비동기적인 부작용은&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Side Effect API&lt;/strong&gt;인 LaunchedEffect, DisposableEffect 등을 사용하여 통제된 시점에만 실행해야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;compose-를-사용할-때-알아야할-사항-다섯-가지&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#compose-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A0-%EB%95%8C-%EC%95%8C%EC%95%84%EC%95%BC%ED%95%A0-%EC%82%AC%ED%95%AD-%EB%8B%A4%EC%84%AF-%EA%B0%80%EC%A7%80&quot; aria-label=&quot;compose 를 사용할 때 알아야할 사항 다섯 가지 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Compose 를 사용할 때 알아야할 사항 다섯 가지&lt;/h3&gt;
&lt;p&gt;공식 문서에는 설명된 다섯 가지 사항이 나와있는데
내용을 보면 위에 설명한 것과 일맥상통한 내용이므로 간단하게 정리해 보겠습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;재구성은 최대한 많은 수의 구성 가능한 함수 및 람다를 건너뜁니다.&lt;/li&gt;
&lt;li&gt;재구성은 낙관적이며 취소될 수 있습니다.&lt;/li&gt;
&lt;li&gt;구성 가능한 함수는 애니메이션의 모든 프레임에서와 같은 빈도로 매우 자주 실행될 수 있습니다.&lt;/li&gt;
&lt;li&gt;구성 가능한 함수는 동시에 실행할 수 있습니다.&lt;/li&gt;
&lt;li&gt;구성 가능한 함수는 순서와 관계없이 실행할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;1-재구성은-최대한-많은-수의-구성-가능한-함수-및-람다를-건너뜁니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#1-%EC%9E%AC%EA%B5%AC%EC%84%B1%EC%9D%80-%EC%B5%9C%EB%8C%80%ED%95%9C-%EB%A7%8E%EC%9D%80-%EC%88%98%EC%9D%98-%EA%B5%AC%EC%84%B1-%EA%B0%80%EB%8A%A5%ED%95%9C-%ED%95%A8%EC%88%98-%EB%B0%8F-%EB%9E%8C%EB%8B%A4%EB%A5%BC-%EA%B1%B4%EB%84%88%EB%9C%81%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;1 재구성은 최대한 많은 수의 구성 가능한 함수 및 람다를 건너뜁니다 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1. 재구성은 최대한 많은 수의 구성 가능한 함수 및 람다를 건너뜁니다.&lt;/h4&gt;
&lt;p&gt;이는 불필요한 작업 최소화를 의미합니다.&lt;br&gt;
데이터가 실제로 변경된 컴포저블만 다시 실행하여 성능을 최적화합니다.&lt;br&gt;
따라서 개발자는 상태 변경에 반응하도록 컴포넌트를 설계하고, 함수에 불필요한 매개변수를 넣지 않도록 주의해야 합니다.&lt;/p&gt;
&lt;h4 id=&quot;2-재구성은-낙관적이며-취소될-수-있습니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%9E%AC%EA%B5%AC%EC%84%B1%EC%9D%80-%EB%82%99%EA%B4%80%EC%A0%81%EC%9D%B4%EB%A9%B0-%EC%B7%A8%EC%86%8C%EB%90%A0-%EC%88%98-%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;2 재구성은 낙관적이며 취소될 수 있습니다 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 재구성은 낙관적이며 취소될 수 있습니다.&lt;/h4&gt;
&lt;p&gt;Compose는 변경 사항을 감지하는 즉시 재구성을 시작합니다(낙관적). 하지만 그 과정에서 상태가 다시 변경되거나, 더 중요한 작업이 발생하면 진행 중인 &lt;strong&gt;재구성이 취소&lt;/strong&gt;될 수 있습니다. 따라서 재구성 가능한 함수는 부수 효과(Side Effect, UI 외의 작업)를 가지지 않도록 작성해야 합니다.&lt;/p&gt;
&lt;h4 id=&quot;3-구성-가능한-함수는-애니메이션의-모든-프레임에서와-같은-빈도로-매우-자주-실행될-수-있습니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#3-%EA%B5%AC%EC%84%B1-%EA%B0%80%EB%8A%A5%ED%95%9C-%ED%95%A8%EC%88%98%EB%8A%94-%EC%95%A0%EB%8B%88%EB%A9%94%EC%9D%B4%EC%85%98%EC%9D%98-%EB%AA%A8%EB%93%A0-%ED%94%84%EB%A0%88%EC%9E%84%EC%97%90%EC%84%9C%EC%99%80-%EA%B0%99%EC%9D%80-%EB%B9%88%EB%8F%84%EB%A1%9C-%EB%A7%A4%EC%9A%B0-%EC%9E%90%EC%A3%BC-%EC%8B%A4%ED%96%89%EB%90%A0-%EC%88%98-%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;3 구성 가능한 함수는 애니메이션의 모든 프레임에서와 같은 빈도로 매우 자주 실행될 수 있습니다 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;3. 구성 가능한 함수는 애니메이션의 모든 프레임에서와 같은 빈도로 매우 자주 실행될 수 있습니다.&lt;/h4&gt;
&lt;p&gt;이는 컴포저블 함수가 매우 빠르게 실행되어야 함을 강조합니다. 함수 내부에서 시간이 오래 걸리는 작업(예: 데이터베이스 접근, 네트워크 요청)을 직접 수행해서는 안 되며, 이는 &lt;strong&gt;LaunchedEffect&lt;/strong&gt;와 같은 Side Effect API를 통해 처리해야 합니다.&lt;/p&gt;
&lt;h4 id=&quot;4-구성-가능한-함수는-동시에-실행할-수-있습니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#4-%EA%B5%AC%EC%84%B1-%EA%B0%80%EB%8A%A5%ED%95%9C-%ED%95%A8%EC%88%98%EB%8A%94-%EB%8F%99%EC%8B%9C%EC%97%90-%EC%8B%A4%ED%96%89%ED%95%A0-%EC%88%98-%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;4 구성 가능한 함수는 동시에 실행할 수 있습니다 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;4. 구성 가능한 함수는 동시에 실행할 수 있습니다.&lt;/h4&gt;
&lt;p&gt;Compose는 성능 향상을 위해 여러 컴포저블을 병렬로(다른 스레드에서) 실행할 수 있습니다. 이는 컴포저블 함수가 스레드 안전해야 하며, 공유 상태에 접근할 때 주의해야 함을 의미합니다. 경쟁 상태(Race Condition)가 발생합니다.&lt;/p&gt;
&lt;h4 id=&quot;5-구성-가능한-함수는-순서와-관계없이-실행할-수-있습니다&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#5-%EA%B5%AC%EC%84%B1-%EA%B0%80%EB%8A%A5%ED%95%9C-%ED%95%A8%EC%88%98%EB%8A%94-%EC%88%9C%EC%84%9C%EC%99%80-%EA%B4%80%EA%B3%84%EC%97%86%EC%9D%B4-%EC%8B%A4%ED%96%89%ED%95%A0-%EC%88%98-%EC%9E%88%EC%8A%B5%EB%8B%88%EB%8B%A4&quot; aria-label=&quot;5 구성 가능한 함수는 순서와 관계없이 실행할 수 있습니다 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;5. 구성 가능한 함수는 순서와 관계없이 실행할 수 있습니다.&lt;/h4&gt;
&lt;p&gt;컴포저블은 항상 결과가 같아야 합니다. 특정 순서에 의존하는 로직을 작성하면 안 됩니다. 즉, 컴포저블은 순수 함수와 비슷하게, 입력(상태/매개변수)만으로 출력(UI)이 결정되도록 작성해야 합니다.&lt;/p&gt;
&lt;h3 id=&quot;마치며&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EC%B9%98%EB%A9%B0&quot; aria-label=&quot;마치며 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마치며&lt;/h3&gt;
&lt;p&gt;공식 문서를 저만의 언어로 정리하는 과정을 통해,&lt;br&gt;
Compose의 핵심 철학인 선언형 패러다임과 재구성(Recomposition)의 멱등성을 다시 한번 깊이 있게 이해할 수 있었습니다.&lt;br&gt;
(처음 읽었을 때와 다르게 Compose 개발 경험이 쌓이고 읽으니 새롭네요.)
특히, 재구성 중 발생하는 부작용(Side Effect)의 위험성과 이를 LaunchedEffect 등의&lt;br&gt;
전용 API로 안전하게 관리해야 하는 이유를 명확히 구분할 수 있게 되었습니다.&lt;/p&gt;
&lt;p&gt;이제 이 기본 지식을 바탕으로, 실제 앱 개발 시 상태 관리(State Management) 패턴(ViewModel, Flow)을&lt;br&gt;
어떻게 재구성의 효율성과 연결지을 수 있을지에 대해 학습을 진행할 계획입니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[로그인&보안 실전 - 초안]]></title><description><![CDATA[시리즈 구성 로그인 방식별 ID/PW, OAuth, 소셜 로그인, 패스키, 2FA 보안 항목 XSS, CSRF 세션 하이재킹 방지 실습 계획서 — 로그인 & 보안 구현 목표 다양한 로그인 방식(이메일/비밀번호, OAuth…]]></description><link>https://songwoody.github.io/blog/login-system/index/</link><guid isPermaLink="false">https://songwoody.github.io/blog/login-system/index/</guid><pubDate>Tue, 12 Aug 2025 21:27:03 GMT</pubDate><content:encoded>&lt;h1 id=&quot;시리즈-구성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%8B%9C%EB%A6%AC%EC%A6%88-%EA%B5%AC%EC%84%B1&quot; aria-label=&quot;시리즈 구성 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;시리즈 구성&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;로그인 방식별
&lt;ul&gt;
&lt;li&gt;ID/PW,&lt;/li&gt;
&lt;li&gt;OAuth,&lt;/li&gt;
&lt;li&gt;소셜 로그인,&lt;/li&gt;
&lt;li&gt;패스키,&lt;/li&gt;
&lt;li&gt;2FA&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;보안 항목
&lt;ul&gt;
&lt;li&gt;XSS,&lt;/li&gt;
&lt;li&gt;CSRF&lt;/li&gt;
&lt;li&gt;세션 하이재킹 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;실습 계획서 — 로그인 &amp;#x26; 보안 구현&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;목표&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;다양한 로그인 방식(이메일/비밀번호, OAuth, 소셜 로그인, 패스키 등) 구현&lt;/p&gt;
&lt;p&gt;각 방식별 주요 보안 위협과 대응책 적용&lt;/p&gt;
&lt;p&gt;실제 동작하는 데모 프로젝트 완성 후, 이를 바탕으로 블로그 포스트 작성&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;환경 설정&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;개발 언어/플랫폼:&lt;/p&gt;
&lt;p&gt;Web: Node.js(Express) + Vanilla JS&lt;/p&gt;
&lt;p&gt;Android: Kotlin (Jetpack Compose or XML)&lt;/p&gt;
&lt;p&gt;iOS: SwiftUI or UIKit&lt;/p&gt;
&lt;p&gt;DB: MySQL / PostgreSQL (Docker로 구동)&lt;/p&gt;
&lt;p&gt;보안 도구: HTTPS(Localhost SSL), Postman, Wireshark(패킷 검증)&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;실습 시나리오&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1단계 – 기본 이메일/비밀번호 로그인
회원가입 API 구현&lt;/p&gt;
&lt;p&gt;비밀번호 해싱(BCrypt) 적용&lt;/p&gt;
&lt;p&gt;세션 기반 로그인 구현&lt;/p&gt;
&lt;p&gt;세션 하이재킹 방지(Cookie HttpOnly, Secure 속성)&lt;/p&gt;
&lt;p&gt;2단계 – JWT 로그인
Access Token / Refresh Token 발급&lt;/p&gt;
&lt;p&gt;토큰 만료 &amp;#x26; 재발급 로직&lt;/p&gt;
&lt;p&gt;토큰 탈취 방지 전략(짧은 TTL + Sliding Session)&lt;/p&gt;
&lt;p&gt;3단계 – OAuth2 소셜 로그인
GitHub 또는 Google OAuth 연동&lt;/p&gt;
&lt;p&gt;Callback 처리 및 토큰 저장&lt;/p&gt;
&lt;p&gt;기존 사용자와 신규 사용자 구분 로직&lt;/p&gt;
&lt;p&gt;4단계 – 패스키(Passkey) / WebAuthn
WebAuthn API로 패스키 로그인 구현&lt;/p&gt;
&lt;p&gt;로컬 장치 인증 연동(FIDO2)&lt;/p&gt;
&lt;p&gt;5단계 – 2단계 인증(2FA)
이메일 OTP / Google Authenticator 연동&lt;/p&gt;
&lt;p&gt;백업 코드 발급 기능&lt;/p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;보안 실습 체크리스트&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;HTTPS 적용 및 인증서 테스트&lt;/p&gt;
&lt;p&gt;CSRF 방지 (CSRF Token 발급 및 검증)&lt;/p&gt;
&lt;p&gt;XSS 방지 (출력 시 HTML 이스케이프)&lt;/p&gt;
&lt;p&gt;브루트포스 방지(로그인 시도 제한)&lt;/p&gt;
&lt;p&gt;비밀번호 최소 길이 및 복잡성 정책 적용&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;결과물&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;각 로그인 방식별 독립된 예제 프로젝트&lt;/p&gt;
&lt;p&gt;기능 + 보안 적용 내용 정리 문서&lt;/p&gt;
&lt;p&gt;테스트 시나리오 및 검증 결과 캡처&lt;/p&gt;
&lt;p&gt;블로그 포스트 작성 초안(Initial Draft)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Poetry 사용법 정리 - python 가상환경 관리]]></title><description><![CDATA[최근 SAM(Segment-Anything-Model)을 활용해 이미지 처리 데스크톱 앱을 만들 일이 생겨, Python 개발 환경을 구성하려던 중, Poetry라는 도구를 알게 되었습니다. 그동안 Conda…]]></description><link>https://songwoody.github.io/blog/poetry-python-virtualenv-guide/</link><guid isPermaLink="false">https://songwoody.github.io/blog/poetry-python-virtualenv-guide/</guid><pubDate>Mon, 12 May 2025 22:30:00 GMT</pubDate><content:encoded>&lt;p&gt;최근 SAM(Segment-Anything-Model)을 활용해 이미지 처리 데스크톱 앱을 만들 일이 생겨,&lt;br&gt;
Python 개발 환경을 구성하려던 중, &lt;strong&gt;Poetry&lt;/strong&gt;라는 도구를 알게 되었습니다.&lt;br&gt;
그동안 Conda 를 주로 사용해왔고, 간단한 분석용 스크립트나 실험 환경을 구성할 때 꽤 유용하게 느껴졌습니다.&lt;br&gt;
하지만 이번에는 실제 배포가 가능한 데스크톱 앱을 만들어야 했기 때문에,&lt;br&gt;
&lt;strong&gt;의존성 관리, 버전 고정, 패키징&lt;/strong&gt;에 강점과 &lt;strong&gt;협업 이점&lt;/strong&gt;까지 있는 Petry 를 사용하게 되어 간단한 사용법을 공유합니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;기본적인 사용법과 가상환경 생성, Jupyter 커널 생성까지만 다루고 패키징은 따로 다루지 않습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;-1-python-설치-확인&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-1-python-%EC%84%A4%EC%B9%98-%ED%99%95%EC%9D%B8&quot; aria-label=&quot; 1 python 설치 확인 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 1. Python 설치 확인&lt;/h2&gt;
&lt;p&gt;Poetry는 Python 기반으로 동작하므로 Python이 반드시 설치되어 있어야 합니다. 아래 명령어로 설치 여부를 확인하세요.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;python &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;설치되어 있지 않다면 &lt;a href=&quot;https://www.python.org/downloads/&quot;&gt;Python 공식 사이트&lt;/a&gt;에서 설치하세요. &lt;strong&gt;설치 시 &quot;Add Python to PATH&quot; 옵션을 반드시 체크&lt;/strong&gt;해야 합니다.&lt;/p&gt;
&lt;h2 id=&quot;-2-poetry-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-2-poetry-%EC%84%A4%EC%B9%98&quot; aria-label=&quot; 2 poetry 설치 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 2. Poetry 설치&lt;/h2&gt;
&lt;p&gt;PowerShell에서 아래 명령어를 실행하세요.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;WebRequest &lt;span class=&quot;token parameter variable&quot;&gt;-Uri&lt;/span&gt; https://install.python-poetry.org -UseBasicParsing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;.Content &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; python -&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;설치 경로는 일반적으로 다음과 같습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Users&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;사용자명&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;AppData&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Roaming&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Python&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Scripts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;해당 경로를 사용자 환경 변수 또는 시스템 환경 변수에 추가해야 &lt;code class=&quot;language-text&quot;&gt;poetry&lt;/code&gt; 명령어가 정상적으로 인식됩니다.&lt;br&gt;
설치 확인:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token parameter variable&quot;&gt;--version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-3-프로젝트-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-3-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1&quot; aria-label=&quot; 3 프로젝트 생성 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 3. 프로젝트 생성&lt;/h2&gt;
&lt;h3 id=&quot;새로운-프로젝트-생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1&quot; aria-label=&quot;새로운 프로젝트 생성 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;새로운 프로젝트 생성&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry new myproject
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; myproject&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;기존-프로젝트에-poetry-적용&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EA%B8%B0%EC%A1%B4-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-poetry-%EC%A0%81%EC%9A%A9&quot; aria-label=&quot;기존 프로젝트에 poetry 적용 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;기존 프로젝트에 Poetry 적용&lt;/h3&gt;
&lt;p&gt;해당 프로젝트 폴더로 이동 -&gt; 명령어 입력&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry init&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;기본적으로 아래와 같은 프로젝트 구조가 생성됩니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;myproject/
├── pyproject.toml
├── src/
│   └── myproject/
│        └── __init__.py
└── tests/
    └── __init__.py&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-4-가상환경-생성-및-진입&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-4-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%A7%84%EC%9E%85&quot; aria-label=&quot; 4 가상환경 생성 및 진입 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 4. 가상환경 생성 및 진입&lt;/h2&gt;
&lt;p&gt;Poetry는 자체적으로 &lt;code class=&quot;language-text&quot;&gt;.venv&lt;/code&gt; 폴더를 생성해 가상환경을 관리합니다. &lt;strong&gt;2.0 버전 이상&lt;/strong&gt;에서는 쉘 기능이 플러그인으로 분리되었으므로 아래 명령으로 추가하세요.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry self &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; poetry-plugin-shell&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;가상환경 생성 및 진입:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;
poetry shell&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;특정 Python 버전을 사용할 경우:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; use python3.12&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Windows에서는 Python 경로를 직접 지정:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;env&lt;/span&gt; use C:&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Users&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;사용자명&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;AppData&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Local&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Programs&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Python&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;Python312&lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;pythonw.exe&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-5-패키지-및-라이브러리-추가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-5-%ED%8C%A8%ED%82%A4%EC%A7%80-%EB%B0%8F-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%B6%94%EA%B0%80&quot; aria-label=&quot; 5 패키지 및 라이브러리 추가 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 5. 패키지 및 라이브러리 추가&lt;/h2&gt;
&lt;p&gt;패키지 추가:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; numpy&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;버전을 고정해서 설치:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; numpy@1.26.4&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;개발용 패키지 추가:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--dev&lt;/span&gt; 패키지명&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;추가 시 &lt;code class=&quot;language-text&quot;&gt;pyproject.toml&lt;/code&gt; 파일의 &lt;code class=&quot;language-text&quot;&gt;tool.poetry.dependencies&lt;/code&gt; 섹션에 자동으로 등록됩니다.&lt;/p&gt;
&lt;h2 id=&quot;-6-가상환경-종료&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-6-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-%EC%A2%85%EB%A3%8C&quot; aria-label=&quot; 6 가상환경 종료 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 6. 가상환경 종료&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-7-python-버전-변경-시-lock-파일-재생성&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-7-python-%EB%B2%84%EC%A0%84-%EB%B3%80%EA%B2%BD-%EC%8B%9C-lock-%ED%8C%8C%EC%9D%BC-%EC%9E%AC%EC%83%9D%EC%84%B1&quot; aria-label=&quot; 7 python 버전 변경 시 lock 파일 재생성 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 7. Python 버전 변경 시 lock 파일 재생성&lt;/h2&gt;
&lt;p&gt;pyproject.toml에서 Python 버전을 수정한 후 poetry install을 실행하면 버전 불일치 오류가 발생할 수 있습니다. 이 경우 다음 명령어로 해결합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry lock
poetry &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h1 id=&quot;-jupyter-notebook에서-poetry-가상환경-사용하기&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-jupyter-notebook%EC%97%90%EC%84%9C-poetry-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0&quot; aria-label=&quot; jupyter notebook에서 poetry 가상환경 사용하기 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;💻 Jupyter Notebook에서 Poetry 가상환경 사용하기&lt;/h1&gt;
&lt;h2 id=&quot;-1-jupyter-notebook-설치&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-1-jupyter-notebook-%EC%84%A4%EC%B9%98&quot; aria-label=&quot; 1 jupyter notebook 설치 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 1. Jupyter Notebook 설치&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; jupyter ipykernel
poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--dev&lt;/span&gt; notebook ipykernel&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Jupyter Lab을 설치할 경우:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--dev&lt;/span&gt; jupyterlab ipykernel&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;-2-가상환경을-jupyter-커널로-추가&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-2-%EA%B0%80%EC%83%81%ED%99%98%EA%B2%BD%EC%9D%84-jupyter-%EC%BB%A4%EB%84%90%EB%A1%9C-%EC%B6%94%EA%B0%80&quot; aria-label=&quot; 2 가상환경을 jupyter 커널로 추가 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 2. 가상환경을 Jupyter 커널로 추가&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry run python &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; ipykernel &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--user&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; MyApp-py312 --display-name &lt;span class=&quot;token string&quot;&gt;&quot;MyApp (Python 3.12)&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;--name : 가상환경 이름&lt;/li&gt;
&lt;li&gt;--display-name : Jupyter에서 표시될 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;-3-jupyter-notebook-실행&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-3-jupyter-notebook-%EC%8B%A4%ED%96%89&quot; aria-label=&quot; 3 jupyter notebook 실행 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 3. Jupyter Notebook 실행&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;poetry run jupyter notebook&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;웹 브라우저에서 Jupyter Notebook이 열립니다.&lt;/p&gt;
&lt;h2 id=&quot;-4-jupyter-notebook-커널-변경&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-4-jupyter-notebook-%EC%BB%A4%EB%84%90-%EB%B3%80%EA%B2%BD&quot; aria-label=&quot; 4 jupyter notebook 커널 변경 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 4. Jupyter Notebook 커널 변경&lt;/h2&gt;
&lt;p&gt;Notebook 내에서 &lt;strong&gt;Kernel &gt; Change kernel&lt;/strong&gt; 메뉴에서 방금 추가한 &quot;MyApp (Python 3.12)&quot;를 선택합니다.&lt;/p&gt;
&lt;h2 id=&quot;-추가---jupyter-커널-삭제&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EC%B6%94%EA%B0%80---jupyter-%EC%BB%A4%EB%84%90-%EC%82%AD%EC%A0%9C&quot; aria-label=&quot; 추가   jupyter 커널 삭제 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;✅ 추가 - Jupyter 커널 삭제&lt;/h2&gt;
&lt;p&gt;설치된 커널 목록 확인:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;jupyter kernelspec list&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;원하는 커널 삭제:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;jupyter kernelspec uninstall MyApp-py312&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;hr&gt;
&lt;h1 id=&quot;-마무리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#-%EB%A7%88%EB%AC%B4%EB%A6%AC&quot; aria-label=&quot; 마무리 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;🎉 마무리&lt;/h1&gt;
&lt;p&gt;Poetry의 고급 기능을 잘 활용하면 패키지 충돌 최소화, 가상환경 관리 효율 향상, 협업 시 일관성 유지 등 실무에서 다양한 이점을 얻을 수 있습니다.&lt;/p&gt;
&lt;p&gt;추가로 poetry config를 통해 글로벌 설정도 변경 가능하니 상황에 맞게 커스터마이징할 수 있습니다.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[마크다운(Markdown) 기본 문법 살펴보기]]></title><description><![CDATA[마크다운이란? 본격적인 문법 설명에 앞서, 마크다운이 무엇인지 가볍게 살펴보겠습니다. 과거에는 웹 페이지에 글을 쓰기 위해 HTML 이나 XML…]]></description><link>https://songwoody.github.io/blog/markdown-summary/</link><guid isPermaLink="false">https://songwoody.github.io/blog/markdown-summary/</guid><pubDate>Thu, 17 Apr 2025 21:27:03 GMT</pubDate><content:encoded>&lt;h1 id=&quot;마크다운이란&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%ED%81%AC%EB%8B%A4%EC%9A%B4%EC%9D%B4%EB%9E%80&quot; aria-label=&quot;마크다운이란 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마크다운이란?&lt;/h1&gt;
&lt;p&gt;본격적인 문법 설명에 앞서, 마크다운이 무엇인지 가볍게 살펴보겠습니다.&lt;/p&gt;
&lt;p&gt;과거에는 웹 페이지에 글을 쓰기 위해 &lt;strong&gt;HTML&lt;/strong&gt; 이나 &lt;strong&gt;XML&lt;/strong&gt; 같은 태그로 텍스트를 일일이 감싸야 했습니다.&lt;br&gt;
예를 들어 글씨를 굵게 만들려면 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;b&gt;내용&amp;lt;/b&gt;&lt;/code&gt;처럼 작성해야 했습니다.&lt;br&gt;
이렇게 태그를 통해 문서의 구조를 정의하는 방식을 &lt;strong&gt;마크업(Markup)&lt;/strong&gt; 언어라고 합니다.&lt;/p&gt;
&lt;p&gt;하지만 마크업 언어는 작성이 복잡하고 글의 내용 자체에 집중하기 어렵다는 단점이 있었습니다.&lt;br&gt;
이를 해결하고자 2004년 존 그루버(John Gruber)는 복잡함을 덜어낸 &lt;strong&gt;마크다운(Markdown)&lt;/strong&gt; 을 만들었습니다.&lt;br&gt;
이름에서 알 수 있듯이, 마크업의 번거로움을 &lt;strong&gt;&apos;다운(Down)&apos;&lt;/strong&gt; 시키고 &quot;읽기 쉽고 쓰기 쉬운 텍스트 형식으로 글을 쓰자&quot;는 철학을 담고 있습니다.&lt;/p&gt;
&lt;p&gt;초기의 마크다운은 자유도가 높았던 만큼 해석 방식이 다양해 문법의 파편화가 일어났습니다.&lt;br&gt;
이를 바로잡기 위해 2014년 엄격한 표준 규격인 CommonMark가 발표되었고, 여기에 실무에 필요한 기능을 더해 현재 가장 널리 쓰이고 있는 것이 바로 &lt;strong&gt;GitHub Flavored Markdown(GFM)&lt;/strong&gt; 입니다.&lt;br&gt;
&lt;strong&gt;GFM&lt;/strong&gt; 은 표준 문법에 &lt;strong&gt;표(Table)&lt;/strong&gt;, &lt;strong&gt;취소선(Strikethrough)&lt;/strong&gt;, &lt;strong&gt;작업 목록(Task Lists)&lt;/strong&gt; 등 실용적인 기능을 추가하여 개발자뿐만 아니라 많은 글쓰기 플랫폼에서 채택하고 있습니다.&lt;/p&gt;
&lt;p&gt;그래서 이 글에서도 가장 범용적인 &lt;strong&gt;GFM 문법을 기준&lt;/strong&gt;으로 사용법과 결과물을 함께 살펴보겠습니다.&lt;/p&gt;
&lt;h1 id=&quot;markdown-기본-문법&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#markdown-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95&quot; aria-label=&quot;markdown 기본 문법 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Markdown 기본 문법&lt;/h1&gt;
&lt;p&gt;블록 요소 (Leaf Blocks &amp;#x26; Container Blocks)와 라인 요소 (Inlines) 를 구분하여 알아봅니다.&lt;/p&gt;
&lt;h2 id=&quot;블록-요소-leaf-blocks--container-blocks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B8%94%EB%A1%9D-%EC%9A%94%EC%86%8C-leaf-blocks--container-blocks&quot; aria-label=&quot;블록 요소 leaf blocks  container blocks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;블록 요소 (Leaf Blocks &amp;#x26; Container Blocks)&lt;/h2&gt;
&lt;h3 id=&quot;11-테마-구분선-thematic-breaks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#11-%ED%85%8C%EB%A7%88-%EA%B5%AC%EB%B6%84%EC%84%A0-thematic-breaks&quot; aria-label=&quot;11 테마 구분선 thematic breaks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.1 테마 구분선 (Thematic Breaks)&lt;/h3&gt;
&lt;p&gt;섹션을 시각적으로 분리할 때 사용합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;***&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;---&lt;/code&gt; 또는 &lt;code class=&quot;language-text&quot;&gt;___&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;***&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;---&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;___&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;12-atx-헤딩-atx-headings&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#12-atx-%ED%97%A4%EB%94%A9-atx-headings&quot; aria-label=&quot;12 atx 헤딩 atx headings permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.2 ATX 헤딩 (ATX Headings)&lt;/h3&gt;
&lt;p&gt;기호를 사용하여 1단계부터 6단계까지의 제목을 만듭니다.&lt;/p&gt;
&lt;p&gt;작성법:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# 제목 1
## 제목 2
### 제목 3
#### 제목 4
##### 제목 5
###### 제목 6&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;제목-1&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9-1&quot; aria-label=&quot;제목 1 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목 1&lt;/h1&gt;
&lt;h2 id=&quot;제목-2&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9-2&quot; aria-label=&quot;제목 2 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목 2&lt;/h2&gt;
&lt;h3 id=&quot;제목-3&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9-3&quot; aria-label=&quot;제목 3 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목 3&lt;/h3&gt;
&lt;h4 id=&quot;제목-4&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9-4&quot; aria-label=&quot;제목 4 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목 4&lt;/h4&gt;
&lt;h5 id=&quot;제목-5&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9-5&quot; aria-label=&quot;제목 5 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목 5&lt;/h5&gt;
&lt;h6 id=&quot;제목-6&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%A0%9C%EB%AA%A9-6&quot; aria-label=&quot;제목 6 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;제목 6&lt;/h6&gt;
&lt;p&gt;또한, 텍스트 아래에 등호(&lt;code class=&quot;language-text&quot;&gt;===&lt;/code&gt;)나 하이픈(&lt;code class=&quot;language-text&quot;&gt;---&lt;/code&gt;)을 여러 개 추가하여 1단계 및 2단계 제목을 만들 수도 있습니다. (Setext 방식)&lt;/p&gt;
&lt;p&gt;작성법:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;제목 1
===

제목 2
---&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;13-코드-블록-code-blocks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#13-%EC%BD%94%EB%93%9C-%EB%B8%94%EB%A1%9D-code-blocks&quot; aria-label=&quot;13 코드 블록 code blocks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.3 코드 블록 (Code Blocks)&lt;/h3&gt;
&lt;p&gt;백틱(```) 3개 또는 물결(~~~) 3개 이상으로 감싸며, 언어 이름을 명시하여 하이라이팅이 가능합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;```javascript
console.log(&quot;Hello GFM&quot;);
``` 

또는

~~~javascript
console.log(&quot;Hello GFM&quot;);
~~~ &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello GFM&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;14-인용문-block-quotes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#14-%EC%9D%B8%EC%9A%A9%EB%AC%B8-block-quotes&quot; aria-label=&quot;14 인용문 block quotes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.4 인용문 (Block Quotes)&lt;/h3&gt;
&lt;p&gt;줄 처음에 &gt; 기호를 붙입니다. 중첩해서 사용할 수도 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; 인용구 내용입니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;인용구 내용입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;15-목록-lists&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#15-%EB%AA%A9%EB%A1%9D-lists&quot; aria-label=&quot;15 목록 lists permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.5 목록 (Lists)&lt;/h3&gt;
&lt;p&gt;순서 없는 목록: &lt;code class=&quot;language-text&quot;&gt;-&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;+&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;*&lt;/code&gt; 중 하나를 사용합니다.&lt;br&gt;
순서 있는 목록: 숫자를 사용합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1. 첫 번째 
2. 두 번째
- 순서 없음
+ 순서 없음
* 순서 없음&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;첫 번째&lt;/li&gt;
&lt;li&gt;두 번째&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;순서 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;순서 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;순서 없음&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;또한, 목록은 들여쓰기를 통해 중첩 구조를 만들 수 있습니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1. 첫 번째 항목
    - 하위 목록 1
    - 하위 목록 2
2. 두 번째 항목
    1. 하위 순서 목록 1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;첫 번째 항목
&lt;ul&gt;
&lt;li&gt;하위 목록 1&lt;/li&gt;
&lt;li&gt;하위 목록 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;두 번째 항목
&lt;ol&gt;
&lt;li&gt;하위 순서 목록 1&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;16-표-tables---gfm-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#16-%ED%91%9C-tables---gfm-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;16 표 tables   gfm 확장 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.6 표 (Tables - GFM 확장)&lt;/h3&gt;
&lt;p&gt;파이프(|)와 하이픈(-)으로 열과 행을 구분합니다. 하이픈 줄에 콜론(:)을 넣어 정렬을 지정합니다.&lt;/p&gt;
&lt;p&gt;작성법:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;| foo | bar |
| --- | --- |
| baz | bim |&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;foo&lt;/th&gt;
&lt;th&gt;bar&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;baz&lt;/td&gt;
&lt;td&gt;bim&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;17-작업-목록-task-list-items---gfm-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#17-%EC%9E%91%EC%97%85-%EB%AA%A9%EB%A1%9D-task-list-items---gfm-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;17 작업 목록 task list items   gfm 확장 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;1.7 작업 목록 (Task List Items - GFM 확장)&lt;/h3&gt;
&lt;p&gt;목록 기호 뒤에 대괄호를 붙여 체크박스를 만듭니다.&lt;/p&gt;
&lt;p&gt;작성법:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;[ ] 미완료, 
[x] 완료&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled&gt; 미완료&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; checked disabled&gt; 완료&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2-인라인-요소-inlines&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#2-%EC%9D%B8%EB%9D%BC%EC%9D%B8-%EC%9A%94%EC%86%8C-inlines&quot; aria-label=&quot;2 인라인 요소 inlines permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2. 인라인 요소 (Inlines)&lt;/h2&gt;
&lt;p&gt;텍스트 내부에서 특정 단어나 문장에 서식을 지정할 때 사용합니다.&lt;/p&gt;
&lt;h3 id=&quot;21-강조-및-강한-강조-emphasis&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#21-%EA%B0%95%EC%A1%B0-%EB%B0%8F-%EA%B0%95%ED%95%9C-%EA%B0%95%EC%A1%B0-emphasis&quot; aria-label=&quot;21 강조 및 강한 강조 emphasis permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.1 강조 및 강한 강조 (Emphasis)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;기울임: *텍스트* 또는 _텍스트_

굵게: **텍스트** 또는 __텍스트__&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;텍스트&lt;/em&gt; 또는 &lt;em&gt;텍스트&lt;/em&gt;&lt;br&gt;
&lt;strong&gt;텍스트&lt;/strong&gt; 또는 &lt;strong&gt;텍스트&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;22-코드-스팬-code-spans&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#22-%EC%BD%94%EB%93%9C-%EC%8A%A4%ED%8C%AC-code-spans&quot; aria-label=&quot;22 코드 스팬 code spans permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.2 코드 스팬 (Code Spans)&lt;/h3&gt;
&lt;p&gt;문장 중간에 코드를 표시할 때 백틱(`) 하나로 감쌉니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;`inline code` 입니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;inline code&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;h3 id=&quot;23-취소선-strikethrough---gfm-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#23-%EC%B7%A8%EC%86%8C%EC%84%A0-strikethrough---gfm-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;23 취소선 strikethrough   gfm 확장 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.3 취소선 (Strikethrough - GFM 확장)&lt;/h3&gt;
&lt;p&gt;텍스트를 물결표 두 개로 감쌉니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;~~취소할 내용~~&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;del&gt;취소할 내용&lt;/del&gt;&lt;/p&gt;
&lt;h3 id=&quot;24-링크-및-이미지-links--images&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#24-%EB%A7%81%ED%81%AC-%EB%B0%8F-%EC%9D%B4%EB%AF%B8%EC%A7%80-links--images&quot; aria-label=&quot;24 링크 및 이미지 links  images permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.4 링크 및 이미지 (Links &amp;#x26; Images)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;링크: [깃주소]([URL](https://github.com/SongWoody)) 입니다.

이미지: ![마크다운](https://upload.wikimedia.org/wikipedia/commons/4/48/Markdown-mark.svg)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/SongWoody&quot;&gt;깃주소&lt;/a&gt; 입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://upload.wikimedia.org/wikipedia/commons/4/48/Markdown-mark.svg&quot; alt=&quot;마크다운&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;25-자동-링크-autolinks--gfm-확장&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#25-%EC%9E%90%EB%8F%99-%EB%A7%81%ED%81%AC-autolinks--gfm-%ED%99%95%EC%9E%A5&quot; aria-label=&quot;25 자동 링크 autolinks  gfm 확장 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.5 자동 링크 (Autolinks &amp;#x26; GFM 확장)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;기본: &amp;lt;https://google.com&gt; 처럼 괄호로 감싸면 링크가 됩니다.

GFM 확장: www.google.com처럼 주소만 적어도 자동으로 링크로 변환됩니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://google.com&quot;&gt;https://google.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.google.com&quot;&gt;www.google.com&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;26-이스케이프-backslash-escapes&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#26-%EC%9D%B4%EC%8A%A4%EC%BC%80%EC%9D%B4%ED%94%84-backslash-escapes&quot; aria-label=&quot;26 이스케이프 backslash escapes permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.6 이스케이프 (Backslash Escapes)&lt;/h3&gt;
&lt;p&gt;마크다운 기호를 문자로 그대로 출력하고 싶을 때 앞에 \를 붙입니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;\# 이 기호는 제목이 아닙니다.
\`code block\` 이 기호는 코드 블록이 아닙니다. &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;# 이 기호는 제목이 아닙니다.&lt;/p&gt;
&lt;p&gt;`code block` 이 기호는 코드 블록이 아닙니다.&lt;/p&gt;
&lt;h3 id=&quot;27-줄-바꿈-line-breaks&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#27-%EC%A4%84-%EB%B0%94%EA%BF%88-line-breaks&quot; aria-label=&quot;27 줄 바꿈 line breaks permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.7 줄 바꿈 (Line Breaks)&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Hard Break: 문장 끝에 공백 2개를 넣거나 백슬래시(\)를 넣고 엔터를 칩니다.

Soft Break: 그냥 엔터를 한 번 치면 문서상에서는 이어져 보이지만 소스 코드상에서는 줄이 바뀝니다.\&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;    안녕하세요.\
    반가워요. 
    이렇게 바로 아래에 쓰면 이어져서 나옵니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;안녕하세요.&lt;br&gt;
반가워요.
이렇게 바로 아래에 쓰면 이어져서 나옵니다.&lt;/p&gt;
&lt;h3 id=&quot;28-html-직접-사용-raw-html&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#28-html-%EC%A7%81%EC%A0%91-%EC%82%AC%EC%9A%A9-raw-html&quot; aria-label=&quot;28 html 직접 사용 raw html permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;2.8 HTML 직접 사용 (Raw HTML)&lt;/h3&gt;
&lt;p&gt;마크다운의 장점 중 하나는 HTML과 완벽하게 호환된다는 점입니다. 마크다운 문법으로 표현하기 힘든 복잡한 스타일이나 구조가 필요할 때, 문서 안에 HTML 태그를 직접 작성할 수 있습니다.&lt;/p&gt;
&lt;p&gt;작성법:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;이 텍스트는 빨간색입니다.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;HTML 표도&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;사용 가능합니다.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p style=&quot;color:red;&quot;&gt;이 텍스트는 빨간색입니다.&lt;/p&gt;
&lt;table&gt;
    &lt;tr&gt;
        &lt;td&gt;HTML 표도&lt;/td&gt;
        &lt;td&gt;사용 가능합니다.&lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
&lt;h1 id=&quot;번외&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B2%88%EC%99%B8&quot; aria-label=&quot;번외 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;번외&lt;/h1&gt;
&lt;h2 id=&quot;밑줄은-어떻게-칠까&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%B0%91%EC%A4%84%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%B9%A0%EA%B9%8C&quot; aria-label=&quot;밑줄은 어떻게 칠까 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;밑줄은 어떻게 칠까?&lt;/h2&gt;
&lt;p&gt;안타깝게도 Markdown 에서는 밑줄을 지원하지 않습니다.&lt;br&gt;
하여 밑줄을 사용하기 위해서는 html 의 &lt;code class=&quot;language-text&quot;&gt;&amp;lt;u&gt;&lt;/code&gt; 태그를 사용해야합니다.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;    &amp;lt;u&gt;밑줄&amp;lt;/u&gt;을 치고 싶습니다.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;u&gt;밑줄&lt;/u&gt;을 치고 싶습니다.&lt;/p&gt;
&lt;h1 id=&quot;마무리&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EB%A7%88%EB%AC%B4%EB%A6%AC&quot; aria-label=&quot;마무리 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;마무리&lt;/h1&gt;
&lt;p&gt;Markdown은 간단하면서도 강력한 문법을 제공하여 문서를 더욱 쉽게 작성할 수 있도록 도와줍니다.&lt;br&gt;
GitHub의 README 파일뿐만 아니라, 블로그, 기술 문서, 협업 문서 등 다양한 곳에서 활용할 수 있으니,&lt;br&gt;
자주 사용하면서 익숙해지면 여러 곳에서 유용하게 사용할 수 있습니다. 😀&lt;/p&gt;
&lt;h1 id=&quot;참고&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#%EC%B0%B8%EA%B3%A0&quot; aria-label=&quot;참고 permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;참고&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://github.github.com/gfm&quot;&gt;GitHub Flavored Markdown Spec&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>