<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dohy-9443.log</title>
        <link>https://velog.io/</link>
        <description>안녕하세요.</description>
        <lastBuildDate>Mon, 12 May 2025 12:53:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dohy-9443.log</title>
            <url>https://velog.velcdn.com/images/dohy-9443/profile/a0c88872-5b6c-402c-ba1c-a3b3e73c7407/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dohy-9443.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dohy-9443" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[소켓(Socket)]]></title>
            <link>https://velog.io/@dohy-9443/%EC%86%8C%EC%BC%93Socket</link>
            <guid>https://velog.io/@dohy-9443/%EC%86%8C%EC%BC%93Socket</guid>
            <pubDate>Mon, 12 May 2025 12:53:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>컴퓨터끼리 “실시간으로 대화”하려면? 바로 소켓(Socket)이 필요</p>
</blockquote>
<hr>
<h2 id="🧭-목차">🧭 목차</h2>
<ol>
<li>소켓이란?</li>
<li>왜 소켓을 사용할까?</li>
<li>WebSocket vs Socket.IO 차이</li>
<li>소켓 관련 주요 패키지</li>
<li>Flutter에서 socket.io 사용하는 법</li>
<li>소켓으로 만들 수 있는 기능 예시</li>
<li>총정리</li>
</ol>
<hr>
<h2 id="1-소켓이란">1. 소켓이란?</h2>
<p><strong>소켓(Socket)</strong>은 두 컴퓨터나 프로그램이 <strong>실시간으로 데이터를 주고받을 수 있게 해주는 통로</strong></p>
<p>HTTP는 요청-응답 방식이고,<br>소켓은 전화처럼 <strong>계속 연결된 상태</strong>로 자유롭게 말할 수 있음</p>
<p>📞 비유하자면:  </p>
<ul>
<li><strong>HTTP</strong> = 편지  </li>
<li><strong>Socket</strong> = 전화</li>
</ul>
<hr>
<h2 id="2-왜-소켓을-사용할까">2. 왜 소켓을 사용할까?</h2>
<h3 id="✅-실시간-반응이-필요한-기능들">✅ 실시간 반응이 필요한 기능들</h3>
<ul>
<li>✅ 채팅 (보내자마자 바로 도착해야 하니까)</li>
<li>✅ 실시간 게임 (위치, HP, 점수 등 즉시 공유)</li>
<li>✅ 실시간 알림 (서버에서 사용자에게 즉시 메시지)</li>
</ul>
<h3 id="❌-http로-하면">❌ HTTP로 하면?</h3>
<ul>
<li>계속 &quot;새 소식 있나요?&quot; 하고 물어봐야 함</li>
<li>연결을 매번 새로 해야 해서 느리고 비효율적</li>
</ul>
<hr>
<h2 id="3-websocket-vs-socketio-차이">3. WebSocket vs socket.io 차이</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>WebSocket</th>
<th>socket.io (소켓 IO)</th>
</tr>
</thead>
<tbody><tr>
<td>표준 여부</td>
<td>✅ 웹 표준 (브라우저 지원)</td>
<td>❌ 라이브러리 기반 (표준 아님)</td>
</tr>
<tr>
<td>기능</td>
<td>연결만 제공</td>
<td>재연결, 이벤트, 채널 기능까지 포함</td>
</tr>
<tr>
<td>난이도</td>
<td>직접 구현 필요 (코드 많음)</td>
<td>코드 간단, 빠른 개발 가능</td>
</tr>
<tr>
<td>사용 사례</td>
<td>게임, 실시간 피드</td>
<td>대부분의 실시간 앱에서 널리 사용됨</td>
</tr>
</tbody></table>
<p>📌 <strong>요약</strong>:  </p>
<blockquote>
<p>WebSocket은 “전화선”,<br>Socket.IO는 “전화선 + 자동다이얼 + 단축버튼 + 대화방 기능 다 있는 스마트폰”</p>
</blockquote>
<hr>
<h2 id="4-소켓-관련-주요-패키지-정리">4. 소켓 관련 주요 패키지 정리</h2>
<table>
<thead>
<tr>
<th>플랫폼</th>
<th>주요 패키지</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Node.js</td>
<td><code>socket.io</code>, <code>ws</code></td>
<td>실시간 서버 구현용 대표 패키지</td>
</tr>
<tr>
<td>Flutter</td>
<td><code>socket_io_client</code>, <code>web_socket_channel</code></td>
<td>앱에서 실시간 통신 구현</td>
</tr>
<tr>
<td>Python</td>
<td><code>websockets</code>, <code>aiohttp</code></td>
<td>간단한 실습용 또는 서버용 소켓</td>
</tr>
<tr>
<td>Java</td>
<td><code>javax.websocket</code>, <code>Socket</code></td>
<td>기본 소켓 또는 웹소켓 구현용</td>
</tr>
</tbody></table>
<hr>
<h2 id="5-flutter에서-socketio-사용법">5. Flutter에서 socket.io 사용법</h2>
<h3 id="1-의존성-추가">1. 의존성 추가</h3>
<pre><code class="language-yaml">dependencies:
  socket_io_client: ^3.1.2</code></pre>
<h3 id="2-소켓-연결하기">2. 소켓 연결하기</h3>
<pre><code class="language-dart">import &#39;package:socket_io_client/socket_io_client.dart&#39; as IO;

final socket = IO.io(&#39;http://localhost:3000&#39;, &lt;String, dynamic&gt;{
  &#39;transports&#39;: [&#39;websocket&#39;],
  &#39;autoConnect&#39;: false,
});

socket.connect();</code></pre>
<h3 id="3-메시지-보내기">3. 메시지 보내기</h3>
<pre><code class="language-dart">socket.emit(&#39;message&#39;, &#39;안녕하세요!&#39;);</code></pre>
<h3 id="4-메시지-받기">4. 메시지 받기</h3>
<pre><code class="language-dart">socket.on(&#39;message&#39;, (data) {
  print(&#39;서버로부터 받은 메시지: $data&#39;);
});</code></pre>
<h3 id="5-연결-끊기">5. 연결 끊기</h3>
<pre><code class="language-dart">socket.disconnect();</code></pre>
<h2 id="6-소켓으로-만들-수-있는-기능-예시">6. 소켓으로 만들 수 있는 기능 예시</h2>
<table>
<thead>
<tr>
<th>기능 이름</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>💬 채팅 기능</td>
<td>실시간 대화 구현</td>
</tr>
<tr>
<td>🔔 알림 시스템</td>
<td>서버에서 클라이언트에게 즉시 알림 전송</td>
</tr>
<tr>
<td>🎮 실시간 게임</td>
<td>위치, 점수 등 빠르게 동기화</td>
</tr>
<tr>
<td>📊 라이브 데이터</td>
<td>차트나 주가 변동 실시간 반영</td>
</tr>
<tr>
<td>📝 협업툴 (예: 문서 편집)</td>
<td>Google Docs처럼 실시간 공동 작업 가능</td>
</tr>
</tbody></table>
<h2 id="7-총정리">7. 총정리</h2>
<table>
<thead>
<tr>
<th>핵심 요소</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>소켓(Socket)</td>
<td>컴퓨터끼리 실시간으로 대화하게 해주는 기술</td>
</tr>
<tr>
<td>WebSocket</td>
<td>브라우저가 지원하는 표준 실시간 통신 방식</td>
</tr>
<tr>
<td>Socket.io</td>
<td>WebSocket을 더 쓰기 쉽게 만든 라이브러리</td>
</tr>
<tr>
<td>Flutter</td>
<td>socket_io_client로 간편하게 사용 가능</td>
</tr>
<tr>
<td>사용 사례</td>
<td>채팅, 게임, 알림, 라이브 데이터 등</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter에서의 비동기 처리(Async/Await) 이해하기]]></title>
            <link>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%ACAsyncAwait-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C%EC%9D%98-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%ACAsyncAwait-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 23 Feb 2025 14:41:04 GMT</pubDate>
            <description><![CDATA[<h2 id="1-비동기-처리가-뭐지">1. 비동기 처리가 뭐지?</h2>
<p>비동기 처리(Asynchronous Processing)란 <strong>작업이 완료될 때까지 기다리지 않고, 다음 작업을 진행하는 방식</strong>을 의미합니다.</p>
<p>쉽게 말해, 우리가 음식을 주문하고 조리가 끝날 때까지 가만히 기다리는 것이 아니라, <strong>다른 일을 하다가 음식이 나오면 다시 돌아와서 먹는 것과 같은 원리</strong>입니다.</p>
<p>Flutter에서는 네트워크 요청, 파일 읽기, 데이터베이스 조회 등 시간이 오래 걸리는 작업을 효율적으로 처리하기 위해 비동기 처리를 사용합니다.</p>
<hr>
<h2 id="2-그럼-동기와-비동기의-차이는">2. 그럼 동기와 비동기의 차이는?</h2>
<table>
<thead>
<tr>
<th>처리 방식</th>
<th>설명</th>
<th>예시</th>
</tr>
</thead>
<tbody><tr>
<td><strong>동기 처리(Synchronous)</strong></td>
<td>작업이 완료될 때까지 기다린 후 다음 작업을 실행</td>
<td>음식을 주문하고 조리가 끝날 때까지 기다리는 것</td>
</tr>
<tr>
<td><strong>비동기 처리(Asynchronous)</strong></td>
<td>작업이 끝날 때까지 기다리지 않고, 다음 작업을 진행</td>
<td>음식을 주문한 후 다른 일을 하다가 완료되면 먹는 것</td>
</tr>
</tbody></table>
<h3 id="❌-동기-처리-코드-예제">❌ <strong>동기 처리 코드 예제</strong></h3>
<pre><code class="language-dart">void main() {
  print(&quot;1. 데이터를 요청합니다.&quot;);
  fetchData(); // 시간이 오래 걸리는 작업
  print(&quot;3. 데이터를 화면에 출력합니다.&quot;);
}

void fetchData() {
  for (int i = 0; i &lt; 1000000000; i++) {} // 시간이 걸리는 연산
  print(&quot;2. 데이터 요청 완료!&quot;);
}</code></pre>
<p><strong>출력 결과:</strong></p>
<pre><code>1. 데이터를 요청합니다.
2. 데이터 요청 완료!
3. 데이터를 화면에 출력합니다.</code></pre><p>위와 같이 <strong>fetchData()가 완료될 때까지 다음 코드가 실행되지 않습니다.</strong></p>
<h3 id="✅-비동기-처리-코드-예제-asyncawait-사용">✅ <strong>비동기 처리 코드 예제 (async/await 사용)</strong></h3>
<pre><code class="language-dart">void main() async {
  print(&quot;1. 데이터를 요청합니다.&quot;);
  await fetchData(); // 비동기 처리
  print(&quot;3. 데이터를 화면에 출력합니다.&quot;);
}

Future&lt;void&gt; fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  print(&quot;2. 데이터 요청 완료!&quot;);
}</code></pre>
<p><strong>출력 결과:</strong></p>
<pre><code>1. 데이터를 요청합니다.
(2초 후)
2. 데이터 요청 완료!
3. 데이터를 화면에 출력합니다.</code></pre><p>비동기 코드를 사용하면 <strong>작업이 끝날 때까지 기다리지 않고 다른 작업을 수행할 수 있습니다.</strong></p>
<hr>
<h2 id="3-비동기-처리-코드는-어떻게-작성해야-돼">3. 비동기 처리 코드는 어떻게 작성해야 돼?</h2>
<p>Flutter에서 비동기 처리는 주로 <code>Future</code>, <code>async/await</code>, 그리고 <code>then</code>을 사용하여 구현합니다.</p>
<h3 id="✅-future를-사용한-비동기-코드"><strong>✅ Future를 사용한 비동기 코드</strong></h3>
<pre><code class="language-dart">Future&lt;String&gt; fetchData() {
  return Future.delayed(Duration(seconds: 2), () =&gt; &quot;데이터 로드 완료&quot;);
}</code></pre>
<p><strong>📌 Future는 비동기 작업의 결과를 나타내는 객체입니다.</strong> </p>
<ul>
<li>Future는 한 번만 값을 반환하며, 해당 작업이 완료되면 값을 받을 수 있습니다.</li>
<li><code>then</code> 또는 <code>await</code>을 사용하여 결과를 처리할 수 있습니다.</li>
</ul>
<h3 id="✅-asyncawait을-사용한-비동기-코드"><strong>✅ async/await을 사용한 비동기 코드</strong></h3>
<pre><code class="language-dart">void main() async {
  print(&quot;데이터를 불러오는 중...&quot;);
  String data = await fetchData();
  print(data);
}</code></pre>
<p><strong>📌 async/await은 Future를 더 직관적으로 사용할 수 있도록 도와줍니다.</strong></p>
<ul>
<li><code>async</code> 키워드를 함수에 추가하면 비동기 함수가 됩니다.</li>
<li><code>await</code> 키워드를 사용하면 Future가 완료될 때까지 기다렸다가 실행됩니다.</li>
<li><code>try-catch</code> 블록을 사용하여 오류를 쉽게 처리할 수 있습니다.</li>
</ul>
<h3 id="✅-then을-사용한-비동기-코드"><strong>✅ then을 사용한 비동기 코드</strong></h3>
<pre><code class="language-dart">void main() {
  print(&quot;데이터를 불러오는 중...&quot;);
  fetchData().then((data) {
    print(data);
  });
}</code></pre>
<p><strong>📌 then() 메서드는 Future의 결과를 받아 처리할 때 사용됩니다.</strong></p>
<ul>
<li><code>await</code>을 사용할 수 없는 경우(예: 기존의 콜백 방식) 유용하게 사용됩니다.</li>
<li>하지만 코드가 길어질수록 <code>then</code> 체인이 복잡해지고 가독성이 떨어질 수 있습니다.</li>
</ul>
<h3 id="future-vs-asyncawait-vs-then-비교"><strong>Future vs async/await vs then 비교</strong></h3>
<table>
<thead>
<tr>
<th>방식</th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Future</strong></td>
<td>비동기 작업을 실행하고 <code>then</code>을 사용해 결과 처리 가능</td>
<td>여러 개의 <code>then</code> 체인이 생기면 가독성이 떨어짐</td>
</tr>
<tr>
<td><strong>async/await</strong></td>
<td>동기 코드처럼 직관적으로 작성 가능, 가독성이 뛰어남</td>
<td><code>await</code>은 async 함수 내에서만 사용 가능</td>
</tr>
<tr>
<td><strong>then</strong></td>
<td>콜백 스타일로 비동기 처리 가능, 간단한 작업에 적합</td>
<td>중첩되면 코드가 복잡해짐 (콜백 지옥 발생 가능)</td>
</tr>
</tbody></table>
<hr>
<h2 id="4-그렇다면-언제-사용해야-될까">4. 그렇다면 언제 사용해야 될까?</h2>
<p>비동기 처리는 다음과 같은 경우에 사용됩니다.</p>
<ul>
<li><strong>네트워크 요청 (API 호출)</strong>: 데이터를 서버에서 가져올 때</li>
<li><strong>파일 입출력 (File I/O)</strong>: 기기 내부에서 파일을 읽고 쓸 때</li>
<li><strong>데이터베이스 조회</strong>: SQLite나 Firebase와 같은 데이터베이스에서 데이터를 가져올 때</li>
<li><strong>애니메이션 처리</strong>: 일정 시간이 걸리는 애니메이션을 실행할 때</li>
</ul>
<blockquote>
<p>비동기 처리는 <strong>사용자의 경험을 부드럽게 만들어주며</strong>, UI가 멈추지 않도록 도와줍니다.</p>
</blockquote>
<hr>
<h2 id="5-모든-코드를-다-비동기로-작성하면-안돼">5. 모든 코드를 다 비동기로 작성하면 안돼?</h2>
<p>비동기 처리는 매우 강력하지만, 모든 코드를 비동기로 작성하면 오히려 <strong>불필요한 복잡성이 증가</strong>할 수 있습니다.</p>
<p><strong>📌 주의해야 할 점</strong></p>
<ul>
<li><strong>필요한 경우에만 비동기 처리 사용</strong> (예: 네트워크 요청, 데이터 로드 등)</li>
<li><strong>UI 관련 작업은 동기적으로 유지</strong> (예: 버튼 클릭 이벤트, 간단한 연산 등)</li>
<li><strong>Future를 남발하지 않기</strong> (불필요한 Future 사용은 코드 가독성을 떨어뜨림)</li>
</ul>
<blockquote>
<p>따라서, <strong>비동기 처리는 필요할 때만 신중하게 사용해야 합니다.</strong></p>
</blockquote>
<hr>
<h2 id="6-추가적으로-알아두면-좋은-개념">6. 추가적으로 알아두면 좋은 개념</h2>
<p>비동기 처리와 관련하여 알아두면 좋은 개념들은 다음과 같습니다.</p>
<h3 id="✅-stream-지속적인-데이터-흐름-처리"><strong>✅ Stream: 지속적인 데이터 흐름 처리</strong></h3>
<p><code>Future</code>는 한 번의 작업을 처리하지만, <code>Stream</code>은 <strong>여러 개의 데이터를 순차적으로 처리</strong>할 수 있습니다.</p>
<pre><code class="language-dart">Stream&lt;int&gt; countStream() async* {
  for (int i = 0; i &lt; 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}</code></pre>
<h3 id="✅-isolate-멀티스레딩-활용"><strong>✅ Isolate: 멀티스레딩 활용</strong></h3>
<p>Flutter는 단일 스레드에서 실행되지만, <strong>Isolate</strong>를 사용하면 별도의 스레드에서 작업을 실행할 수 있습니다.</p>
<pre><code class="language-dart">import &#39;dart:isolate&#39;;

void backgroundTask(SendPort sendPort) {
  sendPort.send(&quot;백그라운드 작업 완료!&quot;);
}</code></pre>
<hr>
<h2 id="7-마무리">7. 마무리</h2>
<p>비동기 처리는 <strong>Flutter 개발에서 필수적인 개념</strong>으로, 앱의 성능과 사용자 경험을 향상시키는 데 중요한 역할을 합니다.</p>
<blockquote>
<ul>
<li><strong>동기 처리와 비동기 처리의 차이를 이해하고, 필요할 때 적절히 사용해야 하며,</strong></li>
</ul>
</blockquote>
<ul>
<li><strong>async/await, Future, Stream 등의 개념을 숙지하여 다양한 상황에서 비동기 처리를 활용할 수 있어야 합니다.</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter에서의 의존성 주입(Dependency Injection) 이해하기]]></title>
            <link>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C%EC%9D%98-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85Dependency-Injection-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C%EC%9D%98-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85Dependency-Injection-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 23 Feb 2025 14:30:30 GMT</pubDate>
            <description><![CDATA[<h2 id="1-의존성-주입이-뭘까">1. 의존성 주입이 뭘까?</h2>
<p>의존성 주입(Dependency Injection, DI)은 <strong>객체 간의 의존성을 외부에서 주입하는 설계 패턴</strong>입니다. </p>
<p>쉽게 말해, <strong>클래스가 직접 다른 객체를 생성하는 것이 아니라, 외부에서 제공된 객체를 사용하는 것</strong>을 의미합니다. 이를 통해 코드의 유지보수성을 높이고, 결합도를 낮출 수 있습니다.</p>
<h3 id="📌-예를-들어보자">📌 예를 들어보자!</h3>
<p>우리가 식당에서 음식을 먹을 때, 주방장이 모든 재료를 직접 구하고, 요리하고, 서빙까지 한다면 엄청 비효율적입니다. 
하지만 주방장은 <strong>재료 공급업체(외부)</strong>에서 재료를 받아서 요리를 만들기만 하면 됩니다. 
이와 마찬가지로, 코드에서도 클래스가 직접 필요한 객체를 만들지 않고, <strong>외부에서 주입받으면 더 효율적으로 관리할 수 있습니다.</strong></p>
<hr>
<h2 id="2-그렇다면-의존성-주입을-왜-사용해야-하는-걸까">2. 그렇다면 의존성 주입을 왜 사용해야 하는 걸까?</h2>
<p>의존성 주입을 사용하면 다음과 같은 장점이 있습니다.</p>
<ul>
<li><strong>유지보수성 향상</strong>: 객체 간의 결합도를 낮추어 코드 수정이 용이해집니다.</li>
<li><strong>테스트 용이성</strong>: Mock 객체를 활용하여 단위 테스트를 쉽게 수행할 수 있습니다.</li>
<li><strong>코드 재사용성 증가</strong>: 특정 기능을 여러 곳에서 쉽게 재사용할 수 있습니다.</li>
</ul>
<hr>
<h2 id="3-만약-의존성-주입을-사용하지-않게-되면-앱에-어떠한-영향을-끼칠까">3. 만약 의존성 주입을 사용하지 않게 되면 앱에 어떠한 영향을 끼칠까?</h2>
<p>의존성 주입 없이 코드를 작성하면 다음과 같은 문제가 발생할 수 있습니다.</p>
<h3 id="❌-의존성-주입-없이-작성한-코드-예제">❌ <strong>의존성 주입 없이 작성한 코드 예제</strong></h3>
<pre><code class="language-dart">class ApiService {
  void fetchData() {
    print(&quot;Fetching data from API...&quot;);
  }
}

class HomeScreen {
  final ApiService apiService = ApiService(); // 직접 객체 생성
}</code></pre>
<p><strong>📌 문제점</strong></p>
<ul>
<li><code>HomeScreen</code>이 <code>ApiService</code>를 직접 생성하므로 두 클래스 간의 <strong>강한 결합(Tightly Coupled)</strong> 이 발생합니다.</li>
<li><code>ApiService</code>를 변경하면 <code>HomeScreen</code>도 함께 수정해야 하는 상황이 발생할 수 있습니다.</li>
<li>객체의 <strong>테스트가 어려워지고</strong>, 코드의 유지보수성이 떨어집니다.</li>
</ul>
<blockquote>
<h3 id="🔍-강한-결합tightly-coupled이란">🔍 <strong>강한 결합(Tightly Coupled)이란?</strong></h3>
<p>강한 결합이란, <strong>한 클래스가 다른 클래스에 강하게 의존하고 있는 상태</strong>를 말합니다. 즉, 한 클래스가 변경되면, 이를 사용하는 다른 클래스도 변경해야 하는 상황을 의미합니다.</p>
</blockquote>
<h3 id="💡-그럼-강한-결합이-있으면-약한-결합도-있을까">💡 <strong>그럼 강한 결합이 있으면 약한 결합도 있을까?</strong></h3>
<ul>
<li><strong>강한 결합(Tightly Coupled)</strong>: 클래스 간의 의존성이 높아 변경이 어렵다.</li>
<li><strong>약한 결합(Loosely Coupled)</strong>: 클래스 간의 의존성이 낮아 변경이 용이하다.</li>
</ul>
<blockquote>
<p>의존성 주입을 활용하면 <strong>강한 결합을 약한 결합으로 바꿀 수 있어 코드의 유지보수성과 확장성이 높아집니다.</strong></p>
</blockquote>
<hr>
<h2 id="4-의존성-주입을-하는-방법">4. 의존성 주입을 하는 방법</h2>
<p>의존성 주입을 구현하는 다양한 방법이 있습니다.</p>
<h3 id="✅-1-생성자를-통한-주입-constructor-injection">✅ <strong>1) 생성자를 통한 주입 (Constructor Injection)</strong></h3>
<pre><code class="language-dart">class ApiService {
  void fetchData() {
    print(&quot;Fetching data from API...&quot;);
  }
}

class HomeScreen {
  final ApiService apiService;

  HomeScreen(this.apiService); // 외부에서 객체를 주입
}</code></pre>
<p>이제 <code>HomeScreen</code>이 <code>ApiService</code>를 직접 생성하는 것이 아니라, <strong>외부에서 주입받도록 변경</strong>되었습니다. 이를 통해 <strong>클래스 간의 결합도를 낮추고, 코드의 유연성과 테스트 용이성이 높아집니다</strong>.</p>
<h3 id="✅-2-provider를-활용한-주입">✅ <strong>2) Provider를 활용한 주입</strong></h3>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;
import &#39;package:provider/provider.dart&#39;;

