<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>manjee-official.log</title>
        <link>https://velog.io/</link>
        <description>느긋하게 살자!</description>
        <lastBuildDate>Sun, 16 Jul 2023 05:59:08 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>manjee-official.log</title>
            <url>https://velog.velcdn.com/images/manjee-official/profile/3e3ad537-90b9-4516-ba2d-8200ee6dd740/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. manjee-official.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/manjee-official" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Jetpack Compose Lifecycle]]></title>
            <link>https://velog.io/@manjee-official/Jetpack-Compose-Lifecycle</link>
            <guid>https://velog.io/@manjee-official/Jetpack-Compose-Lifecycle</guid>
            <pubDate>Sun, 16 Jul 2023 05:59:08 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/manjee-official/post/a120188f-07a2-41de-8ddd-4de3afe6686d/image.png" alt=""></p>
<ul>
<li>출처: <a href="https://developer.android.com/jetpack/compose/lifecycle?hl=ko">https://developer.android.com/jetpack/compose/lifecycle?hl=ko</a></li>
</ul>
<h4 id="composable">Composable</h4>
<ul>
<li>컴포저블 함수에서 사용되는 어노테이션</li>
<li>Jetpack Compose를 구성하는 함수의 이름</li>
<li>선언형 UI의 최소 단위<h4 id="composition">Composition</h4>
</li>
<li>컴포저블 함수가 화면에 그려지기 위한 전체의 과정</li>
<li>제일 처음 그려지는 1회의 과정<h4 id="recomposition">Recomposition</h4>
</li>
<li>컴포지션의 내용이 수정되어 화면의 내용을 고치는데 그 동작을 말함</li>
</ul>
<h3 id="lifecycle">Lifecycle</h3>
<ul>
<li>compose는 각 인스턴스가 각자의 생명주기를 가진다<h4 id="호출-사이트를-이용한-구분">호출 사이트를 이용한 구분</h4>
<pre><code>@Composable
fun LoginScreen(showError: Boolean) {
  if (showError) {
      LoginError()
  }
  LoginInput() // This call site affects where LoginInput is placed in Composition
}
</code></pre></li>
</ul>
<p>@Composable
fun LoginInput() { /* ... */ }</p>
<pre><code>![](https://velog.velcdn.com/images/manjee-official/post/1106822f-7ade-4c56-96be-01fd80e71795/image.png)
- showError이 true로 변경되어 리컴포지션이 발생해도 LoginInput이 새로운 인스턴스로 생성되지않는다
#### 실행 순서를 통한 구분</code></pre><p>@Composable
fun MoviesScreen(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            // MovieOverview composables are placed in Composition given its
            // index position in the for loop
            MovieOverview(movie)
        }
    }
}</p>
<p>```
<img src="https://velog.velcdn.com/images/manjee-official/post/84e99437-50dc-40d8-a47e-bc3766299c39/image.png" alt=""></p>
<ul>
<li>순서를 통해 데이터를 추가하는 경우 인스턴스가 순서에 맞게 생성된다</li>
<li><strong>중간에 내용이 추가되거나 제일 처음에 추가하는 경우에는 모든 인스턴스가 리컴포지션됨!</strong><h4 id="키-값을-이용한-구분">키 값을 이용한 구분</h4>
</li>
<li>식별자를 이용해 구분한다면 리스트에 제일 처음에 데이터가 추가되어도 전체적인 Recomposition을 하지 않는다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Unidirectional Data Flow (단방향 데이터 흐름)]]></title>
            <link>https://velog.io/@manjee-official/Unidirectional-Data-Flow-%EB%8B%A8%EB%B0%A9%ED%96%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84</link>
            <guid>https://velog.io/@manjee-official/Unidirectional-Data-Flow-%EB%8B%A8%EB%B0%A9%ED%96%A5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84</guid>
            <pubDate>Tue, 11 Jul 2023 10:12:50 GMT</pubDate>
            <description><![CDATA[<ul>
<li>Compose의 UI는 수정될 수 없음.</li>
<li>UI 상태가 변경될 때만 변경된 UI 트리를 다시 만듬.</li>
<li>ex) TextFiled는 값을 받고 onValueChange를 반환함. TextFiled가 상태를 같는게 아닌 다른 만들어놓은 값이 가짐</li>
<li>상태 반환 -&gt; 이벤트 반환. ** 단방향 데이터 흐름이 적합</li>
</ul>
<h3 id="이벤트-상태">이벤트, 상태</h3>
<h4 id="이벤트">이벤트</h4>
<ul>
<li>UI의 일부에서 이벤트를 발행해 위로 전달</li>
<li>뷰 모델에서 처리하는 버튼 클릭, 사용자 세션의 만료를 표현하는 앱의 다른 레이어에서 전달되는 이벤트<h4 id="상태-갱신">상태 갱신</h4>
</li>
<li>이벤트 핸들러가 상태를 바꿈<h4 id="상태-표시">상태 표시</h4>
</li>
<li>스테이트 홀더가 아래로 전달한 상태를 UI가 표시함</li>
</ul>
<h3 id="단방향-데이터-흐름의-장점">단방향 데이터 흐름의 장점</h3>
<ul>
<li>테스트 가능성: 상태를 표시하는 UI와 상태를 분리하면, 둘을 분리해 쉽게 테스트할 수 있음</li>
<li>상태 캡슐화: 상태가 한곳에서만 갱신될 수 있으면, 일관되지 않는 상태 때문에 발생하는 버그를 줄일 수 있음(SSOT)</li>
<li>UI 일관성: StateFlow와 LiveData와 같은 관측가능한 상태 홀더를 사용해 UI에 상태 갱신을 즉각 반영할 수 있다.</li>
</ul>
<h3 id="compose의-단방향-데이터-흐름">Compose의 단방향 데이터 흐름</h3>
<ul>
<li>컴포저블은 상태와 이벤트에 따라 동작</li>
<li>컴포즈는 값 홀더로 State 객체를 정의, 상태 값의 변경을 리컴포지션을 유발함</li>
<li>remember, rememberSaveable로 상태를 저장한다</li>
<li>mutableStateOf는 Compose가 관측할 수 있는 타입은 MutableState를 만듬 -&gt; 값이 변경되면 값을 읽는 모든 컴포지션 함수의 리컴포지션을 준비함</li>
<li>remember는 객체를 컴포지션에 저장하고, remember를 호출한 컴포저블이 컴포지션에서 제거될 떄 객체를 잊어버림</li>
<li>rememberSavable은 객체를 Bundle에 저장해 configuration이 변경되어도 유지함</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jetpack Compose 부수 효과]]></title>
            <link>https://velog.io/@manjee-official/Jetpack-Compose-%EB%B6%80%EC%88%98-%ED%9A%A8%EA%B3%BC</link>
            <guid>https://velog.io/@manjee-official/Jetpack-Compose-%EB%B6%80%EC%88%98-%ED%9A%A8%EA%B3%BC</guid>
            <pubDate>Sun, 09 Jul 2023 12:21:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>부수 효과란?</p>
</blockquote>
<ul>
<li>구성 가능한 함수의 범위 밖에서 발생하는 앱 상태에 관한 변경사항</li>
<li>컴포저블의 수명 주기 및 속성으로 인해 컴포저블에는 부수 효과가 없는 것이 좋으나 특정 상황에 필요한 경우가 있음</li>
</ul>
<h3 id="launchedeffect">LaunchedEffect</h3>
<ul>
<li>컴포저블의 범위에서 정지함수를 실행</li>
<li>컴포저블 내에서 안전하게 정지함수를 호출하기 위해 사용<h3 id="remembercoroutinescope">rememberCoroutineScope</h3>
</li>
<li>컴포지션 인식 범위를 확보하여 컴포저블 외부에서 코루틴 실행</li>
<li>컴포저블 외부에 있지만 컴포지션을 종료한 후 자동으로 취소되도록 범위가 지정된 코루틴을 실행하기 위해 사용<h3 id="rememberupdatedstate">rememberUpdatedState</h3>
</li>
<li>값이 변경되는 경우 다시 시작되지 않아야 하는 효과에서 값 참조</li>
<li>주요 매개변수 중 하나가 변경되면 LaunchedEffect가 다시 시작됨. 하지만 경우에 따라 효과에서 값이 변경되면 효과를 다시 시작하지 않을 값을 저장해놓을 수 있음.</li>
<li>상태 자체는 기억되어 있지만 값을 새로운 값으로 업데이트 되어 있음<h3 id="disposableeffect">DisposableEffect</h3>
</li>
<li>정리가 필요한 효과</li>
<li>키가 변경되거나 컴포저블이 컴포지션을 종료한 후 정리해야하는 경우 사용<h3 id="sideeffect">SideEffect</h3>
</li>
<li>Compose 상태를 비 Compose 코드에 게시<h3 id="producestate">produceState</h3>
</li>
<li>비 Compose 상태를 Compose 상태로 변환<h3 id="derivedstateof">derivedStateOf</h3>
</li>
<li>하나 이상의 상태 객체를 다른 상태로 변환</li>
<li>해당 함수를 사용하면 계산에서 사용되는 상태 중 하나가 변경 될 때만 계산이 실행됨</li>
<li>불 필요한 리 컴포지션 호출을 방지할 수 있다.<h3 id="snapshotflow">snapshotFlow</h3>
</li>
<li>Compose의 상태를 Flow로 전환 (State<T> 객체를 콜드 Flow로 변환)</li>
<li>이전에 방출한 값과 다를 경우에 방출한다.</li>
</ul>
<blockquote>
<p>출처 - <a href="https://developer.android.com/jetpack/compose/side-effects?hl=ko#snapshotFlow">https://developer.android.com/jetpack/compose/side-effects?hl=ko#snapshotFlow</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jetpack Compose Recomposition]]></title>
            <link>https://velog.io/@manjee-official/Jetpack-Compose-Recomposition</link>
            <guid>https://velog.io/@manjee-official/Jetpack-Compose-Recomposition</guid>
            <pubDate>Sun, 09 Jul 2023 04:50:41 GMT</pubDate>
            <description><![CDATA[<h3 id="컴포지션">컴포지션</h3>
<ul>
<li>앱의 UI를 설명하는 컴포저블의 트리 구조 (UI가 어떤 형태로 이루어졌는지 기술)<h3 id="초기-컴포지션">초기 컴포지션</h3>
</li>
<li>처음 Composable를 호출했을 때 만들어짐<h3 id="리컴포지션">리컴포지션</h3>
</li>
<li>초기 컴포지션 이후 UI의 상태가 바뀔 때 마다 다시 그려지는 과정</li>
<li>상태가 변경되면 리컴포지션이 트리거됨<h3 id="리컴포지션-스킵">리컴포지션 스킵</h3>
</li>
<li>두 인스턴스 equals 결과가 같을 경우</li>
<li>공개 프로퍼티가 변경되면 컴포지션에 알려야함</li>
<li>모든 공개 프로퍼티는 안정적이어야함</li>
<li>@Stable이 표기되지 않아도 Compose 컴파일러가 안정적이다라고 간주하는 공통 타입들 (모든 프리미티브 타입, 문자열, 모든 함수 타입 (람다) -&gt; 안정적이라고 추론할 수 없는 경우 @Stable 표기</li>
<li>MutableState는 안정적 (State의 value 프로퍼티 값 변경을 알려주기 때문 <h3 id="컴포즈-렌더링-단계">컴포즈 렌더링 단계</h3>
<img src="https://velog.velcdn.com/images/manjee-official/post/7c71b941-af8b-48c5-bf97-b1874163c89d/image.png" alt="렌더링 단계"></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart: Design]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Design</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Design</guid>
            <pubDate>Mon, 12 Jun 2023 00:42:54 GMT</pubDate>
            <description><![CDATA[<h2 id="names">Names</h2>
<h3 id="일관되게-사용하기">일관되게 사용하기</h3>
<ul>
<li>이름을 일관되게 작성한다, 내가 사용한 이름이 다른 외부에서도 사용했던 이름이라면 사용자 들은 해당 기능을 사용하기 전에 학습해야할 양을 줄일 수 있다<h3 id="약어-사용을-피한다">약어 사용을 피한다</h3>
</li>
<li>약어가 일반 단어보다 일반적인 경우가 아니라면 사용하지 않는 것이 좋고 사용하는 경우에는 대문자로 올바르게 표시한다<pre><code class="language-dart">// good case
pageCount
buildRectangles
IOStream
HttpRequest
</code></pre>
</li>
</ul>
<p>// bad case
numPages    // &quot;Num&quot; is an abbreviation of &quot;number (of)&quot;.
buildRects
InputOutputStream
HypertextTransferProtocolRequest</p>
<pre><code>### 이것이 무엇인지 가장 잘 보여줄 수 있는 명사를 마지막에 표시한다</code></pre><p>// good case
pageCount             // A count (of pages).
ConversionSink        // A sink for doing conversions.
ChunkedConversionSink // A ConversionSink that&#39;s chunked.
CssFontFaceRule       // A rule for font faces in CSS.</p>
<p>// bad case
numPages                  // Not a collection of pages.
CanvasRenderingContext2D  // Not a &quot;2D&quot;.
RuleFontFaceCss           // Not a CSS.</p>
<pre><code>### 코드가 문장처럼 읽히도록 하라
```dart
// good case
// &quot;If errors is empty...&quot;
if (errors.isEmpty) ...

