<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yes_circle.log</title>
        <link>https://velog.io/</link>
        <description>분야를 가리지 않는 개발자</description>
        <lastBuildDate>Thu, 25 Sep 2025 06:29:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yes_circle.log</title>
            <url>https://velog.velcdn.com/images/yes_circle/profile/94ca8e33-198e-4409-a02a-e7d70dd6d24f/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yes_circle.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yes_circle" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[StatefulWidget]]></title>
            <link>https://velog.io/@yes_circle/StatefulWidget</link>
            <guid>https://velog.io/@yes_circle/StatefulWidget</guid>
            <pubDate>Thu, 25 Sep 2025 06:29:21 GMT</pubDate>
            <description><![CDATA[<h1 id="statefulwidget이-뭐야">StatefulWidget이 뭐야?</h1>
<ul>
<li><strong>Widget(껍데기)</strong> 는 <strong>immutable</strong>(불변)이고,</li>
<li><strong>State(속 내용)</strong> 가 <strong>mutable</strong>(변경 가능)해서 화면 상태를 들고 있어.</li>
<li><code>build()</code> 는 <strong>State</strong>에 있다는 점이 포인트. <code>setState()</code> 로 상태를 바꾸면 <code>build()</code> 가 다시 그려져.</li>
</ul>
<hr>
<h1 id="구조정석-스켈레톤">구조(정석 스켈레톤)</h1>
<pre><code class="language-dart">class MyWidget extends StatefulWidget {
  final int initialCount;
  const MyWidget({super.key, this.initialCount = 0});

  @override
  State&lt;MyWidget&gt; createState() =&gt; _MyWidgetState();
}

class _MyWidgetState extends State&lt;MyWidget&gt;
    with SingleTickerProviderStateMixin {
  late int count;
  late final AnimationController _controller;

  // 1) 최초 한 번
  @override
  void initState() {
    super.initState();
    count = widget.initialCount;
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    // 프레임 이후 1회 작업 (레이아웃 값 필요할 때 등)
    WidgetsBinding.instance.addPostFrameCallback((_) {
      // do something after first build
    });
  }

  // 2) 상위의 InheritedWidget(예: Theme, MediaQuery) 의존이 바뀔 때
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }

  // 3) 부모가 새 props를 주입했을 때(동일 타입 위젯이 갱신)
  @override
  void didUpdateWidget(covariant MyWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.initialCount != widget.initialCount) {
      // 외부 값 변화에 반응
      setState(() =&gt; count = widget.initialCount);
    }
  }

  // 4) 그리기
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Text(&#39;$count&#39;),
        const SizedBox(width: 8),
        ElevatedButton(
          onPressed: () =&gt; setState(() =&gt; count++),
          child: const Text(&#39;증가&#39;),
        ),
      ],
    );
  }

  // 5) 트리에서 분리 직전(잠깐 빠졌다가 다시 붙을 수도 있음)
  @override
  void deactivate() {
    super.deactivate();
  }

  // 6) 완전 제거(리소스 정리 필수)
  @override
  void dispose() {
    _controller.dispose(); // controller/stream/focus 등 꼭 정리
    super.dispose();
  }
}</code></pre>
<hr>
<h1 id="라이프사이클-한눈에">라이프사이클 한눈에</h1>
<p><code>createState → initState → didChangeDependencies → build → (setState→build 반복) → didUpdateWidget(상위 변화 시) → deactivate → dispose</code></p>
<ul>
<li><strong>초기화</strong>: <code>initState</code></li>
<li><strong>상위 컨텍스트 의존 변경</strong>: <code>didChangeDependencies</code></li>
<li><strong>리렌더</strong>: <code>setState</code> → <code>build</code></li>
<li><strong>외부 파라미터 변경 대응</strong>: <code>didUpdateWidget</code></li>
<li><strong>해제/정리</strong>: <code>dispose</code></li>
</ul>
<hr>
<h1 id="setstate-올바르게-쓰기">setState 올바르게 쓰기</h1>
<pre><code class="language-dart">setState(() {
  // 상태값만 바꿔라 (무거운 연산 X)
  count++;
});</code></pre>
<ul>
<li>클로저 안은 <strong>상태 변경만</strong>. 비동기/네트워크 같은 무거운 일은 밖에서 하고, 끝난 뒤 한 번 <code>setState</code> 하자.</li>
<li>비동기 뒤에 <code>if (!mounted) return;</code> 로 위젯 생존 여부 확인!</li>
</ul>
<hr>
<h1 id="자주-하는-실수--팁">자주 하는 실수 &amp; 팁</h1>
<ul>
<li><strong>컨트롤러/리소스를 build에서 생성</strong> ❌ → <code>initState</code>에서 만들고 <code>dispose</code>에서 정리.</li>
<li><strong>dispose 이후 setState 호출</strong> ❌ → 비동기 처리 후 <code>mounted</code> 체크.</li>
<li><strong>URL/레이아웃 값이 필요한 초기 작업</strong> → <code>addPostFrameCallback</code> 사용.</li>
<li><strong>리스트에서 상태 보존</strong> → <code>Key</code>(특히 <code>ValueKey</code>) 로 식별자 부여.</li>
</ul>
<hr>
<h1 id="언제-statefulwidget을-써">언제 StatefulWidget을 써?</h1>
<ul>
<li>TextField 컨트롤러, 애니메이션, 탭/스크롤 포지션, 타이머/스트림 등 <strong>위젯 자체 수명과 같이 가는 상태</strong>가 있을 때.</li>
<li>반대로 <strong>비즈니스 상태</strong>(API 데이터, 로그인 상태 등)는 Provider/Stacked ViewModel 같은 <strong>상태관리</strong>에 두고 위젯은 가볍게.</li>
</ul>
<hr>
<h1 id="react와-비교감-잡기">React와 비교(감 잡기)</h1>
<table>
<thead>
<tr>
<th>Flutter</th>
<th>React</th>
</tr>
</thead>
<tbody><tr>
<td><code>StatefulWidget + State</code></td>
<td>함수 컴포넌트 + <code>useState</code>/<code>useEffect</code></td>
</tr>
<tr>
<td><code>initState</code></td>
<td><code>useEffect(() =&gt; {...}, [])</code></td>
</tr>
<tr>
<td><code>didUpdateWidget</code></td>
<td><code>useEffect</code>의 deps 변화 반응</td>
</tr>
<tr>
<td><code>dispose</code></td>
<td><code>useEffect</code>의 cleanup 함수 <code>return () =&gt; {...}</code></td>
</tr>
<tr>
<td><code>setState()</code></td>
<td><code>setState(...)</code></td>
</tr>
</tbody></table>
<ul>
<li>React는 컴포넌트 함수가 매 렌더마다 다시 실행되고 Hook으로 상태를 유지.</li>
<li>Flutter는 <strong>State 객체</strong>가 위젯 트리 안에 붙어 <strong>수명주기</strong>로 상태를 관리.</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Stacked CLI]]></title>
            <link>https://velog.io/@yes_circle/Stacked-Stacked-CLI</link>
            <guid>https://velog.io/@yes_circle/Stacked-Stacked-CLI</guid>
            <pubDate>Sun, 06 Jul 2025 12:38:21 GMT</pubDate>
            <description><![CDATA[<h2 id="🛠-stacked-cli-커맨드-라인-인터페이스">🛠 Stacked CLI (커맨드 라인 인터페이스)</h2>
<p>Stacked CLI는 <strong>Stacked 프레임워크를 더 쉽게 사용하기 위한 커맨드 라인 도구</strong>입니다.
코드 생성, 서비스 등록, View 및 ViewModel 생성 등을 명령어로 빠르고 편리하게 처리할 수 있습니다.</p>
<hr>
<h2 id="✅-주요-기능">✅ 주요 기능</h2>
<ul>
<li>View 및 ViewModel 스캐폴딩 (기본 뼈대 코드 자동 생성)</li>
<li>서비스 생성 및 등록 자동화</li>
<li>라우트 및 네비게이션 설정 도움</li>
<li>프로젝트 초기 설정 지원</li>
</ul>
<hr>
<h2 id="1️⃣-설치-방법">1️⃣ 설치 방법</h2>
<pre><code class="language-bash">dart pub global activate stacked_cli</code></pre>
<blockquote>
<p>또는 Flutter 프로젝트 내에서 dev_dependency로 설치 가능</p>
</blockquote>
<hr>
<h2 id="2️⃣-기본-사용법">2️⃣ 기본 사용법</h2>
<p>터미널에서 <code>stacked</code> 명령어로 실행합니다.</p>
<pre><code class="language-bash">stacked create view home</code></pre>
<ul>
<li><code>view</code> 또는 <code>viewmodel</code> 명령어로 컴포넌트 생성</li>
<li><code>service</code> 명령어로 서비스 생성 및 locator 등록 지원</li>
</ul>
<hr>
<h2 id="3️⃣-예시-view와-viewmodel-생성">3️⃣ 예시: View와 ViewModel 생성</h2>
<pre><code class="language-bash">stacked create view home</code></pre>
<p>→ <code>home_view.dart</code>와 <code>home_viewmodel.dart</code>가 자동 생성되며, 기본 구조가 포함됩니다.</p>
<hr>
<h2 id="4️⃣-서비스-생성-및-locator-등록">4️⃣ 서비스 생성 및 locator 등록</h2>
<pre><code class="language-bash">stacked create service api</code></pre>
<p>→ <code>api_service.dart</code> 파일이 생성되고, <code>setupLocator()</code>에 자동으로 등록하는 코드가 추가됩니다.</p>
<hr>
<h2 id="5️⃣-라우트-생성-지원">5️⃣ 라우트 생성 지원</h2>
<p>CLI에서 라우트 관련 명령어를 사용해 라우트 설정과 코드 생성을 돕습니다.</p>
<hr>
<h2 id="6️⃣-기타-유용한-명령어">6️⃣ 기타 유용한 명령어</h2>
<ul>
<li><code>stacked help</code> : 명령어 전체 목록 확인</li>
<li><code>stacked create</code> : 생성 가능한 컴포넌트 타입 확인</li>
<li><code>stacked clean</code> : 빌드 캐시 및 생성 파일 정리</li>
</ul>
<hr>
<h2 id="✅-요약">✅ 요약</h2>
<table>
<thead>
<tr>
<th>명령어 예시</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>stacked create view &lt;name&gt;</code></td>
<td>View + ViewModel 생성</td>
</tr>
<tr>
<td><code>stacked create viewmodel &lt;name&gt;</code></td>
<td>ViewModel만 생성</td>
</tr>
<tr>
<td><code>stacked create service &lt;name&gt;</code></td>
<td>서비스 생성 및 locator 등록</td>
</tr>
<tr>
<td><code>stacked clean</code></td>
<td>캐시 및 자동 생성 파일 정리</td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-장점">💡 장점</h3>
<ul>
<li>반복 작업 자동화 → 개발 생산성 향상</li>
<li>일관된 코드 스타일 유지</li>
<li>Stacked 아키텍처 규칙 준수 보장</li>
</ul>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li>설치 방법, 주요 옵션, 커스텀 템플릿 만드는 법</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/tooling/stacked-cli">Stacked 공식 문서 - Stacked CLI</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Routing Basics]]></title>
            <link>https://velog.io/@yes_circle/Stacked-Routing-Basics-jpmlilbi</link>
            <guid>https://velog.io/@yes_circle/Stacked-Routing-Basics-jpmlilbi</guid>
            <pubDate>Sun, 06 Jul 2025 12:36:39 GMT</pubDate>
            <description><![CDATA[<h2 id="🚦-stacked-router---routing-basics">🚦 Stacked Router - Routing Basics</h2>
<p><strong>Stacked Router</strong>는 <code>auto_route</code> 패키지를 기반으로 구축된 <strong>자동 라우팅 시스템</strong>입니다.
Stacked는 이를 감싸 더 간단하고 구조적인 라우팅 구성을 제공합니다.</p>
<hr>
<h2 id="✅-핵심-목표">✅ 핵심 목표</h2>
<ul>
<li>라우트를 <strong>중앙에서 선언</strong></li>
<li>코드 생성으로 <strong>타이핑 없이 안전하게 라우팅</strong></li>
<li>ViewModel에서도 <strong>라우팅 가능</strong></li>
</ul>
<hr>
<h2 id="1️⃣-stackedapp에-라우트-선언">1️⃣ <code>@StackedApp</code>에 라우트 선언</h2>
<p>먼저 <code>app.dart</code> 파일에 라우트 정의를 합니다.</p>
<pre><code class="language-dart">@StackedApp(
  routes: [
    MaterialRoute(page: HomeView),
    MaterialRoute(page: DetailsView),
  ],
)
class App {}</code></pre>
<ul>
<li><code>@StackedApp</code>은 라우팅, Dialog, BottomSheet 설정 등을 포함한 중앙 설정</li>
<li><code>MaterialRoute</code>는 기본 화면 전환 방식</li>
</ul>
<blockquote>
<p>📌 이 설정 후 <code>flutter pub run build_runner build</code>를 실행하여 자동으로 코드가 생성됩니다.</p>
</blockquote>
<hr>
<h2 id="2️⃣-자동-생성되는-파일">2️⃣ 자동 생성되는 파일</h2>
<p>위 설정을 하면 다음 파일이 생성됩니다:</p>
<ul>
<li><code>app.router.dart</code> → 라우터 클래스 정의</li>
<li><code>app.dart</code> → 메인 앱 설정에서 사용</li>
</ul>
<pre><code class="language-dart">MaterialApp.router(
  routerDelegate: StackedRouter().delegate(),
  routeInformationParser: StackedRouter().defaultRouteParser(),
)</code></pre>
<hr>
<h2 id="3️⃣-네비게이션-서비스에서-라우트-사용">3️⃣ 네비게이션 서비스에서 라우트 사용</h2>
<p>Stacked는 자동 생성된 라우트 이름을 기반으로 메서드를 제공합니다.</p>
<pre><code class="language-dart">final _navigationService = locator&lt;NavigationService&gt;();

_navigationService.navigateToHomeView();
_navigationService.replaceWithDetailsView();
_navigationService.back();</code></pre>
<hr>
<h2 id="4️⃣-파라미터-전달">4️⃣ 파라미터 전달</h2>
<p>라우트를 정의한 View가 <code>@RoutePage()</code>를 사용하고 있다면, 다음처럼 파라미터를 전달할 수 있습니다:</p>
<pre><code class="language-dart">_navigationService.navigateToDetailsView(id: 1, title: &#39;상세 화면&#39;);</code></pre>
<p>View에서는 생성자로 파라미터를 받을 수 있습니다:</p>
<pre><code class="language-dart">@RoutePage()
class DetailsView extends StatelessWidget {
  final int id;
  final String title;

  const DetailsView({required this.id, required this.title, super.key});
}</code></pre>
<hr>
<h2 id="✅-요약">✅ 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>@StackedApp</code></td>
<td>앱의 라우트 중앙 선언</td>
</tr>
<tr>
<td><code>MaterialRoute</code></td>
<td>기본 라우트 타입</td>
</tr>
<tr>
<td><code>navigationService.navigateToXXXView()</code></td>
<td>화면 전환</td>
</tr>
<tr>
<td><code>flutter pub run build_runner build</code></td>
<td>자동 라우트 코드 생성</td>
</tr>
<tr>
<td>파라미터 전달</td>
<td>생성자 기반 전달 가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-장점">💡 장점</h3>
<ul>
<li>라우트 이름, 파라미터 자동 코드 생성 → <strong>실수 줄어듦</strong></li>
<li>ViewModel에서도 화면 전환 → <strong>UI 로직과 분리</strong></li>
<li><code>@StackedApp</code> 한 곳에서 앱 전역 구성</li>
</ul>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li><code>@RoutePage</code>에 애니메이션 효과나 <code>CupertinoRoute</code>, <code>CustomRoute</code> 등의 고급 설정</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/stacked-router/routing-basics">Stacked 공식 문서 - Routing Basics</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Logger]]></title>
            <link>https://velog.io/@yes_circle/Stacked-Logger</link>
            <guid>https://velog.io/@yes_circle/Stacked-Logger</guid>
            <pubDate>Sun, 06 Jul 2025 12:34:31 GMT</pubDate>
            <description><![CDATA[<h2 id="📋-stacked의-logger">📋 Stacked의 Logger</h2>
<p>Stacked는 앱의 상태나 흐름을 추적하고 디버깅하기 위해 간단한 <strong>로깅(Logging)</strong> 기능을 제공합니다.
이를 통해 디버그 중 로그를 체계적으로 남길 수 있고, 문제를 쉽게 파악할 수 있습니다.</p>
<hr>
<h2 id="✅-어떻게-동작하나요">✅ 어떻게 동작하나요?</h2>
<p>Stacked는 내부적으로 <code>LoggerService</code>를 통해 로그를 기록합니다.
이 서비스는 <code>setupLocator()</code> 내에서 등록되고, 이후 어디서든 사용할 수 있습니다.</p>
<hr>
<h2 id="🔧-기본-설정-방법">🔧 기본 설정 방법</h2>
<h3 id="1️⃣-서비스-등록">1️⃣ 서비스 등록</h3>
<p>먼저 <code>Logger</code> 인스턴스를 서비스 로케이터에 등록합니다:</p>
<pre><code class="language-dart">import &#39;package:logger/logger.dart&#39;;

final logger = Logger();

void setupLocator() {
  locator.registerLazySingleton(() =&gt; logger);
}</code></pre>
<blockquote>
<p>또는 <code>LoggerService</code> 클래스를 만들고 커스터마이징하여 등록할 수도 있습니다.</p>
</blockquote>
<hr>
<h3 id="2️⃣-viewmodel이나-service에서-사용하기">2️⃣ ViewModel이나 Service에서 사용하기</h3>
<pre><code class="language-dart">final _logger = locator&lt;Logger&gt;();

_logger.i(&#39;정보 로그입니다&#39;);
_logger.d(&#39;디버그 로그입니다&#39;);
_logger.w(&#39;경고 로그입니다&#39;);
_logger.e(&#39;에러 로그입니다&#39;);</code></pre>
<h4 id="주요-로그-레벨">주요 로그 레벨:</h4>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>v()</code></td>
<td>verbose (자세한 정보)</td>
</tr>
<tr>
<td><code>d()</code></td>
<td>debug (디버그 정보)</td>
</tr>
<tr>
<td><code>i()</code></td>
<td>info (일반 정보)</td>
</tr>
<tr>
<td><code>w()</code></td>
<td>warning (경고)</td>
</tr>
<tr>
<td><code>e()</code></td>
<td>error (에러)</td>
</tr>
<tr>
<td><code>wtf()</code></td>
<td>심각한 에러 (What a Terrible Failure)</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧰-logger-커스터마이징">🧰 Logger 커스터마이징</h2>
<p><code>Logger</code> 클래스는 다양한 설정을 지원합니다. 예:</p>
<pre><code class="language-dart">Logger(
  printer: PrettyPrinter(
    methodCount: 2,
    errorMethodCount: 8,
    lineLength: 120,
    colors: true,
    printEmojis: true,
    printTime: false,
  ),
);</code></pre>
<hr>
<h2 id="🧪-테스트나-프로덕션-환경에서-로깅-끄기">🧪 테스트나 프로덕션 환경에서 로깅 끄기</h2>
<pre><code class="language-dart">Logger(
  level: kReleaseMode ? Level.nothing : Level.debug,
);</code></pre>
<blockquote>
<p>릴리즈 빌드에서는 로그 출력 안 되도록 설정 가능</p>
</blockquote>
<hr>
<h2 id="✅-요약">✅ 요약</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>Logger</code></td>
<td>로그를 남기는 기본 클래스</td>
</tr>
<tr>
<td><code>locator.registerLazySingleton()</code></td>
<td>앱 어디서든 사용 가능하게 등록</td>
</tr>
<tr>
<td>로그 레벨</td>
<td>verbose, debug, info, warning, error, wtf</td>
</tr>
<tr>
<td>커스터마이징</td>
<td>PrettyPrinter 등으로 로그 스타일 지정 가능</td>
</tr>
<tr>
<td>릴리즈 환경 대응</td>
<td><code>kReleaseMode</code> 체크로 로그 차단 가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-핵심-포인트">💡 핵심 포인트</h3>
<ul>
<li>Stacked는 <code>Logger</code>를 통해 <strong>간편하고 강력한 로깅 시스템</strong>을 제공합니다.</li>
<li>로깅은 개발 중 디버깅과 문제 추적에 매우 유용합니다.</li>
<li>앱 전역에서 사용 가능하며, 커스터마이징도 쉽게 할 수 있습니다.</li>
</ul>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li>Firebase Crashlytics와 연동한 로그 수집, 로그 필터링 전략</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/in-depth/logger">Stacked 공식 문서 - Logger</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Services]]></title>
            <link>https://velog.io/@yes_circle/Stacked-</link>
            <guid>https://velog.io/@yes_circle/Stacked-</guid>
            <pubDate>Sun, 06 Jul 2025 12:32:11 GMT</pubDate>
            <description><![CDATA[<h2 id="🧩-stacked의-service란">🧩 Stacked의 Service란?</h2>
<p><strong>Service는 비즈니스 로직을 처리하는 독립적인 클래스</strong>로, ViewModel이나 View와 분리되어 있습니다.
API 호출, 로컬 저장소 접근, 인증 처리 등 다양한 로직을 Service에 분리시켜 앱을 <strong>더 구조적이고 유지보수하기 쉽게</strong> 만들어줍니다.</p>
<hr>
<h2 id="✅-왜-service를-사용하는가">✅ 왜 Service를 사용하는가?</h2>
<ul>
<li>ViewModel이 <strong>UI 상태 관리에만 집중</strong>할 수 있음</li>
<li><strong>비즈니스 로직을 여러 ViewModel 간에 재사용</strong> 가능</li>
<li>테스트가 훨씬 쉬워짐 (ViewModel과 분리되어 mock 가능)</li>
<li>서비스는 앱 전역에서 <strong>싱글톤</strong>으로 관리됨</li>
</ul>
<hr>
<h2 id="🧱-서비스-만들기-예시">🧱 서비스 만들기 예시</h2>
<pre><code class="language-dart">class CounterService {
  int _counter = 0;

  int get counter =&gt; _counter;

  void incrementCounter() {
    _counter++;
  }
}</code></pre>
<p>이렇게 하면 로직이 ViewModel에서 분리되어 독립적으로 관리됩니다.</p>
<hr>
<h2 id="🔧-서비스-등록-setuplocator">🔧 서비스 등록 (setupLocator)</h2>
<p>서비스를 사용하려면 앱 시작 시 등록해야 합니다.</p>
<pre><code class="language-dart">final locator = StackedLocator.instance;

void setupLocator() {
  locator.registerLazySingleton(() =&gt; CounterService());
}</code></pre>
<ul>
<li><code>registerLazySingleton</code>: 첫 호출 시 한 번만 생성</li>
<li><code>setupLocator()</code>는 <code>main.dart</code>에서 호출되어야 함</li>
</ul>
<pre><code class="language-dart">void main() {
  setupLocator();
  runApp(MyApp());
}</code></pre>
<hr>
<h2 id="📦-서비스-사용하기-viewmodel에서">📦 서비스 사용하기 (ViewModel에서)</h2>
<pre><code class="language-dart">final _counterService = locator&lt;CounterService&gt;();

void increase() {
  _counterService.incrementCounter();
  notifyListeners();
}</code></pre>
<p>이제 ViewModel에서 서비스를 가져와서 사용할 수 있습니다.</p>
<hr>
<h2 id="🔄-상태가-있는-서비스-→-viewmodel에서-반응하려면">🔄 상태가 있는 서비스 → ViewModel에서 반응하려면?</h2>
<p>상태가 자주 바뀌는 서비스(<code>_counter</code> 등)를 ViewModel에서 실시간 반영하고 싶을 때는 <code>ReactiveViewModel</code>을 사용합니다.</p>
<pre><code class="language-dart">class HomeViewModel extends ReactiveViewModel {
  final _counterService = locator&lt;CounterService&gt;();

  @override
  List&lt;ListenableServiceMixin&gt; get listenableServices =&gt; [_counterService];
}</code></pre>
<p>→ 이 경우, 서비스의 값이 바뀌면 ViewModel이 자동으로 감지하고 <code>notifyListeners()</code>를 호출합니다.</p>
<p>※ <code>CounterService</code>는 <code>ListenableServiceMixin</code>을 믹스인해야 합니다.</p>
<pre><code class="language-dart">class CounterService with ListenableServiceMixin {
  int _counter = 0;

  int get counter =&gt; _counter;

  void incrementCounter() {
    _counter++;
    notifyListeners(); // mixin에서 제공
  }
}</code></pre>
<hr>
<h2 id="🧪-테스트-시-mock-서비스-등록">🧪 테스트 시 mock 서비스 등록</h2>
<pre><code class="language-dart">locator.registerSingleton&lt;CounterService&gt;(FakeCounterService());</code></pre>
<p>→ 기존 서비스를 Fake로 대체하여 테스트에 사용 가능</p>
<hr>
<h2 id="✅-정리">✅ 정리</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Service</td>
<td>비즈니스 로직 담당 클래스</td>
</tr>
<tr>
<td>setupLocator</td>
<td>서비스 등록 함수</td>
</tr>
<tr>
<td>locator<T>()</td>
<td>서비스 인스턴스를 가져오는 함수</td>
</tr>
<tr>
<td>registerLazySingleton</td>
<td>호출 시 1회만 생성하여 재사용</td>
</tr>
<tr>
<td>ReactiveViewModel + ListenableServiceMixin</td>
<td>상태 변경 자동 감지 &amp; UI 반영</td>
</tr>
<tr>
<td>테스트</td>
<td>Mock/Fake로 서비스 대체 가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-요점-정리">💡 요점 정리</h3>
<ul>
<li>Service는 <strong>비즈니스 로직</strong>을 담는 독립 모듈입니다.</li>
<li>ViewModel과 분리되어 <strong>유지보수와 테스트가 쉬워집니다</strong>.</li>
<li><code>setupLocator</code>를 통해 등록하고, 전역에서 <code>locator&lt;&gt;()</code>로 사용합니다.</li>
<li>상태가 변하는 서비스는 <code>ListenableServiceMixin</code>을 써서 ViewModel에서 자동 감지할 수 있습니다.</li>
</ul>
<hr>
<h3 id="알아두면-좋을-내용">알아두면 좋을 내용</h3>
<ul>
<li>FirebaseService, APIService, AuthService 등 실전용 서비스 구조</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/in-depth/services">Stacked 공식 문서 - Services</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - ViewModels]]></title>
            <link>https://velog.io/@yes_circle/Stacked-ViewModels</link>
            <guid>https://velog.io/@yes_circle/Stacked-ViewModels</guid>
            <pubDate>Sun, 06 Jul 2025 12:28:27 GMT</pubDate>
            <description><![CDATA[<h2 id="👓-viewmodel이란">👓 ViewModel이란?</h2>
<p><strong>ViewModel은 View(UI)와 Business Logic(서비스 등) 사이를 연결해주는 중간 역할</strong>을 합니다.
Stacked 프레임워크에서는 <code>BaseViewModel</code> 또는 다양한 특수 ViewModel 클래스를 상속받아 상태 관리와 로직 분리를 손쉽게 구현할 수 있습니다.</p>
<blockquote>
<p>View는 ViewModel이 제공하는 데이터만 보고, ViewModel은 상태를 바꾸고 알립니다.</p>
</blockquote>
<hr>
<h2 id="✅-기본-viewmodel-사용법">✅ 기본 ViewModel 사용법</h2>
<h3 id="1️⃣-baseviewmodel-상속">1️⃣ <code>BaseViewModel</code> 상속</h3>
<pre><code class="language-dart">class CounterViewModel extends BaseViewModel {
  int _counter = 0;

  int get counter =&gt; _counter;

  void increment() {
    _counter++;
    notifyListeners();
  }
}</code></pre>
<ul>
<li><code>notifyListeners()</code>를 호출하면 UI(View)가 자동으로 업데이트됩니다.</li>
</ul>
<hr>
<h2 id="🛠️-주요-viewmodel-타입들">🛠️ 주요 ViewModel 타입들</h2>
<table>
<thead>
<tr>
<th>ViewModel 타입</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>BaseViewModel</strong></td>
<td>상태를 수동으로 제어할 수 있는 기본 ViewModel</td>
</tr>
<tr>
<td><strong>ReactiveViewModel</strong></td>
<td>특정 서비스의 변화에 반응하여 자동 갱신되는 ViewModel</td>
</tr>
<tr>
<td><strong>FormViewModel</strong></td>
<td><code>@FormView</code>와 함께 사용하는 Form 상태 관리 전용 ViewModel</td>
</tr>
<tr>
<td><strong>FutureViewModel</strong></td>
<td>비동기 작업을 ViewModel 내에서 처리하고, 완료 후 View에 알림</td>
</tr>
<tr>
<td><strong>StreamViewModel</strong></td>
<td>실시간 데이터 스트림과 View를 연결할 때 사용</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔄-상태-변경-알리기">🔄 상태 변경 알리기</h2>
<pre><code class="language-dart">void updateSomething() {
  // 데이터 변경
  notifyListeners();  // UI에게 변경 알림
}</code></pre>
<ul>
<li>모든 ViewModel은 <code>ChangeNotifier</code>를 기반으로 하기 때문에 <code>notifyListeners()</code>를 통해 View를 갱신시킵니다.</li>
</ul>
<hr>
<h2 id="🧪-테스트-용이성">🧪 테스트 용이성</h2>
<ul>
<li>ViewModel은 로직만 담당하기 때문에 <strong>UI에 의존하지 않고 단위 테스트</strong>가 가능합니다.</li>
<li>특히 서비스 로직을 분리하면 mock 데이터를 활용한 테스트가 간편합니다.</li>
</ul>
<hr>
<h2 id="🔍-예시-futureviewmodel">🔍 예시: <code>FutureViewModel</code></h2>
<pre><code class="language-dart">class UserViewModel extends FutureViewModel&lt;User&gt; {
  @override
  Future&lt;User&gt; futureToRun() =&gt; _userService.fetchUser();
}</code></pre>
<ul>
<li>ViewModel이 생성되면 자동으로 <code>futureToRun()</code>이 실행되고,</li>
<li>데이터가 로드되면 View가 자동 갱신됩니다.</li>
</ul>
<hr>
<h2 id="✅-요약">✅ 요약</h2>
<table>
<thead>
<tr>
<th>요소</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>BaseViewModel</code></td>
<td>가장 기본이 되는 ViewModel 클래스</td>
</tr>
<tr>
<td><code>notifyListeners()</code></td>
<td>View 갱신 트리거</td>
</tr>
<tr>
<td><code>FutureViewModel</code></td>
<td>비동기 데이터 처리에 적합</td>
</tr>
<tr>
<td><code>StreamViewModel</code></td>
<td>실시간 데이터 처리에 적합</td>
</tr>
<tr>
<td><code>FormViewModel</code></td>
<td>폼 상태를 쉽게 관리</td>
</tr>
<tr>
<td><code>ReactiveViewModel</code></td>
<td>서비스 상태 변경을 View에 자동 반영</td>
</tr>
</tbody></table>
<hr>
<h3 id="💡-stacked-viewmodel의-장점">💡 Stacked ViewModel의 장점</h3>
<ul>
<li>View와 로직 완전 분리 (MVVM)</li>
<li>테스트 작성이 쉬움</li>
<li>다양한 ViewModel 타입으로 실무에 맞춘 구조 가능</li>
<li>ViewModel마다 생명주기(<code>onModelReady</code>, <code>dispose</code>)가 존재하여 관리 용이</li>
</ul>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li>각 ViewModel 타입별 실전</li>
<li><code>ViewModelBuilder</code> 커스터마이징 방법</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/in-depth/viewmodels">Stacked 공식 문서 - ViewModels</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Service Locator]]></title>
            <link>https://velog.io/@yes_circle/Stacked-Service-Locator</link>
            <guid>https://velog.io/@yes_circle/Stacked-Service-Locator</guid>
            <pubDate>Sun, 06 Jul 2025 12:26:06 GMT</pubDate>
            <description><![CDATA[<h2 id="🧭-service-locator란">🧭 Service Locator란?</h2>
<p>Stacked에서는 <a href="https://pub.dev/packages/get_it">get_it</a> 패키지를 기반으로 한 <strong>서비스 로케이터</strong>를 사용하여, **앱 전체에서 공통적으로 사용하는 객체(서비스 등)**를 <strong>의존성 주입</strong>으로 관리할 수 있게 해줍니다.</p>
<blockquote>
<p>즉, <strong>전역에서 쉽게 접근 가능한 싱글톤 객체를 등록하고 호출</strong>할 수 있는 방식이에요.</p>
</blockquote>
<hr>
<h2 id="✅-왜-써야-하나요">✅ 왜 써야 하나요?</h2>
<ul>
<li>ViewModel 또는 Service 간 <strong>중복된 생성 없이 재사용</strong></li>
<li>테스트와 유지보수가 쉬움</li>
<li>어디서든 동일한 인스턴스를 가져와 사용 가능</li>
</ul>
<hr>
<h2 id="📦-사용-예시">📦 사용 예시</h2>
<h3 id="1️⃣-서비스-클래스-정의">1️⃣ 서비스 클래스 정의</h3>
<pre><code class="language-dart">class AuthenticationService {
  bool isUserLoggedIn = false;
}</code></pre>
<hr>
<h3 id="2️⃣-setuplocator-함수로-서비스-등록">2️⃣ <code>setupLocator()</code> 함수로 서비스 등록</h3>
<pre><code class="language-dart">import &#39;package:stacked/stacked.dart&#39;;

final locator = StackedLocator.instance;

void setupLocator() {
  locator.registerLazySingleton(() =&gt; AuthenticationService());
}</code></pre>
<ul>
<li><code>registerLazySingleton</code>: 최초 요청 시 한 번만 인스턴스를 생성해서 재사용합니다.</li>
</ul>
<hr>
<h3 id="3️⃣-어디서든-사용하기-예-viewmodel">3️⃣ 어디서든 사용하기 (예: ViewModel)</h3>
<pre><code class="language-dart">final _authService = locator&lt;AuthenticationService&gt;();

void checkLogin() {
  if (_authService.isUserLoggedIn) {
    // 로그인 상태
  }
}</code></pre>
<hr>
<h2 id="🧰-기타-등록-방식">🧰 기타 등록 방식</h2>
<table>
<thead>
<tr>
<th>메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>registerSingleton()</code></td>
<td>앱 시작 시 즉시 인스턴스를 생성</td>
</tr>
<tr>
<td><code>registerLazySingleton()</code></td>
<td>필요할 때 인스턴스를 생성 (권장)</td>
</tr>
<tr>
<td><code>registerFactory()</code></td>
<td>호출할 때마다 새 인스턴스를 반환 (상태를 갖지 않는 객체에 적합)</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔄-의존성-제거-reset">🔄 의존성 제거 (reset)</h2>
<p>필요한 경우, 등록된 모든 서비스 또는 특정 서비스만 제거할 수 있습니다.</p>
<pre><code class="language-dart">locator.reset();                // 모든 서비스 제거
locator.unregister&lt;SomeType&gt;(); // 특정 타입만 제거</code></pre>
<hr>
<h2 id="🧪-테스트를-위한-주입">🧪 테스트를 위한 주입</h2>
<p>테스트 환경에서 가짜(Mock) 서비스를 주입할 수 있습니다.</p>
<pre><code class="language-dart">locator.registerSingleton&lt;AuthenticationService&gt;(FakeAuthService());</code></pre>
<hr>
<h2 id="✅-요약">✅ 요약</h2>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>setupLocator()</code></td>
<td>서비스 등록 함수</td>
</tr>
<tr>
<td><code>locator&lt;T&gt;()</code></td>
<td>등록한 서비스 사용</td>
</tr>
<tr>
<td><code>registerLazySingleton()</code></td>
<td>지연 생성 싱글톤 등록</td>
</tr>
<tr>
<td><code>registerFactory()</code></td>
<td>호출 시마다 새 객체 생성</td>
</tr>
<tr>
<td>테스트 교체</td>
<td>다른 구현체로 주입 가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="📌-핵심-요점">📌 핵심 요점</h3>
<ul>
<li>Stacked는 <code>get_it</code> 기반의 서비스 로케이터를 내장하고 있어, 의존성 주입이 매우 쉽습니다.</li>
<li>ViewModel에서 서비스 객체를 전역 접근으로 불러와 사용하며, 싱글톤 관리가 편리합니다.</li>
</ul>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li><code>setupLocator()</code>를 어디서 호출해야 하는지</li>
<li><code>locator</code>와 <code>get_it</code> 차이점</li>
<li>여러 개의 서비스 등록</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/in-depth/service-locator">Stacked 공식 문서 - Service Locator</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Form Basics]]></title>
            <link>https://velog.io/@yes_circle/Stacked-Form-Basics</link>
            <guid>https://velog.io/@yes_circle/Stacked-Form-Basics</guid>
            <pubDate>Sun, 06 Jul 2025 12:23:22 GMT</pubDate>
            <description><![CDATA[<h2 id="📝-form-basics-폼-기본-사용법">📝 Form Basics (폼 기본 사용법)</h2>
<p><strong>Stacked</strong>는 폼(Form)을 쉽게 다루기 위해 <code>@FormView</code> 애너테이션을 제공합니다.
이 기능을 통해 텍스트 필드 컨트롤, 검증(validation), 상태 관리 등을 자동화할 수 있어요.</p>
<hr>
<h3 id="✅-목표">✅ 목표</h3>
<ul>
<li><code>TextField</code> 값을 ViewModel에서 쉽게 접근하고</li>
<li>validation, focus 제어 등을 간편하게 처리하는 것</li>
</ul>
<hr>
<h2 id="1️⃣-formview-애너테이션-사용하기">1️⃣ <code>@FormView</code> 애너테이션 사용하기</h2>
<p>먼저 View 클래스 위에 <code>@FormView</code>를 선언합니다:</p>
<pre><code class="language-dart">@FormView(fields: [
  FormTextField(name: &#39;email&#39;),
  FormTextField(name: &#39;password&#39;, isPassword: true),
])
class LoginView extends StackedView&lt;LoginViewModel&gt; {
  ...
}</code></pre>
<ul>
<li><code>name</code>: 텍스트 필드 이름 (ViewModel에서 사용할 변수명)</li>
<li><code>isPassword</code>: 비밀번호 입력 필드 여부</li>
</ul>
<hr>
<h2 id="2️⃣-build_runner로-코드-생성">2️⃣ <code>build_runner</code>로 코드 생성</h2>
<pre><code class="language-bash">flutter pub run build_runner build --delete-conflicting-outputs</code></pre>
<p>→ 이 명령어를 실행하면 <code>_form_view.form.dart</code> 파일이 생성됩니다.</p>
<hr>
<h2 id="3️⃣-자동-생성된-필드와-컨트롤러-사용하기">3️⃣ 자동 생성된 필드와 컨트롤러 사용하기</h2>
<pre><code class="language-dart">class LoginView extends StackedView&lt;LoginViewModel&gt; with $LoginView {
  @override
  void onViewModelReady(LoginViewModel viewModel) {
    syncFormWithViewModel(viewModel);
  }

  @override
  Widget builder(BuildContext context, LoginViewModel viewModel, Widget? child) {
    return Column(
      children: [
        TextField(controller: emailController),
        TextField(controller: passwordController, obscureText: true),
      ],
    );
  }
}</code></pre>
<ul>
<li><code>emailController</code>, <code>passwordController</code> 등은 자동 생성됨</li>
<li><code>syncFormWithViewModel()</code>을 호출하면 ViewModel과 양방향 바인딩이 연결됨</li>
</ul>
<hr>
<h2 id="4️⃣-viewmodel에서-필드값-접근">4️⃣ ViewModel에서 필드값 접근</h2>
<pre><code class="language-dart">class LoginViewModel extends FormViewModel {
  void submit() {
    print(emailValue);     // 입력된 이메일
    print(passwordValue);  // 입력된 비밀번호
  }
}</code></pre>
<ul>
<li><code>FormViewModel</code>을 상속하면 자동으로 <code>emailValue</code>, <code>passwordValue</code> 등이 생성됩니다.</li>
</ul>
<hr>
<h2 id="5️⃣-추가-기능들">5️⃣ 추가 기능들</h2>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>setFormStatus()</code></td>
<td>폼 상태 갱신 수동 트리거</td>
</tr>
<tr>
<td><code>isFormValid</code></td>
<td>전체 폼 유효성 검사 결과</td>
</tr>
<tr>
<td><code>clearForm()</code></td>
<td>폼 초기화</td>
</tr>
<tr>
<td><code>disposeForm()</code></td>
<td>메모리 정리 (보통 <code>dispose()</code>에서 호출됨)</td>
</tr>
</tbody></table>
<hr>
<h2 id="📌-요약">📌 요약</h2>
<table>
<thead>
<tr>
<th>요소</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>@FormView</code></td>
<td>필드 정의를 위한 애너테이션</td>
</tr>
<tr>
<td><code>build_runner</code></td>
<td>자동 코드 생성</td>
</tr>
<tr>
<td><code>syncFormWithViewModel()</code></td>
<td>View ↔ ViewModel 바인딩 연결</td>
</tr>
<tr>
<td><code>FormViewModel</code></td>
<td>값 접근 및 폼 제어 기능 제공</td>
</tr>
</tbody></table>
<hr>
<h3 id="✅-stacked-form의-장점">✅ Stacked Form의 장점</h3>
<ul>
<li>컨트롤러와 FocusNode 자동 생성</li>
<li>상태/값 추적이 쉬움</li>
<li>ViewModel에서 폼 유효성 체크 가능</li>
<li>복잡한 폼도 깔끔하게 관리 가능</li>
</ul>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li>validation 커스터마이징, 폼 초기값 설정, 여러 폼 필드 간 상호작용 구현</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/getting-started/form-basics">Stacked 공식 문서 - Form Basics</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Navigation Basics]]></title>
            <link>https://velog.io/@yes_circle/Stacked-Navigation-Basics</link>
            <guid>https://velog.io/@yes_circle/Stacked-Navigation-Basics</guid>
            <pubDate>Sun, 06 Jul 2025 12:15:38 GMT</pubDate>
            <description><![CDATA[<h2 id="🚦-navigation-basics-기본-네비게이션">🚦 Navigation Basics (기본 네비게이션)</h2>
<p>Stacked에서는 네비게이션을 쉽게 관리할 수 있도록 <code>NavigationService</code>와 <strong>자동 라우팅 시스템</strong>을 제공합니다. 이로 인해 ViewModel에서도 자유롭게 화면 전환이 가능하고, 코드가 깔끔해집니다.</p>
<hr>
<h3 id="✅-1-stacked_generator를-통한-자동-라우트-생성">✅ 1. <code>stacked_generator</code>를 통한 자동 라우트 생성</h3>
<p>먼저 라우팅을 자동화하기 위해 아래 설정을 해줘야 합니다.</p>
<h4 id="appdart-파일-생성-예시"><code>app.dart</code> 파일 생성 예시:</h4>
<pre><code class="language-dart">@StackedApp(
  routes: [
    MaterialRoute(page: HomeView),
    MaterialRoute(page: DetailsView),
  ],
)
class App {}</code></pre>
<ul>
<li><code>@StackedApp</code> 애너테이션을 통해 앱 내 라우트를 정의합니다.</li>
<li><code>MaterialRoute</code>를 사용하여 일반 화면 이동을 설정합니다.</li>
</ul>
<h4 id="🔁-build_runner로-코드-생성">🔁 <code>build_runner</code>로 코드 생성</h4>
<pre><code class="language-bash">flutter pub run build_runner build --delete-conflicting-outputs</code></pre>
<p>→ 자동으로 <code>app.router.dart</code>, <code>app.dart</code> 등이 생성됨.</p>
<hr>
<h3 id="✅-2-네비게이션-서비스-등록">✅ 2. 네비게이션 서비스 등록</h3>
<p>서비스를 전역에서 사용하려면 의존성 주입 설정에 추가해야 합니다.</p>
<pre><code class="language-dart">final locator = StackedLocator.instance;

void setupLocator() {
  locator.registerLazySingleton(() =&gt; NavigationService());
}</code></pre>
<hr>
<h3 id="✅-3-화면-이동하기">✅ 3. 화면 이동하기</h3>
<h4 id="viewmodel에서-이동">ViewModel에서 이동:</h4>
<pre><code class="language-dart">final _navigationService = locator&lt;NavigationService&gt;();

_navigationService.navigateToHomeView();</code></pre>
<blockquote>
<p>위 함수는 자동 생성된 라우트 이름 기반으로 만들어진 메서드입니다.</p>
</blockquote>
<hr>
<h3 id="✅-4-인자-전달하기">✅ 4. 인자 전달하기</h3>
<pre><code class="language-dart">_navigationService.navigateToDetailsView(
  id: 5,
  name: &#39;Stacked&#39;,
);</code></pre>
<p>→ 라우트 설정 시 <code>DetailsView</code>가 named parameters를 받도록 정의되어 있다면 이렇게 전달 가능.</p>
<hr>
<h3 id="✅-5-화면-되돌아가기">✅ 5. 화면 되돌아가기</h3>
<pre><code class="language-dart">_navigationService.back();</code></pre>
<p>→ 이전 화면으로 돌아가는 기본 동작.</p>
<hr>
<h3 id="✅-6-화면-교체-뒤로-가기-불가능">✅ 6. 화면 교체 (뒤로 가기 불가능)</h3>
<pre><code class="language-dart">_navigationService.replaceWithHomeView();</code></pre>
<p>→ 현재 화면을 <strong>대체</strong>하고 새로운 화면으로 이동합니다.</p>
<hr>
<h2 id="🔧-주요-장점">🔧 주요 장점</h2>
<ul>
<li>ViewModel에서 화면 전환이 가능 → <strong>비즈니스 로직과 UI 완전 분리</strong></li>
<li>라우트 자동 생성 → <strong>실수 줄고 유지보수 쉬움</strong></li>
<li>매개변수 전달, 뒤로가기 등 네비게이션 처리 용이</li>
</ul>
<hr>
<h2 id="📌-정리">📌 정리</h2>
<table>
<thead>
<tr>
<th>기능</th>
<th>코드 예시</th>
</tr>
</thead>
<tbody><tr>
<td>화면 이동</td>
<td><code>_navigationService.navigateToHomeView();</code></td>
</tr>
<tr>
<td>인자 전달</td>
<td><code>_navigationService.navigateToDetailsView(id: 1);</code></td>
</tr>
<tr>
<td>화면 교체</td>
<td><code>_navigationService.replaceWithLoginView();</code></td>
</tr>
<tr>
<td>뒤로 가기</td>
<td><code>_navigationService.back();</code></td>
</tr>
</tbody></table>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li>routes.dart의 커스텀 경로 설정법</li>
<li>BottomSheet, Dialog와의 연동</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/getting-started/navigation-basics">Stacked 공식 문서 - Navigation Basics</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Startup Logic]]></title>
            <link>https://velog.io/@yes_circle/Startup-Logic</link>
            <guid>https://velog.io/@yes_circle/Startup-Logic</guid>
            <pubDate>Sun, 06 Jul 2025 12:04:49 GMT</pubDate>
            <description><![CDATA[<h2 id="🚀-앱-시작-시-실행되는-로직-구성하기-startup-logic">🚀 앱 시작 시 실행되는 로직 구성하기 (Startup Logic)</h2>
<h3 id="✅-목표">✅ 목표</h3>
<p>앱이 시작될 때:</p>
<ul>
<li>사용자 로그인 상태를 확인하고</li>
<li>그에 따라 적절한 첫 화면(HomeView 또는 LoginView)을 보여주는 <strong>초기 로직</strong>을 구성하는 방법을 설명합니다.</li>
</ul>
<hr>
<h2 id="1️⃣-startupviewmodel-만들기">1️⃣ <code>StartUpViewModel</code> 만들기</h2>
<p>앱 시작 시 필요한 로직을 담을 ViewModel을 생성합니다.</p>
<pre><code class="language-dart">class StartUpViewModel extends FutureViewModel {
  @override
  Future&lt;void&gt; futureToRun() async {
    final user = await _authService.getCurrentUser();

    if (user != null) {
      // 홈으로 이동
      _navigationService.replaceWithHomeView();
    } else {
      // 로그인으로 이동
      _navigationService.replaceWithLoginView();
    }
  }
}</code></pre>
<ul>
<li><code>FutureViewModel</code>을 상속받으면 <code>futureToRun()</code> 함수가 자동 실행됩니다.</li>
<li>내부에서 사용자 상태에 따라 <code>NavigationService</code>를 통해 이동을 처리합니다.</li>
</ul>
<hr>
<h2 id="2️⃣-startupview-만들기">2️⃣ <code>StartUpView</code> 만들기</h2>
<pre><code class="language-dart">class StartUpView extends StackedView&lt;StartUpViewModel&gt; {
  @override
  Widget builder(BuildContext context, StartUpViewModel viewModel, Widget? child) {
    return const Scaffold(
      body: Center(child: CircularProgressIndicator()),
    );
  }

  @override
  StartUpViewModel viewModelBuilder(BuildContext context) =&gt; StartUpViewModel();
}</code></pre>
<ul>
<li>앱이 처음 실행될 때 잠시 로딩 인디케이터만 보여주는 화면입니다.</li>
<li>실제 라우팅은 ViewModel에서 처리합니다.</li>
</ul>
<hr>
<h2 id="3️⃣-navigationservice를-통한-라우팅">3️⃣ NavigationService를 통한 라우팅</h2>
<p><code>NavigationService</code>를 사용하면 ViewModel 내에서 자유롭게 화면 전환을 할 수 있습니다.</p>
<pre><code class="language-dart">final _navigationService = locator&lt;NavigationService&gt;();

_navigationService.replaceWithHomeView();  // 홈 화면으로 이동
_navigationService.replaceWithLoginView(); // 로그인 화면으로 이동</code></pre>
<blockquote>
<p><code>replaceWith...</code>는 현재 화면을 대체합니다. 뒤로 가기 불가능.</p>
</blockquote>
<hr>
<h2 id="✅-요약">✅ 요약</h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>StartUpViewModel</code></td>
<td>앱 시작 시 로직 처리 (ex. 로그인 상태 확인)</td>
</tr>
<tr>
<td><code>StartUpView</code></td>
<td>로딩만 보여주고, ViewModel이 라우팅을 담당</td>
</tr>
<tr>
<td><code>NavigationService</code></td>
<td>ViewModel에서 화면 전환 책임</td>
</tr>
</tbody></table>
<blockquote>
<p>이 구조를 사용하면, <strong>앱 시작 시 로직을 UI와 분리하여 깔끔하게 구성</strong>할 수 있습니다.</p>
</blockquote>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li>NavigationService 등록법</li>
<li>replaceWithHomeView()처럼 자동 생성되는 route 이름 설정 방법</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/getting-started/how-it-works">Stacked 공식 문서 - Startup Logic</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - How it works]]></title>
            <link>https://velog.io/@yes_circle/Stacked-How-it-works</link>
            <guid>https://velog.io/@yes_circle/Stacked-How-it-works</guid>
            <pubDate>Sun, 06 Jul 2025 12:01:44 GMT</pubDate>
            <description><![CDATA[<h2 id="📘-stacked는-어떻게-작동하나요">📘 Stacked는 어떻게 작동하나요?</h2>
<p><strong>Stacked</strong>는 Flutter 애플리케이션에서 <strong>UI(View)</strong>, <strong>비즈니스 로직(ViewModel)</strong>, 그리고 **서비스(Service)**를 분리하여 <strong>MVVM(MVVM + 의존성 주입)</strong> 구조를 구성합니다.</p>
<p>이로 인해 다음과 같은 이점을 얻을 수 있어요:</p>
<ul>
<li>코드 구조화</li>
<li>테스트 용이</li>
<li>유지보수 쉬움</li>
<li>협업 효율 향상</li>
</ul>
<hr>
<h3 id="📍-핵심-구조-view--viewmodel">📍 핵심 구조: View + ViewModel</h3>
<pre><code class="language-dart">class HomeView extends StackedView&lt;HomeViewModel&gt; {
  @override
  Widget builder(BuildContext context, HomeViewModel viewModel, Widget? child) {
    return Scaffold(
      body: Center(
        child: Text(viewModel.title),
      ),
    );
  }

  @override
  HomeViewModel viewModelBuilder(BuildContext context) =&gt; HomeViewModel();
}</code></pre>
<p>여기서:</p>
<ul>
<li><code>HomeView</code>는 화면(UI)을 담당하는 <strong>View</strong>입니다.</li>
<li><code>HomeViewModel</code>은 상태와 로직을 담당합니다.</li>
<li><code>builder</code>는 UI를 렌더링하고 <code>viewModelBuilder</code>는 ViewModel을 연결합니다.</li>
</ul>
<hr>
<h3 id="💡-왜-이렇게-구성할까">💡 왜 이렇게 구성할까?</h3>
<h4 id="✅-view는-로직을-알-필요가-없다">✅ View는 로직을 알 필요가 없다.</h4>
<p>View는 단순히 ViewModel이 주는 값을 받아서 화면만 구성하면 돼요. 예:</p>
<pre><code class="language-dart">Text(viewModel.title)</code></pre>
<h4 id="✅-viewmodel은-view를-몰라도-된다">✅ ViewModel은 View를 몰라도 된다.</h4>
<p>ViewModel은 상태를 갖고, View에게 알려줄 뿐 View가 어떻게 렌더링되는지는 모릅니다.
→ <strong>양방향 의존성 없이 느슨하게 연결됨</strong>.</p>
<hr>
<h3 id="🔄-상태-변경-감지">🔄 상태 변경 감지</h3>
<p>Stacked에서 ViewModel은 <code>notifyListeners()</code>를 통해 View에 <strong>상태 변경을 알립니다.</strong>
→ View는 자동으로 리빌드됨.</p>
<p>또한 <code>BaseViewModel</code>에서 다음 상태들도 제공:</p>
<ul>
<li><code>isBusy</code></li>
<li><code>hasError</code></li>
<li><code>hasData</code></li>
</ul>
<hr>
<h3 id="⛓️-viewmodel과-연결된-비즈니스-로직-service">⛓️ ViewModel과 연결된 비즈니스 로직 (Service)</h3>
<p><code>Service</code>는 ViewModel에서 실제 데이터 작업을 담당합니다. 예: API 호출, 로컬 저장소 등</p>
<pre><code class="language-dart">final _counterService = locator&lt;CounterService&gt;();

void incrementCounter() {
  _counterService.increment();
  notifyListeners();
}</code></pre>
<p>→ 서비스는 ViewModel에 주입되고, ViewModel은 그 상태를 View에 전달합니다.</p>
<hr>
<h3 id="📦-서비스-주입-dependency-injection">📦 서비스 주입 (Dependency Injection)</h3>
<p>Stacked는 <a href="https://pub.dev/packages/get_it">get_it</a>을 기반으로 한 의존성 주입 컨테이너를 제공합니다.</p>
<pre><code class="language-dart">final locator = StackedLocator.instance;

void setupLocator() {
  locator.registerLazySingleton(() =&gt; CounterService());
}</code></pre>
<p>이걸 통해 <strong>ViewModel, View, 서비스가 서로 독립적으로 동작</strong>할 수 있게 됩니다.</p>
<hr>
<h2 id="✅-정리">✅ 정리</h2>
<table>
<thead>
<tr>
<th>역할</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>View</strong></td>
<td>UI 담당. ViewModel의 데이터를 UI에 표현</td>
</tr>
<tr>
<td><strong>ViewModel</strong></td>
<td>상태, 로직 처리 담당. View와 Service 중간 다리 역할</td>
</tr>
<tr>
<td><strong>Service</strong></td>
<td>외부 기능/API 수행. ViewModel에 주입됨</td>
</tr>
<tr>
<td><strong>Locator</strong></td>
<td>의존성 주입. 필요한 객체를 전역에서 관리</td>
</tr>
</tbody></table>
<blockquote>
<p>이 구조 덕분에 앱을 테스트하기 쉽고, 유지보수가 편하며, 팀 간 협업도 수월해져요.</p>
</blockquote>
<hr>
<h3 id="알아보면-좋을-내용">알아보면 좋을 내용</h3>
<ul>
<li>setupLocator() 사용법</li>
<li>FutureViewModel, ReactiveViewModel 같은 심화 내용</li>
</ul>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/getting-started/how-it-works">Stacked 공식 문서 - How it wokrs</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Stacked - Overview]]></title>
            <link>https://velog.io/@yes_circle/Stacked-Overview</link>
            <guid>https://velog.io/@yes_circle/Stacked-Overview</guid>
            <pubDate>Sun, 06 Jul 2025 11:59:52 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-stacked-프레임워크-개요">✅ Stacked 프레임워크 개요</h2>
<p><strong>Stacked</strong>는 Flutter 앱 개발에서 <strong>구조화된 코드와 생산성을 높이기 위해 만든 MVVM 아키텍처 기반 프레임워크</strong>입니다.</p>
<hr>
<h3 id="🔧-왜-stacked를-사용할까">🔧 왜 Stacked를 사용할까?</h3>
<ul>
<li><strong>비즈니스 로직과 UI를 분리</strong>하여 깔끔한 코드 유지 가능</li>
<li><strong>재사용성</strong>이 뛰어난 ViewModel 기반 구조</li>
<li><strong>의존성 주입</strong>, <strong>네비게이션</strong>, <strong>다이얼로그</strong>, <strong>BottomSheet</strong>, <strong>Form 관리</strong> 등 실무에서 자주 쓰는 기능들을 통합 제공</li>
<li>프로젝트를 <strong>확장하기 쉽고 유지보수하기 좋게 설계</strong></li>
</ul>
<hr>
<h3 id="🏗️-주요-개념-요약">🏗️ 주요 개념 요약</h3>
<table>
<thead>
<tr>
<th>개념</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>View</strong></td>
<td>UI를 담당. ViewModel과 연결됨.</td>
</tr>
<tr>
<td><strong>ViewModel</strong></td>
<td>상태/비즈니스 로직 담당. View와 분리되어 테스트 용이</td>
</tr>
<tr>
<td><strong>Services</strong></td>
<td>네트워크, 저장소 등 독립적인 기능 구현</td>
</tr>
<tr>
<td><strong>NavigationService</strong></td>
<td>네비게이션을 어디서든 사용할 수 있게 도와주는 서비스</td>
</tr>
<tr>
<td><strong>DialogService</strong></td>
<td>사용자에게 모달/알림을 쉽게 띄울 수 있는 기능</td>
</tr>
<tr>
<td><strong>BottomSheetService</strong></td>
<td>Bottom Sheet를 MVVM 방식으로 구현 가능</td>
</tr>
</tbody></table>
<hr>
<h3 id="🚀-핵심-패키지들">🚀 핵심 패키지들</h3>
<ul>
<li><code>stacked</code></li>
<li><code>stacked_services</code></li>
<li><code>stacked_generator</code> + <code>build_runner</code> (코드 자동 생성)</li>
</ul>
<hr>
<h3 id="📁-기본-프로젝트-구조-예시">📁 기본 프로젝트 구조 예시</h3>
<pre><code>lib/
 ├── app/                # 의존성 주입 설정 등
 ├── ui/
 │   ├── views/          # 각 화면(View + ViewModel)
 │   ├── widgets/        # 공통 위젯
 ├── services/           # API, DB 등 비즈니스 로직
 └── main.dart</code></pre><hr>
<h3 id="⚙️-어떤-기능이-있나">⚙️ 어떤 기능이 있나?</h3>
<ul>
<li><code>@FormView</code>: 자동 Form 관리</li>
<li><code>@ReactiveServiceMixin</code>: 리액티브 서비스 만들기</li>
<li><code>@Route</code>: 네비게이션 라우팅 정의</li>
<li><code>FutureViewModel</code>, <code>StreamViewModel</code>: 비동기 ViewModel 처리 쉽게 지원</li>
</ul>
<hr>
<h3 id="✅-요약">✅ 요약</h3>
<p><strong>Stacked는 Flutter의 구조화된 앱 개발을 도와주는 MVVM 프레임워크로, 유지보수성과 생산성을 높이는 데 매우 효과적인 도구입니다.</strong>
초기 설정만 하면 이후부터는 View-ViewModel-Service의 분리된 구조 덕분에 협업과 테스트, 유지보수가 쉬워집니다.</p>
<hr>
<p>출처 : <a href="https://stacked.filledstacks.com/docs/getting-started/overview">Stacked 공식 문서 - Overview</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue.js 라이프 사이클(Life Cycle)]]></title>
            <link>https://velog.io/@yes_circle/Vue.js-%EB%9D%BC%EC%9D%B4%ED%94%84-%EC%82%AC%EC%9D%B4%ED%81%B4Life-Cycle</link>
            <guid>https://velog.io/@yes_circle/Vue.js-%EB%9D%BC%EC%9D%B4%ED%94%84-%EC%82%AC%EC%9D%B4%ED%81%B4Life-Cycle</guid>
            <pubDate>Thu, 27 Mar 2025 01:00:31 GMT</pubDate>
            <description><![CDATA[<h2 id="✅-vuejs-라이프사이클lifecycle-개요">✅ Vue.js 라이프사이클(Lifecycle) 개요</h2>
<p>Vue.js의 라이프사이클은 <strong>컴포넌트가 생성되고, DOM에 마운트되며, 갱신 및 소멸되는 과정</strong>을 의미합니다.  </p>
<p>라이프사이클 훅(Lifecycle Hooks)을 사용하면 특정 단계에서 코드를 실행할 수 있으며, 이를 통해 <strong>비동기 데이터 가져오기, 이벤트 리스너 등록, DOM 조작</strong> 등을 할 수 있습니다.</p>
<hr>
<h2 id="🔥-vue-3-라이프사이클-훅-전체-흐름">🔥 <strong>Vue 3 라이프사이클 훅 전체 흐름</strong></h2>
<h3 id="🔹-1-생성-단계-creation">🔹 1. <strong>생성 단계 (Creation)</strong></h3>
<ul>
<li><strong><code>setup()</code></strong> (Composition API 사용 시)</li>
<li><strong><code>beforeCreate()</code></strong> (Composition API 사용 시 생략 가능)</li>
<li><strong><code>created()</code></strong> (Composition API 사용 시 생략 가능)</li>
</ul>
<h3 id="🔹-2-마운트-단계-mounting">🔹 2. <strong>마운트 단계 (Mounting)</strong></h3>
<ul>
<li><strong><code>beforeMount()</code></strong></li>
<li><strong><code>mounted()</code></strong> ✅ <strong>DOM이 렌더링된 후 실행됨</strong></li>
</ul>
<h3 id="🔹-3-업데이트-단계-updating">🔹 3. <strong>업데이트 단계 (Updating)</strong></h3>
<ul>
<li><strong><code>beforeUpdate()</code></strong> (DOM이 변경되기 직전)</li>
<li><strong><code>updated()</code></strong> (DOM이 변경된 후 실행됨)</li>
</ul>
<h3 id="🔹-4-소멸-단계-unmounting">🔹 4. <strong>소멸 단계 (Unmounting)</strong></h3>
<ul>
<li><strong><code>beforeUnmount()</code></strong></li>
<li><strong><code>unmounted()</code></strong> ✅ <strong>컴포넌트가 제거된 후 실행됨</strong></li>
</ul>
<hr>
<h2 id="🎯-vue-3-라이프사이클-상세-설명">🎯 <strong>Vue 3 라이프사이클 상세 설명</strong></h2>
<h3 id="🟢-1-생성-단계-creation">🟢 <strong>1. 생성 단계 (Creation)</strong></h3>
<blockquote>
<p><strong>컴포넌트가 메모리에 로드되는 단계 (DOM에 접근할 수 없음)</strong></p>
</blockquote>
<table>
<thead>
<tr>
<th>훅</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>setup()</code></td>
<td>Composition API 사용 시 실행됨 (<code>beforeCreate</code> 및 <code>created</code> 대신 사용)</td>
</tr>
<tr>
<td><code>beforeCreate()</code></td>
<td>컴포넌트가 초기화되기 전 (data, props 접근 불가)</td>
</tr>
<tr>
<td><code>created()</code></td>
<td>data, computed, methods 사용 가능하지만 DOM 접근 불가</td>
</tr>
</tbody></table>
<p>📌 <strong>Example:</strong></p>
<pre><code class="language-vue">&lt;script setup&gt;
console.log(&quot;setup() 실행됨&quot;);
&lt;/script&gt;

&lt;script&gt;
export default {
  beforeCreate() {
    console.log(&quot;beforeCreate() 실행됨&quot;);
  },
  created() {
    console.log(&quot;created() 실행됨&quot;);
  }
};
&lt;/script&gt;</code></pre>
<p>✅ <strong>실행 순서:</strong>  </p>
<pre><code>setup() 실행됨
beforeCreate() 실행됨
created() 실행됨</code></pre><hr>
<h3 id="🔵-2-마운트-단계-mounting">🔵 <strong>2. 마운트 단계 (Mounting)</strong></h3>
<blockquote>
<p><strong>템플릿이 가상 DOM에서 실제 DOM으로 렌더링되는 단계</strong></p>
</blockquote>
<table>
<thead>
<tr>
<th>훅</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>beforeMount()</code></td>
<td>가상 DOM이 준비되었지만, 실제 DOM에는 아직 반영되지 않음</td>
</tr>
<tr>
<td><code>mounted()</code> ✅</td>
<td><strong>컴포넌트가 DOM에 삽입된 후 실행됨 (DOM 접근 가능)</strong></td>
</tr>
</tbody></table>
<p>📌 <strong>Example:</strong></p>
<pre><code class="language-vue">&lt;script&gt;
export default {
  beforeMount() {
    console.log(&quot;beforeMount() 실행됨&quot;);
  },
  mounted() {
    console.log(&quot;mounted() 실행됨&quot;);
  }
};
&lt;/script&gt;</code></pre>
<p>✅ <strong>실행 순서:</strong>  </p>
<pre><code>beforeMount() 실행됨
mounted() 실행됨</code></pre><p>🚀 <code>mounted()</code>에서 API 호출, 이벤트 리스너 등록 등을 수행하는 것이 일반적.</p>
<hr>
<h3 id="🟠-3-업데이트-단계-updating">🟠 <strong>3. 업데이트 단계 (Updating)</strong></h3>
<blockquote>
<p><strong>컴포넌트의 반응형 데이터가 변경될 때 실행됨</strong></p>
</blockquote>
<table>
<thead>
<tr>
<th>훅</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>beforeUpdate()</code></td>
<td>데이터 변경 → DOM이 업데이트되기 직전</td>
</tr>
<tr>
<td><code>updated()</code></td>
<td>데이터 변경 → DOM이 업데이트된 후</td>
</tr>
</tbody></table>
<p>📌 <strong>Example:</strong></p>
<pre><code class="language-vue">&lt;template&gt;
  &lt;div&gt;
    &lt;p&gt;{{ count }}&lt;/p&gt;
    &lt;button @click=&quot;count++&quot;&gt;증가&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  data() {
    return { count: 0 };
  },
  beforeUpdate() {
    console.log(`beforeUpdate(): count가 곧 ${this.count + 1}로 변경됨`);
  },
  updated() {
    console.log(`updated(): count가 ${this.count}로 변경됨`);
  }
};
&lt;/script&gt;</code></pre>
<p>✅ <strong>실행 순서 (버튼 클릭 시):</strong></p>
<pre><code>beforeUpdate(): count가 곧 1로 변경됨
updated(): count가 1로 변경됨</code></pre><hr>
<h3 id="🔴-4-소멸-단계-unmounting">🔴 <strong>4. 소멸 단계 (Unmounting)</strong></h3>
<blockquote>
<p><strong>컴포넌트가 DOM에서 제거될 때 실행됨 (메모리 정리 필수!)</strong></p>
</blockquote>
<table>
<thead>
<tr>
<th>훅</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>beforeUnmount()</code></td>
<td>컴포넌트가 제거되기 직전</td>
</tr>
<tr>
<td><code>unmounted()</code> ✅</td>
<td><strong>컴포넌트가 제거된 후 실행됨 (이벤트 리스너 해제 등)</strong></td>
</tr>
</tbody></table>
<p>📌 <strong>Example:</strong></p>
<pre><code class="language-vue">&lt;template&gt;
  &lt;div v-if=&quot;isVisible&quot;&gt;
    &lt;p&gt;이 컴포넌트는 곧 사라집니다!&lt;/p&gt;
    &lt;button @click=&quot;isVisible = false&quot;&gt;숨기기&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  data() {
    return { isVisible: true };
  },
  beforeUnmount() {
    console.log(&quot;beforeUnmount() 실행됨 - 컴포넌트가 곧 제거됨&quot;);
  },
  unmounted() {
    console.log(&quot;unmounted() 실행됨 - 컴포넌트가 제거됨&quot;);
  }
};
&lt;/script&gt;</code></pre>
<p>✅ <strong>버튼 클릭 시 실행 순서:</strong>  </p>
<pre><code>beforeUnmount() 실행됨 - 컴포넌트가 곧 제거됨
unmounted() 실행됨 - 컴포넌트가 제거됨</code></pre><p>🚀 <code>unmounted()</code>에서 <strong>이벤트 리스너 제거, 타이머 해제 등 메모리 정리</strong>를 수행해야 함.</p>
<hr>
<h2 id="⚡-vue-3-라이프사이클-훅-정리">⚡ <strong>Vue 3 라이프사이클 훅 정리</strong></h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>라이프사이클 훅</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>생성</strong></td>
<td><code>setup()</code></td>
<td>Composition API 사용 시</td>
</tr>
<tr>
<td></td>
<td><code>beforeCreate()</code></td>
<td>초기화 전 (data 접근 불가)</td>
</tr>
<tr>
<td></td>
<td><code>created()</code></td>
<td>초기화 후 (data 접근 가능, DOM 접근 불가)</td>
</tr>
<tr>
<td><strong>마운트</strong></td>
<td><code>beforeMount()</code></td>
<td>가상 DOM 준비 완료 (아직 실제 DOM X)</td>
</tr>
<tr>
<td></td>
<td><code>mounted()</code></td>
<td><strong>실제 DOM 렌더링 완료 (DOM 조작 가능)</strong></td>
</tr>
<tr>
<td><strong>업데이트</strong></td>
<td><code>beforeUpdate()</code></td>
<td>데이터 변경 → DOM 업데이트 전</td>
</tr>
<tr>
<td></td>
<td><code>updated()</code></td>
<td>데이터 변경 → DOM 업데이트 후</td>
</tr>
<tr>
<td><strong>소멸</strong></td>
<td><code>beforeUnmount()</code></td>
<td>컴포넌트 제거 직전</td>
</tr>
<tr>
<td></td>
<td><code>unmounted()</code></td>
<td><strong>컴포넌트 제거 완료 (이벤트 해제 필수)</strong></td>
</tr>
</tbody></table>
<hr>
<h2 id="🎯-결론">🎯 <strong>결론</strong></h2>
<p>1️⃣ <code>setup()</code>은 Composition API에서 가장 먼저 실행됨.<br>2️⃣ <code>mounted()</code>에서 <strong>API 호출 및 DOM 조작</strong>을 수행하는 것이 일반적.<br>3️⃣ <code>updated()</code>에서 <strong>DOM 변경 후 작업</strong>을 수행할 수 있음.<br>4️⃣ <code>unmounted()</code>에서 <strong>이벤트 리스너 해제, 타이머 제거 등 메모리 정리</strong> 필요.  </p>
<p>💡 <strong>Vue 라이프사이클을 활용하면 효율적인 컴포넌트 관리를 할 수 있음!</strong> 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dart] 3가지 프로그래밍]]></title>
            <link>https://velog.io/@yes_circle/Dart%EA%B0%80-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3%EA%B0%80%EC%A7%80</link>
            <guid>https://velog.io/@yes_circle/Dart%EA%B0%80-%EC%A7%80%EC%9B%90%ED%95%98%EB%8A%94-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-3%EA%B0%80%EC%A7%80</guid>
            <pubDate>Thu, 20 Mar 2025 00:16:44 GMT</pubDate>
            <description><![CDATA[<p>Flutter와 Dart는 모바일 앱 개발에서 널리 사용되는 프레임워크와 프로그래밍 언어로, 객체지향 프로그래밍(OOP), 함수형 프로그래밍(FP), 그리고 비동기 프로그래밍을 모두 지원합니다. 아래에서 각각에 대해 자세히 설명하겠습니다.</p>
<hr>
<h3 id="1-객체지향-프로그래밍-object-oriented-programming-oop">1. <strong>객체지향 프로그래밍 (Object-Oriented Programming, OOP)</strong></h3>
<p>Dart는 객체지향 언어로 설계되었으며, 클래스를 기반으로 한 전형적인 OOP 기능을 제공합니다. Flutter는 Dart를 사용하기 때문에 객체지향 개념이 앱 개발의 핵심적인 부분을 차지합니다.</p>
<ul>
<li><p><strong>클래스와 객체</strong>: Dart에서 모든 것은 객체이며, 클래스를 통해 객체를 생성합니다. 예를 들어, Flutter의 위젯(Widget)은 모두 클래스로 정의됩니다.</p>
<pre><code class="language-dart">class Person {
  String name;
  int age;

  Person(this.name, this.age);

  void sayHello() {
    print(&#39;안녕, 나는 $name이야!&#39;);
  }
}

void main() {
  var person = Person(&#39;홍길동&#39;, 30);
  person.sayHello(); // 출력: 안녕, 나는 홍길동이야!
}</code></pre>
</li>
<li><p><strong>상속(Inheritance)</strong>: Dart는 단일 상속을 지원하며, <code>extends</code> 키워드로 클래스를 확장할 수 있습니다. Flutter에서 <code>StatelessWidget</code>이나 <code>StatefulWidget</code>을 상속받아 커스텀 위젯을 만드는 것이 일반적입니다.</p>
</li>
<li><p><strong>캡슐화(Encapsulation)</strong>: private 변수나 메서드는 <code>_</code> 언더스코어로 선언해 접근을 제한합니다.</p>
</li>
<li><p><strong>다형성(Polymorphism)</strong>: 인터페이스나 추상 클래스를 통해 다형성을 구현할 수 있습니다.</p>
</li>
</ul>
<p><strong>Flutter에서의 활용</strong>: Flutter는 위젯 트리를 기반으로 UI를 구성하며, 모든 UI 요소가 객체로 표현됩니다. 예를 들어, <code>Text</code> 위젯, <code>Container</code> 위젯 등은 모두 클래스의 인스턴스입니다.</p>
<hr>
<h3 id="2-함수형-프로그래밍-functional-programming-fp">2. <strong>함수형 프로그래밍 (Functional Programming, FP)</strong></h3>
<p>Dart는 함수형 프로그래밍 요소를 점진적으로 도입했으며, 특히 고차 함수(higher-order functions), 클로저(closure), 불변성(immutability) 등을 지원합니다. Flutter에서도 함수형 스타일을 자주 사용합니다.</p>
<ul>
<li><p><strong>일급 객체로서의 함수</strong>: 함수를 변수에 저장하거나 다른 함수의 인자로 전달할 수 있습니다.</p>
<pre><code class="language-dart">void applyOperation(int a, int b, Function(int, int) operation) {
  print(operation(a, b));
}

void main() {
  applyOperation(5, 3, (a, b) =&gt; a + b); // 출력: 8
}</code></pre>
</li>
<li><p><strong>불변성</strong>: Dart는 <code>final</code>과 <code>const</code> 키워드로 불변 객체를 생성할 수 있습니다. Flutter에서 위젯은 기본적으로 불변(immutable)으로 설계되어 상태 변화 없이 새 인스턴스를 생성합니다.</p>
</li>
<li><p><strong>컬렉션 작업</strong>: <code>map</code>, <code>filter</code>, <code>reduce</code> 같은 함수형 메서드를 제공해 리스트나 맵을 선언적으로 처리합니다.</p>
<pre><code class="language-dart">var numbers = [1, 2, 3, 4];
var doubled = numbers.map((n) =&gt; n * 2).toList(); // [2, 4, 6, 8]</code></pre>
</li>
</ul>
<p><strong>Flutter에서의 활용</strong>: 함수형 프로그래밍은 상태 관리(예: Provider, Riverpod)나 데이터 변환에서 자주 사용됩니다. 위젯 트리를 구성할 때도 함수형 접근법으로 깔끔한 코드를 작성할 수 있습니다.</p>
<hr>
<h3 id="3-비동기-프로그래밍-asynchronous-programming">3. <strong>비동기 프로그래밍 (Asynchronous Programming)</strong></h3>
<p>Dart는 비동기 작업을 처리하기 위해 <code>async</code>와 <code>await</code> 키워드를 사용하며, <code>Future</code>와 <code>Stream</code>을 통해 비동기 흐름을 관리합니다. Flutter에서는 네트워크 요청, 파일 I/O, 애니메이션 등 시간이 걸리는 작업에 비동기 프로그래밍이 필수적입니다.</p>
<ul>
<li><p><strong>Future</strong>: 단일 비동기 작업의 결과를 나타냅니다.</p>
<pre><code class="language-dart">Future&lt;String&gt; fetchData() async {
  await Future.delayed(Duration(seconds: 2)); // 2초 대기
  return &#39;데이터를 가져왔습니다!&#39;;
}

void main() async {
  print(&#39;데이터를 가져오는 중...&#39;);
  String data = await fetchData();
  print(data); // 출력: 데이터를 가져왔습니다!
}</code></pre>
</li>
<li><p><strong>Stream</strong>: 연속적인 비동기 데이터 흐름을 처리합니다. 예를 들어, 실시간 채팅이나 센서 데이터 수신에 사용됩니다.</p>
<pre><code class="language-dart">Stream&lt;int&gt; countStream() async* {
  for (int i = 1; i &lt;= 3; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

void main() {
  countStream().listen((value) =&gt; print(value)); // 출력: 1초마다 1, 2, 3
}</code></pre>
</li>
<li><p><strong>에러 처리</strong>: <code>try-catch</code> 블록을 사용해 비동기 작업의 예외를 처리합니다.</p>
<pre><code class="language-dart">Future&lt;void&gt; riskyOperation() async {
  try {
    await Future.error(&#39;오류 발생!&#39;);
  } catch (e) {
    print(e); // 출력: 오류 발생!
  }
}</code></pre>
</li>
</ul>
<p><strong>Flutter에서의 활용</strong>: HTTP 요청(<code>http</code> 패키지), 데이터베이스 작업(<code>sqflite</code>), 애니메이션 컨트롤러 등에서 비동기 프로그래밍이 사용됩니다. 예를 들어, API에서 데이터를 가져와 UI를 업데이트하려면 <code>FutureBuilder</code>나 <code>StreamBuilder</code>를 활용합니다.</p>
<hr>
<h3 id="요약">요약</h3>
<ul>
<li><strong>객체지향</strong>: Dart의 클래스 기반 구조는 Flutter 위젯 시스템의 기반입니다.</li>
<li><strong>함수형</strong>: 고차 함수와 불변성은 코드의 간결함과 예측 가능성을 높입니다.</li>
<li><strong>비동기</strong>: <code>Future</code>와 <code>Stream</code>으로 비동기 작업을 효율적으로 처리하며, Flutter 앱의 반응성을 유지합니다.</li>
</ul>
<p>이 세 가지 패러다임은 서로 보완적으로 작용하며, Flutter와 Dart로 견고하고 효율적인 앱을 개발할 수 있게 해줍니다. 추가 질문이 있다면 말씀해주세요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[사전 렌더링(Pre Rendering)]]></title>
            <link>https://velog.io/@yes_circle/%EC%82%AC%EC%A0%84-%EB%A0%8C%EB%8D%94%EB%A7%81Pre-Rendering</link>
            <guid>https://velog.io/@yes_circle/%EC%82%AC%EC%A0%84-%EB%A0%8C%EB%8D%94%EB%A7%81Pre-Rendering</guid>
            <pubDate>Sun, 24 Nov 2024 11:47:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h4 id="렌더링의-2가지-의미">렌더링의 2가지 의미</h4>
</blockquote>
<ol>
<li><strong>JS 실행(렌더링)</strong> : 자바스크립트 코트(React 컴포넌트)를 HTML로 변환하는 과정</li>
<li><strong>화면에 렌더링</strong> : HTML 코드를 브라우저가 화면에 그려내는 작업</li>
</ol>
<h2 id="사전-렌더링이란">사전 렌더링이란?</h2>
<p>: 브라우저의 요청에 서버측에서 사전에 렌더링이 완료된 HTML을 응답하는 렌더링 방식</p>
<ul>
<li><p>Client Side Rendering(CSR)의 단점을 효율적으로 해결하는 기술</p>
</li>
<li><p>React APP의 단점 해소(빠른 FCP 달성) + React App의 장점 승계(빠른 페이지 이동)
<span style="color:gray"><em>(React.js는 CSR 방식)</em><span>
<img src="https://velog.velcdn.com/images/yes_circle/post/9118bd9a-1281-4571-851a-5a6df8fc863f/image.png" alt=""></p>
<p>⬇️ 위 이미지는 아래 이미지의 보라색 영역에 대한 상세 내용</p>
<p><img src="https://velog.velcdn.com/images/yes_circle/post/7751e74d-674a-4af3-aada-36778800ead0/image.png" alt=""></p>
</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><strong>FCP(First Contentful Paint)</strong> : 요청 시작 시점으로부터 컨텐츠가 화면에 처음 나타나는데 걸리는 시간.</li>
<li><strong>TTI(Time To Interactive)</strong> : 웹 페이지가 상호 작용할 때까지 걸리는 시간.</li>
<li><strong>수화(Hydration)</strong> : 화면 렌더링이 진행된 브라우저 화면에서는 아직 인터렉션(상호작용)이 불가한데 이후에 서버에게 개발자가 작성한 모든 자바스립트 코드를 받고 코드를 실행해서 화면에 렌더링 되어있는 HTML코드와 연결해주는 과정(상호작용이 가능하게 해주는 과정)</li>
</ul>
<p><br><br></p>
<h3 id="client-side-renderingcsr이란">Client Side Rendering(CSR)이란?</h3>
<ul>
<li>React.js 앱의 기본적인 렌더링 방식</li>
<li>클라이언트(브라우저)에서 직접 화면을 렌더링 하는 방식</li>
<li><strong>장점</strong> : 초기접속 이후에 일어나는 페이지 이동이 매우 빠르고 쾌적
(서버에게 새로운 페이지를 요청할 필요 x)</li>
<li><strong>단점</strong> : 초기 접속 속도(FCP: First Contentful Paint)가 느림
<img src="https://velog.velcdn.com/images/yes_circle/post/3c8ba54f-5abe-4cf8-a073-04141e3688fb/image.png" alt=""><blockquote>
<p><strong>JS Bundle</strong> -&gt; 여기서는 React App
: 이 서비스에서 접근 가능한 모든 컴포넌트 코드가 존재</p>
</blockquote>
</li>
</ul>
<p><br><br><br><br><br>
  <span style="color:gray;font-size:10pt">이미지 출처 : 한 입 크기로 잘라먹는 Next.js 강의</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React.js(라이브러리)와 Next.js(프레임워크) 비교]]></title>
            <link>https://velog.io/@yes_circle/React.js%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%99%80-Next.js%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@yes_circle/React.js%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%99%80-Next.js%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Sun, 24 Nov 2024 11:00:15 GMT</pubDate>
            <description><![CDATA[<h2 id="reactjs란">React.js란?</h2>
<ul>
<li>UI 개발을 위한 JavaScript <span style="color:green"><strong>Library</strong></span></li>
</ul>
<h2 id="nextjs란">Next.js란?</h2>
<ul>
<li>React.js 전용의 웹 개발 <span style="color:green"><strong>Framework</strong></span></li>
</ul>
<blockquote>
<p><strong>Library와 Framework의 차이는?</strong>
기능 구현의 주도권이 누구에게 있는가에 따라 나뉜다.</p>
</blockquote>
<ul>
<li>Library : 주도권이 개발자에게 있음. ex) React.js, jQuery 등<ul>
<li>기능 구현을 원하는 방향으로 진행</li>
<li>쓰고싶은 도구, 쓰고싶은 기술을 사용
ex) React.js 사용 시, 페이지 라우팅 기능을 구현해야한다면 React Router나 TanStack Router 라이브러리를 사용</li>
</ul>
</li>
<li>Framework : 주도권이 개발자에게 없음. ex) Next.js, Remix 등<ul>
<li>프레임워크가 제공하는 기능을 이용하거나, 허용하는 범위 내에서만 추가 도구 사용 가능
ex) Next.js 사용 시, 페이지 라우팅 기능을 구현해야한다면 Next.js가 제공하는 라우터인 Page Router 또는 App Router를 사용</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th></th>
<th>React.js</th>
<th>Next.js</th>
</tr>
</thead>
<tbody><tr>
<td>정의</td>
<td>UI 개발을 위한 JavaScript &quot;Library&quot;<br></td>
<td>React.js 전용의 웹 개발 &quot;Framework&quot;</td>
</tr>
<tr>
<td>장점</td>
<td>자유도가 높다</td>
<td>거의 모든 기능을 제공<br>- Page Routing <br> - Optimizations <br> - Server Pre Rendering</td>
</tr>
<tr>
<td>단점</td>
<td>기본 기능 외 제공 x</td>
<td>자유도가 낮다</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] ios Build Error - RCT-Folly hash No template named 'unary_function']]></title>
            <link>https://velog.io/@yes_circle/React-Native-ios-Build-Error-RCT-Folly-hash-No-template-named-unaryfunction</link>
            <guid>https://velog.io/@yes_circle/React-Native-ios-Build-Error-RCT-Folly-hash-No-template-named-unaryfunction</guid>
            <pubDate>Sun, 17 Mar 2024 14:37:42 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">[문제]</h3>
<p>No template named &#39;unary_function&#39; in namespace &#39;std&#39;; did you mean &#39;__unary_function&#39;?</p>
<p><img src="https://velog.velcdn.com/images/yes_circle/post/8d05fde5-48ee-468f-a9ed-c64ae2a63559/image.png" alt=""></p>
<h3 id="해결">[해결]</h3>
<p>Pods &gt; Build Settings &gt; Apple Clang - Preprocessing &gt; Preprocessor Macros 의
Debug, Release 에 아래 값 추가</p>
<pre><code>LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION</code></pre><p><img src="https://velog.velcdn.com/images/yes_circle/post/d2f73c62-d6d8-4586-890f-58e69e1f985d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dart] 1.Variables]]></title>
            <link>https://velog.io/@yes_circle/Dart-1.Variables</link>
            <guid>https://velog.io/@yes_circle/Dart-1.Variables</guid>
            <pubDate>Sun, 03 Mar 2024 11:38:33 GMT</pubDate>
            <description><![CDATA[<h2 id="main-함수">main 함수</h2>
<ul>
<li>모든 Dart 프로그램의 Entry point이기 때문에 아주 중요!</li>
<li>반드시 main 함수를 작성해야 함.</li>
<li>main 함수에서 내가 작성한 코드가 호출되기 때문</li>
<li><strong>세미콜론(;)</strong>은 꼭 붙여줘야 함.(dart에서는 일부러 세미콜론을 사용하지않을 때가 있으니 주의!)</li>
</ul>
<h2 id="variables">Variables</h2>
<h3 id="■-변수를-만드는-방법">■ 변수를 만드는 방법</h3>
<h4 id="1-var">1. var</h4>
<pre><code class="language-dart">void main() {
    var name = &quot;리아&quot;;
    name = 1; // name의 타입은 String인데 1(int)를 넣으면 에러 발생
    name = &quot;ria&quot;;
}</code></pre>
<ul>
<li>변수를 수정할 땐 같은 타입으로 해줘야 함.</li>
</ul>
<h4 id="2-명시적으로-타입-지정-ex-string">2. 명시적으로 타입 지정 (ex. String)</h4>
<pre><code class="language-dart">void main() {
    String name = &quot;리아&quot;;
    name = &quot;ria&quot;;
}</code></pre>
<blockquote>
<p>관습적으로 </p>
</blockquote>
<ul>
<li>함수나 메소드 내부에 지역 변수를 선언할 때는 var를 사용</li>
<li>class에서 변수나 property를 선언할 때는 타입을 지정</li>
</ul>
<h3 id="■-dynamic">■ dynamic</h3>
<ul>
<li>여러가지 타입을 가질 수 있는 변수에 쓰는 키워드</li>
<li>dynamic 사용을 추전하진 않지만 때때로 유용함(사용해야만 할 때도 있음)</li>
<li><strong>정말 써야할 때만 사용!!!</strong></li>
<li>변수를 선언할 때 dynamic을 쓰거나 값을 지정하지 않으면 dynamic 타입을 가짐<pre><code class="language-dart">void main() {
var name; // dynamic 변수
name = &quot;ria&quot;;
name = 1;
name = true;
}
</code></pre>
</li>
</ul>
<pre><code>``` dart
void main() {
  dynamic name; // dynamic 변수
  name = &quot;ria&quot;;
  name = 1;
  name = true;
}
</code></pre><h4 id="dynamic을-사용하는-경우">dynamic을 사용하는 경우</h4>
<ul>
<li>변수가 어떤 타입일지 알기 어려운 경우
ex) flutter와 json을 함께 사용할 때</li>
</ul>
<pre><code class="language-dart">void main() {
  dynamic name;

  if (name is String) {
      name. /* name. 까지 입력하면 String 타입관련 자동완성 메서드 목록이 나타남. 
             해당 블록 안에서는 name이 String이란 걸 알기 때문 */
  }

  // 여기서 name은 dynamic이기 때문에 타입을 체크해줘야 함

  if (name is int) {
      name. // int 타입관련 자동오나성 메스드 목록이 나타남.
  }
}</code></pre>
<h3 id="■-nullbale-variables">■ Nullbale Variables</h3>
<h4 id="null-safety">null safety</h4>
<ul>
<li>개발자가 null 값을 참조할 수 없도록 함.</li>
<li>코드에서 null 값을 참조하면 <strong>런타임 에러</strong>(사용자가 앱을 사용하는 중에 발생하는 에러) 발생.</li>
<li>기본적으로 모든 변수는 non-nullable(null이 될 수 없음) 임.</li>
<li><strong>&#39;?&#39;</strong>을 사용하면 해당 변수가 null이 될 수 있다는 걸 알게 함.</li>
</ul>
<pre><code class="language-dart">void main() {
    String**?** name = &quot;ria&quot;;
    name = null;

    if (name !== null) { // 컴파일러가 name은 null 아님을 알 수 있음
        name.isNotEmpty;
    }

    // 간단하게 작성하면 이와 같음
    name?.isNotEmpty;
}</code></pre>
<h3 id="■-final-variables">■ Final Variables</h3>
<ul>
<li>한 번 정의된 변수를 수정할 수 없게 만들기 위해 사용.</li>
<li>js/ts의 const와 같음</li>
</ul>
<pre><code class="language-dart">void main() {
    // 값을 변경할 수 있는 변수
    String name = &quot;ria&quot;;
    name = &quot;리아&quot;;

    // 값을 변경할 수 없는 변수
    final name = &quot;ria&quot;;
    // 굳이 필요없지만 type을 추가 할 수 있음 (필수x)
    final String name = &quot;ria&quot;;

}</code></pre>
<h3 id="■-late-variables">■ Late variables</h3>
<ul>
<li><p>final이나 var 앞에 붙여줄 수 있는 수식어</p>
</li>
<li><p>초기 데이터 없이 변수를 선언할 수 있게 해줌</p>
</li>
<li><p>장점</p>
<ul>
<li><p>데이터가 없는 상태에서 해당 변수를 사용하려고 하면 Dart가 막아줌</p>
<pre><code class="language-dart">void main() {
// 변수를 만드는데 데이터가 없음(필요한 데이터가 아직 없음)
late final String name; // 한번만 할당 할 수 있는 변수

// API 요청하여 데이터를 받은 다음, 그 데이터를 변수에 넣음
name = &quot;ria&quot;;
}</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="■-constant-variables-상수">■ Constant Variables (상수)</h3>
<ul>
<li>js/ts의 const와 다름. (js/ts의 const는 Dart의 final과 비슷)</li>
<li>Dart에서 const는 <strong>compile-time constant</strong>를 만들어 줌</li>
<li>const는 compile-time에 알고 있는 값이어야 함
ex) max_allowed_price</li>
</ul>
<pre><code class="language-dart">void main() {
    final API = fetchApi(); // o
    const API = fetchApi(); // x 컴파일 할 때 알고 있는 값이 아님.
                            // 앱스토어에 올리기 전에 알고 있는 값이어야 함.
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dart] 0. Dart 개요]]></title>
            <link>https://velog.io/@yes_circle/Dart-1.-Dart-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@yes_circle/Dart-1.-Dart-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Sun, 04 Feb 2024 14:57:42 GMT</pubDate>
            <description><![CDATA[<h2 id="dart-특징">Dart 특징</h2>
<ol>
<li>구글에서 만든 객체지향 언어</li>
<li>User Interface를 만드는데 최적화</li>
<li>빠르다</li>
<li>다양한 플랫폼에 컴파일 가능</li>
<li>개발자 경험이 좋음</li>
<li>Kotlin, Swift, TypeScript와 비슷</li>
</ol>
<p><br><br></p>
<h3 id="구글이-dart를-flutter의-언어로-선택한-이유">구글이 Dart를 Flutter의 언어로 선택한 이유</h3>
<p>(참고: <a href="https://dart-ko.dev/overview#native-platform">Dart 공식문서 - Dart 개요</a>)
<img src="https://velog.velcdn.com/images/yes_circle/post/bfb4d8eb-9d59-4e3c-93d7-ed4dee61ec64/image.png" alt=""></p>
<ol>
<li>UI에 최적화</li>
<li>생산적인 개발 환경</li>
<li>모든 플랫폼에서 빠름<ul>
<li>Dart Web(JavaScript)<ul>
<li>Dart로 쓴 코드를 JavaScript로 변환해주는 컴파일러</li>
</ul>
</li>
<li>Dart Native<ul>
<li>Dart로 쓴 코드를 여러 CPU의 아키텍쳐에 맞게 변환해주는 컴파일러</li>
<li>ex) ARM32, ARM64(모바일), x86_64(데스크탑)</li>
<li>즉, IOS, Android, Windows, Linux, Mac으로 컴파일이 가능함</li>
<li>사물인터넷(IoT) ex) 자동차 를 만드는데도 사용</li>
</ul>
</li>
</ul>
</li>
<li><strong>null safety</strong> </li>
</ol>
<h4 id="요약">[요약]</h4>
<ol>
<li><strong>JIT</strong>(just-in-time) 컴파일과 <strong>AOT</strong>(ahead-of-time) 컴파일이 둘 다 있음 =&gt; 모바일 개발에 아주 좋은 언어</li>
<li>Dart와 Flutter는 둘 다 구글이 만들었음 =&gt; 구글은 Flutter를 위해 Dart를 최적화 할 수 있음</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Native] Android Build Error - Could not read /node_modules/expo-modules-core/android/build/downloads/boost_1_76_0.tar.gz.]]></title>
            <link>https://velog.io/@yes_circle/React-Native-Android-Build-Error-Could-not-read-nodemodulesexpo-modules-coreandroidbuilddownloadsboost1760.tar.gz</link>
            <guid>https://velog.io/@yes_circle/React-Native-Android-Build-Error-Could-not-read-nodemodulesexpo-modules-coreandroidbuilddownloadsboost1760.tar.gz</guid>
            <pubDate>Tue, 09 Jan 2024 06:08:37 GMT</pubDate>
            <description><![CDATA[<h3 id="문제">[문제]</h3>
<p>2023년 해가 바뀌고 2024년 오랜만에 Android Build를 다시 해보았다.
그동안 발생하지 않던 빌드 에러가 발생했다.
(Expo ejcet 한 프로젝트라 예상치 못한 이슈가 자주 발생한다.)</p>
<p><code>boost_1_76_0.tar.gz.</code> 를 찾지 못한다니..</p>
<pre><code>Execution failed for task &#39;:expo-modules-core:prepareBoost&#39;.
Could not read ${프로젝트 경로}/node_modules/expo-modules-core/android/build/downloads/boost_1_76_0.tar.gz.</code></pre><p><img src="https://velog.velcdn.com/images/yes_circle/post/44d8ce43-4930-4141-b35b-0d4b33b68898/image.png" alt=""></p>
<h3 id="해결">[해결]</h3>
<p>구글링 해본 결과, 이런 이슈가 간간이 발생하는지 <a href="https://github.com/expo/expo/issues/26184#issuecomment-1880639020">expo Github</a> 이슈에 올라와있었다.</p>
<ol>
<li><p>package 소스 코드 수정
/node-modules/expo-modules-core/android/build.gradle 파일을 찾아 <code>task downloadBoost()</code>를 찾아 아래와 같이 코드 수정
<strong>Before</strong></p>
<pre><code class="language-js">task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
 def srcUrl = REACT_NATIVE_TARGET_VERSION &gt;= 69
   ? &quot;https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION.replace(&quot;_&quot;, &quot;.&quot;)}/source/boost_${BOOST_VERSION}.tar.gz&quot;
   : &quot;https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace(&quot;_&quot;, &quot;.&quot;)}-0/boost_${BOOST_VERSION}.tar.gz&quot;
 src(srcUrl)
 onlyIfNewer(true)
 overwrite(false)
 dest(new File(downloadsDir, &quot;boost_${BOOST_VERSION}.tar.gz&quot;))
}</code></pre>
<p><strong>After</strong></p>
<pre><code class="language-js">task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
def transformedVersion = BOOST_VERSION.replace(&quot;_&quot;, &quot;.&quot;)
 def srcUrl = REACT_NATIVE_TARGET_VERSION &gt;= 69
   ? &quot;https://sourceforge.net/projects/boost/files/boost/${transformedVersion}/boost_${BOOST_VERSION}.tar.gz&quot;
   : &quot;https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace(&quot;_&quot;, &quot;.&quot;)}-0/boost_${BOOST_VERSION}.tar.gz&quot;
 src(srcUrl)
 onlyIfNewer(true)
 overwrite(false)
 dest(new File(downloadsDir, &quot;boost_${BOOST_VERSION}.tar.gz&quot;))

}</code></pre>
</li>
<li><p><a href="https://www.npmjs.com/package/patch-package">patch-package</a> 설치 및 아래 cli 실행</p>
<pre><code class="language-shell">$ yarn patch-package expo-modules-core</code></pre>
</li>
<li><p>node_modules 삭제 후, 재설치</p>
<pre><code class="language-shell"> $ rm -rf node_modules &amp;&amp; yarn</code></pre>
</li>
<li><p>재실행</p>
<pre><code class="language-shell"> $ react-native start --reset-cache</code></pre>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>