class ApiService {
  void fetchData() {
    print(&quot;Fetching data from API...&quot;);
  }
}

void main() {
  runApp(
    MultiProvider(
      providers: [
        Provider(create: (_) =&gt; ApiService()), // ApiService 주입
      ],
      child: MyApp(),
    ),
  );
}</code></pre>
<h3 id="✅-3-getit을-활용한-service-locator-패턴">✅ <strong>3) GetIt을 활용한 Service Locator 패턴</strong></h3>
<pre><code class="language-dart">import &#39;package:get_it/get_it.dart&#39;;

final getIt = GetIt.instance;

void setupLocator() {
  getIt.registerLazySingleton&lt;ApiService&gt;(() =&gt; ApiService());
}</code></pre>
<h3 id="✅-4-injectable을-활용한-자동-의존성-주입">✅ <strong>4) Injectable을 활용한 자동 의존성 주입</strong></h3>
<pre><code class="language-dart">import &#39;package:get_it/get_it.dart&#39;;
import &#39;package:injectable/injectable.dart&#39;;

final getIt = GetIt.instance;

@InjectableInit()
void configureDependencies() =&gt; $initGetIt(getIt);</code></pre>
<h3 id="✅-5-riverpod을-활용한-의존성-주입">✅ <strong>5) Riverpod을 활용한 의존성 주입</strong></h3>
<pre><code class="language-dart">import &#39;package:flutter_riverpod/flutter_riverpod.dart&#39;;