// &quot;Hey, subscription, cancel!&quot;
subscription.cancel();

// &quot;Get the monsters where the monster has claws.&quot;
monsters.where((monster) =&gt; monster.hasClaws);

// bad case
// Telling errors to empty itself, or asking if it is?
if (errors.empty) ...

// Toggle what? To what?
subscription.toggle();

// Filter the monsters with claws *out* or include *only* those?
monsters.filter((monster) =&gt; monster.hasClaws);</code></pre><h3 id="명사구를-사용한다">명사구를 사용한다</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart - Asynchrony in usage]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Asynchrony-in-usage</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Asynchrony-in-usage</guid>
            <pubDate>Sun, 11 Jun 2023 13:18:55 GMT</pubDate>
            <description><![CDATA[<h3 id="future을-asyncawait와-같이-사용하는-것을-지향한다">Future을 async/await와 같이 사용하는 것을 지향한다</h3>
<ul>
<li><p>비동기 코드는 future와 같은 추상화를 사용하더라도 읽고 디버깅하기 쉽지않다 -&gt; async/await를 사용하면 문법의 가독성 향상과 비동기 코드에서 Dart의 모든 흐릅 제어문을 사용할 수 있다</p>
<pre><code class="language-dart">Future&lt;int&gt; countActivePlayers(String teamName) async {
try {
  var team = await downloadTeam(teamName);
  if (team == null) return 0;

  var players = await team.roster;
  return players.where((player) =&gt; player.isActive).length;
} catch (e) {
  log.error(e);
  return 0;
}
}</code></pre>
<h3 id="필요가-없다면-async를-사용하지-않는다">필요가 없다면 async를 사용하지 않는다</h3>
</li>
<li><p>메소드의 동작을 변경하지 않고 async를 생략할 수 있다면 사용하지 않는게 좋음</p>
<pre><code class="language-dart">Future&lt;int&gt; fastestBranch(Future&lt;int&gt; left, Future&lt;int&gt; right) {
return Future.any([left, right]);
}</code></pre>
<blockquote>
<p>async가 유용한 경우</p>
</blockquote>
</li>
<li><p>await를 사용할 때</p>
</li>
<li><p>에러를 비동기적으로 반환할 때, async와 throw를 함께 사용하는 것이 return Future.error(...)보다 간결함</p>
</li>
<li><p>값을 반환하고 있지만 명시적으로 Future 사용을 원할 때, async는 Future.value(...) 보다 간결함</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart - Variables, Members, Constructors in usage]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Variables-Members-Constructors-in-usage</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Variables-Members-Constructors-in-usage</guid>
            <pubDate>Sun, 11 Jun 2023 13:07:16 GMT</pubDate>
            <description><![CDATA[<h1 id="변수">변수</h1>
<h3 id="--지역-변수에-var과-final을-사용할-때-일관된-규칙을-따른다">- 지역 변수에 var과 final을 사용할 때 일관된 규칙을 따른다</h3>
<h3 id="--계산이-가능한-값들을-저장하는-것을-피한다">- 계산이 가능한 값들을 저장하는 것을 피한다</h3>
<h1 id="멤버">멤버</h1>
<h3 id="--필드에-불필요한-getter와-setter를-생성하지-않는다">- 필드에 불필요한 getter와 setter를 생성하지 않는다</h3>
<h3 id="--읽기-전용인-프로퍼티를-생성할-때-final-키워드-사용을-지향한다">- 읽기 전용인 프로퍼티를 생성할 때, final 키워드 사용을 지향한다</h3>
<ul>
<li>변수를 읽을 수만 있고 외부 코드로 수정할 수 없는 변수를 생성하는 쉬운 방법임</li>
</ul>
<blockquote>
<p>내부에서 할당하고 외부에서 접근할 수 있는 필드를 구성해야한다면 이 &quot;private 필드, public getter&quot; 패턴이 필요할 수 있지만 꼭 필요한 경우가 아니면 사용하지 않는 것이 좋다</p>
</blockquote>
<pre><code class="language-dart">// good case
class Box {
  final contents = [];
}

// bad case
class Box {
  Object? _contents;
  Object? get contents =&gt; _contents;
}</code></pre>
<h3 id="--간단한-멤버를-선언할-때--사용을-고려한다">- 간단한 멤버를 선언할 때 =&gt; 사용을 고려한다</h3>
<ul>
<li>=&gt;를 함수 표현식으로 사용하는 것 외에도, Dart에서는 멤버를 정의하는데 사용할 수 있다</li>
<li>해당 스타일은 계산을 수행하고 결과를 반환하는 단순한 멤버에게 적합함<pre><code class="language-dart">double get area =&gt; (right - left) * (bottom - top);
</code></pre>
</li>
</ul>
<p>String capitalize(String name) =&gt;
    &#39;${name[0].toUpperCase()}${name.substring(1)}&#39;;</p>
<pre><code>- 선언이 두 줄 이상이거나 중첨된 표현식일 경우 코드 블럭과 명령문을 사용하는 것이 좋다
```dart
// good case
Treasure? openChest(Chest chest, Point where) {
  if (_opened.containsKey(chest)) return null;

  var treasure = Treasure(where);
  treasure.addAll(chest.contents);
  _opened[chest] = treasure;
  return treasure;
}

// bad case
Treasure? openChest(Chest chest, Point where) =&gt; _opened.containsKey(chest)
    ? null
    : _opened[chest] = (Treasure(where)..addAll(chest.contents));</code></pre><h3 id="--named-생성자를-리디렉션하고-충돌을-피하는-경우를-제외하곤-this를-사용하지-않는다">- Named 생성자를 리디렉션하고 충돌을 피하는 경우를 제외하곤 this를 사용하지 않는다</h3>
<h3 id="--가능하다면-필드의-선언과-함께-초기화한다">- 가능하다면 필드의 선언과 함께 초기화한다</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart - Functions in usage]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Functions-in-usage</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Functions-in-usage</guid>
            <pubDate>Sun, 11 Jun 2023 12:39:54 GMT</pubDate>
            <description><![CDATA[<h3 id="함수의-이름과-함수를-바인드-하려면-함수-선언식을-사용">함수의 이름과 함수를 바인드 하려면 함수 선언식을 사용</h3>
<ul>
<li>함수 안에 함수가 선언되는 것을 흔한 일이며 흔히 이런 함수들은 콜백으로 사용되고 이름이 없음 -&gt; 함수 표현식을 이런 경우에 적합</li>
<li>함수에 이름을 부여하고 싶다면 변수에 람다를 바인딩 하지 말고 함수 선언문을 사용<pre><code class="language-dart">// good case
void main() {
void localFunction() {
  ...
}
}
</code></pre>
</li>
</ul>
<p>// bad case
void main() {
  var localFunction = () {
    ...
  };
}</p>
<pre><code>
### 람다 대신 Tear-off를 사용
- 함수, 메서드 또는 named 생성자를 참조할 때 괄호를 생략하면, Dart는 동일한 매개 변수 쌍을 사용하고 호출 시 기본 함수를 호출하는 tear-off 클로저를 사용한다
- 클로저가 받아들이는 매개변수와 동일한 매개변수를 사용하는 named 함수를 호출하는 클로저를 원한다면, 람다로 래핑하지 않는다
```dart
// good case
var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();

// 함수:
charCodes.forEach(print);

// 메서드:
charCodes.forEach(buffer.write);

// Named 생성자:
var strings = charCodes.map(String.fromCharCode);

// Unnamed 생성자:
var buffers = charCodes.map(StringBuffer.new);

// bad case
var charCodes = [68, 97, 114, 116];
var buffer = StringBuffer();

// 함수:
charCodes.forEach((code) {
  print(code);
});

// 메서드:
charCodes.forEach((code) {
  buffer.write(code);
});

// Named 생성자:
var strings = charCodes.map((code) =&gt; String.fromCharCode(code));

// Unnamed 생성자:
var buffers = charCodes.map((code) =&gt; StringBuffer(code));</code></pre><h3 id="named-매개변수와-해당-매개변수의-디폴트-값을-분리시키고-싶다면-을-사용">Named 매개변수와 해당 매개변수의 디폴트 값을 분리시키고 싶다면 <strong>=</strong>을 사용</h3>
<ul>
<li>dart3 이전에는 <strong>:</strong> 와 <strong>=</strong> 를 모두 사용할 수 있었지만 3에서는 =을 사용한다<pre><code class="language-dart">void insert(Object item, {int at = 0}) { ... }</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart - Collections in usage]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Collections-in-usage</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Collections-in-usage</guid>
            <pubDate>Sun, 11 Jun 2023 12:28:15 GMT</pubDate>
            <description><![CDATA[<h3 id="가능하다면-컬렉션-리터럴을-사용한다">가능하다면 컬렉션 리터럴을 사용한다</h3>
<ul>
<li>Dart는 세 개의 핵심 컬렉션 타입을 가지고 있음(List, Map, Set). Dart는 컬랙션 생성을 위한 더 좋은 내장 문법을 가지고 있음<pre><code class="language-dart">// good case
var points = &lt;Point&gt;[];
var addresses = &lt;String, Address&gt;{};
var counts = &lt;int&gt;{};
</code></pre>
</li>
</ul>
<p>// bad case
var addresses = Map&lt;String, Address&gt;();
var counts = Set<int>();</p>
<pre><code>-&gt; 해당 예제는 named 생성자에 적용하지 않음, List.from(), Map.fromIterable() 등 비슷한 생성자들은 저마다 사용법이 존재함 (List 클래스 또한 unnamed 생성자가 존재하지만, null safe Dart에서는 사용이 금지됨)
- 컬렉션 리터럴은 다른 컬렉션의 요소들을 추가할 때 전개 연산자에 접근이 가능하고 요소를 빌드하는 동안 [if 그리고 for]를 사용하여 흐름 제어를 수행할 수 있다
```dart
// good case
var arguments = [
  ...options,
  command,
  ...?modeFlags,
  for (var path in filePaths)
    if (path.endsWith(&#39;.dart&#39;)) path.replaceAll(&#39;.dart&#39;, &#39;.js&#39;)
];

// bad case
var arguments = &lt;String&gt;[];
arguments.addAll(options);
arguments.add(command);
if (modeFlags != null) arguments.addAll(modeFlags);
arguments.addAll(filePaths
    .where((path) =&gt; path.endsWith(&#39;.dart&#39;))
    .map((path) =&gt; path.replaceAll(&#39;.dart&#39;, &#39;.js&#39;)));</code></pre><h3 id="컬렉션이-비어있는지-확인할-때-length를-사용하지-않는다">컬렉션이 비어있는지 확인할 때 .length를 사용하지 않는다</h3>
<ul>
<li>Iterable을 사용한 컬렉션의 구현은 자신의 길이를 알고 있거나 상수 시간 안에 제공하도록 되어있지 않음. 단지 컬렉션에 포함된 것이 있는지 확인하기 위해 .length를 호출하는건 매우 느릴 수 있다</li>
<li>더 빠르고 가독성 높은 .isEmpty와 .isNotEmpty를 사용하는 것이 좋다<pre><code class="language-dart">// good case
if (lunchBox.isEmpty) return &#39;so hungry...&#39;;
if (words.isNotEmpty) return words.join(&#39; &#39;);
</code></pre>
</li>
</ul>
<p>// bad case
if (lunchBox.length == 0) return &#39;so hungry...&#39;;
if (!words.isEmpty) return words.join(&#39; &#39;);</p>
<pre><code>### Iterable.forEach()를 함수 리터럴과 함께 사용하는 것을 피한다
- Dart에서는 시퀀스를 반복할 때 for-in 루프를 사용하는 것이 관용적인 방법임
```dart
// good case
for (final person in people) {
  ...
}

// bad case
people.forEach((person) {
  ...
});</code></pre><h3 id="결과-값의-타입을-바꾸려는-것이-아니면-listfrom을-사용하지-않는다">결과 값의 타입을 바꾸려는 것이 아니면, List.from()을 사용하지 않는다</h3>
<ul>
<li><p>주어진 Iterable를 사용해서 동일한 요소를 가지는 새로운 List를 만드는 확실한 방법은 아래 두가지가 대표적</p>
<blockquote>
<p>var copy1 = iterable.toList();
var copy2 = List.from(iterable);</p>
</blockquote>
</li>
<li><p>위 둘의 명백한 차이는 첫 번째가 더 간결하고 원래 객체의 타입 인자를 보전한다는 것</p>
<pre><code class="language-dart">// Creates a List&lt;int&gt;:
var iterable = [1, 2, 3];
</code></pre>
</li>
</ul>
<p>// good casse
// Prints &quot;List<int>&quot;:
print(iterable.toList().runtimeType);</p>
<p>// bad case
// Prints &quot;List<dynamic>&quot;:
print(List.from(iterable).runtimeType);</p>
<pre><code>- 타입을 바꾸고 싶다면 List.from()을 호출하는 것이 좋음
```dart
// good case
var numbers = [1, 2.3, 4]; // List&lt;num&gt;.
numbers.removeAt(1); // Now it only contains integers.
var ints = List&lt;int&gt;.from(numbers);</code></pre><blockquote>
<p>타입에 대해 신경스찌 않거나, 단지 iterable를 복사하고 원래 타입을 보존하는 것이 목적이라면, toList()를 사용한다</p>
</blockquote>
<h3 id="컬렉션을-타입으로-필터하고-싶다면-wheretype을-사용한다">컬렉션을 타입으로 필터하고 싶다면, whereType()을 사용한다</h3>
<ul>
<li>두 개의 간접 계층과 중복된 런타임 검사를 동반하여 두 개의 wrapper가 생성되도록함</li>
<li>간결하면 불필요한 wrapper의 계층이 없이 원하는 타입의 Iterable을 생성할 수 있음<pre><code class="language-dart">var objects = [1, &#39;a&#39;, 2, &#39;b&#39;, 3];
var ints = objects.whereType&lt;int&gt;();</code></pre>
</li>
</ul>
<h3 id="코드의-주변에서-cast와-같은-역할을-하는-연산이-있다면-cast를-사용하지-마십시오">코드의 주변에서 cast()와 같은 역할을 하는 연산이 있다면, cast()를 사용하지 마십시오.</h3>
<ul>
<li>Iterable 또는 stream을 다룰 때, 종종 cast를 수행, 특정 타입 인자를 사용하여 객체를 생성할 때, cast()를 무작정 호출하기 보다 이미 존재하는 변형이 해당 타입으로의 cast를 수행하는지 먼저 확인한다</li>
<li>toList()를 호출하면, 해당 호출을 원하는 객체의 타입 T를 지정하여 List<T>.from()으로 대체함<pre><code class="language-dart">// good case
var stuff = &lt;dynamic&gt;[1, 2];
var ints = List&lt;int&gt;.from(stuff);
</code></pre>
</li>
</ul>
<p>// bad case
var stuff = <dynamic>[1, 2];
var ints = stuff.toList().cast<int>();</p>
<pre><code>- map()을 호출하면 원하는 타입을 명시적으로 타입 인자로 넘겨주어 해당 타입의 iterable을 생성, 타입 추론이 map()으로 넘겨준 함수를 기반으로 옳은 타입을 선택하지만, 종종 명시해야할 필요가 있음
```dart
// good case
var stuff = &lt;dynamic&gt;[1, 2];
var reciprocals = stuff.map&lt;double&gt;((n) =&gt; 1 / n);

// bad case
var stuff = &lt;dynamic&gt;[1, 2];
var reciprocals = stuff.map((n) =&gt; 1 / n).cast&lt;double&gt;();</code></pre><h3 id="cast-사용을-피한다">cast() 사용을 피한다</h3>
<ul>
<li>때때로 객체의 타입을 고정시킬만한 명령어가 없을 수 있지만 그런 경우라도 가능하다면 cast()를 사용하여 컬렌션 타입을 변경하는 것은 피해야 한다</li>
</ul>
<blockquote>
<p><strong>올바른 타입으로 생성한다</strong> -&gt; 컬렉션이 처음 생성되는 코드를 수정하여 올바른 타입을 가지게 한다
  <strong>요소에 접근할 때 캐스팅을 시도한다</strong> -&gt; 해당 컬렉션에 반복을 수행한다면, 반복 내부에서 각 요소를 캐스팅한다
  <strong>List.from()을 사용하여 캐스팅하도록 한다</strong> -&gt; 컬렉션의 모든 요소에 접근을 끝냈고 원본 객체가 더 이상 필요하지 않다면, List.from()을 사용하여 타입을 변환한다</p>
</blockquote>
<ul>
<li>타입 생성 예제<pre><code class="language-dart">// good case
List&lt;int&gt; singletonList(int value) {
var list = &lt;int&gt;[];
list.add(value);
return list;
}
</code></pre>
</li>
</ul>
<p>// bad case
List<int> singletonList(int value) {
  var list = []; // List<dynamic>.
  list.add(value);
  return list.cast<int>();
}</p>
<pre><code>- 요소에 접근할 때 캐스팅하는 방법
```dart
// good case
void printEvens(List&lt;Object&gt; objects) {
  // List에 int만 존재한다는 것을 알고 있습니다.
  for (final n in objects) {
    if ((n as int).isEven) print(n);
  }
}

// bad case
void printEvens(List&lt;Object&gt; objects) {
  // List에 int만 존재한다는 것을 알고 있습니다.
  for (final n in objects.cast&lt;int&gt;()) {
    if (n.isEven) print(n);
  }
}</code></pre><ul>
<li>List.from()을 사용한 캐스팅<pre><code class="language-dart">// good case
int median(List&lt;Object&gt; objects) {
// List에 int만 존재한다는 것을 알고 있습니다.
var ints = List&lt;int&gt;.from(objects);
ints.sort();
return ints[ints.length ~/ 2];
}
</code></pre>
</li>
</ul>
<p>// bad case
int median(List<Object> objects) {
  // List에 int만 존재한다는 것을 알고 있습니다.
  var ints = objects.cast<int>();
  ints.sort();
  return ints[ints.length ~/ 2];
}
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart - Strings in usage]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Strings-in-usage</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Strings-in-usage</guid>
            <pubDate>Sun, 11 Jun 2023 11:22:40 GMT</pubDate>
            <description><![CDATA[<h4 id="인접-문자열을-사용하여-문자열-리터럴을-연결한다">인접 문자열을 사용하여 문자열 리터럴을 연결한다</h4>
<ul>
<li>두 개의 문자열 리터럴 (값이 아니라 실제 따옴표로 묶은 리터럴 형식)을 연결하는 경우에 +를 사용할 필요가 없고 단순히 나란이 배치시키면 됨. 긴 단일 문자열을 만들 때 좋다<pre><code class="language-dart">// good case
raiseAlarm(&#39;ERROR: Parts of the spaceship are on fire. Other &#39;
  &#39;parts are overrun by martians. Unclear which are which.&#39;);
</code></pre>
</li>
</ul>
<p>// bad case
raiseAlarm(&#39;ERROR: Parts of the spaceship are on fire. Other &#39; +
    &#39;parts are overrun by martians. Unclear which are which.&#39;);</p>
<pre><code>
#### 문자열 값들을 합성할 때 보간 사용을 지향한다
- +를 이용해 긴 체인의 문자열을 만들어도 되지만 Dart에서는 보간을 사용하는게 깔끔하고 좋다
```dart
// good case
&#39;Hello, $name! You are ${year - birth} years old.&#39;;
// bad case
&#39;Hello, &#39; + name + &#39;! You are &#39; + (year - birth).toString() + &#39; y...&#39;;</code></pre><h4 id="보간에-불필요한-중괄호-사용을-피한다">보간에 불필요한 중괄호 사용을 피한다</h4>
<ul>
<li>단순 식별자를 보간하는 경우에는 {}를 생략한다<pre><code class="language-dart">// good case
var greeting = &#39;Hi, $name! I love your ${decade}s costume.&#39;;
// bad case
var greeting = &#39;Hi, ${name}! I love your ${decade}s costume.&#39;;
</code></pre>
</li>
</ul>
<p>```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart: NULL in usage]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-NULL-in-usage</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-NULL-in-usage</guid>
            <pubDate>Sun, 11 Jun 2023 11:14:16 GMT</pubDate>
            <description><![CDATA[<h4 id="변수를-명시적으로-null로-초기화하지-않는다">변수를 명시적으로 null로 초기화하지 않는다</h4>
<ul>
<li><p>변수가 nun-nullable 타입을 초기화하지 전에 사용한다면, Dart는 컴파일 에러를 발생시킴</p>
</li>
<li><p>변수가 nullable이라면, 암묵적으로 null로 초기화됨</p>
<blockquote>
<p><strong>Dart에는 &quot;초기화되지 않은 메모리&quot;라는 개념이 없고 명시적으로 null로 초기화하지 않아도 괜찮음</strong></p>
</blockquote>
<pre><code class="language-dart">Item? bestDeal(List&lt;Item&gt; cart) {
// good case
Item? bestItem;
// bad case
// Item? bestItem = null;

for (final item in cart) {
  if (bestItem == null || item.price &lt; bestItem.price) {
    bestItem = item;
  }
}

return bestItem;
}</code></pre>
</li>
</ul>
<h4 id="매개변수에-기본-값으로-null을-명시하여-사용하지-않는다">매개변수에 기본 값으로 null을 명시하여 사용하지 않는다</h4>
<ul>
<li>기본 값이 없는 nullable 매개변수를 사용하면, Dart는 해당 매개변수 기본 값을 암묵적으로 null로 사용하지 때문에 명시할 필요가 없다<pre><code class="language-dart">// good case
void error([String? message]) {
stderr.write(message ?? &#39;\n&#39;);
}
</code></pre>
</li>
</ul>
<p>// bad case
void error([String? message = null]) {
  stderr.write(message ?? &#39;\n&#39;);
}</p>
<pre><code>
#### 항등 연산자에 true 또는 false를 사용하지 않는다
- 조건문 내의 non-nullable인 boolean 표현식을 평가 할 때, 항등 연산자를 쓰는 것을 불필요하며 필요하다면 단항 부정 연산자인 **!**를 사용하는 것이 좋다
```dart
// good case
if (nonNullableBool) { ... }
if (!nonNullableBool) { ... }

// bad case
if (nonNullableBool == true) { ... }
if (nonNullableBool == false) { ... }</code></pre><ul>
<li>Nullable인 boolean 표현식을 평가하고 싶으면 <strong>??</strong> 을 사용하거나 명시적으로 <strong>!= null</strong> 확인을 해줘야한다<pre><code class="language-dart">// good case
// 값이 null 일 때, 표현식이 false로 처리됨
if (nullableBool ?? false) { ... }
</code></pre>
</li>
</ul>
<p>// 값이 null 일 때, 표현식이 false로 처리되고 아닐 때는 변수 값으로 처리
if (nullableBool != null &amp;&amp; nullableBool) { ... }</p>
<p>// bad case
// 값이 null이라면 정적 에러 발생
if (nullableBool) { ... }</p>
<p>// 값이 null 일 때, false로 처리
if (nullableBool == true) { ... }</p>
<pre><code>&gt; nullableBool == true는 실행 가능한 표현식이지만, 아래 이유로 사용하지 않는다
- 코드가 null과 관련이 있음을 나타내지 않음
- 명백히 null과 관련이 없기 때문에, 항등 연산자가 중복되어 제거될 수 있는 non-nullable의 경우로 쉽게 오인될 수 있다. 왼쪽의 boolean 표현식이 null일 가능성이 없을 경우에만 true이고, null을 생성할 수 있을 떄는 그렇지 않다
- 해당 형식의 boolean 로직은 알아보기 힘들다. nullableBool이 null이라면, nullableBool == true는 조건식이 false로 평가된다는 것을 의미
-&gt; **??** 연산자롤 null이 발생할 수도 있는 상황에서 더 명확한 표현이 가능하고, 중복된 연산자로 헤깔릴 가능성이 줄어듬
-&gt; **??** 같은 null-aware 연산자를 조건식에 사용하는 것은 변수를 non-nullable 타입으로 반환하지 않음
-&gt; if문의 바디에서 변수의 타입을 변환하고 싶다면, **??** 보다 명시적으로 **!= null** 확인하는 것이 좋다

#### 초기화 여부를 확인해야하는 변수를 late로 선언하지 않는다
- Dart는 late 변수가 초기화 또는 할당됭었는지 알 수 있는 방법을 가지고 있지 않음
- late 변수에 접근 했을 때, initializer를 실행하거나, 예외를 발생
- 바로 초기화할 변수에 late를 쓰는 것은 좋지만 아직 초기화를 하지 않았다면 알고 있어야 한다

#### 타입 프로모션을 활성화하고 싶다면 지역 변수에 nullable 필드를 할당하는 것을 고려
- Nullable 변수가 null이 아님을 판단할 때 변수를 non-nullable 타입으로 변환시키는 것도 좋다 (지역 변수와 매개변수에 대해서만 유효하므로 필드와 최상위 변수에는 불가)
```dart
class UploadException {
  final Response? response;

  UploadException([this.response]);

  @override
  String toString() {
    final response = this.response;
    if (response != null) {
      return &#39;Could not complete upload to ${response.url} &#39;
          &#39;(error code ${response.errorCode}): ${response.reason}.&#39;;
    }

    return &#39;Could not upload (no response).&#39;;
  }
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart: Library in usage]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Library-in-useage</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Library-in-useage</guid>
            <pubDate>Sun, 11 Jun 2023 10:16:27 GMT</pubDate>
            <description><![CDATA[<h3 id="라이브러리">라이브러리</h3>
<h4 id="part-of-명령어에-문자열을-사용한다">part of 명령어에 문자열을 사용한다</h4>
<ul>
<li>library 파일을 직접 가리키는 URI string을 사용하는 것이 좋다</li>
<li>이름만 사용한다면 해당 부분이 실제 어떤 라이브러리에 속하는지 모호할 수 있음<pre><code class="language-dart">// good
part of &#39;../../my_library.dart&#39;;
</code></pre>
</li>
</ul>
<p>// bad
part of my_library;</p>
<pre><code>#### 다른 패키지의 src 디렉토리에 있는 라이브러리를 import하지 않는다
- lib 아래 src 디렉토리는 패키지가 자체적으로 구현한 private 라이브러리로 지정됨
- 패키지 관리자가 패키지를 버전화하는 박식은 이런 규칙을 고려함
- 관리자는 패키지를 손상시키지 않고 src 디렉토리 아래의 코드를 자유롭게 수정함
-&gt; 즉 다른 패키지의 private 라이브러리를 import할 경우, 내 코드가 정상동작하지 않을 수 있음
#### Import 경로를 lib 안팎으로 지정하지 않는다
- package: import는 패키지가 컴퓨터의 어디에 저장되어 있는 상관없이 패키지의 lib 디렉토리 내부의 라이브러리에 접근할 수 있게 해줌
- lib 내부 파일의 상대적인 import 경로는 lib 디렉토리 외부의 파일에 도달하거나 접근할 수 없으며 lib 외부의 라이브러리는 lib 디렉토리를 사용할 때 상대경로를 사용할 수 없음
&gt; 예제 구조
my_package
└─ lib
   └─ api.dart
   test
   └─ api_test.dart

```dart
// in api_test.dart
// bad
import &#39;../lib/api.dart&#39;;</code></pre><p>-&gt; Dart에서는 위의 두가지 import가 전혀 관계없는 라이브러리에 대한 import로 간주함 따라서 아래와 같은 규칙을 지켜야한다</p>
<ul>
<li>import 경로에 /lib/를 사용하지 않는다</li>
<li>lib 디렉토리를 벗어나기 위해 ../를 사용하지 않는다<pre><code class="language-dart">// in api_test.dart
// good
import &#39;package:my_package/api.dart&#39;;</code></pre>
</li>
</ul>
<h4 id="상대-경로를-사용하지-import하는-것을-지향">상대 경로를 사용하지 import하는 것을 지향</h4>
<ul>
<li>import가 lib를 벗어나지 않을 때는 상대 경로를 사용하지 import를 하는 것이 좋다<blockquote>
<p>예제 구조
my_package
└─ lib
 ├─ src
 │  └─ stuff.dart
 │  └─ utils.dart
 └─ api.dart
 test
 │─ api_test.dart
 └─ test_utils.dart</p>
</blockquote>
</li>
</ul>
<pre><code class="language-dart">// lib/api.dart
import &#39;src/stuff.dart&#39;;
impott &#39;src/utils.dart&#39;;

// lib/src/utils.dart
import &#39;../api.dart&#39;;
import &#39;stuff.dart&#39;;

// test/api_test.dart
import &#39;package:my_package/api.dart&#39;; // &#39;lib&#39;에 도달하지 못함.
import &#39;test_utils.dart&#39;; // &#39;test&#39;라는 같은 폴더에 존재.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - FittedBox class]]></title>
            <link>https://velog.io/@manjee-official/Flutter-FittedBox-class</link>
            <guid>https://velog.io/@manjee-official/Flutter-FittedBox-class</guid>
            <pubDate>Sun, 11 Jun 2023 04:45:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>FittedBox 내의 컨텐츠를 FittedBox 부모 사이즈에 맞출 때 사용</p>
</blockquote>
<h4 id="sample-code">Sample code</h4>
<pre><code class="language-dart">class FittedBoxExample extends StatelessWidget {
  const FittedBoxExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 400,
      width: 300,
      color: Colors.blue,
      child: const FittedBox(
        // TRY THIS: Try changing the fit types to see how they change the way
        // the placeholder fits into the container.
        fit: BoxFit.fitWidth,
        child: Placeholder(),
      ),
    );
  }
}</code></pre>
<h4 id="option">Option</h4>
<ul>
<li>BoxFit.none: 이미지를 리사이즈 하지 않음</li>
<li>BoxFit.contain: 가로, 세로 중 한쪽으로 가능한 한 크게 표시</li>
<li>BoxFit.cover: 지정한 영역을 비율에 맞게 꽉 채움</li>
<li>BoxFit.fillWidget: 넓이에 맞게 확대 또는 축소</li>
<li>BoxFit.fillHeight: 세로에 맞게 확대 또틑 축소</li>
<li>BoxFit.scaleDown: 가운데에 배치한뒤 대상 밖으로 나가지 않도록 크기를 줄임</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - AspectRatio]]></title>
            <link>https://velog.io/@manjee-official/Flutter-AspectRatio</link>
            <guid>https://velog.io/@manjee-official/Flutter-AspectRatio</guid>
            <pubDate>Sun, 11 Jun 2023 04:28:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>AspectRatio </p>
