<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by 김예본 on Medium]]></title>
        <description><![CDATA[Stories by 김예본 on Medium]]></description>
        <link>https://medium.com/@v954242?source=rss-7e55df8b46b5------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*GhsFdtsYetICCNLUq_4KRQ.jpeg</url>
            <title>Stories by 김예본 on Medium</title>
            <link>https://medium.com/@v954242?source=rss-7e55df8b46b5------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 18 Oct 2026 06:59:54 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@v954242/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[안드로이드에서 찾을 수 있는 디자인 패턴]]></title>
            <link>https://medium.com/@v954242/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%EC%97%90%EC%84%9C-%EC%B0%BE%EC%9D%84-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-80a4593bc651?source=rss-7e55df8b46b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/80a4593bc651</guid>
            <category><![CDATA[design-pattern-kotlin]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[김예본]]></dc:creator>
            <pubDate>Mon, 03 Feb 2025 10:23:30 GMT</pubDate>
            <atom:updated>2025-02-03T10:23:30.840Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V_htIQSQpSJI7LnsaC8syA.jpeg" /></figure><p>디자인 패턴을 공부하면서 안드로이드 코드 내에 다양한 디자인 패턴들이 사용되고 있다는 것을 알게 되었습니다. 특히 이전에 안드로이드 코드 중에서 재밌다고 생각했던 부분들이 디자인 패턴을 활용한 코드들이 많았습니다. 제가 발견했던 안드로이드 코드 내 재밌는 디자인 패턴들을 정리해보았습니다.</p><h3>생성 패턴</h3><h4>1. 팩토리 메서드 패턴</h4><p><a href="https://refactoring.guru/ko/design-patterns/factory-method">팩토리 메서드</a>는 객체 생성에 대한 책임을 인터페이스로 추상화하고, 이 인터페이스를 구체화하여 객체 생성을 다양화 시킬 수 있는 패턴입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1nHe-Z7Leg_wqqKk2A6n2Q.png" /><figcaption>팩토리 메서드 패턴</figcaption></figure><p>안드로이드에서는 ViewModel 생성시 ViewModelProvider.Factory interface를 통해 팩토리 메서드 패턴을 사용하고 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6BQyW88KHwElTsT-uNm1Eg.png" /><figcaption>ViewModelProvider.Factory 예시</figcaption></figure><h4>팩토리 메서드를 통해 해결하는 문제</h4><p>개발자는 AAC ViewModel을 직접 생성하기보단 ViewModelProvider.Factory를 통해 생성합니다. 이는 ViewModelStoreOwner의 생명주기에 맞춰 ViewModel의 생명주기를 안드로이드에서 관리해줄 수 있게 하기 위함입니다.</p><p>ViewModel의 생성을 안드로이드에서 책임지기 때문에 개발자는 ViewModel 생성에 필요한 의존성을 모두 안드로이드에 알려주어야 합니다. 즉, 개발자는 팩토리 메서드 패턴을 통해 안드로이드 내에서 ViewModel을 생성할 수 있도록 돕습니다.</p><h4>2. 빌더 패턴</h4><p><a href="https://refactoring.guru/ko/design-patterns/builder">빌더 패턴</a>은 복잡한 객체를 단계별로 생성해줄 수 있도록 도와주는 디자인 패턴입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_3_8k7TFUiexFNsgv9rd4g.png" /><figcaption>빌더 패턴</figcaption></figure><p>안드로이드 뷰 시스템의 AlertDialog는 빌더 패턴을 통해 생성할 수 있도록 되어 있습니다.</p><pre>val builder = AlertDialog.Builder(this)<br>val dialog = builder<br>  .setTitle(&quot;Alert&quot;)<br>  .setMessage(&quot;This is an alert dialog&quot;)<br>  .setPositiveButton(&quot;OK&quot;) { dialog, which -&gt; dialog.dismiss() }<br>  .setNegativeButton(&quot;Cancel&quot;) { dialog, which -&gt; dialog.dismiss() }<br>  .create()<br><br>dialog.show()</pre><h4>빌더 패턴을 통해 해결하는 문제</h4><p>개발자는 빌더 패턴을 통해 AlertDialog 내 UI 요소들을 다양하게 추가할 수 있습니다. Positive, Negative, Neutral 버튼 등 원하는 액션 버튼 만을 추가할 수 있으며, 타이틀, 아이콘 등 다양한 UI들을 추가할 수 있습니다. 이 UI들에 대한 존재 유무, 각각의 속성들을 빌더 패턴을 사용하지 않고 AlertDialog 생성자로만 설정할 수 있었다면, 생성자의 매개변수 값이 적어도 10개는 필요했을 것입니다. AlertDialog는 빌더 패턴을 통해 길고 복잡한 생성자를 통하지 않고도 UI를 조합해낼 수 있습니다.</p><h3>구조 패턴</h3><h4>3. 어댑터 패턴</h4><p><a href="https://refactoring.guru/ko/design-patterns/adapter">어댑터 패턴</a>은 호환되지 않는 인터페이스를 가진 객체들이 협업할 수 있도록 하는 디자인 패턴입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kcJW6SkBV4i-bv5pdZfjpA.png" /><figcaption>어댑터 패턴</figcaption></figure><p>안드로이드에서 RecyclerView에 데이터를 연결하기 위해 <a href="https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.Adapter">RecyclerView.Adapter</a> 또는 이를 확장한 클래스들(ex&gt; ListAdapter)을 사용해야 합니다.</p><pre>class MyListAdapter : ListAdapter&lt;String, MyViewHolder&gt;(MyDiffUtil) {<br>    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {<br>        val view = LayoutInflater.from(parent.context)<br>            .inflate(R.layout.item_layout, parent, false)<br>        return MyViewHolder(view)<br>    }<br><br>    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {<br>        holder.bind(getItem(position))<br>    }<br>}</pre><h4>어댑터 패턴을 통해 해결하는 문제</h4><p>RecyclerView는 ViewHolder 단위로 UI를 그리므로, 개발자는 코드 내 데이터를 ViewHolder 형태로 변환해야 합니다. 따라서 이 과정에서 위RecyclerView.Adapter 즉 어댑터 패턴을 사용하게 됩니다.</p><p>추가로 RecyclerView.Adapter가 ViewHolder 패턴을 사용하는 이유는, 비용이 많이 드는 View의 inflation(레이아웃 인플레이션) 작업을 최소화하기 위해서 입니다. Adapter는 ViewHolder를 생성해둔 뒤, 리스트가 스크롤 될 때마다 보이지 않게 된 ViewHolder를 재활용하여 새로운 리스트 아이템을 보여줍니다. 이를 통해 성능을 최적화하고, 불필요한 View 생성 비용을 줄일 수 있습니다.</p><h4>4. 복합체(Composite) 패턴</h4><p><a href="https://refactoring.guru/ko/design-patterns/composite">복합체 패턴</a> 은 데이터를 트리 형태로 구성한 뒤, 데이터를 트리 탐색 과정에 따라 처리하는 패턴입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3y724G49TvLNwGAJIWbMrQ.png" /><figcaption>복합체 패턴</figcaption></figure><p>Jetpack Compose는 3가지 단계를 통해 데이터에서 UI로 그려집니다. 아래 3단계 중 Composition단계에서 composable들은 각각 노드로서 트리 구조로 구성되어, 이 트리 구조 순회를 통해 Layout, Drawing 단계가 진행됩니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nYEcHaGqewVHiL9348nLJw.png" /><figcaption>Jetpack Compose의 3단계</figcaption></figure><h4>복합체 패턴을 통해 해결하는 문제</h4><p>UI 요소는 다른 UI 요소를 포함할 수 있기 때문에 이를 부모-자식 관계, 즉 트리 형태로 표현할 수 있습니다. 이를 통해 Compose의 Layout 단계에서 트리 구조를 잘 활용하게 됩니다. 구체적으로 부모 노드는 모든 자식 노드의 UI 너비, 높이를 측정하여 자신의 UI 너비, 높이를 계산하게 되며 자식을 부모의 상대 위치에 위치시키게 됩니다.</p><h3>행동 패턴</h3><h4>5. 책임 연쇄 패턴</h4><p><a href="https://refactoring.guru/ko/design-patterns/chain-of-responsibility">책임 연쇄 패턴</a>은 작업을 체인처럼 연결시켜 작업을 다음 체인에 전달하는 패턴입니다. 이 과정에서 다음 작업이 필요없는 경우 작업을 다음 체인에 전달하지 않는 등 작업 연결을 유동적으로 관리할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wpz6NmUT712omA_PiZplPw.png" /><figcaption>책임 연쇄 패턴</figcaption></figure><p>안드로이드 뷰 시스템에서 아래와 같이 여러 ViewGroup과 View가 중첩되어있다면 유저가 화면을 터치했을 때 어떤 ViewGroup 혹은 View에게 터치가 전달되어야 할까요? 만약 ViewGroupB, View 두 UI 모두 터치 이벤트가 정의되어 있으며, 상황에 따라 번갈아 가며 ViewGroupB, View에 이벤트를 전달해야한다면 어떻게 개발할 수 있을까요?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/948/1*yoCXK4c8jH7B2Un4K3-xkw.png" /></figure><p>안드로이드 뷰 시스템에서는 dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent를 통해 터치 이벤트 흐름을 제어할 수 있습니다. 아래 도표에서 onInterceptTouchEvent 함수의 결과값 Boolean에 따라 이벤트 흐름이 달라지는 것을 볼 수 있습니다. 책임 연계 패턴을 활용하여 필요한 뷰에만 터치 이벤트를 전달하고 있습니다. 이 외에도 GestureDetector 함수의 결과값 Boolean에 따라 이벤트 흐름이 달라지는 등 터치 이벤트 관련 코드에서 책임 연쇄 패턴을 자주 볼 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M9I91pgc3xFwT2XjGbrsvg.png" /><figcaption>안드로이드의 터치 이벤트 흐름</figcaption></figure><h4>책임 연쇄 패턴을 통해 해결하는 문제</h4><p>위 도표에서 onInterceptTouchEvent를 통해 터치 흐름을 제어할 수 없다면 터치 이벤트 처리가 매우 복잡해질 것입니다. 개발자는 기기 스크린의 어떤 지점에 터치 이벤트가 발생했을 때, 해당 지점에 존재하는 모든 View들에 터치 이벤트를 동시에 처리해야할 수도 있습니다. 안드로이드는 책임 연쇄 패턴을 통해 터치 데이터가 처리되는 단계를 줄여 복잡함을 줄이고, 성능상의 이득도 취하고 있습니다.</p><h3>결론</h3><p>이외에도 안드로이드에서 유행하는 코드들에는 상태 패턴, 커맨드 패턴등 다양한 디자인 패턴들이 보이곤 합니다. 반복자, 옵저버, 싱글턴 패턴은 어떤 진영에서든, 개발 언어 차원에서도 관찰되기도 하구요.</p><p>디자인 패턴을 공부하고 보니 꼭 모든 디자인 패턴을 알아야 한다고 생각이 들지는 않습니다. 개발을 하면서 자연스럽게 알게 되는 패턴도 있고, 잘 안쓰게 되는 패턴도 있는 것 같기 때문입니다. 복합체 패턴은 패턴이라기 보다는 그저 자료 구조중 하나인 트리 사용법이라고 생각되기도 하구요.</p><p>그럼에도 한 번 공부해보는 것을 추천하는 이유는 개발하는 동안 어떤 문제가 발생했을 때, 여러 개발자가 이해하고 있는 패턴으로 문제를 해결함으로서 동료간에 코드에 대한 이해도가 높아지는 장점이 있기 때문입니다. 또 문제를 해결할 수 있는 선택지, 도구가 다양해지는 장점이 있어 재미있구요.</p><p>또 다른 패턴을 찾으셨다면 같이 공유해주세요!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=80a4593bc651" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Compose LaunchedEffect, DisposableEffect에서 내가 잘 못 이해한 것들 ‍♀️]]></title>
            <link>https://medium.com/@v954242/compose-launchedeffect-disposableeffect%EC%97%90%EC%84%9C-%EB%82%B4%EA%B0%80-%EC%9E%98-%EB%AA%BB-%EC%9D%B4%ED%95%B4%ED%95%9C-%EA%B2%83%EB%93%A4-%EF%B8%8F-ca97c6c762a1?source=rss-7e55df8b46b5------2</link>
            <guid isPermaLink="false">https://medium.com/p/ca97c6c762a1</guid>
            <category><![CDATA[android-compose]]></category>
            <category><![CDATA[engineering]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[compose]]></category>
            <dc:creator><![CDATA[김예본]]></dc:creator>
            <pubDate>Fri, 19 May 2023 12:00:16 GMT</pubDate>
            <atom:updated>2023-05-19T12:04:55.650Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aRumw1hxKq-_6avQOSdpXw.jpeg" /></figure><p>최근 회사에서도 사이드 프로젝트에서도 대부분의 UI를 Jetpack Compose 로 작성하고 있습니다. UI에 동작을 추가하기 위해 [<a href="https://developer.android.com/jetpack/compose/side-effects">SideEffect</a>] 들을 자주 사용하게 되는데, 공식 문서만 읽었을 때 확실치 않은 부분들이 있어 LaunchedEffect, DisposableEffect 에서 궁금한 것들을 실험해보았습니다. 그 과정에서 Side Effect들에 대하여 잘 못 이해하고 있던 것들이 있어 이를 공유해보려합니다.</p><h3><strong>🤔 LaunchedEffect, DisposableEffect 는 언제 실행될까?</strong></h3><p>저는 LaunchedEffect(Unit) 를 딱 한 번 기록되어야 할 로깅 등 Composable 첫 시작시 1회 수행되어야 할 동작을 담곤 했습니다. 이 첫 1회 라는 개념이 애매하게 느껴져 여러 경우를 테스트 해보았습니다. 첫 1회라는 것이 <strong>Composable 이 로직 흐름상 처음 호출되는 순간일 수도, Compose UI 트리에 추가되는 순간일수도, 혹은 리컴포지션이 일어나는 순간 일수도 </strong>있다고 생각했기 때문입니다.</p><p>그래서 Greeting 이라는 Composable 함수에 아래 같이 LaunchedEffect, DisposableEffect를 추가했습니다.</p><p>그리고 show 버튼으로 Greeting Composable의 visibility를, change버튼으로 Greeting 에 전달할 파라미터의 값을 변경할 수 있습니다. 화면으로는 아래 그림처럼 생겼습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/464/1*ZomWbjtFzweuQDCPOGpIBw.png" /><figcaption>테스트용 앱 UI</figcaption></figure><pre>@Composable<br>fun Greeting(name: String) {<br>  Text(text = &quot;Hello $name!&quot;)<br>  LaunchedEffect(Unit) {<br>    Log.d(&quot;SideEffectTest&quot;, &quot;LaunchedEffect&quot;)<br>  }<br>  DisposableEffect(name) {<br>    Log.d(&quot;SideEffectTest&quot;, &quot;DisposableEffect&quot;)<br><br>    onDispose {<br>      Log.d(&quot;SideEffectTest&quot;, &quot;DisposableEffect - OnDispose&quot;)<br>    }<br>  }<br>}</pre><p>Greeting 외부는 이렇습니다.</p><pre>var showGreeting by remember { mutableStateOf(true) }<br>var name by remember { mutableStateOf(Math.random().toString()) }<br>Column {<br>  if (showGreeting) {<br>    Greeting(name)<br>  }<br>  Button(onClick = {<br>    showGreeting = !showGreeting<br>    Log.d(&quot;SideEffectTest&quot;, &quot;Show Clicked!&quot;)<br>  }) {<br>    Text(&quot;show&quot;)<br>  }<br>  Button(onClick = {<br>    name = Math.random().toString()<br>    Log.d(&quot;SideEffectTest&quot;, &quot;Change Clicked!&quot;)<br>  }) {<br>    Text(&quot;change&quot;)<br>  }<br>}</pre><p>예상했듯이 앱 실행 시에 Greeting 컴포넌트가 그려지면서 LaunchedEffect가 실행됩니다. 이 후 show 버튼이 클릭되어 dispose가 실행되고 다시 show 버튼이 클릭된 후 LaunchedEffect가 시작됩니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N-n57OygSZrNeA2nLzutNg.png" /><figcaption>앱 실행 후 Show 버튼을 2번 누른 후의 로그</figcaption></figure><p>여기서 눈여겨 볼 것 들은,</p><ol><li><strong>if 조건에 의해 Composable이 컴포즈 UI Tree에서 삭제되었다가 다시 추가될 경우 LaunchedEffect 가 기록된다.</strong></li></ol><p>이 부분이 제가 가장 잘 못 이해하고 있던 부분이라고 생각합니다. LaunchedEffect 기록 시점을 Composable 함수의 로직상 첫 호출이라고 생각해 if 조건에 의해서 LaunchedEffect는 찍히지 않을 것이라 생각했습니다. 하지만 show 버튼에 의해 화면에 보이지 않게 되면 onDispose 이벤트도 찍히고 다시 화면에 보이게 되면 LaunchedEffect가 찍히는 것을 볼 수 있습니다.</p><p><strong>2. DisposableEffect의 onDispose 콜백 외부 로그(테스트 결과의 1, 6 라인)는 LaunchedEffect가 시작되는 시점에 같이 로깅되고 있습니다.</strong></p><p>이는 DisposableEffect 내부 코드를 살펴보면 DisposableEffectImpl이 RememberObserver의 onForgotten 뿐만 아니라 onRemembered API를 활용하고 있는 것을 통해 코드로도 확인할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w7E-nvBFgW6PKxMNOYB6ZA.png" /><figcaption>DisposableEffect 구현부 코드</figcaption></figure><p><strong>3. (그 외) 전체 스크린 사이즈 리사이징에 의한 화면 변화에는 configChanges 설정에 따라 LaunchedEffect가 실행될수도 안될 수도 있다.</strong></p><p>처음 테스트에서는 앱을 분할화면으로 열어 앱 스크린 사이즈를 조절할 때 마다 onDispose와 LaunchedEffect가 찍혔습니다. 하지만 이는 스크린 크기 조정에 따른 configurationChange 로 Composable을 담은 Activity가 재시작되기 때문이었습니다. AndroidManifest 에서 configChanges 수정을 통해 스크린 사이즈에 의해 Activity가 재시작되지 않게 설정하면 LaunchedEffect는 실행되지 않습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/492/1*B9NrIM_HCbeWAC_v6tSC_A.png" /><figcaption>최근 앱 실행 목록에서 앱 2개를 상,하로 분할하여 띄운 모습</figcaption></figure><h3><strong>🤔 LaunchedEffect(Unit) 을 여러번 선언하면 같은 코루틴 스콥일까?</strong></h3><p>LaunchedEffect와 DisposableEffect의 차이점을 꼽자면 두 콜백의 실행시점도 있겠지만, coroutineScope이냐 아니냐도 있습니다. 아래 각 함수의 선언부에서 이 차이를 바로 볼 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/718/1*7XgCPHi0MENkD0qLbFSrtA.png" /><figcaption>LaunchedEffect의 선언부</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/992/1*_6GNTHB2FHDdVpbSsjv6CA.png" /><figcaption>DisposableEffect의 선언부</figcaption></figure><p>여기에서 LaunchedEffect를 한 Composable 내에서 여러 번 선언할 경우 같은 코루틴 스콥을 공유하는지 궁금했습니다. 사실 이 부분은 LaunchedEffect 내부 코드를 보면 아니라는 것을 쉽게 알 수 있습니다.</p><p>아래와 같이 부모의 CoroutineContext를 사용하지만 LaunchedEffectImpl 클래스 생성 시마다 새 CoroutineScope을 생성하고 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/968/1*NG94BjGLcYHNuvj6GpVLiw.png" /><figcaption>LaunchedEffectImpl 생성시 부모의 coroutienContext를 사용하는 모습</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3cVyHiL8yOma1TVfzV4_hg.png" /><figcaption>LaunchedEffectImpl 내부 변수로 CoroutineScope을 생성하는 모습</figcaption></figure><p>위에 사용하였던 Greeting Composable을 아래처럼 변형하여 Scope을 한 번더 로깅해보았습니다.</p><pre>@Composable<br>fun Greeting(name: String) {<br>  Text(text = &quot;Hello $name!&quot;)<br><br>  LaunchedEffect(Unit) {<br>    Log.d(&quot;SideEffectTest&quot;, &quot;LaunchedEffect 1&quot;)<br>    Log.d(&quot;SideEffectTest&quot;, this.coroutineContext.toString())<br>  }<br>  LaunchedEffect(Unit) {<br>    Log.d(&quot;SideEffectTest&quot;, &quot;LaunchedEffect 2&quot;)<br>    Log.d(&quot;SideEffectTest&quot;, this.coroutineContext.toString())<br>  }<br>}</pre><p>CoroutineContext에서 StandaloneCoroutine 객체만이 다른 것을 볼 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6Wy11gXQZ3z1l3lT-xCBIw.png" /><figcaption>StandaloneCoroutine 객체가 서로 다름</figcaption></figure><p>테스트가 끝나고 보니 LaunchedEffect에 전달한 key값이 변할 때마다 해당 코루틴을 cancel하고 재실행해야 하는 LaunchedEffect의 기본 동작을 생각하면 코루틴 스콥을 공유하지 않아야 한다는 것을 쉽게 생각할 수 있었습니다.</p><h3><strong>✍️ 결론</strong></h3><p>궁금한 것들을 떠오르는 대로 살펴보다 보니 흐름이 자연스럽게 진행된 글은 아닌 것 같습니다. 그래도 여러 애매한 것들을 테스트하고 코드를 쫓아가다 보니 각 기능이 정확히 어떤 기능인지 더 이해하게 된 것 같습니다. 테스트해 본 SideEffect 들 외에도 derivedStateOf 등 도 파봐야겠습니다.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ca97c6c762a1" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>