final apiServiceProvider = Provider((ref) =&gt; ApiService());</code></pre>
<hr>
<h2 id="5-그렇다면-의존성-주입에-관련된-패키지는-어떠한게-있을까">5. 그렇다면 의존성 주입에 관련된 패키지는 어떠한게 있을까?</h2>
<ul>
<li><strong>Provider</strong>: Flutter에서 권장하는 상태 관리 및 의존성 주입 패턴</li>
<li><strong>GetIt</strong>: Service Locator 패턴을 활용한 전역적 객체 관리</li>
<li><strong>Injectable</strong>: GetIt 기반으로 DI 자동화를 지원하는 패키지</li>
<li><strong>Riverpod</strong>: Provider의 개선 버전으로 더 안전하고 강력한 기능 제공</li>
</ul>
<hr>
<h2 id="6-의존성-주입을-하기-편한-상태관리-패키지는-어떠한게-있을까">6. 의존성 주입을 하기 편한 상태관리 패키지는 어떠한게 있을까?</h2>
<ul>
<li><strong>Provider</strong>: 기본적인 상태 관리 및 DI를 함께 지원</li>
<li><strong>Riverpod</strong>: Provider보다 더 강력한 기능을 제공</li>
<li><strong>GetX</strong>: 간단하고 직관적인 상태 관리 및 DI 지원</li>
<li><strong>Bloc</strong>: 대규모 프로젝트에 적합한 상태 관리 방식</li>
</ul>
<hr>
<h2 id="7-이런-것들을-응용해서-어떠한-이점이-있을까">7. 이런 것들을 응용해서 어떠한 이점이 있을까?</h2>
<ul>
<li><strong>코드 유지보수성이 향상됨</strong></li>
<li><strong>테스트가 쉬워짐 (Mock 객체 사용 가능)</strong></li>
<li><strong>UI와 비즈니스 로직을 분리하여 가독성 증가</strong></li>
<li><strong>재사용 가능한 코드 작성 가능</strong></li>
</ul>
<hr>
<h2 id="8-provider-riverpod-getit-injectable-간단-비교">8. Provider, Riverpod, GetIt, Injectable 간단 비교</h2>
<table>
<thead>
<tr>
<th>방식</th>
<th>장점</th>
<th>단점</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Provider</strong></td>
<td>공식 권장, 간단하고 직관적</td>
<td>규모가 커질수록 관리 어려움</td>
</tr>
<tr>
<td><strong>Riverpod</strong></td>
<td>Provider 개선판, 안전하고 간결함</td>
<td>러닝 커브가 있음</td>
</tr>
<tr>
<td><strong>GetIt</strong></td>
<td>전역적 객체 관리 가능, 사용이 간편</td>
<td>Service Locator 패턴에 대한 이해 필요</td>
</tr>
<tr>
<td><strong>Injectable</strong></td>
<td>GetIt과 함께 사용 가능, DI 자동화</td>
<td>설정이 필요함</td>
</tr>
</tbody></table>
<hr>
<h2 id="9-마무리">9. 마무리</h2>
<p>Flutter에서 의존성 주입(Dependency Injection)은 코드의 유지보수성을 높이고, 확장성을 증가시키는 중요한 개념입니다.</p>
<blockquote>
<p>코드의 결합도를 줄여 유지보수를 쉽게 하고,
테스트가 용이해지며,
재사용 가능한 구조를 만들 수 있습니다.</p>
</blockquote>
<p>의존성 주입을 구현하는 방법에는 Provider, GetIt, Riverpod, Injectable과 같은 다양한 패키지가 있으며, 프로젝트의 규모와 필요에 따라 적절한 방법을 선택하는 것이 중요합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter에서 API 통신 및 데이터 관리]]></title>
            <link>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C-API-%ED%86%B5%EC%8B%A0-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C-API-%ED%86%B5%EC%8B%A0-%EB%B0%8F-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Wed, 15 Jan 2025 12:57:10 GMT</pubDate>
            <description><![CDATA[<h2 id="1️⃣-flutter에서-api-통신하는-방법">1️⃣ Flutter에서 API 통신하는 방법</h2>
<p>Flutter에서는 외부 서버와의 통신을 위해 다양한 패키지를 사용할 수 있으며, 사용 목적에 맞게 선택할 수 있습니다.</p>
<p>API 통신은 서버로부터 데이터를 받아오거나 서버로 데이터를 전송하기 위해 사용됩니다. Flutter에서는 주로 다음과 같은 패키지를 사용합니다.</p>
<h3 id="1-http-패키지">1. <code>http</code> 패키지</h3>
<ul>
<li>Flutter에서 가장 기본적으로 사용하는 HTTP 클라이언트 패키지.</li>
<li>간단한 요청(GET, POST 등)에 적합.</li>
</ul>
<pre><code class="language-dart">import &#39;package:http/http.dart&#39; as http;
import &#39;dart:convert&#39;;

Future&lt;void&gt; fetchData() async {
  final response = await http.get(Uri.parse(&#39;https://jsonplaceholder.typicode.com/posts/1&#39;));

  if (response.statusCode == 200) {
    final data = jsonDecode(response.body);
    print(data);
  } else {
    throw Exception(&#39;Failed to load data&#39;);
  }
}</code></pre>
<h3 id="2-dio-패키지">2. <code>dio</code> 패키지</h3>
<ul>
<li>고급 기능을 지원하는 강력한 HTTP 클라이언트.</li>
<li><strong>인터셉터</strong>, <strong>리퀘스트 취소</strong>, <strong>타임아웃 설정</strong>, <strong>파일 업로드/다운로드</strong> 지원.</li>
</ul>
<pre><code class="language-dart">import &#39;package:dio/dio.dart&#39;;

final dio = Dio();

Future&lt;void&gt; fetchData() async {
  try {
    final response = await dio.get(&#39;https://jsonplaceholder.typicode.com/posts/1&#39;);
    print(response.data);
  } catch (e) {
    print(&#39;Error: $e&#39;);
  }
}</code></pre>
<h3 id="3-getx의-api-통신-기능">3. <code>GetX</code>의 API 통신 기능</h3>
<ul>
<li>상태 관리, 라우팅, 의존성 주입과 함께 API 통신 기능도 제공.</li>
<li>간단한 앱에서는 <strong>빠르고 효율적</strong>이지만, 복잡한 네트워크 로직에는 한계가 있음.</li>
</ul>
<h3 id="4-graphql_flutter-패키지">4. <code>graphql_flutter</code> 패키지</h3>
<ul>
<li><strong>GraphQL API</strong>와 통신을 위한 패키지.</li>
<li>캐싱 및 실시간 데이터 업데이트 지원.</li>
</ul>
<pre><code class="language-dart">import &#39;package:graphql_flutter/graphql_flutter.dart&#39;;

final HttpLink httpLink = HttpLink(&#39;https://example.com/graphql&#39;);

ValueNotifier&lt;GraphQLClient&gt; client = ValueNotifier(
  GraphQLClient(
    link: httpLink,
    cache: GraphQLCache(store: HiveStore()),
  ),
);</code></pre>
<p><strong>✅ 장점:</strong></p>
<ul>
<li>GraphQL API와의 통합이 용이함.</li>
<li>캐싱 및 실시간 데이터 업데이트 가능.</li>
</ul>
<p><strong>❗ 단점:</strong></p>
<ul>
<li>REST API보다 설정이 복잡함.</li>
<li>프로젝트에 GraphQL 도입이 필요함.</li>
</ul>
<h3 id="5-chopper-패키지">5. <code>chopper</code> 패키지</h3>
<ul>
<li>Retrofit 스타일의 HTTP 클라이언트.</li>
<li><strong>인터셉터</strong>, <strong>코드 생성</strong>을 통한 API 요청 간소화.</li>
</ul>
<pre><code class="language-dart">import &#39;package:chopper/chopper.dart&#39;;

part &#39;post_service.chopper.dart&#39;;

@ChopperApi()
abstract class PostService extends ChopperService {
  @Get(path: &#39;/posts/{id}&#39;)
  Future&lt;Response&gt; getPost(@Path(&#39;id&#39;) int id);

  static PostService create() {
    final client = ChopperClient(
      baseUrl: &#39;https://jsonplaceholder.typicode.com&#39;,
      services: [
        _$PostService(),
      ],
      converter: JsonConverter(),
    );

    return _$PostService(client);
  }
}</code></pre>
<p><strong>✅ 장점:</strong></p>
<ul>
<li>REST API 요청을 간결하게 작성 가능.</li>
<li>코드 자동 생성을 통한 유지보수 편리.</li>
</ul>
<p><strong>❗ 단점:</strong></p>
<ul>
<li>추가 설정이 필요하며 학습 곡선이 존재함.</li>
<li><code>build_runner</code>를 통한 코드 생성을 필요로 함.</li>
</ul>
<h3 id="6-retrofit-패키지">6. <code>retrofit</code> 패키지</h3>
<ul>
<li><strong>Dart Retrofit</strong> 스타일의 HTTP 클라이언트.</li>
<li><code>dio</code>와 결합해 더욱 강력한 기능 제공.</li>
</ul>
<pre><code class="language-dart">import &#39;package:retrofit/retrofit.dart&#39;;
import &#39;package:dio/dio.dart&#39;;

part &#39;client.g.dart&#39;;

@RestApi(baseUrl: &quot;https://jsonplaceholder.typicode.com&quot;)
abstract class RestClient {
  factory RestClient(Dio dio, {String baseUrl}) = _RestClient;

  @GET(&quot;/posts/{id}&quot;)
  Future&lt;HttpResponse&gt; getPost(@Path(&quot;id&quot;) int id);
}</code></pre>
<p><strong>✅ 장점:</strong></p>
<ul>
<li><code>dio</code>와 결합해 인터셉터, 에러 핸들링 등 강력한 기능 지원.</li>
<li>Retrofit 스타일의 직관적인 API 작성.</li>
</ul>
<p><strong>❗ 단점:</strong></p>
<ul>
<li>코드 생성 도구(<code>build_runner</code>) 사용 필요.</li>
<li>추가적인 설정이 필요함.</li>
<li>상태 관리, 라우팅, 의존성 주입과 함께 API 통신 기능도 제공.</li>
<li>간단한 앱에서는 <strong>빠르고 효율적</strong>이지만, 복잡한 네트워크 로직에는 한계가 있음.</li>
</ul>
<pre><code class="language-dart">import &#39;package:get/get.dart&#39;;

Future&lt;void&gt; fetchData() async {
  final response = await GetConnect().get(&#39;https://jsonplaceholder.typicode.com/posts/1&#39;);
  if (response.statusCode == 200) {
    print(response.body);
  }
}</code></pre>
<hr>
<h2 id="2️⃣-flutter-api-통신-패키지-비교-및-선택-이유">2️⃣ Flutter API 통신 패키지 비교 및 선택 이유</h2>
<p>다양한 API 통신 패키지가 존재하지만, 프로젝트의 규모와 필요에 따라 적절한 패키지를 선택해야 합니다. 다음은 주요 Flutter API 통신 패키지의 비교입니다.</p>
<table>
<thead>
<tr>
<th>패키지</th>
<th>장점</th>
<th>단점</th>
<th>주 사용 이유</th>
</tr>
</thead>
<tbody><tr>
<td><strong>http</strong></td>
<td>간단한 사용법, 가벼움</td>
<td>인터셉터, 요청 취소, 파일 업로드 등의 고급 기능 부족</td>
<td>작은 프로젝트나 간단한 요청 처리에 적합</td>
</tr>
<tr>
<td><strong>dio</strong></td>
<td>고급 기능 지원(인터셉터, 요청 취소, 파일 업로드), 에러 핸들링 용이</td>
<td>패키지 크기가 크고 설정이 복잡할 수 있음</td>
<td>대규모 프로젝트나 복잡한 API 통신에서 사용</td>
</tr>
<tr>
<td><strong>GetX</strong></td>
<td>상태 관리, 라우팅, 의존성 주입과 통합, 빠르고 간단함</td>
<td>네트워크 로직 구조화 및 확장성 부족, 고급 네트워크 기능 부족</td>
<td>빠른 개발이 필요한 소규모 프로젝트</td>
</tr>
<tr>
<td><strong>graphql_flutter</strong></td>
<td>GraphQL API 통합, 실시간 데이터 처리 및 캐싱 지원</td>
<td>REST API보다 설정이 복잡, GraphQL 도입 필요</td>
<td>GraphQL 기반 서버와 통신 시 사용</td>
</tr>
<tr>
<td><strong>chopper</strong></td>
<td>Retrofit 스타일, 코드 자동 생성, 유지보수 용이</td>
<td>추가 설정 및 코드 생성 필요, 학습 곡선 존재</td>
<td>REST API와의 직관적인 통신이 필요한 경우</td>
</tr>
<tr>
<td><strong>retrofit</strong></td>
<td><code>dio</code>와 결합해 강력한 기능 제공, 직관적인 API 작성</td>
<td><code>build_runner</code> 필요, 추가 설정 필요</td>
<td>고급 네트워크 로직 구현 및 코드 일관성이 중요한 경우</td>
</tr>
</tbody></table>
<hr>
<h2 id="3️⃣-사람들이-많이-사용하는-패키지와-그-이유">3️⃣ 사람들이 많이 사용하는 패키지와 그 이유</h2>
<h3 id="1-dio"><strong>1. dio</strong></h3>
<ul>
<li><strong>가장 많이 사용되는 이유</strong>: 고급 기능(인터셉터, 요청 취소, 파일 업로드 등)을 폭넓게 지원.</li>
<li><strong>유연성</strong>: 복잡한 네트워크 로직에도 유연하게 대처 가능.</li>
<li><strong>유지보수 용이</strong>: 네트워크 레이어 분리가 쉬워 대규모 프로젝트에 적합.</li>
</ul>
<h3 id="2-http"><strong>2. http</strong></h3>
<ul>
<li><strong>간편함</strong>: Flutter 기본 패키지로 간단한 API 통신에 적합.</li>
<li><strong>빠른 학습 곡선</strong>: 초보자도 쉽게 접근 가능.</li>
</ul>
<h3 id="3-getx"><strong>3. GetX</strong></h3>
<ul>
<li><strong>통합 솔루션</strong>: 상태 관리, 라우팅, 의존성 주입, API 통신을 하나의 패키지로 해결.</li>
<li><strong>빠른 개발</strong>: MVP나 프로토타입 앱 개발에 최적화.</li>
</ul>
<h3 id="4-retrofit"><strong>4. retrofit</strong></h3>
<ul>
<li><strong>코드 일관성</strong>: <code>dio</code>와 결합해 효율적인 네트워크 레이어 구성.</li>
<li><strong>직관적 API 작성</strong>: 코드 가독성이 뛰어나고 유지보수에 강점.</li>
</ul>
<hr>
<h2 id="3️⃣-json-데이터-처리-json_serializable-vs-freezed">3️⃣ JSON 데이터 처리: <code>json_serializable</code> vs <code>freezed</code></h2>
<p>API 응답으로 받은 JSON 데이터를 모델로 변환하기 위해 Flutter에서는 **<code>json_serializable</code>**과 **<code>freezed</code>**를 많이 사용합니다.</p>
<h3 id="1-json_serializable">1. <code>json_serializable</code></h3>
<ul>
<li><strong>자동 JSON 직렬화/역직렬화</strong> 지원.</li>
<li>코드 생성기로 <strong>간결한 데이터 모델</strong> 작성 가능.</li>
</ul>
<h4 id="✅-사용-예시">✅ 사용 예시</h4>
<pre><code class="language-dart">import &#39;package:json_annotation/json_annotation.dart&#39;;

part &#39;user.g.dart&#39;;

@JsonSerializable()
class User {
  final int id;
  final String name;

  User({required this.id, required this.name});

  factory User.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$UserFromJson(json);
  Map&lt;String, dynamic&gt; toJson() =&gt; _$UserToJson(this);
}</code></pre>
<p><strong>🔧 의존성 추가</strong></p>
<pre><code class="language-yaml">dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.0
  json_serializable: ^6.6.1</code></pre>
<p><strong>⚡ 코드 생성</strong></p>
<pre><code class="language-bash">flutter pub run build_runner build</code></pre>
<h3 id="2-freezed">2. <code>freezed</code></h3>
<ul>
<li><strong>불변 객체(Immutable Object)</strong> 생성.</li>
<li><strong>데이터 클래스, 복사(copyWith), 비교(equatable) 기능</strong>을 자동 생성.</li>
<li><code>json_serializable</code>과 함께 사용 가능.</li>
</ul>
<h4 id="✅-사용-예시-1">✅ 사용 예시</h4>
<pre><code class="language-dart">import &#39;package:freezed_annotation/freezed_annotation.dart&#39;;

part &#39;user.freezed.dart&#39;;
part &#39;user.g.dart&#39;;

@freezed
class User with _$User {
  const factory User({
    required int id,
    required String name,
  }) = _User;

  factory User.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$UserFromJson(json);
}</code></pre>
<p><strong>🔧 의존성 추가</strong></p>
<pre><code class="language-yaml">dependencies:
  freezed_annotation: ^2.2.0

 dev_dependencies:
  build_runner: ^2.4.0
  freezed: ^2.3.2
  json_serializable: ^6.6.1</code></pre>
<p><strong>⚡ 코드 생성</strong></p>
<pre><code class="language-bash">flutter pub run build_runner build</code></pre>
<hr>
<h2 id="4️⃣-json_serializable-vs-freezed-비교">4️⃣ <code>json_serializable</code> vs <code>freezed</code> 비교</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th><code>json_serializable</code></th>
<th><code>freezed</code></th>
</tr>
</thead>
<tbody><tr>
<td><strong>목적</strong></td>
<td>JSON 직렬화/역직렬화</td>
<td>불변 객체, 데이터 클래스 자동 생성</td>
</tr>
<tr>
<td><strong>가독성</strong></td>
<td>코드가 단순하고 직관적</td>
<td>다양한 기능으로 인해 상대적으로 복잡함</td>
</tr>
<tr>
<td><strong>기능</strong></td>
<td>JSON 변환에 특화</td>
<td>복사, 비교, 패턴 매칭 등 다양한 기능 제공</td>
</tr>
<tr>
<td><strong>유지보수</strong></td>
<td>간편한 데이터 모델 관리</td>
<td>복잡한 로직을 효율적으로 관리 가능</td>
</tr>
<tr>
<td><strong>주 사용처</strong></td>
<td>단순 데이터 변환이 필요한 경우</td>
<td>상태 관리, 복잡한 모델링이 필요한 경우</td>
</tr>
</tbody></table>
<hr>
<h2 id="5️⃣-결론">5️⃣ 결론</h2>
<ul>
<li>간단한 API 통신은 <strong><code>http</code></strong>, 고급 네트워크 처리는 <strong><code>dio</code></strong> 사용.</li>
<li>복잡한 상태 관리나 데이터 처리는 <strong><code>freezed</code></strong>, 단순한 JSON 변환은 <strong><code>json_serializable</code></strong> 사용.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter 기본 및 설치 가이드]]></title>
            <link>https://velog.io/@dohy-9443/Flutter-%EA%B8%B0%EB%B3%B8-%EB%B0%8F-%EC%84%A4%EC%B9%98-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@dohy-9443/Flutter-%EA%B8%B0%EB%B3%B8-%EB%B0%8F-%EC%84%A4%EC%B9%98-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Wed, 15 Jan 2025 12:41:55 GMT</pubDate>
            <description><![CDATA[<h2 id="1️⃣-flutter-설치-준비">1️⃣ Flutter 설치 준비</h2>
<h3 id="✅-시스템-요구사항">✅ 시스템 요구사항</h3>
<p><strong>🟣 macOS</strong></p>
<ul>
<li>운영체제: macOS (Intel 또는 Apple Silicon)</li>
<li>디스크 공간: 2.8 GB (IDE 및 기타 도구 제외)</li>
<li>도구: Git, Xcode (iOS 개발 시)</li>
</ul>
<p><strong>🔵 Windows</strong></p>
<ul>
<li>운영체제: Windows 10 이상 (64-bit)</li>
<li>디스크 공간: 1.64 GB (IDE 및 기타 도구 제외)</li>
<li>도구: PowerShell 5.0 이상, Git</li>
</ul>
<hr>
<h2 id="2️⃣-flutter-sdk-설치">2️⃣ Flutter SDK 설치</h2>
<h3 id="windows">Windows</h3>
<pre><code class="language-bash"># 1. Flutter SDK 다운로드
https://flutter.dev/docs/get-started/install/windows

# 2. 압축 해제
C:\src\flutter

# 3. 환경변수 설정
setx PATH &quot;%PATH%;C:\src\flutter\bin&quot;

# 4. 설치 확인
flutter doctor</code></pre>
<h3 id="macos">macOS</h3>
<h4 id="1-flutter-sdk-다운로드-및-설치">1. Flutter SDK 다운로드 및 설치</h4>
<ul>
<li><a href="https://docs.flutter.dev/get-started/install/macos">Flutter 공식 SDK 다운로드</a></li>
</ul>
<p><strong>수동 설치 방법:</strong></p>
<pre><code class="language-bash"># 1. Flutter SDK 다운로드
https://docs.flutter.dev/get-started/install/macos

# 2. 압축 해제 및 이동
unzip ~/Downloads/flutter_macos_*.zip -d ~/development

# 3. PATH 설정
echo &#39;export PATH=&quot;$PATH:\$HOME/development/flutter/bin&quot;&#39; &gt;&gt; ~/.zshrc
source ~/.zshrc

# 4. 설치 확인
flutter doctor</code></pre>
<h4 id="2-homebrew-설치">2. Homebrew 설치</h4>
<pre><code class="language-bash">/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;</code></pre>
<p><strong>⚠️ Apple Silicon Mac에서 Homebrew 인식 오류 해결</strong></p>
<ul>
<li>설치 후에도 <code>brew</code> 명령어가 동작하지 않는 경우:<pre><code class="language-bash">echo &#39;export PATH=&quot;/opt/homebrew/bin:$PATH&quot;&#39; &gt;&gt; ~/.zshrc
source ~/.zshrc</code></pre>
</li>
</ul>
<h4 id="3-flutter-sdk-설치-homebrew-사용">3. Flutter SDK 설치 (Homebrew 사용)</h4>
<pre><code class="language-bash">brew install --cask flutter</code></pre>
<h4 id="3-cocoapods-설치-ios-빌드-시-필요">3. CocoaPods 설치 (iOS 빌드 시 필요)</h4>
<pre><code class="language-bash">sudo gem install cocoapods</code></pre>
<p><strong>⚠️ CocoaPods 설치 오류 해결</strong></p>
<ul>
<li>설치 중 오류가 발생할 경우:<pre><code class="language-bash">sudo arch -x86_64 gem install ffi
pod install --repo-update</code></pre>
</li>
</ul>
<h4 id="4-path-설정">4. PATH 설정</h4>
<pre><code class="language-bash">echo &#39;export PATH=&quot;$PATH:\$HOME/flutter/bin&quot;&#39; &gt;&gt; ~/.zshrc
source ~/.zshrc</code></pre>
<h4 id="5-설치-확인">5. 설치 확인</h4>
<pre><code class="language-bash">flutter doctor</code></pre>
<hr>
<h2 id="3️⃣-개발-환경-설정">3️⃣ 개발 환경 설정</h2>
<h3 id="✅-macos-환경변수-설정-파일">✅ macOS 환경변수 설정 파일</h3>
<ul>
<li><strong>파일 위치</strong>: <code>~/.zshrc</code> (기본 쉘이 Zsh인 경우)</li>
<li><strong>숨김 파일 보기 단축키</strong>: <code>Command + Shift + .</code></li>
</ul>
<h3 id="✅-vscode로-숨김-파일-수정하기">✅ VSCode로 숨김 파일 수정하기</h3>
<pre><code class="language-bash">code ~/.zshrc</code></pre>
<h3 id="✅-android-개발-환경">✅ Android 개발 환경</h3>
<ul>
<li><strong>Android Studio</strong> 설치 (<a href="https://developer.android.com/studio">다운로드</a>)</li>
<li><strong>Android SDK</strong> 설치 및 환경변수 설정</li>
<li><strong>Android Emulator</strong> 또는 실제 디바이스 연결</li>
</ul>
<h3 id="✅-ios-개발-환경-macos">✅ iOS 개발 환경 (macOS)</h3>
<ul>
<li><strong>Xcode</strong> 설치 (<a href="https://apps.apple.com/">Mac App Store</a>)</li>
<li><strong>Xcode Command Line Tools</strong> 설치</li>
<li>iOS 시뮬레이터 또는 실제 디바이스 연결</li>
</ul>
<h3 id="✅-에디터ide">✅ 에디터(IDE)</h3>
<ul>
<li><strong>Visual Studio Code</strong> + Flutter/Dart 확장 프로그램 설치</li>
<li><strong>Android Studio</strong> + Flutter 플러그인 설치</li>
</ul>
<hr>
<h2 id="4️⃣-flutter-doctor-실행-및-문제-해결">4️⃣ Flutter Doctor 실행 및 문제 해결</h2>
<p><code>flutter doctor</code> 명령어를 사용해 Flutter 개발 환경이 올바르게 설정되었는지 확인합니다.</p>
<pre><code class="language-bash">flutter doctor</code></pre>
<p>출력 결과를 확인하고, 필요 시 추가 설치 및 설정을 진행합니다.</p>
<hr>
<h2 id="5️⃣-첫-번째-flutter-앱-실행">5️⃣ 첫 번째 Flutter 앱 실행</h2>
<h3 id="✅-프로젝트-생성">✅ 프로젝트 생성</h3>
<pre><code class="language-bash">flutter create my_first_app
cd my_first_app</code></pre>
<h3 id="✅-앱-실행">✅ 앱 실행</h3>
<pre><code class="language-bash"># Android 에뮬레이터 또는 연결된 디바이스에서 실행
flutter run</code></pre>
<h3 id="✅-코드-수정-및-핫리로드hot-reload">✅ 코드 수정 및 핫리로드(Hot Reload)</h3>
<ul>
<li><code>lib/main.dart</code> 파일을 수정하고 <strong>Hot Reload</strong>로 즉시 결과 확인</li>
<li><code>Ctrl + S</code> 또는 <code>r</code> 키로 빠른 반영 가능</li>
</ul>
<hr>
<h2 id="6️⃣-자주-발생하는-설치-이슈-및-해결-방법">6️⃣ 자주 발생하는 설치 이슈 및 해결 방법</h2>
<h3 id="❗-android_home-오류">❗ ANDROID_HOME 오류</h3>
<ul>
<li><strong>문제</strong>: Android SDK 경로를 찾을 수 없음</li>
<li><strong>해결</strong>:<pre><code class="language-bash">export ANDROID_HOME=\$HOME/Library/Android/sdk
export PATH=\$PATH:\$ANDROID_HOME/tools
export PATH=\$PATH:\$ANDROID_HOME/platform-tools</code></pre>
</li>
</ul>
<h3 id="❗-디바이스-연결-안-됨">❗ 디바이스 연결 안 됨</h3>
<ul>
<li><strong>문제</strong>: 연결된 디바이스를 인식하지 못함</li>
<li><strong>해결</strong>:<pre><code class="language-bash">flutter devices
adb devices</code></pre>
</li>
</ul>
<h3 id="❗-ios-빌드-실패">❗ iOS 빌드 실패</h3>
<ul>
<li><strong>문제</strong>: macOS에서 iOS 빌드가 실패함</li>
<li><strong>해결</strong>:<pre><code class="language-bash">sudo xcode-select --install
sudo xcodebuild -license</code></pre>
</li>
</ul>
<hr>
<h2 id="7️⃣-마무리">7️⃣ 마무리</h2>
<h3 id="🔗-참고-자료">🔗 참고 자료</h3>
<ul>
<li><a href="https://flutter.dev/docs">Flutter 공식 문서</a></li>
<li><a href="https://dart.dev/">Dart 언어 공식 사이트</a></li>
</ul>
<blockquote>
<p>Flutter 시작! 🚀</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter에서 상태 관리를 사용하는 이유와 다양한 패키지 비교]]></title>
            <link>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%8B%A4%EC%96%91%ED%95%9C-%ED%8C%A8%ED%82%A4%EC%A7%80-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@dohy-9443/Flutter%EC%97%90%EC%84%9C-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0%EC%99%80-%EB%8B%A4%EC%96%91%ED%95%9C-%ED%8C%A8%ED%82%A4%EC%A7%80-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Wed, 15 Jan 2025 12:26:12 GMT</pubDate>
            <description><![CDATA[<h2 id="1-flutter에서-상태-관리를-사용하는-이유">1. Flutter에서 상태 관리를 사용하는 이유</h2>
<p>Flutter 앱은 UI와 상태가 밀접하게 연결되어 있습니다. 사용자의 입력, API 호출 결과, 화면 전환 등 다양한 상황에서 UI가 동적으로 변경되어야 합니다. 이러한 변화를 효율적으로 관리하기 위해 상태 관리가 필요합니다.</p>
<h3 id="단순-전역-변수의-한계">단순 전역 변수의 한계</h3>
<ul>
<li><strong>데이터 일관성 유지가 어렵다</strong>: 여러 위젯에서 데이터를 수정할 경우 예기치 않은 버그가 발생할 수 있음.</li>
<li><strong>UI 업데이트가 자동으로 이루어지지 않음</strong>: 전역 변수를 변경해도 UI가 자동으로 업데이트되지 않아, 수동으로 갱신해야 함.</li>
<li><strong>복잡한 앱에서는 유지보수 및 확장성이 떨어짐</strong>: 화면이 많아질수록 상태 관리가 복잡해짐.</li>
</ul>
<h2 id="2-상태state란">2. 상태(State)란?</h2>
<p><strong>상태(State)</strong>는 앱의 특정 시점에서의 데이터와 UI의 상태를 의미합니다.</p>
<ul>
<li>사용자가 버튼을 눌렀는지 여부</li>
<li>리스트의 데이터</li>
<li>API 호출 결과</li>
</ul>
<p>이 모든 것이 상태에 해당합니다.</p>
<h2 id="3-상태-관리state-management란">3. 상태 관리(State Management)란?</h2>
<p><strong>상태 관리</strong>는 상태의 변화를 감지하고 그에 따라 UI를 효율적으로 업데이트하는 것을 의미합니다.</p>
<ul>
<li><strong>변화 감지</strong>: 데이터가 변경되었는지 감지</li>
<li><strong>UI 업데이트</strong>: 변경된 데이터를 기반으로 UI를 갱신</li>
</ul>
<h2 id="4-flutter의-주요-상태-관리-패키지">4. Flutter의 주요 상태 관리 패키지</h2>
<h3 id="1️⃣-provider">1️⃣ Provider</h3>
<ul>
<li>Google에서 권장하는 상태 관리 패키지</li>
<li>간단하고 직관적이며 InheritedWidget을 기반으로 구현</li>
</ul>
<h3 id="2️⃣-riverpod">2️⃣ Riverpod</h3>
<ul>
<li>Provider의 개선 버전</li>
<li>전역적으로 상태를 관리하고 테스트하기 용이함</li>
</ul>
<h3 id="3️⃣-bloc-business-logic-component">3️⃣ Bloc (Business Logic Component)</h3>
<ul>
<li>상태와 비즈니스 로직을 명확하게 분리</li>
<li>이벤트(Event)와 상태(State)를 기반으로 동작</li>
</ul>
<h3 id="4️⃣-getx">4️⃣ GetX</h3>
<ul>
<li>간단하고 강력한 상태 관리, 라우팅, 의존성 주입을 하나의 패키지에서 제공</li>
<li>가장 인기 있는 패키지 중 하나로, 높은 성능과 쉬운 사용법이 특징</li>
</ul>
<h3 id="5️⃣-mobx">5️⃣ MobX</h3>
<ul>
<li>관찰 가능한 상태(Observable)를 기반으로 반응형 상태 관리</li>
<li>코드의 선언적 방식으로 직관적</li>
</ul>
<h2 id="5-getx-패키지의-특징과-인기-이유">5. GetX 패키지의 특징과 인기 이유</h2>
<ul>
<li><strong>간편한 사용법</strong>: 코드가 간결하고, 상태 관리, 라우팅, 의존성 주입을 하나의 패키지로 해결 가능</li>
<li><strong>고성능</strong>: 별도의 위젯 트리 리빌드 없이 UI 업데이트 가능</li>
<li><strong>반응형 프로그래밍(Rx) 지원</strong>: 데이터의 변화를 자동으로 감지하고 UI 업데이트</li>
</ul>
<h2 id="6-getx의-rx-방식이란">6. GetX의 Rx 방식이란?</h2>
<p><strong>Rx(Reactive Extension)</strong>는 데이터의 변화를 실시간으로 감지하여 UI를 자동으로 업데이트하는 방식입니다.</p>
<h3 id="예시">예시</h3>
<pre><code class="language-dart">var count = 0.obs; // Rx 방식

Obx(() =&gt; Text(&quot;Clicked: \$count&quot;));

// 버튼 클릭 시
count++;</code></pre>
<ul>
<li><strong><code>.obs</code></strong>: 데이터를 관찰 가능한 상태로 만듦</li>
<li><strong><code>Obx</code></strong>: 데이터가 변경되면 UI를 자동으로 업데이트</li>
</ul>
<h2 id="7-반응형-프로그래밍이란">7. 반응형 프로그래밍이란?</h2>
<p><strong>반응형 프로그래밍(Responsive Programming)</strong>은 데이터의 흐름과 변화를 자동으로 감지하고 처리하는 프로그래밍 방식입니다.</p>
<ul>
<li><strong>자동 UI 업데이트</strong>: 상태 변화 시 자동으로 UI 반영</li>
<li><strong>비동기 처리 최적화</strong>: 이벤트 스트림을 통해 효율적인 데이터 처리</li>
</ul>
<h2 id="7-1-그렇다면-getx로-모든-상태-관리를-해결하면-되지-않을까">7-1. 그렇다면 GetX로 모든 상태 관리를 해결하면 되지 않을까?</h2>
<p><strong>GetX</strong>는 빠르고 간편한 상태 관리, 라우팅, 의존성 주입을 통합적으로 제공하지만, 모든 상황에 최적의 선택은 아닙니다. 프로젝트의 규모와 복잡성에 따라 적절한 상태 관리 도구를 선택하는 것이 중요합니다.</p>
<h3 id="🔎-getx만으로-해결하기-어려운-상황">🔎 <strong>GetX만으로 해결하기 어려운 상황</strong></h3>
<ul>
<li><strong>대규모 프로젝트</strong>: 팀원 간 역할 분담 및 비즈니스 로직과 UI의 명확한 분리가 필요할 때는 <strong>Bloc</strong>이나 <strong>Riverpod</strong>가 더 적합함.</li>
<li><strong>테스트 용이성</strong>: GetX는 테스트 환경에서 제어가 다소 어렵고, 상태의 추적이 복잡해질 수 있음. 반면 <strong>Bloc</strong>과 <strong>Riverpod</strong>는 테스트가 용이함.</li>
<li><strong>유지보수성</strong>: GetX는 빠른 개발에는 좋지만, 장기적으로는 코드 구조화가 필요한 프로젝트에서 한계가 있을 수 있음.</li>
</ul>
<h3 id="⚖️-다른-패키지를-사용하는-이유">⚖️ <strong>다른 패키지를 사용하는 이유</strong></h3>
<ul>
<li><strong>Bloc</strong>: 명확한 로직 분리와 상태 추적이 중요한 프로젝트에서 선호됨.</li>
<li><strong>Provider/Riverpod</strong>: 유연성과 확장성이 뛰어나며, 다양한 커스텀 구현이 가능.</li>
<li><strong>MobX</strong>: 선언적 프로그래밍을 선호하거나 복잡한 상태 관리를 간결하게 처리하고자 할 때 적합.</li>
</ul>
<p>결론적으로, <strong>GetX</strong>는 빠르고 간결한 개발에 유리하지만, 프로젝트의 특성과 규모에 따라 적합한 상태 관리 방식을 선택해야 합니다.</p>
<p><strong>반응형 프로그래밍(Responsive Programming)</strong>은 데이터의 흐름과 변화를 자동으로 감지하고 처리하는 프로그래밍 방식입니다.</p>
<ul>
<li><strong>자동 UI 업데이트</strong>: 상태 변화 시 자동으로 UI 반영</li>
<li><strong>비동기 처리 최적화</strong>: 이벤트 스트림을 통해 효율적인 데이터 처리</li>
</ul>
<h2 id="8-다른-상태-관리-패키지는-왜-사용하는가">8. 다른 상태 관리 패키지는 왜 사용하는가?</h2>
<ul>
<li><strong>Bloc</strong>: 대규모 프로젝트에서 <strong>비즈니스 로직과 UI의 분리</strong>가 필요할 때 유용함</li>
<li><strong>Provider/Riverpod</strong>: <strong>단순한 상태 관리</strong>나 <strong>유연성</strong>이 필요한 프로젝트에 적합</li>
<li><strong>MobX</strong>: <strong>선언적 코드 스타일</strong>을 선호하는 개발자에게 적합</li>
<li><strong>GetX</strong>: 빠른 개발과 <strong>간결한 코드</strong>를 원하는 경우 적합</li>
</ul>
<h2 id="9-총정리">9. 총정리</h2>
<table>
<thead>
<tr>
<th>패키지</th>
<th>난이도</th>
<th>특징</th>
<th>추천 용도</th>
</tr>
</thead>
<tbody><tr>
<td>Provider</td>
<td>쉬움</td>
<td>Google 권장, 간단한 상태 관리</td>
<td>소규모 프로젝트, 간단한 앱</td>
</tr>
<tr>
<td>Riverpod</td>
<td>중간</td>
<td>전역 상태 관리, 테스트 용이</td>
<td>확장성 있는 프로젝트</td>
</tr>
<tr>
<td>Bloc</td>
<td>어려움</td>
<td>상태와 로직 분리, 명확한 구조화</td>
<td>대규모 앱, 팀 프로젝트</td>
</tr>
<tr>
<td>GetX</td>
<td>쉬움</td>
<td>상태 관리, 라우팅, 의존성 주입 통합 제공</td>
<td>빠른 개발, 간결한 코드</td>
</tr>
<tr>
<td>MobX</td>
<td>중간</td>
<td>관찰 가능한 상태 기반, 선언적 코드 스타일</td>
<td>선언적 코드를 선호하는 개발자</td>
</tr>
</tbody></table>
<h3 id="✅-결론">✅ 결론</h3>
<ul>
<li>간단한 앱은 <strong>Provider</strong>나 <strong>GetX</strong> 사용</li>
<li>규모가 큰 앱이나 협업이 많은 프로젝트는 <strong>Bloc</strong></li>
<li>유연성과 테스트 용이성을 원하면 <strong>Riverpod</strong></li>
</ul>
<p>상황에 맞는 상태 관리 패키지를 선택해 효율적인 Flutter 개발을 하자 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter version]]></title>
            <link>https://velog.io/@dohy-9443/flutter-version</link>
            <guid>https://velog.io/@dohy-9443/flutter-version</guid>
            <pubDate>Fri, 26 Jul 2024 02:48:11 GMT</pubDate>
            <description><![CDATA[<h2 id="flutter-version-management">flutter version management</h2>
<h3 id="fvm-설치">fvm 설치:</h3>
<pre><code class="language-dart">dart pub global activate fvm</code></pre>
<h3 id="프로젝트-flutter-버전-설치">프로젝트 Flutter 버전 설치:</h3>
<pre><code class="language-dart">cd project
fvm install 3.16.9</code></pre>
<h3 id="해당-프로젝트-flutter-버전-설정">해당 프로젝트 Flutter 버전 설정:</h3>
<pre><code class="language-dart">fvm use 3.16.9</code></pre>
<h3 id="flutter-명령어-실행">Flutter 명령어 실행:</h3>
<pre><code class="language-dart">fvm flutter run</code></pre>
<h3 id="프로젝트-루트-폴더에-flutter-version-파일-생성하여-해당-flutter-version-명시">프로젝트 루트 폴더에 .flutter-version 파일 생성하여 해당 flutter version 명시:</h3>
<pre><code class="language-dart">echo &quot;3.16.9&quot; &gt; .flutter-version</code></pre>
<h2 id="flutter-version-바꾸기">flutter version 바꾸기</h2>
<pre><code class="language-dart">which flutter</code></pre>
<p>/bin/flutter 부분은 제외하고 해당 위치까지 간다.</p>
<h3 id="안정버전-사용하기-">안정버전 사용하기 :</h3>
<pre><code class="language-dart">flutter channel stable</code></pre>
<h3 id="특정-버전-사용하기">특정 버전 사용하기:</h3>
<pre><code class="language-dart">git checkout 3.16.9</code></pre>
<h3 id="버전-확인하기">버전 확인하기:</h3>
<pre><code class="language-dart">flutter --version</code></pre>
<h3 id="최신-버전-업그레이드-하기">최신 버전 업그레이드 하기:</h3>
<pre><code class="language-dart">flutter upgrade</code></pre>
<h3 id="현재-최신-버전의-직전-버전으로-다운-그레이드-하기">현재 최신 버전의 직전 버전으로 다운 그레이드 하기:</h3>
<pre><code class="language-dart">flutter downgrade</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[BLoC 상태관리]]></title>
            <link>https://velog.io/@dohy-9443/BLoC-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@dohy-9443/BLoC-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sat, 20 Jul 2024 14:53:07 GMT</pubDate>
            <description><![CDATA[<h1 id="bloc">BLoC</h1>
<h2 id="bloc-pattern-이란">BLoC Pattern 이란?</h2>
<p>BLoC (Business Logic Component) Pattern 은 Flutter 애플리케이션에서 상태 관리를 위해 사용되는 디자인 패턴</p>
<pre><code>⇒ UI 와 Business Logic 을 분리하여 사용되는 패턴</code></pre><h2 id="bloc-pattern-구조">BLoC Pattern 구조</h2>
<h3 id="event">Event</h3>
<p>사용자의 인터렉션이나 시스템 이벤트가 발생하면, 이러한 이벤트는 BLoC 로 전달</p>
<pre><code>⇒ 이벤트는 버튼 클릭, 데이터 로드 요청 등 사용자 인터페이스에서 발생하는 다양한 액션</code></pre><h3 id="bloc-1">BLoC</h3>
<p>애플리케이션의 비즈니스 로직 담당</p>
<pre><code>⇒ 들어오는 이벤트를 받아 처리하고, 새로운 상태를 생성하여 애플리케이션의 UI 나 다른 BLoC 에 전달</code></pre><h3 id="state">State</h3>
<p>BLoC 에 의해 생성되고 UI 에 전달되는 상태</p>
<pre><code>⇒ 상태는 UI 가 사용자에게 보여줄 데이터의 현재 상태</code></pre><h2 id="bloc-pattern-원리">BLoC Pattern 원리</h2>
<h3 id="1-이벤트-발생">1. 이벤트 발생</h3>
<p>사용자 인터페이스에서 특정 액션이 발생하면, 관련된 이벤트가 BLoC 로 전달</p>
<h3 id="2-비지니스-로직-처리">2. 비지니스 로직 처리</h3>
<p>BLoC 는 전달받은 이벤트를 기반으로 비즈니스 로직을 수행</p>
<pre><code>⇒ 외부 데이터 소스와의 통신, 데이터 가공 등이 이루어 질 수 있다.</code></pre><h3 id="3-상태-생성">3. 상태 생성</h3>
<p>BLoC 는 처리 결과를 바탕으로 새로운 상태 생성</p>
<pre><code>⇒ 상태는 애플리케이션 UI 가 사용자에게 보여줄 데이터를 포함</code></pre><h3 id="4-상태-전달">4. 상태 전달</h3>
<p>생성된 새로운 상태는 UI 로 전달</p>
<pre><code>⇒ UI 는 이 상태에 따라 화면 갱신</code></pre><h2 id="bloc-pattern-장점">BLoC Pattern 장점</h2>
<h3 id="비즈니스-로직과-ui-분리">비즈니스 로직과 UI 분리</h3>
<p>비즈니스 로직을 UI 로 부터 분리</p>
<pre><code>⇒ 비즈니스 로직이 UI 와 독립적으로 유지되어 코드의 재사용성과 유지보수 용이</code></pre><h3 id="테스트-용이성">테스트 용이성</h3>
<p>비즈니스 로직이 UI 와 분리되어 있기 때문에 UI 에 의존하지 않고 독립적으로 테스트 가능</p>
<pre><code>⇒ 애플리케이션의 안정성을 높이는 데 기여</code></pre><h3 id="상태-관리의-일관성">상태 관리의 일관성</h3>
<p>모든 상태 변경이 BLoC 를 통해 이루어지도록 함으로써 상태 관리의 일관성 유지</p>
<pre><code>⇒ 애플리케이션의 예측 가능성과 실뢰성을 높이는 데 기여</code></pre><h3 id="비동기-처리의-효율성">비동기 처리의 효율성</h3>
<p>Stream 을 사용하여 비동기 이벤트와 상태 변화 관리</p>
<pre><code>⇒ 비동기 작업을 효율적으로 처리</code></pre><h2 id="bloc-pattern-단점">BLoC Pattern 단점</h2>
<h3 id="학습-곡선">학습 곡선</h3>
<p>이해하고 사용하기까지 상대적으로 높은 학습 곡선을 가지고 있다.</p>
<pre><code>⇒ Stream 과 비동기 프로그래밍에 익숙하지 않은 개발자에게는 초기 진입 장벽이 될 수 있다.</code></pre><h3 id="보일러플레이트-코드">보일러플레이트 코드</h3>
<p>이벤트, 상태, BLoC 클래스 등을 정의해야 하기 때문에 상당량의 보일러플레이트(반복적인) 코드가 발생할 수 있다.</p>
<pre><code>⇒ 프로젝트의 코드량을 증가시키고, 때로는 가독성을 저해할 수 있다.</code></pre><h3 id="상태-관리의-복잡성">상태 관리의 복잡성</h3>
<p>크고 복잡한 애플리케이션에서 다수의 BLoC 를 관리하고, BLoC 간의 상호작용을 처리하는 것이 복잡해질 수 있다.</p>
<pre><code>⇒ 애플리케이션의 아키텍처를 복잡하게 만들 수 있다.</code></pre><h1 id="bloc-2">bloc</h1>
<h2 id="bloc-이란">bloc 이란?</h2>
<p>Dart 언어를 사용하는 어떤 애플리케이션에서든 상태 관리를 위해 사용될 수 있는 상태 관리 라이브러리</p>
<pre><code>⇒ BLoC 패턴을 쉽고 효과적으로 구현할 수 있도록 도와주는 다양한 기능과 유틸리티 제공</code></pre><h2 id="bloc-목적">bloc 목적</h2>
<p>BLoC 패턴을 구현하여 이벤트를 수신하고 이를 기반으로 상태를 변화시키는 로직을 구현할 수 있게 도와줌</p>
<pre><code>⇒ 애플리케이션의 상태 관리를 추상화하고, 비즈니스 로직을 UI 로부터 분리하는 데 도움을 줌</code></pre><h2 id="cubit">Cubit</h2>
<p>이벤트 없이 상태만 관리하는 간단한 형태</p>
<pre><code>⇒ 함수 호출을 통해 상태를 변경</code></pre><h2 id="bloc-3">Bloc</h2>
<p>이벤트 기반으로 상태를 관리하는 복잡한 형태</p>
<pre><code>⇒ 이벤트를 받아서 상태를 방출</code></pre><h2 id="blocobserver">BlocObserver</h2>
<p>BLoC 패턴에서 상태 관리 시스템의 생명주기 이벤트를 관찰하고 처리하는 용도</p>
<pre><code>⇒ 디버깅, 로깅, 분석 등에 사용</code></pre><h3 id="oncreate">onCreate</h3>
<p>Bloc 또는 Cubit 이 생성될 때 호출</p>
<pre><code>⇒ 초기화 단계에서 발생하는 이벤트를 관찰하는 용도</code></pre><h3 id="onevent">onEvent</h3>
<p>Bloc 에 이벤트가 추가될 때마다 호출</p>
<pre><code>⇒ 초기화 단계에서 발생하는 이벤트를 관찷하는 용도</code></pre><h3 id="onchange">onChange</h3>
<p>Bloc 또는 Cubit 의 상태가 변경될 때 호출</p>
<pre><code>⇒ 상태 변화를 추적하고 분석하는 용도</code></pre><h3 id="ontransition">onTransition</h3>
<p>Bloc 에서 상태 전환이 발생할 때 호출</p>
<pre><code>⇒ 상태 변화의 전체 흐름 파악하는 용도</code></pre><h3 id="onerror">onError</h3>
<p>Bloc 또는 Cubit 내부에서 예외가 발생할 때 호출</p>
<pre><code>⇒ 예외 상황을 처리하거나 로깅하는 용도</code></pre><h3 id="onclose">onClose</h3>
<p>Bloc 또는 Cubit 이 닫힐 때 호출</p>
<pre><code>⇒ 리소스 해제 등에 대한 관찰하는 용도</code></pre><h2 id="bloc-장점">bloc 장점</h2>
<h3 id="보일러-플레이트-코드-최소화">보일러 플레이트 코드 최소화</h3>
<p>이벤트 처리, 상태 변화, 스트림 관리 등을 위한 API 제공</p>
<pre><code>⇒ 개발자가 작성해야 하는 코드의 양을 줄여줌</code></pre><h3 id="상태-변화의-추적">상태 변화의 추적</h3>
<p>상태 변화를 쉽게 추적하고 디버깅 할 수 있도록 도와줌</p>
<h3 id="에러-처리와-리소스-관리">에러 처리와 리소스 관리</h3>
<p>에러 처리와 리소스 해제를 보다 쉽게 관리할 수 있는 패턴과 구조 제공</p>
<h1 id="fluter_bloc">fluter_bloc</h1>
<h2 id="flutter_bloc-이란">flutter_bloc 이란?</h2>
<p>Flutter 애플리케이션에서 BLoC 디자인 패턴을 구현하기 위해 만들어진 Flutter 라이브러리</p>
<h2 id="fluter_bloc-목적">fluter_bloc 목적</h2>
<p>위젯을 제공하여 Flutter UI 구성 요소와 BLoC 패턴 사이의 상호 작용을 용이하게 해줌</p>
<pre><code>⇒ 상태 변화에 따라 UI 를 업데이트하거나, bloc 을 위젯 트리에 추가하고, 상태 변화에 대한 반응을 처리하는 등의 작업을 간소화해줌</code></pre><h2 id="blocbuilder">BlocBuilder</h2>
<p>bloc 의 상태 기반으로 UI 의 일부를 구축하는 데 사용되는 위젯</p>
<pre><code>⇒ StreamBuilder 와 매우 유사하지만, API 가 더 단순하여 필요한 보일러플레이트 코드의 양을 줄임</code></pre><h2 id="blocbuilderbloc-state">BlocBuilder&lt;Bloc, State&gt;</h2>
<h3 id="blocbuilder-1">BlocBuilder</h3>
<p>bloc 의 상태 변화에 반응하여 UI 를 재빌드하는 위젯</p>
<pre><code>⇒ CounterBloc 타입의 Bloc 을 사용하고, Bloc 의 State 타입이 int 임을 명시</code></pre><h3 id="builder">builder</h3>
<p>bloc 의 상태가 변경될 때마다 호출되며, 주어진 상태에 따라 UI 를 어떻게 나타낼지 정의</p>
<pre><code>⇒ context 는 현재의 BuildContext 를 나타내고, state 는 bloc 의 최신 상탤르 나타냄</code></pre><h3 id="bloc-4">bloc</h3>
<p>BlocBuilder 에 사용할 Bloc 인스턴스를 전달</p>
<pre><code>⇒ CounterBloc 인스턴스인 bloc 을 사용하여 주어진 bloc 의 상태 변화를 감지하고 반응함</code></pre><h3 id="buildwhen">buildWhen</h3>
<p>bloc 의 상태 변화가 발생했을 때, UI 를 재빌드할지 결정하는 조건을 정의</p>
<pre><code>⇒ true 를 반환하면 builder 함수가 호출되어 UI 를 재빌드</code></pre><h2 id="blocselector">BlocSelector</h2>
<h3 id="bloc-의-상태-기반으로-ui-의-일부를-구축하는-데-사용되는-위젯">bloc 의 상태 기반으로 UI 의 일부를 구축하는 데 사용되는 위젯</h3>
<pre><code>⇒ BlocBuilder 와 유사하지만 위젯이 응답할 상태의 특정 부분을 선택하는 기능 추가</code></pre><h2 id="blocselectorbloc-state-selectedstate">BlocSelector&lt;Bloc, State, SelectedState&gt;</h2>
<h3 id="blocselector-1">BlocSelector</h3>
<p>bloc 의 상태 변화에 반응하여 UI 를 재빌드하는 위젯</p>
<pre><code>⇒ CounterBloc 타입의 Bloc 을 사용하고, Bloc 의 State 타입이 int 임을 명시하고, 그 중 String 의 값을 선택하도록 명시함</code></pre><h3 id="selector">selector</h3>
<p>bloc 의 수신 상태를 builder 함수에서 UI 를 재빌드하는 데 사용할 새로운 값으로 변환하는 곳</p>
<pre><code>⇒ state 는 bloc 의 최신 상태이며 해당 상태 기반으로 새로운 값으로 반환</code></pre><h3 id="builder-1">builder</h3>
<p>bloc 의 상태가 변경될 때, selector 에서의 상태가 변경될 때만 호출되며, 변경된 상태에 따라 UI 를 어떻게 나타낼지 정의</p>
<pre><code>⇒ context 는 현재의 BuildContext 를 나타내고, state 는 selector 에서 반환된 값</code></pre><h3 id="bloc-5">bloc</h3>
<p>BlocSelector 에 사용할 Bloc 인스턴스를 전달</p>
<p>⇒ CounterBloc 인스턴스인 bloc 을 사용하여 주어진 bloc 의 상태 변화를 감지하고 반응함</p>
<h2 id="blocprovider">BlocProvider</h2>
<h3 id="위젯-트리에-bloc-을-제공하는-위젯">위젯 트리에 bloc 을 제공하는 위젯</h3>
<p>의존성 주입에 사용되며 위젯 트리 전체에서 bloc 에 쉽게 접근 가능</p>
<h3 id="create">create</h3>
<p>bloc 인스턴스를 생성하여 위젯 트리에 제공</p>
<pre><code>⇒ 트리의 모든 하위 위젯에서 bloc 사용 가능</code></pre><h3 id="value">value</h3>
<p>기존 bloc 인스턴스를 가져와 위젯 트리에 제공</p>
<pre><code>⇒ 다양한 위젯 트리에서 bloc 상태를 그대로 유지해야 할 때 유용</code></pre><h3 id="dispose">dispose</h3>
<ul>
<li><p>create : 리소스 자동 해제</p>
</li>
<li><p>value : 리소스 수동 해제</p>
</li>
</ul>
<h3 id="검색-read">검색 (read)</h3>
<p>위젯 트리에서 bloc 인스턴스를 검색하는 데 사용되는 메소드</p>
<pre><code>⇒ 상태가 변경될 때 위젯이 자동으로 다시 작성되지 않음

⇒ 이벤트를 추가하기 위해 bloc 에 접근해야 할 때 일반적으로 사용</code></pre><h3 id="검색-watch">검색 (watch)</h3>
<p>위젯 트리에서 bloc 인스턴스를 검색하는 데 사용되는 메소드</p>
<pre><code>⇒ 상태가 변경될 때 마다 위젯이 재빌드되도록 함

⇒ 상태 변경에 응답하여 위젯이 UI 를 업데이트해야 할 때 유용</code></pre><h3 id="검색-select">검색 (select)</h3>
<p>위젯 트리에서 bloc 인스턴스를 검색하는 데 사용되는 메소드</p>
<pre><code>⇒ 모든 상태 변경이 아닌 선택한 상태 부분이 변경될 때만 위젯을 재빌드 함

⇒ 불필요한 위젯 재빌드를 줄여 성능을 향상 시킬 수 있음</code></pre><h2 id="multiblocprovider">MultiBlocProvider</h2>
<h3 id="여러-blocprovider-위젯을-단일-위젯-트리로-병합하는-위젯">여러 BlocProvider 위젯을 단일 위젯 트리로 병합하는 위젯</h3>
<p>여러 bloc 을 하위 트리에 제공하는 프로세스를 간소화하도록 설계된 위젯</p>
<pre><code>⇒ 코드의 가독성 향상과 여러 BlocProvider 의 중첩과 관련된 복잡성과 혼란 제거</code></pre><h2 id="bloclistener">BlocListener</h2>
<h3 id="상태-변화에-응답하여-리스너를-호출하는-위젯">상태 변화에 응답하여 리스너를 호출하는 위젯</h3>
<p>상태 변화에 대응하여 한 번만 수행되어야 하는 기능에 사용</p>
<pre><code>⇒ 토스트, 스낵바, 다이얼로그 표시 등</code></pre><h2 id="bloclistenerbloc-state">BlocListener&lt;Bloc, State&gt;</h2>
<h3 id="bloclistener-1">BlocListener</h3>
<p>bloc 의 상태 변화에 반응하여 리스너 함수 사용</p>
<pre><code>⇒ CounterBloc 타입의 Bloc 을 사용하고, Bloc 의 State 타입이 int 임을 명시</code></pre><h3 id="listener">listener</h3>
<p>상태 변경시 호출되는 콜백</p>
<pre><code>⇒ 변경 사항에 응답하는 코드 입력</code></pre><h3 id="listenwhen">listenWhen</h3>
<p>리스너 콜백을 호출해야 하는지 여부를 결적하는 선택적 콜백</p>
<pre><code>⇒ 반환 값이 true 이면 리스너가 호출되고 그렇지 않으면 건너 뜀</code></pre><h2 id="multibloclistener">MultiBlocListener</h2>
<h3 id="여러-bloclistener-위젯을-단일-위젯-트리로-병합하는-위젯">여러 BlocListener 위젯을 단일 위젯 트리로 병합하는 위젯</h3>
<p>여러 bloc 의 상태 변경을 수신해야 할 때 사용</p>
<pre><code>⇒ 코드의 가독성 향상과 여러 BlocListener 의 중첩과 관련된 복잡성과 혼란 제거</code></pre><h2 id="blocconsumer">BlocConsumer</h2>
<h3 id="bloclistener-와-blocbuilder-의-기능을-하나의-위젯으로-결합한-위젯">BlocListener 와 BlocBuilder 의 기능을 하나의 위젯으로 결합한 위젯</h3>
<h2 id="repositoryprovider">RepositoryProvider</h2>
<h3 id="위젯-트리에-단일-저장소-인스턴스를-제공하는-데-사용되는-위젯">위젯 트리에 단일 저장소 인스턴스를 제공하는 데 사용되는 위젯</h3>
<p>하위 위젯에서 저장소에 접근하여 API 또는 DB 에서 데이터를 가져오는 등 데이터 작업 수행</p>
<h3 id="검색read">검색(read)</h3>
<p>위젯 트리에서 저장소를 검색하는 데 사용되는 메소드</p>
<h2 id="multirepositoryprovider">MultiRepositoryProvider</h2>
<h3 id="여러-repositoryprovider-위젯을-단일-위젯-트리로-병합하는-위젯">여러 RepositoryProvider 위젯을 단일 위젯 트리로 병합하는 위젯</h3>
<h2 id="flutter_bloc-장점">flutter_bloc 장점</h2>
<h3 id="구조화와-유지-보수성">구조화와 유지 보수성</h3>
<p>애플리케이션의 구조를 명확하게 하고, 코드의 유지 보수성을 향상시켜줌</p>
<h3 id="코드-컨벤션과-문서화">코드 컨벤션과 문서화</h3>
<p>일관된 코드 컨벤션을 제공하고, 문서화가 잘 되어 있어 새로운 개발자들도 쉽게 학습하고 적용할 수 있음</p>
<h3 id="커뮤니티와-지원">커뮤니티와 지원</h3>
<p>널리 사용되는 라이브러리로 활발한 커뮤니티와 다양한 자료를 통해 지원을 받을 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Optional & Closure]]></title>
            <link>https://velog.io/@dohy-9443/Optional-Closure</link>
            <guid>https://velog.io/@dohy-9443/Optional-Closure</guid>
            <pubDate>Tue, 16 Apr 2024 04:14:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>야곰 유튜브와 제가 따로 보는 인강 chat GPT 설명을 인용하여 정리해보았습니다.</p>
</blockquote>
<h1 id="optional">Optional</h1>
<pre><code class="language-swift">let num: Int
num = 123

print(num)

let num2: Int
// num = 123

print(num2) // Constant &#39;num2&#39; used before being initialized
</code></pre>
<blockquote>
<p><em>Constant &#39;num2&#39; used before being initialized
초기화를 하지않고 num2 를 사용할 수 없다.</em></p>
</blockquote>
<h3 id="q--그냥-항상-값을-저장하면-안됨">Q : 그냥 항상 값을 저장하면 안됨?</h3>
<p><em><strong>A : 예를 들어 파일에 어떤 숫자가 있고 파일을 읽어서 값을 저장한다고 생각해봄
지정한 위치에 파일이 있고 파일안에 필요한 값이 필요한 형태로 저장되어있다면 문제가 없다.
하지만 이중에 하나라도 조건이 맞지않는다면 값을 저장할 수 없게된다.
이번엔 서버에 값이 저장되있다고 생각해봄
서버가 정상적으로 실행되고 있고, 아이폰도 네트워크에 연결되어있고, 전달되는 데이터도 올바른 형태로 되어있다면 문제 없다. 하지만 하나라도 조건에 맞지않다면 값을 저장할 수 없다.
어쨌든 값을 저장하지않고 초기화하는 수단이 필요한데 그게 바로 옵셔널이다.</strong></em></p>
<blockquote>
<p><strong>Optional: 선택적인 , 의무적이지 않은</strong></p>
</blockquote>
<p>여기서는 값을 저장하지 않아도 되는 이라고 해석한다.</p>
<pre><code class="language-swift">let num: Int? // Optional Int
num = nil // nil 을 저장하는 건 값을 저장하지않는 것과 같다.</code></pre>
<p>이렇게 type 뒤에 <code>?</code> 를 작성하면 된다.</p>
<p>값을 저장해도되고 안해도 된다. </p>
<pre><code class="language-swift">let count: Int? = nil // count가 없다
let count: Int? = 0 // count가 0이다</code></pre>
<p>이 차이가 중요하다고 함 (<code>0</code>도 값이고 숫자니까)</p>
<pre><code class="language-swift">let num: Int = 123
print(num) // 123

let optionalNum: Int? = 123
print(optionalNum) // Optional(123)</code></pre>
<p>Optional Type 은 값이 포장되어있다고 생각하면 편하다. 그래서 <strong><em>Unwrapping(추출)</em></strong> 을 해줘야한다.</p>
<p>이제 저 포장되어있는 값을 추출해야하는데</p>
<pre><code class="language-swift">print(optionalNum!) // 강제 추출 , Forced Unwrapping</code></pre>
<p>이렇게하면된다. 근데 이건 값이 확실히 있을 때 한다. 만약 값이 없다면</p>
<pre><code class="language-swift">let optionalNum: Int? = nil
print(optionalNum!) // 강제 추출 , Forced Unwrapping</code></pre>
<blockquote>
<p><strong>F<em>atal error: Unexpectedly found nil while unwrapping an Optional value
옵셔널값을 unwrapping 하는 동안 예상치 못하게 nil 을 발견했다.</em></strong></p>
</blockquote>
<h3 id="optional-binding">Optional Binding</h3>
<pre><code class="language-swift">if let name: Type = OptionalExperssion {
    statements
}

while let name: Type = OptionalExperssion {
    statements
}

guard let name: Type = OptionalExpression else {
    statements
}</code></pre>
<pre><code class="language-swift">1. 
let str: String? = &quot;123&quot;

// optional binding
if let str = str { // if let 바인딩할 상수의 이름 = 앞에서 선언했던 옵셔널상수의 이름
  if let num = Int(str) { // 여기에 들어있는 str 은 바인딩할 상수의 이름 이거임
    print(num)
  } else {
    print(&quot;타입 컨버전 실패&quot;)
  }
} else {
  print(&quot;옵셔널 바인딩 실패&quot;)
}

// 123

2.
let str: String? = nil

// optional binding
if let str = str { // if let 바인딩할 상수의 이름 = 앞에서 선언했던 옵셔널상수의 이름
  if let num = Int(str) { // 여기에 들어있는 str 은 바인딩할 상수의 이름 이거임
    print(num)
  } else {
    print(&quot;타입 컨버전 실패&quot;)
  }
} else {
  print(&quot;옵셔널 바인딩 실패&quot;)
}

// 옵셔널 바인딩 실패

3.
let str: String? = &quot;nil&quot;

// optional binding
if let str = str { // if let 바인딩할 상수의 이름 = 앞에서 선언했던 옵셔널상수의 이름
  if let num = Int(str) { // 여기에 들어있는 str 은 바인딩할 상수의 이름 이거임
    print(num)
  } else {
    print(&quot;타입 컨버전 실패&quot;)
  }
} else {
  print(&quot;옵셔널 바인딩 실패&quot;)
}

// 타입 컨버전 실패</code></pre>
<h1 id="closure">Closure</h1>
<p>함수는 반복적으로 사용할 수 있는 코드 블럭이다. 클로져도 마찬가지로 코드 블럭이다. 함수가 반복적으로 사용하는 것에 포커스가 맞춰져있다면 클로져는 코드 블럭을 다른 곳으로 전달하는데에 포커스가 맞춰져있다.</p>
<blockquote>
<p>Function &gt; Named Closure
Closure &gt; Unnamed Closure</p>
</blockquote>
<blockquote>
<p>클로저는 함수와 비슷하게 동작하지만, 보다 간결하고 유연한 문법을 제공해서 코드를 작성할 때 많은 편의성을 제공한다.
일급 시민(first-citizen) 이고 변수 , 상수 등으로 저장 , 전달인자로 전달이 가능하다.</p>
</blockquote>
<h3 id="closure-정의-문법">Closure 정의 문법</h3>
<pre><code class="language-swift">{ (매개변수 목록) -&gt; 반환타입 in 
    실행코드
}</code></pre>
<p>함수를 사용한다면</p>
<pre><code class="language-swift">func sumFunction(a: Int, b: Int) -&gt; Int {
    return a + b
}

var sumResult: Int = sumFunction(a: 1, b: 2)

print(sumResult) // 3</code></pre>
<p>클로져의 사용</p>
<pre><code class="language-swift">var sum: (Int, Int) -&gt; Int = { (a: Int, b: Int) in
    return a + b
}

sumResult = sum(1, 2)
print(sumResult) // 3</code></pre>
<p>당연히 함수는 클로져의 일종이고 위에 만든 <code>sum</code>변수에는 함수도 할당할 수 있다.</p>
<pre><code class="language-swift">sum = sumFunction(a:b:)

sumResult = sum(1, 2)
print(sumResult) // 3</code></pre>
<h3 id="함수의-전달인자로서의-클로져">함수의 전달인자로서의 클로져</h3>
<pre><code class="language-swift">let add: (Int, Int) -&gt; Int
add = { (a: Int, b: Int) -&gt; Int in
    return a + b
}

let substract: (Int, Int) -&gt; Int
substract = { (a: Int, b: Int) -&gt; Int in
    return a - b
}

let divide: (Int, Int) -&gt; Int
divide = { (a: Int, b: Int) -&gt; Int in
    return a / b
}

func calculate(a: Int, b: Int, method: (Int, Int) -&gt; Int) -&gt; Int {
    return method(a, b)
}

var calculated: Int
calculated = calculate(a: 50, b: 10, method: add)
print(calculated) // 60

calculated = calculate(a: 50, b: 10, method: substract)
print(calculated) // 40

calculated = calculate(a: 50, b: 10, method: divide)
print(calculated) // 5

calculated = calculate(a: 50, b: 10, method: { (left: Int, right: Int) -&gt; Int in
    return left * right
})

print(calculated) // 500</code></pre>
<h3 id="후행-클로져">후행 클로져</h3>
<p>클로져가 함수의 마지막 전달인자라면 마지막 매개변수 이름을 생략한 후 함수 소괄호 외부에 클로져를 구현할 수 있다.</p>
<pre><code class="language-swift">var result: Int

result = calculate(a: 10, b: 10) { (left: Int, right: Int) -&gt; Int in
    return left + right
}

print(result) // 20</code></pre>
<h3 id="반환타입-생략">반환타입 생략</h3>
<p><code>calculate</code> 함수의 <code>method</code> 매개변수는 <code>Int</code> 타입을 반환할 것이라는 사실을 컴파일러도 알기 때문에 굳이 클로져에서 반환타입을 명시해 주지 않아도 된다. 대신 <code>in</code> 키워드는 생략할 수 없다.</p>
<pre><code class="language-swift">result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) in
    return left + right
})

print(result) // 20</code></pre>
<p>후행 클로져와 함께 사용가능하다.</p>
<pre><code class="language-swift">result = calculate(a: 10, b: 10) { (left: Int, right: Int) in
    return left + right
}

print(result) // 20</code></pre>
<h3 id="단축-인자이름">단축 인자이름</h3>
<p>클로져의 매개변수 이름이 굳이 불필요하다면 단축 인자이름을 활용할 수 있다.
단축 인자이름은 클로저의 매개변수의 순서대로 <code>$0</code> , <code>$1</code> … 처럼 표현한다</p>
<pre><code class="language-swift">result = calculate(a: 10, b: 10, method: { in
    return $0 + $1
})

print(result) // 20</code></pre>
<p>당연히 후행 클로져와 함께 사용할 수 있다.</p>
<pre><code class="language-swift">result = calculate(a: 10, b: 10) { in
    return $0 + $1
}

print(result) // 20</code></pre>
<h3 id="암시적-반환-표현">암시적 반환 표현</h3>
<p>클로져가 반환하는 값이 있다면 클로져의 마지막 줄의 결과 값은 암시적으로 반환 값으로 취급한다.</p>
<pre><code class="language-swift">result = calculate(a: 10, b: 10) { in
    $0 + $1
}

print(result) // 20</code></pre>
<p>당연하게도 간결하게 한 줄로 표현해 줄 수 있다.</p>
<pre><code class="language-swift">result = calculate(a: 10, b: 10) { $0 + $1 }

print(result) // 20</code></pre>
<h3 id="축약하지-않은-클로져-문법과-축약-후-문법-비교">축약하지 않은 클로져 문법과 축약 후 문법 비교</h3>
<pre><code class="language-swift">result = calculate(a: 10, b: 10, method: { (left: Int, right: Int) -&gt; Int in
    return left + right
})

result = calculate(a: 10, b: 10) { $0 + $1 }

print(result) // 20</code></pre>
<p>이렇게나 줄일 수 있다.</p>
<h3 id="또다른-예">또다른 예</h3>
<p>숫자 배열을 정렬하는 간단한 함수를 작성한다고 가정해보면, 일반적으로 이와같이 작성이 가능하다.</p>
<pre><code class="language-swift">let numbers = [5, 2, 8, 1, 9]

func ascending(_ a: Int, _ b: Int) -&gt; Bool {
    return a &lt; b
}

let sortedNumbers = numbers.sorted(by: ascending)
print(sortedNumbers) // [1, 2, 5, 8, 9]</code></pre>
<p>클로져를 사용하게 되면</p>
<pre><code class="language-swift">let numbers = [5, 2, 8, 1, 9]

let sortedNumbers = numbers.sorted(by: { (a: Int, b: Int) -&gt; Bool in
    return a &lt; b
})
print(sortedNumbers) // [1, 2, 5, 8, 9]
</code></pre>
<p>클로저는 중괄호로 둘러싸여 있으며, <strong><code>sorted(by:)</code></strong> 메서드에 직접 전달된다. 이 클로저는 두 개의 매개변수 <strong><code>a</code></strong>와 <strong><code>b</code></strong>를 취하고, bool 값을 반환한다. 클로저의 내용은 함수와 동일하지만, 함수 이름이 없다.</p>
<p>Swift는 타입 추론을 통해 매개변수 및 반환 유형을 자동으로 추측할 수 있기 때문에, 종종 이러한 유형 선언을 생략할 수 있다.</p>
<pre><code class="language-swift">let sortedNumbers = numbers.sorted(by: { a, b in
    return a &lt; b
})</code></pre>
<p>단일 표현식을 사용하는 클로저의 경우 <code>return</code> 카워드를 생략할 수 있다.</p>
<pre><code class="language-swift">let sortedNumbers = numbers.sorted(by: { a, b in a &lt; b })</code></pre>
<p>여기서 더 간단하게 하자면</p>
<pre><code class="language-swift">let sortedNumbers = numbers.sorted(by: &lt;)</code></pre>
<blockquote>
<p>클로저는 코드를 간결하고 읽기 쉽게 만들어주며, Swift에서 함수형 프로그래밍을 지원하는 중요한 도구이다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Android App requires Multidex support Error]]></title>
            <link>https://velog.io/@dohy-9443/Android-App-requires-Multidex-support-Error</link>
            <guid>https://velog.io/@dohy-9443/Android-App-requires-Multidex-support-Error</guid>
            <pubDate>Mon, 29 May 2023 12:56:11 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dohy-9443/post/30a654ae-1fed-43a6-b3c8-52b0375f1eb7/image.png" alt=""></p>
<p>이번에 에뮬레이터로 실행해보려고했는데 이런 에러가 나왔다. 그래서 혹시나 다음에도 같은 에러가 나지않을까해서 해결방법을 작성하려고한다.</p>
<p>android / app / build.gradle 에서
<img src="https://velog.velcdn.com/images/dohy-9443/post/78862450-e9cb-48ab-9a4c-06fe2a9a8f60/image.png" alt=""></p>
<p>이렇게 48번째 줄을 수정하고 52번째 줄을 추가해주면 에러없이 실행된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error (Xcode): ../../../../../.pub-cache/hosted/pub.dev/file-6.1.2/lib/src/interface/file.dart:15:16: Error: The method 'File.create' has fewer named arguments than those of overridden method 'File.create'.]]></title>
            <link>https://velog.io/@dohy-9443/Error-Xcode-...........pub-cachehostedpub.devfile-6.1.2libsrcinterfacefile.dart1516-Error-The-method-File.create-has-fewer-named-arguments-than-those-of-overridden-method-File.create</link>
            <guid>https://velog.io/@dohy-9443/Error-Xcode-...........pub-cachehostedpub.devfile-6.1.2libsrcinterfacefile.dart1516-Error-The-method-File.create-has-fewer-named-arguments-than-those-of-overridden-method-File.create</guid>
            <pubDate>Tue, 09 May 2023 05:09:58 GMT</pubDate>
            <description><![CDATA[<p>이번에 인강을 보며 해당 인강 챕터 파일을 다운받아 실행하는데 </p>
<blockquote>
<p>Error (Xcode): ../../../../../.pub-cache/hosted/pub.dev/file-6.1.2/lib/src/interface/file.dart:15:16: Error: The method &#39;File.create&#39; has fewer named arguments than those of overridden method &#39;File.create&#39;.
Could not build the application for the simulator.
Error launching application on iPhone 13 mini.</p>
</blockquote>
<p>해당 에러가 발생했다. </p>
<p>해결하기위해 검색하던중 </p>
<blockquote>
<p><a href="https://cishome.tistory.com/270">https://cishome.tistory.com/270</a></p>
</blockquote>
<p>이 블로그를 참고했고, 나중에 또 해당 에러가 발생할까봐 참고하기위해 작성한다.</p>
<p>해결방법 순서는 터미널에 순서대로 4개의 명령어를 입력하면된다.</p>
<pre><code>flutter pub outdated
flutter pub upgrade
flutter channel master
flutter upgrade</code></pre><p>이 4개의 명령어를 순서대로 입력하니 문제없이 잘 해결되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[svg  gradient]]></title>
            <link>https://velog.io/@dohy-9443/svg-gradient</link>
            <guid>https://velog.io/@dohy-9443/svg-gradient</guid>
            <pubDate>Sun, 07 May 2023 07:42:02 GMT</pubDate>
            <description><![CDATA[<p>이번에 그라데이션이 들어가는 svg를 적용중이였는데 아니이게 코드가 잘못된건 분명히 아닌데 일반 단색 svg는 적용이 되었지만 이상하게도 그라데이션이 들어간 svg는 적용이 안됐다. 그래서 열심히 구글링하고 찾던중 발견해서 작성하려고한다.</p>
<p>일단 나의 경우는 예를 들어 abcdefg 라고 알파벳이 적인 이미지라고 했을 때 abcd는 단색이고 efg는 그라데이션이 적용된 이미지였다.</p>
<p>우선 사용한 라이브러리는</p>
<blockquote>
<p>flutter_svg: ^1.1.6</p>
</blockquote>
<p>이것이고 코드는 </p>
<pre><code class="language-dart">SvgPicture.asset(
  &#39;경로&#39;,
)</code></pre>
<p>이렇게 작성했었다. 근데 결과는 abcd까지는 보이는데 efg는 안보이는 상황이였다.
그 결과 해결방법은 일단 한가지만 발견했다. 근데 내 경우가 딱 맞는 경우여서 다른 경우라면 해결방법을 더 찾아봐야 할 것같다. 일단 이렇게 해결했다.
svg파일을 열어 코드를 보는데
코드가 만약</p>
<pre><code class="language-jsx">&lt;svg&gt;
    ...
    &lt;defs&gt;
        ...
    &lt;/defs&gt;
&lt;/svg&gt;</code></pre>
<p>이렇게 defs태그가 밑에 있다면 svg 열리는 태그 바로 밑으로 옮기면 잘 출력된다.</p>
<pre><code class="language-jsx">&lt;svg&gt;
    &lt;defs&gt;
        ...
    &lt;/defs&gt;
  ...
&lt;/svg&gt;</code></pre>
<p>이렇게 하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이미지 좌우 반전시키기]]></title>
            <link>https://velog.io/@dohy-9443/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A2%8C%EC%9A%B0-%EB%B0%98%EC%A0%84%EC%8B%9C%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@dohy-9443/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A2%8C%EC%9A%B0-%EB%B0%98%EC%A0%84%EC%8B%9C%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Sun, 07 May 2023 07:33:43 GMT</pubDate>
            <description><![CDATA[<p>이번에 애니메니션 적용을 해보려고 <strong>animate_do</strong> 를 받아 적용중인데 </p>
<blockquote>
<p><a href="https://pub.dev/packages/animate_do">https://pub.dev/packages/animate_do</a></p>
</blockquote>
<p>그중에서 Swing 이라는 기능을 적용하고있었다.
두개의 이미지를 양쪽에 두고 가운데서 크로스(?)되는 상상을하며 작업했는데 이게 적용하면 Swing이 왼쪽부터 진자운동하듯이 동작을 했다.
그래서 오른쪽 이미지를 반전시키면 되지않을까하며 찾다가 발견했다.</p>
<blockquote>
<p><a href="https://yj95.tistory.com/259">https://yj95.tistory.com/259</a></p>
</blockquote>
<p>이분 블로그를 참고했다. 코드는</p>
<pre><code class="language-dart">import &#39;dart:math&#39; as math;

Transform(
    aligment: Alignment.center,
    transform: Matrix4.rotationY(math.pi),
    child: Swing(
    child: Align(
      alignment: Alignment.topCenter,
      child: SvgPicture.asset(&#39;assets/logo/fork.svg&#39;)
    ),
  ),
)</code></pre>
<p>이렇게하면 좌우반전이 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter widget]]></title>
            <link>https://velog.io/@dohy-9443/flutter-widget</link>
            <guid>https://velog.io/@dohy-9443/flutter-widget</guid>
            <pubDate>Sun, 02 Apr 2023 13:10:19 GMT</pubDate>
            <description><![CDATA[<p>플러터 관련 글들을 보다가 widget에 관한 글을 보게되어 정리해놓으려고 한다.</p>
<blockquote>
<p>Q. 플러터에서 위젯은 무엇인가요?
A. 플러터에서 위젯은 사용자에게 보이는 인터페이스를 결정하는 부품이다. 위젯의 조합으로 다양한 UI를 보여줄 수 있다. 화면에 보이거나 보이지 않는 부분도 모두 위젯으로 이루어져 있기 때문에 굉장히 중요하다.</p>
</blockquote>
<p>플러터 앱의 각 요소는 위젯이다. 화면에 보여지는 뷰는 앱을 만드는데 사용되는 위젯의 종류와 어떻게 위젯을 배치하는지 등에 따라 달라진다.</p>
<blockquote>
<p>Q. 위젯의 타입에는 무엇이 있나요?
A. 위젯 타입은 대표적으로 두가지로 나뉜다.</p>
</blockquote>
<ul>
<li>StatelessWidget : 상태를 저장하지않는 위젯. 상태가 없어 사용자의 이벤트 등으로 인해 바뀌는게 없기 때문에 화면 변화가 이루어지지 않는다. 정적인 화면을 보여줄 때 사용하는 위젯이다. ex) Icon , Text 등과 같은 위젯들이 대표적이다.</li>
<li>StatefulWidget : 상태를 저장하는 위젯. 사용자의 이벤트나 데이터 변경을 모니터링하다가 UI를 업테이트 할 수 있다. 동적인 화면을 보여줄 때 사용한다. ex) CheckBox, Radio, Slider, TextField 등과 같은 위젯들이 대표적이다.</li>
</ul>
<p>statelessWidget 기본 구조</p>
<pre><code class="language-dart">class StatelessWidgetScreen extends StatelessWidget {
  const StatelessWidgetScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}</code></pre>
<p>statefulWidget 기본 구조</p>
<pre><code class="language-dart">class StatefulWidgetScreen extends StatefulWidget {
  const StatefulWidgetScreen({Key? key}) : super(key: key);

  @override
  State&lt;StatefulWidgetScreen&gt; createState() =&gt; _StatefulWidgetScreenState();
}

class _StatefulWidgetScreenState extends State&lt;StatefulWidgetScreen&gt; {
  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}</code></pre>
<p>기본구조는 이렇게 이루어져있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter 반응형 웹 만들어보기]]></title>
            <link>https://velog.io/@dohy-9443/flutter-%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@dohy-9443/flutter-%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 02 Apr 2023 12:02:36 GMT</pubDate>
            <description><![CDATA[<p>flutter도 웹을 만들 수 있다고 하여 flutter 로 웹 만드는 것을 찾아봤다. 그러던 중 라이브러리 하나를 발견했는데</p>
<pre><code class="language-dart">responsive_builder: ^0.6.4</code></pre>
<p><a href="https://pub.dev/packages/responsive_builder">responsive_builder | Flutter Package</a></p>
<p>이 라이브러리를 발견했다. </p>
<p>우선 검색해보면서 하나하나 직접 쳐보면서 해보기로했다.</p>
<pre><code class="language-dart">ScreenTypeLayout.builder(
  breakpoints: ScreenBreakpoints(desktop: 1024, tablet: 768, watch: 250),
  mobile: (_) =&gt; OrientationLayoutBuilder(
    portrait: (context) =&gt; MobileScreen(),
    landscape: (context) =&gt; MobileScreen()
  ),
  tablet: (_) =&gt; TabletScreen(),
  desktop: (_) =&gt; DesktopScreen(),
)</code></pre>
<ol>
<li>breakpoints </li>
</ol>
<pre><code class="language-dart">breakpoints: ScreenBreakpoints(desktop: 1024, tablet: 768, watch: 250)</code></pre>
<p>이렇게 분기를 정해줄 수 있다. </p>
<p>1024보다 크면 pc , 768보다 크면 teblet, 250보다 크면 mobile , 그 밑이면 watch 이렇게 </p>
<ol>
<li>mobile , tablet , desktop</li>
</ol>
<pre><code class="language-dart">mobile: (_) =&gt; OrientationLayoutBuilder(
  portrait: (context) =&gt; MobileScreen(),
  landscape: (context) =&gt; MobileScreen()
),
tablet: (_) =&gt; TabletScreen(),
desktop: (_) =&gt; DesktopScreen(),</code></pre>
<p>이렇게적으면 해당 분기마다 맞게 출력된다.</p>
<pre><code class="language-dart">OrientationLayoutBuilder(
  portrait: (context) =&gt; MobileScreen(),
  landscape: (context) =&gt; MobileScreen()
)</code></pre>
<p><code>OrientationLayoutBuilder</code> 이건 가로 및 세로 위젯에 대한 빌더 기능 제공한다. </p>
<p>웹 개발을 했었을 때 화면구성이 fullscreen이 아니라면 대부분 가운데 정렬로 레이아웃이 구성되게 작업했었다. </p>
<p><img src="https://velog.velcdn.com/images/dohy-9443/post/5b49f776-d2ed-4f28-800b-53e43d1f7625/image.png" alt=""></p>
<p>이런식으로 레이아웃을 화면사이즈에 가운데에 위치하게 한 후 작업을 진행했다. 그렇다고해서 안에 구성들도 다 가운데 정렬로 진행하는 것은 아니다.</p>
<p>가운데에 맞춰 작업하려면 </p>
<pre><code class="language-dart">class CenteredView extends StatelessWidget {
  final Widget child;
  const CenteredView({Key?key, required this.child}) : super(key:key);

@override
Widget build(BuildContextcontext) {
    return Container(
padding: const EdgeInsets.symmetric(horizontal: 70),
alignment: Alignment.topCenter,
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 1200),
child: child,
      ),
    );
  }
}</code></pre>
<p>이렇게 위젯을 만든 후 작업하면 편하다. </p>
<p>LayoutBuilder 라는 위젯도 있다.</p>
<pre><code class="language-dart">LayoutBuilder(
    builder: (context, constraints) {
      if (constraints.maxWidth &gt; 600) {
        return Center(child: Text(&#39;600보다 큼 : ${constraints.maxWidth}&#39;));
      } else {
        return Center(child: Text(&#39;600보다 작음 : ${constraints.maxWidth}&#39;));
      }
    }
)</code></pre>
<p>이런식으로 현재 화면사이즈에 맞게 다른 위젯을 보여줄 수 있다.</p>
<p>앞으로도 더 공부하면서 내용을 더 추가하려고한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter 작업하던 중 http error]]></title>
            <link>https://velog.io/@dohy-9443/flutter-%EC%9E%91%EC%97%85%ED%95%98%EB%8D%98-%EC%A4%91-http-error</link>
            <guid>https://velog.io/@dohy-9443/flutter-%EC%9E%91%EC%97%85%ED%95%98%EB%8D%98-%EC%A4%91-http-error</guid>
            <pubDate>Tue, 07 Mar 2023 08:03:40 GMT</pubDate>
            <description><![CDATA[<p>이번에 같이 flutter 준비하는 친구랑 backend 개발자 형이랑 같이 포트폴리오용 프로젝트 작업하던 중, 글 등록 기능을 작업하다가 발생했던 에러이다.</p>
<pre><code class="language-dart">final jsonStr = await client.post(
        Uri.parse(&#39;$ip/consolations?problemId=$problemId&#39;),
        body: json
      );</code></pre>
<p>이 코드가 내가 작성했던 코드인데 아래와 같이 에러가 났었다.</p>
<p>&quot;status&quot;:415,&quot;error&quot;:&quot;Unsupported Media Type&quot;,&quot;message&quot;:&quot;Content type &#39;text/plain;charset=utf-8&#39; not supported for bodyType=com.hakuna.entity.Consolation&quot;</p>
<p>이렇게 에러가 났었다. 그래서 찾아보던 중 </p>
<blockquote>
<p><a href="https://leeseongho.tistory.com/70">https://leeseongho.tistory.com/70</a></p>
</blockquote>
<p>이 블로그를 봤고, 이 블로그처럼 </p>
<pre><code class="language-dart">final jsonStr = await client.post(
  Uri.parse(&#39;$ip/consolations?problemId=$problemId&#39;),
    headers: {
      &quot;content-type&quot; : &quot;application/json&quot;
    },
    body: jsonEncode(json)
);</code></pre>
<p>위와 같이 수정하니 에러없이 잘 성공했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[unhandled exception: [core/no-app] no firebase app '[default]' has been created - call firebase.initializeapp()]]></title>
            <link>https://velog.io/@dohy-9443/unhandled-exception-coreno-app-no-firebase-app-default-has-been-created-call-firebase.initializeapp</link>
            <guid>https://velog.io/@dohy-9443/unhandled-exception-coreno-app-no-firebase-app-default-has-been-created-call-firebase.initializeapp</guid>
            <pubDate>Sun, 05 Mar 2023 07:04:08 GMT</pubDate>
            <description><![CDATA[<p>이번에 flutter 와 firebase 연동하며 작업하는 것을 공부하고 있던 와중에 
<img src="https://velog.velcdn.com/images/dohy-9443/post/a7ef0c91-a431-4033-a1a3-ff6f43abb3f8/image.png" alt="">
이런 에러가 났다. 그래서 구글에 검색하며 해결방법을 찾던 와중</p>
<pre><code class="language-dart">void main() {
  WidgetsFlutterBinding.ensureInitialized(); // &lt;- 이거

  runApp(const MyApp());
}</code></pre>
<p>이 코드를 써주면 된다는 글을 봤고 바로 적용 해보았지만 되지않았다.</p>
<pre><code class="language-dart">  WidgetsFlutterBinding.ensureInitialized();</code></pre>
<p>다시 열심히 찾아봤는데 </p>
<pre><code class="language-dart">void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(); // &lt;- 이거
  runApp(const MyApp());
}</code></pre>
<p>이걸 넣어주면 문제 없이 동작한다는 글을 보았고 바로 적용해서 다시실행해보았다.</p>
<pre><code class="language-dart">  await Firebase.initializeApp();</code></pre>
<p>문제 없이 잘 등록되었다.
<img src="https://velog.velcdn.com/images/dohy-9443/post/d20942ee-afae-449a-b834-66a93136acda/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MissingPluginException]]></title>
            <link>https://velog.io/@dohy-9443/MissingPluginException</link>
            <guid>https://velog.io/@dohy-9443/MissingPluginException</guid>
            <pubDate>Tue, 28 Feb 2023 06:50:17 GMT</pubDate>
            <description><![CDATA[<p>이번에 친구랑 같이 flutter project를 하고 있다.
그러던 도중 kakao 소셜로그인 기능을 작업을 하고 있었는데 </p>
<blockquote>
<p><em><strong>MissingPluginException(No implementation found for method getAll on channel...</strong></em></p>
</blockquote>
<p>이 에러가 나와버렸다. 
hot reload도 해봤고 에러코드가 있나 찾아보고 그랬는데 해결법은 오히려 정말 간단했다.</p>
<p>플러그인을 설치하고 hot reload가 안되는 경우가 종종 있다고하는데, 그때는 디버깅을 종료하고 다시 시작하면 해결된다고한다.
그래서 디버깅종료 후 다시 시작했더니 오류 없이 잘 출력되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[LateInitializationError]]></title>
            <link>https://velog.io/@dohy-9443/LateInitializationError</link>
            <guid>https://velog.io/@dohy-9443/LateInitializationError</guid>
            <pubDate>Thu, 23 Feb 2023 14:47:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/dohy-9443/post/487a11dd-b0d4-47c7-b04a-f38992d5821b/image.png" alt=""></p>
<p>이전 글에서 작성했는데 짱구 mbti 프로젝트를 작업하던 도중 에러가 발생했다.
이 에러는 처음 테스트를 할땐 나오지 않다가 홈으로 가기 버튼을 누른 후 다시 테스트를 진행하고, 결과페이지로 이동하게되면 나타나던 에러이다.
열심히 검색하고 이 방법, 저 방법 삽질을 했는데 그 과정중에 </p>
<p><em><a href="https://stackoverflow.com/questions/67040839/flutter-lateinitializationerror-field-name-has-not-been-initialized">https://stackoverflow.com/questions/67040839/flutter-lateinitializationerror-field-name-has-not-been-initialized</a></em></p>
<blockquote>
</blockquote>
<p><strong>I had the same problem of lateInitializationError I find out to use ? to avoid it. if some one is facing this problem instead of late try ?.</strong>
<em>저는 lateInitializationError와 동일한 문제를 가지고 있었습니다. 이 문제를 피하기 위해 ?를 사용했습니다. 만약 누군가가 늦은 시도 대신 이 문제에 직면한다면 ?</em></p>
<p>stackoverflow에서 위 글을 보았고, <strong>null</strong> 문제인가싶어 해당 코드를 조금 수정했었다.</p>
<pre><code class="language-dart">_resultDetail = _resultListModel.resultList.firstWhere((e) =&gt; e.type == mbti);</code></pre>
<p>위 코드를 아래로</p>
<pre><code class="language-dart">_resultDetail = _resultListModel.resultList.firstWhereOrNull((e) =&gt; e.type == mbti)!;</code></pre>
<p>수정했더니 똑같은 에러가 발생했다. 그 후 다시 검색해본 결과</p>
<p><a href="https://velog.io/@baekmoon1230/LateInitializationError-Field-%EB%B3%80%EC%88%98%EB%AA%85-has-not-been-initialized">https://velog.io/@baekmoon1230/LateInitializationError-Field-%EB%B3%80%EC%88%98%EB%AA%85-has-not-been-initialized</a></p>
<p><a href="https://www.fluttercampus.com/guide/241/lateinitializatioerror-field-has-not-been-initialized-error/">https://www.fluttercampus.com/guide/241/lateinitializatioerror-field-has-not-been-initialized-error/</a></p>
<p>이 블로그 글들을 보게 됐고, 초기값을 지정해줘야될거같아 코드를 다시 수정해보았다.</p>
<pre><code class="language-dart">late final ResultDetail _resultDetail;</code></pre>
<p>위 코드를 아래 코드로</p>
<pre><code class="language-dart">late ResultDetail _resultDetail = ResultDetail(type: &#39;&#39;, detail: ResultContent(name: &#39;&#39;, contents: &#39;&#39;, path: &#39;&#39;));</code></pre>
<p>수정 해보니 에러 없이 잘 구동되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[flutter Mock Service]]></title>
            <link>https://velog.io/@dohy-9443/flutter-Mock-Service</link>
            <guid>https://velog.io/@dohy-9443/flutter-Mock-Service</guid>
            <pubDate>Thu, 23 Feb 2023 09:45:00 GMT</pubDate>
            <description><![CDATA[<p>이번에 인강을 보면서 mock service를 사용하는 부분을 공부하게 되었다.
그러면서 이 기능을 내 개인 프로젝트에 적용해보면 어떨까 싶어서 사용해보았는데 그 부분을 정리해보려고 한다.</p>
<p>기존에 만들었었던 짱구 MBTI를 수정을 했다.</p>
<p>json 파일을 불러오거나 하는 방법을 몰랐었는데 이번 기회에 적용을 했다.
기존에 사용했던 mbti test용 데이터들을 하나하나 일일이 담아서 적용했는데 이번에 아얘 json 파일로 변경해 asset에 넣었다.
<img src="https://velog.velcdn.com/images/dohy-9443/post/4a9794fd-1a07-4559-9d6a-1dec6ebf4028/image.png" alt="">
그 후 pubspec.yaml 파일에 asset을 추가한다.
<img src="https://velog.velcdn.com/images/dohy-9443/post/6ce49828-c5be-4f6f-8a17-c2f8ecea5239/image.png" alt="">
모델 작업을 진행한다.</p>
<p><strong>mbti 문제 데이터</strong></p>
<pre><code class="language-dart">import &#39;package:json_annotation/json_annotation.dart&#39;;

part &#39;question_model.g.dart&#39;;

@JsonSerializable()
class QuestionListModel {
  @JsonKey(
    name: &#39;list&#39;
  )
  final List&lt;Question&gt; questionList;

  QuestionListModel({required this.questionList});

  factory QuestionListModel.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$QuestionListModelFromJson(json);
}

@JsonSerializable()
class Question {

  final String title;
  final String type;
  final String answerA;
  final String answerB;

  Question({
    required this.title,
    required this.type,
    required this.answerA,
    required this.answerB
  });

  factory Question.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$QuestionFromJson(json);
}</code></pre>
<p><strong>mbti 결과 데이터</strong> </p>
<pre><code class="language-dart">import &#39;package:json_annotation/json_annotation.dart&#39;;

part &#39;result_model.g.dart&#39;;

@JsonSerializable()
class ResultListModel {
  final List&lt;ResultDetail&gt; resultList;

  ResultListModel({
    required this.resultList
  });

  factory ResultListModel.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$ResultListModelFromJson(json);
}

@JsonSerializable()
class ResultDetail {
  final String type;
  final ResultContent detail;

  ResultDetail({
    required this.type,
    required this.detail
  });

  factory ResultDetail.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$ResultDetailFromJson(json);
}

@JsonSerializable()
class ResultContent {
  final String name;
  final String contents;
  final String path;

  ResultContent({
    required this.name,
    required this.contents,
    required this.path
  });

  factory ResultContent.fromJson(Map&lt;String, dynamic&gt; json) =&gt; _$ResultContentFromJson(json);
}</code></pre>
<p>위와같이 모델작업을 한 후 본격적으로 mock service 작업을 시작했다.</p>
<p>json data 들을 dart model 로 변경한 후에 데이터를 전달해줄거기 때문에 dart model 로 변경하는 시간이 조금 걸릴 수가 있다고 한다.</p>
<p>ex)</p>
<pre><code class="language-dart">final jsonString = await rootBundle.loadString(&#39;assets/data.json&#39;);</code></pre>
<p>이렇게하면 json 파일을 불러올 수 있다.
여기서 rootBundle을 사용하면 text assets을 로드 할 수 있다고 한다.</p>
<p>작성을 해보자면</p>
<pre><code class="language-dart">import &#39;dart:convert&#39;;

import &#39;package:chopper/chopper.dart&#39;;
import &#39;package:http/http.dart&#39; as http;
import &#39;package:flutter/services.dart&#39;;
import &#39;package:zzang_gu_mbti/domain/models/models.dart&#39;;

class QuestionMockService {
  late final QuestionListModel _questionListModel;

  void loadQuestion() async {
    final jsonStr = await rootBundle.loadString(&#39;asset/data/test.json&#39;);
    final json = jsonDecode(jsonStr);
    _questionListModel = QuestionListModel.fromJson(json);
  }

  Future&lt;Response&lt;Result&lt;QuestionListModel&gt;&gt;&gt; queryQuestion() {
    return Future.value(
      Response(
        http.Response(
          &#39;Dummy&#39;,
          200,
          request: null
        ),
        Success&lt;QuestionListModel&gt;(_questionListModel)
      )
    );
  }
}</code></pre>
<p>하나씩 보자면 </p>
<pre><code class="language-dart">void loadQuestion() async {
  final jsonStr = await rootBundle.loadString(&#39;asset/data/test.json&#39;);
  final json = jsonDecode(jsonStr);
  _questionListModel = QuestionListModel.fromJson(json);
}</code></pre>
<p>이렇게 작성해 json data를 불러오고 디코딩해준 후 맵핑한다.
그 후 서비스 코드를 작성하면</p>
<pre><code class="language-dart">Future&lt;Response&lt;Result&lt;QuestionListModel&gt;&gt;&gt; queryQuestion() {
  return Future.value(
    Response(
      // 여긴 base
      http.Response(
        &#39;Dummy&#39;, // body
        200, // status code
        request: null, // 안써서 null 로 한다고 함
      ), // 중요한건 이 Response 부분이 아니라
      // 여긴 body
      Success&lt;QuestionListModel&gt;(_questionListModel)  // 이 body 부분이 중요하다고 함
    )
  );
 }</code></pre>
<p>이렇게 작성 할 수 있다.
그리고 이 mock service로 작성한 코드를 Provider에 담아서 사용할 것이기 때문에 main.dart에서 </p>
<pre><code class="language-dart">Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      Provider(create: (_) =&gt; QuestionMockService()..loadQuestion(), lazy: false,),
      Provider(create: (_) =&gt; ResultMockService()..loadResult(), lazy: false,)
    ],
    child: MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        fontFamily: &#39;sunflower&#39;
      ),
      home: const HomeScreen(),
    ),
  );
}</code></pre>
<p>이렇게 작성해서 넣어주었다.
이제 불러와서 사용할 때는</p>
<pre><code class="language-dart">FutureBuilder&lt;Response&lt;Result&lt;QuestionListModel&gt;&gt;&gt;(
  future: Provider.of&lt;QuestionMockService&gt;(context).queryQuestion(),
  builder: (context, snapshot) {
    return ;
  }
),</code></pre>
<p>이런식으로 타입을 작성해주고 사용하면 됐다.</p>
]]></description>
        </item>
    </channel>
</rss>