</blockquote>
<ul>
<li>자식 위젯을 특정한 비율로 만드는 클래스</li>
<li>aspectRatio에 비율 값(분수, 소수로 표현)을 적고, child 위젯을 설정하면 끝</li>
<li>ex) 3/2 너비 3 높이 2 == 1.5 너비가 높이의 1.5배</li>
</ul>
<pre><code class="language-dart">class AspectRatioExample extends StatelessWidget {
  const AspectRatioExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.blue,
      alignment: Alignment.center,
      width: double.infinity,
      height: 100.0,
      child: AspectRatio(
      // 비울 16 /9 [그림 1]
        aspectRatio: 16 / 9,
        child: Container(
          color: Colors.green,
        ),
      // 비율 3.0 (가로가 세로의 3배) [그림 2]
      aspectRatio: 3.0,
        child: Container(
          width: 100.0,
          height: 50.0,
          color: Colors.green,
        ),
       // 비율 0.5
       aspectRatio: 0.5,
        child: Container(
          width: 100.0,
          height: 50.0,
          color: Colors.green,
        ),
      ),
    );
  }
}</code></pre>
<h4 id="그림-1">[그림 1]</h4>
<p><img src="https://velog.velcdn.com/images/manjee-official/post/02bf08fe-7bb1-41ef-93ba-28a45c4f1533/image.png" alt="16:9"></p>
<h4 id="그림-2">[그림 2]</h4>
<p><img src="https://velog.velcdn.com/images/manjee-official/post/9c091436-e4ed-4c29-964c-0ca713439cf5/image.png" alt="3.0"></p>
<blockquote>
<p>츨처 - <a href="https://api.flutter.dev/flutter/widgets/AspectRatio-class.html">https://api.flutter.dev/flutter/widgets/AspectRatio-class.html</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - FractionallySizedBox class]]></title>
            <link>https://velog.io/@manjee-official/Flutter-FractionallySizedBox-class</link>
            <guid>https://velog.io/@manjee-official/Flutter-FractionallySizedBox-class</guid>
            <pubDate>Wed, 07 Jun 2023 10:37:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>UI 사이즈를 상대적으로 세팅할 때 사용 (예: 가로 길이의 70%, 공백이 위젯의 10%)</li>
</ul>
</blockquote>
<ul>
<li>widthFacot, heightFactor를 사용한다 (0~1)</li>
</ul>
<p>예제 1. 화면 가로, 세로의 50% 크기로 UI 그림</p>
<pre><code class="language-dart">class FractionallySizedBoxExample extends StatelessWidget {
  const FractionallySizedBoxExample({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox.expand(
      child: FractionallySizedBox(
        widthFactor: 0.5,
        heightFactor: 0.5,
        alignment: FractionalOffset.center,
        child: DecoratedBox(
          decoration: BoxDecoration(
            border: Border.all(
              color: Colors.blue,
              width: 4,
            ),
          ),
        ),
      ),
    );
  }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/manjee-official/post/418dc027-32cd-42e5-93c4-221643891cb8/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - Expanded class]]></title>
            <link>https://velog.io/@manjee-official/Flutter-Expanded-class</link>
            <guid>https://velog.io/@manjee-official/Flutter-Expanded-class</guid>
            <pubDate>Wed, 07 Jun 2023 08:42:20 GMT</pubDate>
            <description><![CDATA[<blockquote>
<ul>
<li>Row, Column, Flex의 하위에 사용하면 공간을 채우기 위한 클래스</li>
</ul>
</blockquote>
<ul>
<li><p>주축을 따라 사용 가능한 공간을 채루며 하위 자식이 여러 개일땐 flex factor에 따라 공간을 나눠 사용함</p>
</li>
<li><p>예제 1. Column을 이용해 새로로 나열 Container는 정해진 height로 보여주고 가운데 expand 클래스가 나머지 자리를 채운다</p>
<pre><code class="language-dart">class ExpandExample extends StatelessWidget {
const ExpandExample({super.key});

@override
Widget build(BuildContext context) {
  return Center(
    child: Column(
      children: &lt;Widget&gt;[
        Container(
          color: Colors.blue,
          height: 200,
          width: 100,
        ),
        Expanded(
          child: Container(
            color: Colors.amber,
            width: 100,
          ),
        ),
        Container(
          color: Colors.blue,
          height: 200,
          width: 100,
        ),
      ],
    ),
  );
}
}</code></pre>
</li>
<li><p>예제 2. Row를 이용해 가로로 나열하고 첫번째 Expanded에는 flex 2를 줘서 다른 Expanded의 2배의 크기를 가지도록 한다</p>
<pre><code class="language-dart">class ExpandExample extends StatelessWidget {
const ExpandExample({super.key});

@override
Widget build(BuildContext context) {
  return Center(
    child: Row(
      children: &lt;Widget&gt;[
        Expanded(
          flex: 2,
          child: Container(
            color: Colors.amber,
            height: 100,
          ),
        ),
        Container(
          color: Colors.blue,
          height: 100,
          width: 50,
        ),
        Expanded(
          child: Container(
            color: Colors.amber,
            height: 100,
          ),
        ),
      ],
    ),
  );
}
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Effective Dart - Style]]></title>
            <link>https://velog.io/@manjee-official/Effective-Dart-Style</link>
            <guid>https://velog.io/@manjee-official/Effective-Dart-Style</guid>
            <pubDate>Thu, 01 Jun 2023 15:45:38 GMT</pubDate>
            <description><![CDATA[<h4 id="identifiers">Identifiers</h4>
<ul>
<li>UpperCamelCase: 항상 식별자의 첫번째 문자는 대문자로 표기 해야 하며, 만약 여러 단어가 포함되어 있는 경우라면, 각 단어를 구분짓게 해주기 위해서 대문자로 표기해야 한다</li>
<li>lowerCamelCase: 항상 식별자의 첫번째 문자는 소문자로 표기해야 하며, 나머지는 UpperCamelCase와 동일</li>
<li>lowercase_with_undercase: 소문자만 사용하며 단어의 구분은 언더스코어를 사용</li>
</ul>
<h4 id="타입-유형에는-uppercamelcase를-사용">타입 유형에는 UpperCamelCase를 사용</h4>
<ul>
<li>classes, enum types, typedefs, type parameter 등에 사용 (separator 없이 사용한다)<pre><code>class SliderMune { ... }
typedef Predicate&lt;T&gt; = bool Function(T value);</code></pre></li>
<li>메타 데이터 어노테이션에도 사용한다<pre><code>class Foo {
  const Foo([Object? arg]);
}
</code></pre></li>
</ul>
<p>@Foo(anArg)
calss A { ... }</p>
<pre><code>- 어노테이션 클래스가 생성자를 포함하고 있지 않으면 lowerCamelCase로 생성할 수 있다</code></pre><p>const foo = Foo();</p>
<p>@foo
class C { ... }</p>
<pre><code>&lt;br/&gt;

#### 이름에 두 단어 이상이 들어가는 경우 UpperCamelCase 사용
- 각 단어의 첫 글자에 대문자를 사용해야 한다</code></pre><p>extension MyNameList<T> on List<T> { ... }</p>
<p>var nameModel = &quot;Manjee.official&quot;</p>
<pre><code>&lt;br/&gt;

#### 패키지, 폴더, 소스파일 이름에는 lowercase_with_underscores 사용</code></pre><p>// Good case
my_pakage
 -&gt; lib
  -&gt; file_system.dart
  -&gt; bottom_menu.dart</p>
<p>// Bad case
mypakage
 -&gt; lib
  -&gt; file-system.dart
  -&gt; bottomMenu.dart</p>
<pre><code>&lt;br/&gt;

#### Import prefixes에는 lowercase_with_unserscores 사용</code></pre><p>// Good case
import &#39;dart:math&#39; as math;
import &#39;package:angular_components/angular_components.dart&#39; as angular_components;
import &#39;package:js/js.dart&#39; as js;</p>
<p>// Bad case
import &#39;dart:math&#39; as Math;
import &#39;package:angular_components/angular_components.dart&#39; as angularComponents;
import &#39;package:js/js.dart&#39; as JS;</p>
<pre><code>&lt;br/&gt;

#### 다른 식별자들은 lowerCamelCase 사용
- 클래스 멤버, 메소드 명, 변수, 파라미터 등...</code></pre><p>var githubId = &quot;manjee&quot;;</p>
<p>HttpRequest httpRequest;</p>
<p>void align(bool clearItems) { ... }</p>
<pre><code>&lt;br/&gt;

#### 상수 이름에는 lowerCamelCase를 쓰는 것이 좋다</code></pre><p>// Good case
const pi = 3.14l
const defaultTimeout = 1000;
final urlScheme = RegExp(&#39;^([a-z]+):&#39;);</p>
<p>class Dice {
    static final numberGenerator = Random();
}</p>
<pre><code>&gt; ** SCREAMING_CAPS**
- SCREAMING_CAPS 형태 네이밍의 변수를 사용하고 있는 기존 코드나 라이브러리를 사용할 경우
- Dart 코드를 Java 코드와 병행해서 개발할 경우

&lt;br/&gt;

#### 두 개의 단어를 합친 단어의 약어는 대문자로 표현한다</code></pre><p>class HttpConnection {}
class DBIOPort {}
class MrRogers {}</p>
<p>var uiHandler = ...</p>
<pre><code>&lt;br/&gt;

#### 사용하지 않는 콜백 파라미터는 _, __을 사용하는 것이 좋다</code></pre><p>futureOfVoid.then((_) {
  print(&#39;Operation complete.&#39;);
});</p>
<pre><code>
&lt;br/&gt;

#### private이 아닌 경우 식별자 앞에 _를 붙이지 말아야한다
- Dart에서는 변수 등의 앞에 _을 붙임으로써 private을 표시한다

&lt;br/&gt;

#### 라이브러리 이름을 명시하지 않는다
- 기술적으로는 가능하나 레거시임!</code></pre><p>// Bad case
library my_library</p>
<p>// Good case
@TestOn(&#39;browser&#39;)
library;</p>
<pre><code>
&lt;br/&gt;

#### Import 구문 순서
- **dart:** 는 다른 import 보다 먼저 선언한다
- **package:** 는 **dart:** 다음에 import 한다
- **export** 는 모든 import 다음에 선언한다
- import 들은 기본적으로 알파벳 순으로 정렬한다</code></pre><p>import &#39;dart:async&#39;;
import &#39;dart:html&#39;;</p>
<p>import &#39;package:bar/bar.dart&#39;;
import &#39;package:foo/foo.dart&#39;;</p>
<p>import &#39;foo.dart&#39;;
import &#39;foo/foo.dart&#39;;</p>
<p>export &#39;src/error.dart&#39;;</p>
<p>```</p>
<br/>

<h4 id="dartformating">DartFormating</h4>
<blockquote>
<p><a href="https://dart-ko.dev/tools/dart-format">https://dart-ko.dev/tools/dart-format</a></p>
</blockquote>
<p><br/><br/><br/></p>
<blockquote>
<p>출처 - <a href="https://dart-ko.dev/guides/language/effective-dart/style">https://dart-ko.dev/guides/language/effective-dart/style</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dart - Extension method]]></title>
            <link>https://velog.io/@manjee-official/Dart-Extension-method</link>
            <guid>https://velog.io/@manjee-official/Dart-Extension-method</guid>
            <pubDate>Tue, 30 May 2023 17:58:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>다른 개발자들이 개발한 API를 사용할 때 기능을 추가하기 위해 사용</p>
</blockquote>
<h4 id="static-types-and-dynamic">Static types and dynamic</h4>
<pre><code>// dynamic 타입의 변수에는 Extension method를 사용할 수 없다
dynamic d = &#39;10&#39;;
print(d.parseInt()); // 런타임 에러 : NoSuchMethodError

// Extention method는 Dart의 타입 추론을 사용함 -&gt; 아래 v는 String 타입으로 추론되어 정상동작함
var v = &#39;2&#39;;
print(v.parseInt()); // 2</code></pre><h4 id="api-conflicts">API conflicts</h4>
<p><strong>-&gt; extension method가 인터페이스나 다른 extension method와 충돌하는 경우(메소드명 동일)</strong></p>
<ul>
<li>show 혹은 hide를 사용해 노출되는 API를 제한하는 방법<pre><code>// String 확장 메서드인 parseInt()를 정의하는 라이브러리.
import &#39;string_apis.dart&#39;;
</code></pre></li>
</ul>
<p>// parseInt()를 정의하는 또다른 라이브러리.
// hide를 사용하여 NumberParsing2의 확장 메서드를 숨깁니다.
import &#39;string_apis_2.dart&#39; hide NumberParsing2;</p>
<p>// &#39;string_apis.dart&#39;에 정의된 parseInt()를 사용합니다.
print(&#39;42&#39;.parseInt());</p>
<pre><code>- 사용하려는 클래스를 명시해준다</code></pre><p>// 두 라이브러리 모두 parseInt()를 가지는
// String에 대한 확장을 가지고 있고, 해당 확장들은 다른 이름을 가지고 있습니다.
import &#39;string_apis.dart&#39;; // Contains NumberParsing extension.
import &#39;string_apis_2.dart&#39;; // Contains NumberParsing2 extension.</p>
<p>// ···
// print(&#39;42&#39;.parseInt()); // 작동하지 않습니다.
print(NumberParsing(&#39;42&#39;).parseInt());
print(NumberParsing2(&#39;42&#39;).parseInt());</p>
<pre><code>- Extension method의 이름이 같아면 prefix로 이름을 지정해주는 것도 좋음</code></pre><p>// 두 라이브러리 모두 parseInt() 확장 메서드를 가지는 NumberParsing 메서드를 가지고 있습니다.
// &#39;string_apis_3.dart&#39;의 NumberParsingOne 확장도 parseNum()을 가지고 있습니다.
import &#39;string_apis.dart&#39;;
import &#39;string_apis_3.dart&#39; as rad;</p>
<p>// ···
// print(&#39;42&#39;.parseInt()); // Doesn&#39;t work.</p>
<p>// string_apis.dart의 ParseNumbers 확장을 사용합니다.
print(NumberParsing(&#39;42&#39;).parseInt());</p>
<p>// string_apis_3.dart의 ParseNumbers 확장을 사용합니다.
print(rad.NumberParsing(&#39;42&#39;).parseInt());</p>
<p>// string_apis_3.dart만 parseNum()을 가지고 있습니다.
print(&#39;42&#39;.parseNum());</p>
<pre><code>
#### Extension method 구현
&gt; ```
extension &lt;extension name&gt;? on &lt;type&gt; {
  (&lt;member definition&gt;)*
}</code></pre><pre><code>extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }

  double parseDouble() {
    return double.parse(this);
  }
}
</code></pre><blockquote>
<p>출처 <a href="https://dart-ko.dev/language/extension-methods">https://dart-ko.dev/language/extension-methods</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dart - Extend a class]]></title>
            <link>https://velog.io/@manjee-official/Dart-Extend-a-class</link>
            <guid>https://velog.io/@manjee-official/Dart-Extend-a-class</guid>
            <pubDate>Tue, 30 May 2023 16:49:43 GMT</pubDate>
            <description><![CDATA[<ul>
<li>extextnds로 서브 클래스를 생성하고 super로 슈퍼 클래스임을 알려줌<pre><code>class Swim {
  void dive() {
      _standUp();
      _jumping();
  }
}
</code></pre></li>
</ul>
<p>calss Human extends Swim {
    void dive() {
        super.dive();
        _moving();
    }
}</p>
<pre><code>
#### Overriding members
- 서브 클래스는 연산자를 포함한 인스턴스 메소드, getter, setter를 override할 수 있다
- @override 어노테이션을 이용해 다음 처럼 사용할 수 있다
- override 메소드는 메소드 선언이 일치해야함
    - 리턴 타입이 같거나 하위 타입이어야함
    - 메소드의 인자는 같거나 상위 타입이어야함 (아래 코드에서는 int의 상위인 num을 사용)
    - n개의 매게변수를 사용한다면 n개의 매게 변수가 일치해야함
    - generic / non-generic 서로 override 할 수 없다
    - 메소드 인자를 하위 타입으로 사용하는 것은 타입 에러를 발생시킬 수 있으나 에러가 발생하지않음을 보장할 수 있는 경우에는 **cavariant**를 사용해 하위 타입으로 사용할 수 있다</code></pre><p>class Television {
  // ···
  set contrast(int value) {...}
}</p>
<p>class SmartTelevision extends Television {
  @override
  set contrast(num value) {...}
  // ···
}</p>
<pre><code>
#### noSuchMethod()
- 코드가 존재하지 않는 메소드나 인스턴스 변수를 사용하려할 때 감지해주는 메소드</code></pre><p>class A {
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print(&#39;You tried to use a non-existent member: &#39;
        &#39;${invocation.memberName}&#39;);
  }
}</p>
<p>```</p>
<blockquote>
<p>출처 <a href="https://dart.dev/language/extend">https://dart.dev/language/extend</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Dart - Mixins]]></title>
            <link>https://velog.io/@manjee-official/Dart-Mixins</link>
            <guid>https://velog.io/@manjee-official/Dart-Mixins</guid>
            <pubDate>Tue, 30 May 2023 11:40:46 GMT</pubDate>
            <description><![CDATA[<h4 id="mixins">Mixins</h4>
<ul>
<li>여러 클래스 계층에서 재사용할 수 있는 코드는 정의하는 방법이다</li>
<li>다중 상속을 위함</li>
<li>Mixin을 사용하려면 with 키워드를 사용한다 (다중 상속 가능)<pre><code>class Musician extends Performer with Musical {
// ···
}
</code></pre></li>
</ul>
<p>class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}</p>
<pre><code>- Mixin을 정의하려면 mixin을 사용하다
- mixin과 클래스를 모드 정의해야하는 경우에는 mixin class를 정의해야한다</code></pre><p>mixin Musical {
  bool canPlayPiano = false;</p>
<p>  void entertainMe() {
    if (canPlayPiano) {
      print(&#39;Playing piano&#39;);
    } else {
      print(&#39;Humming to self&#39;);
    }
  }
}</p>
<pre><code>- on 키워드를 사용해 슈퍼 클래스를 지정할 수 있다</code></pre><p>class Musician {
  // ...
}
mixin MusicalPerformer on Musician {
  // ...
}
class SingerDancer extends Musician with MusicalPerformer {
  // ...
}</p>
<pre><code>&gt; 위 코드에서 SingerDancer 클래스가 Musician을 확장한 클래스이기 때문데 MusicalPerformer을 사용할 수 있음</code></pre>]]></description>
        </item>
    </channel>
</rss>