<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>졸리운 곰의 정보기술 여행 [김성준]</title>
    <link>https://comphy.tistory.com/</link>
    <description>What a Feelings???</description>
    <language>ko</language>
    <pubDate>Tue, 7 Apr 2026 23:11:07 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>졸리운_곰</managingEditor>
    <image>
      <title>졸리운 곰의 정보기술 여행 [김성준]</title>
      <url>https://t1.daumcdn.net/cfile/tistory/1917944E5000C6C21F</url>
      <link>https://comphy.tistory.com</link>
    </image>
    <item>
      <title>한림대 경제학과 개쌕기 아닌 씹ㅅㄲ 김진영 영화감독 그것이 알고싶다. 영화 &amp;quot;B컷&amp;quot;개봉</title>
      <link>https://comphy.tistory.com/973</link>
      <description>&lt;p data-en-clipboard=&quot;true&quot; data-pm-slice=&quot;1 1 []&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;한림대 경제학과 개쌕기 아닌 씹ㅅㄲ 김진영 영화감독 그것이 알고싶다. 영화&amp;nbsp;&quot;B컷&quot;개봉&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;별로 알고 싶지 않을 사람인지는 모르지만.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;한림대 경제학과일때 꽈보지 이쁘장한거 방송어쩌고 보지에 찝적대고&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;아버지가 어떤 개씩긴지. &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;졸업하자 마자 MBC에 PD로 실습시키고 &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; &amp;nbsp; 순풍산부인과? 과를 골라도 산부인과? 왜? 모 보고싶어서?&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; 그때 당시 인기를 끌던 &quot;꽃보다 할배&quot; 인기에 편승하여 &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; &quot;꽃할배 수사대&quot;라는 걸 남의 밥상에 수저 얻는거 가르치고 &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; &amp;nbsp; 꽃할배 수사대로 &quot;꽃보다 할배&quot; 욕먹이고 &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; &amp;nbsp; 다시 영화 감독한다며 &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; &amp;nbsp; 당시 인기가 있던 &quot;가문의 영광&quot; 시리즈를 도용 &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; &lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; &amp;nbsp; 비슷한 아류작인 &quot;위험한 상견례&quot;라는 가족 영화를 기획 날려먹고&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; 평생을 남의 인기에 편승해서 돈벌려고 하는 영화감독 창작자이다.&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&amp;nbsp; 2022 대선에 편승 결과를 보자&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-04 070721.png&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UVKt4/btsyMzgHkNu/pc8Fa6abcUQ1Qk6X2lKioK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UVKt4/btsyMzgHkNu/pc8Fa6abcUQ1Qk6X2lKioK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UVKt4/btsyMzgHkNu/pc8Fa6abcUQ1Qk6X2lKioK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUVKt4%2FbtsyMzgHkNu%2Fpc8Fa6abcUQ1Qk6X2lKioK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;807&quot; height=&quot;856&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-04 070721.png&quot; data-origin-width=&quot;807&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-05 230549.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnnaaP/btsyOEu2Ve9/PGlyHAZTk7aAOjwgiFLOPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnnaaP/btsyOEu2Ve9/PGlyHAZTk7aAOjwgiFLOPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnnaaP/btsyOEu2Ve9/PGlyHAZTk7aAOjwgiFLOPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnnaaP%2FbtsyOEu2Ve9%2FPGlyHAZTk7aAOjwgiFLOPk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;652&quot; height=&quot;761&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-05 230549.png&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-09 134750.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;803&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nCYKO/btsyH0z2KWV/lO1r0JVKzj67oM7fMVEbiK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nCYKO/btsyH0z2KWV/lO1r0JVKzj67oM7fMVEbiK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nCYKO/btsyH0z2KWV/lO1r0JVKzj67oM7fMVEbiK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnCYKO%2FbtsyH0z2KWV%2FlO1r0JVKzj67oM7fMVEbiK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;681&quot; height=&quot;803&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-09 134750.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;803&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-12 075905.png&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;787&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cU0XPf/btsyMKWNRmZ/pd1IYSVFkQy7vWlid9HRgK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cU0XPf/btsyMKWNRmZ/pd1IYSVFkQy7vWlid9HRgK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cU0XPf/btsyMKWNRmZ/pd1IYSVFkQy7vWlid9HRgK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcU0XPf%2FbtsyMKWNRmZ%2Fpd1IYSVFkQy7vWlid9HRgK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;653&quot; height=&quot;787&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-12 075905.png&quot; data-origin-width=&quot;653&quot; data-origin-height=&quot;787&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-12 231002.png&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;816&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciBvCQ/btsyOY75ObK/nTMmSKp7bH1k7hnH73WpD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciBvCQ/btsyOY75ObK/nTMmSKp7bH1k7hnH73WpD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciBvCQ/btsyOY75ObK/nTMmSKp7bH1k7hnH73WpD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciBvCQ%2FbtsyOY75ObK%2FnTMmSKp7bH1k7hnH73WpD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;816&quot; data-filename=&quot;b컷 김진영 영화감독 힌림대 경제학과 화면 캡처 2022-04-12 231002.png&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;816&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;b컷 한림대 경제학과 김진영 영화감독 화면 캡처 2022-04-14 220531.png&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;801&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XnOYy/btsyHyjxTxo/HX5NC2fEVkTOkQz5mP2f6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XnOYy/btsyHyjxTxo/HX5NC2fEVkTOkQz5mP2f6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XnOYy/btsyHyjxTxo/HX5NC2fEVkTOkQz5mP2f6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXnOYy%2FbtsyHyjxTxo%2FHX5NC2fEVkTOkQz5mP2f6K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;801&quot; data-filename=&quot;b컷 한림대 경제학과 김진영 영화감독 화면 캡처 2022-04-14 220531.png&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;801&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;영화 B컷 한림대 경제학과 영화감독 화면 캡처 2022-04-23 125758.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;623&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvwvCB/btsyLsigsVJ/xGVAjLCKfj8kL6igkrIqv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvwvCB/btsyLsigsVJ/xGVAjLCKfj8kL6igkrIqv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvwvCB/btsyLsigsVJ/xGVAjLCKfj8kL6igkrIqv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvwvCB%2FbtsyLsigsVJ%2FxGVAjLCKfj8kL6igkrIqv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;654&quot; height=&quot;623&quot; data-filename=&quot;영화 B컷 한림대 경제학과 영화감독 화면 캡처 2022-04-23 125758.png&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;623&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/973</guid>
      <comments>https://comphy.tistory.com/973#entry973comment</comments>
      <pubDate>Thu, 19 Oct 2023 10:08:40 +0900</pubDate>
    </item>
    <item>
      <title># 4주차 - API 사용법 익히기 (왓챠피디아)</title>
      <link>https://comphy.tistory.com/972</link>
      <description>&lt;h1&gt;4주차 - API 사용법 익히기 (왓챠피디아)&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0f97b007-85fd-444f-a02f-75fb22212941/4%EC%A3%BC%EC%B0%A8_-_API_%EC%82%AC%EC%9A%A9%EB%B2%95_%EC%9D%B5%ED%9E%88%EA%B8%B0_(%EC%99%93%EC%B1%A0%ED%94%BC%EB%94%94%EC%95%84).pdf&quot;&gt;4주차&lt;em&gt;-&lt;/em&gt;API_사용법&lt;em&gt;익히기&lt;/em&gt;(왓챠피디아).pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;단축키 모음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;새로고침 &lt;code&gt;F5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;저장&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전체선택&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;잘라내기&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;콘솔창 줄바꿈&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;enter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드정렬&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Alt&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;들여쓰기&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;들여쓰기 취소 : &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주석&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API 이해하기&lt;/li&gt;
&lt;li&gt;비동기 이해하기&lt;/li&gt;
&lt;li&gt;API를 이용한 앱 만들기&lt;/li&gt;
&lt;li&gt;Provider 를 이용한 상태관리 익숙해지기&lt;/li&gt;
&lt;li&gt;WebView 사용해보기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;01. API 이해하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) API는 무엇인가요?&lt;/p&gt;
  &lt;aside&gt;
    날씨 앱을 만들려면, 날씨 정보가 필요합니다.
  그 어떤 개발자도 직접 날씨 정보를 수집해서 앱을 만들지 않고, 기상청과 같이 정보를 가지고 있는 곳에서 정보를 받아 앱을 만듭니다.

&lt;p&gt;  기상청에서 정보를 어떻게 받을 수 있을까요?&lt;/p&gt;
&lt;p&gt;  다른 사람들이 내 컴퓨터에 있는 데이터에 접근할 수 있도록 만들어둔 기능을 &lt;strong&gt;API(Application Programming Interface)&lt;/strong&gt;라고 부릅니다. 따라서 기상청 개발자들이 만들어둔 API가 있는지 찾아보면 됩니다.&lt;/p&gt;
&lt;p&gt;  구글에 &lt;code&gt;기상청 API&lt;/code&gt; 또는 &lt;code&gt;날씨 API&lt;/code&gt;라고 검색하면 금방 찾으실 수 있습니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/83dec86f-abb8-4391-8a9a-b9c03db6a7d3/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사해 새 탭에서 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://www.data.go.kr/data/15084084/openapi.do&quot;&gt;&lt;strong&gt;[코드스니펫] 기상청_단기예보 조회서비스 API&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://www.data.go.kr/data/15084084/openapi.do&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      아래 이미지와 같이 사용 설명서를 작성해 두었는데 이를 **API 문서**라고 부릅니다. API를 이해하는데 필요한 배경 지식을 배워보도록 하겠습니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/775c9eae-15a7-4ec8-8460-872d1be881c9/Untitled.png)


&amp;lt;aside&amp;gt;
  그 외에도 `인스타그램 API`, `Youtube API`, `Kakao API`와 같이 검색해보시면 많은 서비스들이 API를 제공하고 있는 것을 보실 수 있습니다. (API는 유료도 있고, 무료도 있습니다)

- **[[코드스니펫] Public APIs](https://github.com/public-apis/public-apis)**

    ```dart
    https://github.com/public-apis/public-apis
    ```

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) API를 이해하기 위한 배경 지식&lt;/p&gt;
  &lt;aside&gt;
    API를 이해하기 위해선, 멀리 떨어진 컴퓨터들이 어떻게 대화(통신)할 수 있는지 먼저 알아야 합니다.

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;클라이언트와 서버&lt;/p&gt;
  &lt;aside&gt;
    웹 브라우저에 `naver.com` 입력 후 엔터를 누르면 네이버 웹 페이지가 뜹니다.

&lt;p&gt;  네이버 주소를 입력하고 엔터를 누르는 경우, 웹 브라우저가 네이버에 있는 컴퓨터로 &lt;strong&gt;요청(Request)&lt;/strong&gt;을 보냅니다. 그러면 요청을 받은 네이버 컴퓨터가 웹 페이지를 &lt;strong&gt;응답(Response)&lt;/strong&gt;해 줍니다.&lt;/p&gt;
&lt;p&gt;  이때 &lt;strong&gt;요청&lt;/strong&gt;하는 컴퓨터를 &lt;strong&gt;클라이언트&lt;/strong&gt;라고 부르고, &lt;strong&gt;응답&lt;/strong&gt;하는 컴퓨터를 &lt;strong&gt;서버&lt;/strong&gt;라고 부릅니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/579bc7c2-7107-4b11-82d4-1e3b13fe3443/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
  &lt;aside&gt;
    웹 브라우저가 아닌 코딩으로도 웹 요청을 보낼 수 있습니다.
  코딩으로 웹 요청을 보내기 위해 필요한 최소한의 지식을 배워보도록 하겠습니다.

  &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로토콜(Protocol)&lt;/p&gt;
  &lt;aside&gt;
    한국 사람끼리 한국어를 쓰고, 미국 사람끼리 영어를 쓰듯 컴퓨터끼리 정보를 주고 받는 방법을 정한 규칙이 **프로토콜**입니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    웹의 경우 컴퓨터들끼리 **HTTP**라는 규칙을 따라 대화합니다.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fe188a68-fd08-47c1-ba42-06cf584ec344/http.png&quot; alt=&quot;http.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HTTP&lt;/p&gt;
  &lt;aside&gt;
    HTTP는 웹에서 컴퓨터끼리 대화(요청과 응답) 할 때 따르는 규칙입니다.

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;요청(Request)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;URL : 목적지&lt;/p&gt;
  &lt;aside&gt;
    요청을 보내는 인터넷 상에 연결된 **다른 컴퓨터의 주소**입니다.

&lt;p&gt;  아래 이미지와 같이 URL도 섹션 별로 다양한 기능이 있지만, 지금은 &lt;strong&gt;목적지&lt;/strong&gt;라고만 생각해주세요!&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/252f1704-ea5d-4b7f-9c88-005e92ec95e5/Untitled.png&quot; alt=&quot;출처 - [https://sitechecker.pro/](https://sitechecker.pro/)&quot;&gt;&lt;/p&gt;
&lt;p&gt;  출처 - &lt;a href=&quot;https://sitechecker.pro/&quot;&gt;https://sitechecker.pro/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;메소드(method) : 원하는 액션&lt;/p&gt;
  &lt;aside&gt;
    HTTP 요청시, **메소드(method)**를 함께 전송합니다.

&lt;p&gt;  &lt;strong&gt;메소드&lt;/strong&gt;는 서버가 수행해야할 동작을 영어 동사로 표현한 것입니다.&lt;/p&gt;
&lt;p&gt;  통신의 목적은 서버에 CRUD를 요청하는 것인데, 일반적으로 데이터 조회는 GET, 그 외의 다른 동작은 POST 구현하기도 합니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GET : 조회&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;POST : 생성 / 수정 / 삭제&lt;/p&gt;
&lt;aside&gt;
  웹 브라우저 주소창에서 검색하는 모든 요청은 **GET 방식**입니다.

&lt;/aside&gt;

&lt;aside&gt;
  그 외 **메소드** 목록은 아래 링크를 참고해주세요.
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/HTTP/Methods&quot;&gt;[코드스니펫] HTTP 요청 메소드&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://developer.mozilla.org/ko/docs/Web/HTTP/Methods&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파라미터(Parameter)&lt;/p&gt;
  &lt;aside&gt;
    요청을 보낼 때 데이터 같이 보낼 수 있는데, 이 데이터를 **파라미터(Parameter)**라고 부릅니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    HTTP 요청을 보낼 때, URL 뒤에 **쿼리스트링(Query String)** 형태로 데이터를 함께 전달할 수 있습니다.

&lt;p&gt;  Query String은 키(Key)와 값(Value) 형태로 구성되어 있습니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d28dbbda-e247-4fdf-984c-7f90fa1a2c87/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  실제 예제를 보도록 하겠습니다. 코드스니펫을 복사해서 새 탭에서 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://en.dict.naver.com/#/search?query=hello&amp;amp;range=all&quot;&gt;[코드스니펫] 네이버 영어사전 URL&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://en.dict.naver.com/#/search?query=hello&amp;amp;range=all&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;            &amp;lt;aside&amp;gt;
              아래와 같이 `hello`가 검색된 영어 사전 페이지가 뜹니다.
            주소창을 자세히 보면 `query=hello`라고 검색어가 동일하게 있는 것을 확인하실 수 있습니다.

            &amp;lt;/aside&amp;gt;

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5cd59c04-73e0-4835-a87f-5d2ed76b52de/Untitled.png)

            &amp;lt;aside&amp;gt;
              주소창에 `hello`를 `good`으로 변경한 뒤 엔터를 눌러보면 검색창에 `good`이 작성되어 있는 것을 보실 수 있습니다.

            이와 같이 네이버 서버에 내가 알고 싶은 단어를 `query`라는 키에 값으로 전달하여 결과를 받아올 수 있습니다.

            &amp;lt;/aside&amp;gt;

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4eccae2b-8d7c-4853-9727-6aa11471031e/Untitled.png)

    - 응답(Response)
        - 웹페이지

            &amp;lt;aside&amp;gt;
              웹 브라우저 주소창에 `naver.com`이라고 입력하면 네이버 웹페이지를 응답해줍니다.

            &amp;lt;/aside&amp;gt;

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd350dfc-c579-47d0-84ba-f17fc10c0152/Untitled.png)

        - 데이터

            &amp;lt;aside&amp;gt;
              개발자가 아닌 이상 평소에 볼 일이 없지만 웹페이지가 아닌 데이터만 응답해주기도 합니다.

            &amp;lt;/aside&amp;gt;

            코드스니펫을 복사해 새 탭에서 접속해주세요.

            - [**[코드스니펫] json 데이터 응답 URL**](https://jsonplaceholder.typicode.com/posts)

                ```dart
                https://jsonplaceholder.typicode.com/posts
                ```


            &amp;lt;aside&amp;gt;
              데이터를 보내줄 때에도 정해진 형식에 따라 주는데, 보통 `JSON`이라는 형식을 많이 사용합니다.

            &amp;lt;/aside&amp;gt;

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cfe18aff-da00-43a9-a7be-cff6184a29a8/Untitled.png)

            - 크롬 JSON Viewer 확장 프로그램 설치

                &amp;lt;aside&amp;gt;
                  JSON 구조를 조금 더 쉽게 파악할 수 있도록 도와주는 크롬 확장 프로그램을 설치해 보도록 하겠습니다.

                &amp;lt;/aside&amp;gt;

                코드스니펫을 복사해서 새 탭에서 열어주세요.

                - **[[코드스니펫] JSON Viewer 확장 프로그램 URL](https://chrome.google.com/webstore/detail/jsonview/gmegofmjomhknnokphhckolhcffdaihd?hl=ko)**

                    ```dart
                    https://chrome.google.com/webstore/detail/jsonview/gmegofmjomhknnokphhckolhcffdaihd?hl=ko
                    ```

                1. `Chrome에 추가` 버튼을 눌러주세요.

                    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/276bbe51-2df3-4a77-862d-2835f367844d/Untitled.png)

                2. `확장 프로그램 추가` 버튼을 눌러서 설치를 진행해주세요.

                    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/775d4ffb-9a25-4f1f-aa8c-2df5eca4719a/Untitled.png)

                3. JSON 샘플 웹페이지를 새로고침 해주시면 아래와 같이 좀 더 가독성 있게 JSON 구조를 볼 수 있습니다.

                    ![Screen Shot 2022-09-18 at 6.46.32 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a00eaad3-598a-48ac-8d11-945349a45da6/Screen_Shot_2022-09-18_at_6.46.32_PM.png)


            &amp;lt;aside&amp;gt;
              JSON은 Dart 문법 시간에 배운 **문자열, 숫자, 배열, 맵(딕셔너리)** 형태 구조로 이루어져 있어 쉽게 익힐 수 있습니다.

            자세한 사용 방법은, 실습하면서 배워보도록 하겠습니다.

            &amp;lt;/aside&amp;gt;

        - 상태 코드

            &amp;lt;aside&amp;gt;
              상태 코드란 HTTP 응답시 요청이 성공했는지 실패했는지 한 번에 알 수 있는 약속된 숫자입니다.

            &amp;lt;/aside&amp;gt;

            &amp;lt;aside&amp;gt;
              자주 사용하는 상태코드

            200 : 성공
            4xx : 잘못된 요청
            5xx : 서버 문제로 실패

            좀 더 자세한 상태코드 종류는 아래 링크를 참고해 주세요.

            - **[[코드스니펫] HTTP 상태코드](https://developer.mozilla.org/ko/docs/Web/HTTP/Status)**

                ```dart
                https://developer.mozilla.org/ko/docs/Web/HTTP/Status
                ```

            &amp;lt;/aside&amp;gt;


    ![_http (1).png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2c13ec2d-379c-4fc3-a46c-50cb451dc2a7/_http_(1).png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) Google Book API 문서 읽어보기&lt;/p&gt;
  &lt;aside&gt;
    **API** 또한 웹을 이용하기 때문에 **HTTP** 규칙을 따릅니다. 따라서 API 문서를 보실 때 다음 내용을 확인하시면 됩니다.

&lt;p&gt;  &lt;strong&gt;요청&lt;/strong&gt; : URL / 메소드 / 파라미터&lt;br&gt;  &lt;strong&gt;응답&lt;/strong&gt; : 형식&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  그럼 직접 API 문서를 읽어볼까요? 아래 코드스니펫을 복사해서 새 탭에서 열면 구글 책 검색 API 문서가 열립니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developers.google.com/books/docs/v1/reference/volumes/list&quot;&gt;[코드스니펫] google 책 검색 API 문서&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://developers.google.com/books/docs/v1/reference/volumes/list&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;aside&amp;gt;
  **요청(Request)**

- URL : `https://www.googleapis.com/books/v1/volumes`
- method : GET
- Parameter : `q=검색어`

![bookApi (1).png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c691e45b-6c91-421c-9d0c-0934dca56b7a/bookApi_(1).png)

&amp;lt;/aside&amp;gt;

&amp;lt;aside&amp;gt;
  **응답(Response)**

- JSON

![bookApiResponse.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8a170df0-5846-422f-8537-30f84495a4ac/bookApiResponse.png)

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) Google Book API 사용해보기&lt;/p&gt;
  &lt;aside&gt;
    웹 브라우저 주소창에 검색하면 GET 방식 요청을 보낼 수 있으니, 직접 API를 호출해 보도록 합니다. 아래 코드 스니펫을 복사해서 새 탭에서 열어주세요.

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.googleapis.com/books/v1/volumes?q=%EA%B3%A0%EC%96%91%EC%9D%B4&quot;&gt;[코드스니펫] google 책 검색 API 예제&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://www.googleapis.com/books/v1/volumes?q=%EA%B3%A0%EC%96%91%EC%9D%B4&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;

&lt;p&gt;&lt;code&gt;q&lt;/code&gt;라는  파라미터에 &lt;code&gt;고양이&lt;/code&gt; 라는 단어를 검색하여 책 데이터를 가져왔습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/75a1d82b-41ef-4b83-9f92-26f9cbe7749a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;aside&gt;
  이번에는 Dart 코드로 동일한 요청을 보내보도록 하겠습니다. 아래 코드 스니펫을 복사해서 새 탭에서 붙여넣으면 DartPad가 열립니다.
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=1519f6b1a7ac26cf42e4e302750650e0&quot;&gt;[코드스니펫] DartPad google 책 검색 API&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/?id=1519f6b1a7ac26cf42e4e302750650e0&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;

&lt;p&gt;&lt;code&gt;Run&lt;/code&gt; 버튼을 눌러 보면 Console에 JSON 응답이 출력되는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aac25b23-90c4-4b47-b1af-e59ed69a6c2c/Screen_Shot_2022-09-18_at_7.05.42_PM.png&quot; alt=&quot;Screen Shot 2022-09-18 at 7.05.42 PM.png&quot;&gt;&lt;/p&gt;
&lt;aside&gt;
  HTTP 요청을 보내면 응답이 받기까지 시간이 걸립니다. 이 때 응답이 올 때까지 기다렸다가 다음 코드를 실행할 수도 있고, 기다리지 않고 바로 다음 코드를 실행할 수도 있습니다.

&lt;p&gt;이와 같이 &lt;strong&gt;실행하는데 시간이 걸리는 코드를 다루는 방법&lt;/strong&gt;을 배워보도록 하겠습니다.&lt;/p&gt;
&lt;/aside&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;02. 비동기 이해하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) &lt;strong&gt;동기&lt;/strong&gt; &amp;amp; &lt;strong&gt;비동기&lt;/strong&gt;&lt;/p&gt;
  &lt;aside&gt;
    **한가지 요리가 끝나야 다음 요리를 하는 요리사**의 조리 순서는 아래 첫 번째 그래프와 같고, **한가지 요리가 끝나기 전에 다음 요리를 시작하는 요리사**의 조리 순서는 두 번째 그래프와 같습니다.

&lt;p&gt;  &lt;strong&gt;동기&lt;/strong&gt; : 하나의 일이 끝난 뒤 다음 일을 하는 방식&lt;br&gt;  &lt;strong&gt;비동기&lt;/strong&gt; : 하나의 일이 끝나기 전에 동시에 다른 일을 하는 방식&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/baeeba16-35b7-4a63-9021-c68648cdd3d1/async__sync.png&quot; alt=&quot;비동기 방식이 소요시간은 더 짧습니다.&quot;&gt;&lt;/p&gt;
&lt;p&gt;  비동기 방식이 소요시간은 더 짧습니다.&lt;/p&gt;
  &lt;aside&gt;
    오래 걸리는 작업은 **비동기** 방식으로 처리하는 것이 좋습니다.

&lt;p&gt;  지금까지 우리가 작성한 대부분의 코드는 &lt;strong&gt;동기&lt;/strong&gt; 방식으로 작동하는 코드입니다.&lt;br&gt;  그러나 &lt;strong&gt;네트워크 요청&lt;/strong&gt;, &lt;strong&gt;데이터베이스 접근&lt;/strong&gt; 처럼 &lt;strong&gt;시간이 오래 걸리는 작업&lt;/strong&gt;들은 기본적으로 &lt;strong&gt;비동기&lt;/strong&gt; 방식으로 동작하도록 되어 있습니다.&lt;/p&gt;
&lt;p&gt;  하지만 HTTP 요청을 보낸 뒤 응답 받은 결과를 보여주는 코드를 작성한다고 생각해 봅시다.&lt;/p&gt;
&lt;p&gt;  &lt;strong&gt;비동기&lt;/strong&gt; 방식으로 동작하는 경우, &lt;strong&gt;응답을 받기전에 결과를 보여주려고 하며 에러가 발생&lt;/strong&gt;하기 때문에 이 상황에서는 &lt;strong&gt;동기&lt;/strong&gt; 방식으로 동작하는게 좋습니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3f29e8cf-6df2-4778-90b2-9ab9e98ec283/http_(1).png&quot; alt=&quot;http (1).png&quot;&gt;&lt;/p&gt;
  &lt;aside&gt;
    이런 문제를 해결하기 위해서는
  1) 요청을 보내기만 하면 되는 코드의 경우 **비동기** 방식으로 처리하고
  2) 요청에 대한 응답을 받아서 보여줘야 하는 코드는 **동기** 방식으로 처리하는 것이 좋겠군요!

&lt;p&gt;  이를 위해 &lt;strong&gt;비동기&lt;/strong&gt; 방식으로 동작하는 코드를 &lt;strong&gt;동기&lt;/strong&gt; 방식으로 실행하는 방법을 배워보도록 하겠습니다.&lt;/p&gt;
  &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) async &amp;amp; await&lt;/p&gt;
  &lt;aside&gt;
    `Future.delayed()` 함수는 대표적인 **비동기** 방식으로 동작하는 코드로 `1초 뒤에 실행해라`와 같은 코드를 작성할 때 사용합니다.

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/33be2ed0-a5ad-4341-b14b-135e14e9e070/future.delayed.png&quot; alt=&quot;future.delayed.png&quot;&gt;&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  코드스니펫을 복사해 새 탭에서 붙여넣어 DartPad를 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=1984d6cae83118a401f59f8f0034c8e4&quot;&gt;[코드스니펫] DartPad async&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/?id=1984d6cae83118a401f59f8f0034c8e4&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;aside&amp;gt;
  console을 통해 실행 순서를 보면, `print(&amp;quot;2&amp;quot;);`의 경우 1초 뒤 실행되기 때문에 **비동기** 방식으로 실행되어 1 → 3 → 2 순서대로 실행된 것을 볼 수 있습니다.

&amp;lt;/aside&amp;gt;

![Screen Shot 2022-09-18 at 7.25.47 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/809b2f90-9be0-4de6-b914-9dd36351a865/Screen_Shot_2022-09-18_at_7.25.47_PM.png)

&amp;lt;aside&amp;gt;
  위 코드를 **동기** 방식으로 실행해 1 → 2 → 3 순서대로 출력되도록 만들어 보겠습니다. 코드스니펫을 복사해서 새 탭에 붙여넣어 DartPad를 열어주세요.

- **[[코드스니펫] DartPad async &amp;amp; await](https://dartpad.dev/?id=83d6c5a955cc9230d3f44ea5106236a7)**

    ```dart
    https://dartpad.dev/?id=83d6c5a955cc9230d3f44ea5106236a7
    ```


![Screen Shot 2022-09-18 at 7.26.18 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1c50e0ca-cc6f-490d-bca8-7be11c43ce32/Screen_Shot_2022-09-18_at_7.26.18_PM.png)

&amp;lt;/aside&amp;gt;

&amp;lt;aside&amp;gt;
  **비동기** 코드인 7번째 줄 앞에 `await`을 붙이고, 해당 코드가 속해있는 `main`함수의 소괄호와 중괄호 사이에 `async`라고 적어주면 **비동기** 방식으로 실행되는 코드를 **동기** 방식으로 실행할 수 있습니다.

![async &amp;amp; await.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b94b2f90-89c9-46b8-b7fd-dd65473a2c70/async__await.png)

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) Future&lt;/p&gt;
  &lt;aside&gt;
    `async/await`을 사용하는 함수는 반환값을 미래에 해당 타입을 반환한다는 의미로 `Future&lt;반환타입&gt;` 형태로 표시해 줍니다.

  &lt;/aside&gt;

&lt;p&gt;  코드스니펫을 복사해 새 탭에서 붙여넣어 DartPad를 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=a6734196a437a06985388c183dc78c44&quot;&gt;[코드스니펫] DartPad Future&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/?id=a6734196a437a06985388c183dc78c44&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/abee377c-58b1-456b-b175-1c45e2f2f4fd/Untitled.png)

&amp;lt;aside&amp;gt;
  `async`가 붙은 함수는 내부에 코드 실행이 완료되기를 기다리는 `await` 코드가 있을 수 있기 때문에, 미래에 언젠간 값을 반환한다는 의미로 반환 값의 타입을 `Future&amp;lt;반환타입&amp;gt;` 형태로 작성해 줍니다.

![future (1).png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/30ef794c-2efd-4d92-b66a-4976bc6c1fd6/future_(1).png)

실행 순서를 보면 9번째 줄에 `await`이 되어있으므로, 해당 코드가 끝날 때까지 기다립니다. 따라서 13번째 반환 값은 9번 째 줄이 끝난 미래에 반환이 되므로 8번째 줄에 반환 타입을 `Future&amp;lt;String&amp;gt;` 이라고 표시한다고 이해하시면 됩니다.

&amp;lt;/aside&amp;gt;

&amp;lt;aside&amp;gt;
  해당 함수를 호출하는 쪽에서도 `await`을 함수 앞에 붙여주면 **동기** 방식으로 결과가 응답될 때까지 기다리기 때문에 `Future`를 벗겨낸 타입으로 반환 값을 받을 수 있습니다.

![future (2).png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9d1e291a-87c2-448a-92e7-2a80bb2b730f/future_(2).png)

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) 요약&lt;/p&gt;
  &lt;aside&gt;
    **동기** : 하나의 일이 끝난 뒤 다음 일을 하는 방식
  **비동기** : 하나의 일이 끝나기 전에 동시에 다른 일을 하는 방식

  &lt;/aside&gt;

  &lt;aside&gt;
    개발시 **비동기** 방식으로 작동하는 상황들

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;네트워크 요청하여 데이터 받아오기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파일 읽기 / 쓰기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;데이터베이스 쓰기&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
  **비동기** 코드 앞에 `await`을 붙이고 해당 코드가 속한 함수에 `async`를 넣어주면 **비동기** 방식의 코드를 **동기** 방식으로 실행할 수 있습니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;Future&amp;lt;String&amp;gt; getName() async {
  String name = await HTTP요청;
  return name;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;async&lt;/code&gt;가 선언된 함수의 반환 값은 &lt;code&gt;Future&amp;lt;반환값&amp;gt;&lt;/code&gt; 형식으로 표현합니다.&lt;/p&gt;
&lt;/aside&gt;

&lt;p&gt;배운 이론들을 활용해 프로젝트에서 API를 사용해 보도록 하겠습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;03. 프로젝트 준비 (왓챠피디아)&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) Flutter 프로젝트 생성&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;VSCode를 실행해주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Command Palette` 단축키
 window : `Ctrl + Shift + P`
 macOS : `Cmd + Shift + P`

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/79df0a20-90b1-47bb-a58a-4b27ec836f34/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;명령어를 검색하는 팝업창이 뜨면, &lt;code&gt;flutter&lt;/code&gt;라고 입력한 뒤 &lt;code&gt;Flutter: New Project&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a6058de-bbf7-410f-9dc3-a96d6bd7ac58/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Application&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c7145f5-c6f6-4306-b78e-e3bb31e24710/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트를 시작할 폴더를 선택하는 화면이 나오면 &lt;code&gt;flutter&lt;/code&gt; 폴더를 선택한 뒤 &lt;code&gt;Select a folder to create the project in&lt;/code&gt; 버튼을 눌러 주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트 이름을 &lt;code&gt;watcha_pedia&lt;/code&gt;으로 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/981569e8-808f-4850-a6be-3e60b0cc2324/Screen_Shot_2022-09-18_at_9.00.52_PM.png&quot; alt=&quot;Screen Shot 2022-09-18 at 9.00.52 PM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 만약 중간에 아래와 같은 팝업이 뜬다면, 체크박스를 선택한 뒤 파란 버튼을 클릭해주세요. (팝업이 안보이시면 넘어가주세요!)&lt;/p&gt;
 &lt;aside&gt;
   아래 팝업에 대한 자세한 사항은 [링크](https://stackoverflow.com/questions/67914668/vs-code-do-you-trust-the-authors-of-the-files-in-this-folder)를 참고해주세요.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6827f86-f59a-4808-9d8d-75245b9ec10d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음과 같이 프로젝트가 생성되고, 다음으로 불필요한 힌트를 숨기도록 하겠습니다.&lt;/p&gt;
&lt;p&gt; 코드스니펫을 복사해서 &lt;code&gt;analysis_options.yaml&lt;/code&gt; 파일에 24번째 라인 뒤에 붙여 넣어주세요.&lt;/p&gt;
 &lt;aside&gt;
   아래 내용은 학습 단계에서 불필요한 내용을 화면에 표시하지 않도록 설정하는 과정입니다.

 &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] analysis_options.yaml&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      prefer_const_constructors: false
      prefer_const_literals_to_create_immutables: false&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8470956b-ccc2-4d75-86d8-83042bafaf22/Untitled.png)

    - 어떤 의미인지 궁금하신 분들을 위해
        - `main.dart` 파일을 열어보시면 파란 실선이 있습니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d1393a08-a362-4be3-bd9b-835e184f5ef3/Untitled.png)

        - 파란 줄은, 개선할 여지가 있는 부분을 VSCode가 알려주는 표시입니다.

            12번째 라인에 마우스를 올리면 아래와 같이 설명이 뜹니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7fd5cee4-d935-4d2d-9c35-e11d4f3c6cae/Untitled.png)

            위젯이 변경될 일이 없기 때문에 `const`라는 키워드를 앞에 붙여 상수로 선언하라는 힌트입니다.

            &amp;lt;aside&amp;gt;
              상수로 만들면 어떤 이점이 있나요?

            상수로 선언된 위젯들은 화면을 새로 고침 할 때 해당 위젯들은 변경을 하지 않기 때문에 스킵하여 성능상 이점이 있습니다.

            &amp;lt;/aside&amp;gt;

            아래와 같이 `Icon`앞에 `const` 키워드를 붙여주시면 됩니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2aef9ee8-c4ef-4c42-bd08-d358c4620341/Untitled.png)

        - 지금은 학습 단계이니 눈에 띄지 않도록 해주도록 하겠습니다.
8. Provider 패키지를 시작하는 코드에서 사용하고 있으므로 패키지 설치부터 진행하도록 하겠습니다. `View` → `Terminal`을 선택해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/db3f319b-cb6e-4e78-96ce-52685882adbe/Untitled.png)

    아래 코드 스니펫을 복사해서 터미널에 붙여넣고 실행해 주세요.

    - **[코드스니펫] provider 패키지 설치**

        ```dart
        flutter pub add provider
        ```


    ![Screen Shot 2022-09-18 at 9.52.23 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/28d0f310-52b4-4da2-b179-4890f65467eb/Screen_Shot_2022-09-18_at_9.52.23_PM.png)

    `pubspec.yaml`을 열어서 39번째 라인에 `provider`가 있으면 설치가 잘 되신 겁니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/89172053-32a7-489d-837d-e51351df92bf/Untitled.png)

9. 아래 `lib/main.dart` 코드스니펫을 복사해서 기존 내용을 모두 지우고, `main.dart` 파일에 붙여 넣고 저장해 주세요.
    - **[코드스니펫]  main.dart**

        ```dart
        import &amp;#39;package:flutter/material.dart&amp;#39;;
        import &amp;#39;package:provider/provider.dart&amp;#39;;

        import &amp;#39;book_service.dart&amp;#39;;

        void main() {
          runApp(
            MultiProvider(
              providers: [
                ChangeNotifierProvider(create: (context) =&amp;gt; BookService()),
              ],
              child: const MyApp(),
            ),
          );
        }

        class MyApp extends StatelessWidget {
          const MyApp({Key? key}) : super(key: key);

          @override
          Widget build(BuildContext context) {
            return MaterialApp(
              debugShowCheckedModeBanner: false,
              home: HomePage(),
            );
          }
        }

        class HomePage extends StatefulWidget {
          HomePage({Key? key}) : super(key: key);

          @override
          State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
        }

        class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
          var bottomNavIndex = 0;
          @override
          Widget build(BuildContext context) {
            return Scaffold(
              body: [
                SearchPage(),
                LikedBookPage(),
              ].elementAt(bottomNavIndex),
              bottomNavigationBar: BottomNavigationBar(
                selectedItemColor: Colors.black,
                unselectedItemColor: Colors.grey,
                showUnselectedLabels: true,
                selectedFontSize: 12,
                unselectedFontSize: 12,
                iconSize: 28,
                type: BottomNavigationBarType.fixed,
                onTap: (value) {
                  setState(() {
                    bottomNavIndex = value;
                  });
                },
                items: [
                  BottomNavigationBarItem(
                    icon: Icon(Icons.search),
                    label: &amp;#39;검색&amp;#39;,
                  ),
                  BottomNavigationBarItem(
                    icon: Icon(Icons.star),
                    label: &amp;#39;좋아요&amp;#39;,
                  ),
                ],
                currentIndex: bottomNavIndex,
              ),
            );
          }
        }

        class SearchPage extends StatelessWidget {
          SearchPage({super.key});

          @override
          Widget build(BuildContext context) {
            return Scaffold(
              appBar: AppBar(
                backgroundColor: Colors.white,
                toolbarHeight: 80,
                title: TextField(
                  onSubmitted: (value) {},
                  cursorColor: Colors.grey,
                  decoration: InputDecoration(
                    prefixIcon: Icon(Icons.search, color: Colors.grey),
                    hintText: &amp;quot;작품, 감독, 배우, 컬렉션, 유저 등&amp;quot;,
                    border: OutlineInputBorder(
                      borderSide: BorderSide(color: Colors.white),
                      borderRadius: BorderRadius.all(Radius.circular(10)),
                    ),
                    focusedBorder: OutlineInputBorder(
                      borderSide: BorderSide(color: Colors.grey),
                      borderRadius: BorderRadius.all(Radius.circular(10)),
                    ),
                  ),
                ),
              ),
              body: Center(
                child: Text(&amp;quot;검색&amp;quot;),
              ),
            );
          }
        }

        class LikedBookPage extends StatelessWidget {
          const LikedBookPage({super.key});

          @override
          Widget build(BuildContext context) {
            return Scaffold(
              body: Center(
                child: Text(&amp;quot;좋아요&amp;quot;),
              ),
            );
          }
        }
        ```

10. 그리고 `lib` 폴더 밑에 `book.dart`와 `book_service.dart` 파일도 만들어주신 뒤 아래 코드스니펫을 붙여 넣어주세요.

    &amp;lt;aside&amp;gt;
      `Book` 클래스는 책에 대한 정보를 담을 클래스 입니다.

    `BookService`는 `Book`에 대한 CRUD를 담당하는 클래스 입니다.

    &amp;lt;/aside&amp;gt;

    - **[코드스니펫]  book.dart**

        ```dart
        class Book {}
        ```

    - **[코드스니펫]  book_service.dart**

        ```dart
        import &amp;#39;package:flutter/material.dart&amp;#39;;

        import &amp;#39;book.dart&amp;#39;;

        class BookService extends ChangeNotifier {
          List&amp;lt;Book&amp;gt; bookList = []; // 책 목록
        }
        ```


    최종적으로 `lib` 폴더 밑에 아래와 같이 파일들이 있으면 됩니다.

    ![Screen Shot 2022-09-18 at 9.38.21 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b4bf8f96-ef8e-4164-9d2b-d25cd968b318/Screen_Shot_2022-09-18_at_9.38.21_PM.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 에뮬레이터 실행하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;VSCode 우측 하단에 &lt;code&gt;Chrome (web-javascript)&lt;/code&gt;를 클릭해주세요.&lt;br&gt;(에뮬레이터가 이미 실행중이라면 3번으로 이동해 주세요.)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5d0db764-7e21-4261-8c19-94162c4d0ba8/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Start Pixel 2 API 29 mobile emulator&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e0e7243-7d95-4b57-99df-08fd28e1b4d7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 잠시 기다리면 Android 에뮬레이터가 실행됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf5640d3-c9a9-4105-bd40-9dcb309412f5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VSCode 우측 상단에 &lt;strong&gt;아래 화살표&lt;/strong&gt;를 눌러 &lt;code&gt;Run Without Debugging&lt;/code&gt;을 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fba923c5-b4b0-4ddb-b381-ae8686c0315c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   `Start Debugging`으로 실행해도 무방합니다. 디버깅 모드는 특정 라인에서 앱 실행을 멈추고 해당 변수에 어떤 값이 들어있는지 볼 수 있지만 `Run without debugging`이 실행 속도가 더 빨라 안내를 위와 같이 드렸습니다  

 &lt;/aside&gt;

&lt;p&gt; 에뮬레이터에 아래와 같이 탭으로 전환되는 화면이 나오면 준비 완료!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4ab5878a-9268-4941-b3e6-0aa91163c2de/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;04. API 연결하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;구현 목표&lt;/p&gt;
  &lt;aside&gt;
    Google Book API 를 활용해 왓챠피디아 앱에 **책 검색 기능**을 구현합니다
  사용 패키지: Dio(네트워크 통신), Provider(상태 관리)

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eb3e9100-55be-4a1c-9aab-347effb83708/Untitled.gif&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) 통신 패키지 dio 설치하기&lt;/p&gt;
  &lt;aside&gt;
    dio는 Dart에서 HTTP 요청을 보낼 수 있도록 도와주는 통신 패키지입니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사해 새 탭에서 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://pub.dev/packages/dio/install&quot;&gt;[코드스니펫] Pub.dev / dio / Installing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://pub.dev/packages/dio/install&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;dio 패키지의 Installing 페이지가 열리면 &lt;code&gt;flutter pub add dio&lt;/code&gt; 우측 아이콘을 눌러서 복사해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/96d66ec6-e77b-4825-b282-f2867e136cbb/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VSCode &lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Terminal&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/51e27651-ff0f-4223-9f57-fcc170e9766c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Terminal&lt;/code&gt;창에 복사한 &lt;code&gt;flutter pub add dio&lt;/code&gt;를 붙여넣고 엔터를 눌러 실행해 주세요.&lt;br&gt;아래와 같이 나오면 정상적으로 작동한 것입니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0c036a83-2642-47af-b08d-49a731224be6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;pubspec.yaml&lt;/code&gt; 파일을 열어서 아래와 같이 40번째 라인에 &lt;code&gt;dio&lt;/code&gt;가 있으면 설치가 완료된 것입니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/49b27221-7d07-4701-b18e-732f52555db5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) dio 사용법&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;아래 코드는 &lt;code&gt;GET&lt;/code&gt; 메소드로 &lt;code&gt;URL&lt;/code&gt;로 요청을 보내는 코드입니다. dio 패키지는 매우 직관적이고 쉽게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  main() {
      Dio().get(&amp;quot;URL&amp;quot;);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;HTTP 요청은 응답까지 시간이 걸리기 때문에 &lt;strong&gt;비동기&lt;/strong&gt; 코드입니다.  따라서 &lt;strong&gt;동기&lt;/strong&gt; 방식으로 작동하게 하려면 아래와 같이 &lt;code&gt;async&lt;/code&gt; &amp;amp; &lt;code&gt;await&lt;/code&gt;을 추가해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  main() async {
      Response result = await Dio().get(&amp;quot;URL&amp;quot;);
      print(result.data); // data 안에 응답 내용이 들어 있습니다.
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) Google Book API 응답값 자세히 보기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;일단 API를 호출해 보도록 하겠습니다. 코드스니펫을 복사해 새 탭에서 열어주세요.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.googleapis.com/books/v1/volumes?q=%EA%B3%A0%EC%96%91%EC%9D%B4&quot;&gt;[코드스니펫] google 책 검색 API 예제&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://www.googleapis.com/books/v1/volumes?q=%EA%B3%A0%EC%96%91%EC%9D%B4&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![Screen Shot 2022-09-18 at 11.49.35 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f722ad7b-42b5-4cae-948d-b4d0112d1de0/Screen_Shot_2022-09-18_at_11.49.35_PM.png)

&amp;lt;aside&amp;gt;
  위와 같이 GET방식으로 해당 URL로 요청시 JSON 응답이 옵니다.  
`thumbnail` 이라고 적힌 부분에 파란 링크를 클릭해보시면 책 표지 사진이 나옵니다.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1171cf1d-9e93-46aa-bd43-7605fd043ef4/Untitled.png)

&amp;lt;/aside&amp;gt;

&amp;lt;aside&amp;gt;
  Google Book API에 대한 자세한 사항은 아래 코드스니펫 링크를 참고해주세요.

- **[[코드스니펫] Google Book API 문서](https://developers.google.com/books/docs/v1/reference/volumes/list)**

    ```dart
    https://developers.google.com/books/docs/v1/reference/volumes/list
    ```

&amp;lt;/aside&amp;gt;

1. 주소창에 `q=고양이` 라고 되어있는 부분을 `q=dog`로 변경해 보면 아래와 같은 결과가 나옵니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8ad130cb-92db-4864-b2cf-c56eba35b8de/Untitled.png)

    `totalItems` 라는 요소 안에 조회된 책의 전채 개수가 나오는 것을 확인할 수 있습니다.

    `items` 안에 있는 토글 버튼(화살표 표시) 를 눌러 요소를 두개 접어봅시다. `items` 안에 각각의 책에 대한 정보가 마치 `Map` 과 같은 형태로 담겨 있습니다.

    ![Screen Shot 2022-09-19 at 12.01.06 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ad41601b-38c4-434b-80fb-8abd97b0bcc0/Screen_Shot_2022-09-19_at_12.01.06_AM.png)

    &amp;lt;aside&amp;gt;
      위 JSON 데이터의 구조는 Dart 문법에서 나오는 `List`와 `Map`의 조합과 같다고 보셔도 좋습니다.

    | 자료형 | 설명 | 예시 |
    | --- | --- | --- |
    | List | 배열 | [1, 2, 3] |
    | Map | {key : value} 형태의 자료형 | {
      &amp;#39;name&amp;#39;: &amp;#39;철수&amp;#39;,
      &amp;#39;age&amp;#39;: 20
    } |
    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      예를 들어 3번째 책의 `title` 을 가져오고 싶다면 아래와 같은 문법으로 데이터에 접근하면 됩니다.

    ```dart
    // data 에 위 json 데이터가 담겨온다고 가정
    String title = data[&amp;#39;items&amp;#39;][2][&amp;#39;volumeInfo&amp;#39;][&amp;#39;title&amp;#39;] 

    print(title) // &amp;quot;Everyday Dog&amp;quot;
    ```

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) Book 클래스 수정하기&lt;/p&gt;
  &lt;aside&gt;
    우리는 이 중에서 아래와 같이 빨간색 동그라미로 표시한 데이터만 추출해서 `Book` 클래스에서 사용하도록 하겠습니다.

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d492c445-b4f6-4714-a308-0382800798ed/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  앱 화면에는 아래와 같이 표시되도록 합니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c346d39-95c5-45df-8fac-724de4458881/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;json 데이터의 값들을 활용해 &lt;code&gt;Book&lt;/code&gt; 객체를 만들고 이를 화면에 띄워주는 과정이 필요하겠죠.&lt;/p&gt;
&lt;p&gt; 먼저 Book 클래스가 &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;subtitle&lt;/code&gt;, &lt;code&gt;thumbnail&lt;/code&gt;, &lt;code&gt;previewLink&lt;/code&gt; 요소를 가질 수 있게끔 &lt;code&gt;book.dart&lt;/code&gt; 파일을 수정해줍니다. 코드스니펫을 복사해 &lt;code&gt;book.dart&lt;/code&gt; 파일 내의 내용을 모두 지우고 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] book.dart&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  class Book {
    String id;
    String title;
    String subtitle;
    String thumbnail; // 썸네일 이미지 링크
    String previewLink; // ListTile 을 눌렀을 때 이동하는 링크

    Book({
      required this.id,
      required this.title,
      required this.subtitle,
      required this.thumbnail, 
      required this.previewLink, 
    });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8c770840-ef46-4dbf-9606-bc3e5a3f735c/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;5) Google Book API 호출하기&lt;/p&gt;
  &lt;aside&gt;
    검색어를 입력하고 `완료`를 누르면 Google Book API 를 호출하도록 합니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    Google Book API 에서 특정 키워드로 검색을 진행하려면, 아래 이미지와 같이 `?q=` 뒤에 검색어를 입력하고 API 를 호출하면 됩니다.

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c691e45b-6c91-421c-9d0c-0934dca56b7a/bookApi_(1).png&quot; alt=&quot;bookApi (1).png&quot;&gt;&lt;/p&gt;
  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;키보드에서 완료 버튼을 누르면, &lt;code&gt;TextField&lt;/code&gt; 에 입력된 문자열을 인자로 받는 &lt;code&gt;onSubmitted&lt;/code&gt; 함수가 실행됩니다. &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7e4929ec-cbcd-499a-8513-46ba50896a90/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;BookService&lt;/code&gt; 내에 검색 로직을 추가한 뒤, 이를 &lt;code&gt;onSubmitted&lt;/code&gt; 에서 호출하도록 하겠습니다.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해 &lt;code&gt;book_service.dart&lt;/code&gt; 6번째 줄 맨 뒤에 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 책 목록 search 함수&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
    void search(String q) async {
      if (q.isNotEmpty) {
        Response res = await Dio().get(
          &amp;quot;https://www.googleapis.com/books/v1/volumes?q=$q&amp;amp;startIndex=0&amp;amp;maxResults=40&amp;quot;,
        );
        List items = res.data[&amp;quot;items&amp;quot;];
        print(items);
      }
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6883dea5-fbac-4b66-b500-decdb75b40e7/Untitled.png)

3. 에러를 해결하겠습니다. 10번째 줄의 `Dio()` 을 클릭한 뒤 Quick Fix(`Ctrl/Cmd + .`)를 눌러 `Import library ‘package:dio/dio.dart’` 를 선택해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/725da2df-eb4e-4a8e-aa89-2bbb683c6f97/Untitled.png)

4. 첫번째 줄에 Import 구문이 추가되며 에러가 해결되었습니다. 아까 설치해준 `Dio` 패키지를 이제 사용할 수 있습니다.

    ![Screen Shot 2022-09-19 at 1.49.06 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55679400-1d20-455b-b18a-d1d0689e6b47/Screen_Shot_2022-09-19_at_1.49.06_AM.png)

5. `SearchPage` 로 돌아와  `BookService` 의 `search` 함수를 사용할 수 있도록 해봅시다. 

    저번 시간에 배운 `Provider` 내용을 복기해봅시다. Service 내의 변수나 함수에 접근하기 위해서는 아래 두가지 방법을 사용합니다.

    &amp;lt;aside&amp;gt;
      Provider 에서 Service 를 사용하는 방법

    1. `Consumer&amp;lt;클래스명&amp;gt;` : 클래스 정보 갱신시 화면을 새로고침 해야 할 때 사용
    2. `context.read&amp;lt;클래스명&amp;gt;` : 1회성으로 클래스 접근할 때 사용 (화면 새로고침이 필요 없을 때)
    &amp;lt;/aside&amp;gt;

    우리는 검색이 일어날 때마다 화면에 책 목록을 다시 그려줄 것이므로, `Consumer` 를 사용해야 합니다.

    `main.dart` 의 79번째 줄의 `Scaffold` 를 누르고, 왼쪽의 전구를 클릭해 `Wrap with Builder` 를 선택합니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d76c4285-7faf-48bf-bfc7-77155363ea95/Untitled.png)

    아래와 같이 `Scaffold` 위젯을 `Builder` 위젯이 감쌉니다. `Consumer` 위젯과 형식이 비슷한 `Builder` 를 사용해 모양을 잡아줬습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/945394a9-d0d2-4e15-a04b-799356ca346e/Untitled.png)

6. 아래 이미지와 같이 79번째 라인의 `Builder`를 `Consumer&amp;lt;BookService&amp;gt;`로 변경해주세요. 

    `context` 뒤에 `bookService` 와 `child` 도 추가해줍니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e172af43-21dc-4931-badb-859a84bfa6c8/Untitled.png)

    그리고 줄 정렬을 예쁘게 해주기 위해 105번째 라인의 중괄호와 소괄호 사이에 콤마(`,`)를 찍은 뒤 저장해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/505edd08-2861-4d71-86d6-c0263d2c040b/Untitled.png)

    저장하면 코드가 정렬됩니다.

    &amp;lt;aside&amp;gt;
      **복습:** `Consumer&amp;lt;BookService&amp;gt;`는 위젯 트리를 타고 올라가 **Provider**로 등록된 **BookService**를 찾습니다. 찾은 **BookService**를 80번째 라인의 두 번째 파라미터인 `bookService` 에 담아줍니다.
    우리는 `bookService` 변수를 통해 **BookService** 안에 있는 변수에 접근해 수정하고, 화면을 새로고침할 수 있습니다.

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      `BookService`에서 값을 변경하고 `notifyListeners();`를 호출하면, 해당 서비스를 `Consumer`로 등록한 모든 위젯의 `builder` 함수가 재호출 되면서 화면이 갱신 됩니다.

    &amp;lt;/aside&amp;gt;

7. 86번째 줄 `onSubmitted` 의 중괄호 사이에 아래 코드스니펫을 붙여넣습니다.
    - **[코드스니펫] TextField 의 onSubmitted**

        ```dart
        bookService.search(value);
        ```


    저장하면 아래 이미지와 같이 코드가 정렬됩니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c1260462-b5e2-4366-9dd5-9afb0d152096/Untitled.png)

8. 이제 키보드에 값을 입력하고 Debug Console 을 확인해볼까요? VS Code 에서 `View → Debug Console` 를 눌러 콘솔을 켜줍니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/25bb78ef-a5f0-4bde-b411-db7bc6775fd1/Untitled.png)

    아래와 같이 dog 라는 키워드를 입력하고 검색을 해보면 콘솔에 리스트가 출력되는 것을 확인할 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a3ff8ec6-b13c-4c28-b29d-deb8d2dd6eb6/Untitled.png)

    이 리스트의 정체는 무엇일까요? 아까 **BookService** 에 만들어둔 `search` 함수를 찬찬히 뜯어봅시다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/64154232-0b2d-445f-8f9c-2f0c809f573f/Untitled.png)

    응답으로 온 데이터 (`res.data`) 에 `items` 라는 Key 로 Value 를 가져오는 코드네요!

    `res.data` 에 담겨오는 데이터는 바로…

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e4040b39-0799-4785-9d80-0d7b3cece952/Untitled.png)

    아까 보았던 **json 데이터**입니다. `totalItems` 아래에 `items` 에 있는 데이터가 우리가 만들어둔 `items` (`book_service.dart` 14번째 줄) 변수에 List 형식으로 담긴 것입니다.&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;6) JSON 파싱(parsing)&lt;/p&gt;
  &lt;aside&gt;
    JSON 데이터에서 원하는 값만 뽑아오는 작업을 파싱(parsing)이라고 부릅니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    목표 : Google Book API 를 호출해서 받아온 정보들을 `**Book**` 클래스의 객체로 만들고, 이를 `BookService`의 `bookList` 변수에 넣기

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;콘솔에서는 JSON 구조가 잘 안보이니, 샘플을 보면서 원하는 데이터만 뽑아보도록 하겠습니다. 코드스니펫을 복사해 새 탭에서 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.googleapis.com/books/v1/volumes?q=dog&amp;amp;startIndex=0&amp;amp;maxResults=40&quot;&gt;[코드스니펫] Google Book API 샘플&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://www.googleapis.com/books/v1/volumes?q=dog&amp;amp;startIndex=0&amp;amp;maxResults=40&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![items 에서 맨 위 두개의 요소는 토글 버튼을 눌러 접어뒀습니다.](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e4040b39-0799-4785-9d80-0d7b3cece952/Untitled.png)

    items 에서 맨 위 두개의 요소는 토글 버튼을 눌러 접어뒀습니다.

    보시는 바와 같이 큰 `Map(맵)` 내에 items 라는 `key` 의 `value` 로 `List(배열)` 가 있고, 이 안에 다시 `Map(맵)`이 들어 있는 형태입니다.

    &amp;lt;aside&amp;gt;
      List 안에 여러개의 `Map`이 들어있습니다. 따라서 **반복문**을 통해 해당 `List`에 있는 모든 `Map`을 각각 `Book` 클래스의 객체로 만들어 `bookList` 에 추가해주면 되겠군요!

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fb6daa22-f1c6-444d-b43b-25ab9c6ab172/Untitled.png)

2. 데이터를 잘 가져오는 데 성공했으니, 이제는 이를 사용하기 편하게 `Book` 클래스의 객체로 만들겠습니다. 위에서 `book.dart` 에 아래와 같이 `Book` 클래스를 만들었습니다. 코드를 다시 볼까요?

    ![Screen Shot 2022-09-19 at 2.49.25 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/01adf851-c3fb-47c5-80f2-c89b62727396/Screen_Shot_2022-09-19_at_2.49.25_AM.png)

    Book 클래스의 생성자(8번째 줄)를 보면 어떤 값들을 넣어줘야 하는지 알 수 있습니다. `id`, `title`, `subtitle`, `thumbnail`, `previewLink` 에 값을 넣어 Book 클래스의 객체를 만들어줍시다. 

    아래 코드스니펫을 복사해 `book_service.dart` 의 15번째 줄을 지우고 붙여넣어주세요.

    - **[코드스니펫] for 문 / Book 객체를 만들고 bookList 에 추가하는 함수**

        ```dart

              for (Map&amp;lt;String, dynamic&amp;gt; item in items) {
                Book book = Book(
                  id: item[&amp;#39;id&amp;#39;],
                  title: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;title&amp;#39;] ?? &amp;quot;&amp;quot;,
                  subtitle: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;subtitle&amp;#39;] ?? &amp;quot;&amp;quot;,
                  thumbnail: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;imageLinks&amp;#39;]?[&amp;#39;thumbnail&amp;#39;] ??
                      &amp;quot;https://thumbs.dreamstime.com/b/no-image-available-icon-flat-vector-no-image-available-icon-flat-vector-illustration-132482953.jpg&amp;quot;,
                  previewLink: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;previewLink&amp;#39;] ?? &amp;quot;&amp;quot;,
                );
                bookList.add(book);
              }
        ```


    ![?? 는 해당 변수에 담긴 값이 null일 경우, 즉 값이 없을 경우 뒤에 오는 값을 사용하겠다는 뜻입니다.](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e2a12f29-d6a8-4ba3-9d23-f32e7ad59a0f/Untitled.png)

    ?? 는 해당 변수에 담긴 값이 null일 경우, 즉 값이 없을 경우 뒤에 오는 값을 사용하겠다는 뜻입니다.

    `item` 이라는 변수에 담긴 `Map` 에서는 key 값으로 데이터들을 가져올 수 있습니다. 

    아래 json 데이터의 `items` 리스트 안에 포함된 각각의 요소가 위 코드의 `item` 변수에 들어갔다고 생각하시면 됩니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/92325eba-d64c-475c-837e-8f00d3d89d7d/Untitled.png)

    만들어진 Book 객체는 25번째 줄의 `bookList.add` 를 통해 `bookList` 에 추가됩니다.

3. 매번 검색을 할 때마다 `bookList` 에 담긴 값을 모두 비워줘야 해당 검색어에 대한 결과만을 볼 수 있겠죠. 아래 코드스니펫을 복사해 `book_service.dart` 9번째 줄 맨 뒤에 붙여넣습니다.
    - **[코드스니펫] bookList 비우기**

        ```dart

            bookList.clear(); // 검색 버튼 누를때 이전 데이터들을 지워주기

        ```


    ![Screen Shot 2022-09-19 at 3.34.27 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3b9d33a1-ded3-45f4-bc71-e77d6494239b/Screen_Shot_2022-09-19_at_3.34.27_AM.png)


&amp;lt;aside&amp;gt;
  `BookService`에서 Google Book API 데이터를 가져와 `bookList`에 저장하는 것까지 완성했습니다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;7) 검색 결과 보여주기&lt;/p&gt;
  &lt;aside&gt;
    `bookList` 에 있는 `Book` 들을 **SearchPage** 에서 보여주도록 하겠습니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;ListView.builder&lt;/code&gt; 를 사용해 bookList 내의 요소들을 화면에 보여주도록 하겠습니다.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해 &lt;code&gt;main.dart&lt;/code&gt; 파일의 104-106 번째 줄을 &lt;strong&gt;지우고&lt;/strong&gt; 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] SearchPage / ListView.builder&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.bookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  Book book = bookService.bookList.elementAt(index);
                  return ListTile();
                },
              ),
            ),&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/44ac70cb-05dc-4a2c-8a7f-c5b51d604413/Untitled.png)

2. 112번째 줄에 있는 Book 을 클릭해 아래 사진처럼 Quick Fix(`Ctrl/Cmd + .`)를 누른 뒤 `Import  library ‘book.dart’` 를 선택합니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/586a27ce-98b9-466f-8d6e-0ee18394bcaf/Untitled.png)

    4번째 줄에 Import 문이 생기며 에러가 사라졌습니다.

    ![Screen Shot 2022-09-19 at 10.04.08 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4ea57bdd-bd69-4a49-bb92-930f88bc23ca/Screen_Shot_2022-09-19_at_10.04.08_AM.png)

3. 이제 ListTile 내에 Book의 내용을 보여주도록 하겠습니다. ListTile 의 소괄호 사이에  `title: Text(book.title)` 라고 입력해보세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e16c593-35ef-43cf-b2c7-7d9ee7f36a4d/Untitled.png)

    위와 같이 검색 결과가 에뮬레이터에 잘 표시되는 것을 확인할 수 있습니다.

4. 이제 나머지 요소들도 화면에 보여주도록 하겠습니다. 아래 코드스니펫을 복사해 114번째 줄을 **지우고** 붙여넣어주세요.
    - **[코드스니펫] ListTile**

        ```dart
        return ListTile(
                          onTap: () {},
                          leading: Image.network(
                            book.thumbnail,
                            fit: BoxFit.fitHeight,
                          ),
                          title: Text(
                            book.title,
                            style: TextStyle(fontSize: 16),
                          ),
                          subtitle: Text(
                            book.subtitle,
                            style: TextStyle(color: Colors.grey),
                          ),
                          trailing: IconButton(
                            onPressed: () {},
                            icon: Icon(Icons.star_border),
                          ),
                        );
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a8a676df-6cd0-465b-adfe-2aa4a85c46e8/Untitled.png)

    `ListTile` 의 각 요소는 아래와 같이 배치됩니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7639eb36-5b37-43b8-87d0-7cf97013552e/Untitled.png)

    다른 검색어를 입력해보세요. 검색이 잘 구현되었나요? 

    화면에 검색 결과가 뜨지 않는군요. 이는 검색을 통해 `bookList` 는 수정되었지만, 이후 화면을 **새로고침하지 않아서** 생기는 문제입니다.

    `notifyListeners();` 를 `book_service.dart` 의 29번째 줄 아래에 추가해줍니다. 
    (위치에 주의해주세요!)

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4a23b383-94e7-4805-a5f7-bea5a93d6d36/Untitled.png)

    &amp;lt;aside&amp;gt;
      `notifyListeners();` 는 해당 `Service` 의 `Consumer` 로 등록된 모든 위젯의 `builder` 함수를 재호출해 화면을 새로고침합니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/036862cd-218a-401b-93e4-afe0c590ee1c/Untitled.png)

5. 이제 검색 결과는 잘 나오지만, `Debug Console` 에 아래와 같은 에러가 발생하고 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0af5da55-36c1-4648-8497-80b1ca2ed385/Untitled.png)

    이는 `BookService` 의 `search` 함수에서 이전 데이터를 지워줄 때 순간적으로 `bookList` 가 비어서 생기는 문제입니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/de463a28-727c-43f5-ae14-a37fd7fb1641/Untitled.png)

    아래 코드스니펫을 복사해 `main.dart` 112번째 줄 뒤에 붙여넣어주세요. 

    - **[코드스니펫] bookList 가 비어있을 때 처리**

        ```dart

                        if (bookService.bookList.isEmpty) return SizedBox();
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/19dad2a7-7193-41d0-97da-874a0efaffde/Untitled.png)

    `bookList` 가 비어있을 때는 `elementAt` 함수를 호출하지 않고 바로 return 하도록 해 `bookList` 의 `range` 를 벗어나는 인덱스로 접근이 일어나지 않도록 합니다.

    이제 에러 없이 잘 동작합니다!&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;05. 좋아요 구현하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;구현 목표&lt;/p&gt;
  &lt;aside&gt;
    각각의 책에 좋아요를 누르고, 이를 좋아요 탭에서 모아볼 수 있게끔 합니다.

  &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) 파일 분리&lt;/p&gt;
  &lt;aside&gt;
    각각의 책 정보를 화면에 표시하는 `ListTile` 을 별도의 위젯으로 분리해 좋아요 목록(**LikedBookPage**)에서도 재활용 할 수 있도록 하겠습니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;main.dart 의 115번째 줄에 있는 ListTile 을 우클릭해 &lt;code&gt;Refactor(Ctrl + Shift + R)&lt;/code&gt;을 선택하고, &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2f6ccacc-c990-4171-a70d-7840cb6a82c9/Screen_Shot_2022-09-19_at_11.05.03_AM.png&quot; alt=&quot;Screen Shot 2022-09-19 at 11.05.03 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;Extract Widget&lt;/code&gt; 을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c62cabb8-daed-4a21-99c8-9ed5bca9c4e0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 위젯의 이름을 입력하는 칸이 나오면 아래와 같이 &lt;code&gt;BookTile&lt;/code&gt; 이라고 적은 후 엔터를 눌러줍니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd3ce572-5364-4dd4-90dc-e16e0643cd8f/Screen_Shot_2022-09-19_at_11.07.03_AM.png&quot; alt=&quot;Screen Shot 2022-09-19 at 11.07.03 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;ListTile&lt;/code&gt; 내에 있던 코드가 &lt;code&gt;BookTile&lt;/code&gt; 이라는 이름의 별도의 위젯으로 분리된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7cfa7b68-a96f-4d05-b4d3-c7da395bc3ec/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) &lt;code&gt;BookService&lt;/code&gt; 에 좋아요 기능 추가&lt;/p&gt;
  &lt;aside&gt;
    BookService 내에 좋아요 기능을 추가하고, 이를 SearchPage 에서 호출하도록 합니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;먼저 좋아요 한 책들을 담는 List 를 만들겠습니다.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해 &lt;code&gt;book_service.dart&lt;/code&gt; 7번째 줄 맨 뒤에 붙여넣어주세요. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] likedBookList&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
    List&amp;lt;Book&amp;gt; likedBookList = [];&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/08c2094c-88ff-45c7-a30f-d36353445eba/Untitled.png)

2. 이제 **좋아요 아이콘을 누르면 호출될 함수**를 추가하겠습니다. 

    &amp;lt;aside&amp;gt;
      해당 함수에서 구현되어야 할 기능은 아래와 같습니다.

    - **좋아요가 눌러져 있지 않은 경우 (`likedBookList` 에 없는 경우)
    ⇒** 좋아요 추가 (`likedBookList` 에 추가)
    - **좋아요가 이미 눌러져있다면 (`likedBookList` 에 있는 경우)
    ⇒** 좋아요 취소 (`likedBookList` 에서 제거)
    &amp;lt;/aside&amp;gt;

    아래 코드스니펫을 복사해 `book_service.dart` 8번째 줄 맨 뒤에 붙여넣어주세요.

    - **[코드스니펫] toggleLikeBook**

        ```dart

          void toggleLikeBook({required Book book}) {
            String bookId = book.id;
            if (likedBookList.map((book) =&amp;gt; book.id).contains(bookId)) {
              likedBookList.removeWhere((book) =&amp;gt; book.id == bookId);
            } else {
              likedBookList.add(book);
            }
            notifyListeners();
          }
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/730739ac-1558-4e4b-9567-ce6efc17d47b/Untitled.png)

    &amp;lt;aside&amp;gt;
      위 코드들에 대한 설명입니다.

    `**likedBookList.map((book) =&amp;gt; book.id)**` : likedBookList 의 요소들을 순회하며 id 들만 뽑아 새로 iterable(List와 비슷) 을 만듭니다.

    `**likedBookList.removeWhere((book) =&amp;gt; book.id == bookId)`** : toggleLikeBook 함수가 인자로 받는 book 과 id 가 같은 Book 이 likedBookList 내에 있다면 제거합니다.

    `**notifyListeners`** : 화면을 새로고침합니다. 

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      왜 `**likedBookList.contains(book)`** 과 같이 작성하지 않을까요?

    클래스를 통해 생성된 두개의 객체는 처음에 대입해주는 값들(Book 에서는 title, subtitle 등)이 같더라도, 서로 다른 존재입니다.

    코드로 작성해보면 아래와 같습니다.

    ```dart
    var book1 = Book(id: &amp;quot;1&amp;quot;, title: &amp;quot;톰 소여의 모험&amp;quot;);
    var book2 = Book(id: &amp;quot;1&amp;quot;, title: &amp;quot;톰 소여의 모험&amp;quot;);

    print(book1 == book2) // false
    ```

    우리가 작성한 코드에서도 똑같은 일이 일어납니다. 서로 다른 객체로 인식하기 때문에 contains 는 항상 false 를 반환할 수밖에 없습니다.

    ```dart
    var likedBookList = [Book(id: &amp;quot;1&amp;quot;, title: &amp;quot;톰 소여의 모험&amp;quot;)];

    var book2 = Book(id: &amp;quot;1&amp;quot;, title: &amp;quot;톰 소여의 모험&amp;quot;);

    print(likedBookList.contains(book2)) // false
    ```

    이를 해결하기 위해서 `==` 연산자를 덮어씌워 비교 로직을 새로 작성하거나, `Equatable` 등의 플러그인 등을 사용하기도 합니다. 핵심은 두개의 객체가 같은 데이터를 가리키고 있는지를 어떤 식으로 알아낼 것이냐는 점입니다.

    우리는 Google Book API 가 제공하는 id (고유한 값입니다) 를 이용해서 비교 로직을 작성했습니다.

    `**likedBookList.map((book) =&amp;gt; book.id)**` 와 같이 id 들이 담긴 iterable(리스트와 유사합니다) 를 만들고, 이것이 우리가 좋아요 버튼을 누른 Book 의 id 를 포함하는지 비교해, 이미 좋아요가 눌린 책인지 아닌지를 구분할 수 있겠죠!

    &amp;lt;/aside&amp;gt;

3. `ListTile` 의 trailing 에 있는 좋아요 버튼을 누르면 `toggleLikeBook` 함수를 호출하고, 좋아요 여부에 따라 아이콘의 색깔을 바꿔주도록 하겠습니다.

    먼저 위에서 분리한 `BookTile` 위젯에서 `BookService` 를 사용할 수 있도록 해야합니다. 다시 Provider 사용법을 복기해볼까요?

    &amp;lt;aside&amp;gt;
      Provider 에서 Service 를 사용하는 방법

    1. `Consumer&amp;lt;클래스명&amp;gt;` : 클래스 정보 갱신시 화면을 새로고침 해야 할 때 사용
    2. `context.read&amp;lt;클래스명&amp;gt;` : 1회성으로 클래스 접근할 때 사용 (화면 새로고침이 필요 없을 때)
    &amp;lt;/aside&amp;gt;

    우리가 만든 `BookTile` 은 좋아요를 누를 때마다 trailing 에 있는 아이콘의 색깔이 바뀌어야 합니다. 즉 새로고침이 일어나는 위젯입니다.

    하지만, `BookTile` 을 포함하고 있는 **SearchPage** 가 `Consumer`로 감싸져 있기 때문에, `BookTile` 은 별개의 `Consumer` 로 감싸지 않더라도 함께 새로고침이 일어나게 됩니다.

    그러므로, `BookTile` 에서는 `context.read` 만 사용해서 BookService 에 접근해주겠습니다.

    아래 코드스니펫을 복사해 main.dart 134번째 줄 맨 뒤에 붙여넣어주세요

    - **[코드스니펫] context.read&amp;lt;BookService&amp;gt;**

        ```dart

            BookService bookService = context.read&amp;lt;BookService&amp;gt;();

        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9673abd3-e54a-4e1f-8ce8-76fa61a8665a/Untitled.png)

    이제 `bookService` 라는 이름으로 `BookService` 클래스에 있는 변수와 함수를 사용할 수 있습니다.

    아래 코드스니펫을 복사해 `main.dart` 의 152, 153번째 줄을 지우고 붙여넣어주세요.

    - **[코드스니펫] ListTile trailing 아이콘**

        ```dart
        onPressed: () {
                  bookService.toggleLikeBook(book: book);
                },
                icon: bookService.likedBookList.map((book) =&amp;gt; book.id).contains(book.id)
                    ? Icon(
                        Icons.star,
                        color: Colors.amber,
                      )
                    : Icon(Icons.star_border),
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fe835df4-045e-4fdc-b019-acd55bed32e4/Untitled.png)

    &amp;lt;aside&amp;gt;
      위 코드들에 대한 설명입니다.

    `**bookService.toggleLikeBook(book: book)**` : BookService 에 생성해준 toggleLikeBook 에 해당 book 데이터를 인자로 넘겨 호출해줍니다.

    `**bookService.likedBookList.map((book) =&amp;gt; book.id).contains(book.id)`** : bookService 에 있는 likedBookList(좋아요 한 책들 담겨있는 리스트) 가 해당 book 을 담고있는지, 즉 이미 좋아요를 누른 상태인지 확인합니다.

    아래 삼항연산자 (조건 ? a : b) 를 통해 좋아요가 눌러진 상태라면 노란색 별 아이콘을 보여줍니다.

    &amp;lt;/aside&amp;gt;

    아래와 같이 좋아요를 누를 때 화면이 잘 갱신되는 것을 볼 수 있습니다.

    ![Screen Shot 2022-09-19 at 12.06.51 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/12e8db01-a01f-449c-8174-436c43da5c97/Screen_Shot_2022-09-19_at_12.06.51_PM.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) &lt;code&gt;LikedBookPage&lt;/code&gt; 에서 좋아요 한 책들 모아보기&lt;/p&gt;
  &lt;aside&gt;
    LikedBookPage 에서 방금 생성한 likedBookList 를 순회하며 책들을 보여주도록 해봅시다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;먼저 &lt;strong&gt;LikedBookPage&lt;/strong&gt; 에서도 likedBookList 의 변화에 따라 화면이 갱신되도록 해야합니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;Consumer&lt;/code&gt; 를 사용해 &lt;code&gt;Scaffold&lt;/code&gt; 를 감싸주겠습니다. 171번째 줄의 Scaffold 를 클릭한 뒤 Quick Fix(&lt;code&gt;Ctrl/Cmd + .&lt;/code&gt;)를 눌러 &lt;code&gt;Wrap with Builder&lt;/code&gt; 를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/74cd92e5-d511-4142-9a4a-92e5f5195a6b/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래와 같이 &lt;code&gt;Scaffold&lt;/code&gt; 위젯을 &lt;code&gt;Builder&lt;/code&gt; 위젯이 감쌉니다. &lt;code&gt;Consumer&lt;/code&gt; 위젯과 형식이 비슷한 &lt;code&gt;Builder&lt;/code&gt; 를 사용해 모양을 잡아줬습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/47aa7957-298e-48a1-adaa-6a8aacd2f43d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 이미지와 같이 79번째 라인의 &lt;code&gt;Builder&lt;/code&gt;를 &lt;code&gt;Consumer&amp;lt;BookService&amp;gt;&lt;/code&gt;로 변경해주세요. &lt;/p&gt;
&lt;p&gt; &lt;code&gt;context&lt;/code&gt; 뒤에 &lt;code&gt;bookService&lt;/code&gt; 와 &lt;code&gt;child&lt;/code&gt; 도 추가해줍니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/896de5e4-553f-4d1e-aaac-08dc59ee5332/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 줄 정렬을 예쁘게 해주기 위해 177번째 라인의 중괄호와 소괄호 사이(위 화살표)에 콤마(&lt;code&gt;,&lt;/code&gt;)를 찍은 뒤 저장해주세요. 저장하면 코드가 정렬됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1ac1d9cd-5258-43ff-a1ce-257f9eecece0/Screen_Shot_2022-09-19_at_12.45.27_PM.png&quot; alt=&quot;Screen Shot 2022-09-19 at 12.45.27 PM.png&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   **복습:** `Consumer&lt;BookService&gt;`는 위젯 트리를 타고 올라가 **Provider**로 등록된 **BookService**를 찾습니다. 찾은 **BookService**를 171번째 라인의 두 번째 파라미터인 `bookService` 에 담아줍니다.
 우리는 `bookService` 변수를 통해 **BookService** 안에 있는 변수에 접근해 수정하고, 화면을 새로고침할 수 있습니다.

 &lt;/aside&gt;

 &lt;aside&gt;
   `BookService`에서 값을 변경하고 `notifyListeners();`를 호출하면, 해당 서비스를 `Consumer`로 등록한 모든 위젯의 `builder` 함수가 재호출 되면서 화면이 갱신 됩니다.

 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 코드스니펫을 복사해 &lt;code&gt;main.dart&lt;/code&gt; 의 174-176 번째 줄을 지우고 붙여넣어주세요. 위 &lt;strong&gt;SearchPage&lt;/strong&gt; 에 작성한 코드를 그대로 가져왔습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] LikedBookPage / BookTile&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.bookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  if (bookService.bookList.isEmpty) return SizedBox();
                  Book book = bookService.bookList.elementAt(index);
                  return BookTile(book: book);
                },
              ),
            ),&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/548eb9ab-6c0c-449f-b847-943299ca4d7b/Untitled.png)

    좋아요 한 책만 모아보기 위해서는 위에 밑줄친 `bookList` 대신에 `likedBookList` 를 사용하면 되겠죠! 모두 아래와 같이 수정해봅시다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3d1e194e-4014-45c6-9e51-76f63128f334/Untitled.png)

    이제 좋아요 탭에서 아래와 같이 좋아요 누른 책들을 모아볼 수 있습니다.

    ![Screen Shot 2022-09-19 at 12.49.08 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1f969888-e359-4b43-8895-506eaad7bb27/Screen_Shot_2022-09-19_at_12.49.08_PM.png)&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;06. 책 상세 페이지 WebView로 보여주기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;구현 목표&lt;/p&gt;
  &lt;aside&gt;
    **webview_flutter** 를 활용해 앱 내에서 웹페이지를 열 수 있도록 합니다.

  &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) WebView 란?&lt;/p&gt;
  &lt;aside&gt;
    **WebView**란 앱 내에서 **웹브라우저 컴포넌트**를 사용해 웹페이지를 띄우는 것을 말합니다.

&lt;p&gt;  즉, &lt;strong&gt;앱 내에 웹 브라우저를 넣는 것&lt;/strong&gt;이라 생각하시면 됩니다.&lt;/p&gt;
  &lt;/aside&gt;

  &lt;aside&gt;
    많은 앱들의 기능 전체, 또는 일부가 이 WebView 를 활용해 구현되었습니다.

  &lt;/aside&gt;

&lt;p&gt;  대표적인 예시가 &lt;strong&gt;네이버 앱&lt;/strong&gt;입니다. 앱에 들어가보시면 네이버 모바일 웹사이트와 거의 동일한 UI 를 볼 수 있습니다. 껍데기 부분만 앱으로 만들고 내부 위젯에 &lt;strong&gt;WebView&lt;/strong&gt; 를 사용해 웹사이트를 그대로 띄우는 방식으로 구현되어 있다는 것을 알 수 있겠죠!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9ea3cf63-004a-4c68-a0de-5a4d1a831efa/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  이외에도 &lt;strong&gt;당근마켓&lt;/strong&gt;, &lt;strong&gt;토스&lt;/strong&gt;와 같은 앱들도 내부 기능 일부를 WebView 를 활용해 구현했습니다. &lt;/p&gt;
  &lt;aside&gt;
    **WebView 의 장단점**

&lt;p&gt;  &lt;strong&gt;장점&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;낮은 비용으로 빠르게 개발할 수 있다 (웹 개발자 풀이 더 크다!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;앱 업데이트와 상관없이 즉시 변경사항 배포가 가능하다 (앱의 경우 매 업데이트마다 심사를 거쳐야 합니다)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;단점*&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;네이티브와 같은 사용자 경험을 온전하게 주기가 어렵다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;앱 성능상의 제약이 따른다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;네트워크 연결이 끊기면 사용하기 어렵다&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) &lt;strong&gt;webview_flutter&lt;/strong&gt; 패키지 설치하기&lt;/p&gt;
  &lt;aside&gt;
    **webview_flutter**는 웹뷰를 앱 내에 손쉽게 구현할 수 있게 해주는 공식 패키지입니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;해당 패키지 사이트는 아래와 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://pub.dev/packages/webview_flutter/install&quot;&gt;[코드스니펫] Pub.dev / webview_flutter&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://pub.dev/packages/webview_flutter&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;강의 영상과 조금 다른 부분이니 주의해서 봐주세요!&lt;/strong&gt; webview_flutter 패키지가 업데이트 되면서 아래 강의 내용대로 진행하면 에러가 뜹니다. 이전 버전 패키지를 설치함으로서 이 에러를 해결해주도록 하겠습니다&lt;/p&gt;
 &lt;aside&gt;
   실무에서도 호환성 이슈로 인해 함부로 패키지의 버전을 올리지 않습니다

 &lt;/aside&gt;

&lt;p&gt; 아래 코드스니펫을 복사해주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] webview_flutter 3.0.4 버전 설치 스크립트&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;  flutter pub add webview_flutter:^3.0.4&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VSCode &lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Terminal&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/51e27651-ff0f-4223-9f57-fcc170e9766c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Terminal&lt;/code&gt;창에 복사한 &lt;code&gt;flutter pub add webview_flutter:^3.0.4&lt;/code&gt; 를 붙여넣고 엔터를 눌러 실행해 주세요.&lt;br&gt;아래와 같이 나오면 정상적으로 작동한 것입니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f02e66ef-39d9-4f19-be8f-94f6cf488732/Screenshot_2022-12-22_at_5.12.45_PM.png&quot; alt=&quot;Screenshot 2022-12-22 at 5.12.45 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;pubspec.yaml&lt;/code&gt; 파일을 열어서 아래와 같이 41번째 라인에 &lt;code&gt;webview_flutter: ^3.0.4&lt;/code&gt;가 있으면 설치가 완료된 것입니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f372932f-0a3c-4604-839c-2962678bfa01/Screen_Shot_2022-09-19_at_1.01.36_PM.png&quot; alt=&quot;Screen Shot 2022-09-19 at 1.01.36 PM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;이제 아래부터는 강의 내용과 같습니다!&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;android/app/build.gradle&lt;/code&gt; 파일을 열고(경로에 유의하세요!)&lt;br&gt;50 번째 줄의 &lt;code&gt;flutter.minSdkVersion&lt;/code&gt; 을 &lt;code&gt;19&lt;/code&gt; 로 수정해줍니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/79444c30-7beb-47a8-88f1-9289abf38de2/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt; android {
     defaultConfig {
         minSdkVersion 19
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 아래와 같이 작성해주면 됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1b8a8a79-0650-43b9-bc45-49f206ecf5f6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;webview_flutter&lt;/strong&gt; 를 앱에서 사용하기 위해서는 앱을 재설치해야합니다. &lt;/p&gt;
&lt;p&gt; 먼저 앱을 정지하고&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3f5b7ae3-a375-4a4a-91f4-9e31f03b9c92/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;main.dart&lt;/code&gt; 를 열고 우측 상단에서 &lt;code&gt;Run Without Debugging&lt;/code&gt; 을 클릭해 다시 앱을 설치 / 실행해줍니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/54699c5f-0260-47c9-886c-9ea22ed16d62/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;aside&amp;gt;
  WebView 를 앱에서 사용할 준비가 완료되었습니다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) WebViewPage 위젯 만들기&lt;/p&gt;
  &lt;aside&gt;
    WebViewPage 라는 위젯을 만들고, ListTile 을 클릭할 때 해당 위젯으로 이동하도록 합니다

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;webview_flutter 의 설치가 끝났으니, 이제 각 Book 의 &lt;code&gt;previewLink&lt;/code&gt; 로 이동하는 기능을 구현해보겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/956d21d5-85db-4b71-b6ac-b77f30ce51e5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 먼저 WebViewPage 라는 StatelessWidget 을 하나 만들어주겠습니다. 아래 코드스니펫을 복사해 192번째 줄 맨 뒤에 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] WebViewPage&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
  class WebViewPage extends StatelessWidget {
    WebViewPage({super.key, required this.url});

    String url;

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.grey,
          title: Text(url),
        ),
        body: WebView(initialUrl: url),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f549f171-9a60-42a5-868b-b94c08e27e62/Untitled.png)

2. 206번째 줄에 에러가 발생했군요. 에러가 발생한 `WebView` 를 클릭한 뒤 Quick Fix(`Ctrl/Cmd + .`)를 누르고, `Import library ‘package:webview_flutter/webview_flutter.dart` 를 선택해주세요.

    ![Screen Shot 2022-09-19 at 3.17.21 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e59b7f90-e05c-4a4d-8193-bc4267b127e1/Screen_Shot_2022-09-19_at_3.17.21_PM.png)

    3번째 줄에 Import 문이 추가되면서 에러가 해결됩니다.

    ![Screen Shot 2022-09-19 at 3.17.51 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f3026c0d-bd4d-49d8-a5c1-62d8c1f18457/Screen_Shot_2022-09-19_at_3.17.51_PM.png)

3. 코드를 자세히 뜯어볼까요? 

    ![Screen Shot 2022-09-19 at 3.18.28 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/be60e694-dae6-46c9-ac11-0cbe5470d9d9/Screen_Shot_2022-09-19_at_3.18.28_PM.png)

    **WebViewPage** 위젯으로 이동하면서 url 이라는 인자를 넘겨받고, 이를 `WebView` 위젯에 다시 인자로 넘겨 웹페이지를 `Scaffold` 의 body 내에 띄워주는 코드입니다.&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) 웹 URL 연결하기&lt;/p&gt;
  &lt;aside&gt;
    Book 의 previewLink 를 WebViewPage 의 인자로 넘겨주도록 합니다.

  &lt;/aside&gt;

&lt;p&gt;  아래 코드스니펫을 복사해 &lt;code&gt;ListTile&lt;/code&gt; 의 &lt;code&gt;onTap&lt;/code&gt; 함수 중괄호 사이 (139번째 줄) 에 붙여넣어줍니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] BookTile / ListTile / onTap&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) =&amp;gt; WebViewPage(
                url: book.previewLink.replaceFirst(&amp;quot;http&amp;quot;, &amp;quot;https&amp;quot;),
              ),
            ),
          );
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![Screen Shot 2022-09-19 at 3.20.05 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/54280695-d3b7-4957-b6c6-06e86cb57f87/Screen_Shot_2022-09-19_at_3.20.05_PM.png)

`ListTile` 을 누르면 방금 만든 **WebViewPage** 로 이동하도록 했습니다. 이 때 해당 book의 `previewLink` 을 인자로 전달해, 해당 링크를 웹뷰로 열도록 합니다.

&amp;lt;aside&amp;gt;
  Book 의 `previewLink` 의 경우 http 로 시작하기 때문에 보안 정책상 웹뷰로 열리지 않습니다. 이 때문에 `replaceFirst`를 사용해 해당 url 의 http 를 https 로 바꿔주었습니다.

http, https 에 대한 보다 자세한 내용이 궁금하시다면 아래 링크를 참고해주세요. 
[**HTTP와 HTTPS 차이점**](https://brunch.co.kr/@hyoi0303/10)

&amp;lt;/aside&amp;gt;

이제 에뮬레이터에서 검색 후에 각각의 BookTile 을 클릭해봅시다. 

아래와 같이 앱 내에서 해당 웹페이지를 잘 보여주는 것을 확인할 수 있습니다.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ab54991c-1b5a-4ae9-9855-91967b4aadb4/Untitled.gif)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;
  import &amp;#39;package:webview_flutter/webview_flutter.dart&amp;#39;;

  import &amp;#39;book.dart&amp;#39;;
  import &amp;#39;book_service.dart&amp;#39;;

  void main() {
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; BookService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  class HomePage extends StatefulWidget {
    HomePage({Key? key}) : super(key: key);

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    var bottomNavIndex = 0;
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: [
          SearchPage(),
          LikedBookPage(),
        ].elementAt(bottomNavIndex),
        bottomNavigationBar: BottomNavigationBar(
          selectedItemColor: Colors.black,
          unselectedItemColor: Colors.grey,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          onTap: (value) {
            setState(() {
              bottomNavIndex = value;
            });
          },
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.search),
              label: &amp;#39;검색&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.star),
              label: &amp;#39;좋아요&amp;#39;,
            ),
          ],
          currentIndex: bottomNavIndex,
        ),
      );
    }
  }

  class SearchPage extends StatelessWidget {
    SearchPage({super.key});

    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;BookService&amp;gt;(
        builder: (context, bookService, child) {
          return Scaffold(
            appBar: AppBar(
              backgroundColor: Colors.white,
              toolbarHeight: 80,
              title: TextField(
                onSubmitted: (value) {
                  bookService.search(value);
                },
                cursorColor: Colors.grey,
                decoration: InputDecoration(
                  prefixIcon: Icon(Icons.search, color: Colors.grey),
                  hintText: &amp;quot;작품, 감독, 배우, 컬렉션, 유저 등&amp;quot;,
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.white),
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.grey),
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                  ),
                ),
              ),
            ),
            body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.bookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  if (bookService.bookList.isEmpty) return SizedBox();
                  Book book = bookService.bookList.elementAt(index);
                  return BookTile(book: book);
                },
              ),
            ),
          );
        },
      );
    }
  }

  class BookTile extends StatelessWidget {
    const BookTile({
      Key? key,
      required this.book,
    }) : super(key: key);

    final Book book;

    @override
    Widget build(BuildContext context) {
      BookService bookService = context.read&amp;lt;BookService&amp;gt;();

      return ListTile(
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) =&amp;gt; WebViewPage(
                url: book.previewLink.replaceFirst(&amp;quot;http&amp;quot;, &amp;quot;https&amp;quot;),
              ),
            ),
          );
        },
        leading: Image.network(
          book.thumbnail,
          fit: BoxFit.fitHeight,
        ),
        title: Text(
          book.title,
          style: TextStyle(fontSize: 16),
        ),
        subtitle: Text(
          book.subtitle,
          style: TextStyle(color: Colors.grey),
        ),
        trailing: IconButton(
          onPressed: () {
            bookService.toggleLikeBook(book: book);
          },
          icon: bookService.likedBookList.map((book) =&amp;gt; book.id).contains(book.id)
              ? Icon(
                  Icons.star,
                  color: Colors.amber,
                )
              : Icon(Icons.star_border),
        ),
      );
    }
  }

  class LikedBookPage extends StatelessWidget {
    const LikedBookPage({super.key});

    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;BookService&amp;gt;(
        builder: (context, bookService, child) {
          return Scaffold(
            body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.likedBookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  if (bookService.likedBookList.isEmpty) return SizedBox();
                  Book book = bookService.likedBookList.elementAt(index);
                  return BookTile(book: book);
                },
              ),
            ),
          );
        },
      );
    }
  }

  class WebViewPage extends StatelessWidget {
    WebViewPage({super.key, required this.url});

    String url;

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.grey,
          title: Text(url),
        ),
        body: WebView(initialUrl: url),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;book.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  class Book {
    String id;
    String title;
    String subtitle;
    String thumbnail; // 썸네일 이미지 링크
    String previewLink; // ListTile 을 눌렀을 때 이동하는 링크

    Book({
      required this.id,
      required this.title,
      required this.subtitle,
      required this.thumbnail,
      required this.previewLink,
    });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;book_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:dio/dio.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;book.dart&amp;#39;;

  class BookService extends ChangeNotifier {
    List&amp;lt;Book&amp;gt; bookList = []; // 책 목록
    List&amp;lt;Book&amp;gt; likedBookList = [];

    void toggleLikeBook({required Book book}) {
      String bookId = book.id;
      if (likedBookList.map((book) =&amp;gt; book.id).contains(bookId)) {
        likedBookList.removeWhere((book) =&amp;gt; book.id == bookId);
      } else {
        likedBookList.add(book);
      }
      notifyListeners();
    }

    void search(String q) async {
      bookList.clear(); // 검색 버튼 누를때 이전 데이터들을 지워주기

      if (q.isNotEmpty) {
        Response res = await Dio().get(
          &amp;quot;https://www.googleapis.com/books/v1/volumes?q=$q&amp;amp;startIndex=0&amp;amp;maxResults=40&amp;quot;,
        );
        List items = res.data[&amp;quot;items&amp;quot;];

        for (Map&amp;lt;String, dynamic&amp;gt; item in items) {
          Book book = Book(
            id: item[&amp;#39;id&amp;#39;],
            title: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;title&amp;#39;] ?? &amp;quot;&amp;quot;,
            subtitle: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;subtitle&amp;#39;] ?? &amp;quot;&amp;quot;,
            thumbnail: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;imageLinks&amp;#39;]?[&amp;#39;thumbnail&amp;#39;] ??
                &amp;quot;https://thumbs.dreamstime.com/b/no-image-available-icon-flat-vector-no-image-available-icon-flat-vector-illustration-132482953.jpg&amp;quot;,
            previewLink: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;previewLink&amp;#39;] ?? &amp;quot;&amp;quot;,
          );
          bookList.add(book);
        }
      }
      notifyListeners();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;07. 숙제 - 왓챠피디아 추가기능 구현&lt;/h2&gt;
&lt;aside&gt;
  왓챠피디아 앱에 기능들을 추가하며 API 개념과, 저번시간에 다룬 shared_preferences 패키지 사용법을 익혀봅시다.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) &lt;strong&gt;Google Book API&lt;/strong&gt; 에서 제공하는 다른 정보 보여주기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 구현 목표&lt;/p&gt;
  &lt;aside&gt;
    `ListTile` 의 `subtitle` 자리에 작가 이름, 책 발행일 가져와서 보여주기

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;작가 이름&lt;/strong&gt;과 &lt;strong&gt;책 발행일&lt;/strong&gt;을 API 에서 가져와, &lt;code&gt;subtitle&lt;/code&gt; 자리에 엔터로 구분지어 보여줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;작가가 여러명인 경우, 각각의 작가명을 쉼표(,)로 구분지어줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d7bc3759-f5a3-4caa-bf49-f052951a34f4/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
  [**구글 책 검색 API](https://www.googleapis.com/books/v1/volumes?q=dog)** 에서 어떤 식으로 데이터를 반환하는지 확인하고, 필요한 데이터를 가져와보세요!

&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/04075458-160e-4262-9653-f7df7e9b829a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 숙제 답안&lt;/p&gt;
  &lt;aside&gt;
    노란색 하이라이트로 변경 사항을 표시해뒀습니다

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;
  import &amp;#39;package:webview_flutter/webview_flutter.dart&amp;#39;;

  import &amp;#39;book.dart&amp;#39;;
  import &amp;#39;book_service.dart&amp;#39;;

  void main() {
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; BookService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  class HomePage extends StatefulWidget {
    HomePage({Key? key}) : super(key: key);

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    var bottomNavIndex = 0;
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: [
          SearchPage(),
          LikedBookPage(),
        ].elementAt(bottomNavIndex),
        bottomNavigationBar: BottomNavigationBar(
          selectedItemColor: Colors.black,
          unselectedItemColor: Colors.grey,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          onTap: (value) {
            setState(() {
              bottomNavIndex = value;
            });
          },
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.search),
              label: &amp;#39;검색&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.star),
              label: &amp;#39;좋아요&amp;#39;,
            ),
          ],
          currentIndex: bottomNavIndex,
        ),
      );
    }
  }

  class SearchPage extends StatelessWidget {
    SearchPage({super.key});

    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;BookService&amp;gt;(
        builder: (context, bookService, child) {
          return Scaffold(
            appBar: AppBar(
              backgroundColor: Colors.white,
              toolbarHeight: 80,
              title: TextField(
                onSubmitted: (value) {
                  bookService.search(value);
                },
                cursorColor: Colors.grey,
                decoration: InputDecoration(
                  prefixIcon: Icon(Icons.search, color: Colors.grey),
                  hintText: &amp;quot;작품, 감독, 배우, 컬렉션, 유저 등&amp;quot;,
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.white),
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.grey),
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                  ),
                ),
              ),
            ),
            body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.bookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  if (bookService.bookList.isEmpty) return SizedBox();
                  Book book = bookService.bookList.elementAt(index);
                  return BookTile(book: book);
                },
              ),
            ),
          );
        },
      );
    }
  }

  class BookTile extends StatelessWidget {
    const BookTile({
      Key? key,
      required this.book,
    }) : super(key: key);

    final Book book;

    @override
    Widget build(BuildContext context) {
      BookService bookService = context.read&amp;lt;BookService&amp;gt;();

      return ListTile(
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) =&amp;gt; WebViewPage(
                url: book.previewLink.replaceFirst(&amp;quot;http&amp;quot;, &amp;quot;https&amp;quot;),
              ),
            ),
          );
        },
        leading: Image.network(
          book.thumbnail,
          fit: BoxFit.fitHeight,
        ),
        title: Text(
          book.title,
          style: TextStyle(fontSize: 16),
        ),
        subtitle: Text(
          &amp;quot;${book.authors.join(&amp;quot;, &amp;quot;)}\n${book.publishedDate}&amp;quot;,
          style: TextStyle(color: Colors.grey),
        ),
        trailing: IconButton(
          onPressed: () {
            bookService.toggleLikeBook(book: book);
          },
          icon: bookService.likedBookList.map((book) =&amp;gt; book.id).contains(book.id)
              ? Icon(
                  Icons.star,
                  color: Colors.amber,
                )
              : Icon(Icons.star_border),
        ),
      );
    }
  }

  class LikedBookPage extends StatelessWidget {
    const LikedBookPage({super.key});

    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;BookService&amp;gt;(
        builder: (context, bookService, child) {
          return Scaffold(
            body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.likedBookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  if (bookService.likedBookList.isEmpty) return SizedBox();
                  Book book = bookService.likedBookList.elementAt(index);
                  return BookTile(book: book);
                },
              ),
            ),
          );
        },
      );
    }
  }

  class WebViewPage extends StatelessWidget {
    WebViewPage({super.key, required this.url});

    String url;

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.grey,
          title: Text(url),
        ),
        body: WebView(initialUrl: url),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;book.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  class Book {
    String id;
    String title;
    String subtitle;
    List authors;
    String publishedDate;
    String thumbnail; // 썸네일 이미지 링크
    String previewLink; // ListTile 을 눌렀을 때 이동하는 링크

    Book({
      required this.id,
      required this.title,
      required this.subtitle,
      required this.authors,
      required this.publishedDate,
      required this.thumbnail,
      required this.previewLink,
    });
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;book_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:dio/dio.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;book.dart&amp;#39;;

  class BookService extends ChangeNotifier {
    List&amp;lt;Book&amp;gt; bookList = []; // 책 목록
    List&amp;lt;Book&amp;gt; likedBookList = [];

    void toggleLikeBook({required Book book}) {
      String bookId = book.id;
      if (likedBookList.map((book) =&amp;gt; book.id).contains(bookId)) {
        likedBookList.removeWhere((book) =&amp;gt; book.id == bookId);
      } else {
        likedBookList.add(book);
      }
      notifyListeners();
    }

    void search(String q) async {
      bookList.clear(); // 검색 버튼 누를때 이전 데이터들을 지워주기

      if (q.isNotEmpty) {
        Response res = await Dio().get(
          &amp;quot;https://www.googleapis.com/books/v1/volumes?q=$q&amp;amp;startIndex=0&amp;amp;maxResults=40&amp;quot;,
        );
        List items = res.data[&amp;quot;items&amp;quot;];

        for (Map&amp;lt;String, dynamic&amp;gt; item in items) {
          Book book = Book(
            id: item[&amp;#39;id&amp;#39;],
            title: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;title&amp;#39;] ?? &amp;quot;&amp;quot;,
            subtitle: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;subtitle&amp;#39;] ?? &amp;quot;&amp;quot;,
            authors: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;authors&amp;#39;] ?? [],
            publishedDate: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;publishedDate&amp;#39;] ?? &amp;quot;&amp;quot;,
            thumbnail: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;imageLinks&amp;#39;]?[&amp;#39;thumbnail&amp;#39;] ??
                &amp;quot;https://thumbs.dreamstime.com/b/no-image-available-icon-flat-vector-no-image-available-icon-flat-vector-illustration-132482953.jpg&amp;quot;,
            previewLink: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;previewLink&amp;#39;] ?? &amp;quot;&amp;quot;,
          );
          bookList.add(book);
        }
      }
      notifyListeners();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 좋아요 누른 책 목록 기기에 저장하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 구현 목표&lt;/p&gt;
  &lt;aside&gt;
    **shared_preferences** 사용해서 기기에 좋아요 책 목록 저장하기

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;앱을 다시 시작하더라도 좋아요 한 책 목록이 초기화되지 않도록 기기에 저장합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이전 강의에서 shared_preferences 를 다뤘던 부분을 참고해서 코드를 짜 봅시다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/70a06ada-a0ff-4829-ac3c-f8dda11fccce/Simulator_Screen_Shot_-_iPhone_14_-_2022-09-26_at_17.36.20.png&quot; alt=&quot;Simulator Screen Shot - iPhone 14 - 2022-09-26 at 17.36.20.png&quot;&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 숙제 답안&lt;/p&gt;
  &lt;aside&gt;
    노란색 하이라이트로 변경 사항을 표시해뒀습니다

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;
  import &amp;#39;package:shared_preferences/shared_preferences.dart&amp;#39;;
  import &amp;#39;package:webview_flutter/webview_flutter.dart&amp;#39;;

  import &amp;#39;book.dart&amp;#39;;
  import &amp;#39;book_service.dart&amp;#39;;

  late SharedPreferences prefs;

  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    prefs = await SharedPreferences.getInstance();
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; BookService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  class HomePage extends StatefulWidget {
    HomePage({Key? key}) : super(key: key);

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    var bottomNavIndex = 0;
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        body: [
          SearchPage(),
          LikedBookPage(),
        ].elementAt(bottomNavIndex),
        bottomNavigationBar: BottomNavigationBar(
          selectedItemColor: Colors.black,
          unselectedItemColor: Colors.grey,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          onTap: (value) {
            setState(() {
              bottomNavIndex = value;
            });
          },
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.search),
              label: &amp;#39;검색&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.star),
              label: &amp;#39;좋아요&amp;#39;,
            ),
          ],
          currentIndex: bottomNavIndex,
        ),
      );
    }
  }

  class SearchPage extends StatelessWidget {
    SearchPage({super.key});

    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;BookService&amp;gt;(
        builder: (context, bookService, child) {
          return Scaffold(
            appBar: AppBar(
              backgroundColor: Colors.white,
              toolbarHeight: 80,
              title: TextField(
                onSubmitted: (value) {
                  bookService.search(value);
                },
                cursorColor: Colors.grey,
                decoration: InputDecoration(
                  prefixIcon: Icon(Icons.search, color: Colors.grey),
                  hintText: &amp;quot;작품, 감독, 배우, 컬렉션, 유저 등&amp;quot;,
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.white),
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.grey),
                    borderRadius: BorderRadius.all(Radius.circular(10)),
                  ),
                ),
              ),
            ),
            body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.bookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  if (bookService.bookList.isEmpty) return SizedBox();
                  Book book = bookService.bookList.elementAt(index);
                  return BookTile(book: book);
                },
              ),
            ),
          );
        },
      );
    }
  }

  class BookTile extends StatelessWidget {
    const BookTile({
      Key? key,
      required this.book,
    }) : super(key: key);

    final Book book;

    @override
    Widget build(BuildContext context) {
      BookService bookService = context.read&amp;lt;BookService&amp;gt;();

      return ListTile(
        onTap: () {
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) =&amp;gt; WebViewPage(
                url: book.previewLink.replaceFirst(&amp;quot;http&amp;quot;, &amp;quot;https&amp;quot;),
              ),
            ),
          );
        },
        leading: Image.network(
          book.thumbnail,
          fit: BoxFit.fitHeight,
        ),
        title: Text(
          book.title,
          style: TextStyle(fontSize: 16),
        ),
        subtitle: Text(
          &amp;quot;${book.authors.join(&amp;quot;, &amp;quot;)}\n${book.publishedDate}&amp;quot;,
          style: TextStyle(color: Colors.grey),
        ),
        trailing: IconButton(
          onPressed: () {
            bookService.toggleLikeBook(book: book);
          },
          icon: bookService.likedBookList.map((book) =&amp;gt; book.id).contains(book.id)
              ? Icon(
                  Icons.star,
                  color: Colors.amber,
                )
              : Icon(Icons.star_border),
        ),
      );
    }
  }

  class LikedBookPage extends StatelessWidget {
    const LikedBookPage({super.key});

    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;BookService&amp;gt;(
        builder: (context, bookService, child) {
          return Scaffold(
            body: Padding(
              padding: const EdgeInsets.symmetric(horizontal: 12),
              child: ListView.separated(
                itemCount: bookService.likedBookList.length,
                separatorBuilder: (context, index) {
                  return Divider();
                },
                itemBuilder: (context, index) {
                  if (bookService.likedBookList.isEmpty) return SizedBox();
                  Book book = bookService.likedBookList.elementAt(index);
                  return BookTile(book: book);
                },
              ),
            ),
          );
        },
      );
    }
  }

  class WebViewPage extends StatelessWidget {
    WebViewPage({super.key, required this.url});

    String url;

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.grey,
          title: Text(url),
        ),
        body: WebView(initialUrl: url),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;book.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  class Book {
    String id;
    String title;
    String subtitle;
    List authors;
    String publishedDate;
    String thumbnail; // 썸네일 이미지 링크
    String previewLink; // ListTile 을 눌렀을 때 이동하는 링크

    Book({
      required this.id,
      required this.title,
      required this.subtitle,
      required this.authors,
      required this.publishedDate,
      required this.thumbnail,
      required this.previewLink,
    });

    Map toJson() {
      return {
        &amp;quot;id&amp;quot;: id,
        &amp;quot;title&amp;quot;: title,
        &amp;quot;subtitle&amp;quot;: subtitle,
        &amp;quot;authors&amp;quot;: authors,
        &amp;quot;publishedDate&amp;quot;: publishedDate,
        &amp;quot;thumbnail&amp;quot;: thumbnail,
        &amp;quot;previewLink&amp;quot;: previewLink,
      };
    }

    factory Book.fromJson(json) {
      return Book(
        id: json[&amp;#39;id&amp;#39;],
        title: json[&amp;#39;title&amp;#39;],
        subtitle: json[&amp;#39;subtitle&amp;#39;],
        authors: json[&amp;#39;authors&amp;#39;],
        publishedDate: json[&amp;#39;publishedDate&amp;#39;],
        thumbnail: json[&amp;#39;thumbnail&amp;#39;],
        previewLink: json[&amp;#39;previewLink&amp;#39;],
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;book_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;dart:convert&amp;#39;;

  import &amp;#39;package:dio/dio.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;book.dart&amp;#39;;
  import &amp;#39;main.dart&amp;#39;;

  class BookService extends ChangeNotifier {
    BookService() {
      loadLikedBookList();
    }

    List&amp;lt;Book&amp;gt; bookList = []; // 책 목록
    List&amp;lt;Book&amp;gt; likedBookList = [];

    void toggleLikeBook({required Book book}) {
      String bookId = book.id;
      if (likedBookList.map((book) =&amp;gt; book.id).contains(bookId)) {
        likedBookList.removeWhere((book) =&amp;gt; book.id == bookId);
      } else {
        likedBookList.add(book);
      }
      notifyListeners();
      saveLikedBookList();
    }

    void search(String q) async {
      bookList.clear(); // 검색 버튼 누를때 이전 데이터들을 지워주기

      if (q.isNotEmpty) {
        Response res = await Dio().get(
          &amp;quot;https://www.googleapis.com/books/v1/volumes?q=$q&amp;amp;startIndex=0&amp;amp;maxResults=40&amp;quot;,
        );
        List items = res.data[&amp;quot;items&amp;quot;];

        for (Map&amp;lt;String, dynamic&amp;gt; item in items) {
          Book book = Book(
            id: item[&amp;#39;id&amp;#39;],
            title: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;title&amp;#39;] ?? &amp;quot;&amp;quot;,
            subtitle: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;subtitle&amp;#39;] ?? &amp;quot;&amp;quot;,
            authors: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;authors&amp;#39;] ?? [],
            publishedDate: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;publishedDate&amp;#39;] ?? &amp;quot;&amp;quot;,
            thumbnail: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;imageLinks&amp;#39;]?[&amp;#39;thumbnail&amp;#39;] ??
                &amp;quot;https://thumbs.dreamstime.com/b/no-image-available-icon-flat-vector-no-image-available-icon-flat-vector-illustration-132482953.jpg&amp;quot;,
            previewLink: item[&amp;#39;volumeInfo&amp;#39;][&amp;#39;previewLink&amp;#39;] ?? &amp;quot;&amp;quot;,
          );
          bookList.add(book);
        }
      }
      notifyListeners();
    }

    saveLikedBookList() {
      List likedBookJsonList =
          likedBookList.map((book) =&amp;gt; book.toJson()).toList();
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      String jsonString = jsonEncode(likedBookJsonList);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      prefs.setString(&amp;#39;likedBookList&amp;#39;, jsonString);
    }

    loadLikedBookList() {
      String? jsonString = prefs.getString(&amp;#39;likedBookList&amp;#39;);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      if (jsonString == null) return; // null 이면 로드하지 않음

      List likedBookJsonList = jsonDecode(jsonString);
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      likedBookList =
          likedBookJsonList.map((json) =&amp;gt; Book.fromJson(json)).toList();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/972</guid>
      <comments>https://comphy.tistory.com/972#entry972comment</comments>
      <pubDate>Thu, 27 Apr 2023 20:49:18 +0900</pubDate>
    </item>
    <item>
      <title>[3주차_-_패키지_사용법_익히기__앱의_기능_만들기_(마이메모)</title>
      <link>https://comphy.tistory.com/971</link>
      <description>&lt;h1&gt;3주차 - 패키지 사용법 익히기 &amp;amp; 앱의 기능 만들기 (마이메모)&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/153c8b75-3ebe-4801-a3e0-5e585716bd54/3%EC%A3%BC%EC%B0%A8_-_%ED%8C%A8%ED%82%A4%EC%A7%80_%EC%82%AC%EC%9A%A9%EB%B2%95_%EC%9D%B5%ED%9E%88%EA%B8%B0__%EC%95%B1%EC%9D%98_%EA%B8%B0%EB%8A%A5_%EB%A7%8C%EB%93%A4%EA%B8%B0_(%EB%A7%88%EC%9D%B4%EB%A9%94%EB%AA%A8).pdf&quot;&gt;3주차&lt;em&gt;-&lt;/em&gt;패키지&lt;em&gt;사용법_익히기__앱의_기능_만들기&lt;/em&gt;(마이메모).pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;단축키 모음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;새로고침 &lt;code&gt;F5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;저장&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전체선택&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;잘라내기&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;콘솔창 줄바꿈&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;enter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드정렬&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Alt&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;들여쓰기&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;들여쓰기 취소 : &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주석&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;패키지 사용법 익히기&lt;/li&gt;
&lt;li&gt;Create / Read / Update / Delete 기능 구현하기&lt;/li&gt;
&lt;li&gt;상태 관리의 필요성 이해하기&lt;/li&gt;
&lt;li&gt;Provider 사용법 익히기&lt;/li&gt;
&lt;li&gt;shared_preferences 사용법 익히기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;01. 패키지(Package)&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 패키지란?&lt;/p&gt;
  &lt;aside&gt;
    다른 사람들이 만들어 둔 위젯 또는 기능들을 **패키지**라고 부릅니다.

&lt;p&gt;  패키지를 활용하면 개발 시간을 획기적으로 단축할 수 있습니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Flutter는 &lt;code&gt;pub.dev&lt;/code&gt;라는 사이트에서 패키지들을 검색하실 수 있습니다. 코드스니펫을 복사해서 새 탭에서 띄워주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://pub.dev/&quot;&gt;&lt;strong&gt;[코드스니펫] pub.dev&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://pub.dev/&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9e74b84f-b51e-4789-9a74-c38325a6f3c8/Untitled.png)

- 밑으로 조금 내려와 보면 Flutter Favorites라고 인증된 신뢰할 만한 패키지 목록이 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/68298554-45dc-4a21-8796-49647462169c/Untitled.png)

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7a7eb553-ac5a-45dd-ab1e-5bf60e361959/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 패키지 문서 보는 방법&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사해 새 탭에서 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://pub.dev/packages/google_fonts&quot;&gt;&lt;strong&gt;[코드스니펫] Pub.dev google_fonts&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://pub.dev/packages/google_fonts&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;aside&amp;gt;
  처음에는 패키지 설치 방법 및 사용 설명서를 읽는데 집중해주세요!

모든 문서가 영어로 되어 있지만, 계속 보다 보면 나오는 용어들이 계속 나오는 것을 보실 수 있습니다.

&amp;lt;/aside&amp;gt;

![Pub.dev.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6eaff257-24ce-4f9f-957f-249dd7030249/Pub.dev.png)

&amp;lt;aside&amp;gt;
  Readme와 Example 탭을 보면 해당 패키지의 사용법을 익힐 수 있습니다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) 패키지 추천 사이트&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/Solido/awesome-flutter#ui&quot;&gt;Awesome Flutter&lt;/a&gt; :  Flutter에 관련된 모든 자료를 모아둔 Github 문서입니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/772db082-ac3b-414a-8e68-c7114512f429/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://fluttergems.dev/&quot;&gt;Flutter Gems&lt;/a&gt; : 카테고리별 Flutter 패키지 모음&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/84468fdd-e626-463f-9b02-590d30b24e02/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;aside&amp;gt;
  직접 프로젝트를 진행하면서 패키지 사용법을 배워보도록 하겠습니다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;02. 마이메모 앱 만들기&lt;/h2&gt;
&lt;aside&gt;
  나만의 메모를 작성하는 앱을 만들어 보도록 하겠습니다.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;완성본&lt;/p&gt;
  &lt;aside&gt;
    메모장 앱의 기능은 다음과 같습니다.

&lt;p&gt;  **1. 메모 작성(Create)&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;메모 조회(Read)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;메모 수정(Update)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;메모 삭제(Delete)**&lt;/p&gt;
&lt;/aside&gt;

&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dc320bac-451d-40dc-b002-1ff32064c493/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;aside&gt;
  **C**reate / **R**ead / **U**pdate / **D**elete의 앞 글자를 따서 **CRUD**라고 부릅니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;  &lt;strong&gt;CRUD&lt;/strong&gt;는 가장 기본이 되는 데이터 처리 기능입니다.&lt;/p&gt;
&lt;p&gt;  &lt;strong&gt;게시판 기능&lt;/strong&gt;을 만든다면 아래 &lt;strong&gt;CRUD&lt;/strong&gt; 기능이 필수적으로 제공되어야 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;글 쓰기(Create)&lt;/li&gt;
&lt;li&gt;글 읽기(Read)&lt;/li&gt;
&lt;li&gt;글 수정(Update)&lt;/li&gt;
&lt;li&gt;글 삭제(Delete)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;  &lt;strong&gt;유저 정보&lt;/strong&gt;를 다루는 과정도 &lt;strong&gt;CRUD&lt;/strong&gt;로 표현하면 다음과 같습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;회원 가입(Create)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로필 보여주기(Read)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;회원 정보 수정(Update)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;회원 탈퇴(Delete)&lt;/p&gt;
&lt;p&gt;이와 같이 &lt;strong&gt;CRUD&lt;/strong&gt;는 다루는 데이터의 종류만 바뀌고 항상 기본적으로 구현하는 &lt;strong&gt;데이터 처리 기능&lt;/strong&gt;입니다.&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) 프로젝트 준비&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Flutter 프로젝트 생성&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;VSCode에서 아래와 같이 네모 모양의 &lt;code&gt;Stop&lt;/code&gt; 버튼을 눌러 기존에 실행한 앱을 종료해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d4333a9a-cd31-4b15-b08e-e932b1dd2c58/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Command Palette` 단축키
 window : `Ctrl + Shift + P`
 macOS : `Cmd + Shift + P`

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/79df0a20-90b1-47bb-a58a-4b27ec836f34/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;명령어를 검색하는 팝업창이 뜨면, &lt;code&gt;flutter&lt;/code&gt;라고 입력한 뒤 &lt;code&gt;Flutter: New Project&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a6058de-bbf7-410f-9dc3-a96d6bd7ac58/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Application&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c7145f5-c6f6-4306-b78e-e3bb31e24710/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트를 저장할 폴더를 선택하는 화면이 나오면 &lt;code&gt;flutter&lt;/code&gt; 폴더를 선택한 뒤 &lt;code&gt;Select a folder to create the project in&lt;/code&gt; 버튼을 눌러 주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트 이름을 &lt;code&gt;mymemo&lt;/code&gt; 로 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e8d2f5d2-c9e8-4365-9467-2dec963ea9d4/Screen_Shot_2022-09-07_at_8.04.46_PM.png&quot; alt=&quot;Screen Shot 2022-09-07 at 8.04.46 PM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 만약 중간에 아래와 같은 팝업이 뜬다면, 체크박스를 선택한 뒤 파란 버튼을 클릭해주세요. (팝업이 안보이시면 넘어가주세요!)&lt;/p&gt;
 &lt;aside&gt;
   아래 팝업에 대한 자세한 사항은 [링크](https://stackoverflow.com/questions/67914668/vs-code-do-you-trust-the-authors-of-the-files-in-this-folder)를 참고해주세요.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6827f86-f59a-4808-9d8d-75245b9ec10d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음과 같이 프로젝트가 생성됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/76f1f124-704d-4497-a439-b566f090223f/Screen_Shot_2022-09-07_at_8.06.51_PM.png&quot; alt=&quot;Screen Shot 2022-09-07 at 8.06.51 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;불필요한 힌트 숨기기&lt;/p&gt;
&lt;p&gt; 코드스니펫을 복사해서 &lt;code&gt;analysis_options.yaml&lt;/code&gt; 파일의 24번째 라인 뒤에 붙여 넣고 저장해주세요.&lt;/p&gt;
 &lt;aside&gt;
   아래 내용은 학습 단계에서 불필요한 내용을 화면에 표시하지 않도록 설정하는 과정입니다.

 &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] analysis_options.yaml&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      prefer_const_constructors: false
      prefer_const_literals_to_create_immutables: false&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8470956b-ccc2-4d75-86d8-83042bafaf22/Untitled.png)

        - 어떤 의미인지 궁금하신 분들을 위해
            - `main.dart` 파일을 열어보시면 파란 실선이 있습니다.

                ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d1393a08-a362-4be3-bd9b-835e184f5ef3/Untitled.png)

            - 파란 줄은, 개선할 여지가 있는 부분을 VSCode가 알려주는 표시입니다.

                12번째 라인에 마우스를 올리면 아래와 같이 설명이 뜹니다.

                ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7fd5cee4-d935-4d2d-9c35-e11d4f3c6cae/Untitled.png)

                위젯이 변경될 일이 없기 때문에 `const`라는 키워드를 앞에 붙여 상수로 선언하라는 힌트입니다.

                &amp;lt;aside&amp;gt;
                  상수로 만들면 어떤 이점이 있나요?

                상수로 선언된 위젯들은 화면을 새로 고침 할 때 해당 위젯들은 변경을 하지 않기 때문에 스킵하여 성능상 이점이 있습니다.

                &amp;lt;/aside&amp;gt;

                아래와 같이 `Icon`앞에 `const` 키워드를 붙여주시면 됩니다.

                ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2aef9ee8-c4ef-4c42-bd08-d358c4620341/Untitled.png)

            - 지금은 학습 단계이니 눈에 띄지 않도록 해주도록 하겠습니다.
    9. 아래 코드스니펫을 복사해서, `main.dart`의 기존 코드를 모두 지우고 붙여 넣은 뒤 저장해 주세요.
        - **[코드스니펫]  main.dart**

            ```dart
            import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
            import &amp;#39;package:flutter/material.dart&amp;#39;;

            void main() {
              runApp(const MyApp());
            }

            class MyApp extends StatelessWidget {
              const MyApp({super.key});

              @override
              Widget build(BuildContext context) {
                return MaterialApp(
                  debugShowCheckedModeBanner: false,
                  home: HomePage(),
                );
              }
            }

            // 홈 페이지
            class HomePage extends StatelessWidget {
              const HomePage({super.key});

              @override
              Widget build(BuildContext context) {
                return Scaffold(
                  appBar: AppBar(
                    title: Text(&amp;quot;mymemo&amp;quot;),
                  ),
                  body: Center(child: Text(&amp;quot;메모를 작성해주세요&amp;quot;)),
                  floatingActionButton: FloatingActionButton(
                    child: Icon(Icons.add),
                    onPressed: () {
                      // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (_) =&amp;gt; DetailPage()),
                      );
                    },
                  ),
                );
              }
            }

            // 메모 생성 및 수정 페이지
            class DetailPage extends StatelessWidget {
              DetailPage({super.key});

              TextEditingController contentController = TextEditingController();

              @override
              Widget build(BuildContext context) {
                return Scaffold(
                  appBar: AppBar(
                    actions: [
                      IconButton(
                        onPressed: () {
                          // 삭제 버튼 클릭시
                        },
                        icon: Icon(Icons.delete),
                      )
                    ],
                  ),
                  body: Padding(
                    padding: const EdgeInsets.all(16),
                    child: TextField(
                      controller: contentController,
                      decoration: InputDecoration(
                        hintText: &amp;quot;메모를 입력하세요&amp;quot;,
                        border: InputBorder.none,
                      ),
                      autofocus: true,
                      maxLines: null,
                      expands: true,
                      keyboardType: TextInputType.multiline,
                      onChanged: (value) {
                        // 텍스트필드 안의 값이 변할 때
                      },
                    ),
                  ),
                );
              }
            }
            ```

- 에뮬레이터 실행하기
    1. VSCode 우측 하단에 `Chrome (web-javascript)`를 클릭해주세요.
    (에뮬레이터가 이미 실행중이라면 3번으로 이동해 주세요.)

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5d0db764-7e21-4261-8c19-94162c4d0ba8/Untitled.png)

    2. `Start Pixel 2 API 29 mobile emulator`를 선택해주세요. macOS의 경우 iOS 에뮬레이터로 진행하셔도 무방합니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e0e7243-7d95-4b57-99df-08fd28e1b4d7/Untitled.png)

        잠시 기다리면 에뮬레이터가 실행됩니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf5640d3-c9a9-4105-bd40-9dcb309412f5/Untitled.png)

    3. VSCode 우측 상단에 **아래 화살표**를 눌러 `Run Without Debugging`을 눌러주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fba923c5-b4b0-4ddb-b381-ae8686c0315c/Untitled.png)

        &amp;lt;aside&amp;gt;
          `Start Debugging`으로 실행해도 무방합니다. 디버깅 모드는 특정 라인에서 앱 실행을 멈추고 해당 변수에 어떤 값이 들어있는지 볼 수 있지만 `Run without debugging`이 실행 속도가 더 빨라 안내를 위와 같이 드렸습니다  

        &amp;lt;/aside&amp;gt;

        에뮬레이터에 아래와 같이 **메모장** 화면이 나오면 완료!

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1092421d-a876-4748-aa68-7370d5a1cb3b/Untitled.png)

        &amp;lt;aside&amp;gt;
          위와 같이 메모를 보여주는 `HomePage`와 메모를 작성하는 `DetailPage` 두 페이지로 구성된 앱입니다.

        기본적인 레이아웃과 페이지 이동 기능은 구현되어 있고, CRUD 기능을 함께 구현해 보도록 하겠습니다.

        &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 메모 목록 조회(Read)&lt;/p&gt;
  &lt;aside&gt;
    구현 목표

&lt;ul&gt;
&lt;li&gt;메모가 없는 경우, &lt;code&gt;메모를 작성해 주세요&lt;/code&gt; 문구를 보여주기&lt;/li&gt;
&lt;li&gt;메모가 있는 경우, 메모 목록을 보여주기&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;HomePage&lt;/code&gt;는 &lt;strong&gt;메모의 유무, 메모의 개수라는 상태에 따라 다른 화면을 갱신해야 하므로&lt;/strong&gt; &lt;code&gt;StatefulWidget&lt;/code&gt;으로 변경해줍니다.&lt;/p&gt;
&lt;p&gt; 21번째 라인에 &lt;code&gt;StatelessWidget&lt;/code&gt;을 클릭한 뒤 Quick Fix(&lt;code&gt;Ctrl/Cmd + .&lt;/code&gt;)를 누른 뒤 &lt;code&gt;Convert to StatefulWidget&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/61ebbaef-1e53-44e7-8b53-abe669de966f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 아래와 같이 &lt;code&gt;_HomePageState&lt;/code&gt;라는 상태 클래스가 추가되었습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/64221ed7-d6e8-44a0-afbd-a99df4d53641/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;메모 목록을 가지고 있을 상태 변수 &lt;code&gt;memoList&lt;/code&gt;를 추가해 줍니다.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해서 28번째 라인 맨 뒤에 붙여 넣어 주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] HomePage / memoList 상태 변수&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      List&amp;lt;String&amp;gt; memoList = [&amp;#39;장보기 목록: 사과, 양파&amp;#39;]; // 전체 메모 목록
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      `memoList`는 `String`만 받는 배열로 선언하였습니다.
    미리 `memoList`에 `&amp;#39;장보기 목록: 사과, 양파&amp;#39;` 라는 항목을 하나 넣어 두었습니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/46fc74b3-4b74-48d5-8eb8-f4530e9f546d/Untitled.png)

3. `memoList`에 항목이 있을 때 `body`에 해당 항목이 보이도록 해봅시다.

    코드스니펫을 복사해서 37번째 라인을 교체해 주세요.

    - **[코드스니펫] HomePage / memoList 조건문**

        ```dart
        body: memoList.isEmpty
                  ? Center(child: Text(&amp;quot;메모를 작성해 주세요&amp;quot;))
                  : Center(child: Text(&amp;#39;메모가 존재합니다!&amp;#39;)),
        ```


    &amp;lt;aside&amp;gt;
      `조건 ? true : false` 형태의 조건문을 사용하여 조건에 따라 위젯을 다르게 보여줄 수 있습니다. (이를 “삼항 연산자” 라고 합니다.)

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a52ec0e1-e3de-4ccb-affe-afc086f9a0c3/Untitled.png)

    미리 `memoList`에 `&amp;#39;장보기 목록: 사과, 양파’`라는 항목을 하나 넣어 두었기 때문에 `memoList.isEmpty`는 `false`가 되고, `메모가 존재합니다!` 라는 문구가 에뮬레이터에 나오는 것을 보실 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/204c4470-92f2-4ebf-821b-6b5601a53f13/Untitled.png)

4. `memoList`가 존재하는 경우, `ListView`를 이용해 보여주도록 코드스니펫을 복사해서 39번째 라인을 교체해 주세요.
    - **[코드스니펫] HomePage / ListView**

        ```dart
        : ListView.builder(
                      itemCount: memoList.length, // memoList 개수 만큼 보여주기
                      itemBuilder: (context, index) {
                        String memo = memoList[index]; // index에 해당하는 memo 가져오기
                        return ListTile(
                          // 메모 고정 아이콘
                          leading: IconButton(
                            icon: Icon(CupertinoIcons.pin),
                            onPressed: () {
                              print(&amp;#39;$memo : pin 클릭 됨&amp;#39;);
                            },
                          ),
                          // 메모 내용 (최대 3줄까지만 보여주도록)
                          title: Text(
                            memo,
                            maxLines: 3,
                            overflow: TextOverflow.ellipsis,
                          ),
                          onTap: () {
                            // 아이템 클릭시
                            print(&amp;#39;$memo : 클릭 됨&amp;#39;);
                          },
                        );
                      },
                    ),
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aa6d810c-cacf-42a1-8ec9-e3f71fd0c928/Untitled.png)

    &amp;lt;aside&amp;gt;
      `ListTile` 위젯을 활용하면 박스 안에 여러 영역에 다른 위젯을 손쉽게 배치할 수 있습니다.

    ![ListTile.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2a86c971-bcf5-4882-a07f-37a674357946/ListTile.png)

    좀 더 상세한 설명은 아래 공식 문서를 참고해 주세요.

    - **[[코드스니펫] ListTile 공식문서 URL](https://api.flutter.dev/flutter/material/ListTile-class.html)**

        ```dart
        https://api.flutter.dev/flutter/material/ListTile-class.html
        ```

    &amp;lt;/aside&amp;gt;

    저장해주시면 에뮬레이터에 메모가 추가된 것을 볼 수 있습니다.

    ![simulator_screenshot_5FAD2CD3-E9AC-4DCD-B347-58A12C2F36AB.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2e67a7ca-f59f-4be8-b089-a5e4c0042c09/simulator_screenshot_5FAD2CD3-E9AC-4DCD-B347-58A12C2F36AB.png)

    &amp;lt;aside&amp;gt;
      클릭시 print문은 `view` → `Debug Console`을 선택하셔서 보실 수 있습니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d7d99dcc-642e-4282-80df-8b8331584a01/Untitled.png)

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/23627feb-d8e8-43e1-89b1-e1c313210f5c/Untitled.png)

5. `memoList` 상태 변수에 `, &amp;#39;새 메모&amp;#39;` 를 추가해 봅시다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e886bb08-2a5d-4f4d-b75d-0d6591ac1217/Untitled.png)

    &amp;lt;aside&amp;gt;
      저장을 해도 에뮬레이터에 반영이 안되실 겁니다.
    아직 우리는 변경된 상태에 따라 화면을 새로고침 해주는 코드를 작성하지 않았습니다.
    따라서 `Restart` 를 해야 앱에 반영됩니다.

    &amp;lt;/aside&amp;gt;

    우측 상단에 `Restart` 버튼을 눌러주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fed3dbef-6b58-4dfd-a519-419a3db55550/Untitled.png)

    앱에 새로 추가된 메모가 보이는 것을 보실 수 있습니다.

    ![Screen Shot 2022-09-07 at 11.02.35 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/31a55655-af59-48d9-9dde-f5ca2027ead9/Screen_Shot_2022-09-07_at_11.02.35_PM.png)

    여기까지 메모 목록 조회 기능 완성!&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) 메모 개별 조회 (Read)&lt;/p&gt;
  &lt;aside&gt;
    구현 목표

&lt;ul&gt;
&lt;li&gt;메모 목록을 클릭하면 해당 메모 상세 페이지로 넘어가기&lt;/li&gt;
&lt;li&gt;메모 상세 페이지에서 해당 메모 내용을 보여주기&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;메모 목록에서 &lt;code&gt;ListTile&lt;/code&gt; 을 눌렀을 때 &lt;code&gt;DetailPage&lt;/code&gt; 로 이동하도록 합니다.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해서 59번째 라인을 지우고 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] ListTile onTap / DetailPage 로 이동&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  Navigator.push(
                        context,
                        MaterialPageRoute(builder: (_) =&amp;gt; DetailPage()),
                      );&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/61e61367-b85b-4169-8c80-811d65988e2f/Untitled.png)

    &amp;lt;aside&amp;gt;
      메모 목록의 메모 하나를 누르면 DetailPage 로 잘 넘어가는 것을 확인할 수 있습니다.

    &amp;lt;/aside&amp;gt;

2. 이제 `DetailPage` 에서 해당 메모 내용을 볼 수 있도록 해야겠죠!

    `memoList` 와 해당 메모의 `index` 를 `DetailPage` 로 넘겨주겠습니다. 

    (`memoList[index]` 와 같은 식으로 개별 메모의 내용을 불러오기 위함입니다)

    우선 `DetailPage` 가 메모의 내용을 변수에 담아 받도록 해야합니다. 아래 코드스니펫을 복사해서 83번째 라인 뒤에 붙여넣어주세요.

    - **[코드스니펫] ListTile onTap / DetailPage 로 이동**

        ```dart

          final List&amp;lt;String&amp;gt; memoList;
          final int index;
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/89a8640a-f80b-4bd4-bdf8-4d77a5fa949a/Untitled.png)

    83번째 라인에 빨간 밑줄이 그어졌네요! 이는 우리가 선언한 `memoList` 와 `index` 라는 변수에 아무것도 담지 않아 생긴 일입니다. 

    &amp;lt;aside&amp;gt;
      빨간 밑줄이 그어진 코드에 마우스를 올려보면 보다 자세한 에러 메시지를 확인할 수 있습니다

    &amp;lt;/aside&amp;gt;

    ![Screen Shot 2022-09-12 at 4.11.53 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0930cb3c-6dda-4117-ae2f-28964cb38360/Screen_Shot_2022-09-12_at_4.11.53_PM.png)

    아래 사진처럼 Quick Fix(`Ctrl/Cmd + .`)를 누른 뒤 `Add final field formal parameters` 를 클릭해줍니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/62fc6605-6198-4b1c-a7a4-fe5892871acf/Untitled.png)

    아래와 같이 생성자에 넘겨받는 변수를 추가해줌으로서 문제가 해결되었습니다!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e76e8c3-b1b1-448b-bc8a-8a68611f6026/Untitled.png)

    그러자 이번엔 `HomePage` 에서 에러가 발생했네요! (61번째 줄, 73번째 줄)

    &amp;lt;aside&amp;gt;
      아래 사진처럼 VS Code 우측의 빨간 부분을 클릭해서 문제가 발생한 코드 줄을 빠르게 확인할 수 있습니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/03eae13b-dd60-4d91-9dea-74b5892b86e1/Untitled.png)

    61번째 줄에서 마우스를 에러가 난 부분 위에 올려보면, `memoList` 와 `index` 변수를 받아야 하는데 넘겨주지 않아서 생긴 문제라는 것을 확인할 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/36c0f8f5-8e35-4196-880c-2a6cbfbbda46/Untitled.png)

    `Quick Fix(Ctrl/Cmd + .)`를 누른 뒤 `Add required argument ‘index’` 와 `Add required argument ‘memoList’` 를 클릭해줍니다.

    ![Screen Shot 2022-09-12 at 4.16.56 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9d970a5c-3522-41e0-a626-9633f19c70dd/Screen_Shot_2022-09-12_at_4.16.56_PM.png)

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5fe8b75c-4a48-4f56-b68a-b37fbcb31588/Untitled.png)

    index 뒤 `null` 이 들어간 자리에 `index` 라고 작성해 위에서 만든 `index` 변수를 대입해줍니다.

    memoList 뒤에도 마찬가지로 `memoList` 변수를 그대로 대입해줍니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0fc0df8e-5ce3-495f-9a38-297be4098a8c/Untitled.png)

    `DetailPage` 괄호가 닫히는 부분(위 체크 표시) 에 쉼표를 찍어서 코드 정렬도 맞춰줍시다.

    ![Screen Shot 2022-09-12 at 4.24.05 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1026ebcc-e93a-4bb0-a14c-1d9fce463ec5/Screen_Shot_2022-09-12_at_4.24.05_PM.png)

    나머지 에러도 마저 해결해보겠습니다.

    아래 코드스니펫을 복사해 76번째~79번째 줄을 지우고 붙여넣어줍니다.

    - **[코드스니펫] 메모 생성 / floatingActionButton onPressed**

        ```dart
        String memo = &amp;#39;&amp;#39;; // 빈 메모 내용 추가
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (_) =&amp;gt; DetailPage(
                        index: memoList.indexOf(memo),
                        memoList: memoList,
                      ),
                    ),
                  );
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b3199c26-e897-4f03-bd5f-1ad945c031a3/Untitled.png)

3. 넘겨받은 변수에 담긴 내용을 화면에 표시해줍니다.

    `DetailPage` 에서 받은 `memoList` 의 `index` 번째에 있는 메모 내용을 화면에 띄워줘야겠죠! 

    아래 코드스니펫을 복사해 102번째 줄 맨 뒤에 붙여넣어줍니다.

    - **[코드스니펫] commentController 의 text 값 설정**

        ```dart

            contentController.text = memoList[index];

        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7f66f71d-fa75-4aca-b5dd-3bd1a57c825e/Untitled.png)

    이제 `DetailPage` 에 해당 메모의 내용이 보이는 것을 확인하실 수 있습니다!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c1d60e22-5141-4f8c-8aeb-d15d8e459ebd/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) 메모 작성(Create)&lt;/p&gt;
  &lt;aside&gt;
    구현 목표

&lt;ul&gt;
&lt;li&gt;메모 추가 버튼을 누르는 순간 메모가 생성되도록 하기&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;우측 하단의 &lt;code&gt;FloatingActionButton&lt;/code&gt; 을 클릭할 때마다 앞에서 만들어준 memoList 변수에 빈 메모를 추가 해주겠습니다.&lt;/p&gt;
&lt;p&gt; 지금은 해당 버튼을 누를 때 아래와 같은 에러가 발생할텐데요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/928098f9-7a2d-4f07-af4b-1b8f58916695/Screen_Shot_2022-09-12_at_4.34.50_PM.png&quot; alt=&quot;Screen Shot 2022-09-12 at 4.34.50 PM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 이는 우리가 새로운 메모를 &lt;code&gt;memoList&lt;/code&gt; 에 추가하지 않았기 때문에 &lt;code&gt;memoList.indexOf(memo)&lt;/code&gt; 가 &lt;code&gt;-1&lt;/code&gt; 을 반환해서 생기는 문제입니다.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해 76번째 줄 맨 뒤에 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] memoList 에 memo 추가&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
            memoList.add(memo);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d4e62b4f-5996-415b-a2d5-42cd96a3b83b/Untitled.png)

    이제 해당 버튼을 누를 때마다 `memoList` 에 빈 메모가 추가되고, `DetailPage` 로 이동하겠죠!

    &amp;lt;aside&amp;gt;
      생성한 메모의 index 를 `indexOf` 를 통해 찾아줍니다. 
    `memoList.length - 1` 로 작성해도 같은 결과가 나옵니다
    (add 는 배열의 맨 뒤에 요소를 추가하므로)

    &amp;lt;/aside&amp;gt;

2. 화면 상에 잘 나타나는지 확인해봅시다. 

    우측 하단의 `FloatingActionButton` 을 누르면 내용이 빈 메모의 `DetailPage`로 잘 넘어갑니다. 

    다시 `HomePage` 로 돌아와 보세요.

    ![Screen Shot 2022-09-08 at 2.39.59 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/77174237-2dff-4d4c-beb1-9537d703ac33/Screen_Shot_2022-09-08_at_2.39.59_AM.png)

    메모가 새로 생성이 되지 않는군요.

    `memoList` 에는 변화가 생겼지만, 화면을 새로 그려주지 않아서 생기는 문제입니다. 
    (`Hot Reload` 를 한 번 실행해주면 새로 생성된 메모가 보일거에요)

    &amp;lt;aside&amp;gt;
      이럴 때는 `setState` 를 활용해 변화한 상태(변수)에 맞게 화면을 새로 그려주면 됩니다.

    &amp;lt;/aside&amp;gt;

    아래 코드스니펫을 복사해 77번째 줄을 지우고 붙여넣어주세요. `memoList` 에 `memo` 를 추가하는 코드를 `setState` 로 감싸줬습니다.

    - **[코드스니펫] setState**

        ```dart
        setState(() {
                    memoList.add(memo);
                  });
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4dce72fc-434e-4d94-8bc1-23332bd18ddb/Untitled.png)

    이제 정상적으로 추가된 빈 메모가 화면에 나타나는 것을 볼 수 있습니다.&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;5) 메모 수정 (Update)&lt;/p&gt;
  &lt;aside&gt;
    **구현 목표**

&lt;ul&gt;
&lt;li&gt;DetailPage 에서 텍스트를 변경할 때마다 메모 내용을 업데이트 하기&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DetailPage&lt;/code&gt; 에 있는 &lt;code&gt;TextField&lt;/code&gt; 의 &lt;code&gt;onChange&lt;/code&gt;  에 들어가는 함수를 수정하면, 텍스트가 변할 때마다 특정한 동작을 할 수 있습니다.&lt;/p&gt;
&lt;p&gt; 먼저, 우측 하단의 &lt;code&gt;FloatingActionButton&lt;/code&gt;을 클릭하여 &lt;code&gt;DetailPage&lt;/code&gt;로 이동해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/60f63294-4f01-4fd1-ba50-3843c48909f5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;iOS 에뮬레이터에서 키보드를 보이게 하는 방법&lt;/p&gt;
  &lt;aside&gt;
    `iOS 에뮬레이터 선택` → `I/O` → `Keyboard` → `Connect Hardware Keyboard`를 해제하면 키보드가 올라옵니다.

&lt;p&gt;  또는 iOS 에뮬레이터 선택 후 단축키로 &lt;code&gt;Cmd + Shift + K&lt;/code&gt;&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d3a674dc-7187-4cb1-9c40-2dab861c2223/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e97f60b1-3dce-4790-92ea-8a63b74d23e4/simulator_screenshot_7A27686D-DC81-4E6A-A974-50A4A89576A0.png&quot; alt=&quot;simulator_screenshot_7A27686D-DC81-4E6A-A974-50A4A89576A0.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    133번째 줄에 `print(value)` 를 입력하고 `TextField` 의 값을 변경해보면

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c20d3969-7f49-452a-8b40-2e683b853962/Untitled.png)

    `TextField` 에 내용을 적는대로 콘솔에 프린트 되는 것을 볼 수 있습니다. 이제 `memoList` 에 해당 내용을 추가해주면 되겠네요! 

    아래 코드스니펫을 복사해 133번째 줄을 지우고 붙여넣어줍니다.

    - **[코드스니펫] TextField onChanged /  memoList[index] 의 값 설정**

        ```dart
        memoList[index] = value;
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7b32a817-4ae7-49c8-85f4-d0aa474152c5/Untitled.png)

    자 그럼 메모를 작성하고 돌아와볼까요? `HomePage` 에 변화가 생겼는지 확인해봅시다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/12182fc3-bc81-49f3-a1c8-f8cf8e98b266/Untitled.png)

    메모 내용이 변경되지 않는군요! 

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cce61e92-1a2c-4056-b048-8b36d97e7d3c/Untitled.png)

    `Hot Reload` 를 실행해야 정상적으로 변경된 메모 내용이 뜨는 것을 확인할 수 있습니다. 이는 HomePage 의 화면이 갱신되지 않았다는 것인데요.

    &amp;lt;aside&amp;gt;
      기존에 우리는 `StatefulWidget` 의 `setState` 를 활용해 **해당 위젯의 화면**을 갱신할 수 있었습니다. 그러나 위 경우는 `DetailPage` 에서 발생한 변화에 따라 `HomePage` 의 화면을 갱신해줘야 합니다.

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      `HomePage` 의 `setState` 함수를 통으로 `DetailPage` 로 넘겨주는 방법도 있지만, 보다 편리하게 코드를 작성하기 위해서는 **상태 관리(State Management)** 라는 것을 사용하게 됩니다.

    우선 메모 삭제를 구현한 후, 아래에서 이에 대해 보다 자세히 다뤄보겠습니다.

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;6) 메모 삭제 (Delete)&lt;/p&gt;
  &lt;aside&gt;
    **구현 목표**

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DetailPage&lt;/code&gt; 에서 삭제 아이콘을 누르는 경우, 삭제 확인 팝업(dialog)이 뜨고 &lt;code&gt;확인&lt;/code&gt; 버튼을 누르는 경우 아이템이 삭제되도록 합니다.&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;삭제 버튼 클릭 이벤트는 112번째 줄, &lt;code&gt;DetailPage&lt;/code&gt; 의 우상단에 있는 &lt;code&gt;IconButton&lt;/code&gt; 위젯의 &lt;code&gt;onPressed&lt;/code&gt;의 함수로 받을 수 있습니다. &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/be1ceb88-68b8-4c1a-9291-4866ddca807f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 먼저 삭제 버튼을 누르는 경우 &lt;code&gt;Dialog&lt;/code&gt;를 띄워보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt; 코드스니펫을 복사해 113번째 라인 맨 뒤에 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] DetailPage / showDialog&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
                showDialog(
                  context: context,
                  builder: (context) {
                    return AlertDialog(
                      title: Text(&amp;quot;정말로 삭제하시겠습니까?&amp;quot;),
                    );
                  },
                );&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f0333d17-7044-46c2-88c2-6db40c8e8496/Untitled.png)

    삭제 버튼을 눌러서 `Dialog`를 확인해보세요

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f84a2a93-7d23-4b9e-80d0-6463fdf86384/Untitled.png)

    &amp;lt;aside&amp;gt;
      Dialog 배경을 클릭하여 끌 수 있습니다.

    &amp;lt;/aside&amp;gt;

2. `AlertDialog`에 `취소`와 `확인` 버튼을 추가해 보도록 하겠습니다.

    코드스니펫을 복사해 118번째 라인 뒤에 붙여 넣어주세요.

    - **[코드스니펫] DetailPage / Dialog actions**

        ```dart

                            actions: [
                              // 취소 버튼
                              TextButton(
                                onPressed: () {
                                  Navigator.pop(context);
                                },
                                child: Text(&amp;quot;취소&amp;quot;),
                              ),
                              // 확인 버튼
                              TextButton(
                                onPressed: () {
                                  Navigator.pop(context);
                                },
                                child: Text(
                                  &amp;quot;확인&amp;quot;,
                                  style: TextStyle(color: Colors.pink),
                                ),
                              ),
                            ],
        ```


    &amp;lt;aside&amp;gt;
      `AlertDialog`의 `actions`는 배열로 원하는 위젯을 넣을 수 있는 파라미터 입니다.

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      버튼을 클릭하는 경우 `Navigator.pop(context);`를 호출하여 `Dialog`를 종료할 수 있습니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/661d9525-c0d1-49f7-a577-a1d7a6d16029/Untitled.png)

    ![Screen Shot 2022-09-12 at 6.31.28 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/da8304ff-d203-404c-8f18-11931aec5805/Screen_Shot_2022-09-12_at_6.31.28_PM.png)

3. `확인` 버튼 클릭 시 `MemoList`에서 항목을 삭제해 보도록 하겠습니다.

    코드스니펫을 복사해 130번째 줄을 지우고 붙여넣어주세요.

    - **[코드스니펫] HomePage / delete memo**

        ```dart
        memoList.removeAt(index); // index에 해당하는 항목 삭제
                                  Navigator.pop(context); // 팝업 닫기
                                  Navigator.pop(context); // HomePage 로 가기
        ```


    &amp;lt;aside&amp;gt;
      `List.removeAt(index)`를 이용하면 배열에서 index에 해당하는 항목을 삭제할 수 있습니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ef4c648c-8295-44ae-9585-dd7787622b4f/Untitled.png)

4. 저장 후 다이얼로그를 다시 띄우고 `확인` 버튼을 누르면..

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9da3d5a0-cc5c-49bb-af18-ee65ee35478a/Untitled.png)

    이번에도 “메모 수정” 때와 마찬가지로, Hot Reload 를 활용해 화면을 새로고침 해줘야 변경사항이 반영되는 것을 확인하실 수 있습니다. 

    &amp;lt;aside&amp;gt;
      위와 같은 상황에서는 `DetailPage` 에서 발생한 변화에 따라 `HomePage` 의 화면을 갱신해줘야 합니다.

    서로 다른 페이지에서 변수를 공유하고, 변수의 값을 바꾸며 여러 페이지의 화면을 한꺼번에 갱신해주기 위해 **상태 관리(State Management)** 라는 것을 배우고 적용해보겠습니다.

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt; 완성 코드&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(const MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({super.key});

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  // 홈 페이지
  class HomePage extends StatefulWidget {
    const HomePage({super.key});

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    List&amp;lt;String&amp;gt; memoList = [&amp;#39;장보기 목록: 사과, 양파&amp;#39;, &amp;#39;새 메모&amp;#39;]; // 전체 메모 목록

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text(&amp;quot;mymemo&amp;quot;),
        ),
        body: memoList.isEmpty
            ? Center(child: Text(&amp;quot;메모를 작성해 주세요&amp;quot;))
            : ListView.builder(
                itemCount: memoList.length, // memoList 개수 만큼 보여주기
                itemBuilder: (context, index) {
                  String memo = memoList[index]; // index에 해당하는 memo 가져오기
                  return ListTile(
                    // 메모 고정 아이콘
                    leading: IconButton(
                      icon: Icon(CupertinoIcons.pin),
                      onPressed: () {
                        print(&amp;#39;$memo : pin 클릭 됨&amp;#39;);
                      },
                    ),
                    // 메모 내용 (최대 3줄까지만 보여주도록)
                    title: Text(
                      memo,
                      maxLines: 3,
                      overflow: TextOverflow.ellipsis,
                    ),
                    onTap: () {
                      // 아이템 클릭시
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (_) =&amp;gt; DetailPage(
                            index: index,
                            memoList: memoList,
                          ),
                        ),
                      );
                    },
                  );
                },
              ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: () {
            // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
            String memo = &amp;#39;&amp;#39;; // 빈 메모 내용 추가
            setState(() {
              memoList.add(memo);
            });
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (_) =&amp;gt; DetailPage(
                  index: memoList.indexOf(memo),
                  memoList: memoList,
                ),
              ),
            );
          },
        ),
      );
    }
  }

  // 메모 생성 및 수정 페이지
  class DetailPage extends StatelessWidget {
    DetailPage({super.key, required this.memoList, required this.index});

    final List&amp;lt;String&amp;gt; memoList;
    final int index;

    TextEditingController contentController = TextEditingController();

    @override
    Widget build(BuildContext context) {
      contentController.text = memoList[index];

      return Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              onPressed: () {
                // 삭제 버튼 클릭시
                showDialog(
                  context: context,
                  builder: (context) {
                    return AlertDialog(
                      title: Text(&amp;quot;정말로 삭제하시겠습니까?&amp;quot;),
                      actions: [
                        // 취소 버튼
                        TextButton(
                          onPressed: () {
                            Navigator.pop(context);
                          },
                          child: Text(&amp;quot;취소&amp;quot;),
                        ),
                        // 확인 버튼
                        TextButton(
                          onPressed: () {
                            memoList.removeAt(index); // index에 해당하는 항목 삭제
                            Navigator.pop(context); // 팝업 닫기
                            Navigator.pop(context); // HomePage 로 가기
                          },
                          child: Text(
                            &amp;quot;확인&amp;quot;,
                            style: TextStyle(color: Colors.pink),
                          ),
                        ),
                      ],
                    );
                  },
                );
              },
              icon: Icon(Icons.delete),
            )
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: TextField(
            controller: contentController,
            decoration: InputDecoration(
              hintText: &amp;quot;메모를 입력하세요&amp;quot;,
              border: InputBorder.none,
            ),
            autofocus: true,
            maxLines: null,
            expands: true,
            keyboardType: TextInputType.multiline,
            onChanged: (value) {
              // 텍스트필드 안의 값이 변할 때
              memoList[index] = value;
            },
          ),
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;03. 상태 관리 패키지 Provider 준비하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;상태 관리(State Management)의 필요성&lt;/p&gt;
  &lt;aside&gt;
    아주 작은 프로젝트에서는 위에서 배우신 것과 같이 **StatefulWidget**만 활용하여도 충분히 만들 수 있습니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    위에서 다뤘듯이, `StatefulWidget` 의 `setState` 만을 활용해서는 **서로 다른 페이지**의 화면을 함께 갱신해주기가 까다롭습니다.

&lt;p&gt;  단순한 경우에는 부모 위젯 (HomePage) 의 &lt;code&gt;setState&lt;/code&gt; 를 자녀 위젯(DetailPage)으로 넘겨줘서 자녀 위젯에서 부모 위젯의 화면을 갱신할 수 있도록 하는 방법도 있습니다. 그러나 이러면 페이지가 조금만 많아져도 지나치게 코드가 복잡해지는 문제가 발생합니다.&lt;/p&gt;
&lt;p&gt;  현업에서는 이런 문제를 해결하기 위해 &lt;strong&gt;상태관리(State Management)&lt;/strong&gt; 패키지를 사용합니다.&lt;/p&gt;
&lt;p&gt;  상태관리란 &lt;strong&gt;최신 상태의 데이터를 보여주도록 페이지 간 데이터를 주고받고 관리하는 행위&lt;/strong&gt;를 의미합니다.&lt;/p&gt;
  &lt;/aside&gt;

  &lt;aside&gt;
    상태관리 관련 공식 문서는 [링크](https://docs.flutter.dev/development/data-and-backend/state-mgmt)를 참고해주세요.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5be5d378-7698-4b81-a87b-ec1de29f0141/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
  &lt;aside&gt;
    대부분의 **상태 관리 패키지**들은 위 사진과 같이 **중앙 집중식으로 데이터를 한 곳에 모아서 관리**합니다. 앞으로 데이터를 담당하는 클래스를 **서비스(Service)**라고 부르도록 하겠습니다.

&lt;p&gt;  이렇게 되면 각 페이지에서는 데이터에 대한 CRUD는 모두 &lt;strong&gt;서비스&lt;/strong&gt;에게 요청하는 방식으로 구현되고, 이를 통해 각 화면 간 데이터를 주고받는 문제를 해결할 수 있습니다.&lt;/p&gt;
  &lt;/aside&gt;

  &lt;aside&gt;
    상태관리 패키지는 여러 종류가 있습니다. 
  [pub.dev](http://pub.dev) 에서 Likes 를 많이 받은 순으로 나열하면 아래와 같습니다.

&lt;p&gt;  1) GetX&lt;/p&gt;
&lt;p&gt;  2) Provider&lt;/p&gt;
&lt;p&gt;  3) BloC&lt;/p&gt;
&lt;p&gt;  4) Riverpod&lt;/p&gt;
&lt;p&gt;  우리는 이 중, 사용법이 쉽고, Flutter 공식 문서에서도 추천하는 패키지인&lt;br&gt;  &lt;strong&gt;Provider&lt;/strong&gt;라는 패키지를 이용해보도록 하겠습니다.&lt;/p&gt;
  &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) &lt;strong&gt;Provider&lt;/strong&gt; 패키지 추가&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사해서 새 탭에서 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://pub.dev/packages/provider/install&quot;&gt;&lt;strong&gt;[코드스니펫] pub.dev / provider&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://pub.dev/packages/provider/install&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provider&lt;/strong&gt; 패키지 Install 탭이 아래와 같이 나오면, &lt;code&gt;flutter pub add provider&lt;/code&gt; 명령어 옆에 아이콘을 눌러 복사해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/30f05b4a-0e46-4849-a9cb-24143b00665d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Terminal&lt;/code&gt;을 열어 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d5bd89a5-15a1-41ad-bc72-e1d3e15631c3/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 아래와 같이 복사한 코드를 붙여넣고 엔터를 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/156af0d4-2c66-406e-926a-462abdcd2025/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그러면 아래와 같이 실행이 됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2286caef-ee2b-46b7-8d6b-6421eefef81c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;pubspec.yaml&lt;/code&gt; 파일에 39번째 라인에 아래와 같이 &lt;strong&gt;Provider&lt;/strong&gt;가 있으면 정상 설치 완료입니다.&lt;/p&gt;
 &lt;aside&gt;
   향후 **Provider**의 버전은 다르게 설치될 수 있으나 기본적인 사용법은 동일하므로 진행하셔도 됩니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2e65dc87-d260-40d5-8531-6203036da7f8/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) &lt;strong&gt;MemoService&lt;/strong&gt; 만들기&lt;/p&gt;
  &lt;aside&gt;
    모든 메모 데이터를 담당해줄 **MemoService**를 별도 파일로 만들어보도록 하겠습니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;lib&lt;/code&gt; 폴더에서 우클릭을 해주신 뒤 &lt;code&gt;New File&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6790c29b-9538-4ba0-9b1c-a2e73f28654b/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파일 이름은 &lt;code&gt;memo_service.dart&lt;/code&gt;로 짓도록 하겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/02dde3b1-1a9d-456f-9ad7-0796c5cc498c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사해 &lt;code&gt;memo_service.dart&lt;/code&gt;파일에 붙여 넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] MemoService / init&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;main.dart&amp;#39;;

  // Memo 데이터의 형식을 정해줍니다. 추후 isPinned, updatedAt 등의 정보도 저장할 수 있습니다.
  class Memo {
    Memo({
      required this.content,
    });

    String content;
  }

  // Memo 데이터는 모두 여기서 관리
  class MemoService extends ChangeNotifier {
    List&amp;lt;Memo&amp;gt; memoList = [
      Memo(content: &amp;#39;장보기 목록: 사과, 양파&amp;#39;), // 더미(dummy) 데이터
      Memo(content: &amp;#39;새 메모&amp;#39;), // 더미(dummy) 데이터
    ];
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      **MemoService**의 `memoList`가 변경되는 경우 해당 값을 보여주는 화면들을 갱신시켜 주는 기능을 구현하기 위해 `ChangeNotifier` 클래스의 기능을 물려 받았습니다.

    `ChangeNotifier`를 상속 받은 경우 `notifylisteners();`를 호출하여 위젯들을 갱신하는 기능을 사용할 수 있습니다. 자세한 사용법은 뒤쪽에서 알아보도록 하겠습니다.

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      앞으로 `Memo` 관련 데이터는 모두 **MemoService** 에서 담당할 예정이므로, 이전에 HomePage에서 구현했던 `memoList` 배열을 **MemoService** 에 추가했습니다.

    17, 18번째 `Memo`는 바로 테스트를 진행할 수 있도록 넣은 가짜(dummy) 데이터입니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/831f2d05-82e5-4307-a4ff-095cf682151a/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) &lt;strong&gt;Provider&lt;/strong&gt; 패키지 설정&lt;/p&gt;
  &lt;aside&gt;
    **Provider**를 이용하여 **MemoService**를 위젯 트리(Widget tree)의 꼭대기에 배치하고, 어디서든 쉽게 접근할 수 있도록 만들어 줍니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사해 &lt;code&gt;main.dart&lt;/code&gt; 파일의 5번째 라인을 지우고 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] Provider init&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; MemoService()),
        ],
        child: const MyApp(),
      ),
    );&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      `MultiProvider`로 `MyApp`을 감싸서 모든 위젯들의 최상단에 `provider`들을 등록해줍니다. `MultiProvider`는 위젯트리 꼭대기에 여러 `Service` 들을 등록할 수 있도록 만들 때 사용합니다.

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      **MemoService**는 `memoList` 값이 변하는 경우 `HomePage`에 변경사항을 알려주도록 구현해야 하므로 `ChangeNotifierProvider`로 **Provider**에 등록해줍니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/33619a81-8d46-402f-ac51-c5fe41edc020/Untitled.png)

2. **Provider**와 **MemoService**가 `Import` 되지 않아 에러가 발생합니다.

    6번째 라인에 `MultiProvider`를 클릭한 뒤 Quick Fix(`Ctrl/Cmd + .`)를 눌르고 `Import &amp;#39;package:provider/provider.dart&amp;#39;;`를 선택해주세요.

    &amp;lt;aside&amp;gt;
      만약 `import &amp;#39;package:provider/provider.dart&amp;#39;;`가 안보이신다면 `pubspec.yaml` 파일을 열고 저장 단축키를 눌러주세요.

    저장 단축키
    window : `Ctrl + S`
    macOS : `Cmd + S`

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8580c991-b81d-4001-90d3-7e21e45d081e/Untitled.png)

3. 마찬가지로 9번째 라인 **MemoService**를 클릭하신 뒤 `import &amp;#39;memo_service.dart&amp;#39;;`를 선택해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0135ea61-101f-4f4d-8de6-305f851db553/Untitled.png)

    **Provider** 설정이 완료 되었습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b44f89cb-ce78-4833-b8f8-cb8ee4ef104f/Untitled.png)

4. `Restart`를 클릭해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c26d3381-5b05-433e-8429-a25e46733820/Untitled.png)

    이제 `Provider` 를 사용할 준비가 되었습니다!&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;04. 마이메모 앱에 Provider 적용하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 메모 목록 조회(Read)&lt;/p&gt;
  &lt;aside&gt;
    **MemoService**에 있는 `memoList` 값을 가져와서 `HomePage`에 보여줘야 합니다. `Consumer` 위젯을 이용하면 위젯트리 꼭대기에 등록된 **MemoService**에 접근할 수 있습니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  Consumer&amp;lt;MemoService&amp;gt;(
      builder: (context, memoService, child) { 
          // memoService 라는 변수에 MemoService 를 담아서 쓸 수 있습니다.
          return 위젯;
      }
  )&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Consumer&amp;lt;MemoService&amp;gt;&lt;/code&gt; : 위젯트리 상단에서 찾아올 클래스 이름을 &lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt;사이에 적습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;builder: (context, memoService, child) { return 위젯; }&lt;/code&gt; : 화면에 보여줄 위젯을 반환하는 함수로, 위젯트리 상단에서 찾아온 클래스를 두 번째 파라미터로 받을 수 있습니다.&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;HomePage 전체 위젯을 &lt;code&gt;Consumer&lt;/code&gt;로 감싸주겠습니다. &lt;code&gt;main.dart&lt;/code&gt; 파일에 43번째 라인에 &lt;code&gt;Scaffold&lt;/code&gt;를 우클릭하여 &lt;code&gt;Refactor(Ctrl + Shift + R)&lt;/code&gt;을 선택하고, &lt;code&gt;Wrap with Builder&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6395d8e9-a080-4776-9a4b-7a8de53236c8/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 아래와 같이 &lt;code&gt;Scaffold&lt;/code&gt; 위젯을 &lt;code&gt;Builder&lt;/code&gt; 위젯이 감싸집니다. &lt;/p&gt;
&lt;p&gt; &lt;code&gt;Consumer&lt;/code&gt; 위젯과 형식이 비슷한 &lt;code&gt;Builder&lt;/code&gt; 를 사용해 형식을 잡아줬습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3afd633a-b972-4f94-9f7b-40ba27be240d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 이미지와 같이 43번째 라인에 &lt;code&gt;Builder&lt;/code&gt;를 &lt;code&gt;Consumer&amp;lt;MemoService&amp;gt;&lt;/code&gt;로 변경해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e9d9918-bbea-4acc-9c5b-2037550a842e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;에러를 해결하겠습니다.&lt;/p&gt;
&lt;p&gt; 아래 이미지와 같이  &lt;code&gt;,memoService, child&lt;/code&gt; 를 43번째 라인의 context 뒤에 추가해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/589ba20c-dfa5-453c-b299-a6342961495c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 줄 정렬을 예쁘게 해주기 위해 103번째 라인의 중괄호와 소괄호 사이에 콤마(&lt;code&gt;,&lt;/code&gt;)를 찍은 뒤 저장해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/deed8f13-609f-42b2-93c1-b46b2c13895a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 저장하면 코드가 정렬됩니다.&lt;/p&gt;
 &lt;aside&gt;
   `Consumer&lt;MemoService&gt;`는 위젯 트리를 타고 올라가 **Provider**로 등록된 **MemoService**를 찾습니다. 찾은 **MemoService**를 45번째 라인에 두 번째 파라미터인 `memoService` 에 담아줍니다.
 우리는 `memoService` 변수를 통해 MemoService 안에 있는 변수에 접근해 수정하고, 화면을 새로고침할 수 있습니다.

 &lt;/aside&gt;

 &lt;aside&gt;
   `MemoService`에서 값이 변경되는 경우, `StatefulWidget`의 `setState`와 같이 `notifyListeners();`를 호출하는데, 이 때 해당 서비스의 `Consumer`로 등록된 모든 위젯의 `builder` 함수가 재호출 되면서 화면이 갱신 됩니다.

 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사해 44번째 라인 뒤에 붙여넣어 주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] MemoService에서 memoList 가져오기&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
          // memoService로 부터 memoList 가져오기
          List&amp;lt;Memo&amp;gt; memoList = memoService.memoList;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f52d5fa9-d3bf-4749-97c2-84112f9b6c87/Untitled.png)

5. `Provider` 를 사용해 모든 `Memo` 데이터는 `MemoService` 에서 관리하기로 했습니다. 

    39번째 줄에 있는 `memoList` 는 더이상 사용하지 않으므로 지워줍니다.

    지우고 난 후 코드는 아래와 같습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a4375405-ca68-4ff3-95cd-40ff0df47381/Untitled.png)

6. 에러들을 해결하겠습니다.

    &amp;lt;aside&amp;gt;
      에러를 모두 해결하고 나면 자연스럽게 **메모 목록 조회(Read)** 기능이 구현되어 있을 겁니다!

    &amp;lt;/aside&amp;gt;

    이전에는 `String` 자료형의 메모 데이터를 사용했다면, 이번엔 `memo_service.dart` 에서 `Memo` 라는 클래스로 데이터의 틀을 잡고, 각각의 메모 데이터를 객체의 형태로 만들어 활용한다는 점을 유의해주세요.

    이렇게 데이터의 형식(자료형)이 바뀌었기 때문에 코드상에 에러가 발생합니다. 55번째 줄의 에러 위에 마우스를 올려봅시다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ff555c78-1f86-42c0-a36b-f27a37fc52ee/Untitled.png)

    `String` 자료형의 변수인 `memo` 에 `Memo` 자료형의 값이 들어갈 수 없다는 뜻이군요!

    Quick Fix(`Ctrl/Cmd + .`)를 누른 뒤 `‘Change ‘String’ to ‘Memo’ type annotation` 을 눌러 변수의 타입(자료형)을 바꿔줍니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d556b38b-9f54-4db7-ae9f-7556f474eba9/Untitled.png)

    코드가 아래와 같이 바뀌면서 에러가 사라지는 것을 볼 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5978d022-8b8e-4df5-81d2-81cd5152713a/Untitled.png)

    66번째 줄의 `memo` 를 `memo.content` 로 바꿔줍니다.

    &amp;lt;aside&amp;gt;
      `Text` 위젯은 `String` 자료형의 값을 받아서 보여주는 위젯입니다. 메모의 내용인 `content`를 넘겨줘야겠죠.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b2e67e54-d29e-44e2-b7a1-c343738c3f4b/Untitled.png)

    72-80 번째 줄을 지워줍니다. **메모 개별 조회 (Read)** 기능은 아래에서 다시 구현하겠습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c6b4bba1-52df-44af-81f8-cefb933930d5/Untitled.png)

    80-92 번째 줄을 지워줍니다. **메모 생성 (Create)** 기능도 아래에서 새로 구현하겠습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ecce9a27-36f2-4061-b729-56fb45871976/Untitled.png)

7. `Restart`를 클릭해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c26d3381-5b05-433e-8429-a25e46733820/Untitled.png)

    지금 화면에 보이는 메모 목록은 우리가 생성해준 더미 데이터들입니다. 

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0c74725c-5ac3-41d4-89a7-cb243aa72de8/Untitled.png)

    `Provider` 를 활용한 **메모 목록 조회 기능(Read)** 을 구현했습니다!&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 메모 개별 조회 (Read)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;이제 메모 데이터를 &lt;code&gt;MemoService&lt;/code&gt; 에서 관리하니, &lt;code&gt;DetailPage&lt;/code&gt;가 &lt;code&gt;memoList&lt;/code&gt; 를 따로 가지고 있을 필요가 없겠죠.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해 &lt;code&gt;main.dart&lt;/code&gt; 90-92번째 라인을 모두 지우고 붙여넣습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] DetailPage / 파라미터&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  DetailPage({super.key, required this.index});
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd990739-5a05-4fbe-bdf9-42dff52aabaf/Untitled.png)

2. `memoService` 에서 `memoList` 를 가져와 `index` 번째에 있는 메모를 뽑아보겠습니다.

    코드스니펫을 복사해 `main.dart` 98번째 라인을 지우고 붙여넣어주세요. 

    - **[코드스니펫] DetailPage / context.read 로 memo 1개 조회**

        ```dart
        MemoService memoService = context.read&amp;lt;MemoService&amp;gt;();
            Memo memo = memoService.memoList[index];

            contentController.text = memo.content;
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/87501087-8236-4773-b4ac-87f54e46141b/Untitled.png)

    &amp;lt;aside&amp;gt;
      `context.read&amp;lt;클래스명&amp;gt;();`를 이용하면 위젯 트리 상단에 있는 Provider로 등록한 클래스에 접근할 수 있습니다.

    변화에 따라 화면을 새로 그려줄 필요가 있을 때는 `Consumer` 를 사용하며, 화면을 새로고침할 필요없이 MemoService 의 변수나 함수만 이용하고자 한다면 `context.read&amp;lt;클래스명&amp;gt;()` 를 사용해도 됩니다.

    &amp;lt;/aside&amp;gt;

3. `main.dart` 125번째 라인, 157번째 라인에 있는 에러를 각각 없애보겠습니다. 

    아래에서 메모의 수정 및 삭제를 따로 구현할 것이기 때문에 지금은 코드를 모두 주석처리 해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b82a2e83-02c0-4217-bd24-6200214e89bd/Untitled.png)

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/733b6f3d-b91d-42b2-b0b4-61e530c90434/Untitled.png)

4. `ListTile` 을 클릭시 `DetailPage` 로 이동하도록 하겠습니다. 

    `main.dart` 의 71번째 라인 뒤에 아래 코드스니펫을 복사해 붙여넣어주세요.

    - **[코드스니펫] HomePage 에서 DetailPage 로 이동하는 Navigator**

        ```dart

                                Navigator.push(
                                  context,
                                  MaterialPageRoute(
                                    builder: (_) =&amp;gt; DetailPage(
                                      index: index,
                                    ),
                                  ),
                                );
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aabfe72e-6353-4771-b545-54fd4db6ee19/Untitled.png)

    **메모 개별 조회(Read)** 기능이 구현되었습니다. `ListTile` 을 눌러 상세페이지로 이동해보세요!

    ![Screen Shot 2022-09-13 at 4.41.46 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/20af2c23-98bd-4290-ba7b-55f93ea7be28/Screen_Shot_2022-09-13_at_4.41.46_AM.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) 메모 작성 (Create)&lt;/p&gt;
  &lt;aside&gt;
    `MemoService` 에 메모를 생성(Create)하는 로직을 작성하고, 이를 `HomePage` 에서 이용합니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;먼저 코드스니펫을 복사해 &lt;code&gt;MemoService&lt;/code&gt; 에 &lt;code&gt;Memo&lt;/code&gt; 를 추가하는 함수를 구현해봅시다. &lt;code&gt;memo_service.dart&lt;/code&gt; 의 19번째 라인 맨 뒤에 붙여넣습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] MemoService / createMemo&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
    createMemo({required String content}) {
      Memo memo = Memo(content: content);
      memoList.add(memo);
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      **MemoService**에 `memoList`에 대한 CRUD 기능을 담당하는 함수를 추가할 예정입니다. 그 중 Create를 담당하는 `createMemo` 함수를 생성하였습니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c584b852-baa2-4db3-84a2-cfb725acc092/Untitled.png)

    &amp;lt;aside&amp;gt;
      `String` 자료형의 `content` 변수를 받아 `Memo`를 생성하고, 이를 `memoList` 맨 뒤에 추가하는 로직을 작성했습니다.

    &amp;lt;/aside&amp;gt;

2. `HomePage` 에서 `FloatingActionButton` 을 누를 때, 방금 만든 `createMemo` 함수를 호출해 내용이 비어있는 메모가 추가되도록 하겠습니다.

    아래 코드스니펫을 복사해 `main.dart` 의 87번째 라인 맨 뒤에 붙여넣습니다

    - **[코드스니펫] HomePage / createMemo 호출**

        ```dart

                      memoService.createMemo(content: &amp;#39;&amp;#39;);
                      Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (_) =&amp;gt; DetailPage(
                            index: memoService.memoList.length - 1,
                          ),
                        ),
                      );
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b7306c0c-3194-4004-97c0-3f562b5b79cb/Untitled.png)

    &amp;lt;aside&amp;gt;
      `memoList` 의 맨 뒤에 memo가 추가되었으므로, `memoList.length - 1` 의 인덱스로 해당 memo에 접근합니다.

    &amp;lt;/aside&amp;gt;

3. 저장한 뒤 에뮬레이터에서 `FloatingActionButton` 를 눌러 메모를 추가해봅시다.

    &amp;lt;aside&amp;gt;
      메모를 추가했지만 `HomePage` 에는 아무런 변화가 없습니다.
    `Hot Reload` 를 해보면 비로소 메모가 추가되는 것을 확인할 수 있는데요. 이는 `memoList` 에 memo 가 추가되었지만 화면의 갱신이 일어나지 않았다는 것을 의미합니다.

    &amp;lt;/aside&amp;gt;

4. 이를 해결하기 위해 코드스니펫을 복사해 **MemoService**에 23번째 라인 맨 뒤에 붙여 넣어주세요.
    - **[코드스니펫] notifyListeners**

        ```dart

            notifyListeners(); // Consumer&amp;lt;MemoService&amp;gt;의 builder 부분을 호출해서 화면 새로고침
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/66f3a459-46a5-42d7-b186-247a49975361/Untitled.png)

    &amp;lt;aside&amp;gt;
      **MemoService**에서 `notifyListeners();`를 호출해야 `Consumer&amp;lt;MemoService&amp;gt;`의 `builder` 에 있는 함수를 호출해 화면이 갱신됩니다.

    &amp;lt;/aside&amp;gt;

    이제 에뮬레이터에서 `FloatingActionButton` 를 누를 때마다 `HomePage` 에 빈 메모가 추가되는 것을 확인할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) 메모 수정 (Update)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;먼저 &lt;code&gt;MemoService&lt;/code&gt; 에 메모를 &lt;strong&gt;수정&lt;/strong&gt;하는 로직을 추가하겠습니다. 아래 코드스니펫을 복사해 &lt;code&gt;memo_service.dart&lt;/code&gt; 의 25번째 라인 맨 뒤에 붙여넣습니다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] MemoService / updateMemo&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
    updateMemo({required int index, required String content}) {
      Memo memo = memoList[index];
      memo.content = content;
      notifyListeners();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b88fd71-7b53-40d9-a126-b25730ecb281/Untitled.png)

    &amp;lt;aside&amp;gt;
      `index` 와 `content` 변수를 받아 `memoList` 의 index 번째 memo 의 `content` 를 바꿔주는 로직을 작성했습니다.
    또, `notifyListeners()` 를 호출해 `Consumer&amp;lt;MemoService&amp;gt;` 의 `builder` 부분에 있는 위젯을 다시 그려줍니다.

    &amp;lt;/aside&amp;gt;

2. `DetailPage` 에서 `TextField` 에 텍스트를 입력할 때마다 메모 수정이 일어나도록 하겠습니다. 
아래 코드스니펫을 복사해 `main.dart` 의 174번째 라인을 지우고 붙여넣습니다
    - **[코드스니펫] DetailPage / TextField onChange**

        ```dart
        memoService.updateMemo(index: index, content: value);
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1debffca-b607-4396-b4ca-4fc9af942580/Untitled.png)

    &amp;lt;aside&amp;gt;
      `TextField` 의 값이 변할 때, 즉 타이핑할때마다 `memoService` 의 `updateMemo` 를 호출하도록 했습니다.

    &amp;lt;/aside&amp;gt;

3. `DetailPage` 에서 메모를 작성하고 `HomePage` 로 돌아오면 변경 내역이 잘 반영되어있는 것을 확인할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;5) 메모 삭제 (Delete)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;먼저 &lt;code&gt;MemoService&lt;/code&gt; 에 메모를 &lt;strong&gt;삭제&lt;/strong&gt;하는 로직을 추가하겠습니다. 아래 코드스니펫을 복사해 &lt;code&gt;memo_service.dart&lt;/code&gt; 의 31번째 라인 맨 뒤에 붙여넣습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] MemoService / deleteMemo&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
    deleteMemo({required int index}) {
      memoList.removeAt(index);
      notifyListeners();
    }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/532b94be-5756-4b30-89ab-b22b4be58437/Untitled.png)

    &amp;lt;aside&amp;gt;
      `index` 변수를 받아 `memoList` 의 index 번째 memo 를 삭제하는 로직을 작성했습니다.
    또, `notifyListeners()` 를 호출해 `Consumer&amp;lt;MemoService&amp;gt;` 의 `builder` 부분에 있는 위젯을 다시 그려줍니다.

    &amp;lt;/aside&amp;gt;

2. `DetailPage` 에서 삭제 버튼을 누르면 뜨는 `Dialog` 가 뜨고, 여기서 `확인`을 누르면 삭제가 일어나도록 해봅시다. 아래 코드스니펫을 복사해 `main.dart` 의 142번째 라인을 지우고 붙여넣습니다
    - **[코드스니펫] DetailPage / TextButton onPressed**

        ```dart
        memoService.deleteMemo(index: index);
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6ec414aa-b3a9-4cbb-97b9-14f85dbcca75/Untitled.png)

    &amp;lt;aside&amp;gt;
      삭제 버튼을 누르면 뜨는 `Dialog` 가 뜨고, 여기서 `확인`을 누르면 삭제가 일어나며, 뒤로가기를 두번 (Dialog 닫기 → DetailPage 닫고 HomePage 로 이동) 실행합니다.

    &amp;lt;/aside&amp;gt;

3. `DetailPage` 에서 삭제 버튼을 눌러 구현이 잘 되었는지 확인합니다.&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;6) &lt;strong&gt;Provider&lt;/strong&gt; 사용법 정리&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;전역적으로 사용되는 데이터를 담당할 &lt;strong&gt;서비스&lt;/strong&gt;로 만들고, 해당 데이터에 대한 CRUD를 모두 해당 서비스에서 구현합니다.&lt;/p&gt;
  &lt;aside&gt;
    `ChangeNotifier`를 상속받아야 `notifyListeners();`를 호출하여 데이터를 사용하는 화면을 갱신할 수 있습니다.

  &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  class MemoService extends ChangeNotifier {

    ...

    void deleteMemo({required int index}) {
      memoList.removeAt(index);
      notifyListeners(); // Consumer의 builder 함수를 호출하여 화면 갱신
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Provider&lt;/strong&gt; 패키지를 이용하여 최상단 위젯 &lt;strong&gt;서비스&lt;/strong&gt;를 등록해 줍니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1f4eaae1-5e04-4024-a344-e30677e0faeb/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위젯트리 꼭대기에 있는 &lt;strong&gt;Provider&lt;/strong&gt;로 등록한 클래스에 접근 방법&lt;/p&gt;
  &lt;aside&gt;
    요약

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Consumer&amp;lt;클래스명&amp;gt;&lt;/code&gt; : 클래스 정보 갱신시 화면을 새로고침 해야 할 때 사용&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;context.read&amp;lt;클래스명&amp;gt;&lt;/code&gt; : 1회성으로 클래스 접근할 때 사용 (화면 새로고침이 필요 없을 때)&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Consumer&amp;lt;클래스명&amp;gt;&lt;/code&gt; : 클래스 정보 갱신시 함께 새로고침 할 때 사용&lt;/p&gt;
 &lt;aside&gt;
   **서비스(ChangeNotifier)**에서 `notifyListeners()`를 호출 하는 경우 `Consumer`의 `builder`가 호출되며 자동으로 화면이 갱신됩니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c18aa81a-26d0-4a0f-8fa2-fc01542e0b9e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   HomePage 위젯 내 Consumer 바깥쪽과 안쪽에 각각 print 로직을 추가한 뒤 앱을 재시작하면 `Debug Console`에서 아래와 같은 출력을 볼 수 있습니다.

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2c92d5cd-4456-4196-b14a-43f38fcee486/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 메모의 상태를 업데이트하면 &lt;code&gt;Consumer&lt;/code&gt;의 &lt;code&gt;builder&lt;/code&gt; 부분만 호출되는 것을 볼 수 있습니다. (이 때, &lt;code&gt;HomePage&lt;/code&gt; 의 &lt;code&gt;build&lt;/code&gt; 는 다시 실행되지 않습니다!)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8f58f8c6-9e49-4a48-89a2-730ba9afc71a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;context.read&amp;lt;클래스명&amp;gt;()&lt;/code&gt; : 화면을 새로고침할 필요 없이, 일회성으로 서비스의 변수나 함수에 접근하고 싶은 경우에 사용&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/68216ded-1078-4276-bf7f-1f284f421d66/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;7) 리팩토링&lt;/p&gt;
  &lt;aside&gt;
    `Dialog`를 띄우는 로직이 너무 길어서 보기 힘든 것 같습니다. 해당 로직을 별도의 함수로 분리 시켜보도록 하겠습니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    이와 같이 동작의 변경 없이 코드를 정리하는 과정을 리팩토링(Refactoring)라고 부릅니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;showDeleteDialog&lt;/code&gt; 메소드(함수)로 분리하기&lt;/p&gt;
 &lt;aside&gt;
   코드를 함수로 나누면 보기도 좋고 재활용도 할 수 있습니다.

 &lt;/aside&gt;

&lt;p&gt; 126번째 줄에 &lt;code&gt;showDialog&lt;/code&gt;를 클릭한 뒤 왼쪽에 전구( ) 아이콘을 선택하고, &lt;code&gt;Extract Method&lt;/code&gt;를 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0fa23ad5-36c5-4835-8530-c68ed47ca0f7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 메소드(함수)의 이름을 입력하라고 뜨면 &lt;code&gt;showDeleteDialog&lt;/code&gt;라고 입력한 뒤 엔터를 누르고 저장(&lt;code&gt;Ctrl/Cmd + S&lt;/code&gt;)를 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5b0a583b-2e20-482d-95aa-f4d859aa433a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그러면 아래와 같이 126번째 줄이 생성된 함수를 호출하도록 바뀝니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bd2e5ed2-8d4b-430b-902d-807a9dee3b92/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 153번째 라인으로 내려가보면 &lt;code&gt;showDeleteDialog&lt;/code&gt;라는 함수가 생성되어 있는 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b96b5da8-12e4-41b8-8c44-2100b66f2876/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;153번째 줄을 보면 함수의 반환 타입이 &lt;code&gt;Future&amp;lt;dynamic&amp;gt;&lt;/code&gt; 이라고 되어있는데, 이 부분을 &lt;code&gt;void&lt;/code&gt;로 변경하고, 155번째 줄에 있는 &lt;code&gt;return&lt;/code&gt;을 삭제해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a706eee8-f37e-4265-a067-9b43f64eb654/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   `void`는 함수가 아무것도 반환하지 않는다는 의미이고, 이에 맞추어 `return`을 삭제했습니다.

 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;저장한 뒤, 메모를 하나 삭제해보면 정상적으로 작동하는 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;최종 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;

  import &amp;#39;memo_service.dart&amp;#39;;

  void main() {
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; MemoService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({super.key});

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  // 홈 페이지
  class HomePage extends StatefulWidget {
    const HomePage({super.key});

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;MemoService&amp;gt;(
        builder: (context, memoService, child) {
          // memoService로 부터 memoList 가져오기
          List&amp;lt;Memo&amp;gt; memoList = memoService.memoList;

          return Scaffold(
            appBar: AppBar(
              title: Text(&amp;quot;mymemo&amp;quot;),
            ),
            body: memoList.isEmpty
                ? Center(child: Text(&amp;quot;메모를 작성해 주세요&amp;quot;))
                : ListView.builder(
                    itemCount: memoList.length, // memoList 개수 만큼 보여주기
                    itemBuilder: (context, index) {
                      Memo memo = memoList[index]; // index에 해당하는 memo 가져오기
                      return ListTile(
                        // 메모 고정 아이콘
                        leading: IconButton(
                          icon: Icon(CupertinoIcons.pin),
                          onPressed: () {
                            print(&amp;#39;$memo : pin 클릭 됨&amp;#39;);
                          },
                        ),
                        // 메모 내용 (최대 3줄까지만 보여주도록)
                        title: Text(
                          memo.content,
                          maxLines: 3,
                          overflow: TextOverflow.ellipsis,
                        ),
                        onTap: () {
                          // 아이템 클릭시
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (_) =&amp;gt; DetailPage(
                                index: index,
                              ),
                            ),
                          );
                        },
                      );
                    },
                  ),
            floatingActionButton: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
                memoService.createMemo(content: &amp;#39;&amp;#39;);
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) =&amp;gt; DetailPage(
                      index: memoService.memoList.length - 1,
                    ),
                  ),
                );
              },
            ),
          );
        },
      );
    }
  }

  // 메모 생성 및 수정 페이지
  class DetailPage extends StatelessWidget {
    DetailPage({super.key, required this.index});

    final int index;

    TextEditingController contentController = TextEditingController();

    @override
    Widget build(BuildContext context) {
      MemoService memoService = context.read&amp;lt;MemoService&amp;gt;();
      Memo memo = memoService.memoList[index];

      contentController.text = memo.content;

      return Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              onPressed: () {
                // 삭제 버튼 클릭시
                showDeleteDialog(context, memoService);
              },
              icon: Icon(Icons.delete),
            )
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: TextField(
            controller: contentController,
            decoration: InputDecoration(
              hintText: &amp;quot;메모를 입력하세요&amp;quot;,
              border: InputBorder.none,
            ),
            autofocus: true,
            maxLines: null,
            expands: true,
            keyboardType: TextInputType.multiline,
            onChanged: (value) {
              // 텍스트필드 안의 값이 변할 때
              memoService.updateMemo(index: index, content: value);
            },
          ),
        ),
      );
    }

    void showDeleteDialog(BuildContext context, MemoService memoService) {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(&amp;quot;정말로 삭제하시겠습니까?&amp;quot;),
            actions: [
              // 취소 버튼
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: Text(&amp;quot;취소&amp;quot;),
              ),
              // 확인 버튼
              TextButton(
                onPressed: () {
                  memoService.deleteMemo(index: index);
                  Navigator.pop(context); // 팝업 닫기
                  Navigator.pop(context); // HomePage 로 가기
                },
                child: Text(
                  &amp;quot;확인&amp;quot;,
                  style: TextStyle(color: Colors.pink),
                ),
              ),
            ],
          );
        },
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;memo_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;main.dart&amp;#39;;

  // Memo 데이터의 형식을 정해줍니다. 추후 isPinned, updatedAt 등의 정보도 저장할 수 있습니다.
  class Memo {
    Memo({
      required this.content,
    });

    String content;
  }

  // Memo 데이터는 모두 여기서 관리
  class MemoService extends ChangeNotifier {
    List&amp;lt;Memo&amp;gt; memoList = [
      Memo(content: &amp;#39;장보기 목록: 사과, 양파&amp;#39;), // 더미(dummy) 데이터
      Memo(content: &amp;#39;새 메모&amp;#39;), // 더미(dummy) 데이터
    ];

    createMemo({required String content}) {
      Memo memo = Memo(content: content);
      memoList.add(memo);
      notifyListeners(); // Consumer&amp;lt;MemoService&amp;gt;의 builder 부분을 호출해서 화면 새로고침
    }

    updateMemo({required int index, required String content}) {
      Memo memo = memoList[index];
      memo.content = content;
      notifyListeners();
    }

    deleteMemo({required int index}) {
      memoList.removeAt(index);
      notifyListeners();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;05. shared_preferences 를 이용해 메모 데이터 기기에 저장하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;shared_preferences&lt;/strong&gt; 패키지를 사용하는 이유&lt;/p&gt;
  &lt;aside&gt;
    앱을 재시작 하는 경우, 모든 메모가 사라집니다. **shared_preferences** 패키지를 사용하여 앱을 재시작해도 데이터를 유지하도록 만들어봅시다.

  &lt;/aside&gt;

  &lt;aside&gt;
    앱을 재시작하거나 종료할 때 데이터가 날아가는 이유는 데이터가 **RAM(Random Access Memory)**이라는 휘발성 메모리에 저장되어있기 때문입니다. 데이터를 **RAM**이 아닌 다른 곳에 저장해 앱을 재시작해도 이전 데이터를 유지하는 방법을 알아보겠습니다.

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;기기에 파일로 저장하기&lt;/strong&gt;&lt;br&gt;내용을 파일로 저장해두고 앱을 시작할 때 파일을 읽어오는 방식입니다. (패키지 : &lt;a href=&quot;https://pub.dev/packages/shared_preferences&quot;&gt;shared_preferences&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;기기 데이터베이스에 저장하기&lt;/strong&gt;&lt;br&gt;모든 폰에는 SQLite라는 데이터베이스(데이터 저장 전문 프로그램)가 있습니다. 이 데이터베이스를 이용하여 데이터를 보존할 수 있습니다. 좀 더 복잡한 데이터를 기기에 저장할 수 있습니다. (패키지 : &lt;a href=&quot;https://pub.dev/packages/sqflite&quot;&gt;sqflite&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;다른 컴퓨터(서버)에 저장하기&lt;/strong&gt;&lt;br&gt;인터넷을 통해 다른 컴퓨터에 데이터를 전송하여 저장하는 방식입니다.&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
  여기선 **shared_preferences 패키지**를 이용하여 `1. 기기에 파일로 저장` 방법을 배워보도록 하겠습니다.

&lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) &lt;strong&gt;shared_preferences&lt;/strong&gt; 패키지 설치&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;패키지를 설치해 보도록 하겠습니다. 코드스니펫을 복사해 새 탭에서 열어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://pub.dev/packages/shared_preferences/install&quot;&gt;[코드스니펫] pub.dev shared_preferences&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://pub.dev/packages/shared_preferences/install&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    `flutter pub add shared_preferences` 우측에 아이콘을 눌러 복사해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c859621-73d6-42cb-86eb-99d54bfeac18/Untitled.png)

2. `View` → `Terminal`을 열고 복사한 내용을 붙여 넣은 뒤 엔터를 눌러 실행해 주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9ad5c331-063e-4fce-81d8-0148093ce7b1/Untitled.png)

    `pubspec.yaml`파일을 열어서 40번째 라인에 `shared_preferences`를 확인해 주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c4342608-4cf6-4955-a4c6-c7dd417fc167/Untitled.png)

3. 패키지 설치가 끝나셨다면 우측 상단의 정지 버튼을 눌러 앱을 끈 후

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ed82c5f1-f39f-48a9-a11f-5ea54ca69fa5/Untitled.png)

    `Run without debugging` 을 눌러 앱을 다시 설치해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c40bd256-eaf2-48ff-ba16-2f51c5da6701/Untitled.png)

    앱이 재설치가 된 후에 `shared_preferences` 를 사용할 수 있습니다.&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) &lt;strong&gt;shared_preferences&lt;/strong&gt; 사용법&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;사용 준비&lt;/strong&gt;&lt;br&gt;&lt;code&gt;SharedPreferences&lt;/code&gt;를 이용하려면 먼저 &lt;code&gt;SharedPreferences&lt;/code&gt; 인스턴스를 가져와야합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; // 인스턴스 생성
 SharedPreferences prefs = await SharedPreferences.getInstance();&lt;/code&gt;&lt;/pre&gt;
 &lt;aside&gt;
   기기에 저장된 파일을 읽어오는데, 다소 시간이 걸리는데 완료될 때 까지 기다리도록 앞에 `await` 이라는 키워드를 붙여줍니다. `await` 키워드에 대한 상세한 설명은 다음 강의 때 상세히 설명드리도록 하겠습니다.

 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;값 저장하기&lt;/strong&gt;&lt;br&gt;SharedPreferences는 데이터를 Key와 Value로 구성된 Map 형태로 데이터를 저장합니다. 저장시 사용되는 &lt;code&gt;Key&lt;/code&gt;는 &lt;strong&gt;원하는 이름&lt;/strong&gt;으로 정하시면 됩니다.&lt;/p&gt;
 &lt;aside&gt;
   예를들어, 유저 이름을 `username`이라는 이름의 `Key`에 `String` 타입으로 저장한다면 아래와 같습니다.

 &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt; prefs.setString(&amp;quot;username&amp;quot;, &amp;quot;John Doe&amp;quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;값 불러오기&lt;/strong&gt;&lt;br&gt;저장할 때 사용한 &lt;code&gt;username&lt;/code&gt; 이라는 &lt;code&gt;Key&lt;/code&gt;를 이용해 다시 값을 가져올 수 있습니다. 저장된 값이 없는 경우 &lt;code&gt;null&lt;/code&gt;을 반환하는 점 유의해 주세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; String? value = prefs.getString(&amp;quot;username&amp;quot;); &lt;/code&gt;&lt;/pre&gt;
 &lt;aside&gt;
   SharedPreferences에는 **`String`, `List&lt;String&gt;`, `double`, `int`, `bool`** 타입을 저장 할 수 있습니다. 좀 더 자세한 사항은 공식 문서를 참고해 주세요.

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://pub.dev/documentation/shared_preferences/latest/shared_preferences/SharedPreferences-class.html&quot;&gt;[코드스니펫] pub.dev shared_preferences 공식 문서&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  [https://pub.dev/documentation/shared_preferences/latest/shared_preferences/SharedPreferences-class.html](https://pub.dev/documentation/shared_preferences/latest/shared_preferences/SharedPreferences-class.html)&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 기기에 &lt;code&gt;memoList&lt;/code&gt; 데이터 저장하기&lt;/p&gt;
  &lt;aside&gt;
    **shared_preferences** 패키지에는 `**String**` / `**List&lt;String&gt;`** / `**int**` / `**double**` / **`bool`** 타입의 데이터만 저장할 수 있기 때문에 `memoList` 는 **shared_preferences** 에 그대로 저장할 수 없습니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    하지만 `memoList`를 `String` 형태로 변환하면 저장할 수 있습니다. 아래와 같은 프로세스를 따릅니다.

&lt;p&gt;  1) &lt;code&gt;memoList&lt;/code&gt; 의 &lt;code&gt;Memo&lt;/code&gt; 를 먼저 &lt;code&gt;Map(Json)&lt;/code&gt; 형태로 변환한 후&lt;br&gt;  2) 변환된 &lt;code&gt;Map&lt;/code&gt; 을 담은 &lt;code&gt;List&lt;/code&gt; 전체를 &lt;code&gt;String&lt;/code&gt; 형태로 변환해&lt;/p&gt;
&lt;p&gt;  3) &lt;strong&gt;shared_preferences&lt;/strong&gt; 에 저장합니다&lt;/p&gt;
&lt;p&gt;  (데이터를 불러올 때는 역순으로 진행합니다)&lt;/p&gt;
  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;먼저 전역으로 &lt;code&gt;prefs&lt;/code&gt;라는 변수를 선언해 코드 어디서든 &lt;code&gt;SharedPreferences&lt;/code&gt; 객체를 사용할 수 있도록 합니다. 아래 코드스니펫을 복사해 &lt;code&gt;main.dart&lt;/code&gt; 의 7번째 줄을 지우고 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 전역 변수 prefs 선언 / main 함수 수정&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  late SharedPreferences prefs;

  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    prefs = await SharedPreferences.getInstance();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/67734020-ec2c-41f2-990d-419ef1f0cc28/Untitled.png)

    7번째 라인에 `SharedPreferences`을 클릭한 뒤 Quick Fix(`Ctrl/Cmd + .`)를 누른 뒤 `Import library…` 를 선택해주세요. `shared_preferences` 패키지를 import 하지 않아 발생하는 문제입니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5fb816b4-9425-4df6-828b-a8f9e60b5305/Untitled.png)

    아래와 같이 import 가 되면 에러가 해결됩니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a31272db-f17e-48b6-abc0-e2e027d5851b/Untitled.png)

2. `Memo` 객체를 `Map` 자료형으로 바꿔주는 함수 **toJson**, `Map` 자료형에서 다시 `Memo` 객체를 복원하는 함수 **fromJson** 을 `Memo` 클래스 내에 작성해줍니다. 아래 코드스니펫을 복사해 `memo_service.dart` 의 11번째 라인 맨 마지막에 붙여넣어주세요.
    - **[코드스니펫] Memo toJson, fromJson**

        ```dart

          Map toJson() {
            return {&amp;#39;content&amp;#39;: content};
          }

          factory Memo.fromJson(json) {
            return Memo(content: json[&amp;#39;content&amp;#39;]);
          }
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c8132838-be7a-42de-8395-f5e01ff2cbfc/Untitled.png)

3. **MemoService** 에 `memoList` 를 기기에 저장하는 함수 **saveMemoList** 와, 저장된 `memoList` 를 다시 복원하는 함수 **loadMemoList** 를 추가합니다. 아래 코드스니펫을 복사해 `memo_service.dart` 의 44번째 라인 뒤에 붙여넣어주세요.
    - **[코드스니펫] saveMemoList / loadMemoList**

        ```dart

          saveMemoList() {
            List memoJsonList = memoList.map((memo) =&amp;gt; memo.toJson()).toList();
            // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

            String jsonString = jsonEncode(memoJsonList);
            // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

            prefs.setString(&amp;#39;memoList&amp;#39;, jsonString);
          }

          loadMemoList() {
            String? jsonString = prefs.getString(&amp;#39;memoList&amp;#39;);
            // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

            if (jsonString == null) return; // null 이면 로드하지 않음

            List memoJsonList = jsonDecode(jsonString);
            // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

            memoList = memoJsonList.map((json) =&amp;gt; Memo.fromJson(json)).toList();
          }
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e500d5a7-5c03-45c9-a223-9dcaae341ef9/Untitled.png)

    50번째 라인에 에러가 발생한 부분에서 Quick Fix(`Ctrl/Cmd + .`)를 누른 뒤 `Import library &amp;#39;dart:convert&amp;#39;` 를 눌러 에러를 해결해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c9fe2dbe-b948-4bca-bc8c-edfbf1af9b8b/Untitled.png)

    &amp;lt;aside&amp;gt;
      각 과정을 거치며 데이터가 어떻게 변하는지 주석으로 작성해뒀습니다. 대략적인 흐름은 아래와 같습니다.

    1) `saveMemoList` 

    `**List&amp;lt;Memo&amp;gt;**` ⇒ (toJson) ⇒  `**List&amp;lt;Map&amp;gt;**` ⇒ (jsonEncode) ⇒  **`String`**

    2) `loadMemoList`

    `**String**` ⇒ (jsonDecode) ⇒ `**List&amp;lt;Map&amp;gt;**` ⇒ (fromJson) ⇒ `**List&amp;lt;Memo&amp;gt;**`

    &amp;lt;/aside&amp;gt;

4. 메모를 생성, 변경, 삭제할 때마다 메모를 저장하도록 합니다. 아래 코드스니펫을 복사해 `memo_service.dart` 의 `MemoService` 클래스 함수들에 있는 `notifyListeners()` 뒤에 **모두** 붙여넣어줍니다.
    - **[코드스니펫] memoList 기기에 저장**

        ```dart

            saveMemoList();
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/49bda047-bee9-4cd3-9919-c6d3e7e98da2/Untitled.png)

5. **MemoService** 를 시작할 때 저장된 메모를 불러와서 `memoList` 변수에 담아주도록 생성자를 수정해줍니다. 아래 코드스니펫을 복사해 `memo_service.dart` 의 25번째 줄 맨 뒤에 붙여넣어주세요.
    - **[코드스니펫] memoList 불러오기**

        ```dart

          MemoService() {
            loadMemoList();
          }

        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d0034163-f49b-4ab4-9769-27c1c4a932ba/Untitled.png)

6. 자 이제 메모를 생성하고, 우측 상단의 `Restart` 버튼을 눌러주세요. 앱을 재부팅하는 것과 동일한 동작입니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a3804f24-5072-4989-9dae-d34b77c9b50d/Untitled.png)

    메모가 삭제되지 않고 잘 남아있는 것을 확인할 수 있습니다. 이는 메모 데이터가 `shared_preferences` 를 통해 기기에 저장되고, 불러와지기 때문입니다. &lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;
  import &amp;#39;package:shared_preferences/shared_preferences.dart&amp;#39;;

  import &amp;#39;memo_service.dart&amp;#39;;

  late SharedPreferences prefs;

  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    prefs = await SharedPreferences.getInstance();
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; MemoService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({super.key});

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  // 홈 페이지
  class HomePage extends StatefulWidget {
    const HomePage({super.key});

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;MemoService&amp;gt;(
        builder: (context, memoService, child) {
          // memoService로 부터 memoList 가져오기
          List&amp;lt;Memo&amp;gt; memoList = memoService.memoList;

          return Scaffold(
            appBar: AppBar(
              title: Text(&amp;quot;mymemo&amp;quot;),
            ),
            body: memoList.isEmpty
                ? Center(child: Text(&amp;quot;메모를 작성해 주세요&amp;quot;))
                : ListView.builder(
                    itemCount: memoList.length, // memoList 개수 만큼 보여주기
                    itemBuilder: (context, index) {
                      Memo memo = memoList[index]; // index에 해당하는 memo 가져오기
                      return ListTile(
                        // 메모 고정 아이콘
                        leading: IconButton(
                          icon: Icon(CupertinoIcons.pin),
                          onPressed: () {
                            print(&amp;#39;$memo : pin 클릭 됨&amp;#39;);
                          },
                        ),
                        // 메모 내용 (최대 3줄까지만 보여주도록)
                        title: Text(
                          memo.content,
                          maxLines: 3,
                          overflow: TextOverflow.ellipsis,
                        ),
                        onTap: () {
                          // 아이템 클릭시
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (_) =&amp;gt; DetailPage(
                                index: index,
                              ),
                            ),
                          );
                        },
                      );
                    },
                  ),
            floatingActionButton: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
                memoService.createMemo(content: &amp;#39;&amp;#39;);
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) =&amp;gt; DetailPage(
                      index: memoService.memoList.length - 1,
                    ),
                  ),
                );
              },
            ),
          );
        },
      );
    }
  }

  // 메모 생성 및 수정 페이지
  class DetailPage extends StatelessWidget {
    DetailPage({super.key, required this.index});

    final int index;

    TextEditingController contentController = TextEditingController();

    @override
    Widget build(BuildContext context) {
      MemoService memoService = context.read&amp;lt;MemoService&amp;gt;();
      Memo memo = memoService.memoList[index];

      contentController.text = memo.content;

      return Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              onPressed: () {
                // 삭제 버튼 클릭시
                showDeleteDialog(context, memoService);
              },
              icon: Icon(Icons.delete),
            )
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: TextField(
            controller: contentController,
            decoration: InputDecoration(
              hintText: &amp;quot;메모를 입력하세요&amp;quot;,
              border: InputBorder.none,
            ),
            autofocus: true,
            maxLines: null,
            expands: true,
            keyboardType: TextInputType.multiline,
            onChanged: (value) {
              // 텍스트필드 안의 값이 변할 때
              memoService.updateMemo(index: index, content: value);
            },
          ),
        ),
      );
    }

    void showDeleteDialog(BuildContext context, MemoService memoService) {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(&amp;quot;정말로 삭제하시겠습니까?&amp;quot;),
            actions: [
              // 취소 버튼
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: Text(&amp;quot;취소&amp;quot;),
              ),
              // 확인 버튼
              TextButton(
                onPressed: () {
                  memoService.deleteMemo(index: index);
                  Navigator.pop(context); // 팝업 닫기
                  Navigator.pop(context); // HomePage 로 가기
                },
                child: Text(
                  &amp;quot;확인&amp;quot;,
                  style: TextStyle(color: Colors.pink),
                ),
              ),
            ],
          );
        },
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;memo_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;dart:convert&amp;#39;;

  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;main.dart&amp;#39;;

  // Memo 데이터의 형식을 정해줍니다. 추후 isPinned, updatedAt 등의 정보도 저장할 수 있습니다.
  class Memo {
    Memo({
      required this.content,
    });

    String content;

    Map toJson() {
      return {&amp;#39;content&amp;#39;: content};
    }

    factory Memo.fromJson(json) {
      return Memo(content: json[&amp;#39;content&amp;#39;]);
    }
  }

  // Memo 데이터는 모두 여기서 관리
  class MemoService extends ChangeNotifier {
    MemoService() {
      loadMemoList();
    }

    List&amp;lt;Memo&amp;gt; memoList = [
      Memo(content: &amp;#39;장보기 목록: 사과, 양파&amp;#39;), // 더미(dummy) 데이터
      Memo(content: &amp;#39;새 메모&amp;#39;), // 더미(dummy) 데이터
    ];

    createMemo({required String content}) {
      Memo memo = Memo(content: content);
      memoList.add(memo);
      notifyListeners(); // Consumer&amp;lt;MemoService&amp;gt;의 builder 부분을 호출해서 화면 새로고침
      saveMemoList();
    }

    updateMemo({required int index, required String content}) {
      Memo memo = memoList[index];
      memo.content = content;
      notifyListeners();
      saveMemoList();
    }

    deleteMemo({required int index}) {
      memoList.removeAt(index);
      notifyListeners();
      saveMemoList();
    }

    saveMemoList() {
      List memoJsonList = memoList.map((memo) =&amp;gt; memo.toJson()).toList();
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      String jsonString = jsonEncode(memoJsonList);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      prefs.setString(&amp;#39;memoList&amp;#39;, jsonString);
    }

    loadMemoList() {
      String? jsonString = prefs.getString(&amp;#39;memoList&amp;#39;);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      if (jsonString == null) return; // null 이면 로드하지 않음

      List memoJsonList = jsonDecode(jsonString);
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      memoList = memoJsonList.map((json) =&amp;gt; Memo.fromJson(json)).toList();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;06. 숙제 - 마이메모 추가 기능 구현&lt;/h2&gt;
&lt;aside&gt;
  마이메모 앱에 기능들을 추가하며 CRUD, 상태관리 개념에 익숙해져봅시다.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 메모 핀 기능 구현&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 구현 목표&lt;/p&gt;
  &lt;aside&gt;
    메모 핀 기능 구현

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;좌측의 핀 아이콘을 누르면 아이템이 상단에 고정됩니다&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b36ca8b3-057d-4a3e-a9f2-b9445af2e266/Simulator_Screen_Recording_-_iPhone_14_-_2022-09-26_at_12.57.06.mp4&quot;&gt;Simulator Screen Recording - iPhone 14 - 2022-09-26 at 12.57.06.mp4&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9dd98365-68f9-4128-81f0-d727cc37adca/Simulator_Screen_Shot_-_iPhone_14_-_2022-09-26_at_12.52.24.png&quot; alt=&quot;Simulator Screen Shot - iPhone 14 - 2022-09-26 at 12.52.24.png&quot;&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 숙제 답안&lt;/p&gt;
  &lt;aside&gt;
    노란색 하이라이트로 변경 사항을 표시해뒀습니다

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;
  import &amp;#39;package:shared_preferences/shared_preferences.dart&amp;#39;;

  import &amp;#39;memo_service.dart&amp;#39;;

  late SharedPreferences prefs;

  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    prefs = await SharedPreferences.getInstance();
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; MemoService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({super.key});

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  // 홈 페이지
  class HomePage extends StatefulWidget {
    const HomePage({super.key});

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;MemoService&amp;gt;(
        builder: (context, memoService, child) {
          // memoService로 부터 memoList 가져오기
          List&amp;lt;Memo&amp;gt; memoList = memoService.memoList;

          return Scaffold(
            appBar: AppBar(
              title: Text(&amp;quot;mymemo&amp;quot;),
            ),
            body: memoList.isEmpty
                ? Center(child: Text(&amp;quot;메모를 작성해 주세요&amp;quot;))
                : ListView.builder(
                    itemCount: memoList.length, // memoList 개수 만큼 보여주기
                    itemBuilder: (context, index) {
                      Memo memo = memoList[index]; // index에 해당하는 memo 가져오기
                      return ListTile(
                        // 메모 고정 아이콘
                        leading: IconButton(
                          icon: Icon(memo.isPinned
                              ? CupertinoIcons.pin_fill
                              : CupertinoIcons.pin),
                          onPressed: () {
                            memoService.updatePinMemo(index: index);
                          },
                        ),
                        // 메모 내용 (최대 3줄까지만 보여주도록)
                        title: Text(
                          memo.content,
                          maxLines: 3,
                          overflow: TextOverflow.ellipsis,
                        ),
                        onTap: () {
                          // 아이템 클릭시
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (_) =&amp;gt; DetailPage(
                                index: index,
                              ),
                            ),
                          );
                        },
                      );
                    },
                  ),
            floatingActionButton: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
                memoService.createMemo(content: &amp;#39;&amp;#39;);
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) =&amp;gt; DetailPage(
                      index: memoService.memoList.length - 1,
                    ),
                  ),
                );
              },
            ),
          );
        },
      );
    }
  }

  // 메모 생성 및 수정 페이지
  class DetailPage extends StatelessWidget {
    DetailPage({super.key, required this.index});

    final int index;

    TextEditingController contentController = TextEditingController();

    @override
    Widget build(BuildContext context) {
      MemoService memoService = context.read&amp;lt;MemoService&amp;gt;();
      Memo memo = memoService.memoList[index];

      contentController.text = memo.content;

      return Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              onPressed: () {
                // 삭제 버튼 클릭시
                showDeleteDialog(context, memoService);
              },
              icon: Icon(Icons.delete),
            )
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: TextField(
            controller: contentController,
            decoration: InputDecoration(
              hintText: &amp;quot;메모를 입력하세요&amp;quot;,
              border: InputBorder.none,
            ),
            autofocus: true,
            maxLines: null,
            expands: true,
            keyboardType: TextInputType.multiline,
            onChanged: (value) {
              // 텍스트필드 안의 값이 변할 때
              memoService.updateMemo(index: index, content: value);
            },
          ),
        ),
      );
    }

    void showDeleteDialog(BuildContext context, MemoService memoService) {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(&amp;quot;정말로 삭제하시겠습니까?&amp;quot;),
            actions: [
              // 취소 버튼
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: Text(&amp;quot;취소&amp;quot;),
              ),
              // 확인 버튼
              TextButton(
                onPressed: () {
                  memoService.deleteMemo(index: index);
                  Navigator.pop(context); // 팝업 닫기
                  Navigator.pop(context); // HomePage 로 가기
                },
                child: Text(
                  &amp;quot;확인&amp;quot;,
                  style: TextStyle(color: Colors.pink),
                ),
              ),
            ],
          );
        },
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;memo_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;dart:convert&amp;#39;;

  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;main.dart&amp;#39;;

  // Memo 데이터의 형식을 정해줍니다. 추후 isPinned, updatedAt 등의 정보도 저장할 수 있습니다.
  class Memo {
    Memo({
      required this.content,
      this.isPinned = false,
    });

    String content;
    bool isPinned;

    Map toJson() {
      return {
        &amp;#39;content&amp;#39;: content,
        &amp;#39;isPinned&amp;#39;: isPinned,
      };
    }

    factory Memo.fromJson(json) {
      return Memo(
        content: json[&amp;#39;content&amp;#39;],
        isPinned: json[&amp;#39;isPinned&amp;#39;] ?? false,
      );
    }
  }

  // Memo 데이터는 모두 여기서 관리
  class MemoService extends ChangeNotifier {
    MemoService() {
      loadMemoList();
    }

    List&amp;lt;Memo&amp;gt; memoList = [
      Memo(content: &amp;#39;장보기 목록: 사과, 양파&amp;#39;), // 더미(dummy) 데이터
      Memo(content: &amp;#39;새 메모&amp;#39;), // 더미(dummy) 데이터
    ];

    createMemo({required String content}) {
      Memo memo = Memo(content: content);
      memoList.add(memo);
      notifyListeners(); // Consumer&amp;lt;MemoService&amp;gt;의 builder 부분을 호출해서 화면 새로고침
      saveMemoList();
    }

    updateMemo({required int index, required String content}) {
      Memo memo = memoList[index];
      memo.content = content;
      notifyListeners();
      saveMemoList();
    }

    updatePinMemo({required int index}) {
      Memo memo = memoList[index];
      memo.isPinned = !memo.isPinned;
      memoList = [
        ...memoList.where((element) =&amp;gt; element.isPinned),
        ...memoList.where((element) =&amp;gt; !element.isPinned)
      ];
      notifyListeners();
      saveMemoList();
    }

    deleteMemo({required int index}) {
      memoList.removeAt(index);
      notifyListeners();
      saveMemoList();
    }

    saveMemoList() {
      List memoJsonList = memoList.map((memo) =&amp;gt; memo.toJson()).toList();
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      String jsonString = jsonEncode(memoJsonList);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      prefs.setString(&amp;#39;memoList&amp;#39;, jsonString);
    }

    loadMemoList() {
      String? jsonString = prefs.getString(&amp;#39;memoList&amp;#39;);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      if (jsonString == null) return; // null 이면 로드하지 않음

      List memoJsonList = jsonDecode(jsonString);
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      memoList = memoJsonList.map((json) =&amp;gt; Memo.fromJson(json)).toList();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 메모 수정 시간 저장&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 구현 목표&lt;/p&gt;
  &lt;aside&gt;
    메모 수정 시간 저장 및 표시

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;메모를 생성, 수정할 때마다 해당 시간을 저장하고, 이를 목록 화면 우측에 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9201ffa9-207f-4184-bfcf-cad28799ee82/Untitled.mp4&quot;&gt;Untitled&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5592137d-6e26-47c8-8207-1b1e22f77aea/Simulator_Screen_Shot_-_iPhone_14_-_2022-09-26_at_13.14.20.png&quot; alt=&quot;Simulator Screen Shot - iPhone 14 - 2022-09-26 at 13.14.20.png&quot;&gt;&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
  Memo 클래스에 새로운 요소 (Property)를 추가할 때, 기기에 저장되는 데이터를 load 해오는 부분에서 null 값을 처리하는 로직을 유의해주세요.

&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 숙제 답안&lt;/p&gt;
  &lt;aside&gt;
    노란색 하이라이트로 변경 사항을 표시해뒀습니다

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;
  import &amp;#39;package:shared_preferences/shared_preferences.dart&amp;#39;;

  import &amp;#39;memo_service.dart&amp;#39;;

  late SharedPreferences prefs;

  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    prefs = await SharedPreferences.getInstance();
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; MemoService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({super.key});

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  // 홈 페이지
  class HomePage extends StatefulWidget {
    const HomePage({super.key});

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;MemoService&amp;gt;(
        builder: (context, memoService, child) {
          // memoService로 부터 memoList 가져오기
          List&amp;lt;Memo&amp;gt; memoList = memoService.memoList;

          return Scaffold(
            appBar: AppBar(
              title: Text(&amp;quot;mymemo&amp;quot;),
            ),
            body: memoList.isEmpty
                ? Center(child: Text(&amp;quot;메모를 작성해 주세요&amp;quot;))
                : ListView.builder(
                    itemCount: memoList.length, // memoList 개수 만큼 보여주기
                    itemBuilder: (context, index) {
                      Memo memo = memoList[index]; // index에 해당하는 memo 가져오기
                      return ListTile(
                        // 메모 고정 아이콘
                        leading: IconButton(
                          icon: Icon(memo.isPinned
                              ? CupertinoIcons.pin_fill
                              : CupertinoIcons.pin),
                          onPressed: () {
                            memoService.updatePinMemo(index: index);
                          },
                        ),
                        // 메모 내용 (최대 3줄까지만 보여주도록)
                        title: Text(
                          memo.content,
                          maxLines: 3,
                          overflow: TextOverflow.ellipsis,
                        ),
                        trailing: Text(memo.updatedAt == null
                            ? &amp;quot;&amp;quot;
                            : memo.updatedAt.toString().substring(0, 19)),
                        onTap: () {
                          // 아이템 클릭시
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (_) =&amp;gt; DetailPage(
                                index: index,
                              ),
                            ),
                          );
                        },
                      );
                    },
                  ),
            floatingActionButton: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () {
                // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
                memoService.createMemo(content: &amp;#39;&amp;#39;);
                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) =&amp;gt; DetailPage(
                      index: memoService.memoList.length - 1,
                    ),
                  ),
                );
              },
            ),
          );
        },
      );
    }
  }

  // 메모 생성 및 수정 페이지
  class DetailPage extends StatelessWidget {
    DetailPage({super.key, required this.index});

    final int index;

    TextEditingController contentController = TextEditingController();

    @override
    Widget build(BuildContext context) {
      MemoService memoService = context.read&amp;lt;MemoService&amp;gt;();
      Memo memo = memoService.memoList[index];

      contentController.text = memo.content;

      return Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              onPressed: () {
                // 삭제 버튼 클릭시
                showDeleteDialog(context, memoService);
              },
              icon: Icon(Icons.delete),
            )
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: TextField(
            controller: contentController,
            decoration: InputDecoration(
              hintText: &amp;quot;메모를 입력하세요&amp;quot;,
              border: InputBorder.none,
            ),
            autofocus: true,
            maxLines: null,
            expands: true,
            keyboardType: TextInputType.multiline,
            onChanged: (value) {
              // 텍스트필드 안의 값이 변할 때
              memoService.updateMemo(index: index, content: value);
            },
          ),
        ),
      );
    }

    void showDeleteDialog(BuildContext context, MemoService memoService) {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(&amp;quot;정말로 삭제하시겠습니까?&amp;quot;),
            actions: [
              // 취소 버튼
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: Text(&amp;quot;취소&amp;quot;),
              ),
              // 확인 버튼
              TextButton(
                onPressed: () {
                  memoService.deleteMemo(index: index);
                  Navigator.pop(context); // 팝업 닫기
                  Navigator.pop(context); // HomePage 로 가기
                },
                child: Text(
                  &amp;quot;확인&amp;quot;,
                  style: TextStyle(color: Colors.pink),
                ),
              ),
            ],
          );
        },
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;memo_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;dart:convert&amp;#39;;

  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;main.dart&amp;#39;;

  // Memo 데이터의 형식을 정해줍니다. 추후 isPinned, updatedAt 등의 정보도 저장할 수 있습니다.
  class Memo {
    Memo({
      required this.content,
      this.isPinned = false,
      this.updatedAt,
    });

    String content;
    bool isPinned;
    DateTime? updatedAt;

    Map toJson() {
      return {
        &amp;#39;content&amp;#39;: content,
        &amp;#39;isPinned&amp;#39;: isPinned,
        &amp;#39;updatedAt&amp;#39;: updatedAt?.toIso8601String(),
      };
    }

    factory Memo.fromJson(json) {
      return Memo(
        content: json[&amp;#39;content&amp;#39;],
        isPinned: json[&amp;#39;isPinned&amp;#39;] ?? false,
        updatedAt:
            json[&amp;#39;updatedAt&amp;#39;] == null ? null : DateTime.parse(json[&amp;#39;updatedAt&amp;#39;]),
      );
    }
  }

  // Memo 데이터는 모두 여기서 관리
  class MemoService extends ChangeNotifier {
    MemoService() {
      loadMemoList();
    }

    List&amp;lt;Memo&amp;gt; memoList = [
      Memo(content: &amp;#39;장보기 목록: 사과, 양파&amp;#39;), // 더미(dummy) 데이터
      Memo(content: &amp;#39;새 메모&amp;#39;), // 더미(dummy) 데이터
    ];

    createMemo({required String content}) {
      Memo memo = Memo(content: content, updatedAt: DateTime.now());
      memoList.add(memo);
      notifyListeners(); // Consumer&amp;lt;MemoService&amp;gt;의 builder 부분을 호출해서 화면 새로고침
      saveMemoList();
    }

    updateMemo({required int index, required String content}) {
      Memo memo = memoList[index];
      memo.content = content;
      memo.updatedAt = DateTime.now();
      notifyListeners();
      saveMemoList();
    }

    updatePinMemo({required int index}) {
      Memo memo = memoList[index];
      memo.isPinned = !memo.isPinned;
      memoList = [
        ...memoList.where((element) =&amp;gt; element.isPinned),
        ...memoList.where((element) =&amp;gt; !element.isPinned)
      ];
      notifyListeners();
      saveMemoList();
    }

    deleteMemo({required int index}) {
      memoList.removeAt(index);
      notifyListeners();
      saveMemoList();
    }

    saveMemoList() {
      List memoJsonList = memoList.map((memo) =&amp;gt; memo.toJson()).toList();
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      String jsonString = jsonEncode(memoJsonList);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      prefs.setString(&amp;#39;memoList&amp;#39;, jsonString);
    }

    loadMemoList() {
      String? jsonString = prefs.getString(&amp;#39;memoList&amp;#39;);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      if (jsonString == null) return; // null 이면 로드하지 않음

      List memoJsonList = jsonDecode(jsonString);
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      memoList = memoJsonList.map((json) =&amp;gt; Memo.fromJson(json)).toList();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 빈 메모 삭제&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 구현 목표&lt;/p&gt;
  &lt;aside&gt;
    **DetailPage** 에서 나올 때, 메모가 비어있으면 삭제

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;메모를 새로 만들 때, 내용을 추가하지 않은 채로 홈화면으로 돌아오면 해당 메모를 삭제합니다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;기존 메모의 내용을 모두 지우고 홈화면으로 돌아오면 해당 메모를 삭제합니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ae38f40e-ca2c-4369-bb31-417f5887d419/Simulator_Screen_Recording_-_iPhone_14_-_2022-09-26_at_16.37.34.mp4&quot;&gt;Simulator Screen Recording - iPhone 14 - 2022-09-26 at 16.37.34.mp4&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 숙제 답안&lt;/p&gt;
  &lt;aside&gt;
    노란색 하이라이트로 변경 사항을 표시해뒀습니다

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;
  import &amp;#39;package:provider/provider.dart&amp;#39;;
  import &amp;#39;package:shared_preferences/shared_preferences.dart&amp;#39;;

  import &amp;#39;memo_service.dart&amp;#39;;

  late SharedPreferences prefs;

  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    prefs = await SharedPreferences.getInstance();
    runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (context) =&amp;gt; MemoService()),
        ],
        child: const MyApp(),
      ),
    );
  }

  class MyApp extends StatelessWidget {
    const MyApp({super.key});

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  // 홈 페이지
  class HomePage extends StatefulWidget {
    const HomePage({super.key});

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    @override
    Widget build(BuildContext context) {
      return Consumer&amp;lt;MemoService&amp;gt;(
        builder: (context, memoService, child) {
          // memoService로 부터 memoList 가져오기
          List&amp;lt;Memo&amp;gt; memoList = memoService.memoList;

          return Scaffold(
            appBar: AppBar(
              title: Text(&amp;quot;mymemo&amp;quot;),
            ),
            body: memoList.isEmpty
                ? Center(child: Text(&amp;quot;메모를 작성해 주세요&amp;quot;))
                : ListView.builder(
                    itemCount: memoList.length, // memoList 개수 만큼 보여주기
                    itemBuilder: (context, index) {
                      Memo memo = memoList[index]; // index에 해당하는 memo 가져오기
                      return ListTile(
                        // 메모 고정 아이콘
                        leading: IconButton(
                          icon: Icon(memo.isPinned
                              ? CupertinoIcons.pin_fill
                              : CupertinoIcons.pin),
                          onPressed: () {
                            memoService.updatePinMemo(index: index);
                          },
                        ),
                        // 메모 내용 (최대 3줄까지만 보여주도록)
                        title: Text(
                          memo.content,
                          maxLines: 3,
                          overflow: TextOverflow.ellipsis,
                        ),
                        trailing: Text(memo.updatedAt == null
                            ? &amp;quot;&amp;quot;
                            : memo.updatedAt.toString().substring(0, 19)),
                        onTap: () async {
                          // 아이템 클릭시
                          await Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (_) =&amp;gt; DetailPage(
                                index: index,
                              ),
                            ),
                          );
                          if (memo.content.isEmpty) {
                            memoService.deleteMemo(index: index);
                          }
                        },
                      );
                    },
                  ),
            floatingActionButton: FloatingActionButton(
              child: Icon(Icons.add),
              onPressed: () async {
                // + 버튼 클릭시 메모 생성 및 수정 페이지로 이동
                memoService.createMemo(content: &amp;#39;&amp;#39;);
                await Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (_) =&amp;gt; DetailPage(
                      index: memoService.memoList.length - 1,
                    ),
                  ),
                );
                if (memoList[memoService.memoList.length - 1].content.isEmpty) {
                  memoService.deleteMemo(index: memoList.length - 1);
                }
              },
            ),
          );
        },
      );
    }
  }

  // 메모 생성 및 수정 페이지
  class DetailPage extends StatelessWidget {
    DetailPage({super.key, required this.index});

    final int index;

    TextEditingController contentController = TextEditingController();

    @override
    Widget build(BuildContext context) {
      MemoService memoService = context.read&amp;lt;MemoService&amp;gt;();
      Memo memo = memoService.memoList[index];

      contentController.text = memo.content;

      return Scaffold(
        appBar: AppBar(
          actions: [
            IconButton(
              onPressed: () {
                // 삭제 버튼 클릭시
                showDeleteDialog(context, memoService);
              },
              icon: Icon(Icons.delete),
            )
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(16),
          child: TextField(
            controller: contentController,
            decoration: InputDecoration(
              hintText: &amp;quot;메모를 입력하세요&amp;quot;,
              border: InputBorder.none,
            ),
            autofocus: true,
            maxLines: null,
            expands: true,
            keyboardType: TextInputType.multiline,
            onChanged: (value) {
              // 텍스트필드 안의 값이 변할 때
              memoService.updateMemo(index: index, content: value);
            },
          ),
        ),
      );
    }

    void showDeleteDialog(BuildContext context, MemoService memoService) {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Text(&amp;quot;정말로 삭제하시겠습니까?&amp;quot;),
            actions: [
              // 취소 버튼
              TextButton(
                onPressed: () {
                  Navigator.pop(context);
                },
                child: Text(&amp;quot;취소&amp;quot;),
              ),
              // 확인 버튼
              TextButton(
                onPressed: () {
                  memoService.deleteMemo(index: index);
                  Navigator.pop(context); // 팝업 닫기
                  Navigator.pop(context); // HomePage 로 가기
                },
                child: Text(
                  &amp;quot;확인&amp;quot;,
                  style: TextStyle(color: Colors.pink),
                ),
              ),
            ],
          );
        },
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;memo_service.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;dart:convert&amp;#39;;

  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;main.dart&amp;#39;;

  // Memo 데이터의 형식을 정해줍니다. 추후 isPinned, updatedAt 등의 정보도 저장할 수 있습니다.
  class Memo {
    Memo({
      required this.content,
      this.isPinned = false,
      this.updatedAt,
    });

    String content;
    bool isPinned;
    DateTime? updatedAt;

    Map toJson() {
      return {
        &amp;#39;content&amp;#39;: content,
        &amp;#39;isPinned&amp;#39;: isPinned,
        &amp;#39;updatedAt&amp;#39;: updatedAt?.toIso8601String(),
      };
    }

    factory Memo.fromJson(json) {
      return Memo(
        content: json[&amp;#39;content&amp;#39;],
        isPinned: json[&amp;#39;isPinned&amp;#39;] ?? false,
        updatedAt:
            json[&amp;#39;updatedAt&amp;#39;] == null ? null : DateTime.parse(json[&amp;#39;updatedAt&amp;#39;]),
      );
    }
  }

  // Memo 데이터는 모두 여기서 관리
  class MemoService extends ChangeNotifier {
    MemoService() {
      loadMemoList();
    }

    List&amp;lt;Memo&amp;gt; memoList = [
      Memo(content: &amp;#39;장보기 목록: 사과, 양파&amp;#39;), // 더미(dummy) 데이터
      Memo(content: &amp;#39;새 메모&amp;#39;), // 더미(dummy) 데이터
    ];

    createMemo({required String content}) {
      Memo memo = Memo(content: content, updatedAt: DateTime.now());
      memoList.add(memo);
      notifyListeners(); // Consumer&amp;lt;MemoService&amp;gt;의 builder 부분을 호출해서 화면 새로고침
      saveMemoList();
    }

    updateMemo({required int index, required String content}) {
      Memo memo = memoList[index];
      memo.content = content;
      memo.updatedAt = DateTime.now();
      notifyListeners();
      saveMemoList();
    }

    updatePinMemo({required int index}) {
      Memo memo = memoList[index];
      memo.isPinned = !memo.isPinned;
      memoList = [
        ...memoList.where((element) =&amp;gt; element.isPinned),
        ...memoList.where((element) =&amp;gt; !element.isPinned)
      ];
      notifyListeners();
      saveMemoList();
    }

    deleteMemo({required int index}) {
      memoList.removeAt(index);
      notifyListeners();
      saveMemoList();
    }

    saveMemoList() {
      List memoJsonList = memoList.map((memo) =&amp;gt; memo.toJson()).toList();
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      String jsonString = jsonEncode(memoJsonList);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      prefs.setString(&amp;#39;memoList&amp;#39;, jsonString);
    }

    loadMemoList() {
      String? jsonString = prefs.getString(&amp;#39;memoList&amp;#39;);
      // &amp;#39;[{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]&amp;#39;

      if (jsonString == null) return; // null 이면 로드하지 않음

      List memoJsonList = jsonDecode(jsonString);
      // [{&amp;quot;content&amp;quot;: &amp;quot;1&amp;quot;}, {&amp;quot;content&amp;quot;: &amp;quot;2&amp;quot;}]

      memoList = memoJsonList.map((json) =&amp;gt; Memo.fromJson(json)).toList();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/971</guid>
      <comments>https://comphy.tistory.com/971#entry971comment</comments>
      <pubDate>Wed, 19 Apr 2023 21:02:34 +0900</pubDate>
    </item>
    <item>
      <title># 2주차 - 다양한 위젯을 활용해 화면 그리기</title>
      <link>https://comphy.tistory.com/970</link>
      <description>&lt;h1&gt;2주차 - 다양한 위젯을 활용해 화면 그리기&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6d652eb1-9be2-4cb8-a995-1d655be42942/2%EC%A3%BC%EC%B0%A8_-_%EB%8B%A4%EC%96%91%ED%95%9C_%EC%9C%84%EC%A0%AF%EC%9D%84_%ED%99%9C%EC%9A%A9%ED%95%B4_%ED%99%94%EB%A9%B4_%EA%B7%B8%EB%A6%AC%EA%B8%B0.pdf&quot;&gt;2주차&lt;em&gt;-&lt;/em&gt;다양한_위젯을_활용해_화면_그리기.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;단축키 모음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;새로고침 &lt;code&gt;F5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;저장&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전체선택&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;잘라내기&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;콘솔창 줄바꿈&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;enter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드정렬&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Alt&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;들여쓰기&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;들여쓰기 취소 : &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주석&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flutter의 Widget 이해하기&lt;/li&gt;
&lt;li&gt;화면 그리는 위젯 이해하기&lt;/li&gt;
&lt;li&gt;당근마켓 만들기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;01. Flutter Widget 이해하기&lt;/h2&gt;
&lt;aside&gt;
  Flutter의 모든 위젯은 **StatelessWidget** 과 **StatefulWidget**으로 나눌 수 있어요.

&lt;p&gt;&lt;strong&gt;StatelessWidget : 상태 변화가 없어 화면을 새로고침 할 필요가 없는 위젯&lt;br&gt;StatefulWidget&lt;/strong&gt; : &lt;strong&gt;상태 변화가 있어 화면을 새로고침 할 필요가 있는 위젯&lt;/strong&gt;&lt;/p&gt;
&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) &lt;strong&gt;StatelessWidget&lt;/strong&gt;&lt;/p&gt;
  &lt;aside&gt;
    상태(화면상에 나타나는 정보) 변화가 없어 화면을 새로고침 할 필요가 없는 위젯
  즉, **화면 내 내용이 변하지 않는 위젯**을 의미합니다

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;StatelessWidget&lt;/strong&gt; 생김새&lt;/p&gt;
 &lt;aside&gt;
   코드를 외우실 필요는 전혀 없습니다! 구조와 실행 순서만 아시면 됩니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dff65087-5f81-4c13-a528-d42fe908bbc9/stateless_(1).png&quot; alt=&quot;stateless (1).png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;extends StatelessWidget&lt;/code&gt; : &lt;strong&gt;StatelessWidget&lt;/strong&gt;의 기능을 물려받습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;생성자&lt;/code&gt; : 클래스 이름과 동일한 함수&lt;/li&gt;
&lt;li&gt;&lt;code&gt;build 함수&lt;/code&gt; : 화면에 보여줄 자식 위젯을 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사한 뒤 주소창에 붙여 넣어, DartPad로 접속하여 &lt;strong&gt;StatelessWidget&lt;/strong&gt;을 배워봅시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=f8a7bf195ec729c400634d97b10f0f84&quot;&gt;[코드스니펫] DartPad StatelessWidget 학습&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/?id=f8a7bf195ec729c400634d97b10f0f84&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      **StatelessWidget**의 **생김새** 및 **실행 순서**만 집중해주세요!

    &amp;lt;/aside&amp;gt;

    `Run` 버튼을 누르면 우측에 `hello Stateless Widget` 이라는 문구가 표시됩니다.

    ![Screen Shot 2022-09-01 at 12.37.09 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/42501e38-82f0-442a-867e-ec2ab58635f8/Screen_Shot_2022-09-01_at_12.37.09_AM.png)

    9번째 줄에 `MyApp` 클래스가 직접 만든 **StatelessWidget** 위젯입니다.

    ![Screen Shot 2022-09-01 at 12.41.14 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dcc19904-977a-45a2-bd13-285be1c8537b/Screen_Shot_2022-09-01_at_12.41.14_AM.png)

    참고: [Key 란 무엇인가](https://nsinc.tistory.com/214)

3. 실행 순서

    좌측 하단에 `Console` 버튼을 클릭해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7e528288-600b-4e8e-a1b1-0a2ac6a0e812/Untitled.png)

    1. 처음에 3번째 줄 `main()` 함수가 호출되어 Console에 `시작`이 출력됩니다.
    2. MyApp 위젯이 첫 번째 위젯으로 등록되고 `build` 함수가 호출 되면서 Console에 `build 호출`이 출력됩니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2ec5bad2-6cf2-4a07-9f83-753e01ad67c7/Untitled.png)


    &amp;lt;aside&amp;gt;
      화면에 보이는 첫 번째 위젯은 일반적으로 **MaterialApp** 또는 **CupertinoApp** 위젯으로 시작합니다.

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) &lt;strong&gt;StatefulWidget&lt;/strong&gt;&lt;/p&gt;
  &lt;aside&gt;
    상태(화면상에 나타나는 정보) 변화가 있어 화면을 새로고침 할 필요가 있는 위젯
  즉, **화면 내 내용이 변하는 위젯**을 의미합니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;StatefulWidget&lt;/strong&gt; 생김새&lt;/p&gt;
 &lt;aside&gt;
   코드를 외우실 필요는 전혀 없습니다! 구조와 실행 순서만 아시면 됩니다.

 &lt;/aside&gt;

&lt;p&gt; 기본적으로 2개의 클래스로 구성되어 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a9c5c5bf-9dd3-41a3-9a26-3d5972cfd8d0/stateful_(2).png&quot; alt=&quot;stateful (2).png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;MyApp&lt;/code&gt; : &lt;strong&gt;StatefulWidget&lt;/strong&gt;의 기능을 물려받은 클래스&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;_MyAppState&lt;/code&gt; : &lt;code&gt;MyApp&lt;/code&gt; 상태를 가진 클래스(실질적인 내용은 여기에 들어가요!)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2f60e70b-4e83-452f-8d60-35f82d72a70e/_stateful.png&quot; alt=&quot;_stateful.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;화면을 그리는 &lt;code&gt;build 함수&lt;/code&gt;는 &lt;strong&gt;상태 클래스 (&lt;code&gt;_MyAppState&lt;/code&gt;)&lt;/strong&gt; 에 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사한 뒤 주소창에 붙여 넣어, DartPad로 접속하여 &lt;strong&gt;StatefulWidget&lt;/strong&gt;을 배워봅시다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=513188d8ad8004aaf524ce52d668d84c&quot;&gt;[코드스니펫] DartPad StatefulWidget 학습&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/?id=513188d8ad8004aaf524ce52d668d84c&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      **StatefulWidget**의 **생김새** 및 **실행 순서**만 집중해주세요!

    &amp;lt;/aside&amp;gt;

    `Run` 버튼을 누르면 우측에 `1`이 표시됩니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4b296df2-cda6-495b-9f5f-fd5033a36e30/Untitled.png)

3. 실행 순서

    좌측 하단에 Console 버튼을 클릭해 주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/71b1ba4f-f307-41f1-97ee-1793e5175058/Untitled.png)

    4번째 줄의 `main 함수`와 23번째 줄의 `build 함수`가 차례대로 실행되어 아래와 같이 출력됩니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1711ccdb-fc11-4c16-81fa-a5b289191ad3/Untitled.png)

    우측에 파란 버튼을 누르면 화면에는 숫자가 2로 변경된 것을 볼 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dfc42de8-6de9-45f1-b283-5fd7290863fc/Untitled.png)

    Console을 보면 `클릭 됨` 과 `build 호출`이 추가되어 있습니다.

    &amp;lt;aside&amp;gt;
      버튼 클릭시 실행 순서는 다음과 같습니다.

    1. 34번째 `print(&amp;quot;클릭 됨&amp;quot;);` 출력
    2. 38번째 number 1 증가
    3. 37번째 라인의 `setState`로 인해 화면 갱신 (= build 함수 호출)
    4. build 함수가 호출되어 24번째 `print(&amp;quot;build 호출&amp;quot;);` 출력
    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      **StatefulWidget** 위젯에서 `setState()`를 호출하면 `build()` 함수가 다시 실행되면서 화면이 갱신됩니다.

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) &lt;strong&gt;Navigation (화면 이동)&lt;/strong&gt;&lt;/p&gt;
  &lt;aside&gt;
    위에서 배운 `**StatelessWidget**`과 `**StatefulWidget**`을 활용해 화면들을 그렸다면, 이 화면들 사이를 오갈 수도 있어야겠죠!

&lt;p&gt;  Flutter에선 각 화면을 라우트(Route)라고 부르며, 화면을 이동할 때 &lt;code&gt;네비게이터(Navigator)&lt;/code&gt;를 사용합니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;다음 페이지로 이동하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) =&amp;gt; SecondPage()), // 이동하려는 페이지
  );&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;현재 화면 종료&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  Navigator.pop(context); // 종료&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사한 뒤 주소창에 붙여 넣으면, DartPad로 접속합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=185dbad13d31ea8b99d7f83fe1f8ec3a&quot;&gt;[코드스니펫] DartPad Routing 학습&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/?id=185dbad13d31ea8b99d7f83fe1f8ec3a&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Routing.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0d93233f-58cb-448a-bb05-7b6f4aa6c438/Routing.png)


&amp;lt;aside&amp;gt;
  화면이 많아지는 경우, [Named Route](https://docs.flutter.dev/cookbook/navigation/named-routes) 방식을 사용하기도 합니다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;02. 프로젝트 준비&lt;/strong&gt;&lt;/h2&gt;
&lt;aside&gt;
  위에서 배운 위젯들을 활용하여 실제 화면을 만들어 봅시다.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) Flutter 프로젝트 생성하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Visual Studio Code(VSCode)를 실행해 주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt; 버튼을 클릭해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Command Palette` 기능은 앞으로도 자주 사용하게 될 거라 단축키를 알아두시면 좋습니다.
 window : `Ctrl + Shift + P`
 macOS : `Cmd + Shift + P`

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2e8bac1-770e-4f78-80a8-5b799f1f40e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;명령어를 검색하는 팝업창이 뜨면, &lt;code&gt;flutter&lt;/code&gt;라고 입력한 뒤 &lt;code&gt;Flutter: New Project&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a6058de-bbf7-410f-9dc3-a96d6bd7ac58/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Application&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c7145f5-c6f6-4306-b78e-e3bb31e24710/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트를 시작할 폴더를 선택하는 과정입니다. 미리 생성해 둔 &lt;code&gt;flutter&lt;/code&gt; 폴더를 선택한 뒤 &lt;code&gt;Select a folder to create the project in&lt;/code&gt; 버튼을 눌러 주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트 이름을 &lt;code&gt;daangn&lt;/code&gt; 으로 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55ea666a-d030-4a1d-8826-866b8f5e65a9/Screen_Shot_2022-09-04_at_11.17.06_PM.png&quot; alt=&quot;Screen Shot 2022-09-04 at 11.17.06 PM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 만약 중간에 아래와 같은 팝업이 뜬다면, 체크박스를 선택한 뒤 파란 버튼을 클릭해주세요.&lt;/p&gt;
 &lt;aside&gt;
   아래 팝업에 대한 자세한 사항은 [링크](https://stackoverflow.com/questions/67914668/vs-code-do-you-trust-the-authors-of-the-files-in-this-folder)를 참고해주세요.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6827f86-f59a-4808-9d8d-75245b9ec10d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음과 같이 프로젝트가 생성됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2e7c934f-17b1-4217-8971-f8ba4379050a/Screen_Shot_2022-09-05_at_7.27.13_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 7.27.13 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 &lt;code&gt;main.dart&lt;/code&gt; 코드스니펫을 복사해서 기존 코드를 모두 지운 뒤, &lt;code&gt;main.dart&lt;/code&gt; 파일에 붙여 넣고 저장해주세요.&lt;/p&gt;
 &lt;aside&gt;
   저장 단축키

&lt;p&gt; window : &lt;code&gt;Ctrl + S&lt;/code&gt;&lt;br&gt; macOS : &lt;code&gt;Cmd + S&lt;/code&gt;&lt;/p&gt;
 &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫]  main.dart&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) VSCode Dart 세팅&lt;/p&gt;
  &lt;aside&gt;
    VSCode에서 Flutter 개발시 좀 더 수월하게 도와주는 설정을 추가해 볼게요!

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt;를 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2e8bac1-770e-4f78-80a8-5b799f1f40e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래와 같이 &lt;code&gt;dart recommend&lt;/code&gt;라고 검색한 뒤 &lt;code&gt;Dart: Use Recommended Settings&lt;/code&gt;를 선택해 주세요. 그러면 자동으로 저장 시 자동 줄 정렬해 주는 기능과 같이 편의 기능 설정이 적용됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/15eba0b2-ef5b-4f6f-bf6a-fa937ca21af4/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   설정에 대한 상세한 내용은 아래 링크를 참고해 주세요.

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://dartcode.org/docs/recommended-settings/&quot;&gt;&lt;strong&gt;[코드스니펫] Dart Code Recommended Setting&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartcode.org/docs/recommended-settings/&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음으로 학습 단계에서 불필요한 내용을 화면에 표시하지 않도록 설정해 주겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;analysis_options.yaml&lt;/code&gt; 파일을 열고, 아래 코드스니펫을 복사해서 24번째 라인 뒤에 붙여 넣고 저장해 주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] analysis_options.yaml&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      prefer_const_constructors: false
      prefer_const_literals_to_create_immutables: false&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8470956b-ccc2-4d75-86d8-83042bafaf22/Untitled.png)

    &amp;lt;aside&amp;gt;
      상세 내용은 아래를 참고해 주세요.

    - 어떤 의미인지 궁금하신 분들을 위해

        `main.dart` 파일을 열어보시면 파란 실선이 있습니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d1393a08-a362-4be3-bd9b-835e184f5ef3/Untitled.png)

        파란 줄은, 개선할 여지가 있는 부분을 VSCode가 알려주는 표시입니다.

        12번째 라인에 마우스를 올리면 아래와 같이 설명이 보입니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7fd5cee4-d935-4d2d-9c35-e11d4f3c6cae/Untitled.png)

        위젯이 변경될 일이 없기 때문에 `const`라는 키워드를 앞에 붙여 상수(변하지 않는 수)로 선언하라는 힌트입니다. 상수로 만들면 화면을 새로고침 할 때, 상수로 선언된 위젯들은 새로고침을 할 필요가 없어서 성능상 이점이 있습니다.

        아래와 같이 `Icon`앞에 `const` 키워드를 붙여주시면 됩니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2aef9ee8-c4ef-4c42-bd08-d358c4620341/Untitled.png)

        지금은 학습 단계이니 눈에 띄지 않도록 `analysis_options.yaml` 파일에 아래와 같이 설정하여 힌트를 숨기도록 하겠습니다.

        ```dart
            prefer_const_constructors: false
            prefer_const_literals_to_create_immutables: false
        ```

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) 에뮬레이터 실행하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;VSCode 우측 하단에 &lt;code&gt;Chrome (web-javascript)&lt;/code&gt;를 클릭해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5d0db764-7e21-4261-8c19-94162c4d0ba8/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Start Pixel 2 API 29 mobile emulator&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
 &lt;aside&gt;
   macOS의 경우 iOS 에뮬레이터를 사용하셔도 됩니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e0e7243-7d95-4b57-99df-08fd28e1b4d7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 잠시 기다리면 에뮬레이터가 실행됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf5640d3-c9a9-4105-bd40-9dcb309412f5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VSCode 우측 상단에 &lt;strong&gt;아래 화살표&lt;/strong&gt;를 눌러 &lt;code&gt;Run Without Debugging&lt;/code&gt;을 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fba923c5-b4b0-4ddb-b381-ae8686c0315c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   `Start Debugging`으로 실행해도 무방합니다. 디버깅 모드는 특정 라인에서 앱 실행을 멈추고 해당 변수에 어떤 값이 들어있는지 볼 수 있지만 `Run Without Debugging`이 실행 속도가 더 빨라 안내를 위와 같이 드렸습니다  

 &lt;/aside&gt;

&lt;p&gt; 에뮬레이터에 아래와 같이 흰 화면이 나오면 정상적으로 실행이 완료된 것입니다!&lt;/p&gt;
&lt;p&gt; [Android]&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1a0d4bfb-a6ca-4d30-9aee-c89f70d1a9a5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; [iOS]&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9ee06fbe-191f-4ac9-8f6d-84126fbfb15a/Screen_Shot_2022-09-01_at_1.58.31_AM.png&quot; alt=&quot;Screen Shot 2022-09-01 at 1.58.31 AM.png&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   에뮬레이터가 뜨고나면 VSCode 우측 상단에 아래와 같은 메뉴가 뜹니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ffb58924-b346-4282-858d-bba3b9c5dc36/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;03. 당근마켓 화면 만들기&lt;/h2&gt;
&lt;aside&gt;
  당근마켓 메인 피드 화면을 만들며 위젯 사용법을 익히도록 하겠습니다.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;최종 완성 이미지&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![simulator_screenshot_4030D76F-D1E6-40EA-BE1A-761CA515E677.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d6ab797b-808d-4665-80ee-9b4f8035ae49/simulator_screenshot_4030D76F-D1E6-40EA-BE1A-761CA515E677.png)

![ezgif-4-9067a9c5c7.webp](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eb313c25-f041-4f70-b4d9-7b91c8a17e61/ezgif-4-9067a9c5c7.webp)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) HomePage 만들기&lt;/p&gt;
  &lt;aside&gt;
    시작 페이지인 HomePage 위젯을 만들어 보겠습니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt; 파일 19번째 라인에 &lt;code&gt;st&lt;/code&gt;라고 입력한 뒤, 자동 완성으로 추천되는 &lt;code&gt;Flutter Stateless Widget&lt;/code&gt; 을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6dd42f72-9c61-4da9-b4d7-dadc7e62df65/Screen_Shot_2022-09-01_at_1.52.19_AM.png&quot; alt=&quot;Screen Shot 2022-09-01 at 1.52.19 AM.png&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   `statelessWidget`이라고 입력하면 자동완성이 안보이니 꼭 `st`까지만 입력하고 자동완성을 눌러주세요.

 &lt;/aside&gt;

 &lt;aside&gt;
   자동완성이 사라진 경우 아래 단축키를 눌러 띄울 수 있습니다.
 window : `Ctrl + Space`
 macOS : `option + ESC` 또는 `fn + Ctrl + Space`

 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 아래와 같이 &lt;strong&gt;StatelessWidget&lt;/strong&gt; 클래스가 완성됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/860924b6-2183-4148-8a78-945fae51c95d/Screen_Shot_2022-09-04_at_11.37.27_PM.png&quot; alt=&quot;Screen Shot 2022-09-04 at 11.37.27 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;HomePage&lt;/code&gt;라고 이름을 적어주세요. 그러면 클래스의 이름과 생성자(constructor)에 아래와 같이 작성이 됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9c06c2a6-e374-4860-a514-3b6ae3226ea9/Screen_Shot_2022-09-04_at_11.42.47_PM.png&quot; alt=&quot;Screen Shot 2022-09-04 at 11.42.47 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 코드스니펫을 복사해서 24번째 라인을 지우고 붙여 넣어 저장해주세요.&lt;br&gt;(window : &lt;code&gt;ctrl + s&lt;/code&gt; / macOS : &lt;code&gt;cmd + s&lt;/code&gt;)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] HomePage Scaffold&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  return Scaffold(
      body: Center(child: Text(&amp;quot;home page&amp;quot;)),
  );&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Screen Shot 2022-09-05 at 1.17.07 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ce61b0d3-b543-4139-8a5a-e7845b64bb6e/Screen_Shot_2022-09-05_at_1.17.07_AM.png)

5. 앱 실행시 14번째 라인에 `home: Scaffold()`를 `home: HomePage()`로 변경한 뒤 저장해주세요.

    ![Screen Shot 2022-09-05 at 1.17.38 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b69d28e4-1c9d-4101-851b-2017934c9d62/Screen_Shot_2022-09-05_at_1.17.38_AM.png)

6. 이제 에뮬레이터에 `HomePage`가 뜨는 것을 볼 수 있습니다.

    ![Screen Shot 2022-09-01 at 1.57.55 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d297b20a-6ca8-4a47-b590-5290a9f88617/Screen_Shot_2022-09-01_at_1.57.55_AM.png)


&amp;lt;aside&amp;gt;
  내용 요약

앱을 시작할 때 `MaterialApp`으로 앱을 시작하고, `home`이라는 **이름지정 매개변수(named parameter)**에 첫 번째 페이지 위젯을 만들어 전달합니다.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/46d02d2b-5fce-4384-a49e-bd1a012fa8d6/Untitled.png)

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) appBar 만들기&lt;/p&gt;
  &lt;aside&gt;
    상단에 AppBar를 추가해 보도록 하겠습니다.

  &lt;/aside&gt;

&lt;p&gt;  AppBar의 영역에 대한 명칭은 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5eeafa59-aad6-4186-8bc9-24ff29381903/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  우리는 아래와 같이 &lt;code&gt;leading&lt;/code&gt; 과 &lt;code&gt;actions&lt;/code&gt; 에 각각 아이콘과 이미지를 넣어주면 되겠군요!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d4a85478-5df9-426f-8218-9ca4ea63645d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;leading&lt;/code&gt; &amp;amp; &lt;code&gt;actions&lt;/code&gt; 아이콘 버튼 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;아래 코드스니펫을 복사해서 24번째 &lt;code&gt;return Scaffold(&lt;/code&gt; 뒤에 붙여 넣어주세요!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] AppBar 아이콘&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;                  appBar: AppBar(
          leading: Row(
            children: [
              SizedBox(width: 16),
              Text(
                &amp;#39;중앙동&amp;#39;,
                style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.bold,
                  fontSize: 20,
                ),
              ),
              Icon(
                Icons.keyboard_arrow_down_rounded,
                color: Colors.black,
              ),
            ],
          ),
          leadingWidth: 100,
          actions: [
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.search, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.menu_rounded, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.bell, color: Colors.black),
            ),
          ],
        ),&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;        ![Screen Shot 2022-09-05 at 2.07.08 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a8d700e-75d2-4a0c-9744-8dadb5913dc3/Screen_Shot_2022-09-05_at_2.07.08_AM.png)

    2. 47번째 라인에 `CupertinoIcons`가 빨간 줄로 그어져 있습니다. 마우스를 올려보면 아래와 같이 `CupertinoIcons` 위젯이 인식이 안된다고 나옵니다.

        &amp;lt;aside&amp;gt;
          **빨간 줄**은 문제가 있다는 표시입니다. 이 상태에서는 저장을 해도 앱에 반영이 되지 않으니 항상 해결을 하고 넘어가야 합니다.

        &amp;lt;/aside&amp;gt;

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1131e670-a5c6-4f0a-85e6-7902fbb6f773/Untitled.png)

        위에 `Quick Fix`를 클릭해주세요.

        &amp;lt;aside&amp;gt;
          에러가 있는 곳을 클릭하신 뒤 단축키를 눌러 Quick Fix를 바로 실행하실 수도 있어요.

        window : `ctrl + .`
        macOS : `cmd + .`

        - window 단축키가 안되는 경우

            `ctrl + .`을 눌렀을 때 `·` 가 입력되고 단축키가 먹히지 않는 경우가 있습니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c71b281e-23ca-4efd-a6fe-94260d9290e7/Untitled.png)

            `win + space` 를 눌서 `한컴 입력기`가 아닌 `Microsoft 입력기`를 선택해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d8676cf6-948c-4662-8bc4-873c135425b8/Untitled.png)

            한컴 입력기를 삭제하는 방법은 아래 링크를 참고해 주세요.

            - **[[코드스니펫] window 한컴 입력기 삭제방법](https://www.lesstif.com/life/ms-ide-75956246.html)**

                ```dart
                https://www.lesstif.com/life/ms-ide-75956246.html
                ```

        &amp;lt;/aside&amp;gt;

        아래와 같이 `import library &amp;#39;package:flutter/cupertino.dart&amp;#39;;`를 선택해주세요

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0ca6288c-4cb4-43d5-a401-7f10ee216d6b/Untitled.png)

        그러면 맨 위에 이런 구문이 추가된 것을 보실 수 있습니다.
        `import &amp;#39;package:flutter/cupertino.dart&amp;#39;;` 

        &amp;lt;aside&amp;gt;
          `cupertino.dart` 에는 아이콘이나 위젯들이 미리 정의되어 있습니다.
        우리는 이를 가져다 쓰기 위해 import 해준 것입니다.

        &amp;lt;/aside&amp;gt;

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/06583a4e-4290-4de8-a277-e23599d34626/Untitled.png)

    3. 저장하면 에뮬레이터에 AppBar와 아이콘이 출력됩니다.

        ![Screen Shot 2022-09-05 at 2.13.58 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6333c7cc-57e5-4514-a8fc-6d602294601a/Screen_Shot_2022-09-05_at_2.13.58_AM.png)

    4. AppBar 의 색상과 그림자를 변경해보겠습니다.

        26번째 줄 맨 뒤에 엔터를 눌러 빈 라인을 추가한 뒤, 코드스니펫을 복사해서 붙여 넣어 주세요. 

        - **[코드스니펫] AppBar backgroundColor, elevation**

            ```dart
            backgroundColor: Colors.white,
            elevation: 0.5,
            ```


        각각 `AppBar`의 속성으로 들어가는 값들 이기 때문에 쉼표로 구분해줍니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/17696096-88e0-4725-a99a-7c1ee6e20c80/Untitled.png)

        &amp;lt;aside&amp;gt;
          AppBar를 완성했습니다!

        &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) &lt;code&gt;body&lt;/code&gt; 만들기 - 레이아웃&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;레이아웃 나누기&lt;/p&gt;
  &lt;aside&gt;
    `Row`와 `Column`(여러 위젯을 가로, 세로로 배치할 수 있도록 묶어주는 위젯)을 이용해서 아래와 같이 섹션 별로 나누어 큰 틀부터 작업을 시작합니다.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/41ce8a5c-8ea9-41c3-9cb8-a0d64ea28888/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;62번째 줄에 &lt;code&gt;Center&lt;/code&gt;를 클릭한 뒤 &lt;code&gt;마우스 우클릭&lt;/code&gt; → &lt;code&gt;Refactor&lt;/code&gt;를 선택합니다.&lt;/p&gt;
 &lt;aside&gt;
   `Refactor` 는 자주 쓰는 사용하므로 단축키를 알아두시면 좋습니다.
 window : `Ctrl + Shift + R`
 macOS: `Ctrl + Shift + R`

&lt;p&gt; 왼쪽에 뜨는  전구 모양 아이콘을 클릭하셔도 됩니다.&lt;/p&gt;
 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/90d6e726-bc05-424c-8cdc-b0e80e574091/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Wrap with Row&lt;/code&gt;을 선택합니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/032f6c17-e6a9-4a18-9440-13488180afc5/Screen_Shot_2022-09-05_at_2.37.07_AM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 2.37.07 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;Center&lt;/code&gt; 위젯이 아래와 같이 &lt;code&gt;Row&lt;/code&gt; 위젯으로 감싸집니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f20f25cf-9400-4d85-83b1-4bb99027731a/Screen_Shot_2022-09-05_at_2.37.27_AM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 2.37.27 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;64번째 라인을 삭제하고 아래 코드 스니펫을 붙여넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 레이아웃&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;                      // 이미지 들어갈 자리
            Column(
              children: [
                // &amp;#39;M1 아이패드 프로 11형(3세대) 와이파이 128G 팝니다.&amp;#39;
                // &amp;#39;봉천동 · 6분 전&amp;#39;
                // &amp;#39;100만원&amp;#39;
                Row(
                  children: [
                    // 빈 칸
                    // 하트 아이콘
                    // &amp;#39;1&amp;#39;
                  ],
                ),
              ],
            ),&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;        ![Screen Shot 2022-09-05 at 2.46.03 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/398473da-ae01-43eb-b560-a7d7d00ef573/Screen_Shot_2022-09-05_at_2.46.03_AM.png)

        - 먼저 `Row` 안에 가로 방향으로 이미지와 나머지 요소들의 `Column` 을 배치해줍니다.
        - `Column` 안에 각각의 텍스트 요소들을 넣어줍니다.
        - 마지막 줄에 나오는 하트 아이콘과 숫자는 다시 `Row` 로 묶어줍니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/41ce8a5c-8ea9-41c3-9cb8-a0d64ea28888/Untitled.png)

        &amp;lt;aside&amp;gt;
          디자인을 보고 큰 단위에서부터 차근차근 Row 와 Column 을 사용해 요소들을 배치하는 연습이 필요합니다.

        레이아웃을 잡았으니, `children`에 위젯들을 하나씩 넣어봅시다.

        &amp;lt;/aside&amp;gt;

        &amp;lt;aside&amp;gt;
          이 둘만으로도 많은 레이아웃을 만들어낼 수 있으나, 여러 요소들을 겹치게 표현하기 위해서는 Stack 이라는 클래스를 사용해야 합니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cafe8d72-ce81-401f-bc4b-cbb3f2abf5e7/Untitled.png)

        자세한 사용법과 Column 및 Row 와의 비교는 아래의 글을 참고해주세요!

        [[Flutter] Stack과 Positioned Class](https://ahang.tistory.com/24)

        &amp;lt;/aside&amp;gt;

- 이미지 만들기

    &amp;lt;aside&amp;gt;
      이번에는 인터넷에 있는 고양이 사진 URL을 `Image.network()` 위젯을 이용해 가져오겠습니다.

    &amp;lt;/aside&amp;gt;

    1. 코드스니펫을 복사해서 64번째 라인의 주석을 지우고 붙여 넣어 주세요. 
        - **[코드스니펫] body 이미지**

            ```dart
                                // CilpRRect 를 통해 이미지에 곡선 border 생성
                      ClipRRect(
                        borderRadius: BorderRadius.circular(8),
                        // 이미지
                        child: Image.network(
                          &amp;#39;https://cdn2.thecatapi.com/images/6bt.jpg&amp;#39;,
                          width: 100,
                          height: 100,
                          fit: BoxFit.cover,
                        ),
                      ),
            ```


        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2a2c497b-3217-476b-985b-cf56c72fce71/Untitled.png)

        &amp;lt;aside&amp;gt;
          우리는 정사각형의 위젯에 이미지를 채워서 보여주려 합니다.  
        `fit: BoxFit.cover`라고 넣어주면 이미지의 비율을 유지하면서 고정된 폭과 높이에 맞추어 이미지를 적절히 잘라서 보여줍니다.

        ![폭과 높이를 넘어가는 이미지 부분은 모두 잘립니다](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d085ff5d-8600-439b-bb91-0ba3d81be3e6/Untitled.png)

        폭과 높이를 넘어가는 이미지 부분은 모두 잘립니다

        [BoxFit 이 더 궁금하다면?](https://api.flutter.dev/flutter/painting/BoxFit.html)

        &amp;lt;/aside&amp;gt;

- 텍스트 만들기

    &amp;lt;aside&amp;gt;
      아래와 같이 텍스트를 세로로 배치해보겠습니다. `Column` 위젯 속에 `Text` 위젯들을 배치하면 되겠죠!

    ![Screen Shot 2022-09-05 at 3.31.56 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5c33048e-13ea-443a-80ba-052f4db1cfb8/Screen_Shot_2022-09-05_at_3.31.56_AM.png)

    &amp;lt;/aside&amp;gt;

    1. 코드스니펫을 복사해서 77, 78, 79번째 라인을 모두 지우고 붙여 넣어 주세요.
        - **[코드스니펫] 텍스트 세로 배치**

            ```dart
                                            Text(
                              &amp;#39;M1 아이패드 프로 11형(3세대) 와이파이 128G 팝니다.&amp;#39;,
                              style: TextStyle(
                                fontSize: 16,
                                color: Colors.black,
                              ),
                              softWrap: false,
                              maxLines: 2,
                              overflow: TextOverflow.ellipsis,
                            ),
                            SizedBox(height: 2),
                            Text(
                              &amp;#39;봉천동 · 6분 전&amp;#39;,
                              style: TextStyle(
                                fontSize: 12,
                                color: Colors.black45,
                              ),
                            ),
                            SizedBox(height: 4),
                            Text(
                              &amp;#39;100만원&amp;#39;,
                              style: TextStyle(
                                fontSize: 14,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
            ```


        ![simulator_screenshot_196B932F-273E-4490-A02B-486EDD508ADB.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b3eb7c74-1d52-4226-8deb-b8524264dfa7/simulator_screenshot_196B932F-273E-4490-A02B-486EDD508ADB.png)

        위와 같이 overflow가 발생할 겁니다! 이는 지정된 사이즈를 위젯이 넘어갔다는 뜻인데요.

        이는 `Column` 위젯의 폭이 설정되지 않았기 때문에 발생하는 문제입니다. `Expanded` 위젯을 통해 이 문제를 해결해봅시다.

        75번째 줄의 `Column` 위젯에 마우스 커서를 가져다두면 왼쪽에 전구 모양 아이콘이 생길텐데요. 이를 눌러봅시다. (마우스 우클릭해서 나오는 `Refactor` 와 같은 옵션입니다!)

        ![Screen Shot 2022-09-05 at 10.49.41 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/72357c1b-9673-4296-acc3-521c7ed87d8c/Screen_Shot_2022-09-05_at_10.49.41_AM.png)

        `Wrap with widget` 을 눌러주시면 아래와 같이 `widget` 으로 `Column` 이 쌓이게 됩니다. widget 자리에 우리가 원하는 위젯 이름을 넣어주면 됩니다.

        ![Screen Shot 2022-09-05 at 10.50.59 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6922308b-447f-4db2-b8ca-94348fd18710/Screen_Shot_2022-09-05_at_10.50.59_AM.png)

        `Expanded` 라고 입력해줍니다.

        ![Screen Shot 2022-09-05 at 10.52.03 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2930ec8-7a33-438c-a806-ea07b2ce4c86/Screen_Shot_2022-09-05_at_10.52.03_AM.png)

        `Expanded` 위젯으로 `Column` 을 감싸면 해당 위젯이 차지할 수 있는 공간을 최대한 차지하게 됩니다. 남는 공간 만큼을 위젯의 폭으로 사용할 수 있는 것이죠. 위젯의 폭이 설정되니 overflow 도 해결됩니다. 저장하고 확인해볼까요?

        ![Screen Shot 2022-09-05 at 10.52.43 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/09029cb1-5c99-4189-9fd4-73d4eed67045/Screen_Shot_2022-09-05_at_10.52.43_AM.png)

        오른쪽에 뜨던 overflow 가 사라졌습니다!

        &amp;lt;aside&amp;gt;
          `Expanded` 위젯은 child 위젯이 차지할 수 있는 공간을 최대한 차지하도록 그 크기를 지정해주는 위젯입니다.

        보다 자세한 내용은 아래 링크를 참고해주세요!
        [[플러터]Flexible과 Expanded 위젯](https://mike123789-dev.tistory.com/entry/%ED%94%8C%EB%9F%AC%ED%84%B0Flexible%EA%B3%BC-Expanded-%EC%9C%84%EC%A0%AF)

        &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) &lt;code&gt;body&lt;/code&gt; 만들기 - 요소 정렬&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;crossAxisAlignment&lt;/code&gt; 설정하기&lt;/p&gt;
&lt;p&gt;  이제 이미지와 텍스트의 정렬을 맞춰주면 되겠군요.&lt;/p&gt;
&lt;p&gt;  &lt;code&gt;Row&lt;/code&gt; 와 &lt;code&gt;Column&lt;/code&gt; 은 주축, 부축을 기준으로 정렬할 수 있는데요. 주축을 main axis, 부축을 cross axis 라고 합니다. 아래 사진을 참고해주세요!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7f5ec4a9-ca2e-45bf-9059-fcc1ea90e447/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0d6140ca-ff22-49e8-8fa8-7df9bf935202/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  내부 요소들의 정렬은 &lt;code&gt;MainAxisAlignment.center&lt;/code&gt; 와 같은 식으로 설정해줍니다.  &lt;/p&gt;
&lt;p&gt;  Alignment 방법들을 시각화하면 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f6d88258-4017-42bd-836a-16454dc772bd/Untitled.png&quot; alt=&quot;출처: [https://morioh.com/p/9c7541691c2c](https://morioh.com/p/9c7541691c2c)&quot;&gt;&lt;/p&gt;
&lt;p&gt;  출처: &lt;a href=&quot;https://morioh.com/p/9c7541691c2c&quot;&gt;https://morioh.com/p/9c7541691c2c&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/247e696a-b938-4043-b6a4-61088f85cf01/Untitled.png&quot; alt=&quot;출처: [https://morioh.com/p/9c7541691c2c](https://morioh.com/p/9c7541691c2c)&quot;&gt;&lt;/p&gt;
&lt;p&gt;  출처: &lt;a href=&quot;https://morioh.com/p/9c7541691c2c&quot;&gt;https://morioh.com/p/9c7541691c2c&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d71ea975-f9d3-4eb2-ac3f-cbe298178b11/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  위와 같이 배치되어 있는 요소들을 같은 선상에 맞추려면, &lt;code&gt;Row&lt;/code&gt; 의 &lt;code&gt;CrossAxisAlignment&lt;/code&gt; 를 설정해주면 되겠죠!&lt;/p&gt;
&lt;p&gt;  코드스니펫을 복사해서 62번째 라인 뒤에 붙여 넣어 요소들을 위로 정렬해 보도록 하겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] Row CrossAxisAlignment&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
  crossAxisAlignment: CrossAxisAlignment.start,&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Screen Shot 2022-09-05 at 3.23.32 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1efe54fb-6370-49ae-939c-313e3546476b/Screen_Shot_2022-09-05_at_3.23.32_PM.png)

    아래 사진처럼 요소들이 위쪽 시작점에 붙는 것을 볼 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4cb7fb62-c4ac-43a2-a7ba-896e8b5ae70b/Untitled.png)

    마찬가지로 코드스니펫을 복사해서 77번째 라인 뒤에 붙여 넣어 요소들을 왼쪽으로 정렬해 보도록 하겠습니다.

    - **[코드스니펫] Column CrossAxisAlignment**

        ```dart

        crossAxisAlignment: CrossAxisAlignment.start,
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2a0cead6-1afb-4d7d-aca9-4052d64756ac/Untitled.png)

    텍스트 요소들이 왼쪽 시작점에 붙는 것을 볼 수 있습니다.

    &amp;lt;aside&amp;gt;
      **MainAxisAlignment, CrossAxisAlignment** 를 설정해서 `Row`, `Column` 내 요소들을 정렬할 수 있습니다.

    &amp;lt;/aside&amp;gt;

- `SizedBox` 를 이용해 위젯간 간격 설정하기

    &amp;lt;aside&amp;gt;
      아래 사진처럼 이미지와 텍스트가 너무 붙어있을 때는 어떻게 하는 게 좋을까요?

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/122c3ceb-a8ac-48f5-94c6-a079661f11c4/Untitled.png)

    이미지와 텍스트 사이에 빈 `SizedBox` 위젯을 넣어 간격을 조정할 수 있습니다.

    &amp;lt;/aside&amp;gt;

    코드스니펫을 복사해서 75번째 라인 뒤에 붙여 넣어 `SizedBox` 를 추가해주겠습니다.

    - **[코드스니펫] SizedBox**

        ```dart

        SizedBox(width: 12),
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/14b8e1cb-1b32-4ee0-aeef-27669e7efed4/Untitled.png)

    저장하면 아래와 같이 요소 사이에 간격이 추가된 것을 확인할 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e04f335c-8780-47e1-af9d-240b8ef562f3/Untitled.png)

- 좋아요 버튼 추가하기

    &amp;lt;aside&amp;gt;
      오른쪽 아래에 좋아요 버튼을 추가하기 위해 `Row` 위젯과 `Spacer` 위젯을 활용해봅시다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cc2d9b5d-fb6d-4dff-b7c2-6a3abaf650a1/Untitled.png)

    &amp;lt;/aside&amp;gt;

    코드스니펫을 복사해서 109번째, 110번째, 111번째 줄에 있는 주석을 지우고 `Row` 의 `children` 안에 붙여 넣어줍니다.

    - **[코드스니펫] 좋아요 아이콘, 숫자**

        ```dart

                                            Spacer(),
                            GestureDetector(
                              onTap: () {},
                              child: Row(
                                children: [
                                  Icon(
                                    CupertinoIcons.heart,
                                    color: Colors.black54,
                                    size: 16,
                                  ),
                                  Text(
                                    &amp;#39;1&amp;#39;,
                                    style: TextStyle(color: Colors.black54),
                                  ),
                                ],
                              ),
                            )
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f1cc1318-9ccd-4e84-8125-0cd7c63c27f4/Untitled.png)

    좋아요 아이콘과 숫자가 오른쪽 끝에 배치된 것을 볼 수 있습니다.
    (`GestureDetector` 는 추후에 좋아요 버튼 클릭을 구현하기 위해 우선 추가했습니다.)

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c5330b6-4c0b-465e-88cd-6ed421c690d9/Untitled.png)

    &amp;lt;aside&amp;gt;
      `Spacer` 위젯은 빈 공간을 차지하는 위젯입니다.

    &amp;lt;/aside&amp;gt;

    피드 1개를 완성했습니다!&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;5) &lt;code&gt;floatingActionButton&lt;/code&gt; 만들기&lt;/p&gt;
&lt;p&gt;  아래 코드스니펫을 복사해 132번째 라인 뒤에 붙여 넣습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] FloatingActionButton&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
              floatingActionButton: FloatingActionButton(
          onPressed: () {},
          backgroundColor: Color(0xFFFF7E36),
          elevation: 1,
          child: Icon(
            Icons.add_rounded,
            size: 36,
          ),
        ),&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![Screen Shot 2022-09-05 at 4.10.04 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/20190587-ca31-4b00-9265-f7dd448e2a52/Screen_Shot_2022-09-05_at_4.10.04_PM.png)

저장하면 아래와 같이 `FloatingActionButton` 이 생성된 것을 볼 수 있습니다.

![Screen Shot 2022-09-05 at 4.10.28 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6fc0c60c-9b78-400d-a2d4-4ee5a1928342/Screen_Shot_2022-09-05_at_4.10.28_PM.png)

&amp;lt;aside&amp;gt;
  코드가 너무 길 때는 body 요소가 어디에서 끝나는지 닫는 괄호를 찾기가 힘들 수 있습니다.

이럴 때는 아래와 같은 VS Code 의 접어두기 기능이 유용합니다.

![Screen Shot 2022-09-05 at 4.12.05 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/506744f3-ec9d-4e26-94c6-6c7eba000484/Screen_Shot_2022-09-05_at_4.12.05_PM.png)

해당 버튼을 누르면 아래와 같이 해당 요소가 접힙니다. 새로운 요소를 추가하고 싶다면, 이 아래에 추가해주면 되겠죠.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ce88fec3-4874-4c0d-87ad-79e424039f27/Untitled.png)

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;6) &lt;code&gt;bottomNavigationBar&lt;/code&gt; 만들기&lt;/p&gt;
&lt;p&gt;  아래 코드스니펫을 복사해 141번째 라인 뒤에 붙여 넣습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] BottomNavigationBar&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
              bottomNavigationBar: BottomNavigationBar(
          fixedColor: Colors.black,
          unselectedItemColor: Colors.black,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home_filled),
              label: &amp;#39;홈&amp;#39;,
              backgroundColor: Colors.white,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.my_library_books_outlined),
              label: &amp;#39;동네생활&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.fmd_good_outlined),
              label: &amp;#39;내 근처&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.chat_bubble_2),
              label: &amp;#39;채팅&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.person_outline,
              ),
              label: &amp;#39;나의 당근&amp;#39;,
            ),
          ],
          currentIndex: 0,
        ),&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/684b79a1-a894-4aa7-9d3e-3991693eec1d/Untitled.png)

저장하면 아래와 같이 `BottomNavigationBar` 이 생성된 것을 볼 수 있습니다.

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a7b616c-1a15-48e5-8df6-e38d11723772/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt; 최종 코드&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }

  class HomePage extends StatelessWidget {
    const HomePage({super.key}); // 생성자

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          elevation: 0.5,
          leading: Row(
            children: [
              SizedBox(width: 16),
              Text(
                &amp;#39;중앙동&amp;#39;,
                style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.bold,
                  fontSize: 20,
                ),
              ),
              Icon(
                Icons.keyboard_arrow_down_rounded,
                color: Colors.black,
              ),
            ],
          ),
          leadingWidth: 100,
          actions: [
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.search, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.menu_rounded, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.bell, color: Colors.black),
            ),
          ],
        ),
        body: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // CilpRRect 를 통해 이미지에 곡선 border 생성
            ClipRRect(
              borderRadius: BorderRadius.circular(8),
              // 이미지
              child: Image.network(
                &amp;#39;https://cdn2.thecatapi.com/images/6bt.jpg&amp;#39;,
                width: 100,
                height: 100,
                fit: BoxFit.cover,
              ),
            ),
            SizedBox(width: 12),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    &amp;#39;M1 아이패드 프로 11형(3세대) 와이파이 128G 팝니다.&amp;#39;,
                    style: TextStyle(
                      fontSize: 16,
                      color: Colors.black,
                    ),
                    softWrap: false,
                    maxLines: 2,
                    overflow: TextOverflow.ellipsis,
                  ),
                  SizedBox(height: 2),
                  Text(
                    &amp;#39;봉천동 · 6분 전&amp;#39;,
                    style: TextStyle(
                      fontSize: 12,
                      color: Colors.black45,
                    ),
                  ),
                  SizedBox(height: 4),
                  Text(
                    &amp;#39;100만원&amp;#39;,
                    style: TextStyle(
                      fontSize: 14,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  Row(
                    children: [
                      Spacer(),
                      GestureDetector(
                        onTap: () {},
                        child: Row(
                          children: [
                            Icon(
                              CupertinoIcons.heart,
                              color: Colors.black54,
                              size: 16,
                            ),
                            Text(
                              &amp;#39;1&amp;#39;,
                              style: TextStyle(color: Colors.black54),
                            ),
                          ],
                        ),
                      )
                    ],
                  ),
                ],
              ),
            ),
          ],
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {},
          backgroundColor: Color(0xFFFF7E36),
          elevation: 1,
          child: Icon(
            Icons.add_rounded,
            size: 36,
          ),
        ),
        bottomNavigationBar: BottomNavigationBar(
          fixedColor: Colors.black,
          unselectedItemColor: Colors.black,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home_filled),
              label: &amp;#39;홈&amp;#39;,
              backgroundColor: Colors.white,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.my_library_books_outlined),
              label: &amp;#39;동네생활&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.fmd_good_outlined),
              label: &amp;#39;내 근처&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.chat_bubble_2),
              label: &amp;#39;채팅&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.person_outline,
              ),
              label: &amp;#39;나의 당근&amp;#39;,
            ),
          ],
          currentIndex: 0,
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;04. 파일 분리&lt;/h2&gt;
&lt;aside&gt;
  모든 코드가 `main.dart`에 있는데, 코드가 길어질수록 코드를 찾는 시간이 길어지기 때문에 파일을 분리해 보도록 하겠습니다.
일반적으로 위젯들을 각각 다른 파일에 분리해두곤 합니다.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 파일 구조&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cab325e1-9cb5-4442-9c27-2307be78ec84/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt; : 앱 시작&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;home_page.dart&lt;/code&gt; : 첫 번째 페이지 레이아웃&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;feed.dart&lt;/code&gt; : &lt;code&gt;HomePage&lt;/code&gt;에서 &lt;code&gt;body&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/599101a5-8949-44c9-9390-3038c24f45fd/Screen_Shot_2022-09-05_at_4.45.51_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 4.45.51 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) &lt;code&gt;home_page.dart&lt;/code&gt; 파일 분리&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;lib&lt;/code&gt; 폴더를 선택한 뒤 마우스 우클릭으로 &lt;code&gt;New File&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1cfed070-4d7a-4e26-b740-88e9be05527d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이름을 &lt;code&gt;home_page.dart&lt;/code&gt;로 지어주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2681e6c1-9fcb-4359-a1b9-13377a75c9a0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt; 파일에 20번째 라인부터 마지막 줄 까지 &lt;code&gt;HomePage&lt;/code&gt; 위젯이므로 잘라서 &lt;code&gt;home_page.dart&lt;/code&gt; 파일로 옮겨주세요!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;처음 옮기고 나면 아래와 같이 빨간줄 투성이로 에러가 발생하는데, &lt;code&gt;Material&lt;/code&gt;과 &lt;code&gt;Cupertino&lt;/code&gt; import가 안 돼서 발생하는 에러입니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/116e2fbe-47f4-4c96-b9ca-7a916cd5c84a/Screen_Shot_2022-09-05_at_4.56.09_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 4.56.09 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;첫 번째 줄에 &lt;code&gt;StatelessWidget&lt;/code&gt;을 클릭하신 뒤 Quick Fix(window : &lt;code&gt;Ctrl + .&lt;/code&gt; / macOS : &lt;code&gt;Cmd + .&lt;/code&gt;)를 누르면 아래와 같이 나옵니다. 여기서 &lt;code&gt;import &amp;#39;package:flutter/cupertino.dart&amp;#39;;&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/573735c9-d319-49af-84bd-70d99998dcdf/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cupertino 패키지가 import 되고도 에러가 발생하는데, 빨간 줄이 있는 곳에서 Quick Fix(window : &lt;code&gt;Ctrl + .&lt;/code&gt; / macOS : &lt;code&gt;Cmd + .&lt;/code&gt;)를 눌러주신 뒤 &lt;code&gt;import &amp;#39;package:flutter/material.dart&amp;#39;;&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e2bbfe46-7abd-4770-9c74-d9bab1e6db89/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 빨간 줄이 모두 없어졌습니다. &lt;code&gt;Ctrl + S&lt;/code&gt; 또는 &lt;code&gt;Cmd + S&lt;/code&gt;를 눌러 저장해주세요.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/33840f77-9d86-4e75-8fba-8c0a0abcad72/Screen_Shot_2022-09-05_at_4.58.04_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 4.58.04 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음 &lt;code&gt;main.dart&lt;/code&gt; 파일을 보면 15번째 라인에 에러가 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;HomePage&lt;/code&gt; 위젯을 별도로 옮겼기 때문에 발생하는 에러로 Quick Fix(window : &lt;code&gt;Ctrl + .&lt;/code&gt; / macOS : &lt;code&gt;Cmd + .&lt;/code&gt;) 눌러서  &lt;code&gt;import library &amp;#39;home_page.dart&amp;#39;&lt;/code&gt;를 선택해줍니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/29112dd5-acd4-49d4-aa45-e7160b85e010/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4번째 라인에 &lt;code&gt;home_page&lt;/code&gt; 위젯을 불러오는 코드가 추가 되며 문제가 해결되었습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e0fb70b7-d08b-4d5a-8761-9fbd9a65f2b2/Screen_Shot_2022-09-05_at_4.58.58_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 4.58.58 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;저장을 하시면 에뮬레이터에서 정상적으로 결과가 나오는 것을 보실 수 있습니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) &lt;code&gt;feed.dart&lt;/code&gt; 파일 분리&lt;/p&gt;
  &lt;aside&gt;
    당근마켓에서는 동일한 화면에 내용만 다른 피드를 스크롤하며 컨텐츠를 볼 수 있습니다.

&lt;p&gt;  (대부분의 앱들이 이런 식으로 비슷한 모양의 피드들을 스크롤 할 수 있도록 구현되어 있습니다)&lt;br&gt;  &lt;code&gt;HomePage&lt;/code&gt; 위젯의 &lt;code&gt;body&lt;/code&gt;가 피드에 해당하는데, 각 피드마다 매번 같은 코드를 반복해 만들게 되면 디자인이나 기능이 바뀌었을 때, 하나씩 다 수정해야해서 관리가 힘듭니다.&lt;/p&gt;
&lt;p&gt;  따라서 반복되는 부분을 별도의 위젯으로 만들면 코드가 중복되는 문제를 해결할 수 있습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/97d5107f-5166-4a57-b6bc-0ba20e8f51e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
  &lt;/aside&gt;

  &lt;aside&gt;
    `HomePage` 위젯의 `body` 부분을 `Feed` 라는 이름의 위젯으로 분리해보겠습니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;home_page.dart&lt;/code&gt;에 46번째 라인 &lt;code&gt;Row&lt;/code&gt; 위젯을 우클릭하여 &lt;code&gt;Refactor&lt;/code&gt; 메뉴를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c7d7d262-212f-4cb1-a77a-149f2974b8db/Screen_Shot_2022-09-05_at_5.07.55_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 5.07.55 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Extract Widget&lt;/code&gt;을 선택 해주세요. 그러면 해당 위젯을 별도의 &lt;code&gt;StatelessWidget&lt;/code&gt; 클래스로 분리할 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a2f9a7e5-b064-45b9-b484-5715b02fc2d9/Screen_Shot_2022-09-05_at_5.08.12_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 5.08.12 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위젯 이름을 &lt;code&gt;Feed&lt;/code&gt;라고 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9eb62573-30df-4d15-9b99-4b118ee9a9c0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 아래 사진과 같이 46번째 라인에 &lt;code&gt;Feed()&lt;/code&gt; 인스턴스가 입력되고, 95번째 라인에 &lt;code&gt;Feed&lt;/code&gt; 클래스가 생성됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f3fd146f-969e-48bc-aca6-53ca7a9a11e9/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;lib&lt;/code&gt; 폴더를 우클릭한 뒤 &lt;code&gt;New File&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e48c9d87-098f-4d9a-a636-7b229ec1c723/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파일 이름은 &lt;code&gt;feed.dart&lt;/code&gt;라고 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7c04802e-4a2f-433b-a554-5b5dd7299de7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;home_page.dart&lt;/code&gt;에 95번째 라인에 있는 &lt;code&gt;Feed&lt;/code&gt; 클래스를 잘라내어 &lt;code&gt;feed.dart&lt;/code&gt; 파일로 옮겨주세요. &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/09f3f12c-adef-4c6f-8248-99e854a8849d/Screen_Shot_2022-09-05_at_5.11.04_PM.png&quot; alt=&quot;Screen Shot 2022-09-05 at 5.11.04 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;1. `feed.dart` 파일에 1번째 `StatelessWidget`을 클릭한 뒤 Quick Fix(window : `Ctrl + .` / macOS : `Cmd + .`)을 눌러 `cupertino`를 `import`해 주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/51b66047-25af-4cc8-8dbb-a83f6a9d8ff1/Untitled.png)

    33번째 줄에 빨간 줄이 있는 `Colors`을 선택한 뒤 Quick Fix(window : `Ctrl + .` / macOS : `Cmd + .`)을 눌러 `material`을 `import`하고 저장해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a9fbdbc5-bd6a-4247-84ed-bc2e63e527c4/Untitled.png)

    그럼 `feed.dart` 파일의 모든 에러가 해결됩니다.

2. `home_page.dart`에서 46번째 라인에 `Feed`도 Quick Fix(window : `Ctrl + .` / macOS : `Cmd + .`) 눌러서를 눌러서 `import` 한 뒤 저장해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4e2c84ae-94fe-4f2f-8470-5623941311fb/Untitled.png)

3. VSCode 우측 상단에 `Restart` 버튼을 눌러보면 정상적으로 작동하는 것을 볼 수 있습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b9202832-289a-47f5-bfcb-933c95484e81/Untitled.png)


&amp;lt;aside&amp;gt;
  이와 같이 기능을 변경하거나 추가하지 않고, 코드만 관리하기 쉽게 변경하는 과정을 리팩토링(refactoring)이라고 부릅니다.

더 큰 앱을 만들수록 코드의 복잡도가 올라가므로 주기적인 리팩토링을 통해 복잡도를 낮춰줘야 프로젝트가 손을 떠나지 않습니다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt; 전체 파일&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;home_page.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;home_page.dart&lt;/code&gt; 전체 파일&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;feed.dart&amp;#39;;

  class HomePage extends StatelessWidget {
    const HomePage({super.key}); // 생성자

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          elevation: 0.5,
          leading: Row(
            children: [
              SizedBox(width: 16),
              Text(
                &amp;#39;중앙동&amp;#39;,
                style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.bold,
                  fontSize: 20,
                ),
              ),
              Icon(
                Icons.keyboard_arrow_down_rounded,
                color: Colors.black,
              ),
            ],
          ),
          leadingWidth: 100,
          actions: [
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.search, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.menu_rounded, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.bell, color: Colors.black),
            ),
          ],
        ),
        body: Feed(),
        floatingActionButton: FloatingActionButton(
          onPressed: () {},
          backgroundColor: Color(0xFFFF7E36),
          elevation: 1,
          child: Icon(
            Icons.add_rounded,
            size: 36,
          ),
        ),
        bottomNavigationBar: BottomNavigationBar(
          fixedColor: Colors.black,
          unselectedItemColor: Colors.black,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home_filled),
              label: &amp;#39;홈&amp;#39;,
              backgroundColor: Colors.white,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.my_library_books_outlined),
              label: &amp;#39;동네생활&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.fmd_good_outlined),
              label: &amp;#39;내 근처&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.chat_bubble_2),
              label: &amp;#39;채팅&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.person_outline,
              ),
              label: &amp;#39;나의 당근&amp;#39;,
            ),
          ],
          currentIndex: 0,
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;feed.dart&lt;/code&gt; 전체 파일&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  class Feed extends StatelessWidget {
    const Feed({
      Key? key,
    }) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // CilpRRect 를 통해 이미지에 곡선 border 생성
          ClipRRect(
            borderRadius: BorderRadius.circular(8),
            // 이미지
            child: Image.network(
              &amp;#39;https://cdn2.thecatapi.com/images/6bt.jpg&amp;#39;,
              width: 100,
              height: 100,
              fit: BoxFit.cover,
            ),
          ),
          SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  &amp;#39;M1 아이패드 프로 11형(3세대) 와이파이 128G 팝니다.&amp;#39;,
                  style: TextStyle(
                    fontSize: 16,
                    color: Colors.black,
                  ),
                  softWrap: false,
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
                SizedBox(height: 2),
                Text(
                  &amp;#39;봉천동 · 6분 전&amp;#39;,
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.black45,
                  ),
                ),
                SizedBox(height: 4),
                Text(
                  &amp;#39;100만원&amp;#39;,
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Row(
                  children: [
                    Spacer(),
                    GestureDetector(
                      onTap: () {},
                      child: Row(
                        children: [
                          Icon(
                            CupertinoIcons.heart,
                            color: Colors.black54,
                            size: 16,
                          ),
                          Text(
                            &amp;#39;1&amp;#39;,
                            style: TextStyle(color: Colors.black54),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ],
            ),
          ),
        ],
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;05. 좋아요 구현하기 &amp;amp; 피드 리스트 만들기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 모습&lt;/p&gt;
  &lt;aside&gt;
    피드 리스트를 보여주고 및 특정 피드에 좋아요 표시를 할 수 있도록 만들어 봅시다.

&lt;p&gt;  (피드 리스트에서 좋아요를 누르는 기능은 실제 당근마켓 서비스에는 없는 기능이지만, StatefulWidget 의 연습을 위해 추가했습니다)&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eea98f78-7223-4329-b8ef-72b7a0a3ff7a/ezgif-4-89b3150141.gif&quot; alt=&quot;ezgif-4-89b3150141.gif&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) 좋아요 표시하기&lt;/p&gt;
  &lt;aside&gt;
    하트 아이콘 클릭 시 아이콘의 색상을 변경해보도록 하겠습니다.

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Feed&lt;/code&gt; 를 &lt;code&gt;StatefulWidget&lt;/code&gt;로 변환하기&lt;/p&gt;
&lt;aside&gt;
  `좋아요 여부`에 따라 화면을 갱신하려면 `StatefulWidget`으로 변경해야 합니다.

&lt;/aside&gt;

&lt;p&gt;&lt;code&gt;feed.dart&lt;/code&gt; 파일을 열고, 4번째 줄에 &lt;code&gt;StatelessWidget&lt;/code&gt;을 클릭한 뒤 Refactor(window : &lt;code&gt;ctrl + .&lt;/code&gt; / macOS : &lt;code&gt;Cmd + .&lt;/code&gt;)를 누르고 아래와 같은 팝업이 뜨면 &lt;code&gt;Convert to StatefulWidget&lt;/code&gt;을 선택한 뒤 저장해 주세요.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/73c925c0-a8ae-4440-9ed2-ad128c3821d1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;StatefulWidget&lt;/code&gt;이 되면서 상태를 관리하는 &lt;code&gt;_FeedState&lt;/code&gt; 클래스가 추가됩니다&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2e05c2e6-b6b9-4ee1-8b0d-2ef0e4c0f13d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;isFavorite&lt;/code&gt; 상태 추가하기&lt;/p&gt;
&lt;p&gt;코드스니펫을 복사해서 아래 이미지와 같이 13번째 줄에 맨 뒤에 추가하고 저장해주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] isFavorite 선언&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;     // 좋아요 여부
   bool isFavorite = false;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      `isFavorite` 변수는 좋아요 여부를 나타내는 상태 변수입니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6f1dfb5b-6a34-4547-8780-e3d5c769d989/Untitled.png)

- 3. 하트 버튼 클릭시 상태 변경하기

    &amp;lt;aside&amp;gt;
      버튼 클릭 이벤트를 코드의 어느 부분에서 받을 수 있는지 흐름을 잘 알고 있어야합니다.

    &amp;lt;/aside&amp;gt;

    하트 버튼을 클릭하는 경우 68번째 줄의 `onTap` 함수가 실행되므로, 코드스니펫을 복사해 68번째 라인에 `onTap` 함수의 중괄호 안에 붙여넣고 저장해 주세요.

    - **[코드스니펫] heart 버튼 클릭**

        ```dart

                                        // 화면 갱신
                        setState(() {
                          isFavorite = !isFavorite; // 좋아요 토글
                        });
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9795f8f4-1526-48af-9eb2-5b9d20ba6ffa/Untitled.png)

    아래와 같이 추가해주면, 클릭시 `isFavorite` 변수의 값이 반전되면서 화면이 갱신 됩니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f4926ae3-1283-4f5f-b43e-fac6b43260db/Untitled.png)

    &amp;lt;aside&amp;gt;
      클릭시 상태 변경하기 : 71번째 줄

    ```dart
    isFavorite = !isFavorite;
    ```

    - `!isFavorite` : `!`는 not의 의미로 `isFavorite`가 true라면 false로 반환 해줍니다.
    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      화면 갱신 : 71번째 줄을 감싸고 있는 70 ~ 72번째 줄

    ```dart
    setState(() {
        // 안쪽 코드 실행 후 화면 갱신
    })
    ```

    - `setState()` 호출시 StatefulWidget의 `build()`함수를 호출해 화면을 갱신시켜 줍니다.
    &amp;lt;/aside&amp;gt;

- 4. `isFavorite` 상태에 따라 하트 색상 바꾸기

    &amp;lt;aside&amp;gt;
      `isFavorite` 상태에 따라 하트의 색상을 바꾸고, 아이콘도 채워지도록 해보겠습니다.

    &amp;lt;/aside&amp;gt;

    코드스니펫을 복사해서 77, 78번째 라인을 지우고, 붙여 넣은 뒤 저장해주세요.

    - **[코드스니펫] heart 색상 변경**

        ```dart
                                  isFavorite
                                      ? CupertinoIcons.heart_fill
                                      : CupertinoIcons.heart,
                                  color: isFavorite ? Colors.pink : Colors.black,
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ed5e4af0-d244-48a1-81b4-48b02d1933a2/Untitled.png)

    &amp;lt;aside&amp;gt;
      위젯에 전달하는 값을 조건에 따라 다르게 보여줄 때 아래와 같이 한 줄 if문을 사용합니다.

    ```dart
    조건 ? 반환값1 : 반환값2
    ```

    - `조건`이 `true`인 경우 `반환값1`이 할당되고, `false`인 경우에는 `반환값2`가 할당됩니다.
    &amp;lt;/aside&amp;gt;

    에뮬레이터에서 `좋아요 버튼`을 누르면 버튼 색상이 바뀌는 것을 볼 수 있습니다.

    ![Screen Shot 2022-09-05 at 5.35.19 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3467f6ba-e7e8-4ec3-8269-e27cde5f38ea/Screen_Shot_2022-09-05_at_5.35.19_PM.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 피드 리스트 만들기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;ListView 위젯&lt;/p&gt;
&lt;aside&gt;
  `ListView` 위젯은 아래와 같이 동일한 레이아웃에 다른 정보를 보여줄 때 사용하면 유용합니다.
`SingleChildScrollView` + `Column`, 즉 **스크롤 가능한 `Column`** 이라고 생각하시면 편합니다.

&lt;/aside&gt;

&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7f1ec699-ad66-4f71-a2ae-e6ddfab58d32/list_view.png&quot; alt=&quot;출처 - [dribbble](https://dribbble.com/shots/5027549/attachments/5027549-Get-Wheels-List-Styles?mode=media)&quot;&gt;&lt;/p&gt;
&lt;p&gt;출처 - &lt;a href=&quot;https://dribbble.com/shots/5027549/attachments/5027549-Get-Wheels-List-Styles?mode=media&quot;&gt;dribbble&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;DartPad에서 ListView 위젯을 사용해 보도록 하겠습니다. 코드스니펫을 복사한 뒤 주소창에 붙여 넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=5aa6067a9ea6f34a6beca110cb882117&quot;&gt;[코드스니펫] DartPad ListView 학습&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/?id=5aa6067a9ea6f34a6beca110cb882117&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;        ![Screen Shot 2022-09-01 at 12.34.29 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cddba52c-f407-42fd-950f-7935e4185878/Screen_Shot_2022-09-01_at_12.34.29_PM.png)

        ```dart
        ListView(
            children: [
                Text(&amp;quot;0&amp;quot;),
                Text(&amp;quot;1&amp;quot;),
                Text(&amp;quot;2&amp;quot;),
                ...
            ]
        );
        ```

        &amp;lt;aside&amp;gt;
          위와 같이 `children`을 직접 작성할 수도 있지만 `ListView.builder()`를 이용하면 적은 코드로 `itemCount` 만큼 화면을 그릴 수 있습니다.

        ```dart
        ListView.builder(
          itemCount: 100, // 전체 아이템 개수
          itemBuilder: (context, index) { // index는 0 부터 99까지 증가
                return Text(&amp;quot;$index&amp;quot;); // 100번 실행
          }
        ),
        ```

        &amp;lt;/aside&amp;gt;

    - 코드스니펫을 복사한 뒤 주소창에 붙여 넣으면, DartPad로 접속합니다.
        - **[[코드스니펫] DartPad ListView.builder 학습](https://dartpad.dev/?id=fd5b0e646af4d9ebd0618b08111e0fd2)**

            ```dart
            https://dartpad.dev/?id=fd5b0e646af4d9ebd0618b08111e0fd2
            ```


        ![Screen Shot 2022-09-01 at 12.35.42 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b07a1849-7eeb-474f-9ff3-4be4f1c0b2bc/Screen_Shot_2022-09-01_at_12.35.42_PM.png)

- 2. `ListView.builder` 적용하기

    &amp;lt;aside&amp;gt;
      `ListView.builder` 를 활용해 원하는 개수만큼의 Feed 를 보여주도록 하겠습니다.

    &amp;lt;/aside&amp;gt;

    `home_page.dart` 의 48번째 줄에 있는 `Feed` 를 여러개 반복해서 보여주겠습니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/31511111-07c6-467d-bb7c-f44e3b22735b/Untitled.png)

    `Feed`를 마우스 우클릭하고 Refactor(window : `ctrl + .` / macOS : `Cmd + .`) 를 눌러줍니다. (왼쪽의 전구 아이콘을 눌러도 됩니다)

    `Wrap with Builder` 옵션을 선택해주세요.

    ![Screen Shot 2022-09-05 at 6.06.23 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cd5e1edf-1eed-447e-905d-368662fe21ef/Screen_Shot_2022-09-05_at_6.06.23_PM.png)

    48 번째 줄의 Builder 를 `ListView.builder` 로 바꿔줍니다.

    ![Screen Shot 2022-09-05 at 6.07.07 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c7f2d2d-a437-47c4-91ed-ac158ac334d1/Screen_Shot_2022-09-05_at_6.07.07_PM.png)

    50번째 줄 중괄호와 소괄호 사이에 쉼표를 추가해 정렬을 맞춰줍니다.

    `builder` 를 `itemBuilder` 로 바꾸고, context 뒤에 `index` 를 써줍니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0fa0906f-8d56-4f83-811d-5397709c4a4d/Untitled.png)

    저장하고 앱을 스크롤 해보면 피드가 여러 개로 늘어난 것을 보실 수 있습니다. (무한대로 늘어나 있습니다!)

    ![simulator_screenshot_5C2F3D9A-389E-408F-B16C-7B83FCB17424.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f259b857-661f-4f7a-95db-b3dd5f7d8a3f/simulator_screenshot_5C2F3D9A-389E-408F-B16C-7B83FCB17424.png)

    `ListView.builder` 에서는 itemCount 라는 속성을 조정해 자식 위젯이 그려지는 개수를 조절할 수 있습니다. 48번째 줄 맨 뒤에 아래 코드 스니펫을 추가해줍니다.

    - **[코드스니펫] itemCount**

        ```dart

        itemCount: 10,
        ```


    &amp;lt;aside&amp;gt;
      이제 딱 10개의 피드만 나타나는 것을 확인할 수 있습니다.

    &amp;lt;/aside&amp;gt;

- 3. `ListView.separated` 적용하기

    &amp;lt;aside&amp;gt;
      `ListView.separated` 를 활용해 각각의 Feed 사이에 `Divider` 를 추가해주겠습니다.

    &amp;lt;/aside&amp;gt;

    48 번째 줄의 builder 를 separated 로 바꿔줍니다. 

    ![Screen Shot 2022-09-05 at 6.09.55 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/79ee2909-3ea9-4350-8ac3-d904879ea1d6/Screen_Shot_2022-09-05_at_6.09.55_PM.png)

    52번째 줄 맨 뒤에 아래 코드스니펫을 복사한 후 붙여넣어줍니다.

    - **[코드스니펫] separatorBuilder**

        ```dart
                        separatorBuilder: (context, index) {
                  return Divider();
                },
        ```


    ![Screen Shot 2022-09-05 at 6.12.01 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9205206c-b0a3-4a3e-ba19-6e2931add851/Screen_Shot_2022-09-05_at_6.12.01_PM.png)

    &amp;lt;aside&amp;gt;
      각각의 요소 사이에 `Divider` (구분선) 이 추가된 것을 볼 수 있습니다.

    &amp;lt;/aside&amp;gt;

- 4. Padding 추가하기

    &amp;lt;aside&amp;gt;
      `Padding` 을 조절해 Feed 를 보기 좋게 수정해봅시다.

    &amp;lt;/aside&amp;gt;

    48번째 줄의 ListView 를 마우스 우클릭하고 Refactor (window : `ctrl + .` / macOS : `Cmd + .`) 를 눌러줍니다. 

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e52227d3-1cdd-4b0c-9aa8-b2a9c70c845f/Untitled.png)

    `Wrap with Padding` 을 선택해주세요.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5703917d-08e8-4a5d-b3cb-545b11f30705/Untitled.png)

    49번째 줄을 지우고 아래 코드스니펫을 붙여넣어주세요

    - **[코드스니펫] ListView horizontal padding**

        ```dart
        padding: const EdgeInsets.symmetric(horizontal: 16),
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1f15c1a7-019c-4c74-867e-0d59f9e681d2/Untitled.png)

    &amp;lt;aside&amp;gt;
      가로 방향 패딩이 추가된 것을 볼 수 있습니다.

    &amp;lt;/aside&amp;gt;

    53번째 `Feed()`를 마우스 우클릭하고 Refactor (window : `ctrl + .` / macOS : `Cmd + .`) 를 눌러줍니다. 

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/45157278-0715-4ed4-b70f-0e4f2f430c21/Untitled.png)

    `Wrap with Padding` 을 선택합니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ad055937-e596-4952-ac8c-3a2959d74ada/Untitled.png)

    54번째 줄을 지우고 아래 코드스니펫을 붙여넣어주세요

    - **[코드스니펫] Feed vertical padding**

        ```dart
        padding: const EdgeInsets.symmetric(vertical: 12),
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/07a5143f-c971-4029-8b0b-3db297ea8547/Untitled.png)

    &amp;lt;aside&amp;gt;
      이제 각 피드 사이의 간격도 보기 좋게 설정되었습니다.

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;home_page.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;home_page.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;feed.dart&amp;#39;;

  class HomePage extends StatelessWidget {
    const HomePage({super.key}); // 생성자

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          elevation: 0.5,
          leading: Row(
            children: [
              SizedBox(width: 16),
              Text(
                &amp;#39;중앙동&amp;#39;,
                style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.bold,
                  fontSize: 20,
                ),
              ),
              Icon(
                Icons.keyboard_arrow_down_rounded,
                color: Colors.black,
              ),
            ],
          ),
          leadingWidth: 100,
          actions: [
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.search, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.menu_rounded, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.bell, color: Colors.black),
            ),
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: ListView.separated(
            itemCount: 10,
            itemBuilder: (context, index) {
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 12),
                child: Feed(),
              );
            },
            separatorBuilder: (context, index) {
              return Divider();
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {},
          backgroundColor: Color(0xFFFF7E36),
          elevation: 1,
          child: Icon(
            Icons.add_rounded,
            size: 36,
          ),
        ),
        bottomNavigationBar: BottomNavigationBar(
          fixedColor: Colors.black,
          unselectedItemColor: Colors.black,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home_filled),
              label: &amp;#39;홈&amp;#39;,
              backgroundColor: Colors.white,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.my_library_books_outlined),
              label: &amp;#39;동네생활&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.fmd_good_outlined),
              label: &amp;#39;내 근처&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.chat_bubble_2),
              label: &amp;#39;채팅&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.person_outline,
              ),
              label: &amp;#39;나의 당근&amp;#39;,
            ),
          ],
          currentIndex: 0,
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;feed.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  class Feed extends StatefulWidget {
    const Feed({
      Key? key,
    }) : super(key: key);

    @override
    State&amp;lt;Feed&amp;gt; createState() =&amp;gt; _FeedState();
  }

  class _FeedState extends State&amp;lt;Feed&amp;gt; {
    // 좋아요 여부
    bool isFavorite = false;

    @override
    Widget build(BuildContext context) {
      return Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // CilpRRect 를 통해 이미지에 곡선 border 생성
          ClipRRect(
            borderRadius: BorderRadius.circular(8),
            // 이미지
            child: Image.network(
              &amp;#39;https://cdn2.thecatapi.com/images/6bt.jpg&amp;#39;,
              width: 100,
              height: 100,
              fit: BoxFit.cover,
            ),
          ),
          SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  &amp;#39;M1 아이패드 프로 11형(3세대) 와이파이 128G 팝니다.&amp;#39;,
                  style: TextStyle(
                    fontSize: 16,
                    color: Colors.black,
                  ),
                  softWrap: false,
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
                SizedBox(height: 2),
                Text(
                  &amp;#39;봉천동 · 6분 전&amp;#39;,
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.black45,
                  ),
                ),
                SizedBox(height: 4),
                Text(
                  &amp;#39;100만원&amp;#39;,
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Row(
                  children: [
                    Spacer(),
                    GestureDetector(
                      onTap: () {
                        // 화면 갱신
                        setState(() {
                          isFavorite = !isFavorite; // 좋아요 토글
                        });
                      },
                      child: Row(
                        children: [
                          Icon(
                            isFavorite
                                ? CupertinoIcons.heart_fill
                                : CupertinoIcons.heart,
                            color: isFavorite ? Colors.pink : Colors.black,
                            size: 16,
                          ),
                          Text(
                            &amp;#39;1&amp;#39;,
                            style: TextStyle(color: Colors.black54),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ],
            ),
          ),
        ],
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;06. 피드마다 각각 다른 이미지 보여주기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 모습&lt;/p&gt;
  &lt;aside&gt;
    각 피드에 다른 사진을 보여주도록 하겠습니다.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/36688fd9-62ac-4bc3-882b-73feb0487aee/simulator_screenshot_12C862BD-B8FD-4784-AAE1-355E352AAC09.png&quot; alt=&quot;simulator_screenshot_12C862BD-B8FD-4784-AAE1-355E352AAC09.png&quot;&gt;&lt;/p&gt;
  &lt;aside&gt;
    기존에는 `feed.dart` 파일에 27번째 줄을 보면 이미지 URL이 고정되어 있기 때문에 모든 피드에서 동일한 사진이 나옵니다.

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c99a8313-8b41-4fcc-a564-e67067dd7e36/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
  &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) Feed 위젯 수정하기&lt;/p&gt;
  &lt;aside&gt;
    Feed 위젯의 이미지를 외부에서 전달 받아 보여주도록 만들어봅시다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;feed.dart&lt;/code&gt; 파일에 8번째 줄에 아래 코드를 붙여 넣어 이미지 URL을 담을 변수를 만들어 줍니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] feed.dart / imageUrl 변수 선언&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
  final String imageUrl; // 이미지를 담을 변수
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;aside&amp;gt;
      `imageUrl`은 한 번 전달받은 뒤 변경되지 않기 때문에 앞에 `final` 키워드를 붙여줍니다.

    &amp;lt;/aside&amp;gt;

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1f175007-d9de-4928-9688-e3ab69c401fd/Untitled.png)

2. Feed 위젯의 생성자를 호출 할 때, `imageUrl`을 전달받아 방금 생성한 변수에 할당하도록 만들어주겠습니다. 아래 코드스니펫을 복사해서 6번째 줄에 붙여 넣어 주세요.
    - **[코드스니펫] feed.dart / imageUrl 변수 주입**

        ```dart

            required this.imageUrl,
        ```


    &amp;lt;aside&amp;gt;
      `required` : 필수 전달 매개 변수로 만들어줍니다.
    `this.imageUrl` : 많은 Feed 인스턴스 중 현재 인스턴스의 `imageUrl`

    &amp;lt;/aside&amp;gt;

    ![_constructor.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/95dcd08e-baed-4513-a9e0-d237452aa3fa/_constructor.png)

3. 전달 받은 `imageUrl`을 화면에 보여줍시다.

    아래 코드 스니펫을 복사해서 30번째 줄을 지우고, 붙여 넣은 뒤 저장해주세요.

    - **[코드스니펫] feed.dart / imageUrl 사용하기**

        ```dart
        widget.imageUrl, // 10번째 줄의 imageUrl 가져오기
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e9115cd0-cde3-4593-9e57-4375901aadba/Untitled.png)

    &amp;lt;aside&amp;gt;
      **StatefulWidget**의 상태 클래스에서 `widget.변수명`으로 전달받은 변수에 접근할 수 있습니다.

    ![_stateful.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b8ea0dec-ab36-4580-8ca0-3837737e5e8d/_stateful.png)

    &amp;lt;/aside&amp;gt;


&amp;lt;aside&amp;gt;
  Feed 위젯 이미지 url를 전달 받을 준비가 완료되었습니다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 데이터 전달하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;화면에 보여줄 데이터를 HomePage 위젯에 추가해 봅시다.&lt;/p&gt;
&lt;p&gt; 아래 코드스니펫을 복사해 &lt;code&gt;home_page.dart&lt;/code&gt; 파일에 10번째 줄 맨 뒤에 붙여 넣어주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] home_page.dart / images 추가&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      final List&amp;lt;String&amp;gt; images = [
        &amp;quot;https://cdn2.thecatapi.com/images/6bt.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/ahr.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/arj.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/brt.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/cml.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/e35.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/MTk4MTAxOQ.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/MjA0ODM5MQ.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/AuY1uMdmi.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/AKUofzZW_.png&amp;quot;,
      ];&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2775e36c-ea68-4761-98de-75d224e42751/Untitled.png)

2. `images`의 개수만큼 Feed를 보여주도록 수정해봅시다. 63번째 줄의 `itemCount : 10,` 을 삭제하고 아래 코드스니펫을 붙여 넣어주세요.
    - **[코드스니펫] home_page.dart /  itemCount**

        ```dart
        itemCount: images.length, // 이미지 개수만큼 보여주기
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0f4b75d9-4a54-4a25-9b32-219174878609/Untitled.png)

3. `Feed` 위젯에 `index`에 해당하는 image를 전달해 봅시다. 

    먼저 이미지 url 1개를 `image` 라는 이름의 변수에 저장합니다. 64번째 줄 맨 뒤에 아래 코드스니펫을 붙여 넣어주세요.

    - **[코드스니펫] home_page.dart /  image 변수 선언**

        ```dart

        final image = images[index]; // index에 해당하는 이미지
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b0a9d915-ae70-4661-b16b-f071a6c59539/Untitled.png)

    `Feed`의 `imageUrl`에 image 변수에 저장된 url 을 넘겨줍니다. 68번째 줄을 지우고 아래 코드스니페을 붙여 넣어주세요.

    - **[코드스니펫] home_page.dart /  Feed 에 imageUrl 전달**

        ```dart
        child: Feed(imageUrl: image), // imageUrl 전달
        ```


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e557d4c3-5e3b-442c-857e-b169e9081995/Untitled.png)

4. 그리고 저장해주면 완성!

    ![Simulator Screen Shot - iPhone 13 - 2022-09-05 at 19.23.13.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4f389f36-c316-4845-a6af-e070678beebb/Simulator_Screen_Shot_-_iPhone_13_-_2022-09-05_at_19.23.13.png)

    &amp;lt;aside&amp;gt;
      개발자들은 데이터를 보여주는 껍데기를 뷰(View)라고 부릅니다.
    View와 데이터를 분리하면 View를 재활용 할 수 있습니다.

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 코드&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;main.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;home_page.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;home_page.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  import &amp;#39;feed.dart&amp;#39;;

  class HomePage extends StatelessWidget {
    const HomePage({super.key}); // 생성자

    @override
    Widget build(BuildContext context) {
      final List&amp;lt;String&amp;gt; images = [
        &amp;quot;https://cdn2.thecatapi.com/images/6bt.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/ahr.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/arj.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/brt.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/cml.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/e35.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/MTk4MTAxOQ.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/MjA0ODM5MQ.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/AuY1uMdmi.jpg&amp;quot;,
        &amp;quot;https://cdn2.thecatapi.com/images/AKUofzZW_.png&amp;quot;,
      ];
      return Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          elevation: 0.5,
          leading: Row(
            children: [
              SizedBox(width: 16),
              Text(
                &amp;#39;중앙동&amp;#39;,
                style: TextStyle(
                  color: Colors.black,
                  fontWeight: FontWeight.bold,
                  fontSize: 20,
                ),
              ),
              Icon(
                Icons.keyboard_arrow_down_rounded,
                color: Colors.black,
              ),
            ],
          ),
          leadingWidth: 100,
          actions: [
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.search, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.menu_rounded, color: Colors.black),
            ),
            IconButton(
              onPressed: () {},
              icon: Icon(CupertinoIcons.bell, color: Colors.black),
            ),
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16),
          child: ListView.separated(
            itemCount: images.length, // 이미지 개수만큼 보여주기
            itemBuilder: (context, index) {
              final image = images[index]; // index에 해당하는 이미지
              return Padding(
                padding: const EdgeInsets.symmetric(vertical: 12),
                child: Feed(imageUrl: image), // imageUrl 전달
              );
            },
            separatorBuilder: (context, index) {
              return Divider();
            },
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {},
          backgroundColor: Color(0xFFFF7E36),
          elevation: 1,
          child: Icon(
            Icons.add_rounded,
            size: 36,
          ),
        ),
        bottomNavigationBar: BottomNavigationBar(
          fixedColor: Colors.black,
          unselectedItemColor: Colors.black,
          showUnselectedLabels: true,
          selectedFontSize: 12,
          unselectedFontSize: 12,
          iconSize: 28,
          type: BottomNavigationBarType.fixed,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home_filled),
              label: &amp;#39;홈&amp;#39;,
              backgroundColor: Colors.white,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.my_library_books_outlined),
              label: &amp;#39;동네생활&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.fmd_good_outlined),
              label: &amp;#39;내 근처&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(CupertinoIcons.chat_bubble_2),
              label: &amp;#39;채팅&amp;#39;,
            ),
            BottomNavigationBarItem(
              icon: Icon(
                Icons.person_outline,
              ),
              label: &amp;#39;나의 당근&amp;#39;,
            ),
          ],
          currentIndex: 0,
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;feed.dart&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/cupertino.dart&amp;#39;;
  import &amp;#39;package:flutter/material.dart&amp;#39;;

  class Feed extends StatefulWidget {
    const Feed({
      Key? key,
      required this.imageUrl,
    }) : super(key: key);

    final String imageUrl; // 이미지를 담을 변수

    @override
    State&amp;lt;Feed&amp;gt; createState() =&amp;gt; _FeedState();
  }

  class _FeedState extends State&amp;lt;Feed&amp;gt; {
    // 좋아요 여부
    bool isFavorite = false;

    @override
    Widget build(BuildContext context) {
      return Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          // CilpRRect 를 통해 이미지에 곡선 border 생성
          ClipRRect(
            borderRadius: BorderRadius.circular(8),
            // 이미지
            child: Image.network(
              widget.imageUrl, // 10번째 줄의 imageUrl 가져오기
              width: 100,
              height: 100,
              fit: BoxFit.cover,
            ),
          ),
          SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  &amp;#39;M1 아이패드 프로 11형(3세대) 와이파이 128G 팝니다.&amp;#39;,
                  style: TextStyle(
                    fontSize: 16,
                    color: Colors.black,
                  ),
                  softWrap: false,
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
                SizedBox(height: 2),
                Text(
                  &amp;#39;봉천동 · 6분 전&amp;#39;,
                  style: TextStyle(
                    fontSize: 12,
                    color: Colors.black45,
                  ),
                ),
                SizedBox(height: 4),
                Text(
                  &amp;#39;100만원&amp;#39;,
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Row(
                  children: [
                    Spacer(),
                    GestureDetector(
                      onTap: () {
                        // 화면 갱신
                        setState(() {
                          isFavorite = !isFavorite; // 좋아요 토글
                        });
                      },
                      child: Row(
                        children: [
                          Icon(
                            isFavorite
                                ? CupertinoIcons.heart_fill
                                : CupertinoIcons.heart,
                            color: isFavorite ? Colors.pink : Colors.black,
                            size: 16,
                          ),
                          Text(
                            &amp;#39;1&amp;#39;,
                            style: TextStyle(color: Colors.black54),
                          ),
                        ],
                      ),
                    )
                  ],
                ),
              ],
            ),
          ),
        ],
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;07. 숙제 - Shazam 클론 코딩&lt;/h2&gt;
&lt;aside&gt;
  클론 코딩은 실제 서비스를 따라 만들면서 배우는 공부 방식입니다.
노래찾기 앱 [Shazam](https://www.shazam.com/ko/home) 의 레이아웃을 따라 만들어봅시다.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 프로젝트 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt; 버튼을 클릭해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Command Palette` 기능은 앞으로도 자주 사용하게 될 거라 단축키를 알아두시면 좋습니다.
 window : `Ctrl + Shift + P`
 macOS : `Cmd + Shift + P`

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2e8bac1-770e-4f78-80a8-5b799f1f40e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;명령어를 검색하는 팝업창이 뜨면, &lt;code&gt;flutter&lt;/code&gt;라고 입력한 뒤 &lt;code&gt;Flutter: New Project&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a6058de-bbf7-410f-9dc3-a96d6bd7ac58/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Application&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c7145f5-c6f6-4306-b78e-e3bb31e24710/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트를 시작할 폴더를 선택하는 과정입니다. 미리 생성해 둔 &lt;code&gt;flutter&lt;/code&gt; 폴더를 선택한 뒤 &lt;code&gt;Select a folder to create the project in&lt;/code&gt; 버튼을 눌러 주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트 이름을 &lt;code&gt;shazam&lt;/code&gt; 으로 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/543a9b92-a26b-4912-a290-4cb63a03ccc3/Screen_Shot_2022-04-16_at_11.22.52_PM.png&quot; alt=&quot;Screen Shot 2022-04-16 at 11.22.52 PM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 만약 중간에 아래와 같은 팝업이 뜬다면, 체크박스를 선택한 뒤 파란 버튼을 클릭해주세요.&lt;/p&gt;
 &lt;aside&gt;
   아래 팝업에 대한 자세한 사항은 [링크](https://stackoverflow.com/questions/67914668/vs-code-do-you-trust-the-authors-of-the-files-in-this-folder)를 참고해주세요.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6827f86-f59a-4808-9d8d-75245b9ec10d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음과 같이 프로젝트가 생성됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/109be7a6-5b01-462c-8798-e1105a9ffde9/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 &lt;code&gt;main.dart&lt;/code&gt; 코드스니펫을 복사해서 기존 코드를 모두 지운 뒤, &lt;code&gt;main.dart&lt;/code&gt; 파일에 붙여 넣고 저장해주세요.&lt;/p&gt;
 &lt;aside&gt;
   저장 단축키

&lt;p&gt; window : &lt;code&gt;Ctrl + S&lt;/code&gt;&lt;br&gt; macOS : &lt;code&gt;Cmd + S&lt;/code&gt;&lt;/p&gt;
 &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫]  main.dart&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(const MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    // This widget is the root of your application.
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        title: &amp;#39;Shazam&amp;#39;,
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: HomePage(),
      );
    }
  }

  class HomePage extends StatefulWidget {
    const HomePage({Key? key}) : super(key: key);

    @override
    State&amp;lt;HomePage&amp;gt; createState() =&amp;gt; _HomePageState();
  }

  class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
    @override
    Widget build(BuildContext context) {
      return DefaultTabController(
        initialIndex: 1,
        length: 3,
        child: Builder(builder: (context) {
          DefaultTabController.of(context)?.addListener(() {
            setState(() {});
          });

          return Scaffold(
            body: Stack(
              children: [
                TabBarView(
                  children: [
                    FirstTab(),
                    SecondTab(),
                    ThirdTab(),
                  ],
                ),
                SafeArea(
                  child: Padding(
                    padding:
                        const EdgeInsets.symmetric(vertical: 20, horizontal: 16),
                    child: Column(
                      children: [
                        Container(
                          alignment: Alignment.topCenter,
                          child: TabPageSelector(
                            color: DefaultTabController.of(context)?.index == 1
                                ? Colors.blue[300]
                                : Colors.grey[400],
                            selectedColor:
                                DefaultTabController.of(context)?.index == 1
                                    ? Colors.white
                                    : Colors.blue,
                            indicatorSize: 8,
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          );
        }),
      );
    }
  }

  // 첫번째 페이지
  class FirstTab extends StatelessWidget {
    const FirstTab({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      const songs = [
        {
          &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg&amp;#39;,
          &amp;#39;title&amp;#39;: &amp;#39;가을밤에 든 생각&amp;#39;,
          &amp;#39;artist&amp;#39;: &amp;#39;잔나비&amp;#39;,
        },
        {
          &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg&amp;#39;,
          &amp;#39;title&amp;#39;: &amp;#39;가을밤에 든 생각&amp;#39;,
          &amp;#39;artist&amp;#39;: &amp;#39;잔나비&amp;#39;,
        },
        {
          &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg&amp;#39;,
          &amp;#39;title&amp;#39;: &amp;#39;가을밤에 든 생각&amp;#39;,
          &amp;#39;artist&amp;#39;: &amp;#39;잔나비&amp;#39;,
        },
        {
          &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg&amp;#39;,
          &amp;#39;title&amp;#39;: &amp;#39;가을밤에 든 생각&amp;#39;,
          &amp;#39;artist&amp;#39;: &amp;#39;잔나비&amp;#39;,
        },
        {
          &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg&amp;#39;,
          &amp;#39;title&amp;#39;: &amp;#39;가을밤에 든 생각&amp;#39;,
          &amp;#39;artist&amp;#39;: &amp;#39;잔나비&amp;#39;,
        },
        {
          &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ytimg.com/vi/jAO0KXRdz_4/hqdefault.jpg&amp;#39;,
          &amp;#39;title&amp;#39;: &amp;#39;가을밤에 든 생각&amp;#39;,
          &amp;#39;artist&amp;#39;: &amp;#39;잔나비&amp;#39;,
        },
      ];

      return Center(child: Text(&amp;#39;첫번째 페이지&amp;#39;));
    }
  }

  // 두번째 페이지
  class SecondTab extends StatelessWidget {
    const SecondTab({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return Center(child: Text(&amp;#39;두번째 페이지&amp;#39;));
    }
  }

  // 세번째 페이지
  class ThirdTab extends StatelessWidget {
    const ThirdTab({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      const chartData = {
        &amp;#39;korea&amp;#39;: [
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
        ],
        &amp;#39;global&amp;#39;: [
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
        ],
        &amp;#39;newyork&amp;#39;: [
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
          {
            &amp;#39;imageUrl&amp;#39;: &amp;#39;https://i.ibb.co/xf2HpfG/dynamite.jpg&amp;#39;,
            &amp;#39;name&amp;#39;: &amp;#39;Dynamite&amp;#39;,
            &amp;#39;artist&amp;#39;: &amp;#39;BTS&amp;#39;,
          },
        ],
      };

      return Center(child: Text(&amp;#39;세번째 페이지&amp;#39;));
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음으로 학습 단계에서 불필요한 내용을 화면에 표시하지 않도록 설정해 주겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;analysis_options.yaml&lt;/code&gt; 파일을 열고, 아래 코드스니펫을 복사해서 24번째 라인 뒤에 붙여 넣고 저장해 주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] analysis_options.yaml&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      prefer_const_constructors: false
      prefer_const_literals_to_create_immutables: false&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8470956b-ccc2-4d75-86d8-83042bafaf22/Untitled.png)

    &amp;lt;aside&amp;gt;
      상세 내용은 아래를 참고해 주세요.

    - 어떤 의미인지 궁금하신 분들을 위해

        `main.dart` 파일을 열어보시면 파란 실선이 있습니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d1393a08-a362-4be3-bd9b-835e184f5ef3/Untitled.png)

        파란 줄은, 개선할 여지가 있는 부분을 VSCode가 알려주는 표시입니다.

        12번째 라인에 마우스를 올리면 아래와 같이 설명이 보입니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7fd5cee4-d935-4d2d-9c35-e11d4f3c6cae/Untitled.png)

        위젯이 변경될 일이 없기 때문에 `const`라는 키워드를 앞에 붙여 상수(변하지 않는 수)로 선언하라는 힌트입니다. 상수로 만들면 화면을 새로고침 할 때, 상수로 선언된 위젯들은 새로고침을 할 필요가 없어서 성능상 이점이 있습니다.

        아래와 같이 `Icon`앞에 `const` 키워드를 붙여주시면 됩니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2aef9ee8-c4ef-4c42-bd08-d358c4620341/Untitled.png)

        지금은 학습 단계이니 눈에 띄지 않도록 `analysis_options.yaml` 파일에 아래와 같이 설정하여 힌트를 숨기도록 하겠습니다.

        ```dart
            prefer_const_constructors: false
            prefer_const_literals_to_create_immutables: false
        ```

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 구현 목표&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;작동 영상&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e192803-51b6-415b-ad13-bb8f9f9c45ce/Simulator_Screen_Recording_-_iPhone_13_-_2022-04-17_at_14.30.01.mp4&quot;&gt;Simulator Screen Recording - iPhone 13 - 2022-04-17 at 14.30.01.mp4&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/69d7b3a1-e116-4bb9-a911-80fe461d4f50/Untitled.png)

&amp;lt;aside&amp;gt;
  만들고 싶은 페이지를 선택해서 정답을 참고하며 구현해 보세요.

&amp;lt;/aside&amp;gt;

|  | 첫 번째 페이지 | 두 번째 페이지 | 세 번째 페이지 |
| --- | --- | --- | --- |
| 난이도 | ⭐️⭐️⭐️ | ⭐️ | ⭐️⭐️ |&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;숙제 답안&lt;/p&gt;
  &lt;aside&gt;
    아래 링크를 클릭해서 DartPad에서 답안을 확인해 보세요.

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/?id=4c1bef6b01b3180e964fdec5ffcaf2bb&quot;&gt;[DartPad] Shazam 완성본 보기&lt;/a&gt;&lt;/strong&gt;&lt;/aside&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;이전 강의 &lt;code&gt;바로가기&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/1-Flutter-Dart-bea66b4d59324574a00600a3de276905&quot;&gt;1주차 - Flutter 앱 개발 맛보기 &amp;amp; Dart 문법 익히기&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;code&gt;다음 강의 바로가기&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/3-0816f43b8ec244fea37220b2456ca62c&quot;&gt;3주차 - 패키지 사용법 익히기 &amp;amp; 앱의 기능 만들기 (마이메모)&lt;/a&gt; &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Copyright ⓒ TeamSparta All rights reserved.&lt;/p&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/970</guid>
      <comments>https://comphy.tistory.com/970#entry970comment</comments>
      <pubDate>Wed, 12 Apr 2023 20:42:48 +0900</pubDate>
    </item>
    <item>
      <title>[1주차_-_Flutter_앱_개발_맛보기__Dart_문법_익히기.pdf]</title>
      <link>https://comphy.tistory.com/969</link>
      <description>&lt;h1&gt;1주차 - Flutter 앱 개발 맛보기 &amp;amp; Dart 문법 익히기&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/611f3802-d119-4e8c-b35e-58f87c3a5e02/1%EC%A3%BC%EC%B0%A8_-_Flutter_%EC%95%B1_%EA%B0%9C%EB%B0%9C_%EB%A7%9B%EB%B3%B4%EA%B8%B0__Dart_%EB%AC%B8%EB%B2%95_%EC%9D%B5%ED%9E%88%EA%B8%B0.pdf&quot;&gt;1주차&lt;em&gt;-&lt;/em&gt;Flutter_앱_개발_맛보기__Dart_문법_익히기.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;단축키 모음&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;새로고침 &lt;code&gt;F5&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;저장&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;전체선택&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;A&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;잘라내기&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;X&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;콘솔창 줄바꿈&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;enter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;코드정렬&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;Alt&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;option&lt;/code&gt; + &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;L&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;들여쓰기&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;들여쓰기 취소 : &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;Tab&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;주석&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;command&lt;/code&gt; + &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flutter 앱 개발 과정 이해하기&lt;/li&gt;
&lt;li&gt;VSCode와 친해지기&lt;/li&gt;
&lt;li&gt;Dart 문법 이해하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;01. Why Flutter&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 앱 개발 방법&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;네이티브 앱(Native App)&lt;/p&gt;
  &lt;aside&gt;
  ✔️ 모바일 기기는 Android와 iOS 두가지로 나뉘어 있습니다

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Android&lt;/p&gt;
&lt;p&gt;  → Google에서 제공하는 Android SDK(Software Development Kit)를 이용하여 개발&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프로그래밍 언어 : &lt;code&gt;Java / Kotlin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;개발 툴 : &lt;code&gt;Android Studio&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;iOS&lt;/p&gt;
&lt;p&gt;  → Apple에서 제공하는 iOS SDK(Software Development Kit)를 이용하여 개발&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프로그래밍 언어 : &lt;code&gt;Objective-C / Swift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;개발 툴 : &lt;code&gt;XCode&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;특이사항 : macOS 에서만 개발이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt;
  네이티브 앱은, 하나의 앱을 Android와 iOS를 각각의 개발 방법에 따라, 
개별로 `2 번`만드는 방법입니다.

&lt;p&gt;다른 언어로 두 번 앱을 제작해야 하는 단점이 있지만,&lt;br&gt;성능이 가장 빠르고 최신 기능을 빠르게 적용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;ex) 최신 기능을 적용한 사례 - &lt;a href=&quot;https://apps.apple.com/kr/app/photo-widget-simple-%ED%8F%AC%ED%86%A0%EC%9C%84%EC%A0%AF/id1530149106&quot;&gt;포토위젯&lt;/a&gt; / &lt;a href=&quot;https://paxnetnews.com/articles/71383&quot;&gt;기사&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;

&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/24b8cf19-994a-475f-8286-085fae602e3b/Untitled.png&quot; alt=&quot;출처 - [지식iN 앱을 Flutter로 개발하는 이유](https://d2.naver.com/helloworld/3384599)&quot;&gt;&lt;/p&gt;
&lt;p&gt;출처 - &lt;a href=&quot;https://d2.naver.com/helloworld/3384599&quot;&gt;지식iN 앱을 Flutter로 개발하는 이유&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;크로스 플랫폼 앱(Cross Platform App)&lt;/p&gt;
  &lt;aside&gt;
  ✔️ 크로스 플랫폼 앱은, **하나의** 프로그래밍 언어와 소스코드로 
  **Android와 iOS를 모두 개발하는 방법입니다.**

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;React Native&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프로그래밍 언어 : &lt;code&gt;JavaScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;페이스북에서 출시한 오픈 소스 모바일 애플리케이션 프레임 워크&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Flutter&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;프로그래밍 언어 : &lt;code&gt;Dart&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;구글에서 출시한 오픈 소스 모바일 애플리케이션 프레임 워크&lt;/li&gt;
&lt;/ul&gt;
&lt;aside&gt;
  네이티브 앱의 성능을 뛰어넘을 순 없지만, 
하나의 언어로 두 플랫폼에서 모두 실행되는 앱을 **빠르게 만들 수 있습니다.**

&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 왜 크로스 플랫폼을 사용할까요?&lt;/p&gt;
&lt;p&gt;  &lt;strong&gt;  생산성이 월등하다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d51dbc5b-137e-4773-9595-21ebb1617f97/Untitled.png&quot; alt=&quot;[출처 - 네이버 지식iN 앱을 Flutter로 만든 이유](https://d2.naver.com/helloworld/3384599)코&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://d2.naver.com/helloworld/3384599&quot;&gt;출처 - 네이버 지식iN 앱을 Flutter로 만든 이유&lt;/a&gt;코&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 왜 Flutter를 사용할까요?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;  Flutter가 훨씬 핫하다 (React Native 대비. 커뮤니티 &amp;amp; 자료 ⬆)&lt;/p&gt;
&lt;p&gt; → 개발을 할 때 훨씬 편하게 찾아볼 수 있다는 뜻입니다 :)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5b7f3443-bb00-4469-b9d2-1a1db38319ad/Untitled.png&quot; alt=&quot;출처 - [getstream.io](https://getstream.io/blog/flutter-vs-react-native-the-ultimate-comparison/)&quot;&gt;&lt;/p&gt;
&lt;p&gt; 출처 - &lt;a href=&quot;https://getstream.io/blog/flutter-vs-react-native-the-ultimate-comparison/&quot;&gt;getstream.io&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;  공식 문서가 잘 되어 있다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/714c420a-8ad8-403c-957b-5b1186fd1c67/Untitled.png&quot; alt=&quot;[https://docs.flutter.dev/](https://docs.flutter.dev/)&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;a href=&quot;https://docs.flutter.dev/&quot;&gt;https://docs.flutter.dev/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;  성능이 뛰어나다&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1864162f-6d0e-4826-bc7e-7c80e085a122/Untitled.png&quot; alt=&quot;출처 - [Flutter vs Native vs React-Native: Examining performance](https://medium.com/swlh/flutter-vs-native-vs-react-native-examining-performance-31338f081980)&quot;&gt;&lt;/p&gt;
&lt;p&gt; 출처 - &lt;a href=&quot;https://medium.com/swlh/flutter-vs-native-vs-react-native-examining-performance-31338f081980&quot;&gt;Flutter vs Native vs React-Native: Examining performance&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;02. 설치하기&lt;/h2&gt;
&lt;aside&gt;
  **Flutter**는 **Android와 iOS를 하나의 코드로** 모두 만들 수 있습니다 :)
그래서 Android와 iOS를 따로 개발하는 것 보다는 훨씬 간편하겠죠? ㅎㅎ 
**단, 처음에 환경 설정을 하는데  시간이 조금 걸릴 수 있어요! 수업 전 미리 설치해봅시다~**

&lt;p&gt;&lt;strong&gt;[꿀팁!] 설치할 프로그램 한 줄 요약!&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Flutter : Android와 iOS 앱을 하나의 코드로 구현할 수 있도록 도와주는 프레임워크&lt;/li&gt;
&lt;li&gt;VSCode : 코드를 작성할 때 사용하는 에디터&lt;/li&gt;
&lt;li&gt;Android Studio : &lt;strong&gt;Android 앱&lt;/strong&gt;을 개발하기 위해 필요한 IDE. 에뮬레이터 포함&lt;/li&gt;
&lt;li&gt;Xcode : &lt;strong&gt;iOS 앱&lt;/strong&gt;을 개발하기 위해 필요한 IDE. 에뮬레이터 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;Windows 사용자를 위한 프로그램 설치 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;- [잠깐] 혹시 Windows 7를 쓰신다면 아래 가이드를 따라주세요 :)
    - [링크](https://www.microsoft.com/ko-kr/p/powershell/9mz1snwt0n5d?activetab=pivot:overviewtab)에 접속하셔서 Power Shell을 설치해주세요.

**[프레임워크 설치하기]**

- 1. Flutter 설치하기

    &amp;lt;aside&amp;gt;
      Flutter 설치 과정은 일반적인 프로그램 설치와는 조금 다릅니다. 일반적으로는 `.exe` 파일등을 실행해 프로그램을 설치하는데요.

    Flutter 설치는 아래와 같은 과정으로 진행됩니다.

    1. zip 파일을 다운로드 받아서 적절한 경로에 압축 해제
    2. 압축 해제한 flutter 폴더 경로를 환경변수에 등록 
    (flutter 라는 명령어가 이 경로에 있다는 것을 윈도에게 알리는 것과 같습니다)
    &amp;lt;/aside&amp;gt;

    - 1) 다운로드
        1. 먼저 flutter 를 설치할 폴더를 만들겠습니다. 

            아래 사진에서 밑줄친 부분을 폴더 경로(주소)라고 하는데요. 

            여기에 **한글이나 공백이 들어가면** 인식을 못하고 에러가 발생합니다.

            ![Screenshot 2022-09-20 024333.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9b6779d0-a45f-40b8-b848-542e143236be/Screenshot_2022-09-20_024333.png)

            에러를 막기 위해 `C 드라이브` 바로 아래에 `src` 라는 이름의 폴더를 생성해줍니다.

            ![Screenshot 2022-09-20 024147.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2090724a-a484-4960-8c32-5fa8f3e49c3b/Screenshot_2022-09-20_024147.png)

        2. flutter 를 다운로드할 수 있는 [링크](https://docs.flutter.dev/get-started/install/windows)로 이동합니다.

            [Windows install](https://docs.flutter.dev/get-started/install/windows)

        3. 파란색 버튼(zip 파일 다운로드 링크)을 클릭합니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/962ad32c-bfeb-410e-bb13-a8117bebdc90/Untitled.png)

            &amp;lt;aside&amp;gt;
              아래 경고 메시지를 꼭 확인해주세요
            1. 특수문자(한글 포함)나 공백이 설치 경로에 포함되면 안된다
            2. `C:\Program Files\` 밑에는 설치하면 안된다 (상위 권한 필요)

            &amp;lt;/aside&amp;gt;

        4. 아까 만들어준 `C:\src` 폴더에 해당 zip 파일을 다운로드 해줍니다. 

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/22a72d03-2d30-4090-94e7-03b78ed87e80/Untitled.png)

            &amp;lt;aside&amp;gt;
              zip 파일은 어디에 다운로드하셔도 상관 없습니다. 
            대신 압축을 풀어줄 때 만큼은 꼭 `C:\src` 폴더에 풀어주세요

            &amp;lt;/aside&amp;gt;

        5. 압축을 해제해주세요!

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e70291ca-afad-49c1-b4ee-368d5f797f21/Untitled.png)

            누르면 아래와 같이 압축 해제 대상 폴더를 지정하는 화면이 나옵니다. 
            여기서 `flutter_windows_…` 이 부분을 지워주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/210a3788-ccb5-4728-b9ca-5823bec0fadd/Untitled.png)

            지우고 난 경로는 아래와 같습니다. 마저 압축 해제를 눌러줄까요?

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1bf07a7d-1a97-421a-837d-13248cf836e4/Untitled.png)

            압축을 모두 해제하는 데 10-20분 정도 걸립니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/187db94f-296d-4810-ab42-b87792dc7d1c/Untitled.png)

            압축 해제가 끝나고 나면 아래와 같이 flutter 라는 폴더가 생성되어 있을 것입니다. 
            (위에서 경로를 잘 지정해줬다면요!)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c4af3a34-0d85-4dcc-be30-8c988e71a46c/Untitled.png)

            &amp;lt;aside&amp;gt;
              간혹가다 위에서 경로 설정을 실수해 `flutter_windows_…` 이런 이름으로 해제된 경우가 있습니다.
            이 때는, 해당 폴더를 클릭해서 들어가면 비로소 `flutter` 라는 이름의 폴더를 보실 수 있습니다. 해당 `flutter` 폴더를 위 사진과 같이 밖으로 빼주세요! 
            (아래 환경 변수 설정을 원활히 하기 위함입니다)

            &amp;lt;/aside&amp;gt;

    - 2) 환경 변수 설정

        &amp;lt;aside&amp;gt;
          다운로드한 Flutter를 어디서든지 사용 가능하도록 설정하는 절차입니다.

        &amp;lt;/aside&amp;gt;

        1. 압축이 해제된 `flutter` 폴더로 들어가 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4833238e-f729-4f8c-9071-961a744110e9/Untitled.png)

        2. `bin` 폴더로 들어가주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/160d2074-5c6c-447b-96b5-6f7b6a7b24b2/Untitled.png)

        3. 들어가시면 `dart`, `dart.bat`, `flutter`, `flutter.bat` 과 같은 파일들이 있습니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c811e337-c8c9-4e33-afa4-789a2b915b6e/Untitled.png)

            &amp;lt;aside&amp;gt;
              이들이 바로 flutter 실행 파일입니다. cmd 로 이 폴더를 열고 `flutter` 라고 치면, 여기 있는 파일이 실행됩니다.

            지금은 플러터가 설치된 폴더 에서만 `flutter` 라는 명령어를 실행할 수 있습니다.

            우리는 이 flutter 실행 파일을 **컴퓨터 어디에서도 모두 접근 가능하게끔**, 즉 어디에서도 `flutter` 라는 명령어를 사용할 수 있게끔 해야 합니다.

            환경 변수에 flutter 가 위치한 **폴더 경로를 추가**하면, 시스템의 모든 경로에서 이 flutter 파일에 접근하고, 실행할 수 있습니다.

            &amp;lt;/aside&amp;gt;

        4. 이를 위해 우선 해당 폴더 경로를 복사하겠습니다. 위 과정을 잘 따라오셨다면 경로는 아래와 같습니다.

            ```
            C:\src\flutter\bin
            ```

            선택된 경로를 복사해주세요. (`Ctrl + C`)

            ![Screenshot 2022-09-20 033732.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3be69e5a-d3cc-4dd2-ac50-0a6337570189/Screenshot_2022-09-20_033732.png)

        5. 윈도우에 `환경 변수` 라고 검색합니다. (띄어쓰기도 하셔야 해요!)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8ec7c12c-472c-495c-894c-a1053c69ad96/Untitled.png)

        6. 아래와 같은 창에서 `환경 변수` 버튼을 클릭해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4b070e0b-98ea-458e-a852-0eb81fe4d496/Untitled.png)

        7. `사용자 변수`에 `path`를 선택한 뒤, `편집` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/85218bab-1214-48fd-be84-88664017d379/Untitled.png)

        8. `새로 만들기(N)`을 선택하신 뒤 복사한 주소를 붙여 넣고 `확인` 버튼을 눌러주세요.

            ![Screenshot 2022-09-20 033954.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b5246c68-425b-4d7b-924c-d03b9c6a09df/Screenshot_2022-09-20_033954.png)

        9. 켜져있는 창들을 모두 `확인` 버튼을 눌러 닫아주세요.


            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c2ab250-9830-4b25-b116-7dbd8c6c3757/Untitled.png)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/71c100c4-a2f5-4776-87df-d9cb79a55dc2/Untitled.png)

    - 3) 설치 확인
        1. `cmd`를 검색해서 `명령 프롬프트`를 실행해줍니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/383052bb-12ac-468a-8044-7f663e4eaac2/Untitled.png)

        2. 명령 프롬프트에 `flutter --version` 이라고 검색했을 때, 아래와 같은 문구가 뜨면 설치 완료!

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/37002ed1-e60e-4265-a98f-1da627f8faf7/Untitled.png)

            &amp;lt;aside&amp;gt;
              이제 시스템의 모든 경로에서 `flutter` 라는 명령어를 사용할 수 있습니다!

            &amp;lt;/aside&amp;gt;


**[에디터 설치하기]**

- 2. Visual Studio Code

    &amp;lt;aside&amp;gt;
      Visual Studio Code (줄여서 VSCode) 앞으로 실제 코드를 작성할 편집 툴입니다.

    Flutter 개발은 Android Studio와 VSCode 둘 중 원하는 툴을 사용하여 개발할 수 있는데 VSCode가 더 가볍기 때문에 앞으로 수업은 VSCode에서 진행하도록 하겠습니다.

    &amp;lt;/aside&amp;gt;

    - 1) VSCode 설치
        1. [링크](https://code.visualstudio.com/download)에 접속해 주세요.
        2. Window 버튼을 클릭합니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/63572ab2-31fb-47de-8ee1-edb8a771ad70/Untitled.png)

        3. 내 문서에 저장합니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b8406fa7-10d5-4e0c-a62a-8c71df502976/Untitled.png)

        4. VSCodeUserSetup 파일을 실행해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/15e64328-4565-4b1e-9997-a1caf5b97980/Untitled.png)

        5. `동의합니다` 선택 후 `다음` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/26c4868e-98de-4a72-8234-0cf8cdd98c95/Untitled.png)

        6. 경로를 확인하시고, `다음` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ee0ad10c-50f8-4c5e-95ce-a10813d226b1/Untitled.png)

        7. `다음` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3af5829c-429a-423c-9ff8-f79400f451bd/Untitled.png)

        8. `바탕 화면에 바로가기 만들기`를 선택해주시고, `다음` 버튼을 클릭해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/85d38b8f-cd2c-4f29-9dd6-e590ea63fcd1/Untitled.png)

        9. `설치` 버튼을 클릭해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dd0dcbcc-e464-4f96-b883-1b225ae0ab10/Untitled.png)

        10. `종료` 버튼을 클릭해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c6ba3eb-5b6b-4089-b70a-68dc787e9d3c/Untitled.png)

        11. 아래와 같이 Visual Studio Code가 실행되면, 설치 완료!

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eb31a4ee-2ed8-48df-961e-1d856ff46188/Untitled.png)

            &amp;lt;aside&amp;gt;
              우측 하단에 한국어로 변경하라는 팝업이 뜹니다.
            하지만 VSCode 사용법이나 대부분의 개발 자료는 영어로 되어 있기 때문에, 가급적 적용하지 않기를 권장 드립니다. (수업 자료도 영어 버전으로 되어있어요!)

            해당 알람을 다시 보지 않으려면 `우측 톱니바퀴 ⚙` 아이콘을 누른 뒤 `Don&amp;#39;t Show Again`을 선택해 주세요. (만약 사라져서 버튼을 누르지 못했다면 다음번에 눌러주세요!)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1bbcc25a-bac1-412e-a0ee-a8302ce700e2/Untitled.png)

            &amp;lt;/aside&amp;gt;

    - 2) Extension 설치

        &amp;lt;aside&amp;gt;
          VSCode는 Flutter 뿐만 아니라 다양한 개발을 모두 할 수 있는 통합 에디터입니다. VSCode에서 Flutter 앱 개발을 하려면 VSCode에 Extension 탭에서 아래 목록의 Extension 들을 설치해야 합니다.

        **Flutter** : VSCode에서 Flutter 개발 환경 지원
        **Dart** : Flutter 개발 시 사용되는 Dart 개발 환경 지원

        &amp;lt;/aside&amp;gt;

        1. 좌측에 extension 아이콘(동그라미)을 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/069b334c-7c6b-4eca-814c-24e159788880/Untitled.png)

        2. `flutter` 라고 검색한 뒤, 해당 익스텐션을 선택하고 `install` 버튼을 눌러 설치해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3fd60cf3-786d-427f-852f-08b92eae5795/Untitled.png)

        3. 위 `flutter` 익스텐션을 설치하면서 `dart` 익스텐션도 일반적으로 함께 설치가 됩니다.

            `dart` 라고 검색하신 뒤 혹시 설치가 안되었다면 해당 익스텐션도 같이 설치해주세요. 

            `uninstall`이라고 뜨신다면 이미 설치가 된 것이니 넘어가시면 됩니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b4bbc0d8-e9bc-4b7b-9fb8-ef0989d06ce0/Untitled.png)


**[IDE 설치하기]** ** IDE : 소프트웨어 애플리케이션*

- 3. Android Studio
    - 1) Android Studio 설치
        1. [링크](https://developer.android.com/studio)에 접속한 뒤, `Download Android Studio`를 클릭해주세요.

            ![Screenshot 2022-09-20 035221.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7ce46c8b-e17a-40d0-b310-104655399374/Screenshot_2022-09-20_035221.png)

        2. 팝업 창이 뜨면, 쭉 읽어보시고, 바닥까지 스크롤을 내려주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/11a77b36-7bdd-4011-bc10-522b6a25894a/Untitled.png)

        3. 약관에 동의하도록 체크하신 뒤, 다운로드 버튼을 눌러주세요.

            ![Screenshot 2022-09-20 035239.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/58b91dbe-952b-49ac-90f2-13e05d6d0c3d/Screenshot_2022-09-20_035239.png)

        4. 다운로드 폴더에 다운로드를 진행해주세요.

            ![Screenshot 2022-09-20 035410.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d923689f-e997-4dd2-b440-1e9b09646f72/Screenshot_2022-09-20_035410.png)

        5. 다운로드가 완료되면, `android-studio`를 클릭해서 실행해주세요.

            ![Screenshot 2022-09-20 035510.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b28f7995-98a2-4fa6-b100-d9ab0fa47387/Screenshot_2022-09-20_035510.png)

        6. 아래 이미지들을 모두 `Next` 버튼을 눌러 설치를 진행해 주세요.

            ![Screenshot 2022-09-20 035732.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5779e09c-c112-48f2-bbb0-952057a29503/Screenshot_2022-09-20_035732.png)

            ![Screenshot 2022-09-20 035744.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0b3b3939-942a-4861-bdce-ae3ffdbed13f/Screenshot_2022-09-20_035744.png)

            ![Screenshot 2022-09-20 035803.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6762f22f-60db-4c64-939d-0df372f4867a/Screenshot_2022-09-20_035803.png)

        7. `Install` 버튼을 눌러 주세요.

            ![Screenshot 2022-09-20 035818.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f8eca212-ff82-4d19-bd05-6cc7799c41a5/Screenshot_2022-09-20_035818.png)

        8. `Next` 버튼을 눌러주세요.

            ![Screenshot 2022-09-20 035928.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/025bbed0-ae37-4c57-8fb2-a34844a07596/Screenshot_2022-09-20_035928.png)

        9. 설치가 완료되었고, `Finish` 버튼을 눌러 Android Studio를 실행해 주세요.

            ![Screenshot 2022-09-20 035954.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c06fc74-6322-46f0-a89e-251455d29c79/Screenshot_2022-09-20_035954.png)

        10. 아래와 같은 창이 뜨면 `OK`를 눌러주세요.

            ![Screenshot 2022-09-20 040050.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fe9509eb-7bb4-4cbe-8bf9-a3b54db635b1/Screenshot_2022-09-20_040050.png)

        11. Android Studio 사용성 개선을 위해 Google에 데이터를 공유하고 싶으시다면, `Send usage statistics to Google` 버튼을 누르시고, 그렇지 않으시면 `Don&amp;#39;t send` 버튼을 눌러주세요.

            ![Screenshot 2022-09-20 040150.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/014b2fd1-a3b1-4610-aa6c-a0a52b65caa5/Screenshot_2022-09-20_040150.png)

        12. Setup Wizard가 실행되면, `Next` 버튼을 눌러주세요.

            ![Screenshot 2022-09-20 040252.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1c2cdfad-1ca2-4ec5-9c9b-99b4ee05f9dc/Screenshot_2022-09-20_040252.png)

        13. `Next` 버튼을 눌러주세요.

            ![Screenshot 2022-09-20 040236.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/382ea58f-be44-4640-8347-8e6d21f13e54/Screenshot_2022-09-20_040236.png)

        14. 원하시는 테마를 선택하신 뒤 `Next`를 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f7e2a5fc-217d-4e63-badc-265e7439904e/Untitled.png)

        15. 만약 계정 이름이 한글이거나 띄어쓰기가 들어가 있다면`Your Android SDK location contains non-ASCII characters` 혹은 `your path contains white space etc.` 라도 뜨며 진행이 안됩니다.

            &amp;lt;aside&amp;gt;
              에러가 없으신 분들은 `Next` 버튼을 누르고 26 순서로 넘어가 주세요

            &amp;lt;/aside&amp;gt;

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/efaf14eb-3915-4a67-9509-a86dbad1991e/Untitled.png)

            Android SDK Location 경로를 직접 만들어주도록 하겠습니다.

        16. 이런 경우, 아래 그림과 같이 Android SDK Location에 `C:\Users\내 계정이름\Local`까지만 선택해 복사해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/453fce97-843d-4451-9fb7-a3dc81198db4/Untitled.png)

        17. `탐색기`를 열고 주소창에 붙여 넣은 뒤 엔터를 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0f83a23d-a1ac-4975-ac74-d12b2fd756d9/Untitled.png)

        18. 현재 Local이라는 폴더에 들어와 있고, 여기에 `Android`라는 폴더를 만들어 주세요. (대문자로 시작합니다)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/69dd03b0-8637-46ff-95f8-989545a5f507/Untitled.png)

        19. 생성한 `Android` 폴더에 들어간 뒤 `Sdk` 라는 폴더를 만들어주세요. (대문자로 시작합니다)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/36f523e9-78f0-4bf8-ac4a-e088101e3c89/Untitled.png)

        20. 생성한 `Sdk` 폴더 안으로 들어가 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/983e5efe-61c7-453f-a6f2-b794d91e14c7/Untitled.png)

        21. 윈도우 검색창에 `명령`이라고 검색한 뒤 `명령 프롬프트`를 우클릭하여 `관리자 권한으로 실행`해 주세요. 그냥 실행하면 안됩니다! 

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d164c4b7-9cc1-4e36-820e-423bcce82e1e/Untitled.png)

            실행할지 물어보는 팝업이 뜨면 예를 눌러서 실행해주세요.

        22. 아래 명령어를 복사한 뒤 명령 프롬프트에 붙여 넣어주세요. 아직 실행하시면 안됩니다.

            ```bash
            mklink /D C:\android-sdk 
            ```

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aa888d25-2d68-41e1-a200-4ee498e8b403/Untitled.png)

            다음 탐색기에서 주소창을 클릭해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c09bbf0b-9031-4dff-80ec-5995cc77b6ac/Untitled.png)

            주소를 복사해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8707cb46-2d75-497e-b4af-c0f8d13b4564/Untitled.png)

            복사한 경로를 명령 프롬프트 `android-sdk` 뒤에 붙여 넣어주세요. 참고로 아래 그림과 같이 사이에 띄어쓰기가 있어야합니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0b2b2e71-d645-4b4e-9912-8268a6cfcdc2/Untitled.png)

            명령어를 실행해 아래와 같이 기호화된 링크를 만들었다고 뜨면 성공입니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/447f5527-cee0-4a38-95ef-ec8e9ea839af/Untitled.png)

            &amp;lt;aside&amp;gt;
              Android/Sdk 폴더에 바로가기를 C 드라이브 바로 밑에 생성하는 과정입니다.

            &amp;lt;/aside&amp;gt;

        23. 다시 Android Studio Setup Wizard로 돌아와서, 아래 그림과 같이 Android SDK Location 밑에 폴더 아이콘을 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f15a708c-8f05-4d5b-9b4c-058734307ee2/Untitled.png)

            그러면 아래와 같이 경로를 선택하는 창이 뜹니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a7ea5d26-5815-42d4-a2c1-e05c8c1f0e48/Untitled.png)

        24. 아래 코드를 복사해서 아래 그림과 같이 경로 선택창의 주소에 붙여 넣고 `OK`를 눌러주세요.

            ```bash
            C:\android-sdk
            ```

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ca60021d-a9d3-4d03-a0d4-ea2e36a8b442/Untitled.png)

        25. 이제 경로 관련 빨간 에러가 사라지고 활성화된 `Next` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d970a8c9-2e3f-4e48-b03f-bd534f3b942d/Untitled.png)

        26. `Next` 를 눌러서 세팅을 진행해 주세요.

            ![Screenshot 2022-09-20 040522.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f03635e8-f1ff-4c6a-b91a-85506c8249eb/Screenshot_2022-09-20_040522.png)

        27. **License Agreement** 화면이 나옵니다. 왼쪽 밑줄 친 부분을 클릭해 모두 `Accept` 를 눌러주세요

            ![Screenshot 2022-09-20 040605.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c88f04d6-c9be-4227-a04d-864194411d00/Screenshot_2022-09-20_040605.png)

        28. `Accept` 를 모두 누르고 나면 `Finish` 버튼이 활성화됩니다. 눌러주세요.

            ![Screenshot 2022-09-20 041010.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/99167e06-dc28-4302-8b1a-4e7728c397eb/Screenshot_2022-09-20_041010.png)

        29. 설치가 진행됩니다. 시간이 다소 소요되니 천천히 기다려주세요!

            ![Screenshot 2022-09-20 041021.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0d41bef0-1e89-4670-9de5-2dc25755c5f7/Screenshot_2022-09-20_041021.png)

        30. 중간에 `이 앱이 디바이스를 변경할 수 있도록 허락하시겠어요?` 라는 팝업이 뜨면 `예를 선택`해주세요. 
        31. 모든 세팅이 완료되면 `Finish`를 클릭해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/59ef93c2-cb59-4883-9297-7d78b73b3e77/Untitled.png)

    - 2) Android Command-line Tools 설치

        &amp;lt;aside&amp;gt;
          `Android Command-line Tools`는 Flutter에서 Android에 명령을 내리기 위해 필요합니다.

        &amp;lt;/aside&amp;gt;

        1. 아래와 같이 Android Studio가 실행되면, `More Actions`를 클릭한 뒤 `SDK Manager`를 클릭해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8375a11c-71c1-40ba-ba2a-6d5929d738b6/Untitled.png)

        2. `SDK Tools`를 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/31ad9747-b889-4dc8-8b6e-efce041de413/Untitled.png)

        3. `Android SDK Command-line Tools (latest)`를 선택한 뒤 `OK`를 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c6faa542-270d-495f-bb77-3a7cd2b94cbf/Untitled.png)

        4. Dialog가 뜨면 `OK` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5def3ca8-3748-4ae3-bec2-bb118c427cd9/Untitled.png)

        5. 설치가 완료되면 `Finish` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/32b5f758-804f-48c1-85a2-6c710144e705/Untitled.png)

    - 3) Android Virtual Devices 설치

        &amp;lt;aside&amp;gt;
          앱을 개발시 실제 휴대폰을 연결하여 개발을 진행할 때도 있지만, 대부분의 경우 Virtual Device(컴퓨터에 가상의 휴대폰을 띄우는 소프트웨어)를 이용하여 개발합니다.

        &amp;lt;/aside&amp;gt;

        1. `More Actions` → `Virtual Device Manager` (또는 `AVD Manager`)를 선택해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/69051de0-5876-460e-8f11-d77cd168cd75/Untitled.png)

        2. 이미 Device 가 있는 분들은 아래 절차를 진행하지 않으셔도 됩니다.

            ![Screenshot 2022-09-20 042622.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8527ebd0-b48f-4daf-ba4b-f470669a0c6b/Screenshot_2022-09-20_042622.png)

        3. (Device 가 없는 경우) `Create Virtual Device`를 선택해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c6e4b33d-a56d-46c0-a041-70ef779cbb25/Untitled.png)

        4. 기본적으로 선택되어 있는, `Phone` → `Pixel 2` 디바이스를 `Next` 버튼을 눌러 생성합니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dbb2fe70-9970-491e-a29b-204668dfc7e1/Untitled.png)

        5. Release Name `Q`의 `Download`를 클릭하여 가상 기기에 설치할 OS를 다운로드 합니다.

            &amp;lt;aside&amp;gt;
              R 버전은 Virtual Device에서 문제가 있다고 해요. 그래서 **Q 버전**으로 진행할게요!

            &amp;lt;/aside&amp;gt;

            ![Screenshot 2022-09-20 042844.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf446b06-b759-4156-af88-5e7de8b1f696/Screenshot_2022-09-20_042844.png)

        6. 설치가 완료되면 `Finish` 버튼을 눌러 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a8304bbc-b8d8-42f4-a5f1-0253ab75dd3a/Untitled.png)

        7. `Q` 버전의 OS를 선택한 뒤 `Next` 버튼을 눌러 주세요.
        **API Level 29**인지 확인해주세요 :)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0442c914-01e1-42bb-afd2-c02bde2ffb9d/Untitled.png)

        8. `Finish` 버튼을 눌러 Virtual Device 설치를 완료 해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c733f125-59d4-423b-93ae-d2434ba46a55/Untitled.png)

        9. 성공적으로 추가된 Virtual Device를 확인하시고, 이제 Android Studio는 종료해주세요!

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3215221e-ec74-4c41-ae1c-fa738500babb/Untitled.png)

    - 4) Android Licenses
        1. `cmd`를 검색해서 `명령 프롬프트`를 실행해줍니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/383052bb-12ac-468a-8044-7f663e4eaac2/Untitled.png)

        2. `flutter doctor`라고 입력한 뒤 엔터를 누릅니다.
        아래와 같이 `Android toolchain`의 좌측에  `[!]` 표시가 되어있습니다.

            ![Screenshot 2022-09-20 043142.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/504d7c2f-2725-479d-93b6-889615c328d4/Screenshot_2022-09-20_043142.png)

        3. 문제를 해결하기 위해 `flutter doctor --android-licenses`를 복사해서 실행해 줍니다.
        4. 실행하면 라이센스에 대한 동의를 여러번 구하는데, `y`를 입력하고 엔터를 눌러 진행해줍니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/299f3349-5434-4687-9a37-c0b94b9ce408/Untitled.png)

        5. `All SDK package licenses accepted` 라는 메시지가 뜨면 완료된 것입니다.

            ![Screenshot 2022-09-20 043215.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/915ccacc-e383-48bb-a183-b121b27b5096/Screenshot_2022-09-20_043215.png)

        6. `flutter doctor` 를 입력했을 때 아래와 같이 `Android toolchain`의 좌측에 체크(`[v]`) 되었다면 완료!

            ![Screenshot 2022-09-20 043631.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/64df1c29-20fa-409a-bcfe-fabaa84af36a/Screenshot_2022-09-20_043631.png)

            &amp;lt;aside&amp;gt;
              Visual Studio 는 windows 용 앱을 개발할 때 필요한 툴입니다. 지금은 Android, iOS 앱 개발만 진행하므로 신경쓰지 않으셔도 됩니다.

            &amp;lt;/aside&amp;gt;

- 4. Xcode → 애플 정책상 맥환경에서만 사용 가능합니다  

    &amp;lt;aside&amp;gt;
      Xcode는 iOS 앱 개발시 필요한 툴로 macOS에서만 설치가 가능하므로 넘어가도록 하겠습니다.

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      [잠깐!] 그러면 window에서는 iOS 앱 개발을 할 수 없는 건가요?

    애플의 정책상 iOS 앱은 macOS에서만 개발이 가능합니다.  

    하지만 Flutter로 작성한 코드는 Android 뿐만 아니라 iOS에서도 이용할 수 있으니 향후 macOS가 생기신다면 기존 소스코드를 그대로 사용하여 iOS 앱도 출시할 수 있습니다!

    &amp;lt;/aside&amp;gt;


**설치를 완료하셨나요~? 잘 설치되었는지 확인해봅시다! ㅎㅎ**

- 최종 설치 확인
    1. `cmd`를 검색해서 `명령 프롬프트`를 실행해줍니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/383052bb-12ac-468a-8044-7f663e4eaac2/Untitled.png)

    2. `flutter doctor` 를 입력했을 때 아래와 같이 모든 항목이 체크(`[v]`) 되었다면 설치 완료!

        ![Screenshot 2022-09-20 043631.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/64df1c29-20fa-409a-bcfe-fabaa84af36a/Screenshot_2022-09-20_043631.png)


    &amp;lt;aside&amp;gt;
      고생하셨습니다! 원래 개발 환경을 설정하는데 시간이 많이 들어갑니다  
    그럼 1주 차 수업 때 뵙도록 하겠습니다  

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;MacOS ver 프로그램 설치 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;**[프레임워크 설치하기]**

- **1. Flutter** - Android와 iOS 앱을 하나의 코드로 구현할 수 있도록 도와주는 프레임워크

    &amp;lt;aside&amp;gt;
      Flutter 설치 과정은 일반적인 프로그램 설치와는 조금 다릅니다. 일반적으로는 `.pkg` 파일등을 실행하거나, `Applications` 폴더에 드래그 &amp;amp; 드롭해 설치를 진행하는데요.

    Flutter 설치는 아래와 같은 과정으로 진행됩니다.

    1. zip 파일을 다운로드 받아서 적절한 경로에 압축 해제
    2. 압축 해제한 flutter 폴더 경로를 환경변수에 등록 
    (flutter 라는 명령어가 이 경로에 있다는 것을 macOS에게 알리는 것과 같습니다)
    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      시작하기에 앞서 **Apple Sillicon (M1, M2 등)** 을 사용하시는 분들은 **터미널**을 열고 아래 명령어를 입력해주세요 (인텔용 소프트웨어를 실행시킬 수 있는 **Rosetta** 라는 번역기를 설치하는 명령어입니다.)

    ```bash
    sudo softwareupdate --install-rosetta --agree-to-license
    ```

    ### 터미널이란?

    터미널은 마우스 클릭이 아닌 키보드로 컴퓨터에게 명령을 내리는 프로그램 입니다.

    ![terminal.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8def37ba-6141-4924-919d-9712a68a8981/terminal.png)

    ### 터미널 실행 방법

    1. macOS 우측 상단에 `돋보기  ` 아이콘을 선택해 주세요. 그러면 아래와 같이 `Spotlight 검색` 창이 뜹니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1d6cc87e-0a8a-4477-bf51-eb6f302c89b5/Untitled.png)

    2. `Spotlight`에 `terminal`이라고 검색한 뒤, 아래와 같이 하단에 `터미널` 프로그램이 보이면 엔터를 눌러주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6f1ffd0-6481-4c04-b40c-9a530a51fcb2/Untitled.png)

    3. 터미널 프로그램이 실행됩니다.

        ![스크린샷 2021-12-08 오후 10.11.21.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b52a3237-b714-410f-baed-2372d0c844da/스크린샷_2021-12-08_오후_10.11.21.png)

    &amp;lt;/aside&amp;gt;

    - 1) 다운로드
        1. 먼저 flutter 를 설치할 폴더를 만들겠습니다. Downloads 폴더를 먼저 열어줍니다

            ![Screen Shot 2022-09-20 at 12.20.17 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/15210334-edd2-43ce-bf65-9e3004dd5942/Screen_Shot_2022-09-20_at_12.20.17_PM.png)

        2. 여기서 `Cmd + ↑(화살표 위 버튼)` 을 눌러주세요. 상위 폴더로 이동하는 단축키입니다. 아래와 같이 유저명과 같은 이름의 폴더로 이동하면 됩니다.

            ![Screen Shot 2022-09-20 at 12.22.11 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0ae831c6-cec3-4b99-8034-2e3e4fb6cd0a/Screen_Shot_2022-09-20_at_12.22.11_PM.png)

        3. 이제 여기에 development 라는 이름의 폴더를 생성하겠습니다. 우측 상단의 `Actions` (점 3개 아이콘) 를 클릭하고 `New Folder` 를 선택해주세요.

            ![Screen Shot 2022-09-20 at 12.26.11 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/27a47ddd-37dc-431c-bfd9-04e6809be11a/Screen_Shot_2022-09-20_at_12.26.11_PM.png)

            `**development**` 라는 이름으로 생성해줍니다.

            ![Screen Shot 2022-09-20 at 12.28.07 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/08feceb1-3e60-41b4-a885-ed44ba1bfa46/Screen_Shot_2022-09-20_at_12.28.07_PM.png)

        4. 이제 생성한 폴더에 flutter 압축 파일을 다운로드하겠습니다.

            Chrome 브라우저에서 [링크](https://docs.flutter.dev/get-started/install/macos)를 열어주세요.

        5. 밑으로 조금 스크롤한 뒤, 파란색 버튼을 클릭해 다운로드를 진행해 주세요.

            ![Screen Shot 2022-09-20 at 12.01.03 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0bc321ce-513c-4200-baa8-251d187fbdbc/Screen_Shot_2022-09-20_at_12.01.03_PM.png)

            Intel 칩을 사용하는 맥북은 왼쪽을 Apple 칩을 사용하는 맥북은 오른쪽을 선택해 주세요.

            &amp;lt;aside&amp;gt;
              **내 mac 이 어떤 프로세서를 사용하는지 알고 싶다면**

            좌측 상단 `Apple 로고` 클릭 → `이 Mac에 관하여`를 클릭하여 프로세서가 Intel 칩인지 Apple 칩인지 확인할 수 있습니다.

            - Intel chip

                ![processor.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2e9a82a1-aed3-4ccf-9f4f-d7db45b782a0/processor.png)

            - Apple chip

                ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/01d99ad9-718d-4a10-8e52-9e0ea731ec38/Untitled.png)

            &amp;lt;/aside&amp;gt;

            우선 `다운로드` 폴더에 저장하고, 다운로드가 끝나면 파일을 옮기겠습니다. 저장 버튼을 눌러주세요

            ![Screen Shot 2022-09-20 at 12.31.23 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3cb97280-feb8-4658-bbc3-ac556a116674/Screen_Shot_2022-09-20_at_12.31.23_PM.png)

        6. 바탕화면에서 휴지통 우측에 있는 `다운로드` 폴더를 선택한 뒤 방금 다운로드한 flutter zip 파일을 우리가 만들어준 `development` 폴더로 드래그 앤 드롭합니다.

            ![Screen Shot 2022-09-20 at 12.42.27 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4539ac39-e0fb-47e9-b57a-3e23d9b06b83/Screen_Shot_2022-09-20_at_12.42.27_PM.png)

            아래와 같이 zip 파일이 해당 폴더로 이동했는지 확인합니다.

            ![Screen Shot 2022-09-20 at 12.42.40 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/db5d3cc0-d64e-49ee-9bc9-155c9d96d6ed/Screen_Shot_2022-09-20_at_12.42.40_PM.png)

        7. 다운받은 flutter 압축 파일을 실행해 주세요.

            ![Screen Shot 2022-09-20 at 12.45.08 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d4cc81c9-dfba-4829-bc01-b6286fb98a0d/Screen_Shot_2022-09-20_at_12.45.08_PM.png)

        8. 아래 사진과 같이 압축이 해제되고 `flutter`라는 폴더가 생성 됩니다.

            &amp;lt;aside&amp;gt;
              만약 압축이 해제된 폴더 이름이 아래 사진과 다른 경우 `flutter`로 변경해 주시기 바랍니다.

            &amp;lt;/aside&amp;gt;

            ![Screen Shot 2022-09-20 at 12.45.20 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/67baef48-1295-479d-96e8-096fd893ff70/Screen_Shot_2022-09-20_at_12.45.20_PM.png)

        9. `flutter` → `bin` 폴더에 들어가볼까요? 
        `dart`, `dart.bat`, `flutter`, `flutter.bat` 등의 파일이 있는 것을 볼 수 있습니다. 이들이 flutter 실행 파일입니다.

            ![Screen Shot 2022-09-20 at 12.58.43 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c93cea50-d1db-4fdc-bf47-268ba856efeb/Screen_Shot_2022-09-20_at_12.58.43_PM.png)


        &amp;lt;aside&amp;gt;
          우리는 이 flutter 실행 파일을 **컴퓨터 어디에서도 모두 접근 가능하게끔**, 즉 어디에서도 `flutter` 라는 명령어를 사용할 수 있게끔 해야 합니다.

        환경 변수에 flutter 가 위치한 **폴더 경로를 추가**하면, 시스템의 모든 경로에서 이 flutter 파일에 접근하고, 실행할 수 있습니다.

        &amp;lt;/aside&amp;gt;

    - 2) 환경변수 설정 및 설치
        1. 바탕화면에서 `사과 아이콘` → `이 Mac에 관하여`를 클릭하여 macOS 버전을 확인해 주세요.

            ![os version.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/311d4d76-7e17-4987-beee-a4690d4db783/os_version.png)

        2. macOS 버전을 확인한 뒤, 해당하는 명령어를 복사해 주세요.

            &amp;lt;aside&amp;gt;
              mac OS 버전 순서

            ![Untitled.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f864e554-d9b0-4eb3-8c3e-70f0f952ace6/Untitled.png)

            **macOS Mojave** 이하 버전을 사용하는 경우 **설정하는 파일이 다릅**니다.

            &amp;lt;/aside&amp;gt;

            - macOS 카타리나(Catalina) `**이상**`버전 명령어 (최신 버전은 여기에요!)

                ```bash
                echo &amp;#39;export PATH=&amp;quot;$PATH:$HOME/development/flutter/bin&amp;quot;&amp;#39; &amp;gt;&amp;gt; ~/.zshrc &amp;amp;&amp;amp; source ~/.zshrc
                ```

            - macOS 모하비(Mojave) **`이하`** 버전 명령어

                ```bash
                echo &amp;#39;export PATH=&amp;quot;$PATH:$HOME/development/flutter/bin&amp;quot;&amp;#39; &amp;gt;&amp;gt; ~/.bash_profile &amp;amp;&amp;amp; source ~/.bash_profile
                ```


            &amp;lt;aside&amp;gt;
              어떤 명령어인지 궁금하신 분들은 아래 설명을 참고해 주세요.

            1. `~/development/flutter/bin` 폴더에 있는 flutter 파일을 어디서든 실행할 수 있도록 등록(환경변수에 등록)

                (~ 는 유저명과 같은 이름의 Home 폴더를 의미합니다)

                &amp;gt; macOS 모하비 버전에서는 `.bash_profile`이라는 파일에 등록하고 이후 버전에선 `.zshrc` 파일에 등록하기 때문에 명령어가 다릅니다.
                &amp;gt; 

                ```bash
                echo &amp;#39;export PATH=&amp;quot;$PATH:$HOME/Developments/flutter/bin&amp;quot;&amp;#39; &amp;gt;&amp;gt; ~/.zshrc
                ```

            2. 설정 반영

                ```bash
                source ~/.zsh
                ```

            &amp;lt;/aside&amp;gt;

        3. 터미널에 단축키 `Cmd + v` 또는 마우스 우클릭하여 `붙여넣기`를 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8a6e79fa-b9a7-4591-8701-d31a13c59735/Untitled.png)

            아래와 같이 붙여넣으면 아래와 같이 나오고, 엔터(enter)를 눌러 실행해 주세요.

            ![Screen Shot 2022-09-20 at 2.17.02 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9324f3b5-9935-4f32-9cd0-7620d8caac47/Screen_Shot_2022-09-20_at_2.17.02_PM.png)

        4. 다음 명령어는 flutter의 버전을 확인하는 명령어입니다. 아래 명령어를 복사해 주세요.

            ```bash
            flutter --version
            ```

            터미널에 붙여넣고 실행해 주세요.

            ![Screen Shot 2022-09-20 at 2.19.36 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bf87e55f-450b-41b2-a62f-da725622a58f/Screen_Shot_2022-09-20_at_2.19.36_PM.png)

        - [잠깐] 혹시 다음 팝업이 뜨면 `설치` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e62eddb-6ee7-4664-a88b-51476ae199c2/Untitled.png)

            1. 사용권 계약 팝업이 뜨면 `동의` 버튼을 눌러주세요.

                ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d8f0617d-4b21-45ec-b5e2-ba1aea1b67f8/Untitled.png)

            2. 아래와 같이 설치가 완료되면 `완료` 버튼을 눌러주세요.

                ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/122d8290-ec39-4221-b23b-b25a0bfc93ef/Untitled.png)

    - 3) 설치 확인
        1. 터미널 창에 Flutter 버전을 확인하는 아래 명령어를 붙여넣고 실행해 주세요.

            ```bash
            flutter --version
            ```

            실행하면 `Building flutter tool...` 이라고 출력되고 잠시 후 아래와 같이 Flutter 버전이 출력되면 Flutter 설치 완료!

            ![Screen Shot 2022-09-20 at 2.19.15 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/97a87ad4-67c9-4a28-ab0c-6428707eb0de/Screen_Shot_2022-09-20_at_2.19.15_PM.png)

            &amp;lt;aside&amp;gt;
              위 이미지에선 Flutter 3.3.2 버전이 출력되는데 시간이 지나면 최신 버전으로 업데이트 되어 버전이 다를 수 있습니다. 전혀 문제 없으니 그대로 진행해주세요.

            &amp;lt;/aside&amp;gt;

        2. 다음 명령어를 복사해 터미널에 붙여넣고 실행해 주세요.

            &amp;lt;aside&amp;gt;
              Flutter 개발하는데 필요한 항목들의 상태를 확인하는 명령어 입니다.

            &amp;lt;/aside&amp;gt;

            ```bash
            flutter doctor
            ```

            ![_flutter doctor.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2cae54a1-1763-4e82-880f-37c3f0cfccdb/_flutter_doctor.png)

            &amp;lt;aside&amp;gt;
              Android 앱을 만드는데 필요

            - Android Studio
            - Android SDK

            iOS 앱을 만드는데 필요

            - Xcode
            - CocoaPods
            &amp;lt;/aside&amp;gt;

            위 프로그램들을 하나씩 설치해 보도록 하겠습니다. 


**[에디터 설치하기]**

- 2. Visual Studio Code

    &amp;lt;aside&amp;gt;
      Visual Studio Code (줄여서 VSCode) 는 앞으로 실제 코드를 작성할 편집 툴입니다.

    Flutter 개발은 Android Studio와 VSCode 둘 중 원하는 툴을 사용하여 진행할 수 있습니다. VSCode가 더 가볍기 때문에 앞으로 수업은 VSCode를 활용해 진행하도록 하겠습니다.

    &amp;lt;/aside&amp;gt;

    - 1) VSCode 설치
        1. [링크](https://code.visualstudio.com/download)에 접속해 주세요.
        2. 애플 아이콘 하단에 있는 Mac 버튼을 클릭해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/732ff958-a930-4446-9b3a-2eae43005ef4/Untitled.png)

        3. `다운로드` 폴더에 저장해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fc103a3e-b9a5-400a-bcb2-fffcbbb63e28/Untitled.png)

        4. 바탕화면에 다운로드 폴더를 클릭한 뒤 `Finder에서 열기` 버튼을 클릭해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e0efbc3d-75c5-4299-b0ad-94bbcfbfd511/Untitled.png)

        5. 다운받은 `VSCode-darwin-universal.zip` 파일을 실행해 압축을 풀어주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/292fa7d8-ecb7-426b-913e-78e335908468/Untitled.png)

        6. 압축이 풀리고 생성된 `Visual Studio Code` 파일을 드래그해서 왼쪽 `응용 프로그램`에 떨어뜨려 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d6388951-bc4a-4d04-8937-2a0783830a6a/Untitled.png)

        7. 화면 우측 상단 `돋보기  ` 아이콘을 클릭한 뒤 `visual` 이라고 검색해 주세요. 그리고 하단에 `Visual Studio Code`가 보이면 엔터를 눌러 실행해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/72e585cc-a9b0-4a46-932d-c725469e69cd/Untitled.png)

        8. 아래와 같은 팝업이 뜨면 `열기` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1b1713a2-0f9a-4bbb-848b-38f76eef5292/Untitled.png)

        9. 그러면 아래와 같이 VSCode가 실행되면 설치 완료!

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/64675e1d-55d2-41b9-8a4e-027d93b08eda/Untitled.png)

            &amp;lt;aside&amp;gt;
              우측 하단에 한국어로 변경하라는 팝업이 뜹니다.
            하지만 VSCode 사용법이나 대부분의 개발 자료는 영어로 되어 있기 때문에, 가급적 적용하지 않기를 권장 드립니다. (수업 자료도 영어 버전으로 되어있어요!)

            해당 알람을 다시 보지 않으려면 `우측 톱니바퀴 ⚙` 아이콘을 누른 뒤 `Don&amp;#39;t Show Again`을 선택해 주세요. (만약 사라져서 버튼을 누르지 못했다면 다음번에 눌러주세요!)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1bbcc25a-bac1-412e-a0ee-a8302ce700e2/Untitled.png)

            &amp;lt;/aside&amp;gt;

    - 2) Extension 설치

        &amp;lt;aside&amp;gt;
          VSCode는 Flutter 뿐만 아니라 다양한 개발을 모두 할 수 있는 통합 에디터입니다. VSCode에서 Flutter 앱 개발을 하려면 VSCode에 Extension 탭에서 아래 목록의 Extension 들을 설치해야 합니다.

        **Flutter** : VSCode에서 Flutter 개발 환경 지원
        **Dart** : Flutter 개발 시 사용되는 Dart 개발 환경 지원

        &amp;lt;/aside&amp;gt;

        1. 좌측에 extension 아이콘(동그라미)을 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5fea993b-6a6f-4d4f-9354-fa2a33248122/Untitled.png)

        2. `flutter` 라고 검색한 뒤, 해당 익스텐션을 선택하고 `install` 버튼을 눌러 설치해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c51b36d-aa33-4ad7-abde-6a334e7bb2cb/Untitled.png)

        3. 위 `flutter` 익스텐션을 설치하면서 `dart` 익스텐션도 일반적으로 함께 설치가 됩니다.

            `dart` 라고 검색하신 뒤 혹시 설치가 안되었다면 해당 익스텐션도 같이 설치해주세요. `uninstall`이라고 뜨신다면 이미 설치가 된 것이니 넘어가시면 됩니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c1ea5cf2-200a-451f-83e9-330a5d4c533e/Untitled.png)


**[IDE 설치하기]** ** IDE : 소프트웨어 애플리케이션*

&amp;lt;aside&amp;gt;
  **MacOS 에서는 안드로이드 환경, iOS 환경 모두에서 코드를 돌려볼 수 있습니다!**

아래의 상황에 따라 원하는 원하시는 애뮬레이터를 골라 설치해보세요! ㅎㅎ

**애뮬레이터 : 컴퓨터에서 가상으로 스마트폰 OS를 돌리는 프로그램*

1. **안드로이드와 ios 모두 확인하고 싶은 경우**

    → Android Studio 와 Xcode 모두 설치합니다.

2. **안드로이드만 확인하고 싶은 경우**

    → Android Studio만 설치합니다.

3. **ios만 확인하고 싶은 경우**

    → Xcode만 설치합니다.


**[잠깐!] Xcode 는 용량을 크게 차지하니 주의해주세요! :)**

- 용량이 부족하다면 Android Studio 만 설치하셔도 괜찮습니다. 
이러면 iOS Simulator 는 사용할 수 없지만, 개발에는 큰 지장이 없습니다! ㅎㅎ
&amp;lt;/aside&amp;gt;

- 3. Android Studio
    - 1) Android Studio 설치
        1. [링크](https://developer.android.com/studio)에 접속한 뒤, `Download Android Studio` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a9598ce0-fa0e-4c98-acc0-3222c2a34adb/Untitled.png)

        2. 약관이 뜨면 아래로 쭉 스크롤 해주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/95e19502-1158-45c4-9d84-9afdf2df50c7/Untitled.png)

        3. Intel 칩을 사용하는 맥북은 왼쪽 `Mac with Intel chip`을 Apple 칩을 사용하는 맥북은 오른쪽 `Mac with Apple chip`을 선택해 주세요.

            ![Screen Shot 2022-09-20 at 2.29.56 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/39c5a846-f4a1-4a38-b5c4-7ce788a51996/Screen_Shot_2022-09-20_at_2.29.56_PM.png)

            &amp;lt;aside&amp;gt;
              좌측 상단 `Apple 로고` 클릭 → `이 Mac에 관하여`를 클릭하여 Intel 칩인지 Apple 칩인지 확인할 수 있습니다.

            - Intel chip

                ![processor.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2e9a82a1-aed3-4ccf-9f4f-d7db45b782a0/processor.png)

            - Apple chip

                ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/01d99ad9-718d-4a10-8e52-9e0ea731ec38/Untitled.png)

            &amp;lt;/aside&amp;gt;

        4. 다운로드 팝업이 뜨면 `저장` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c495cdb-daa3-4529-a9c0-66c42c002404/Untitled.png)

        5. 바탕화면 하단에 휴지통 좌측에 있는 `다운로드` 폴더를 클릭한 뒤 `Finder`에서 열기 버튼을 클릭해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b129d044-3975-4ff7-ad8b-2dc69078b22f/Untitled.png)

        6. 다운로드가 완료된 `android-studio~~.dmg` 파일을 실행해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/021331d6-475a-4ddf-ae6a-c53aa3078490/Untitled.png)

        7. 아래와 같은 창이 뜨면 왼쪽에 `Android Studio`를 드래그해서 `Applications`에 떨어뜨려주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c72763c1-1eec-4770-a954-bfebbeb7ada1/Untitled.png)

        8. 설치가 완료되면 좌측 상단에 빨간 X를 눌러서 아래 화면을 종료해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c4a1637d-6518-4aa8-b93e-e2cd52e064cd/Untitled.png)

            &amp;lt;aside&amp;gt;
              바탕화면에 아래 사진과 같은 파일이 생겼다면 휴지통으로 드래그해 삭제하셔도 됩니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dfa41421-9575-429e-af13-3d999315cf89/Untitled.png)

            &amp;lt;/aside&amp;gt;

        9. 우측 상단에 `돋보기 ` 아이콘을 클릭하고, 팝업창이 뜨면 `android`라고 입력해 주세요. 그리고 아래와 같이 `Android Studio`가 자동완성으로 뜨면 엔터를 눌러 실행해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a6bad6de-8aa9-4896-a9d8-5372870f604e/Untitled.png)

        10. 아래와 같이 팝업이 뜨면 `열기` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/328ff0d3-085c-4bfb-ad59-f82c67659e6d/Untitled.png)

        11. 아래와 같은 창이 뜬다면 `OK` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd9a712e-8000-48fd-9764-9bdec2a8aa7f/Untitled.png)

        12. `Import Android Studio Settings` 팝업이 뜨면 `Do not import settings`를 선택하신 뒤 `OK` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/00c3d439-86fd-4334-b944-4b93b2633ea0/Untitled.png)

        13. 안드로이드 스튜디오 사용 데이터를 구글에 전달하여 사용성 개선에 참여하고 싶다면 `Send usage statistics to Google`을 선택해주시고, 그렇지 않은 경우 `Don&amp;#39;t send`를 선택해 주세요.

            ![Screen Shot 2022-09-20 at 2.35.27 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7910e8fc-9433-4cfb-a277-0b2c2dbed8e0/Screen_Shot_2022-09-20_at_2.35.27_PM.png)

        14. 다음과 같은 안드로이드 스튜디오 설정 화면이 나오면 `Next` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/748967d6-689f-4aaf-af7c-f8c7da147582/Untitled.png)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/90290328-7729-429e-bd5f-1b6fd2b48311/Untitled.png)

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/43a8a218-0bc9-4ddb-a5d4-658650c7cbb9/Untitled.png)

        15. `Finish` 버튼을 눌러 Android SDK 설치를 진행해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f8728adf-ae30-4237-aac8-da26ed3af750/Untitled.png)

        16. **License Agreement** 화면이 나온다면 왼쪽 밑줄 친 부분을 클릭해 모두 `Accept` 를 눌러주세요

            ![Screenshot 2022-09-20 040605.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c88f04d6-c9be-4227-a04d-864194411d00/Screenshot_2022-09-20_040605.png)

        17. `Accept` 를 모두 누르고 나면 `Finish` 버튼이 활성화됩니다. 눌러주세요.

            ![Screenshot 2022-09-20 041010.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/99167e06-dc28-4302-8b1a-4e7728c397eb/Screenshot_2022-09-20_041010.png)

        18. 설치가 완료되면 `Finish` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/caef348a-0104-4904-81ac-1f3d62a6e53a/Untitled.png)

        19. 아래와 같은 화면이 뜨면 Android Studio 설치 완료!

            ![Screen Shot 2022-09-20 at 2.43.20 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2c9e3e9a-a781-482d-91a5-92deb90993d8/Screen_Shot_2022-09-20_at_2.43.20_PM.png)

    - 2) Android Command-line Tools 설치

        &amp;lt;aside&amp;gt;
          `Android Command-line Tools`는 Flutter에서 Android에 명령을 내리기 위해 필요합니다.

        &amp;lt;/aside&amp;gt;

        1. Android Studio에서 `More Actions`를 선택한 뒤 `SDK Manager`를 선택해 주세요.

            ![Screen Shot 2022-09-20 at 2.43.43 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/765f159a-d30c-452b-82db-56925a758f31/Screen_Shot_2022-09-20_at_2.43.43_PM.png)

        2. 그러면 아래와 같이 `Preferences for New Projects` 팝업이 뜨면 `SDK Tools` 탭을 선택 → `Android SDK Command-line Tools (latest)` 선택 → `Apply` 를 선택해 주세요.

            ![Screen Shot 2022-09-20 at 2.44.40 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/86cdd3d2-0c6d-4bf0-9044-3b1640723ad3/Screen_Shot_2022-09-20_at_2.44.40_PM.png)

        3. 팝업이 뜨면 `OK` 버튼을 클릭해 주세요.

            ![Screen Shot 2022-09-20 at 2.45.33 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5b5466b0-3eda-4a24-b326-5e8f361b0aac/Screen_Shot_2022-09-20_at_2.45.33_PM.png)

        4. 설치가 완료되면 `Finish` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/179110f8-4fef-49e6-a781-8672450baef3/Untitled.png)

    - 3) Android Virtual Devices 설치

        &amp;lt;aside&amp;gt;
          앱을 개발시 실제 휴대폰을 연결하여 개발을 진행할 때도 있지만, 대부분의 경우 Virtual Device(컴퓨터에 가상의 휴대폰을 띄우는 소프트웨어)를 이용하여 개발합니다.

        &amp;lt;/aside&amp;gt;

        1. `More Actions` → `Virtual Device Manager` (또는 `AVD Manager`)를 선택해 주세요.

            ![Screen Shot 2022-09-20 at 2.46.47 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e72abe9e-f703-405d-ada1-08eafbdb80e1/Screen_Shot_2022-09-20_at_2.46.47_PM.png)

        2. 이미 Device 가 있는 분들은 아래 절차를 진행하지 않으셔도 됩니다.

            ![Screen Shot 2022-09-20 at 2.51.09 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8d5b8d36-7e78-4ef2-911d-d5a5dc7ef4a4/Screen_Shot_2022-09-20_at_2.51.09_PM.png)

        3. (Device 가 없는 경우) `Create Virtual Device...`를 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/35588fa8-02ce-4a17-82b2-e4ebd4cc1924/Untitled.png)

        4. 하드웨어를 선택하는 화면이 나오면 `Next`를 눌러서 기본으로 설정된 `Pixel 2` 휴대폰을 설치하도록 하겠습니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fe1e55c6-ea12-4d75-b619-e41587b9e51b/Untitled.png)

        5. 휴대폰에 설치할 Android OS를 선택하는 화면입니다. `Q` 옆에 있는 `Download` 버튼을 클릭하여 OS를 다운로드해 주세요.

            &amp;lt;aside&amp;gt;
              R 버전은 Virtual Device에서 문제가 있다고 해요. 그래서 **Q 버전**으로 진행할게요!

            &amp;lt;/aside&amp;gt;

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4ca338ba-a53c-448a-84fa-be1735599608/Untitled.png)

        6. 설치가 완료되면 `Finish` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/58629f13-a9e6-4ddd-ac93-acc9441fbfab/Untitled.png)

        7. `Q` 옆에 `Download` 버튼이 사라졌습니다. 우측에 현재 선택된 OS 버전이 29인지 확인한 뒤 `Next` 버튼을 눌러주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6d8fade8-aa03-46b7-b417-c55b3b7b2342/Untitled.png)

        8. `Finish` 버튼을 눌러 Virtual Device 설치를 완료해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/23afd0e5-79c6-426e-9ee3-690f6998ba43/Untitled.png)

        9. 아래와 같이 `Pixel 2 API 29` 라는 Virtual Device가 추가되었습니다.

            이제 좌측 상단에 빨간 버튼을 눌러 창을 종료해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1134aa4e-c890-4437-9820-264c40413b8a/Untitled.png)

        10. 하단에 Android Studio 아이콘을 우클릭한 뒤 `종료` 버튼을 눌러를 Android Studio를 종료해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0ffcab44-bef5-43d2-b10a-7d0c1d1b1a86/Untitled.png)

            &amp;lt;aside&amp;gt;
              Android Studio를 클릭한 상태에서 단축키(`Cmd + Q`)를 누르셔도 됩니다.

            &amp;lt;/aside&amp;gt;

    - 4) Android Licenses
        1. 터미널에서 `flutter doctor`라고 입력한 뒤 엔터를 누릅니다.
        아래와 같이 `Android toolchain`의 좌측에  `[!]` 표시가 되어있습니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cfa0d817-282d-4edd-b7e6-0178ad077990/Untitled.png)

        2. 문제를 해결하기 위해 `flutter doctor --android-licenses`를 복사해서 터미널에 붙여 넣고 실행해 주세요. 실행시 라이센스에 대한 동의를 여러번 구하는데, `y`를 입력하고 엔터를 눌러 진행해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d0492492-b2e0-4205-8875-71fd7c3bc5ce/Untitled.png)

        3. 모든 동의가 완료되면 `All SDK package licenses accepted` 라고 뜹니다.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ef18b6df-8d01-40f7-9657-8b7cfbda449e/Untitled.png)

        4. 마지막으로 터미널에 `flutter doctor` 를 입력했을 때 아래와 같이 `Android toolchain`, `Android Studio` 가 체크 완료되면 완료!

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bfb47eed-12cb-418d-86f0-9044be87cfba/Untitled.png)

- 4. Xcode

    &amp;lt;aside&amp;gt;
      iOS 앱을 개발하는데 필요한 Xcode를 설치해 보도록 하겠습니다.

    &amp;lt;/aside&amp;gt;

    1. [링크](https://apps.apple.com/us/app/xcode/id497799835)를 클릭해 열어주세요.
    2. 아래와 같은 팝업이 띄면 `App Store 열기` 버튼을 눌러주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dd013e1b-8bc8-415c-8826-7daf6173016b/Untitled.png)

    3. `App Store`가 실행되고 `Xcode`가 아래와 같이 뜨면 `받기` 버튼을 눌러주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/63f8b6d9-4a9d-4c7d-918c-c50d2bf3299c/Untitled.png)

    4. `설치` 버튼으로 변하면 한 번 더 눌러주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/12804375-a9da-4223-9e13-730cfc461d7c/Untitled.png)

    5. 만약 App Store에 Apple ID로 로그인이 되어있지 않아 아래와 같이 창이 뜨는 경우, 로그인을 진행해 주세요.

        &amp;lt;aside&amp;gt;
          계정이 없다면 `Apple ID 생성`을 눌러 가입을 진행해주신 뒤, 로그인을 진행해 주세요.

        &amp;lt;/aside&amp;gt;

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/001c2d44-028a-4b49-962a-86773edc12c1/Untitled.png)

    6. 로그인을 완료하면 설치가 진행 됩니다.

        &amp;lt;aside&amp;gt;
          Xcode 설치 시간은 인터넷 상황에 따라 다르지만 보통 1시간 30분 ~ 2시간 정도 소요 됩니다.  

        &amp;lt;/aside&amp;gt;

    7. 설치가 완료되면 `열기` 버튼을 눌러주세요!

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/adcee5ed-0d4a-4782-8879-c9945d7d9e49/Untitled.png)

    8. 라이센스 동의 팝업이 뜨면 `Agree`를 선택해 주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2338d57a-712a-482b-a577-27921cb7f9f1/Untitled.png)

    9. 아래와 같이 암호를 입력하는 창이 뜨면 컴퓨터 시작 비밀번호를 입력한 뒤 `확인` 버튼을 눌러주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f3cf1771-5843-48fb-96e5-f6d1b30877bd/Untitled.png)

    10. 만약 Xcode가 실행이 안되면, 다시 `열기` 버튼을 눌러주세요.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/adcee5ed-0d4a-4782-8879-c9945d7d9e49/Untitled.png)


    12. 아래와 같은 창이 뜨면 Xcode 설치완료!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c7c37404-84c3-4289-8133-7ead2d049c85/Untitled.png)

    1. 설치를 완료했으니`AppStore`와 `Xcode`를 종료해 주세요.
        - 하단에 App Store를 우클릭하여 `종료`를 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e4b2242-4ea0-404d-9da3-4ad29444212e/Untitled.png)

        - 하단에 Xcode를 우클릭하여 `종료`를 선택해 주세요.

            ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ac0c0aeb-75bf-420a-8b63-591333ea792b/Untitled.png)

    2. 이제 Homebrew 를 설치하도록 하겠습니다.

        &amp;lt;aside&amp;gt;
          Homebrew 는 맥에서 소프트웨어를 설치 삭제할 수 있는 패키지 관리자입니다.
        아래에서 brew install 명령어를 쓰기 위해 미리 설치해줍니다.

        &amp;lt;/aside&amp;gt;

        아래 명령어를 복사해 터미널에 붙여넣고 엔터를 눌러주세요.

        ```bash
        /bin/bash -c &amp;quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&amp;quot;
        ```

        ![Screen Shot 2022-09-20 at 3.11.02 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a3828ed3-a3e0-4880-aa76-d312f986ecd1/Screen_Shot_2022-09-20_at_3.11.02_PM.png)

    3. 다음으로 CocoaPods을 설치해 보도록 하겠습니다.

        &amp;lt;aside&amp;gt;
          CocoaPods은 다른 사람이 만든 코드를 가져올 때 필요한 프로그램으로 Xcode와 함께 iOS 앱 개발시 필요합니다.

        &amp;lt;/aside&amp;gt;

        아래 명령어를 복사해 터미널에서 붙여넣고 엔터를 눌러주세요.

        ```bash
        brew install cocoapods
        sudo gem install cocoapods
        ```

    4. 아래와 같이 비밀번호를 입력하는 창이 나오면, 컴퓨터 시작시 입력하는 비밀번호를 입력한 뒤 엔터를 눌러주세요.

        &amp;lt;aside&amp;gt;
          참고로 키보드를 눌러도 화면에 입력되는 모습은 보이지 않으니, 입력한 뒤 엔터를 누르면 됩니다.

        비밀번호를 틀린 경우, `Ctrl + C`를 누르면 명령이 종료되고, 다시 명령어를 붙여넣고 실행해주세요.

        &amp;lt;/aside&amp;gt;

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e74269ff-349a-4ae5-addf-2b5d3344dd54/Untitled.png)

    5. 명령이 정상적으로 실행되면 아래와 같이 뜹니다.

        ![Screen Shot 2022-09-20 at 3.12.24 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/06f113d2-7915-4d06-ba45-5a2f9c3b521c/Screen_Shot_2022-09-20_at_3.12.24_PM.png)

    6. 아래 명령어를 복사해 터미널에서 실행해 주세요.

        ```bash
        flutter doctor
        ```

        그러면 아래와 같이 `Xcode` 설치가 완료된 것을 보실 수 있습니다.

        ![Screen Shot 2022-09-20 at 3.13.31 PM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dbcf4554-1f37-4303-a74e-f828f447b5ef/Screen_Shot_2022-09-20_at_3.13.31_PM.png)


**설치를 완료하셨나요~? 잘 설치되었는지 확인해봅시다! ㅎㅎ**

- 최종 설치 확인

    터미널에서 `flutter doctor`라고 검색한 뒤 아래와 같이 화면이 나온다면 모든 설치가 완료하신 것입니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/610c4f60-5d66-4495-baf7-7a4b82fa0d6d/Untitled.png)

    &amp;lt;aside&amp;gt;
      다운로드 폴더에 있는 파일들은 모두 삭제하셔도 됩니다.

    &amp;lt;/aside&amp;gt;

    &amp;lt;aside&amp;gt;
      고생하셨습니다! 원래 개발 환경을 설정하는데 시간이 많이 들어갑니다  
    그럼 1주 차 수업 때 뵙도록 하겠습니다  

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;03. Flutter 이해하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 레고 같이 조립할 수 있는 위젯(Widget)&lt;/p&gt;
  &lt;aside&gt;
    Flutter는 모든 것이 위젯(Widget)으로 만들어져 있습니다.
  **위젯(Widget)**은 레고 블럭과 같이 앱을 만드는데 사용되는 작은 모듈입니다.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/71e2396b-c6ac-4ccc-acb9-3338ffe4a7d9/Untitled.png&quot; alt=&quot;출처 : [pixabay](https://pixabay.com/vectors/lego-toys-blocks-puzzle-6390233/)&quot;&gt;&lt;/p&gt;
&lt;p&gt;  출처 : &lt;a href=&quot;https://pixabay.com/vectors/lego-toys-blocks-puzzle-6390233/&quot;&gt;pixabay&lt;/a&gt;&lt;/p&gt;
  &lt;aside&gt;
    위젯들을 조합해서 화면을 그리는데, 이 위젯들의 조합이 나무와 같이 생겼다하여 
  **위젯 트리(Widget Tree)**라고 부릅니다.

  &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) &lt;strong&gt;Android Material &amp;amp;&lt;/strong&gt;  &lt;strong&gt;iOS Cupertino&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  코드스니펫을 복사해서 새 탭에서 열면 Flutter에서 위젯을 소개하는 공식 문서가 보입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://docs.flutter.dev/development/ui/widgets&quot;&gt;&lt;strong&gt;[코드스니펫] Flutter 위젯 카탈로그&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://docs.flutter.dev/development/ui/widgets&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![Widget Catalog.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d3775be8-a1eb-49e0-b5a1-84e49f827086/Widget_Catalog.png)

&amp;lt;aside&amp;gt;
  **머터리얼 위젯(Material Widget)**은 **Android**에서 사용되는 기본 화면 구성 요소를 Flutter에서 재현한 위젯입니다.

**쿠퍼티노 위젯(Cupertino Widget)**은 **iOS**에서 사용되는 화면 구성 요소를 Flutter에서 재현한 위젯입니다.

Flutter는 특정 플랫폼에 종속되지 않은 고유의 디자인을 입힌 **커스텀 위젯(Custom Widget)**도 쉽게 만들 수 있습니다.

&amp;lt;/aside&amp;gt;

&amp;lt;aside&amp;gt;
  **Material**, **Cupertino** 그리고 **Custom 위젯** 중 어떤 방법을 사용하든 **사용성만 해치지 않는다면** 앱을 출시하실 수 있습니다.

&amp;lt;/aside&amp;gt;

&amp;lt;aside&amp;gt;
  Flutter에서 위젯이 어떻게 사용되는지 로그인 페이지를 만들며 배워봅시다.

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;05. 프로젝트 준비&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) Flutter 프로젝트 생성하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;바탕화면에 &lt;code&gt;flutter&lt;/code&gt; 폴더를 만들어주세요.&lt;/p&gt;
&lt;p&gt; (이 때, 플러터 프로젝트 경로에 &lt;strong&gt;한글이 오지 않도록 주의&lt;/strong&gt;해주세요!)&lt;/p&gt;
 &lt;aside&gt;
   앞으로 만들어볼 앱들을 flutter 폴더에 모아두도록 하겠습니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/330f9a0d-0a6e-42db-9072-2c1e749517c0/Screen_Shot_2022-08-28_at_7.40.46_PM.png&quot; alt=&quot;Screen Shot 2022-08-28 at 7.40.46 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Visual Studio Code(VSCode)를 실행해 주세요.&lt;/p&gt;
 &lt;aside&gt;
   VSCode 사용법을 인터넷에 검색해보면 대부분 영문으로 된 자료가 많기 때문에 **한국어 언어 팩은 설치하지 않는 것**을 권장 드립니다. (팝업 우측 상단에 톱니바퀴 아이콘을 누르고 `Don't Show Again`을 누르면 다시는 안뜹니다.)

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55785a22-0a46-4387-a63c-bd93ebb5147d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2752c45-9037-42b6-81fb-496858e59835/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt; 버튼을 클릭해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Command Palette` 기능은 앞으로도 자주 사용하게 될 거라 단축키를 알아두시면 좋습니다.
 window : `Ctrl + Shift + P`
 macOS : `Cmd + Shift + P`

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2e8bac1-770e-4f78-80a8-5b799f1f40e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;명령어를 검색하는 팝업창이 뜨면, &lt;code&gt;flutter&lt;/code&gt;라고 입력한 뒤 &lt;code&gt;Flutter: New Project&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a6058de-bbf7-410f-9dc3-a96d6bd7ac58/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Application&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c7145f5-c6f6-4306-b78e-e3bb31e24710/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트를 시작할 폴더를 선택하는 과정입니다. 미리 생성해 둔 &lt;code&gt;flutter&lt;/code&gt; 폴더를 선택한 뒤 &lt;code&gt;Select a folder to create the project in&lt;/code&gt; 버튼을 눌러 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b1f10615-9531-4938-9e35-d6dab0c4d56d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트 이름을 &lt;code&gt;hello_flutter&lt;/code&gt;로 입력해주세요.&lt;/p&gt;
 &lt;aside&gt;
   프로젝트 이름은 영어 소문자와 언더바(_)만 사용 가능합니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/02bccccb-7533-434e-985f-ab98a1a6fdc7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; macOS의 경우 아래와 같은 팝업이 뜨면 &lt;code&gt;확인&lt;/code&gt; 버튼을 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/88067ee0-d97f-476e-b2da-7128a6a2c39c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 중간에 아래와 같은 팝업이 뜬다면, 체크박스를 선택한 뒤 파란 버튼을 클릭해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6827f86-f59a-4808-9d8d-75245b9ec10d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음과 같이 프로젝트가 생성됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5d355592-e1a3-4ae2-ba33-abe79d43c48a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 왼쪽 폴더 구조를 살짝 보고 가도록 하겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/07c9123f-135e-4980-a191-41d813205af4/Directory.png&quot; alt=&quot;Directory.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;lib&lt;/code&gt; : 주로 코딩하는 폴더&lt;/p&gt;
&lt;p&gt; &lt;code&gt;pubspec.yaml&lt;/code&gt; : 라이브러리 및 설정을 하는 폴더&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt; &lt;code&gt;android&lt;/code&gt; : Android 프로젝트 폴더&lt;/p&gt;
&lt;p&gt; &lt;code&gt;ios&lt;/code&gt; :  iOS 프로젝트 폴더&lt;/p&gt;
&lt;p&gt; &lt;code&gt;web&lt;/code&gt; : Web 프로젝트 폴더&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래 &lt;code&gt;main.dart&lt;/code&gt; 코드스니펫을 복사해서 기존 코드를 모두 지운 뒤, &lt;code&gt;main.dart&lt;/code&gt; 파일에 붙여 넣고 저장해주세요.&lt;/p&gt;
 &lt;aside&gt;
   저장 단축키

&lt;p&gt; window : &lt;code&gt;Ctrl + S&lt;/code&gt;&lt;br&gt; macOS : &lt;code&gt;Cmd + S&lt;/code&gt;&lt;/p&gt;
 &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫]  main.dart&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
          appBar: AppBar(),
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음으로 학습 단계에서 불필요한 내용을 화면에 표시하지 않도록 설정해 주겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;analysis_options.yaml&lt;/code&gt; 파일을 열고, 아래 코드스니펫을 복사해서 24번째 라인 뒤(rules 뒤)에 붙여 넣고 저장해 주세요. (들여쓰기를 꼭 맞춰주세요)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] analysis_options.yaml&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      prefer_const_constructors: false
      prefer_const_literals_to_create_immutables: false&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8470956b-ccc2-4d75-86d8-83042bafaf22/Untitled.png)

    &amp;lt;aside&amp;gt;
      상세 내용은 아래를 참고해 주세요.

    - 어떤 의미인지 궁금하신 분들을 위해

        `main.dart` 파일을 열어보시면 파란 실선이 있습니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d1393a08-a362-4be3-bd9b-835e184f5ef3/Untitled.png)

        파란 줄은, 개선할 여지가 있는 부분을 VSCode가 알려주는 표시입니다.

        12번째 라인에 마우스를 올리면 아래와 같이 설명이 보입니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7fd5cee4-d935-4d2d-9c35-e11d4f3c6cae/Untitled.png)

        위젯이 변경될 일이 없기 때문에 `const`라는 키워드를 앞에 붙여 상수(변하지 않는 수)로 선언하라는 힌트입니다. 화면을 새로고침 할 때(화면 내 값이 변할 때마다 화면은 새로고침 됩니다) 상수로 선언된 위젯들은 새로고침을 할 필요가 없어서 성능상 이점이 있습니다.

        아래와 같이 `Icon`앞에 `const` 키워드를 붙여주시면 됩니다.

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2aef9ee8-c4ef-4c42-bd08-d358c4620341/Untitled.png)

        지금은 학습 단계이니 눈에 띄지 않도록 `analysis_options.yaml` 파일에 아래와 같이 설정하여 힌트를 숨기도록 하겠습니다.

        ```dart
            prefer_const_constructors: false
            prefer_const_literals_to_create_immutables: false
        ```

    &amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) VSCode Dart 세팅&lt;/p&gt;
  &lt;aside&gt;
    VSCode에서 Flutter 개발시 좀 더 수월하게 도와주는 설정을 추가해 볼게요!

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt;를 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2e8bac1-770e-4f78-80a8-5b799f1f40e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래와 같이 &lt;code&gt;dart recommend&lt;/code&gt;라고 검색한 뒤 &lt;code&gt;Dart: Use Recommended Settings&lt;/code&gt;를 선택해 주세요. 그러면 &lt;strong&gt;저장 시 자동 줄 정렬 기능&lt;/strong&gt;과 같이 편의 기능 설정이 적용됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/15eba0b2-ef5b-4f6f-bf6a-fa937ca21af4/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   설정에 대한 상세한 내용은 아래 링크를 참고해 주세요.

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://dartcode.org/docs/recommended-settings/&quot;&gt;&lt;strong&gt;[코드스니펫] Dart Code Recommended Setting&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartcode.org/docs/recommended-settings/&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) Emulator 실행하기&lt;/p&gt;
  &lt;aside&gt;
    에뮬레이터(Emulator)란, 실제 기기를 연결하지 않고도 개발할 수 있도록 만든 컴퓨터 상의 가상의 휴대폰을 의미합니다.

&lt;p&gt;  에뮬레이터를 이용하면 실제 해당 기기가 없어도 테스트를 진행할 수 있습니다.&lt;br&gt;  단, 카메라나 Apple 로그인과 같이 특정 기능은 실제 기기에서만 테스트할 수 있는 경우도 있습니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palatte&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b5f2c68a-f17f-4985-a9b0-7a410bdbe108/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;launch&lt;/code&gt;를 입력한 뒤 &lt;code&gt;Flutter: Launch Emulator&lt;/code&gt;를 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/00b86cc2-41b0-4544-ab3d-4c7501243694/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;android&lt;/code&gt; 에뮬레이터를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/39c9a1c5-3f33-431c-9cd9-96f96ed9c898/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; macOS의 경우, &lt;code&gt;Start iOS Simulator&lt;/code&gt;를 선택하실 수도 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8609f66c-fdca-4194-b184-372bf7c68e5e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; macOS의 경우 아래와 같은 팝업이 뜬다면 &lt;code&gt;확인&lt;/code&gt; 버튼을 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e3046d78-1c15-48e0-8122-9d0fbd5ee6bb/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;잠시 기다리시면, 에뮬레이터가 실행 됩니다. 아래와 같이 완전히 부팅이 끝날 때 까지 기다려주세요!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0491ec0b-8057-48d5-be64-a87ba5ff734b/emulator.png&quot; alt=&quot;emulator.png&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   VSCode 우측 하단에 현재 선택된 에뮬레이터가 표시됩니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cc763f75-6925-4d8a-9ad2-aec5f1de532f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) 실행하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;VSCode 우측 상단에 &lt;code&gt;ⅴ&lt;/code&gt; 버튼을 누르고 &lt;code&gt;Run Without Debugging&lt;/code&gt; 버튼을 클릭해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Start Debugging`과 `Run Without Debugging` 모두 앱을 실행할 수 있지만, `Run Without Debugging`으로 실행하는게 성능상 더 빠릅니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/49d5aaf4-f24e-4d03-8a8c-13be21925dea/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;첫 번째 실행은 시간이 다소 소요되니 잠시만 기다려주세요!&lt;/p&gt;
&lt;p&gt; 드디어 첫 번째 앱 화면을 띄웠습니다! 축하드립니다 &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fb9524b1-75e0-45f5-840d-c9357df06688/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   위 기기는 iOS 13 에뮬레이터로 Android 또는 iOS 에뮬레이터 버전에 따라 다르게 보일 수 있습니다.

 &lt;/aside&gt;

 &lt;aside&gt;
   아래와 같이 VSCode 하단에 창이 뜨면 `X` 또는 `Never Ask` 버튼을 눌러서 화면을 넓게 사용할 수 있습니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c603b4b9-c947-429d-a9df-3271856e39b8/console.png&quot; alt=&quot;console.png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;다시 켜는 방법이 궁금하다면?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;VS Code 의 좌측 아래 설정 버튼을 누르고, &lt;code&gt;Settings&lt;/code&gt; 를 눌러주세요&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/13268a85-6b6d-4261-a134-17e02ae0a77d/Screen_Shot_2022-08-28_at_9.22.58_PM.png&quot; alt=&quot;Screen Shot 2022-08-28 at 9.22.58 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;검색창에 &lt;code&gt;widget inspect&lt;/code&gt; 라고 검색하시고, &lt;code&gt;Dart: Open Dev Tools&lt;/code&gt; 옵션을 never 에서 flutter 로 변경해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f96b22b9-e2b0-4f9b-af10-2a63b343e8db/Screen_Shot_2022-08-28_at_9.24.45_PM.png&quot; alt=&quot;Screen Shot 2022-08-28 at 9.24.45 PM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 앱을 실행할 때 아래와 같이 오른쪽에 &lt;code&gt;Widget Inspector&lt;/code&gt; 탭이 열립니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9c2c3d82-3fd8-4f0e-bf75-8d9c7ab5b80d/Screen_Shot_2022-08-29_at_4.03.00_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 4.03.00 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;05. 로그인 페이지 만들기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;최종 완성 이미지&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1092a6a6-107f-4b85-aecd-b72ddd019ec4/Screen_Shot_2022-08-29_at_6.28.42_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 6.28.42 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1) Scaffold &amp;amp; Text&lt;/p&gt;
&lt;p&gt;  &lt;code&gt;lib&lt;/code&gt; 폴더 밑에 있는 &lt;code&gt;main.dart&lt;/code&gt; 파일에서 코딩을 해보도록 하겠습니다.&lt;/p&gt;
  &lt;aside&gt;
    아래 이미지에 표시한 것과 같이 14번째 ~ 16번째줄에 `home`이라고 되어있는 부분이 화면에 보이는 영역이므로 일단 여기만 집중해주세요.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0dc36a47-f6f7-4844-8354-3e334ece18e2/Group_90.png&quot; alt=&quot;Group 90.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;  14번째 줄을 보면 &lt;code&gt;home&lt;/code&gt;이라고 되어있습니다. 이곳에 첫 화면에 보여줄 위젯을 넣어주는데, 여기에 &lt;code&gt;Scaffold&lt;/code&gt;라는 위젯이 사용되고 있습니다. &lt;/p&gt;
  &lt;aside&gt;
    건물을 지을 때 공사장에 먼저 비계(Scaffold)를 설치하고 작업을 시작합니다.
  앱에서도 화면을 그릴 때 `Scaffold` 위젯으로 시작합니다.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4dde6d3f-175e-483e-95e0-80337ed4436b/Untitled.png&quot; alt=&quot;출처 - [https://pixabay.com/photos/construction-work-framework-670278/](https://pixabay.com/photos/construction-work-framework-670278/)&quot;&gt;&lt;/p&gt;
&lt;p&gt;  출처 - &lt;a href=&quot;https://pixabay.com/photos/construction-work-framework-670278/&quot;&gt;https://pixabay.com/photos/construction-work-framework-670278/&lt;/a&gt;&lt;/p&gt;
  &lt;aside&gt;
    Scaffold 위젯은 한 페이지의 특정 영역에 위젯을 쉽게 배치할 수 있도록 틀을 잡아주는 역할을 합니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  Scaffold(
      appBar: 다른 위젯, // 상단 바
      body: 다른 위젯, // 화면 중앙에 가장 큰 면적
      bottomNavigationBar: 다른 위젯, // 하단 바
      floatingActionButton: 다른 위젯, // 하단 화면 최상위에 떠있는 버튼
  ),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b50d2d69-029a-49d2-a75b-8ebc16e353a4/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  15번째 줄에 Scaffold의 appBar 영역에 &lt;code&gt;AppBar()&lt;/code&gt; 위젯이 들어가 에뮬레이터에 상단 영역을 차지하고 있습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/195f5d70-9edd-499e-a2a8-a47956ea4f01/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Scaffold에 &lt;code&gt;body&lt;/code&gt; 영역에 Text 위젯을 추가해보겠습니다. 16번째 줄에 &lt;code&gt;body: Text(&amp;quot;Hello Flutter&amp;quot;),&lt;/code&gt; 라고 입력한 뒤 저장(&lt;code&gt;Ctrl/Cmd + S&lt;/code&gt;)하면 에뮬레이터에 &lt;code&gt;Hello Flutter&lt;/code&gt;가 나타납니다.&lt;/p&gt;
 &lt;aside&gt;
   저장 단축키
 window : `Ctrl + S`
 핫 macOS : `Cmd + S`

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9ab64226-b410-440b-8750-2c5c4b3b9a73/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   **핫 리로드**
 저장시 에뮬레이터에 바로 반영되는 기능을 **핫 리로드(Hot Reload)**라고 부릅니다.
 변경 사항들이 바로바로 반영되기 때문에 빠르게 개발을 진행할 수 있어요!

&lt;p&gt; 앱 시작시 VSCode 우측 상단에 아래와 같은 컨트롤 바가 나타나는데, 번개 모양 아이콘을 눌러 핫 리로드를 호출 할 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ffb58924-b346-4282-858d-bba3b9c5dc36/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 일부 코드는 핫 리로드로 갱신이 안되는데, 이 때는 restart 버튼을 누르면 됩니다.&lt;/p&gt;
 &lt;/aside&gt;

 &lt;aside&gt;
   **코드** **정렬**
 `body: Text(&quot;Hello Flutter&quot;),` 맨 뒤에 `콤마(,)`를 안붙이면 저장시 아래와 같이 정렬이 되므로 뒤에 `콤마(,)`를 붙여주세요.

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c5efeccc-5fa4-4bad-9dba-5210648ac871/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;/aside&gt;

 &lt;aside&gt;
   텍스트를 보여줄 때는 Text 위젯을 사용합니다. 
 위젯에 미리 지정된 속성(property)값을 변경해 위젯의 모양을 바꿀 수 있습니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; Text(
   &amp;#39;텍스트 위젯&amp;#39;,
   style: TextStyle(
     fontSize: 35, // 폰트 크기
     fontWeight: FontWeight.bold, // 폰트 두께
     color: Colors.amber, // 폰트 색상
   ),
 ),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/63bc10f5-01ec-4a94-b915-7857525897ea/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;16번째 줄에 텍스트 크기를 키워보도록 하겠습니다.&lt;/p&gt;
 &lt;aside&gt;
   Text 위젯이 어떤 속성을 가지고 있는지, 각각에 어떤 값을 넣으면 어떻게 변하는지 모두 외우지 않아도 됩니다.
 **자동완성 기능**을 적극적으로 이용하면 편하고 빠르게 개발할 수 있습니다 

 &lt;/aside&gt;

&lt;p&gt; 먼저 16번째 줄 &lt;code&gt;“Hello Flutter&amp;quot;&lt;/code&gt; 뒤에 &lt;code&gt;콤마(,)&lt;/code&gt;를 추가한 뒤 저장(&lt;code&gt;Ctrl/Cmd + S&lt;/code&gt;)해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/917b67d3-0d50-456c-b979-15ef5a779d6c/Group_91.png&quot; alt=&quot;Group 91.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 17번째 줄 맨 뒤에서 아래 자동완성 단축키를 눌러주세요.&lt;/p&gt;
 &lt;aside&gt;
   **자동완성 단축키**

&lt;p&gt; window : &lt;code&gt;Ctrl + Space&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; macOS : &lt;code&gt;option + Esc&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; 수업 자료에서 설명된 단축키는 &lt;a href=&quot;https://www.notion.so/8f93d0afe8b449a3ac4702727ef14889&quot;&gt;링크&lt;/a&gt;에서 모아볼 수 있습니다.&lt;/p&gt;
 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f39dd945-552a-4529-9f04-5f2d86e53498/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그러면 &lt;code&gt;Text&lt;/code&gt; 위젯이 가진 &lt;strong&gt;속성 목록&lt;/strong&gt;이 나옵니다. 이 중에서 &lt;code&gt;style&lt;/code&gt;을 이용하면 뭔가 꾸밀 수 있을 것 같다는 느낌이 옵니다. &lt;code&gt;style&lt;/code&gt;을 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b4505032-7bdc-4ac5-8bd7-5652d1458cec/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그러면 위와 같이 &lt;code&gt;style&lt;/code&gt; 속성이 추가되는데 여기에 무엇을 넣어야하는지 궁금하실 겁니다.&lt;/p&gt;
&lt;p&gt; 이런 경우, &lt;strong&gt;style 속성에 마우스를 올리면&lt;/strong&gt; 여기에 넣는 값의 타입을 볼 수 있습니다. 아래 이미지를 보면 &lt;code&gt;style&lt;/code&gt;의 경우&lt;code&gt;TextStyle&lt;/code&gt;을 받는다고 적혀있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/500b7cbe-b848-4ddb-8019-5151453f1ded/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;TextStyle&lt;/code&gt;을 조금만 타이핑해 보면 자동완성으로 아래와 같이 &lt;code&gt;TextStyle&lt;/code&gt;을 추천해 줍니다. 아래 이미지에서 첫 번째 항목을 선택하면 &lt;code&gt;TextStyle&lt;/code&gt;까지만 완성해주고, 네 번째 항목을 선택하면 &lt;code&gt;TextStyle()&lt;/code&gt;까지 완성해 주므로 네 번째 항목을 선택해 줍니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/227c983a-c9d6-4ad3-8285-81628f18e648/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 저장해주면 아래와 같이 18번째 줄에 &lt;code&gt;style: TextStyle(),&lt;/code&gt;이 추가 됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eedd3baa-a434-4a37-8d5d-59750cb4ec00/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;TextStyle&lt;/code&gt;에 어떤 값을 넣을 수 있는지 자동완성 기능으로 보도록 하겠습니다. 18번째 줄 &lt;code&gt;TextStyle&lt;/code&gt;의 소괄호 사이에서 자동완성 단축키(&lt;code&gt;Ctrl + Space / Option + Esc&lt;/code&gt;)를 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0c7932ca-1164-4344-bdba-dd3cd627138a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 위와 같이 추천된 속성 중에 &lt;code&gt;fontSize&lt;/code&gt;를 이용하면 텍스트 크기를 변경할 수 있을 것 같습니다. &lt;code&gt;fontSize&lt;/code&gt;를 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7ecc4f92-6456-41ec-aa77-f54a41dd5e43/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;code&gt;fontSize&lt;/code&gt;에는 숫자를 넣으시면 됩니다(&lt;code&gt;double&lt;/code&gt;은 실수를 의미하는 자료형입니다). 28을 입력한 뒤 저장해보면 에뮬레이터에서 폰트 크기가 커진 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ef39c11e-b2ae-4f29-a296-de3277a2bb41/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) Column &amp;amp; TextField&lt;/p&gt;
&lt;p&gt;  Text 위젯 밑에 ID를 입력받는 입력창을 추가해 보도록 하겠습니다. 세로 방향으로 위젯들을 나열하려면  &lt;code&gt;Column&lt;/code&gt; 위젯을 사용해야합니다.&lt;/p&gt;
  &lt;aside&gt;
    Column 위젯은 세로 방향으로 여러 위젯을 나열할 때 사용합니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  Column(
    children: [ // 자식 위젯들
      Text(&amp;quot;위젯1&amp;quot;),
      Text(&amp;quot;위젯2&amp;quot;),
    ],
  ),&lt;/code&gt;&lt;/pre&gt;
  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;16번째 줄에 &lt;code&gt;Text&lt;/code&gt; 위젯을 클릭한 뒤, 왼쪽에 있는 전구( ) 아이콘을 클릭해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bacfce92-8186-4239-88ec-4154b1db2343/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그러면 아래와 같이 &lt;code&gt;Text&lt;/code&gt; 위젯을 손쉽게 다른 위젯을 감싸거나 추출할 수 있도록 도와주는 &lt;code&gt;리펙터(Refactor)&lt;/code&gt; 기능이 나타납니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f487ddea-a636-4614-b7bc-e3d3970dd769/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   리펙터(Refactor)는 위와 같이 전구 아이콘을 누르거나 위젯을 마우스 우클릭하여 `Refactor`를 선택할 수 도 있습니다.

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/390f17d2-9654-4809-8fdb-02e8542bcaee/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 또한 자주 사용하므로 단축키를 외워두시는 것도 좋습니다.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;리펙터 단축키&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; window / macOS : &lt;code&gt;Ctrl + Shift + R&lt;/code&gt;&lt;/p&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Wrap with Column&lt;/code&gt;을 선택하여 &lt;code&gt;Text&lt;/code&gt; 위젯을 &lt;code&gt;Column&lt;/code&gt; 위젯으로 감싸주겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/238c1dd5-cf86-4124-87ab-6a847f5219fe/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그러면 &lt;code&gt;Text&lt;/code&gt; 위젯이 &lt;code&gt;Column&lt;/code&gt; 위젯의 children 속성으로 들어간 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/60c663a4-23a4-4f64-a76b-07025bf250c1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;21번째 줄 맨 뒤에, &lt;code&gt;TextField(),&lt;/code&gt;를 추가해주세요.&lt;/p&gt;
 &lt;aside&gt;
   Flutter에서 텍스트 입력을 받을 때 **`TextField`** 위젯을 사용합니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/397f1215-00df-432c-9e52-9c7b59e12174/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 저장(&lt;code&gt;Ctrl/Cmd + S&lt;/code&gt;)해주면  아래와 같이 에뮬레이터에 텍스트 입력란이 생성됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ecd8ce42-2943-4694-9f26-d9ff20af054a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;iOS 에뮬레이터에서 가상 키보드 보이도록 설정하는 법&lt;/p&gt;
&lt;p&gt;  실제 모바일 환경에서는 가상 키보드가 화면에 나타나는 부분을 고려하여 화면을 만들어야 합니다. 이럴 때 아래와 같은 방법을 이용해주세요.&lt;/p&gt;
&lt;p&gt;  iOS 에뮬레이터에서 처음에 텍스트 입력이 안되는 경우, 에뮬레이터를 선택한 뒤 상단에 &lt;code&gt;I/O&lt;/code&gt; → &lt;code&gt;Keyboard&lt;/code&gt; → &lt;code&gt;Connect Hardware Keyboard&lt;/code&gt;를 선택해 주세요.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/98dfcbec-8c75-48e6-84a7-5a29b30afb9d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  그리고 &lt;code&gt;Hello Flutter&lt;/code&gt; 텍스트 밑에 파란 줄을 클릭하고 키보드를 입력하면 입력이 잘 됩니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4e18fb28-2e38-46f0-bfbc-0c480849e538/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  키보드를 보이게 하고 싶은 경우, 상단에 &lt;code&gt;I/O&lt;/code&gt; → &lt;code&gt;Keyboard&lt;/code&gt; → &lt;code&gt;Connect Hardware Keyboard&lt;/code&gt;를 다시 선택하여 체크를 해제하면 키보드를 눌러도 입력되지 않고, 에뮬레이터에 나타난 가상 키보드를 입력할 수 있습니다. 키보드를 내리려면 &lt;code&gt;완료&lt;/code&gt; 버튼을 눌러주세요.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bf3b9e88-ca64-4e91-ae16-d20f5a1fbcaf/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  mac OS 의 경우 &lt;code&gt;Cmd + K&lt;/code&gt; 를 눌러 가상 키보드를 올리고 내릴 수 있습니다&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;TextField&lt;/code&gt;에 이메일을 입력하도록 이름표를 달아 봅시다. 아래 이미지와 같이 &lt;code&gt;TextField&lt;/code&gt;에 &lt;code&gt;decoration&lt;/code&gt;속성에  &lt;code&gt;InputDecoration()&lt;/code&gt;을 넣고, &lt;code&gt;labelText: &amp;quot;이메일&amp;quot;,&lt;/code&gt;를 추가해주세요.&lt;br&gt;(쉼표를 잘 찍어서 코드가 정렬되도록 합니다)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1900a16c-ae09-470a-98ad-d931ab9eebbb/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 에뮬레이터 상에 “이메일”이라는 label이 생성 되었습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a0d7066d-30a6-429d-b9b6-55fab94542b5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;22 ~ 26번째 줄에 &lt;code&gt;TextField&lt;/code&gt;를 그대로 복사해서 비밀번호 입력란을 만들어 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dc32b91c-66e2-431d-8274-7fec56585eda/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9136852e-c91a-4fb1-a654-5885c1fe2c7a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 값을 입력해보면 비밀번호가 그대로 보입니다. 입력된 비밀번호를 보이지 않도록 만들어봅시다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5a38b4c1-013e-4ed3-92f8-ffc597a83050/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;28번째 줄에 &lt;code&gt;obscureText: true,&lt;/code&gt; 라는 속성을 주면 비밀번호가 안보이게 됩니다.&lt;br&gt;&lt;code&gt;TextField&lt;/code&gt; 위젯이 이미 속성들을 가지고 있기 때문에 우리는 해당 속성에 적절한 값만 넣어주면 됩니다!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/46249906-92da-40a5-8dd5-810451a75cbe/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) Button&lt;/p&gt;
&lt;p&gt;  로그인 버튼을 만들어 봅시다.&lt;/p&gt;
  &lt;aside&gt;
    Flutter는 클릭 이벤트를 받는 다양한 Button을 제공합니다.

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7bbd3610-1b2e-4d61-9bda-b63b4d43cffa/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  // 위로 올라와 있는 듯한 버튼
  ElevatedButton(
    onPressed: () {},
    child: Text(&amp;#39;Elevated Button&amp;#39;),
  ),

  // 텍스트 버튼
  TextButton(
    onPressed: () {},
    child: Text(&amp;#39;Text Button&amp;#39;),
  ),

  // 아이콘 버튼
  IconButton(
    onPressed: () {},
    icon: Icon(Icons.add),
  ),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  여기서 &lt;code&gt;onPressed&lt;/code&gt; 뒤에 오는 요소는 함수입니다.&lt;/p&gt;
&lt;p&gt;  일반적으로 dart 문법에서 함수는 아래처럼 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  add_func (a, b) {
      return a + b;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;code&gt;onPressed&lt;/code&gt; 뒤에 오는 &lt;code&gt;() {}&lt;/code&gt; 요소는 add_func 등의 이름이 없는 함수라는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;  버튼을 누르게 되면 &lt;code&gt;onPressed&lt;/code&gt; 에 담아준 함수를 실행하도록 Button 위젯 속에 미리 로직이 짜여있다고 생각하시면 됩니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;33번째 줄(Column 내부)에 &lt;code&gt;elev&lt;/code&gt;라고 입력하면 자동완성으로 &lt;code&gt;ElevatedButton()&lt;/code&gt;이 추천되고, 해당 항목을 선택해 주세요.&lt;/p&gt;
 &lt;aside&gt;
   만약 자동완성을 실수로 꺼버렸다면, 단축키 `Ctrl + Space` / `Option + Esc`를 눌러서 다시 볼 수 있습니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5a0f258e-345e-441e-ba08-c51830b29966/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 아래와 같이 완성이 되고, 버튼에 필수로 입력해야하는 속성 두 가지가 뜹니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6d5e44b-e3f9-44af-a972-c1ea94bc8d0a/Group_92.png&quot; alt=&quot;Group 92.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 33번째 줄 앞쪽 &lt;code&gt;onPressed&lt;/code&gt;에 마우스를 올려보면, 필수로 전달하라는 의미인 &lt;code&gt;required&lt;/code&gt;가 보이고 전달해야하는 타입은 함수를 의미하는 &lt;code&gt;void Function()&lt;/code&gt;이라고 적혀 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b7de025a-383d-4453-ba6d-0e9cbb18d23e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아직 문법을 배우지 않았으므로, 아래와 같이 &lt;code&gt;onPressed&lt;/code&gt;에 &lt;code&gt;() {}&lt;/code&gt;를 입력하고, child에 &lt;code&gt;Text(&amp;quot;로그인&amp;quot;),&lt;/code&gt; 이라고 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/84739a5a-9fb8-4c26-bd2c-f9cef360a0d5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 저장(&lt;code&gt;Ctrl/Cmd + S&lt;/code&gt;)를 누르면 아래와 같이 정렬됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b248329c-160d-4f21-8411-237510302cdc/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 에뮬레이터 상에 버튼이 추가된 것을 확인할 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/06fee691-eb41-4055-9095-d5121660f252/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) AppBar&lt;/p&gt;
&lt;p&gt;  15번째 줄에 &lt;code&gt;AppBar()&lt;/code&gt; 위젯에 18 ~ 21번째 줄의 Text를 넣어보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5ce9a0c8-9093-48e7-a53c-91eff44bdfb1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;15번째 줄 &lt;code&gt;AppBar()&lt;/code&gt;의 소괄호 사이 &lt;code&gt;title:&lt;/code&gt;이라고 입력해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3d622cd0-5568-495d-9f9b-3903b5294369/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;18 ~ 21번째 줄의 Text 위젯을 15번째 줄에 &lt;code&gt;title:&lt;/code&gt; 뒤로 이동해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f074b70c-e2c3-4a76-9657-82eefb4da6e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그리고 저장(&lt;code&gt;Ctrl/Cmd + S&lt;/code&gt;)을 눌러주시면 앱바에 title 영역으로 &lt;code&gt;Hello Flutter&lt;/code&gt;가 이동됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b7948005-7f4e-4126-ad43-bca09297f401/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   `AppBar` 위젯의 `title`은 iOS와 Android에서 서로 다르게 보입니다.

&lt;p&gt; iOS : 중앙 정렬&lt;/p&gt;
&lt;p&gt; Android : 왼쪽 정렬&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4e570c1f-6b4f-469e-93a8-995ba3a83a94/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;AppBar&lt;/code&gt;에 &lt;code&gt;centerTitle: true,&lt;/code&gt; 라고 넣어 두 플랫폼에서 모두 중앙 정렬이 되도록 만들어줍시다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/50d3e5a1-1853-47f9-b6b6-5b605b6829b2/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 이제 Android에서도 중앙 정렬이 됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2df1f49d-9022-47be-b071-e56b30eb70b1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5) Padding&lt;/p&gt;
&lt;p&gt;  에뮬레이터 상에서 보면 &lt;code&gt;TextField&lt;/code&gt;가 기기 외곽에 너무 붙어 있는 것 같아 여백을 추가해 보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4af5bef4-c4bd-4daf-9da8-bc0a2d943ee4/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;code&gt;Scaffold&lt;/code&gt;의 &lt;code&gt;body&lt;/code&gt; 속성에 위젯들은 아래와 같이 배치되어 있습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7b9cf103-54c2-4239-9d94-7005865722f9/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;code&gt;Column&lt;/code&gt; 위젯을 &lt;code&gt;Padding&lt;/code&gt;이라는 위젯으로 감싸면 여백을 추가할 수 있습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/29ca1fb3-130e-4495-a0db-64a2210abfb9/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;22번째 줄에 &lt;code&gt;Column&lt;/code&gt; 위젯을 클릭한 뒤, 왼쪽 전구( ) 아이콘을 선택해 주세요. 그리고 &lt;code&gt;Wrap with Padding&lt;/code&gt;을 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2025cb8a-619c-467f-8abb-0635f880dfa3/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Column&lt;/code&gt; 위젯을 &lt;code&gt;Padding&lt;/code&gt; 위젯으로 감싸졌고, 어느정도 여백을 줄지 설정하는 23번 째 줄에 &lt;code&gt;padding&lt;/code&gt; 속성이 추가되었습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/03160dbf-8e51-4923-9af0-1305d653a6d5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   EdgeInsets 사용법

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4c98c76f-2319-4467-9ba6-854551ac3249/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 전방위 모두 동일하게 적용&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; EdgeInsets.all(8)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 특정 방위만 적용&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; EdgeInsets.only(
   left: 8,
   right: 8,
 )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 위아래 또는 좌우 적용&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; EdgeInsets.symmetric(
     vertical: 8,
     horizontal: 8,
 )&lt;/code&gt;&lt;/pre&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;23번 째 줄에 8.0을 16으로 변경한 뒤 저장해보면 에뮬레이터에 내부에 여백이 추가된 것을 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/82545143-d981-4f76-aab6-85403e223ae7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;6) Container&lt;/p&gt;
&lt;p&gt;  로그인 버튼을 화면 가로로 가득 채우도록 키워보겠습니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/90c2ea92-fd0c-4b66-8859-b6ba653ae544/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;ElevatedButton 자체에는 &lt;code&gt;width&lt;/code&gt; 속성이 없고, 부모 위젯에 크기를 이용해 조절 할 수 있습니다.&lt;/p&gt;
&lt;p&gt; 37번째 줄에 ElevatedButton을 클릭한 뒤 왼쪽 전구( ) 아이콘을 클릭하고 &lt;code&gt;Wrap with Container&lt;/code&gt;를 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/271cb730-0bbc-4743-8600-2d3887d87bb3/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 그러면 아래와 같이 Container 위젯이 ElevatedButton 위젯을 감싸게 됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6974d8d9-7830-417e-a275-87c5ddb7ad06/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   Container는 Box 형태의 기본적인 위젯으로 다방면으로 많이 사용됩니다.

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/92fd5a8c-7956-42a5-b565-6a4ddbfd86af/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; Container(
   width: 200, // 폭
   height: 200, // 높이
   color: Colors.amber, // 박스 색상
   child: Text(&amp;quot;I Love Flutter!&amp;quot;), // 자식 위젯
 ),&lt;/code&gt;&lt;/pre&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음 Container 위젯에 &lt;code&gt;width: double.infinity,&lt;/code&gt;라고 추가해주세요. 그러면 Container의 폭이 부모를 가득 채우게 되고, 버튼도 함께 최대 크기로 늘어납니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/36536a3e-dc1b-4710-8233-f6026ad0479b/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;비밀번호를 입력하는 TextField와 로그인 버튼 사이에 여백을 추가해 보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt; 아래 이미지와 같이 Container에 &lt;code&gt;margin: EdgeInsets.only(top: 24),&lt;/code&gt;라고 추가해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/043dd423-d829-4e09-aef8-04ac91301a8c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   Container에 margin 속성은 Container 바깥 영역에 여백을 줄 때 사용합니다. (아까 Padding 은 안쪽 여백이었죠!)

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8e06476c-f2bc-46bf-be3b-4cbcb748bf25/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; Padding: 안쪽 여백&lt;/p&gt;
&lt;p&gt; Border: 경계선&lt;/p&gt;
&lt;p&gt; Margin: 바깥쪽 여백&lt;/p&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;7) Image &amp;amp; SingleChildScrollView&lt;/p&gt;
&lt;p&gt;  이메일 입력란 상단에 이미지를 넣어보겠습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;26번째 줄에 아래와 같이 &lt;code&gt;Image.network&lt;/code&gt; 위젯을 추가해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Image.network(&quot;URL&quot;)`을 이용하면 URL로 된 이미지를 보여줄 수 있습니다.

 &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; Image.network(&amp;quot;https://i.ibb.co/nngK6j3/startup.png&amp;quot;),&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/957ad457-5ebf-460d-a35c-7ff70cd8ff6b/Screen_Shot_2022-08-29_at_4.54.23_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 4.54.23 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 이미지에 크기를 지정하지 않았으므로, 원본 사진의 크기로 들어가게 되고, 이 상태에서 이메일 입력란을 클릭하여 가상 키보드를 올려보면 아래와 같이 하단 화면이 짤린다고 에뮬레이터에 표시됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4a47b093-2c10-4410-9f5b-35a25f75d5de/Screen_Shot_2022-08-29_at_4.55.48_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 4.55.48 AM.png&quot;&gt;&lt;/p&gt;
 &lt;aside&gt;
   사용자의 입력을 받는 화면의 경우, 화면을 만들 때 가상 키보드가 올라왔을 때 어떻게 동작할지 고려해야합니다. 현재 개발중인 에뮬레이터에선 괜찮아 보일지라도 기기마다 높이와 폭이 다르기 때문에 문제가 발생할 수 있습니다.

&lt;p&gt; 이럴 때는 아래와 같은 패키지가 도움이 될 수 있습니다.&lt;br&gt; &lt;a href=&quot;https://pub.dev/packages/device_preview&quot;&gt;https://pub.dev/packages/device_preview&lt;/a&gt;&lt;/p&gt;
 &lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;키보드가 올라와 화면이 가려지는 경우, 스크롤을 할 수 있도록 만들어봅시다. 22번째 줄에 &lt;code&gt;Padding&lt;/code&gt; 위젯을 클릭한 뒤 왼쪽에 전구( ) 아이콘을 선택해 주세요. &lt;code&gt;Wrap with widget...&lt;/code&gt;을 선택해 &lt;code&gt;Padding&lt;/code&gt; 위젯을 다른 위젯으로 감싸주겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eed63ce2-9e83-4a65-8a00-6af8d670ecb5/Screen_Shot_2022-08-29_at_4.59.11_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 4.59.11 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 아래와 같이 &lt;code&gt;Padding&lt;/code&gt; 위젯이 &lt;code&gt;widget&lt;/code&gt;이라는 익명의 위젯으로 감싸집니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fede1771-bdd4-4a92-b7fb-2b1ed50132d3/Screen_Shot_2022-08-29_at_5.00.13_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.00.13 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;22번째 줄에 &lt;code&gt;widget&lt;/code&gt;을 &lt;code&gt;SingleChildScrollView&lt;/code&gt;로 변경한 뒤 저장해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5d970fd6-5c54-4b2a-9819-639367b280b6/Screen_Shot_2022-08-29_at_5.00.35_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.00.35 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 이제 에뮬레이터에서 화면을 이메일을 클릭해도 화면이 넘치지 않고, 스크롤을 할 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/369f9d13-c745-4b8a-96cb-d2b0216c6094/Screen_Shot_2022-08-29_at_5.01.14_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.01.14 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이미지를 적절한 크기로 조절해보도록 하겠습니다. &lt;code&gt;width: 81,&lt;/code&gt;을 &lt;code&gt;Image.network&lt;/code&gt; 위젯에 추가해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d59cff42-5e44-49f3-9295-9af2fd76a729/Screen_Shot_2022-08-29_at_5.02.30_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.02.30 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이미지를 &lt;code&gt;Padding&lt;/code&gt;으로 감싸 다른 위젯과 간격을 추가해 보겠습니다. 27번째 줄의 &lt;code&gt;Image&lt;/code&gt;를 선택한 뒤 오른쪽 전구( ) 아이콘을 누르고 &lt;code&gt;Wrap with Padding&lt;/code&gt;을 선택해 주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3824f74b-409d-4d84-8dff-9b9ea8fbded2/Screen_Shot_2022-08-29_at_5.04.59_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.04.59 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 아래와 같이 &lt;code&gt;Padding&lt;/code&gt; 위젯으로 &lt;code&gt;Image.network&lt;/code&gt; 위젯이 감싸집니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c9d74a64-c1df-46db-b866-dce6624a9ede/Screen_Shot_2022-08-29_at_5.06.16_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.06.16 AM.png&quot;&gt;&lt;/p&gt;
&lt;p&gt; 28번째 줄에 &lt;code&gt;EdgeInsets&lt;/code&gt;에 &lt;code&gt;8.0&lt;/code&gt;을 &lt;code&gt;32&lt;/code&gt;로 변경한 뒤 저장해주면 완성!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b73fbab2-4a25-45db-9c13-447502a3f900/Screen_Shot_2022-08-29_at_5.06.45_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.06.45 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;최종 완성 코드&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: Scaffold(
          appBar: AppBar(
            centerTitle: true,
            title: Text(
              &amp;quot;Hello Flutter&amp;quot;,
              style: TextStyle(fontSize: 28),
            ),
          ),
          body: SingleChildScrollView(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                children: [
                  Padding(
                    padding: const EdgeInsets.all(32),
                    child: Image.network(
                      &amp;quot;https://i.ibb.co/nngK6j3/startup.png&amp;quot;,
                      width: 81,
                    ),
                  ),
                  TextField(
                    decoration: InputDecoration(
                      labelText: &amp;#39;이메일&amp;#39;,
                    ),
                  ),
                  TextField(
                    obscureText: true,
                    decoration: InputDecoration(
                      labelText: &amp;#39;비밀번호&amp;#39;,
                    ),
                  ),
                  Container(
                    width: double.infinity,
                    margin: EdgeInsets.only(top: 24),
                    child: ElevatedButton(
                      onPressed: () {},
                      child: Text(&amp;quot;로그인&amp;quot;),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;06. Flutter 공부 방법&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Flutter 위젯(Widget) 살펴보기&lt;/p&gt;
  &lt;aside&gt;
    로그인 페이지를 만들며 사용한 `Text`, `TextField`, `ElevatedButton` 이런 친구들을 `Widget`이라고 부릅니다.

&lt;p&gt;  Flutter 개발을 하려면, 어떤 Widget이 있는지 알아야 합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://docs.flutter.dev/development/ui/widgets&quot;&gt;[코드스니펫] Widget Catalog URL&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://docs.flutter.dev/development/ui/widgets&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG&quot;&gt;[코드스니펫] Widget of the week URL&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://www.youtube.com/playlist?list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;

&lt;aside&gt;
  위젯이 굉장히 많아서 모든 것을 외울 순 없습니다!
'이런 위젯이 있구나' 구경하면서 괜찮은 위젯들을 나중에 찾기 쉽게 **기록해주세요!
(**[notion](notion.so) 을 추천합니다. 지금 이 자료도 노션으로 작성했어요!)

&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;에러 메시지 잘 읽고 구글링 잘하기&lt;/p&gt;
  &lt;aside&gt;
    에러가 발생한다면 메시지를 유심히 봐주세요. 핵심 메시지는 보통 가장 위에, 다른 색으로 표시될 겁니다. 이를 그대로 복사해서 구글에 검색해주세요.
  일반적으로 stackoverflow 같은 사이트에 답변이 잘 올라와 있습니다!

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aa9789e5-5f07-424d-b4a1-8b0e3b5dec55/Screen_Shot_2022-08-29_at_5.31.19_AM.png&quot; alt=&quot;Screen Shot 2022-08-29 at 5.31.19 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;커뮤니티 가입하기&lt;/p&gt;
  &lt;aside&gt;
    커뮤니티에 질문하고 답변하며 함께 성장하세요.
  Flutter 트랜드 파악 및 최근 이슈들을 따라가실 수 있어요.

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://open.kakao.com/o/gYyufB6&quot;&gt;Flutter Developers Korea + Dart 오픈톡방&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://flutter.dev/community&quot;&gt;Flutter Community&lt;/a&gt;&lt;/p&gt;
&lt;aside&gt;
  **개발자 커뮤니티 기본 에티켓!**
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;질문하기 전에 스스로 Google에 검색해서 해결하려고 노력해보기!&lt;br&gt;(검색 키워드 자체를 모른다면 빠르게 물어보기)&lt;/li&gt;
&lt;li&gt;문제 발생시 에러 로그도 함께 올리기!&lt;br&gt;Flutter는 에러 발생시 Debug Console에 에러 메세지를 보여줍니다. 먼저 에러 메세지를 먼저 구글에 검색해보고, 해결이 잘 안된다면 에러 로그와 함께 질문해주세요.&lt;br&gt;(에러 메세지가 없으면 다른 개발자들이 도와주기가 힘들어요!)&lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로그래밍 언어 다트(Dart) 배우기&lt;/p&gt;
  &lt;aside&gt;
    Flutter에서 작성한 모든 코드는 **다트(Dart)**라는 프로그래밍 언어입니다.

&lt;p&gt;  코드를 이해하고, 작성하기 위해 &lt;strong&gt;Dart 문법&lt;/strong&gt;을 배워야합니다.&lt;/p&gt;
  &lt;/aside&gt;

  &lt;aside&gt;
    다른 프로그래밍 언어를 배우신 적이 있다면 금방 쉽게 배우실 수 있습니다.

&lt;p&gt;  프로그래밍 언어를 처음 배우신다면,&lt;br&gt;  일단 &lt;strong&gt;문법을 이해하고 읽는 법&lt;/strong&gt;과 &lt;strong&gt;실행 순서&lt;/strong&gt;가 어떻게 되는지에 집중해주세요!&lt;/p&gt;
&lt;p&gt;  전혀 외울 필요 없고, 직접 작성할 때에는 검색해서 예제를 보고하시면 됩니다.  &lt;/p&gt;
  &lt;/aside&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;07. Dart 문법&lt;/h2&gt;
&lt;aside&gt;
  Flutter 코드를 작성하는데 필요한 Dart 문법을 배워보도록 하겠습니다.
여기에는 간단한 내용만 담았습니다. 보다 디테일한 내용과 실습은 아래 페이지에 정리되어 있으니 공부하시면서 참고해주세요!

&lt;p&gt;&lt;a href=&quot;https://www.notion.so/Dart-4ec9668b6fbd45ac9880d523956ce9b1&quot;&gt;Dart 문법 상세&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
  코딩은 오픈북 테스트와 같기 때문에, 외우지 마시고 이해하신 뒤 필요할 때 찾아서 적용하시면 됩니다.
실습 먼저, 이론은 나중에!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;DartPad&lt;/p&gt;
  &lt;aside&gt;
    DartPad는 온라인 상에서 Dart를 실행할 수 있는 웹사이트입니다.

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;코드스니펫을 복사한 뒤 주소창에 붙여 넣으면, DartPad로 접속합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dartpad.dev/&quot;&gt;[코드스니펫] DartPad 접속&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  https://dartpad.dev/&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ```dart
    void main() {
      for (int i = 0; i &amp;lt; 5; i++) {
        print(&amp;#39;hello ${i + 1}&amp;#39;);
      }
    }
    ```

- 실행 순서

    &amp;lt;aside&amp;gt;
      `main`은 Dart에서 처음 시작 시 호출하는 약속된 ****함수입니다. 
    앞의 void 자리는 함수가 반환하는 값의 자료형을 표시하는 것입니다. 비워둬도 괜찮습니다.

    &amp;lt;/aside&amp;gt;

    ```dart
    void main() {}

    main2 () {}

    String main3 () {
        return &amp;quot;Hello&amp;quot;;
    }
    ```

    ![Screen Shot 2022-08-29 at 5.45.09 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/da41bae5-2351-4cea-9f8b-654647bc7de5/Screen_Shot_2022-08-29_at_5.45.09_AM.png)

- `//` : 주석으로 컴퓨터가 읽지 않습니다. 주로 개발하면서 메모할 때 사용합니다.
- `print()` : print의 소괄호 안쪽에 값을 넣으면 오른쪽 `Console`에 값이 출력 됩니다. 정상적으로 잘 작동하는지 확인(디버깅) 할 때 사용합니다.
- `;` : Dart에서는 마지막에 세미콜론(semicolon)을 찍어줍니다.
- 에러 로그 읽는 법

    &amp;lt;aside&amp;gt;
      아래 이미지를 보면 3번 째 줄에 마지막 세미콜론이 빠졌습니다. 이 상태로 `Run`을 하게되면 우측에 에러 메세지가 나옵니다.

    3:27 (3번째 줄 27번째 칸)에 에러가 발생했군요!

    **에러 메세지를 보면 문제가 발생하는 위치와 해결 방법을 알 수 있습니다!** 앞으로 에러가 발생한다면 에러 메세지 부터 확인해 주세요  

    &amp;lt;/aside&amp;gt;

    ![Screen Shot 2022-08-29 at 5.46.28 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4575b348-6506-4f62-b826-4de805032f07/Screen_Shot_2022-08-29_at_5.46.28_AM.png)


&amp;lt;aside&amp;gt;
  DartPad에서 `Reset` 버튼을 누르면, 변경한 내용이 초기화 됩니다.

![Screen Shot 2022-08-29 at 5.48.19 AM.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/96a5d3ae-eaa8-4847-b75b-e1c11d0ac9af/Screen_Shot_2022-08-29_at_5.48.19_AM.png)

&amp;lt;/aside&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 변수&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;변수 만들기&lt;/p&gt;
 &lt;aside&gt;
   변수는 어떤 값을 담는 바구니라고 생각하시면 쉽습니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bd3388fd-5f7d-4fdb-baf5-9811424556f1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 1) 자료형 (= 바구니에 담을 수 있는 값의 종류)&lt;/p&gt;
&lt;p&gt; &lt;code&gt;var&lt;/code&gt; : 처음 담긴 값으로 타입이 지정됩니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;String&lt;/code&gt; : 문자만 담을 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;String?&lt;/code&gt; : 문자 또는 비어있는(&lt;code&gt;null&lt;/code&gt;) 상태일 수 있습니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;const&lt;/code&gt;: 처음에 변수를 선언하며 담은 값을 변경할 수 없습니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;final&lt;/code&gt; : 선언하고 나중에 값을 담을 수 있으나, 한 번 담으면 변경할 수 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; void main() {
   var a = 1;
   String b = &amp;quot;hello world&amp;quot;;
   String? c = null;
   const d = 1;
   final e;
   e = 2;
     print(a);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; 2) 변수명 (= 바구니 이름)&lt;/p&gt;
 &lt;aside&gt;
   Dart의 변수명 만드는 규칙

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;영문&lt;/code&gt; / &lt;code&gt;_&lt;/code&gt; / &lt;code&gt;$&lt;/code&gt; / &lt;code&gt;숫자&lt;/code&gt;만 사용&lt;/li&gt;
&lt;li&gt;&lt;code&gt;숫자&lt;/code&gt;로 시작 불가능&lt;/li&gt;
&lt;li&gt;카멜케이스(camelCase) 사용&lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 조건문&lt;/p&gt;
  &lt;aside&gt;
    if문이라고도 불리며 조건에 따라 실행하고 싶은 코드를 분기할 때 사용합니다.

  &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  if (bool1) {
      // bool1이 **true**면 실행
  } else {
      // bool1이 **false**면 실행
  }&lt;/code&gt;&lt;/pre&gt;
  &lt;aside&gt;
    조건문은 `else if` 형태로 계속해서 꼬리에 꼬리를 물 수 있습니다. 앞에서 부터 하나씩 비교해서 진행하면서 하나라도 **true**가 되어 실행되면, 뒤에 있는 조건문은 실행되지 않습니다.

  &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  if (bool1) {
      // bool1이 **true**면 실행
  } else if (bool2) {
      // bool1이 **false**이고, bool2가 **true**이면 실행
  } else if (bool3) {
      // bool1과 bool2가 **false**이고, bool3가 **true**이면 실행
  } else {
      // bool1, bool2, bool3가 모두 **false**이면 실행
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 반복문&lt;/p&gt;
  &lt;aside&gt;
    반복문은 특정한 코드를 반복해서 실행하도록 흐름을 제어하는 방법으로 `for문` 이라고 불리기도 합니다.

  &lt;/aside&gt;

  &lt;aside&gt;
    직접 입력하면 5줄을 작성해야 하지만,

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  hello 1
  hello 2
  hello 3
  hello 4
  hello 5&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  반복문을 사용하면 3줄로 작성할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  for (int i = 0; i &amp;lt; 5; i++) {
      print(&amp;#39;hello ${i + 1}&amp;#39;);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  5줄이 아니라 100만줄이 되어도, 반복문을 사용하면 3줄만에 가능합니다  &lt;/p&gt;
  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4262075d-389a-4d0f-924c-eae5a62d1cac/_for.png&quot; alt=&quot;_for.png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;반복문 구성&lt;/p&gt;
&lt;p&gt;  1 : &lt;code&gt;int i = 0&lt;/code&gt; → &lt;code&gt;i&lt;/code&gt;라는 변수가 0으로 시작합니다. (한 번만 실행됩니다)&lt;/p&gt;
&lt;p&gt;  2 : &lt;code&gt;i &amp;lt; 5&lt;/code&gt; → &lt;code&gt;i&lt;/code&gt;의 값이 5보다 작은지 조건을 확인합니다. (false → 반복문 종료 / true → 3번)&lt;/p&gt;
&lt;p&gt;  3 : &lt;code&gt;중괄호 안쪽 영역&lt;/code&gt; → 반복해 실행하는 코드들이 들어있습니다.&lt;/p&gt;
&lt;p&gt;  4 : &lt;code&gt;i++&lt;/code&gt; → &lt;code&gt;i&lt;/code&gt;값을 1만큼 증가 시키고 2번으로 흐름이 다시 넘어갑니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) 함수(function)&lt;/p&gt;
  &lt;aside&gt;
    함수(function)는 여러 코드를 묶어둔 상자입니다.

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;함수의 호출 &amp;amp; 실행 순서&lt;/p&gt;
&lt;p&gt;아래 코드 스니펫을 복사해서 DartPad에서 실행해보세요!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] Dart 함수&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt; void main() {
   print(&amp;quot;1. 시작&amp;quot;);

   say();

   print(&amp;quot;4. 종료!&amp;quot;);
 }

 void say() {
   print(&amp;quot;2. 안녕&amp;quot;);
   print(&amp;quot;3. Hello&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;함수의 생김새&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a51a0623-07c7-4e7e-aea5-23632966745e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;say&lt;/code&gt;라고 적혀있는 부분이 &lt;strong&gt;함수의 이름&lt;/strong&gt;입니다.&lt;/li&gt;
&lt;li&gt;중괄호(&lt;code&gt;{ }&lt;/code&gt;) 안쪽 영역이 함수가 가진 &lt;strong&gt;실행 코드&lt;/strong&gt;들 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;함수 호출 방법&lt;/p&gt;
 &lt;aside&gt;
   say함수가 가진 코드들을 실행하려면,  `say()`와 같이 함수의 이름부터 뒤에 있는 소괄호까지를 불러주시면 됩니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1f1d4c99-22b0-4ded-8260-146c6d7b5f77/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d1db976e-f8b2-4aa9-a2b5-f8d82d8ef6c2/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;함수의 표현 방법&lt;/p&gt;
&lt;aside&gt;
  첫 번째 줄에 있는 main도 함수입니다. 단, Dart에서 시작할 때 호출하기로 약속된 함수로 이름을 변경하면 안됩니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;void main() {

}&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;

&lt;aside&gt;
  화살표 함수는 함수 표현식의 단축형입니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;say() {
   return &amp;quot;hello&amp;quot;;
}

sayArrow() =&amp;gt; &amp;quot;hello&amp;quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5) 클래스(Class)&lt;/p&gt;
  &lt;aside&gt;
    **클래스**는 변수와 함수를 모아둔 틀입니다.

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;클래스 생김새&lt;/p&gt;
&lt;aside&gt;
  클래스 이름은 대문자로 시작합니다.

&lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;class 클래스이름 {

}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;p&gt;클래스의 구성 요소&lt;/p&gt;
&lt;aside&gt;
  클래스는 다음의 구성 요소로 이루어져 있습니다.

&lt;p&gt;&lt;code&gt;속성(Property)&lt;/code&gt; : 클래스 내의 &lt;strong&gt;변수&lt;/strong&gt;&lt;br&gt;&lt;code&gt;메소드(Method)&lt;/code&gt; : 클래스 내의 &lt;strong&gt;함수&lt;/strong&gt;&lt;br&gt;&lt;code&gt;생성자(Constructor)&lt;/code&gt; : 클래스 명과 동일한 &lt;strong&gt;함수&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;즉, 앞서 배운 &lt;strong&gt;변수&lt;/strong&gt;와 &lt;strong&gt;함수&lt;/strong&gt;로 이루어져 있습니다.&lt;/p&gt;
&lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;class Bread {
   // 생성자 함수 (클래스명과 똑같음. 클래스의 객체가 생성될 때 호출되는 함수)
   Bread(String core) {
       content = core; // 전달 받은 core를 content에 넣어줍니다.
   }

   // Bread 클래스가 가진 content 속성 (클래스 내의 변수)
   String? content;

   // Bread 클래스가 가진 getDescription 메소드 (클래스 내의 함수)
   String getDescription() {
   return &amp;quot;맛있는 $content빵입니다.&amp;quot;; // 맛있는 팥빵입니다.
 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;p&gt;인스턴스(Instance)&lt;/p&gt;
&lt;aside&gt;
  `클래스(빵틀)` → `생성자 호출(빵을 만들기)` → `인스턴스(빵)`

&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/69a49c9c-d373-4cfb-8045-751f943592ff/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
  빵 틀에서 빵을 찍어내 듯, 생성자 함수를 호출하여 클래스에서 정의해 둔 속성과 메소드를 가진 데이터 객체를 만들 수 있는데, 이를 `인스턴스(Instance)`라고 부릅니다.

&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;void main() {
   Bread bread = Bread(); // Bread 생성자를 호출하여 인스턴스를 반환
   print(bread); // Instance of &amp;#39;Bread&amp;#39;
}

class Bread {

   // 생성자
   Bread();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;p&gt;상속(extends)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3022d8fa-d638-4b03-9ab1-37461b035e33/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;aside&gt;
  10번째와 15번째 줄과 같이 `class 클래스A extends 클래스B`이라고 입력하면, **클래스B**의 변수와 함수를 **클래스A**에서 그대로 사용할 수 있습니다. 이를 **클래스A**가 **클래스B**의 기능을 그대로 물려받았다고 해서 **상속(extends)** 받았다고 표현합니다.

&lt;p&gt;그리고 &lt;strong&gt;변수와 함수를 물려받은 클래스A를 자식(child)&lt;/strong&gt;, &lt;strong&gt;기능을 물려준 클래스B를 부모(super)&lt;/strong&gt;라고 부르기도 합니다.&lt;/p&gt;
&lt;/aside&gt;

&lt;aside&gt;
  앞으로 `extends &lt;클래스&gt;`를 보면 해당 `클래스`가 가진 변수와 함수를 물려받아 그대로 쓸 수 있다고 보시면 됩니다.

&lt;/aside&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;p&gt;Dart의 모든 것은 Class&lt;/p&gt;
&lt;aside&gt;
  앞에서 배우신 자료형도 모두 Class로 구성되어 있습니다.

&lt;p&gt;이제 공식 문서에 있는 Constructor(생성자) / Properties(속성) / Methods(메소드)가 어떤 것을 의미하는지 이해하실 수 있습니다.&lt;/p&gt;
&lt;/aside&gt;

&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/31198be2-ddc2-40a8-90a1-ddb1e6563102/_docs.png&quot; alt=&quot;_docs.png&quot;&gt;&lt;/p&gt;
&lt;aside&gt;
  위 내용을 이해하시면 기본적인 Dart 문법은 이해하실 수 있습니다.
Dart 문법에 대해 좀 더 알고 싶은 분은 아래 공식문서 링크를 참고해 주세요.

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://dart.dev/guides/language/language-tour&quot;&gt;[코드스니펫] Dart 공식문서 URL&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt; https://dart.dev/guides/language/language-tour&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/aside&gt;


&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;08. 숙제 - Movie Reviews 만들기&lt;/h2&gt;
&lt;aside&gt;
  처음 보는 위젯은 구글에 `flutter &lt;위젯이름&gt;` 형태로 검색해서 사용법을 익히며 실습을 진행하세요. (ex. flutter appbar)

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;최종 완성 모습&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;[Simulator Screen Recording - iPhone 13 - 2022-08-29 at 06.19.22.mp4](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a674a8d1-f48c-4b42-86fd-58d07abbf343/Simulator_Screen_Recording_-_iPhone_13_-_2022-08-29_at_06.19.22.mp4)

## 사용한 위젯

Scaffold

AppBar

Text

IconButton

Column

Padding

TextField

Icon

Divider

Expanded

ListView.builder

Card

Stack

Image.network

Container

Text&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 실습 준비&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) Flutter 프로젝트 생성하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;View&lt;/code&gt; → &lt;code&gt;Command Palette&lt;/code&gt; 버튼을 클릭해주세요.&lt;/p&gt;
 &lt;aside&gt;
   `Command Palette` 기능은 앞으로도 자주 사용하게 될 거라 단축키를 알아두시면 좋습니다.
 window : `Ctrl + Shift + P`
 macOS : `Cmd + Shift + P`

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c2e8bac1-770e-4f78-80a8-5b799f1f40e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;명령어를 검색하는 팝업창이 뜨면, &lt;code&gt;flutter&lt;/code&gt;라고 입력한 뒤 &lt;code&gt;Flutter: New Project&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9a6058de-bbf7-410f-9dc3-a96d6bd7ac58/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Application&lt;/code&gt;을 선택해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c7145f5-c6f6-4306-b78e-e3bb31e24710/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트를 시작할 폴더를 선택하는 과정입니다. 미리 생성해 둔 &lt;code&gt;flutter&lt;/code&gt; 폴더를 선택한 뒤 &lt;code&gt;Select a folder to create the project in&lt;/code&gt; 버튼을 눌러 주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트 이름을 &lt;code&gt;movie_reviews&lt;/code&gt; 로 입력해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/720cc4bd-a08a-4317-97ac-d867335a0b1e/Screen_Shot_2022-06-20_at_5.17.14_AM.png&quot; alt=&quot;Screen Shot 2022-06-20 at 5.17.14 AM.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로젝트가 생성되면 아래 &lt;code&gt;main.dart&lt;/code&gt; 코드스니펫을 복사해서 기존 코드를 모두 지운 뒤, &lt;code&gt;main.dart&lt;/code&gt; 파일에 붙여 넣고 저장해주세요.&lt;/p&gt;
 &lt;aside&gt;
   저장 단축키

&lt;p&gt; window : &lt;code&gt;Ctrl + S&lt;/code&gt;&lt;br&gt; macOS : &lt;code&gt;Cmd + S&lt;/code&gt;&lt;/p&gt;
 &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫]  main.dart&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(), // 홈페이지 보여주기
      );
    }
  }

  class HomePage extends StatelessWidget {
    const HomePage({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      // 음식 사진 데이터
      List&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; dataList = [
        {
          &amp;quot;category&amp;quot;: &amp;quot;탑건: 매버릭&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/sR32PN3/topgun.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;마녀2&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/CKMrv91/The-Witch.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;범죄도시2&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/2czdVdm/The-Outlaws.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;헤어질 결심&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/gM394CV/Decision-to-Leave.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;브로커&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/MSy1XNB/broker.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;문폴&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/4JYHHtc/Moonfall.jpg&amp;quot;,
        },
      ];

      // 화면에 보이는 영역
      return Scaffold();
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음으로 학습 단계에서 불필요한 내용을 화면에 표시하지 않도록 설정해 주겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;code&gt;analysis_options.yaml&lt;/code&gt; 파일을 열고, 아래 코드스니펫을 복사해서 24번째 라인 뒤에 붙여 넣고 저장해 주세요.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] analysis_options.yaml&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;
      prefer_const_constructors: false
      prefer_const_literals_to_create_immutables: false&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 에뮬레이터 실행하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;VSCode 우측 하단에 &lt;code&gt;Chrome (web-javascript)&lt;/code&gt;를 클릭해주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dc5ebed1-67da-41dd-b567-2ecdbd43ed99/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Start Pixel 2 API 29 mobile emulator&lt;/code&gt;를 선택해주세요.&lt;/p&gt;
 &lt;aside&gt;
   macOS의 경우 iOS 에뮬레이터를 사용하셔도 됩니다.

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e0e7243-7d95-4b57-99df-08fd28e1b4d7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 잠시 기다리면 에뮬레이터가 실행됩니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf5640d3-c9a9-4105-bd40-9dcb309412f5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;VSCode 우측 상단에 &lt;strong&gt;아래 화살표&lt;/strong&gt;를 눌러 &lt;code&gt;Run Without Debugging&lt;/code&gt;을 눌러주세요.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fba923c5-b4b0-4ddb-b381-ae8686c0315c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; 에뮬레이터에 아래와 같이 흰 화면이 나오면 정상적으로 실행이 완료된 것입니다!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1a0d4bfb-a6ca-4d30-9aee-c89f70d1a9a5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) AppBar 만들기&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![simulator_screenshot_429E6845-74A6-4FFB-9C5B-31AC91DCAA3E.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ed279ba5-e234-405a-a87c-16dca9a0de65/simulator_screenshot_429E6845-74A6-4FFB-9C5B-31AC91DCAA3E.png)

## 사용한 위젯

Scaffold

AppBar

IconButton

Text

Icon&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;3) Body 만들기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;![simulator_screenshot_52382C76-EC68-46A1-8F79-EF5D14DD85BE.png](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eb77a00c-232a-483d-83ff-b377c084364b/simulator_screenshot_52382C76-EC68-46A1-8F79-EF5D14DD85BE.png)

## 사용한 위젯

Column

Padding

TextField

Icon

Divider

Expanded

ListView.builder

Card

Stack

Image.network

Container

Text&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;숙제 답안 (&lt;code&gt;main.dart&lt;/code&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-dart&quot;&gt;  import &amp;#39;package:flutter/material.dart&amp;#39;;

  void main() {
    runApp(MyApp());
  }

  class MyApp extends StatelessWidget {
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        debugShowCheckedModeBanner: false,
        home: HomePage(), // 홈페이지 보여주기
      );
    }
  }

  class HomePage extends StatelessWidget {
    const HomePage({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) {
      // 영화 제목, 사진 데이터
      List&amp;lt;Map&amp;lt;String, dynamic&amp;gt;&amp;gt; dataList = [
        {
          &amp;quot;category&amp;quot;: &amp;quot;탑건: 매버릭&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/sR32PN3/topgun.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;마녀2&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/CKMrv91/The-Witch.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;범죄도시2&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/2czdVdm/The-Outlaws.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;헤어질 결심&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/gM394CV/Decision-to-Leave.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;브로커&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/MSy1XNB/broker.jpg&amp;quot;,
        },
        {
          &amp;quot;category&amp;quot;: &amp;quot;문폴&amp;quot;,
          &amp;quot;imgUrl&amp;quot;: &amp;quot;https://i.ibb.co/4JYHHtc/Moonfall.jpg&amp;quot;,
        },
      ];

      // 화면에 보이는 영역
      return Scaffold(
        appBar: AppBar(
          elevation: 0,
          backgroundColor: Colors.white,
          centerTitle: false,
          iconTheme: IconThemeData(color: Colors.black),
          title: Text(
            &amp;quot;Movie Reviews&amp;quot;,
            style: TextStyle(
              color: Colors.black,
              fontSize: 28,
              fontWeight: FontWeight.bold,
            ),
          ),
          actions: [
            IconButton(
              onPressed: () {},
              icon: Icon(Icons.person_outline),
            )
          ],
        ),
        body: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: TextField(
                decoration: InputDecoration(
                  hintText: &amp;quot;영화 제목을 검색해주세요.&amp;quot;,
                  border: OutlineInputBorder(
                    borderSide: BorderSide(color: Colors.black),
                  ),
                  suffixIcon: IconButton(
                    icon: Icon(Icons.search),
                    onPressed: () {},
                  ),
                ),
              ),
            ),
            Divider(height: 1),
            Expanded(
              child: ListView.builder(
                itemCount: dataList.length,
                itemBuilder: (context, index) {
                  String category = dataList[index][&amp;#39;category&amp;#39;];
                  String imgUrl = dataList[index][&amp;#39;imgUrl&amp;#39;];

                  return Card(
                    child: Stack(
                      alignment: Alignment.center,
                      children: [
                        Image.network(
                          imgUrl,
                          width: double.infinity,
                          height: 200,
                          fit: BoxFit.cover,
                        ),
                        Container(
                          width: double.infinity,
                          height: 200,
                          color: Colors.black.withOpacity(0.5),
                        ),
                        Text(
                          category,
                          style: TextStyle(
                            color: Colors.white,
                            fontSize: 36,
                          ),
                        ),
                      ],
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      );
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;전체 목록 바로가기&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/268c5d47b8994c709084d91182a2688b&quot;&gt;[스파르타코딩클럽] 앱개발 종합반&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;다음 강의 바로가기&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/2-794296848c0642d9a98ff0de21b6739d&quot;&gt;2주차 - 다양한 위젯을 활용해 화면 그리기&lt;/a&gt; &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Copyright ⓒ TeamSparta All rights reserved.&lt;/p&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/969</guid>
      <comments>https://comphy.tistory.com/969#entry969comment</comments>
      <pubDate>Fri, 7 Apr 2023 18:24:15 +0900</pubDate>
    </item>
    <item>
      <title>경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다. 2023-03-20'sdi-s-line 기공식</title>
      <link>https://comphy.tistory.com/968</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;경축 에스천사게임즈 재오픈.jpeg&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wozS5/btr4JLyKYfl/zpU1tgh160q0bkLkwsnpxk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wozS5/btr4JLyKYfl/zpU1tgh160q0bkLkwsnpxk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wozS5/btr4JLyKYfl/zpU1tgh160q0bkLkwsnpxk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwozS5%2Fbtr4JLyKYfl%2FzpU1tgh160q0bkLkwsnpxk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;600&quot; data-filename=&quot;경축 에스천사게임즈 재오픈.jpeg&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;경축!&amp;nbsp;아무것도&amp;nbsp;안하여&amp;nbsp;에스천사게임즈가&amp;nbsp; &lt;br /&gt;새로운&amp;nbsp;모습으로&amp;nbsp;재오픈&amp;nbsp;하였습니다. &lt;br /&gt;&lt;br /&gt;어린이용이며,&amp;nbsp;설치가&amp;nbsp;필요없는&amp;nbsp;브라우저&amp;nbsp;게임입니다. &lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;http://s1004games.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://s1004games.com&lt;/a&gt;&lt;/h3&gt;
&lt;figure id=&quot;og_1679301603002&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;s1004games.com (에스천사게임즈닷컴) | 천사같은 천사개의 게임을 개발합니다.&quot; data-og-description=&quot;s1004games.com (에스천사게임즈닷컴) &amp;copy; 2023. All rights reserved. V-1.5.6&quot; data-og-host=&quot;s1004games.com&quot; data-og-source-url=&quot;http://s1004games.com&quot; data-og-url=&quot;http://s1004games.com&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;http://s1004games.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;http://s1004games.com&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;s1004games.com (에스천사게임즈닷컴) | 천사같은 천사개의 게임을 개발합니다.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;s1004games.com (에스천사게임즈닷컴) &amp;copy; 2023. All rights reserved. V-1.5.6&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;s1004games.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;s1004games 2023-03-20 173148.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;890&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/seXnO/btr4G7IDKLS/aK1LK7HuqDR0J2bruPBn5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/seXnO/btr4G7IDKLS/aK1LK7HuqDR0J2bruPBn5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/seXnO/btr4G7IDKLS/aK1LK7HuqDR0J2bruPBn5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FseXnO%2Fbtr4G7IDKLS%2FaK1LK7HuqDR0J2bruPBn5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1179&quot; height=&quot;890&quot; data-filename=&quot;s1004games 2023-03-20 173148.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/968</guid>
      <comments>https://comphy.tistory.com/968#entry968comment</comments>
      <pubDate>Mon, 20 Mar 2023 17:40:52 +0900</pubDate>
    </item>
    <item>
      <title># [스파르타코딩클럽] 게임개발 종합반 - 5주차</title>
      <link>https://comphy.tistory.com/967</link>
      <description>&lt;h1&gt;[스파르타코딩클럽] 게임개발 종합반 - 5주차&lt;/h1&gt;
&lt;aside&gt;
&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; width=&quot;40px&quot; /&gt; **매 주차 강의자료 시작에 PDF파일을 올려두었어요!**

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9bbb2d1b-837d-463e-afd2-9adfc9922d36/%E1%84%89%E1%85%B3%E1%84%91%E1%85%A1%E1%84%85%E1%85%B3%E1%84%90%E1%85%A1%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B5%E1%86%BC%E1%84%8F%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A5%E1%86%B8_%E1%84%80%E1%85%A6%E1%84%8B%E1%85%B5%E1%86%B7%E1%84%80%E1%85%A2%E1%84%87%E1%85%A1%E1%86%AF_%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%92%E1%85%A1%E1%86%B8%E1%84%87%E1%85%A1%E1%86%AB_5%E1%84%8C%E1%85%AE%E1%84%8E%E1%85%A1_211108.pdf&quot;&gt;스파르타코딩클럽_게임개발_종합반_5주차_211108.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;런칭을 위해 필요한 주변 기술 학습&lt;/li&gt;
&lt;li&gt;소리넣기, 광고, 출시까지 직접 경험하기&lt;/li&gt;
&lt;li&gt;만들 수 있는 게임의 범위 알기&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;01. 오늘 배울 것&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 5주차 수업의 목표와 범위&lt;/p&gt;
  &lt;aside&gt;
    오늘의 키워드! `런칭을 위한 그 외 작업들`

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;이번주는 편하게 따라하시면 되는 것이랍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;런칭을 하려면 준비해야 할 몇 가지를 더 다뤄볼게요!&lt;/p&gt;
&lt;p&gt;  → 게임에 음악 입히기&lt;/p&gt;
&lt;p&gt;  → 스플래시 화면 붙이기&lt;/p&gt;
&lt;p&gt;  → 광고 붙이기&lt;/p&gt;
&lt;p&gt;  → 무료 에셋 구경하기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;오늘은 머리 아픈 것 없습니다! 찬찬히 따라하면 끝~!  &lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 오늘 만들 것&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;인트로 + 음악 + 광고까지 붙인 &lt;code&gt;르탄이 카드 뒤집기 게임&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3c131b86-60b6-49ce-bd8b-cdc4d81c254c/%EB%A5%B4%ED%83%84%EC%9D%B4_%EB%A7%9E%EC%B6%94%EA%B8%B0_%EA%B2%8C%EC%9E%84(%EC%99%84%EC%84%B1).mp4&quot;&gt;르탄이 맞추기 게임(완성).mp4&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 4주차 &lt;code&gt;르탄이 카드 뒤집기 게임&lt;/code&gt; 작동 확인하기&lt;/p&gt;
  &lt;aside&gt;
    `Unity hub` 를 켜고 findRtan 프로젝트를 오픈합니다.

  &lt;/aside&gt;

&lt;p&gt;  → 잘 작동하네요! 이제, 시작합니다!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d2a80285-6094-4e7e-b4a3-42a3db691db9/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;02. 시작화면 만들기&lt;/strong&gt;&lt;/h2&gt;
&lt;aside&gt;
  3주차 때 했던 것 복습. 복습!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 시작씬을 만들어봅니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Scenes 폴더 → StartScene 만들기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/52d8fc33-69a5-4fbf-b2b9-d4500cdd401c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;화면 색 바꾸기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;카메라 색 : &lt;strong&gt;rgb ⇒ 90, 90, 225&lt;/strong&gt;으로 설정 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6b344e69-e28d-4214-972b-8e6859584a2f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이미지 넣기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;오른쪽 클릭 - &amp;gt; UI → Image 클릭 → 이름은 rtans라고 지정해 줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;크기는 &lt;strong&gt;width: 400, height: 400&lt;/strong&gt;으로 설정 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4980bb3a-dcdc-440d-b846-da6ceb55b024/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;타이틀 만들기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 폰트 다운로드&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  [https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week04/BMHANNA_11yrs_ttf.ttf](https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week04/BMHANNA_11yrs_ttf.ttf)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/edd68044-4e2b-43b4-a92a-e959f0d86bda/BMHANNA_11yrs_ttf.ttf&quot;&gt;BMHANNA_11yrs_ttf.ttf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Assets → fonts 폴더 생성 → 폰트 파일 넣어줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;font-size: 70, y: 350&lt;/strong&gt;으로 설정 해줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;width: 760, height: 200&lt;/strong&gt;으로 설정 해줍니다&lt;code&gt;.&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;text에 르탄이를 찾아라 라고 적어 줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;폰트를 적용 해주고, 폰트 사이즈는 70으로 적용시켜 줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;가운데 정렬로 맞춰줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;color: (255, 255, 255, 255)&lt;/strong&gt;로, 컬러는 흰색을 맞춰 줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a380f2e8-c56e-4b6e-be66-f6be83322e48/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;버튼 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Canvas → 오른쪽 클릭 → UI →  Image 만들어서 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이름은 &lt;strong&gt;startBtn&lt;/strong&gt;로 변경&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;사이즈와 위치는 &lt;strong&gt;width: 360, height: 120, PosY: -400&lt;/strong&gt; 로 적용해 줍니다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add component →  shadow 컴포넌트 추가 &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;distance는 &lt;strong&gt;&lt;code&gt;x: 10, y: -10&lt;/code&gt;&lt;/strong&gt;, 색 (color)은 &lt;strong&gt;250, 250, 0 , 200&lt;/strong&gt;로 설정 해줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Title 복사( &lt;code&gt;ctrl+d&lt;/code&gt; )해서 startBtn 아래에 두기. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PosY: 0, width: 360, height: 120, color: (0, 0, 0, 255)&lt;/strong&gt;로 설정 해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e74af535-7467-44b7-8a55-b08be48c460b/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 르탄이 이미지 애니메이션 넣기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Animations 폴더 → rtans 만들기 → rtans 이미지에 붙이기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aa0f7791-e3c8-4a96-9c99-f520e80735b5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;애니메이션 만들기&lt;/p&gt;
&lt;p&gt; → 애니메이션 더블클릭하고, 르탄이 이미지 전체(0~7)를 끌어다놓기&lt;/p&gt;
&lt;p&gt; → 오른쪽 바를 끌어서 40에 맞추기. 르탄이가 빠르게 회전하는 것을 확인!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f4c54740-64eb-4b04-913b-359dcd265c77/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) startBtn 에 기능 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;startBtn.cs&lt;/code&gt; 만들기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using UnityEngine.SceneManagement;

 public void startGame()
 {
     SceneManager.LoadScene(&amp;quot;MainScene&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;버튼 컴포넌트 만들고 클릭 붙이기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/db427dda-de13-48d7-8248-a59e052e7508/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;잘 되는지 확인하기&lt;/p&gt;
&lt;p&gt; → 완료! 이제 시작해볼까요?&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/955f5ba1-f409-4149-94d7-f9ff6b94d6a2/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;03. 스플래시 이미지 만들기&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 스플래시 이미지란?&lt;/p&gt;
  &lt;aside&gt;
    스플래시 이미지란? - 앱을 켰을 때 떴다가 사라지는 이미지!

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;예) 카카오톡 시작할 때 뜨는 &lt;code&gt;이미지&lt;/code&gt; 같은 것이랍니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8e6f9568-b338-4907-93d1-4f00261915e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 씬은 우리가 만드는 게 아니라, 유니티에서 몇 가지 세팅만으로 만들 수가 있어요.&lt;/p&gt;
&lt;p&gt;  → 즉, Scene 만들어 &lt;code&gt;SceneManager.Loadscene..&lt;/code&gt; 하는 게 아닙니다.  &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다만! 무료버전에서는 유니티 로고가 함께 노출된답니다.&lt;/p&gt;
&lt;p&gt;  → 아쉽게도 로고를 지우려면 &lt;code&gt;Pro&lt;/code&gt; 버전을 구매해야..&lt;/p&gt;
&lt;p&gt;  → 한편, &lt;code&gt;made with Unity&lt;/code&gt; 라고 쓰여진 게임들은 모두 무료버전의 유니티로 제작했음을 우리도 알 수 있겠죠! 자, 그럼 직접 세팅해볼까요?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 이미지 세팅하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Edit → Project settings → Player → Splash Image로 접근하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a6916864-28b5-4466-81b4-369136bb8774/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Preview를 눌러 확인하기&lt;/p&gt;
&lt;p&gt; → Splash Style : 배경 / 로고 색&lt;/p&gt;
&lt;p&gt; → Animation : Dolly - 잠깐 커짐 / Static - 일정 크기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9891e3b9-6097-4fc3-a039-9dde914d9d75/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Images 폴더에 로고 준비하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] logo 다운로드&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week05/spartaMsg.png&quot;&gt;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week05/spartaMsg.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a85c2ada-bc8d-44b2-8e4b-60d0f5e243a5/spartaMsg.png&quot;&gt;spartaMsg.png&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    → Mesh Type : Full Rect ⇒ Apply 클릭

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e8396268-b03d-47a3-946e-d62074c3fc89/Untitled.png)

4. 스플래시 화면 세팅하기

    → Animation : Static 으로 맞추기

    → Draw Mode : All Sequential 로 맞추기

    → 이미지 : select → spartaMsg 클릭

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b10603a3-0039-4afd-8dcf-ce3c35c2c45a/Untitled.png)

5. preview 눌러서 확인하기

    → 잘 되네요!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6bd7db93-b51d-4669-817e-f171f56fd666/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;04. 소리 &amp;amp; 배경음악 넣기&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 사운드 구상하기&lt;/p&gt;
  &lt;aside&gt;
    `르탄이 찾기` 게임에서는 어디에 소리를 넣으면 좋을까요?

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;배경음악 : 게임이 시작하면 배경음악이 나오면 좋겠어요!&lt;/li&gt;
&lt;li&gt;뒤집을 때 : 카드 뒤집을 때 뒤집는 소리가 나면 좋겠죠!&lt;/li&gt;
&lt;li&gt;맞췄을 때 : 카드 두 장이 같을 때 소리가 나면 좋겠죠!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 음원 준비하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;다운받아서 적당한 폴더에 풀고 → 파일을 들어봅니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 사운드 파일&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week05/sounds.zip&quot;&gt;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week05/sounds.zip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/221a5cc2-2d9b-4279-bc40-5f7e74cfd34a/sounds.zip&quot;&gt;sounds.zip&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unity에 &lt;code&gt;Sounds&lt;/code&gt; 폴더를 만들고 가져다놓기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/418392cd-7711-449a-b52d-80110686fd12/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 소리 재생하기: 카드 뒤집기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;card&lt;/code&gt; 에 &lt;code&gt;AudioSource&lt;/code&gt; 컴포넌트 달기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/db01c56d-5ed0-492a-9a99-15062d3a4653/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;card.cs&lt;/code&gt; 준비하기&lt;/p&gt;
&lt;p&gt; → AudioClip, AudioSource 받기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public AudioClip flip;
 public AudioSource audioSource;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aab43e5c-10e2-4991-9cf9-723756774a06/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;적절한 순간에 재생되게 하기&lt;/p&gt;
&lt;p&gt; → flip을 한번만 재생되게 하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void openCard()
 {
     audioSource.PlayOneShot(flip);

     anim.SetBool(&amp;quot;isOpen&amp;quot;, true);
     transform.Find(&amp;quot;front&amp;quot;).gameObject.SetActive(true);
     transform.Find(&amp;quot;back&amp;quot;).gameObject.SetActive(false);

     if (gameManager.I.firstCard == null)
     {
         gameManager.I.firstCard = gameObject;
     }
     else
     {
         gameManager.I.secondCard = gameObject;
         gameManager.I.isMatched();
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) 소리 재생하기: 카드 맞췄을 때&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에서 같게 세팅하기&lt;/p&gt;
&lt;p&gt; → AudioSource 컴포넌트 붙이고&lt;/p&gt;
&lt;p&gt; → AudioSource, AudioClip 받고&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public AudioSource audioSource;
 public AudioClip match;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd808498-6a9a-4644-93aa-8a0474856e29/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;재생하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void isMatched()
 {
     string firstCardImage = firstCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;
     string secondCardImage = secondCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;

     if (firstCardImage == secondCardImage)
     {
         audioSource.PlayOneShot(match);

         firstCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();

         int cardsLeft = GameObject.Find(&amp;quot;cards&amp;quot;).transform.childCount;
         if (cardsLeft == 2)
         {
             endTxt.SetActive(true);
             Time.timeScale = 0.0f;
         }
     }
     else
     {
         firstCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
     }

     firstCard = null;
     secondCard = null;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5) 조금 특별하게 - audioManager 만들기&lt;/p&gt;
  &lt;aside&gt;
    배경음악은 `계속` 재생되어야 하기 때문에, 따로 관리해주어야 해요!

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;gameManager처럼, audioManager를 만들어 줍니다.&lt;/p&gt;
&lt;p&gt; → 스크립트도 &lt;code&gt;audioManager.cs&lt;/code&gt; 를 만들어 붙여주세요!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/94561331-7477-4b21-a993-3647c30904f7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;AudioSource 컴포넌트를 붙이고, 준비합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public AudioSource audioSource;
 public AudioClip bgmusic;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/761763b4-6c1f-44d8-a11f-7c6cbeed0d73/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Start()&lt;/code&gt; 에서 실행해줍니다.&lt;/p&gt;
&lt;p&gt; → 이번엔 계속 실행될 예정이니, 아래 두 줄을 넣어주세요!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; audioSource.clip = bgmusic;
 audioSource.Play();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;05. 빌드하기&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 마켓에 올리기 전 확인해야 하는 설정들&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Edit&lt;/code&gt; → &lt;code&gt;Preference&lt;/code&gt; → &lt;code&gt;External Tools&lt;/code&gt; 체크&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Android 빌드를 위해서는 JDK, NDK, SDK 설정이 필수&lt;/p&gt;
&lt;p&gt;  → Unity Hub에서 1주차에 설치시 함께 완료했습니다!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;만약 설치하지 않았다면?&lt;/p&gt;
&lt;p&gt;  → Unity Hub 를 통해서 추가 설치가 가능합니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/44ea4f0c-df2f-4cb6-aca5-16e844e2162f/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/44ea4f0c-df2f-4cb6-aca5-16e844e2162f/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55c2489d-9403-4255-a2f0-2d55ef4887d1/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/55c2489d-9403-4255-a2f0-2d55ef4887d1/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/040f86ea-7f52-4eb0-8246-abac147403e2/Untitled.png)

- `Edit` → `Project Settings` → `Player`
    - Company Name 과 Product Name, 그리고 Version 을 적절히 입력해주세요

        → Company Name : `SpartaCodingClub`

        → Product Name : `findRtan`

        → Version : `1.0`


    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a869b62f-360f-4d94-81a7-9dc081c8d1fd/Untitled.png)

- Icon
    - Select아이콘을 누르고 spartaMsg를 선택.
- Resolution and Presentation
    - 안드로이드로 바꾸고, Landscape Right, Left를 꺼줍니다. (우리는 세로형 게임!)

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a464db26-3bfb-4f7e-b47d-339f98e05d62/Untitled.png)

- Other Settings

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/94bb555c-663b-4a1f-93ff-04dc2173df80/Untitled.png)

    - 안드로이드 마켓에 배포하려면 64 bit 지원이 필수가 되었기 때문에 Scripting Backend 를 IL2CPP 로 변경합니다
    - Target Architectures 에서 ARM64 를 체크하도록 합니다.
- Publishing Settings

    &amp;lt;aside&amp;gt;
      Keystore란? 안드로이드에서 이 앱을 배포할 수 있는 권리!

    &amp;lt;/aside&amp;gt;

    1. `Keystore Manager` 눌러서 만들기

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f2d9ddf3-c84b-43a2-9a0d-83b737d301f6/Untitled.png)

    2. Keystore → Create New → Anywhere 클릭

        → `spartakey` 로 바탕화면에 저장해주세요

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1309b92a-3604-44b0-a683-f86d890844aa/Untitled.png)

    3. 그 외 입력하기

        → Alias : `spartakey`

        → Password : `123456` (간단하게 설정해주세요)

        → Add Key 클릭!

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/89834ffa-9f34-4d09-8c6b-1527a294d72e/Untitled.png)

    4. `Yes` 를 클릭

        ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2d783adf-1ab1-41a5-933d-45b4e9e6347a/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;2) 원하는 OS 대상으로 &lt;code&gt;Switch Platform&lt;/code&gt; 하고 빌드하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Build Settings 항목 클릭&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dfcf8d95-e38c-49a3-b49d-2a8cd0edf58f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scenes in Build에 씬 추가하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4e5add15-23e8-4856-a3b0-1f32a0057c93/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Switch Platform&lt;/code&gt; 눌러주기 (한참 시간이 걸립니다)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/875769b8-ddfd-43e1-a57e-e2371751c567/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다시 화면 사이즈를 정해주고&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/942e57d0-80f4-4b8d-8270-ff4415d56f0a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다시, 우측 하단 &lt;code&gt;Build&lt;/code&gt;를 눌러서 빌드해보기 (한참 시간이 걸립니다!)&lt;/p&gt;
&lt;p&gt; → apk 파일 이름은 &lt;code&gt;myFirstGame&lt;/code&gt; 으로 할게요!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6cfc0b4a-83f9-4381-beac-28b3f5906cc5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; → 아래와 같이 파일이 생성되면 완료!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/98867a5a-af08-4dbd-86c3-fe30f1a2ab60/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[참고] 빌드시 Can not sign the application 오류가 뜨는 경우!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6ee168f-87d6-4790-87ba-043ab9e799de/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  keystore의 비밀번호를 틀린 경우이니 keystore의 비밀번호를 다시 확인해주세요!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) 배포하려면&lt;/p&gt;
  &lt;aside&gt;
    필요할 때 다시 찾아보면서 할 수 있도록, `알아만 두기!`

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;(1) 안드로이드 폰이 있다면 → 바로 볼 수 있어요!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;컴퓨터에 폰을 usb로 연결하고 → 개발자 옵션 → USB디버깅을 켜주기!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/25eb68fd-65bf-434f-a1ba-b2a93fa5fb54/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;target device&lt;/code&gt; 를 설정하고 → &lt;code&gt;build and run&lt;/code&gt; 을 누르면 끝!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/42a45a8a-0f3c-4228-a37b-99004f91a130/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(2) 방금 만든 &lt;code&gt;.apk&lt;/code&gt; 파일을 &amp;quot;구글플레이스토어&amp;quot;나, &amp;quot;애플 앱스토어&amp;quot;에 올리면 되는 것!&lt;/p&gt;
  &lt;aside&gt;
    스파르타가 준비해둔 참고자료가 있지만,
  인터넷의 자료들을 참고해서 찬찬히 진행하시면 더 좋아요!

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;구글플레이에는 → 누구나 올릴 수 있습니다!&lt;/p&gt;
&lt;p&gt;  → 참고: $25 (1회)의 개발자 등록 비를 내야 한답니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;참고자료&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/47d42cb9-97cc-426e-9472-694206f20d8d/-.pdf&quot;&gt;스파르타코딩클럽-안드로이드배포.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;애플 앱스토어에 올리려면 → Mac PC가 있어야 해요!&lt;/p&gt;
&lt;p&gt;  → 참고: 1년 129,000원의 개발자 등록비를 내야 한답니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;참고자료&lt;/p&gt;
  &lt;aside&gt;
    전체 iOS 배포과정은 이렇습니다.

&lt;p&gt;  1) 애플 개발자 계정 준비(가입 및 라이센스 구매)&lt;br&gt;  2) 빌드: ipa( 앱 파일 ) 생성  및 앱 스토어에 배포하는 중이라는 것을 알려주기&lt;br&gt;  3) 앱스토어에서 개발중인 앱 선택 및 작업 공간 활성화&lt;br&gt;  4) 트랜스포터로 앱 파일을 앱스토어로 전송&lt;br&gt;  5) 이미지 및 최종 정보 등록 후 승인 요청&lt;/p&gt;
  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 애플 개발자 등록&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;애플 개발자 사이트에 들어가서 계정을 생성 및 라이센스를 구매합니다&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/&quot;&gt;애플 개발자 센터&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/56b9b3cf-5c2c-4f78-9dd6-58cb81110f13/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/56b9b3cf-5c2c-4f78-9dd6-58cb81110f13/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;하단의 Join the Apple Developer Program을 눌러 개발자 라이센스 구매절차를 따릅니다&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/78b18d54-253e-45aa-aaef-8e02bb07a5bc/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/78b18d54-253e-45aa-aaef-8e02bb07a5bc/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0322a804-dea0-4297-92a0-0733b61e3dc6/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0322a804-dea0-4297-92a0-0733b61e3dc6/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;개인을 눌러주세요( 혹시 회사나 단체면 그에 맞는 것을 눌러주세요&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/361a0cbb-b1a2-4d03-be70-8a32fc63ab05/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/361a0cbb-b1a2-4d03-be70-8a32fc63ab05/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eb9acf80-b0f1-455a-9294-3b10724f3408/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/eb9acf80-b0f1-455a-9294-3b10724f3408/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;aside&gt;
  애플에서 제품을 사듯 애플 개발자 라이센스를 구매해야 합니다. 
개인 회원은 1년에 $99, 기업회원은 $299입니다 꽤 비싸요! 
라이센스를 구매하고 승인 받는데 즉시에서 하루정도 소요됩니다

&lt;p&gt;다음단계 부턴 애플 개발자 라이센스가 있어서 진행이 가능해요!&lt;/p&gt;
&lt;/aside&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) iOS 빌드&lt;/p&gt;
  &lt;aside&gt;
    IOS 배포도 안드로이드 앱 배포와 마찬가지로 앱 파일을 만들고 동시에 앱 스토어에 알려줘야 합니다(빌드). 

&lt;p&gt;  터미널 화면에서의 선택! 선택! 흐름이 꽤나 어지럽습니다.&lt;br&gt;  당황하지 않고 다음 순서대로 하시면 되는데, 중간중간 저랑 다를 수 있습니다.&lt;/p&gt;
&lt;p&gt;  그때마다 캡쳐에 설명을 달아놓을테니 당황하지 마세요! &lt;/p&gt;
&lt;p&gt;  그럼 다음 명령어를 터미널에서 입력하여 실행시킵니다.&lt;/p&gt;
  &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;  expo build:ios&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;우린 배포를 할거니 아카이브를 선택합니다. 그리고 애플 계정을 만들고, 개발자 라이센스도 구매하였다면! 여기서 Y를 누릅니다&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e58bac47-0433-4a09-ae9f-2e0b6ad31d1c/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e58bac47-0433-4a09-ae9f-2e0b6ad31d1c/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음과 같이 계정을 입력하면, 추가 인증을 할 수도 있습니다. 자동으로 맥에서 추가인증 코드가 뜨거나 문자로 받아 6자리 숫자를 알려주니, 이걸 그대로 터미널에 입력 후 엔터!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd88cc8b-c31d-4b05-89c9-9a998c46d91f/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fd88cc8b-c31d-4b05-89c9-9a998c46d91f/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 다음과 같이 인증키(안드로이드에서의 싸인키 정도의 의미)를 여러분들이 직접 추가할지, 엑스포가 알아서 하게 할지 결정하라고 합니다. 엑스포에게 맡겨줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;혹시 기존에 앱을 만들었던 적이 있으면 다음과 같이 기존 키를 사용할것인가? 라고 선택하는 문구를 볼수도 있는데, 이땐 새로 추가를 하시면 됩니다. 그럼 방금 전의 화면처럼 새로 만들 키를 엑스포가 알아서 만들어 추가하게 할지 말지를 결정하는 화면을 보게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0da73def-bb73-4374-874b-736f7d8d53b0/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0da73def-bb73-4374-874b-736f7d8d53b0/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;키생성을 엑스포에 위임했다면 엑스포가 추후에 앱에서 사용할 수도 있는 노티피케이션(푸시 알람) 기능을 위한 푸시 키까지 생성을 도와줍니다. 이것 또한 엑스포가 알아서 생성하고 처리할 수 있게 위임합니다. 이것 또한 기존에 만들었던게 있으면 다음과 같이 n을 누르고 엑스포에 위임한다!를 선택하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6860ec1f-efab-495a-8a86-45246c08e703/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6860ec1f-efab-495a-8a86-45246c08e703/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로비져닝도 엑스포에 위임&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8a51641e-74f4-473c-b5d8-d2a4f4594237/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8a51641e-74f4-473c-b5d8-d2a4f4594237/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;aside&gt;
  천천히 읽어보면 결국 엑스포에 전부 위임한다로 진행하면 됩니다. 복잡한 키생성과 관리까지 엑스포가 알아서 해줍니다.

&lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 모든 과정이 끝나면 빌드 단계에 들어갑니다. ipa 파일을 생성하고 있는 겁니다. 그럼 끝다길 기다립니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d80f1615-191a-48ec-9fc4-f63adae8ff91/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d80f1615-191a-48ec-9fc4-f63adae8ff91/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c30a71dd-7db5-4190-ad86-70c67fd55eaf/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c30a71dd-7db5-4190-ad86-70c67fd55eaf/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;빌드가 완료됐다!라는 터미널 화면을 보게되면 엑스포 대시보드에 로그인하여 artifact 파일을 내려봤습니다. 쉽게 말해 앱 파일입니다. 그럼 드디어 우린 ipa 앱 파일을 다운 받게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/39ce4437-73a0-421d-8cd4-d80163f77a54/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/39ce4437-73a0-421d-8cd4-d80163f77a54/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 앱스토어에서 개발중인 앱 선택 및 작업 공간 활성화&lt;/p&gt;
  &lt;aside&gt;
   

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6b4fb6ee-d2d5-4546-8e1e-f276e652a711/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6b4fb6ee-d2d5-4546-8e1e-f276e652a711/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/27c01740-7e61-413b-9c97-434253e3da71/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/27c01740-7e61-413b-9c97-434253e3da71/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/760afeaa-46d0-47af-b0c5-8ea8518c93be/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/760afeaa-46d0-47af-b0c5-8ea8518c93be/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;플랫폼은 iOS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이름은 여러분이 원하는데로!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;기본언어는 한국어!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;번들 ID는 여러분들이 방금 엑스포에서 빌드(배포)단계를 거쳤다면 표시가 나옵니다. 그걸 누르세요!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SKU는 유니크한 앱 아이디를 쓰면 되는데, app.json 에 썼던 &amp;quot;bundleIdentifier&amp;quot;: &amp;quot;com.sparta.psytest&amp;quot;, 의 값을 넣어주세요. com.sparta.psytest 이거요!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그리고 전체 엑세스!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6a68eb77-f188-42e9-846a-eaff49df14bb/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6a68eb77-f188-42e9-846a-eaff49df14bb/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) 트랜스포터로 앱 파일을 앱스토어로 전송&lt;/p&gt;
  &lt;aside&gt;
    앱스토어에 앱파일을 올릴땐, 사이트에서 사진 파일 업로드 하듯이 올릴 수 없습니다. 프로그램을 사용해야 하는데요! Expo에선 보통 트랜스포터(transporter)를 사용합니다.

  &lt;/aside&gt;

&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8f331f9b-966a-4490-b5f8-61bbfaf6fb24/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8f331f9b-966a-4490-b5f8-61bbfaf6fb24/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9e09a4ea-cec8-44b1-9d51-a1ddf1e370f8/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9e09a4ea-cec8-44b1-9d51-a1ddf1e370f8/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;앱 스토어에서 해당 프로그램을 다운받고 실행합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/618fdaee-7671-4149-ae8c-8285982bae5f/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/618fdaee-7671-4149-ae8c-8285982bae5f/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3d1dbfc1-079f-4361-88a7-f75161d61d7d/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3d1dbfc1-079f-4361-88a7-f75161d61d7d/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expo 대시보드에서 내려받은 ipa 파일(대시보드 상에서 보았던 artifact 파일)을 선택하고 전송을 누릅니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3193ca0f-0447-4b29-b51d-efcca65eee15/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3193ca0f-0447-4b29-b51d-efcca65eee15/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/50b5de6a-7c63-4e05-a899-1188f1f794a0/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/50b5de6a-7c63-4e05-a899-1188f1f794a0/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;전송이 완료되면, 앱 스토어 관리자 페이지 활동 내역에서 처리중인것을 확인이 가능합니다. 좀 기다리면 처리중에서 사용 가능한 단계로 바뀝니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;우리가 안드로이드 배포때 만든 앱로고도 보이죠?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/777ceb44-4691-4e24-a3d9-749b5f5cfda7/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/777ceb44-4691-4e24-a3d9-749b5f5cfda7/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5) 이미지 및 최종 정보 등록 후 승인 요청&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;그럼 실제 사용 단계 전까지 기다리는 동안 나머지 이미지들과 정보를 기입합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이미지는 권고사항을 그대로 따라야 합니다. &lt;a href=&quot;https://help.apple.com/app-store-connect/#/devd274dd925&quot;&gt;(권고 사항 보기)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이 또한 온라인 포토샵 또는 디자인 툴로 준비를 해서 차근차근 업로드를 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8e7761cf-31c2-48c4-977f-74d112207a50/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8e7761cf-31c2-48c4-977f-74d112207a50/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프로모션, 설명, 키워드는 앱 관련해서 적고 싶은 정보를 기입하고, 지원 URL, 마케팅 URL은 일단! 스파르타코딩 클럽 홈페이지 주소로 넣습니다. 혹시 심사가 이것때문에 통과되지 않는다면 관련 홈페이지를 간단하게 준비하셔야해요!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f7e7c4f2-ab1e-4122-97e0-56f4573d446e/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f7e7c4f2-ab1e-4122-97e0-56f4573d446e/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;앱클립, iMessage, Apple Watch 모두 넘어갑니다. 우립 앱을 만들고 있으니까요. 그리고 빌드 파일 선택은 위에서 우리가 트랜스포터로 전송 시킨 앱이 처리중에서 사용가능 단계가 되면 선택을 할 수 있게 됩니다. 일단 넘어갑니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c91341a4-b972-4103-8cfb-4617dbe13508/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c91341a4-b972-4103-8cfb-4617dbe13508/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다음 정보에선 등급만 설정합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;저희 앱은 전부 아니오를 체크하면 됩니다. 해당사항이 없습니다  &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4c6fc260-3d12-4955-a05a-e1a91ffa7136/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4c6fc260-3d12-4955-a05a-e1a91ffa7136/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;앱 심사 정보는 앱이 제대로 통과가 안되면, 안된 사유를 알려주기 위해 혹은 커뮤니케이션 할 사람의 정보를 기입하면됩니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;유의할점은 전화번호 형식은+8210~과 같이 국가 번호를 입력해야합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b5f69d0a-c443-4696-908b-15394ac9c3b2/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b5f69d0a-c443-4696-908b-15394ac9c3b2/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ab8d92cf-f743-4ab4-b51e-4edbdf48736d/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ab8d92cf-f743-4ab4-b51e-4edbdf48736d/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이번 절차 또는 추후(앱 재배포)에 앱 심사를 거친다음 앱을 본인이 직접 버튼을 눌러 앱 스토어에 제출할건지 아니면 자동으로 제출하게 할건지 결정합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;광고 식별자는 우리에게 해당사항이 없으므로 아니요!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e409446a-884a-4191-aa14-48274c9994ce/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e409446a-884a-4191-aa14-48274c9994ce/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그리고 저장을 누릅니다&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/82239bb9-7a70-4af4-8284-a8c0a2fba6d0/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/82239bb9-7a70-4af4-8284-a8c0a2fba6d0/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;저장을 눌렀으면 왼쪽에 앱 정보를 누릅니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;개인정보 처리방침 URL은 스파르타코딩 클럽 홈페이지 주소를 넣습니다.&lt;br&gt;( 사실 이렇게하면 결과적으로 앱 심사를 통과할 수 없습니다. iOS 앱 심사는 정말 까다롭기 때문에, 실체적이고 유효한 값들을 넣어야 하는데, 일단 그 과정만 같이 하는 것에 의의를 두고 추후에 앱을 정말 배포 하실땐 관련 정보를 제대로 준비하셔야 합니다.)&lt;/p&gt;
&lt;p&gt;깃허브 리파짓 토리를 파고 다음 사례처럼 &lt;a href=&quot;http://readme.md&quot;&gt;README.md&lt;/a&gt; 에 작성하여 URL로 사용할 수 있습니다.&lt;br&gt;&lt;a href=&quot;https://github.com/inswag/FrenchVocaPrivacyPolicy/blob/master/readme.md&quot;&gt;(사례)&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;부재도 적당히 기입해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/917f9831-1dcc-4539-ba3b-8ca120f65bc7/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/917f9831-1dcc-4539-ba3b-8ca120f65bc7/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;카테고리도 앱 성격에 맞게 선택해주시고, 콘텐츠 권한도 심리테스트 앱에서 타사 콘텐츠에 엑세스 안함을 선택해줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그리고 저장!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d8205872-a42a-4bc8-a080-2545d79cb599/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d8205872-a42a-4bc8-a080-2545d79cb599/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b2f35112-4f1f-4ca5-a762-91061e886700/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b2f35112-4f1f-4ca5-a762-91061e886700/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;왼쪽의 가격 및 사용 가능 여부를 선택한다음 무료를 선택해주세요&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그리고 저장!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/625c0b94-742f-4f2f-b393-3a7e763cb54d/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/625c0b94-742f-4f2f-b393-3a7e763cb54d/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;자 이제 상단의 App Store 탭을 누르면 우리가 초반에 작성했던 페이지를 보게됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9e6aeb60-85a8-42fc-a30a-7c9d7c52502a/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9e6aeb60-85a8-42fc-a30a-7c9d7c52502a/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/33e7b0d0-cd6e-4bab-b338-736fa635bc9b/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/33e7b0d0-cd6e-4bab-b338-736fa635bc9b/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;암호화를 사용하고 있지 않음을 눌러줍니다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그 다음 마지막으로 우측 상단에 심사를 위해 제출을 누릅니다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그러면 왼쪽에 심사 대기중을 보게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c9917bfe-9fe6-4fad-badc-077158b7069a/Untitled.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c9917bfe-9fe6-4fad-badc-077158b7069a/Untitled.png&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;06. 광고 붙이기&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;[준비하기]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  `06` 번 빌드를 안해두면 → 광고를 붙일 수 없으니 평소에도 참고하세요!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) Unity Ads 란?&lt;/p&gt;
  &lt;aside&gt;
    Unity 자체적으로 광고를 붙일 수 있고, 광고비를 정산해주는 시스템이에요!

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;기존에도 광고를 붙일 수 있는 방법은 많았으나, Unity 에서 직접 지원하는 이 방법을 통해 코딩을 잘 몰라도 손쉽게 광고를 붙이고 관리할 수 있게 되었습니다!&lt;/li&gt;
&lt;li&gt;이 밖에 google ads 등이 있지만, 게임을 만들 때에는 Unity Ads가 무척 편하답니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) Unity 에디터 내에서 Unity Ads 추가하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Windows&lt;/code&gt; → &lt;code&gt;General&lt;/code&gt; → &lt;code&gt;Services&lt;/code&gt; 탭을 클릭하여 Service 메뉴 보기&lt;/p&gt;
&lt;p&gt; → General Settings 클릭&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4bb64d7e-e270-42e7-9465-e7ccdbd534f7/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;organizations 드롭다운 해서 선택 → &lt;code&gt;Create Project ID&lt;/code&gt; 클릭&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/be216026-1db8-4498-bb2c-24722eca11ed/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ads의 off 클릭&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/284860ea-8db4-4297-a8ca-dfa2ea55f55c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;패키지 매니저 사용하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[참고] 추가 설치를 왜 진행 하나요?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  유니티 업데이트로 기본 Advertisement 설치시 4.4.1 버전이 설치됩니다. &lt;/p&gt;
&lt;p&gt;  이 버전으로 설치하시고 진행하시면 오류가 발생하니 &lt;/p&gt;
&lt;p&gt;  강의 영상과 동일한 3.7.5 버전의 패키지 설치를 위해 패키지 매니저를 사용합니다!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    1) 14세 이하 ... **No**

    2) Window → Package Manager로 패키지 매니저 열고 Packages: Unity Registry 클릭합니다.

    ![unity registry.jpg](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8f21317e-2e20-474c-986d-bc69be5f3c3b/unity_registry.jpg)

    3) Advertisement install 클릭합니다 ( 3.7.5 버전인지 꼭 확인! )

    ![advertisement1.jpg](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b8f9f85-1350-47b4-9f6a-b22cbd945de9/advertisement1.jpg)

    4) Service로 돌아와서 **off** 를 **on** 으로 변경 합니다.

    5) **on** 이라고 뜨면 일단 준비 끝납니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dacd38c9-8bc1-4224-8569-70b4d76eeff3/Untitled.png)

    → 강의 영상과 달리 **Install Lastest Version** 은 클릭하실 필요 없습니다!

    6) Test mode 체크하기!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0d642823-e5ee-484f-893e-282a6b436997/Untitled.png)

    7) Ads Package 클릭 → **Install Lastest Version** 해주기&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;[게임 끝나면 붙이기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) adsManager 준비하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;adsManager&lt;/code&gt; 만들기&lt;/p&gt;
 &lt;aside&gt;
   광고도 gameManager 처럼 한 군데에서 관리되고, 이것을 여기저기서 불러서 사용하는 것이 편하답니다!

 &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;단, 이번엔 꽤 작업할 것이 많아서, 붙여놓고 살펴볼게요!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;adsManager를 만들고, &lt;code&gt;adsManager.cs&lt;/code&gt; 를 붙여줍니다.&lt;/p&gt;
&lt;p&gt;  → 아래를 먼저 추가하고, 코드스니펫 추가!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  using UnityEngine.Advertisements;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] adManager.cs 코드&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  public static adsManager I;

  string adType;
  string gameId;
  void Awake()
  {
      I = this;

      if (Application.platform == RuntimePlatform.IPhonePlayer)
      {
          adType = &amp;quot;Rewarded_iOS&amp;quot;;
          gameId = &amp;quot;iOS 아이디&amp;quot;;
      }
      else
      {
          adType = &amp;quot;Rewarded_Android&amp;quot;;
          gameId = &amp;quot;Android 아이디&amp;quot;;
      }

      Advertisement.Initialize(gameId, true);
  }

  public void ShowRewardAd()
  {
      if (Advertisement.IsReady())
      {
          ShowOptions options = new ShowOptions { resultCallback = ResultAds };
          Advertisement.Show(adType, options);
      }
  }

  void ResultAds(ShowResult result)
  {
      switch (result)
      {
          case ShowResult.Failed:
              Debug.LogError(&amp;quot;광고 보기에 실패했습니다.&amp;quot;);
              break;
          case ShowResult.Skipped:
              Debug.Log(&amp;quot;광고를 스킵했습니다.&amp;quot;);
              break;
          case ShowResult.Finished:
              // 광고 보기 보상 기능 
              Debug.Log(&amp;quot;광고 보기를 완료했습니다.&amp;quot;);
              break;
      }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;몇가지 세팅하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Start()&lt;/code&gt; 와 &lt;code&gt;Update()&lt;/code&gt; 는 지워주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;iOS 아이디&lt;/code&gt;, &lt;code&gt;Android 아이디&lt;/code&gt; 부분에 내 아이디 (숫자)를 적으세요&lt;/p&gt;
&lt;p&gt;  → 어디있냐고요?&lt;/p&gt;
&lt;p&gt;  → Window → General → Services → Ads 클릭 → 아랫쪽에!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9addc8c5-de2a-44b9-ac6c-0734e053031b/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;광고 활성화 시켜주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Dashboard를 누르고 로그인 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e1aa8bba-23a9-447f-975b-7fd4ed625ee6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;b.  &lt;strong&gt;Get Started&lt;/strong&gt; 를 누르고 활성화를 시켜줍니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e6957f82-c3d0-494e-a48b-2901a9f16c00/unity_monetization_1.jpg&quot; alt=&quot;unity monetization_1.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;c.  Project Setup 으로 진행 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5876de90-dc09-4f95-bc98-c5eb8d4b8fa5/ads.jpg&quot; alt=&quot;ads.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e3b9ab38-80f2-4ef4-ab22-04cd83049b25/ads1_.jpg&quot; alt=&quot;ads1_.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/15df67ed-63d4-4aaa-af4a-58dde0d07fb6/ads2_.jpg&quot; alt=&quot;ads2_.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;d.  완료하면 아래 화면에서 ID를 확인할 수 있습니다  &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6c4725f4-2ed2-4db3-a92e-3f209e1a8879/ad_fin.jpg&quot; alt=&quot;ad fin.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;e.  Finish setup 이후에는 Ad Units에서 확인하실 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6582d022-57b2-43ad-91e2-de654153b7c0/ad_unit.jpg&quot; alt=&quot;ad_unit.jpg&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) &lt;strong&gt;끝&lt;/strong&gt; 텍스트를 눌렀을 때 광고 뜨게 하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에서 30초 → 3초에 끝나게 하기  &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; if (time &amp;gt; 3.0f)
 {
     endTxt.SetActive(true);
     Time.timeScale = 0.0f;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;endTxt.cs&lt;/code&gt; 에서 넘어가던 씬을 &lt;code&gt;gameManager.cs&lt;/code&gt; 의 함수로 만들기&lt;/p&gt;
&lt;p&gt; → 우선, 따라해볼게요!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using UnityEngine.SceneManagement;

 public void retryGame()
 {
     SceneManager.LoadScene(&amp;quot;MainScene&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;adsManager.cs&lt;/code&gt; 에서 보상을 적어두기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void ResultAds(ShowResult result)
 {
     switch (result)
     {
         case ShowResult.Failed:
             Debug.LogError(&amp;quot;광고 보기에 실패했습니다.&amp;quot;);
             break;
         case ShowResult.Skipped:
             Debug.Log(&amp;quot;광고를 스킵했습니다.&amp;quot;);
             break;
         case ShowResult.Finished:
             // 광고 보기 보상 기능 
             gameManager.I.retryGame();
             break;
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;endTxt.cs&lt;/code&gt; 에서 마지막으로 텍스트가 눌렸을 때 광고를 보게 하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void retryGame()
 {
     adsManager.I.ShowRewardAd();
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;끝&lt;/code&gt; 텍스트를 눌렀을 때 아래와 같이 나오면 완성!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e59de8e4-5bdd-42a6-8910-2e27441f91db/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) 주의사항&lt;/p&gt;
  &lt;aside&gt;
    배포를 할 때에는 `두 가지`를 다시 바꿔주어야 해요!

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Advertisement.Initialize(gameId, true);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → 런칭할 때는 여기 true 를 false 로만 바꿔줘야 합니다. (true는 테스트를 하겠다는 뜻)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;여기도 체크 해제해줘야겠죠!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/117d8b2f-f0cc-4b80-a50a-8490d47f37f0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그리고 다 했으면!&lt;/p&gt;
&lt;p&gt; → 게임 종료 조건 &lt;code&gt;3.0f&lt;/code&gt; 를 다시 → &lt;code&gt;30.0f&lt;/code&gt; 로 바꿔둬야겠죠!  &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;strong&gt;07. 게임제작 꿀팁 _ 에셋스토어, next step&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;[무료 에셋스토어 구경하기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 무료 에셋스토어란?&lt;/p&gt;
  &lt;aside&gt;
    만약 당신이 1인 개발자라면!

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;음악, 배경, 캐릭터, 애니메이션.. 모든 것을 할 수 없겠죠!&lt;/li&gt;
&lt;li&gt;그럴 때 이용하는 것이 &amp;quot;무료 에셋스토어&amp;quot;들이랍니다.&lt;/li&gt;
&lt;li&gt;사실은 &amp;quot;유료&amp;quot;라고 하더라도 비싼 가격은 아니기에, 실제 1인 개발자들은 틈 날 때마다 이 곳을 들여다보고 괜찮은 것들을 사두기도 한답니다. 할인 이벤트도 자주 해요!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 유명한 곳 둘러보기 (1)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] &lt;a href=&quot;http://opengameart.org&quot;&gt;OpenGameArt.org&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  https://opengameart.org/&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UI가 조금 올드해보여도 이만한 데가 없답니다!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Browse → 2D Art → &lt;code&gt;CCO&lt;/code&gt; 에 클릭하고 다시 검색을 눌러 둘러볼까요?&lt;/p&gt;
  &lt;aside&gt;
    **알아두기 : 여기있는 에셋은 무료이긴 하지만, `저작권 사용법` 이 조금씩 달라요!**

  &lt;/aside&gt;

&lt;p&gt;  [우선 이 정도로만 알아둘게요]&lt;/p&gt;
&lt;p&gt;  → CC-BY , GPL , ... ⇒ 사용에 뭔가 조건이 있음&lt;/p&gt;
&lt;p&gt;  → CC0 ⇒ 사용에 아무런 조건이 없음&lt;/p&gt;
&lt;p&gt;  [사용법]&lt;/p&gt;
&lt;p&gt;  → 눌러서 다운로드 받아 사용하면 된답니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/807cbd38-ebf9-463d-9b2a-d9c70edb910f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 유명한 곳 둘러보기 (2)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 유니티에셋스토어&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  https://assetstore.unity.com/2d?category=2d&amp;amp;free=true&amp;amp;orderBy=1&amp;amp;rows=264&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 예제 import 해보기&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  https://assetstore.unity.com/packages/audio/sound-fx/free-casual-game-sfx-pack-54116&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;→ `Add to My Assets` 클릭 → Accept 클릭 → Open in Unity 클릭

→ `Download` 클릭 → `Import` 클릭

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2b87411f-9557-466b-8262-5890203dfae5/Untitled.png)

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7943cbea-eb76-42aa-8145-04e1d90eda08/Untitled.png)

→ Import 를 클릭하면 폴더가 생긴답니다!

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/117be35f-6d63-4f1c-907f-c3d87b79b776/Untitled.png)

→ 여기서 쓰고 싶은 것만 남기고 나머지를 삭제하셔도 무방합니다. (또는 다른 곳에 보관!)

![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/dfe81a5d-1619-444e-83ed-f562464a0906/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;4) 팁 - 사용 방법&lt;/p&gt;
  &lt;aside&gt;
    사실 우리에게는.. 애니메이션, 캐릭터 보다는  

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;배경이미지&lt;/code&gt;, &lt;code&gt;효과음&lt;/code&gt;, &lt;code&gt;배경음악&lt;/code&gt; 을 가져다 쓰는 게 좀 더 적당하답니다.&lt;/li&gt;
&lt;li&gt;사실 여러분이 오늘 붙인 효과음도 &lt;a href=&quot;http://opengameart.org&quot;&gt;&lt;code&gt;OpenGameArt.org&lt;/code&gt;&lt;/a&gt; 에서 가져온 것!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[우리가 만들만한 게임의 범위]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 되짚어보기 + 더 공부한다면?&lt;/p&gt;
  &lt;aside&gt;
    5주 동안 무엇을 배웠나요? 다시 찬찬히 돌아보지요!

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1주차: 유니티의 기본 사용법 - &lt;code&gt;빗물받는르탄이&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2주차: 유니티 사용법 복습1 + 데이터저장 - &lt;code&gt;풍선을 지켜라&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3주차: 유니티 사용법 복습2 + 레벨 구현 - &lt;code&gt;고양이 밥주기&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4주차: 퍼즐게임 만들기 &amp;amp; 로직체험 - &lt;code&gt;르탄이 맞추기&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5주차: 소리, 광고&lt;/p&gt;
&lt;aside&gt;
  아래 내용은 조금만 더 공부하면 깨우칠 수 있어요!

&lt;/aside&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;서버에 데이터 저장하기( firebase ), 3D로 카메라를 활용하기&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;  ⇒ 이것으로 딱 만들만한 것은,  &lt;code&gt;하이퍼캐주얼 게임&lt;/code&gt; !&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 하이퍼캐주얼 게임이란?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;대부분 1) 세로로 플레이, 2) 원 버튼(터치, 슬라이드), 3) 30~60초에 한 판&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;아래와 같은 게임들 많이 보셨죠!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5db8f513-d4e9-41c3-991e-eb563a5b8170/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;막간상식) 하이퍼캐주얼 게임으로 굉장히 유명해진 회사들도 많아요!&lt;/p&gt;
&lt;p&gt;  → 한 달에 3~4개씩 게임을 만들어, 몇 개가 초 대박을 터뜨리면서 큰! 회사가 됐답니다.&lt;/p&gt;
&lt;p&gt;  → &lt;code&gt;Voodoo&lt;/code&gt; , &lt;code&gt;Ketchapp&lt;/code&gt; , &lt;code&gt;Lion studio&lt;/code&gt;와 같은 회사들이 있어요!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 내가 게임을 기획해본다면?&lt;/p&gt;
  &lt;aside&gt;
    아주 큰 비약으로 알아보는 하이퍼캐주얼 게임의 종류 (재미로 나눠본!  )

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;쫄깃 류 : 아주 간단한데, 콤보가 핵심이라, 10 → 11 → 12콤보 갈 때에 심장이 쫄깃!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5a88f88f-007d-4eef-a8c9-ca3307e8d804/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/79f9ddd7-e835-4f1a-86c0-85eee15814c0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;통쾌 류 : 다다다다다 없앨 때 느끼는 쾌감&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0ff5e6ef-52ff-40a3-b8b4-a235aa0a945a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;경쟁 류 : 내가 죽나 네가 죽나 해보자! (지금 수준에선 만들기 어려워요!)&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0dfe00d5-de94-471e-9ebc-2b6c2decb35d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;콘텐츠 류 : 더 궁금해. 궁금해!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2a68ccda-33f7-4bfe-b142-28abbd04bddc/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;퍼즐 류 : 으으으. 깨고 싶다 깨고 싶어!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2656df0d-c998-442b-8df7-c3bdfec822b1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;08. 숙제 - 게임 둘러보기&lt;/h2&gt;
&lt;aside&gt;
  이제 수업 끝! 구글플레이 스토어를 둘러보고, 나중에 참고할 만한 게임을 하나 골라주세요.

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 구글플레이 스토어&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  https://play.google.com/store/search?q=ketchapp&amp;amp;c=apps&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;힌트요정 -  &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  → 우선 &lt;code&gt;Ketchapp&lt;/code&gt; 이라는 회사로 맞춰뒀어요.&lt;/p&gt;
&lt;p&gt;  → 여기서부터 출발해서, 유사한 콘텐츠를 타고 돌아다녀보세요!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fdb0b63b-3790-4ea0-9d65-f88008cc63e0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  예) 제출 링크 예시&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  https://play.google.com/store/apps/details?id=com.ketchapp.dunkshot&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;HW. 5주차 숙제 해설&lt;/h2&gt;
&lt;p&gt;마음에 드는 게임의 URL을 제출하면 끝!&lt;/p&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/967</guid>
      <comments>https://comphy.tistory.com/967#entry967comment</comments>
      <pubDate>Sun, 8 Jan 2023 19:52:33 +0900</pubDate>
    </item>
    <item>
      <title># [스파르타코딩클럽] 게임개발 종합반 - 4주차</title>
      <link>https://comphy.tistory.com/966</link>
      <description>&lt;h1&gt;[스파르타코딩클럽] 게임개발 종합반 - 4주차&lt;/h1&gt;
&lt;aside&gt;
&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; width=&quot;40px&quot; /&gt; **매 주차 강의자료 시작에 PDF파일을 올려두었어요!**

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9511eb36-5f13-45bc-b523-a27e95180800/%E1%84%89%E1%85%B3%E1%84%91%E1%85%A1%E1%84%85%E1%85%B3%E1%84%90%E1%85%A1%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B5%E1%86%BC%E1%84%8F%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A5%E1%86%B8_%E1%84%80%E1%85%A6%E1%84%8B%E1%85%B5%E1%86%B7%E1%84%80%E1%85%A2%E1%84%87%E1%85%A1%E1%86%AF_%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%92%E1%85%A1%E1%86%B8%E1%84%87%E1%85%A1%E1%86%AB_4%E1%84%8C%E1%85%AE%E1%84%8E%E1%85%A1_211108.pdf&quot;&gt;스파르타코딩클럽_게임개발_종합반_4주차_211108.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;보드 게임을 만들어보기&lt;/li&gt;
&lt;li&gt;카드 뒤집기 게임을 만들면서, 총복습하기&lt;/li&gt;
&lt;li&gt;게임에 필요한 &amp;quot;로직&amp;quot;을 경험하기&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;01. 오늘 배울 것&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 4주차 수업의 목표와 범위&lt;/p&gt;
  &lt;aside&gt;
    오늘의 키워드! `총 복습+보드게임의 시작`

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;여태까지 배웠던 것들로 만들거예요!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;카드 뒤집기 게임을 만들면서 보드게임 만들기의 기초를 배워봅니다.&lt;/p&gt;
&lt;p&gt;  → 보드게임은 우리가 만들었던 게임과 살~짝 다르게, &amp;quot;로직&amp;quot;이 중요하답니다.&lt;/p&gt;
&lt;p&gt;  → 지난 주차에 했던 것들이 새록새록 기억 날 거예요!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 오늘 만들 것 : &lt;code&gt;르탄이 카드 뒤집기 게임&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;같은 모양의 르탄이를 뒤집는 게임! 해본 적 있죠?  &lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/df06f082-6520-4b06-9685-abeae861e3ed/%EB%A5%B4%ED%83%84%EC%9D%B4_%EB%A7%9E%EC%B6%94%EA%B8%B0.mp4&quot;&gt;르탄이 맞추기.mp4&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 만들 순서&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;기본 씬 구성하기 : 배경, 타이머, 리소스 받아두기&lt;/li&gt;
&lt;li&gt;시간 보내기&lt;/li&gt;
&lt;li&gt;카드 깔기&lt;/li&gt;
&lt;li&gt;카드 뒤집기 애니메이션 만들기&lt;/li&gt;
&lt;li&gt;같은 카드을 뒤집었을 때 없애기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[기본 씬 구성하기]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  `findRtan` 이라는 프로젝트를 만들고 시작할게요!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 기본 세팅하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;windows → 2x3 layout 클릭! free aspect → phone 클릭!&lt;/p&gt;
&lt;p&gt;  → &lt;code&gt;rgb ⇒ 90, 90, 225&lt;/code&gt; 로 맞출게요! (MainCamera)&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9afb087c-38ee-4c0e-b22a-ec55a140122e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 타이머 만들어두기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;UI → Text 클릭 → timeTxt 로 만들어두기&lt;/p&gt;
&lt;p&gt;  → &lt;code&gt;font size: 70 , y: 400, width: 200, height: 200, Color: (255, 255, 255, 255)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d863a963-d395-4d2c-8b03-5a83ca050dd8/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;02. 카드 만들기 - 한 장&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 르탄이 이미지 받아두기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 르탄이 이미지 모음&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week04/rtan.zip&quot;&gt;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week04/rtan.zip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8f3843e9-bfa4-41f5-b1b6-a769162bfabf/rtan.zip&quot;&gt;rtan.zip&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Images 폴더 아래에 rtan 이미지 풀어두기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3d9b3a70-0acb-4557-b287-3e070de3a57d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 카드 한 장만 만들어두기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Cards (Create Empty!) 아래에 → card (Create Empty) 한 장만 만들어둘게요&lt;/p&gt;
&lt;p&gt; → 앞면(front)은 르탄이 이미지가 들어가고, 뒷면(back)은 &lt;code&gt;?&lt;/code&gt; 물음표가 들어갑니다.&lt;/p&gt;
&lt;p&gt; → card 아래에 front/back 로 sprite를 만들어둘게요. 아래처럼!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f584e1a9-8b2d-469e-8fac-e787c7fcffa4/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;front&lt;/code&gt; 스프라이트에 rtan0 이미지를 끌어다 놓습니다.&lt;/p&gt;
&lt;p&gt; → 앗, 너무 크네요! 이미지를 클릭해서 &lt;code&gt;pixels per unit&lt;/code&gt; 을 450으로 맞춥니다.&lt;/p&gt;
&lt;p&gt; → 다른 르탄이들도 모두 450으로 맞춰주세요! (shift 눌러서 한번에 잡아 바꾸기)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/26f7531f-1381-4363-8db0-3ff173f0055e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;우선 &lt;code&gt;front&lt;/code&gt; 는 꺼두겠습니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/37704e23-4868-486a-bbe8-7b003c993801/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;back 아래에 Canvas를 만들어 Text로 &lt;code&gt;?&lt;/code&gt; 표시를 넣어주세요&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Canvas UI를 만듭니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Render-Mode를 &lt;code&gt;World Space&lt;/code&gt; 로 변경 해줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transform Reset 버튼을 통해 Rect Transform을 초기화합니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e32bf9ce-1bde-44cd-bae8-9f05291631ea/a5.jpg&quot; alt=&quot;a5.jpg&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Order in layer를 &lt;code&gt;1&lt;/code&gt;로 바꾸어 줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Text에서 Font Size는 50으로 설정 합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scale 부분을 (0.01 , 0.01)로 맞추어 줍니다. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2e7af2e6-0ee0-40ed-9140-085d17853f9a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;마지막으로 Card의 Scale을 1.3으로 바꾸기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/950e1692-b833-40b2-929b-2aec1bed0d8f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;03. 시간 가게 하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) gameManager 세팅하기 (쉬운 것부터!)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;gameManager 만들기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/02ea1c4a-5e11-4eee-bf74-7fdc08efc772/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;시간이 가게 하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public Text timeTxt;
 float time = 0.0f;

 void Update()
 {
     time += Time.deltaTime;
     timeTxt.text = time.ToString(&amp;quot;N2&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Play 해서 확인해보기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f3783ff4-d907-448b-b6c5-60a53d7f1602/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;04. 카드(1)_배치하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 배치 전략&lt;/p&gt;
  &lt;aside&gt;
    GameManager에서, 코드로 만들어서 붙입시다!

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;카드를 16장 만들어서 직접 배치하는 방법은 → 100장이면 너무 힘들잖아요!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;지금 카드 사이즈가 &lt;code&gt;x:1.3, y:1.3&lt;/code&gt; 이니까, &lt;code&gt;1.4&lt;/code&gt; 만큼씩 띄워서 붙여주려고요!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b3c05acc-bd9d-4dac-b233-6d2d3b5aba84/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 자동으로 카드 생성하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;card를 프리팹으로 만들기&lt;/p&gt;
&lt;p&gt; → 기존 것은 과감히 삭제하기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0aa194c3-5d93-4154-ac58-cdd18facb2e5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;카드 생성하기 전에 &lt;code&gt;반복문&lt;/code&gt; 을 구경해보기 (튜터만 할게요!)&lt;/p&gt;
 &lt;aside&gt;
   이렇게 하면 i 가 0~15까지 로그를 찍어준답니다. 즉, 하나씩 커지면서 `반복` !

 &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     for (int i = 0; i &amp;lt; 16; i++)
     {
         Debug.Log(i);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;card를 새로 만들어서 cards 아래에 붙이기&lt;/p&gt;
&lt;p&gt; → 실행해서 확인해볼까요?&lt;/p&gt;
 &lt;aside&gt;
   Instantiate를 아래와 같이 받으면 생성된 오브젝트를 제어할 수 있습니다.

 &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject card;

 void Start()
 {
     for (int i = 0; i &amp;lt; 16; i++)
     {
         GameObject newCard = Instantiate(card);
         newCard.transform.parent = GameObject.Find(&amp;quot;cards&amp;quot;).transform;
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b9e3cc7f-ab54-4dbb-b759-9306dde376dd/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 카드 위치 잡아주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;전략을 생각하기&lt;/p&gt;
 &lt;aside&gt;
   참고로 0번째 부터 시작하니, 1번째는 사실상 두 번째 카드!

 &lt;/aside&gt;

&lt;p&gt; [예를 들어 &lt;code&gt;1번째&lt;/code&gt; 라고 하면]&lt;/p&gt;
&lt;p&gt; → x : 1을 4로 나눈 몫 = 0&lt;/p&gt;
&lt;p&gt; → y : 1을 4로 나눈 나머지 = 1&lt;/p&gt;
&lt;p&gt; ⇒ (0,1) 위치 ⇒ (1.4씩 곱해주면) ⇒ &lt;code&gt;(0, 1.4)&lt;/code&gt; 위치&lt;/p&gt;
&lt;p&gt; [예를 들어 7번째 라고 하면]&lt;/p&gt;
&lt;p&gt; → x : 7을 4로 나눈 몫 = 1&lt;/p&gt;
&lt;p&gt; → y : 7을 4로 나눈 나머지 = 3&lt;/p&gt;
&lt;p&gt; ⇒ (1,3) 위치 ⇒ (1.4씩 곱해주면) ⇒ &lt;code&gt;(1.4, 4.2)&lt;/code&gt; 위치&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;카드 위치 잡아주기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     for (int i = 0; i &amp;lt; 16; i++)
     {
         GameObject newCard = Instantiate(card);
         newCard.transform.parent = GameObject.Find(&amp;quot;cards&amp;quot;).transform;

         float x = (i / 4) * 1.4f;
         float y = (i % 4) * 1.4f;
         newCard.transform.position = new Vector3(x, y, 0);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9dbe1612-cd33-4746-b8f9-e5501e2f5304/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;카드를 전체적으로 옮겨주기&lt;/p&gt;
&lt;p&gt; → x, y를 적당히 빼줘서 전체를 가운데에 위치하게 하기&lt;/p&gt;
&lt;p&gt; → 여기서는 &lt;code&gt;x: -2.1f, y: -3.0f&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     for (int i = 0; i &amp;lt; 16; i++)
     {
         GameObject newCard = Instantiate(card);
         newCard.transform.parent = GameObject.Find(&amp;quot;cards&amp;quot;).transform;

         float x = (i / 4) * 1.4f - 2.1f;
         float y = (i % 4) * 1.4f - 3.0f;
         newCard.transform.position = new Vector3(x, y, 0);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;완성된 배치 구경하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ed9fbbb6-e176-4b32-a0fd-22e76527e04a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;05. 카드(2)_르탄이 넣기, 애니메이션&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;[르탄이 넣기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 랜덤으로 섞기 전략&lt;/p&gt;
  &lt;aside&gt;
    C#에서 리스트 요소들을 랜덤하게 섞는 방법! (눈으로만 봐두기!)

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;일단 [0, 0, 1, 1, 2, 2, ..., 7, 7]까지 쓰인 리스트를 만들고&lt;/li&gt;
&lt;li&gt;이걸 섞어서 [2, 3, 4, 1, 2, 0, 1, ..., 7]로 만들고&lt;/li&gt;
&lt;li&gt;카드가 만들어 질 때 하나씩 꺼내서 르탄이 이미지를 붙여주기!&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 리스트를 랜덤으로 섞기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;우선 리스트 만들고 출력하기&lt;/p&gt;
&lt;p&gt; → 카드가 만들어지면서 &lt;code&gt;Debug.Log&lt;/code&gt; 해보면 되겠죠?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
         int[] rtans = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };

     for (int i = 0; i &amp;lt; 16; i++)
     {
         GameObject newCard = Instantiate(card);
         newCard.transform.parent = GameObject.Find(&amp;quot;cards&amp;quot;).transform;

         float x = (i / 4) * 1.4f - 2.1f;
         float y = (i % 4) * 1.4f - 3.0f;
         newCard.transform.position = new Vector3(x, y, 0);

         Debug.Log(rtans[i]);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;리스트를 섞기&lt;/p&gt;
&lt;p&gt; → 아래를 맨 위에 추가&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using System.Linq;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 랜덤하게 섞기&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  rtans = rtans.OrderBy(item =&amp;gt; Random.Range(-1.0f, 1.0f)).ToArray();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    ```csharp
    void Start()
    {
            int[] rtans = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };

        rtans = rtans.OrderBy(item =&amp;gt; Random.Range(-1.0f, 1.0f)).ToArray();

        for (int i = 0; i &amp;lt; 16; i++)
        {
            GameObject newCard = Instantiate(card);
            newCard.transform.parent = GameObject.Find(&amp;quot;cards&amp;quot;).transform;

            float x = (i / 4) * 1.4f - 2.1f;
            float y = (i % 4) * 1.4f - 3.0f;
            newCard.transform.position = new Vector3(x, y, 0);

            Debug.Log(rtans[i]);
        }
    }
    ```

3. `Play` 해서 확인해보기

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7c6a4f13-1954-4a0f-8d85-7026bee3c74c/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;3) 르탄이 붙여주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;이미지를 꺼내오려면? → Resources 폴더에 옮겨두기&lt;/p&gt;
 &lt;aside&gt;
   어떤 이미지나 파일을 코드로 지정하고 싶을 때는 &quot;Resources&quot;폴더에 옮겨두기

 &lt;/aside&gt;

&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/942e94b1-b337-4999-9e65-921e0c876843/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;르탄이 붙이기&lt;/p&gt;
&lt;p&gt; &lt;code&gt;string rtanName = &amp;quot;rtan&amp;quot; + rtans[i].ToString();&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → 르탄이 이름(이미지 이름)을 만들어두기&lt;/p&gt;
&lt;p&gt; &lt;code&gt;newCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite =&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → 새 카드 아래에 &lt;code&gt;front&lt;/code&gt; 를 찾아서, sprite를 변경&lt;/p&gt;
&lt;p&gt; &lt;code&gt;Resources.Load&amp;lt;Sprite&amp;gt;(rtanName);&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;Resources&lt;/code&gt; 폴더에 있는 rtanName 이미지를 가져오자&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     int[] rtans = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };

     rtans = rtans.OrderBy(item =&amp;gt; Random.Range(-1.0f, 1.0f)).ToArray();

     for (int i = 0; i &amp;lt; 16; i++)
     {
         GameObject newCard = Instantiate(card);
         newCard.transform.parent = GameObject.Find(&amp;quot;cards&amp;quot;).transform;

         float x = (i / 4) * 1.4f - 2.1f;
         float y = (i % 4) * 1.4f - 3.0f;
         newCard.transform.position = new Vector3(x, y, 0);

         string rtanName = &amp;quot;rtan&amp;quot; + rtans[i].ToString();
         newCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite = Resources.Load&amp;lt;Sprite&amp;gt;(rtanName);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;확인해보기&lt;/p&gt;
&lt;p&gt; → 프리팹에서 front를 켜고, back을 끈 다음 확인합니다.&lt;/p&gt;
&lt;p&gt; → 잘 나오네요!  &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0b2d4a10-7d8e-4168-a525-e5d859b18cf1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[카드 애니메이션]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 기본 애니메이션 만들기 &lt;code&gt;card_idle&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Animations 폴더 생성 후 card_idle 애니매이션 만들기&lt;/p&gt;
&lt;p&gt; → 프리팹 card를 열어서 붙여놓기, Loop Time 체크하는 것 잊지 말기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d1a465f1-c79a-4d78-a6a9-7fd7fc231e9f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;카드를 꺼내놓기&lt;/p&gt;
&lt;p&gt; → 안보이면 만들기 어려우니까!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b65aa5a6-8ec8-4c92-9c39-03c6b6e39c30/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;애니메이션 레코딩하기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;0:20&lt;/code&gt; 에만 rotation &lt;code&gt;z:3&lt;/code&gt; 을 만들어놓기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ebc1fd43-689c-46fe-928f-a70081dfb779/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 뒤집기 애니메이션 만들기 &lt;code&gt;card_flip&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Animations 폴더 생성 후  card_flip 애니메이션 만들기&lt;/p&gt;
&lt;p&gt; → 프리팹을 열어 card에 붙여놓기&lt;/p&gt;
&lt;p&gt; → 뒤집기는 &lt;code&gt;한번&lt;/code&gt; 이므로 loop time 체크할 필요 없음!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/47b4a4de-c3c2-4b49-b4ee-948950e551fc/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;애니메이션 만들기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;0:10&lt;/code&gt; 부분만 Scale 을 &lt;code&gt;x:1.2, y:1.2&lt;/code&gt; 로 만들어두기&lt;/p&gt;
&lt;p&gt; → 살짝 눌린 것처럼!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bff4e50e-c2c7-41b0-a932-149e34f166db/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 애니메이션 조건 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Animator 를 열고, transition 만들어 줍니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;카드의 오른쪽 클릭하고 make transition 을 클릭합니다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;오는 것과 가는 것 두 개의 transition을 생성합니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;각 transition에 대해서, &lt;strong&gt;has exit time&lt;/strong&gt; 에 체크 해제하고, &lt;strong&gt;transition duration&lt;/strong&gt;을 0으로 변경합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/74aa0955-3f68-420c-8d65-dc7e68f908c8/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;파라미터 만들기&lt;/p&gt;
&lt;p&gt; → bool 형식(true / false)의 &lt;code&gt;isOpen&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c01e4650-d196-4a61-916e-466729defd54/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;transition에 파라미터 조건 붙이기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;idle ⇒ flip&lt;/code&gt; : bool이 true 가 되면 발동&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;flip ⇒ idle&lt;/code&gt; : bool이 false 가 되면 발동&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ca76d133-aaec-4109-b7bc-01acf88d6428/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;06. 카드(3)_뒤집기, 매칭하기&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;[뒤집기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 카드 클릭하면 뒤집어지기 (=쉬어가기  )&lt;/p&gt;
  &lt;aside&gt;
    `프리팹` 을 열어서 작업해야 한답니다!

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;card c# 스크립트에서 button 속성 붙이기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;card.cs&lt;/code&gt; 만들어서, 클릭하면 front가 보이게 해줍니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public void openCard()
{
    transform.Find(&amp;quot;front&amp;quot;).gameObject.SetActive(true);
    transform.Find(&amp;quot;back&amp;quot;).gameObject.SetActive(false);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    b.온클릭 함수에 card를 붙여 넣은 후, card에, openCard button 함수를 붙여 줍니다.

    ![opencard.jpg](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/22262d1e-0413-4224-898c-55db44be50fc/opencard.jpg)


1. 카드에 animtion 적용하기

    ```csharp
    public Animator anim;

    public void openCard()
    {
        anim.SetBool(&amp;quot;isOpen&amp;quot;, true);
        transform.Find(&amp;quot;front&amp;quot;).gameObject.SetActive(true);
        transform.Find(&amp;quot;back&amp;quot;).gameObject.SetActive(false);
    }
    ```

2. `Play` 해서 잘 뒤집어지는지 실행해보기

    → 잘 되네요!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e93f57c8-9ad7-4f2d-af7d-cbf43ef78a90/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;[카드 매칭하기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 카드 매칭하기 - 전략&lt;/p&gt;
  &lt;aside&gt;
    첫 번째 카드의 이름을 갖고 있다가 → 두 번째 카드와 일치하는 지 보기

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;일치하면? ⇒ (1초 후에) 카드를 둘 다 없애주기&lt;/li&gt;
&lt;li&gt;안 일치하면? ⇒ (1초 후에) 두 카드를 다시 뒤집어주기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 카드 매칭하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;우선, gameManager 싱글톤화&lt;/p&gt;
&lt;p&gt; → 이제 다른 곳에서 막 부를 것이니까!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public static gameManager I;

 void Awake()
 {
     I = this;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gameManager에서 카드 이름을 저장해둘 수 있게 하기 + 매칭 로직 함수 만들어두기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject firstCard;
 public GameObject secondCard;

 public void isMatched()
 {
     Debug.Log(&amp;quot;판단하자&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;card.cs&lt;/code&gt; 에서 openCard하면 firstCard 또는 secondCard에 나를 넣기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void openCard()
 {
     anim.SetBool(&amp;quot;isOpen&amp;quot;, true);
     transform.Find(&amp;quot;front&amp;quot;).gameObject.SetActive(true);
     transform.Find(&amp;quot;back&amp;quot;).gameObject.SetActive(false);

     if (gameManager.I.firstCard == null)
     {
         gameManager.I.firstCard = gameObject;
     }
     else
     {
         gameManager.I.secondCard = gameObject;
         gameManager.I.isMatched();
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에서 &lt;code&gt;isMatched()&lt;/code&gt; 만들기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;front&lt;/code&gt; 를 찾아서 카드 이미지 이름을 받아오기&lt;/p&gt;
&lt;p&gt; → 다 끝났으면 다시 비워주기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void isMatched()
 {
     string firstCardImage = firstCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;
     string secondCardImage = secondCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;

     if (firstCardImage == secondCardImage)
     {
         Debug.Log(&amp;quot;같다!&amp;quot;);
     }
     else
     {
         Debug.Log(&amp;quot;같지 않다!&amp;quot;);
     }

     firstCard = null;
     secondCard = null;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;card 행동 준비하기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;card.cs&lt;/code&gt; 안에 없애기, 뒤집어주기 함수 만들어두기&lt;/p&gt;
&lt;p&gt; → 같으면 → 1초 후에 둘 다 없애기 / 다르면 → 1초 후에 둘 다 뒤집어주기&lt;/p&gt;
&lt;p&gt; → 1초 후에 실행해야하니까, invoke를 따로 만들어줘야 하겠죠?&lt;/p&gt;
 &lt;aside&gt;
   gameManager에서는 `destoryCard` 를 부를 것이니까,
 부르고나면 1초 뒤에 `destroyCardInvoke` 가 실행되도록!

 &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void destroyCard()
 {
     Invoke(&amp;quot;destroyCardInvoke&amp;quot;, 1.0f);
 }

 void destroyCardInvoke()
 {
     Destroy(gameObject);
 }

 public void closeCard()
 {
     Invoke(&amp;quot;closeCardInvoke&amp;quot;, 1.0f);
 }

 void closeCardInvoke()
 {
     anim.SetBool(&amp;quot;isOpen&amp;quot;, false);
     transform.Find(&amp;quot;back&amp;quot;).gameObject.SetActive(true);
     transform.Find(&amp;quot;front&amp;quot;).gameObject.SetActive(false);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에서 같을 때 / 같지 않을 때 적절한 함수 불러주기&lt;/p&gt;
&lt;p&gt; &lt;code&gt;firstCard.GetComponent&amp;lt;card&amp;gt;().destroyCard()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → firstCard에 붙어있는 card.cs 에서 destoryCard 를 불러라!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void checkMatched()
 {
     string firstCardImage = firstCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;
     string secondCardImage = secondCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;

     if (firstCardImage == secondCardImage)
     {
         firstCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();
     }
     else
     {
         firstCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
     }

     firstCard = null;
     secondCard = null;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;확인해보기 ⇒ 잘 되네요!  &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a92c9fa0-0fd5-4369-82a3-e097397088ce/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;07. 게임 끝내기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 카드가 모두 없어지면 게임 끝내기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;끝! 텍스트를 미리 만들어두기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;fontsize 20, timeTxt 를 [윈도우] &lt;code&gt;ctrl+d&lt;/code&gt; [맥] command  + D 해서 만들면 편하겠죠! &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;width: 300, height: 300, posY: 0&lt;/code&gt; 으로 설정 해주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;color : (220, 255, 0, 255)&lt;/code&gt;도 설정해줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;안보이게 세팅 하여 감춰두세요!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3eea777f-6ffb-4a09-abd9-dfcd9912dc20/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에서, 매칭 됐을 때 남은 카드를 체크하기&lt;/p&gt;
&lt;p&gt; → cards를 찾아서, 아래에 몇 개의 자식이 있는지를 판단하면 끝!&lt;/p&gt;
&lt;p&gt; → 실행해보기!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void checkMatched()
 {
     string firstCardImage = firstCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;
     string secondCardImage = secondCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;

     if (firstCardImage == secondCardImage)
     {
         firstCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();

         int cardsLeft = GameObject.Find(&amp;quot;cards&amp;quot;).transform.childCount;
         Debug.Log(cardsLeft);
     }
     else
     {
         firstCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
     }

     firstCard = null;
     secondCard = null;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;실행해보면, 지금 맞춘 두 장을 포함해서 나오는 것을 알 수 있어요&lt;/p&gt;
&lt;p&gt; → 즉, &lt;code&gt;2&lt;/code&gt; 가 나오면 마지막이라는 뜻!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/373cd6cc-80dc-4893-b8d5-ae1537107aac/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;게임 끝내기!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject endTxt;

 public void isMatched()
 {
     string firstCardImage = firstCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;
     string secondCardImage = secondCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;

     if (firstCardImage == secondCardImage)
     {
         firstCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();

         int cardsLeft = GameObject.Find(&amp;quot;cards&amp;quot;).transform.childCount;
         if (cardsLeft == 2)
         {
             endTxt.SetActive(true);
             Time.timeScale = 0.0f;
         }
     }
     else
     {
         firstCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
         secondCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
     }

     firstCard = null;
     secondCard = null;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;끝&lt;/code&gt; 버튼에 다시시작하기 버튼 붙이기!&lt;/p&gt;
&lt;p&gt; → (1) 버튼 컴포넌트 추가하기&lt;/p&gt;
&lt;p&gt; → (2) &lt;code&gt;endTxt.cs&lt;/code&gt; 만들고, endTxt에 붙이기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using UnityEngine.SceneManagement;

 public void retryGame()
 {
     SceneManager.LoadScene(&amp;quot;MainScene&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; → (3) &lt;code&gt;gameManager.cs&lt;/code&gt; start에서 TimeScale을 다시 되돌려놓는 것도 잊지말기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; Time.timeScale = 1.0f;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;확인하기 ⇒ 잘 되네요!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4ca1317e-e9ae-49b9-bd0b-f2f1b3d13624/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 미세 조정하기&lt;/p&gt;
  &lt;aside&gt;
    게임이 너무 쉽지 않나요?  

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;이렇게, 다 만들고 변수들을 조정해서 마지막으로 게임을 손 본답니다!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;card.cs&lt;/code&gt; 에서 없어지거나 / 다시 뒤집히는 시간을 &lt;code&gt;0.5f&lt;/code&gt; 로 바꿔둘게요!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;public void destroyCard()
{
  Invoke(&amp;quot;destroyCardInvoke&amp;quot;, 0.5f);
}

void destroyCardInvoke()
{
  Destroy(gameObject);
}

public void closeCard()
{
  Invoke(&amp;quot;closeCardInvoke&amp;quot;, 0.5f);
}

void closeCardInvoke()
{
  anim.SetBool(&amp;quot;isOpen&amp;quot;, false);
  transform.Find(&amp;quot;back&amp;quot;).gameObject.SetActive(true);
  transform.Find(&amp;quot;front&amp;quot;).gameObject.SetActive(false);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;08. 숙제 - 30초가 지나면 게임 끝내기&lt;/h2&gt;
&lt;aside&gt;
  그래요! 게임이 심장 쫄깃한(?) 맛이 있어야죠..!  

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;이렇게 되면 완성!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aad0e2b4-1575-4dea-9ed6-d0bf154baee6/%EB%A5%B4%ED%83%84%EC%9D%B4_%EB%A7%9E%EC%B6%94%EA%B8%B0_%EC%88%99%EC%A0%9C%EC%99%84%EC%84%B1.mp4&quot;&gt;르탄이 맞추기_숙제완성.mp4&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;힌트요정 -  &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  [수정해야할 부분]&lt;/p&gt;
&lt;p&gt;  &lt;code&gt;gameManager.cs&lt;/code&gt; 의 &lt;code&gt;Update()&lt;/code&gt; 부분만 수정하면 된답니다!&lt;/p&gt;
&lt;p&gt;  → 어떤 조건이 되면 아래 코드가 실행되면 되겠죠!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  endTxt.SetActive(true);
  Time.timeScale = 0.0f;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;HW. 4주차 숙제 해설&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 부분&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;
  using UnityEngine.UI;

  using System.Linq;

  public class gameManager : MonoBehaviour
  {
      public Text timeTxt;
      float time = 0.0f;

      public GameObject endTxt;

      public GameObject card;

      public static gameManager I;

      public GameObject firstCard;
      public GameObject secondCard;

      void Awake()
      {
          I = this;
      }

      // Start is called before the first frame update
      void Start()
      {
          Time.timeScale = 1.0f;

          int[] rtans = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };

          rtans = rtans.OrderBy(item =&amp;gt; Random.Range(-1.0f, 1.0f)).ToArray();

          for (int i = 0; i &amp;lt; 16; i++)
          {
              GameObject newCard = Instantiate(card);
              newCard.transform.parent = GameObject.Find(&amp;quot;cards&amp;quot;).transform;

              float x = (i / 4) * 1.4f - 2.1f;
              float y = (i % 4) * 1.4f - 3.0f;
              newCard.transform.position = new Vector3(x, y, 0);

              string rtanName = &amp;quot;rtan&amp;quot; + rtans[i].ToString();
              newCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite = Resources.Load&amp;lt;Sprite&amp;gt;(rtanName);
          }
      }

      // Update is called once per frame
      void Update()
      {
          time += Time.deltaTime;
          timeTxt.text = time.ToString(&amp;quot;N2&amp;quot;);

          if (time &amp;gt; 30.0f)
          {
              endTxt.SetActive(true);
              Time.timeScale = 0.0f;
          }
      }

      public void isMatched()
      {
          string firstCardImage = firstCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;
          string secondCardImage = secondCard.transform.Find(&amp;quot;front&amp;quot;).GetComponent&amp;lt;SpriteRenderer&amp;gt;().sprite.name;

          if (firstCardImage == secondCardImage)
          {
              firstCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();
              secondCard.GetComponent&amp;lt;card&amp;gt;().destroyCard();

              int cardsLeft = GameObject.Find(&amp;quot;cards&amp;quot;).transform.childCount;
              if (cardsLeft == 2)
              {
                  endTxt.SetActive(true);
                  Time.timeScale = 0.0f;
              }
          }
          else
          {
              firstCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
              secondCard.GetComponent&amp;lt;card&amp;gt;().closeCard();
          }

          firstCard = null;
          secondCard = null;
      }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/3-1074ebc561e34cf4b10370c5b43e9c6c&quot;&gt;[스파르타코딩클럽] 게임개발 종합반 - 3주차&lt;/a&gt; &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Copyright ⓒ TeamSparta All rights reserved.&lt;/p&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/966</guid>
      <comments>https://comphy.tistory.com/966#entry966comment</comments>
      <pubDate>Sun, 8 Jan 2023 15:50:27 +0900</pubDate>
    </item>
    <item>
      <title># [스파르타코딩클럽] 게임개발 종합반 - 3주차</title>
      <link>https://comphy.tistory.com/965</link>
      <description>&lt;h1&gt;[스파르타코딩클럽] 게임개발 종합반 - 3주차&lt;/h1&gt;
&lt;aside&gt;
&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; width=&quot;40px&quot; /&gt; **매 주차 강의자료 시작에 PDF파일을 올려두었어요!**

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/455717b2-f3ab-44f7-9742-bc99008495ae/%E1%84%89%E1%85%B3%E1%84%91%E1%85%A1%E1%84%85%E1%85%B3%E1%84%90%E1%85%A1%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B5%E1%86%BC%E1%84%8F%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A5%E1%86%B8_%E1%84%80%E1%85%A6%E1%84%8B%E1%85%B5%E1%86%B7%E1%84%80%E1%85%A2%E1%84%87%E1%85%A1%E1%86%AF_%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%92%E1%85%A1%E1%86%B8%E1%84%87%E1%85%A1%E1%86%AB_3%E1%84%8C%E1%85%AE%E1%84%8E%E1%85%A1_211108.pdf&quot;&gt;스파르타코딩클럽_게임개발_종합반_3주차_211108.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;그럴싸한 게임을 완성해보기&lt;/li&gt;
&lt;li&gt;hp바 만들기&lt;/li&gt;
&lt;li&gt;레벨 시스템을 구상해보기&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;01. 3주차 오늘 배울 것&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 3주차 수업의 목표와 범위&lt;/p&gt;
  &lt;aside&gt;
    오늘의 키워드! `**이제 조금 익숙해졌는걸**`

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;스파르타가 만들어둔 이미지를 가지고 게임을 만들어봅니다.&lt;/p&gt;
&lt;p&gt;  → 이미지들을 진짜 &lt;code&gt;디자이너&lt;/code&gt; 에게 받았다고 생각하고 만들어보세요!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3주차 내용도 1, 2주차의 것과 80% 같고, 20%만 새롭답니다.&lt;/p&gt;
&lt;p&gt;  → 한번 더 복습해볼까요? 이번주까지 하고나면, 꽤 익숙해질거예요!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 오늘 만들 것 : &lt;code&gt;고양이 밥주기 게임&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;배고픈 고양이들에게 밥을 주고, 생선가게를 지켜보아요!&lt;/p&gt;
&lt;p&gt;  → 새롭게 배우는 것: 여러 Scene 만들기!, 슬라이더 바, 레벨시스템&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/35b1291f-26e4-4791-8c9b-c8e108163bcd/%EA%B3%A0%EC%96%91%EC%9D%B4_%EB%B0%A5%EC%A3%BC%EA%B8%B0_%EA%B2%8C%EC%9E%84.mp4&quot;&gt;고양이 밥주기 게임.mp4&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 만들 순서&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;기본 씬 구성하기 (UI, 강아지, 고양이)&lt;/li&gt;
&lt;li&gt;강아지 움직임 더하기 + 밥 쏘기&lt;/li&gt;
&lt;li&gt;고양이 내려오게 하기&lt;/li&gt;
&lt;li&gt;고양이 밥 먹기 + 옆으로 가게 하기&lt;/li&gt;
&lt;li&gt;새로운 고양이 나오게 하기&lt;/li&gt;
&lt;li&gt;레벨업하기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[기본 씬 구성하기]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  `dogvscat` 이라는 프로젝트를 만들고 시작할게요!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 기본 세팅하기 &amp;amp; 배경 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;windows → 2x3 layout 클릭! free aspect → phone 클릭!&lt;/p&gt;
&lt;p&gt; MainScene으로 변경하는 것도 잊지마세요!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6b5d7b1d-3ade-4c79-9a2b-dc126cf9b20d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;메인카메라의 size를 5 → 25로 바꿀게요!&lt;/p&gt;
&lt;p&gt; → 약간 더 멀리서 보겠다는 이야기! 이미지들이 이 사이즈에 맞춰 작업되어 있습니다.  &lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/72f60ee8-cecc-42e9-a6fc-a99ea4356cb1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;카메라 색은 &lt;code&gt;hex ⇒ FFF0B2&lt;/code&gt; 으로 할게요! 그러면, 배경과 같은 효과!&lt;/p&gt;
&lt;p&gt; → 이렇게도 할 수 있다는 사실! 알고갈게요!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4fd52626-520e-4e97-b600-67b6d8bbc3d5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 이미지 받아두기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Assets 폴더 아래에 Images(대, 소문자 구분) 폴더 생성합니다.&lt;/li&gt;
&lt;li&gt;Images 폴더 아래에 파일 전체를 저장 해줍니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] dogvscat - 이미지 저장&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week03/dogvscat.zip&quot;&gt;https://s3.ap-northeast-2.amazonaws.com/materials.spartacodingclub.kr/game_new/week03/dogvscat.zip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c43da0d1-5524-4399-8c39-d249d7e1f1f7/dogvscat.zip&quot;&gt;dogvscat.zip&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 오브젝트 배치하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;생선가게 : &lt;code&gt;y: -22&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;강아지 : &lt;code&gt;y: -16.1&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;위치를 지정하신 후, Inspector의 Sprite에 이미지를 드래그해주세요!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/945eba53-f354-4cf1-b4a4-a0f7ade01e69/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;02. 시작 씬 만들기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 시작씬 구성하기&lt;/p&gt;
  &lt;aside&gt;
    시작 씬은 처음이죠? 가볍게 한번 만들고 갑시다!

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Scenes 폴더 안에 create → Scene 이름은 &lt;code&gt;StartScene&lt;/code&gt; 그리고 더블클릭!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;마찬가지로 Camera &lt;code&gt;size ⇒ 25&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sprite 이미지 만들어서 ⇒ &lt;code&gt;intro&lt;/code&gt; 이미지 끌어다넣기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/67c2a969-057f-40d6-bae7-bf971cde54c9/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UI → Image를 만들어서 &lt;code&gt;startBtn&lt;/code&gt; 해두기&lt;/p&gt;
&lt;p&gt; → startBtn 이미지 끌어다넣기&lt;/p&gt;
&lt;p&gt; → posY: &lt;code&gt;-300&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;width: 300, height: 100&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f71cc0d5-9ca4-4332-9907-f56acdfbdd92/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 씬 넘어가기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;startBtn 에 버튼 컴포넌트 달기&lt;/p&gt;
&lt;p&gt; → 참고) 버튼 컴포넌트는 &amp;quot;sprite&amp;quot;가 아니라, 반드시 &amp;quot;UI Image&amp;quot;에 붙여야 작동한다는 사실!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;스크립트 만들기&lt;/p&gt;
&lt;p&gt; → Scripts 폴더 만들고, &lt;code&gt;startBtn.cs&lt;/code&gt; 만들기 → startBtn에 붙여두기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using UnityEngine.SceneManagement;

 public void GameStart()
 {
     SceneManager.LoadScene(&amp;quot;MainScene&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;onclick에 붙이기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fe289595-eff0-4eaf-8c52-858c4075d02e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) &lt;code&gt;Play&lt;/code&gt; 해서 확인해보기!&lt;/p&gt;
&lt;p&gt;  → 잘 되네요! 이제 MainScene으로 이동해서 작업할게요! (더블클릭!)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;03. 밥 쏘기&lt;/h2&gt;
&lt;aside&gt;
  무언가를 복사해서 → 움직이게 하는 일종의 총복습! 찬찬히 따라해보아요

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 밥 만들어두기&lt;/p&gt;
&lt;p&gt;  → sprite → circle 클릭, 이름 : &lt;code&gt;food&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;  → scale: &lt;code&gt;x: 6, y: 6&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;  → Sprite : Knob 으로 설정하고, 색은 &lt;code&gt;컬러추출기&lt;/code&gt; 를 이용해서 강아지 색으로 설정하기&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/859938b6-b672-4be4-b8f9-d5ba3cc86953/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 밥 복사해서 쏘기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;food.cs&lt;/code&gt; 만들어서 붙여두기. 생성하면 무조건 위로 직진!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Update()
 {
     transform.position += new Vector3(0.0f, 0.5f, 0.0f);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프리팹으로 만들어두기&lt;/p&gt;
&lt;p&gt; → Prefabs 폴더 만들고 끌어다놓기! 과감하기 원래 있던 food는 삭제!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aececb4d-bcf1-44a4-a627-4dbc5746b10e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GameManager 만들기&lt;/p&gt;
&lt;p&gt; → 0.2초 마다 밥을 쏘기&lt;/p&gt;
&lt;p&gt; → 강아지 위치에서 y 좌표만 2.0f 높아진 곳에서 쏘기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;Quaternion.identity&lt;/code&gt; 는 회전 없다는 뜻! (no rotation)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject food;
 public GameObject dog;

 void Start()
 {
     InvokeRepeating(&amp;quot;makeFood&amp;quot;, 0.0f, 0.2f);
 }

 void makeFood()
 {
     float x = dog.transform.position.x;
     float y = dog.transform.position.y + 2.0f;
     Instantiate(food, new Vector3(x,y,0), Quaternion.identity);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;앗, 그런데 food가 안 없어진다!&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;food.cs&lt;/code&gt; 에서 y 좌표가 26.0f 가 넘으면 없어지게 하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Update()
 {
     transform.position += new Vector3(0.0f, 0.5f, 0.0f);
     if (transform.position.y &amp;gt; 26.0f)
     {
         Destroy(gameObject);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 강아지 움직이기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;강아지 마우스 따라 움직입니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;dog.cs 만들어주세요.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;마우스 좌표 중 X 좌표만 가져옵니다. (2주차에서 썼던 코드 입니다.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;y 좌표는 transform.position.y 내 좌표를 그대로 씁니다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;여기서 잠깐! 외워서 쓰는 게 아니라, 검색해보고 쓴다!라고 생각하세요. &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;void Update()
{
 Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
 transform.position = new Vector3(mousePos.x, transform.position.y, 0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fishShop을 벗어나지 않게 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;x 좌표 -8.5f ~ 8.5f 로 고정합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;void Update()
{
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
float x = mousePos.x;
if (x &amp;gt; 8.5f)
{
    x = 8.5f;
}
if (x &amp;lt; -8.5f)
{
    x = -8.5f;
}
transform.position = new Vector3(x, transform.position.y, 0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;04. 고양이 나타내기(1) - 만들기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) normalCat 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;create empty → &lt;code&gt;normalCat&lt;/code&gt; 만들기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;normalCat 안에 full / hungry로 sprite를 만들어 붙이기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;hungry 안에 canvas → image를 만들고 back을 만들기!&lt;/p&gt;
&lt;p&gt; → Canvas를 만들 때, RenderMode를 World Space로 변경해주기!&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;width: 4, height: 0.5&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → canvas의 position을 &lt;code&gt;y:-4&lt;/code&gt; 로 고정하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7c864fef-c345-4c0e-ab90-981ef85670cf/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;front는 back을 (ctrl+d)해서 복사한 뒤 작업시작!&lt;/p&gt;
&lt;p&gt; → 색 : 추출기로 fish-shop 색 추출&lt;/p&gt;
&lt;p&gt; → pivot의 x 값을 0 으로 만들고, scale의 x 값을 0.4로 조정해보기!&lt;/p&gt;
&lt;p&gt; → 이제 x scale만 조정해주면 게이지를 만들 수 있음. 이것이 바로 hp 바 만들기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4306c5de-eeb2-42dd-b0fd-40261e8f7aa0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) normalCat에 애니메이션 붙이기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Animations 폴더 만들고 &lt;code&gt;normalCat&lt;/code&gt; 만들기&lt;/p&gt;
&lt;p&gt; → Loop time 체크 잊지 말기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f77f0dc7-b6ed-42ae-bb88-52dcf4951e08/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;normalCat에 붙여넣고, 더블클릭해서 녹화시작하기&lt;/p&gt;
&lt;p&gt; → 0:10에 &lt;code&gt;normalCat_2&lt;/code&gt;, 0:20에 다시 &lt;code&gt;normalCat_1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a12874b6-0633-4f59-8ddc-5cbf264982d2/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) fatCat 만들기&lt;/p&gt;
&lt;p&gt;  → ctrl+d 를 이용해서 만들거예요! 두 번째는 처음부터 만들 필요가 없겠죠  &lt;/p&gt;
&lt;p&gt;  → (1) hungry, full 의 이미지를 교체해주기&lt;/p&gt;
&lt;p&gt;  → (2) Canvas의 x 좌표를 &lt;code&gt;-0.5&lt;/code&gt; 해두기&lt;/p&gt;
&lt;p&gt;  → (3) animation 을 새로 만들어서 넣어두기 (기존에 있던 animator를 먼저 삭제하기)&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ff638f5a-8e97-4381-9e59-46dc775dfbf1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) 두 개를 모두 프리팹 화 해두기&lt;/p&gt;
&lt;p&gt;  → fatCat은 game view에서는 삭제해두기&lt;/p&gt;
&lt;p&gt;  → 참고) 프리팹을 고칠 때는 프리팹을 더블클릭해서 작업합니다!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/53048dec-2a50-476a-a7f5-e16d2f1ea9db/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;05. 고양이 나타내기(2) - 내려오기/충돌/등장하기 / 종료&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 고양이 내려오게 하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cat.cs&lt;/code&gt; 만들기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;임의의 위치에서 내려오게 하기&lt;/p&gt;
&lt;p&gt; → y 좌표는 30.0f 화면 밖에서 내려오게 하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     float x = Random.Range(-8.5f, 8.5f);
     float y = 30.0f;
     transform.position = new Vector3(x, y, 0);
 }

 // Update is called once per frame
 void Update()
 {
     transform.position += new Vector3(0.0f, -0.05f, 0.0f);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[추가 팁!] 고양이가 너무 빨라요!&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;왜 이런 현상이 발생 하나요?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1강과 동일하게 Update 함수의 기본 기능에 의해 발생하는 문제입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;어떻게 해결 하나요?&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/41eb15ec-d3fa-4c11-b237-013da0ce93da/rrr.jpg&quot; alt=&quot;rrr.jpg&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VSync 옵션을 켠다면 →  Update의 프레임은 사용자 모니터의 주파수에 맞게 조절되어 속도가 느려지게 됩니다.&lt;/li&gt;
&lt;li&gt;단, VSync 옵션은 유니티 에디터 내부에서만 적용되는 옵션입니다.&lt;/li&gt;
&lt;li&gt;그리하여, 추후 실제 게임으로 만들어 플레이시에는 적용이 되지 않을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 고양이와 밥 충돌하게 하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;밥에 tag 주기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/98758d58-3ceb-4b51-b449-0b3271ac070d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;밥에 &lt;code&gt;rigidbody 2D&lt;/code&gt;, &lt;code&gt;circle collider 2D&lt;/code&gt; 달아주기&lt;/p&gt;
&lt;p&gt; → 참고) 충돌 = 한쪽에 rigidbody + 양쪽에 collider&lt;/p&gt;
&lt;p&gt; → 단! Body Type을 &lt;code&gt;Kinematic&lt;/code&gt; 으로 잡아주기 = 중력의 영향을 안 받겠다는 뜻!&lt;/p&gt;
&lt;p&gt; → 그리고 &lt;code&gt;isTrigger&lt;/code&gt; 에 체크! 중력의 영향을 안 받을 때에는 이것을 체크해주세요! &lt;/p&gt;
&lt;p&gt; (옵션을 체크하지 않으면 충돌감지가 안됩니다!)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c99d1349-7e3d-4692-8fa3-a46312572eb4/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/c8b26dda-c83b-4a1e-922a-5a97db6c3446/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;고양이에 box collider 2D 달아주기 (나중에 fatCat에도 달아야겠죠?)&lt;/p&gt;
&lt;p&gt; → collider size를 조정해줘야 해요! &lt;code&gt;x:4, y:7&lt;/code&gt; 로 맞춰볼까요?&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e8667cd3-b38b-48a5-b3d1-db8a60bf85e6/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cat.cs&lt;/code&gt; 에 충돌 구현하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void OnTriggerEnter2D(Collider2D coll)
 {
     if (coll.gameObject.tag == &amp;quot;food&amp;quot;)
     {
         Debug.Log(&amp;quot;맞았다!&amp;quot;);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f27df197-0e3c-459e-b753-cddf43acee56/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 충돌하면 에너지를 채워주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;기본 에너지 세팅&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; float full = 5.0f;
 float energy = 0.0f;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;밥 충돌하면 에너지 올라가게 하기 + 밥은 없애기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void OnTriggerEnter2D(Collider2D coll)
 {
     if (coll.gameObject.tag == &amp;quot;food&amp;quot;)
     {
         energy += 1.0f;
         Destroy(coll.gameObject);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;energy 와 full이 같아지면 배불렀다!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void OnTriggerEnter2D(Collider2D coll)
 {
     if (coll.gameObject.tag == &amp;quot;food&amp;quot;)
     {
         if (energy &amp;lt; full)
                 {
                         energy += 1.0f;
                 Destroy(coll.gameObject);
                 }
         else
         {
             Debug.Log(&amp;quot;배가 다 찼어요&amp;quot;);
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7f162d70-b5f4-44be-a337-c74730a9e257/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;게이지 채우기&lt;/p&gt;
&lt;p&gt; → 시작 게이지를 0으로 해둘게요! (front의 x scale을 0으로)&lt;/p&gt;
&lt;p&gt; → hungry 밑에, Canvas 밑에, front를 찾아서, x scale을 enery/full 값으로 조절&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void OnTriggerEnter2D(Collider2D coll)
 {
     if (coll.gameObject.tag == &amp;quot;food&amp;quot;)
     {
         if (energy &amp;lt; full)
                 {
                         energy += 1.0f;
                 Destroy(coll.gameObject);
                         gameObject.transform.Find(&amp;quot;hungry/Canvas/front&amp;quot;).transform.localScale = new Vector3(energy / full, 1.0f, 1.0f);
                 }
         else
         {
             Debug.Log(&amp;quot;배가 다 찼어요&amp;quot;);
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;게이지가 모두 찼으면, hungry는 안보이고, full이 보이게 하기!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void OnTriggerEnter2D(Collider2D coll)
 {
     if (coll.gameObject.tag == &amp;quot;food&amp;quot;)
     {
         if (energy &amp;lt; full)
                 {
                         energy += 1.0f;
                 Destroy(coll.gameObject);
                         gameObject.transform.Find(&amp;quot;hungry/Canvas/front&amp;quot;).transform.localScale = new Vector3(energy / full, 1.0f, 1.0f);
                 }
         else
         {
                         gameObject.transform.Find(&amp;quot;hungry&amp;quot;).gameObject.SetActive(false);
             gameObject.transform.Find(&amp;quot;full&amp;quot;).gameObject.SetActive(true);
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e4b6ba9-7de3-4177-a148-a7f57564aa4c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;에너지 꽉 차면 옆으로 비키게 하기&lt;/p&gt;
&lt;p&gt; → 단, 화면 오른쪽에 있었으면(x&amp;gt;0) 오른쪽으로, 왼쪽이면 왼쪽으로 비키게 하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Update()
 {
     if (energy &amp;lt; full)
     {
         transform.position += new Vector3(0.0f, -0.05f, 0.0f);
     }
     else
     {
         if (transform.position.x &amp;gt; 0)
         {
             transform.position += new Vector3(0.05f, 0.0f, 0.0f);
         }
         else
         {
             transform.position += new Vector3(-0.05f, 0.0f, 0.0f);
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[참고] 고양이 이미지 변경 시점이 다른 이유는?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  게임 설계상 &lt;code&gt;OnColliderEnter2D&lt;/code&gt;와 &lt;code&gt;Update&lt;/code&gt;에서 ‘따로’ energy를 체크 하기 때문에 고양이의 이미지가 변경되는 시점과 옆으로 비키는 시점이 조금 다릅니다. &lt;/p&gt;
&lt;p&gt;  어떻게 다를지 로직을 체크해봅니다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3초 뒤에 없애기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Update()
 {
     if (energy &amp;lt; full)
     {
         transform.position += new Vector3(0.0f, -0.05f, 0.0f);
     }
     else
     {
         if (transform.position.x &amp;gt; 0)
         {
             transform.position += new Vector3(0.05f, 0.0f, 0.0f);
         }
         else
         {
             transform.position += new Vector3(-0.05f, 0.0f, 0.0f);
         }
         Destroy(gameObject, 3.0f);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) gameManager에서 고양이 부르기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;등장시키기&lt;/p&gt;
&lt;p&gt; → 1.0초에 한 마리씩 등장. 이제 &lt;code&gt;normalCat&lt;/code&gt; 을 Hierachy에서 없애도 됩니다!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject normalCat;

 void Start()
 {
     InvokeRepeating(&amp;quot;makeFood&amp;quot;, 0.0f, 0.2f);
     InvokeRepeating(&amp;quot;makeCat&amp;quot;, 0.0f, 1.0f);
 }

 void makeCat()
 {
     Instantiate(normalCat);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;order-in-layer 조정하기&lt;/p&gt;
&lt;p&gt; → 실행시켜보면 고양이의 에너지바가 fish-shop보다 위에 온답니다.&lt;/p&gt;
&lt;p&gt; → dog와 fish-shop의 order-in-layer를 &lt;code&gt;1&lt;/code&gt; 로 조정하기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6ceb11f8-185a-47fe-8696-57c8a7adc7bd/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;5) 특정 y 좌표 밑으로 내려오면 게임 종료&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;retryBtn 만들고, 숨겨두기&lt;/p&gt;
&lt;p&gt; → Image로 만들기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;width: 300, height: 100&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → button 컴포넌트 달기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;retryBtn.cs&lt;/code&gt; 만들어서 넣고, onclick에 연결하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using UnityEngine.SceneManagement;

 public void ReGame()
 {
     SceneManager.LoadScene(&amp;quot;MainScene&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/825d0143-5b8a-4050-bf74-13abb241b514/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gameManager 싱글톤 화&lt;/p&gt;
&lt;p&gt; → 어딘가에서 나를 부르기 전에는 반드시!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public static gameManager I;

 void Awake()
 {
     I = this;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gameManager 내에 gameOver() 함수 만들기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject retryBtn;

 public void gameOver()
 {
     retryBtn.SetActive(true);
     Time.timeScale = 0.0f;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cat.cs&lt;/code&gt; 에서 특정 y 좌표 밑으로 내려가면 gameOver() 부르기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Update()
 {
     if (energy &amp;lt; full)
     {
         transform.position += new Vector3(0.0f, -0.05f, 0.0f);

         if (transform.position.y &amp;lt; -16.0f)
         {
             gameManager.I.gameOver();
         }
     }
     else
     {
         if (transform.position.x &amp;gt; 0)
         {
             transform.position += new Vector3(0.05f, 0.0f, 0.0f);
         }
         else
         {
             transform.position += new Vector3(-0.05f, 0.0f, 0.0f);
         }
         Destroy(gameObject, 3.0f);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/198c2b1d-d7b5-42f6-bf4a-0f3de3a47d82/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;06. 레벨 구성하기&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;[레벨업 표기하기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 레벨 UI 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;오른쪽 위에 레벨 표기하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;level 폴더를&lt;/strong&gt; Canvas 게임오브젝트로 묶어 배치합니다. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rect Transform을 먼저 리셋해주시고 위치는 &lt;strong&gt;x:250, y:500&lt;/strong&gt; 으로 설정 해줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;back / front는 &lt;strong&gt;width: 100, height: 15, y: -70&lt;/strong&gt; 으로 설정 해줍니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Text는 &lt;strong&gt;width: 20, height: 100, font size: 40, font style: Bold, x: -50&lt;/strong&gt; 으로 설정 합니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1c2743e6-6511-48c7-a491-6706686a5a08/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 5마리 당 레벨 1씩 올리기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;gameManager에 레벨업 함수 만들기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; int level = 0;
 int cat = 0;

 public void addCat()
 {
     cat += 1;
     level = cat / 5;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;고양이가 배부를 때 &lt;code&gt;addCat()&lt;/code&gt; 함수 부르기&lt;/p&gt;
&lt;p&gt; → 다만, 이렇게 하면 안됩니다!&lt;/p&gt;
&lt;p&gt; → 이유는, 이렇게하면 옆으로 빠질 때 계속 점수가 올라가겠죠!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void OnTriggerEnter2D(Collider2D coll)
 {
     if (coll.gameObject.tag == &amp;quot;food&amp;quot;)
     {
         if (energy &amp;lt; full)
         {
             energy += 1.0f;
             Destroy(coll.gameObject);

             gameObject.transform.Find(&amp;quot;hungry/Canvas/front&amp;quot;).transform.localScale = new Vector3(energy / full, 1.0f, 1.0f);
         }
         else
         {
             gameManager.I.addCat();
             gameObject.transform.Find(&amp;quot;hungry&amp;quot;).gameObject.SetActive(false);
             gameObject.transform.Find(&amp;quot;full&amp;quot;).gameObject.SetActive(true);
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;그래서! 약간 수정을 합니다.&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;상태&lt;/code&gt; 를 isFull 로 만들고, 제어합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; bool isFull = false;

 if (isFull == false)
 {
     gameManager.I.addCat();
     gameObject.transform.Find(&amp;quot;hungry&amp;quot;).gameObject.SetActive(false);
     gameObject.transform.Find(&amp;quot;full&amp;quot;).gameObject.SetActive(true);

     isFull = true;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 레벨업 표기해주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;레벨 업 라벨 초기화해주기&lt;/p&gt;
&lt;p&gt; → 레벨업 텍스트 0으로&lt;/p&gt;
&lt;p&gt; → 레벨업 front 바 x scale 0으로&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;레벨 업 라벨 바꿔주기&lt;/p&gt;
&lt;p&gt; → 레벨업 텍스트, front 바&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using 

 public Text levelText;
 public GameObject levelFront;

 public void addCat()
 {
     cat += 1;
     level = cat / 5;

     levelText.text = level.ToString();
     levelFront.transform.localScale = new Vector3((cat - level * 5) / 5.0f, 1.0f, 1.0f);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6f11406b-9150-4db5-8042-ab122d78a722/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[레벨 반영하기]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  더 많은 레벨을 구현할 수 있겠지만, 우리는 `Lv.3` 까지만 디자인 해볼게요!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) &lt;code&gt;Lv.1&lt;/code&gt;, &lt;code&gt;Lv.2&lt;/code&gt; : 더 많은 고양이 출현시키기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에서 확률에 따른 추가 고양이 출현&lt;/p&gt;
&lt;p&gt; → 단, 빠른 진행을 위해 makeFood를 &lt;code&gt;0.2f&lt;/code&gt; → &lt;code&gt;0.1f&lt;/code&gt; 로 바꿔둘게요!&lt;/p&gt;
&lt;p&gt; → 이미 생각보다 쉽지 않을걸요!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void makeCat()
 {
     Instantiate(normalCat);

     if (level == 1)
     {
         float p = Random.Range(0, 10);
         if (p &amp;lt; 2) Instantiate(normalCat);
     }
     else if (level &amp;gt;= 2)
     {
         float p = Random.Range(0, 10);
         if (p &amp;lt; 5) Instantiate(normalCat);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) &lt;code&gt;Lv.3&lt;/code&gt; : fatCat 출현시키기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cat.cs&lt;/code&gt; 에 고양이 타입을 만들기!&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;type: 0&lt;/code&gt; 은 normalCat, &lt;code&gt;type: 1&lt;/code&gt; 은 fatCat !!&lt;/p&gt;
&lt;p&gt; → 아래와 같이 써두면, 오브젝트에서 타입을 조절할 수 있습니다.&lt;/p&gt;
&lt;p&gt; → 다시 normalCat 프리팹에 가서 &lt;code&gt;type: 0&lt;/code&gt; 으로 만들어보기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public int type;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/78a28c6c-6641-40a5-8979-4312762b0a39/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fatCat 프리팹 준비하기&lt;/p&gt;
&lt;p&gt; → front의 &lt;code&gt;x scale: 0&lt;/code&gt;으로 만들기&lt;/p&gt;
&lt;p&gt; → 스크립트 붙이고 &lt;code&gt;type: 1&lt;/code&gt;로 만들기&lt;/p&gt;
&lt;p&gt; → box collider 2D를 붙이는 것도 잊지 말기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e2eb700d-e2b0-4709-bdc3-46d228cb008c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4dcf382c-9915-4351-9594-5af50d55873a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;고양이 타입을 고려한 &lt;code&gt;cat.cs&lt;/code&gt; 만들기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;type:1&lt;/code&gt; (fatCat)인 경우 &lt;code&gt;full: 10&lt;/code&gt; 이고, 내려오는 속도가 늦음&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     float x = Random.Range(-8.5f, 8.5f);
     float y = 30.0f;
     transform.position = new Vector3(x, y, 0);

     if (type == 1)
     {
         full = 10.0f;
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; // 업데이트 구문 안의 if문
 if (energy &amp;lt; full)
 {
     if (type == 0)
     {
         transform.position += new Vector3(0.0f, -0.05f, 0.0f);
     }
     else if (type == 1)
     {
         transform.position += new Vector3(0.0f, -0.03f, 0.0f);
     }

     if (transform.position.y &amp;lt; -16.0f)
     {
         gameManager.I.gameOver();
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;fatCat&lt;/code&gt; 을 등장시키기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void makeCat()
 {
     Instantiate(normalCat);

     if (level == 1)
     {
         float p = Random.Range(0, 10);
         if (p &amp;lt; 2) Instantiate(normalCat);
     }
     else if (level == 2)
     {
         float p = Random.Range(0, 10);
         if (p &amp;lt; 5) Instantiate(normalCat);
     }
     else if (level &amp;gt;= 3)
     {
         float p = Random.Range(0, 10);
         if (p &amp;lt; 5) Instantiate(normalCat);

         Instantiate(fatCat);
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/29100d45-dc3f-41ad-ab36-3bec90448fb0/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;07. 마무리 - 게임 즐겨보기 &amp;amp; 버그잡기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) startScene으로 가서 게임을 즐겨봅니다!&lt;/p&gt;
&lt;p&gt;  → 앗, 그런데 &lt;code&gt;Replay&lt;/code&gt; 후에 총알 발사가 안되네요!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8d237516-9ef9-4bf2-895f-02adf9534d05/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) gameManager 시작할 때 &lt;code&gt;timeScale = 1.0f&lt;/code&gt; 로 바꿔주기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  void Start()
  {
      Time.timeScale = 1.0f;
      InvokeRepeating(&amp;quot;makeFood&amp;quot;, 0.0f, 0.1f);
      InvokeRepeating(&amp;quot;makeCat&amp;quot;, 0.0f, 1.0f);
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;08. 숙제 - 해적 고양이 만들기!&lt;/h2&gt;
&lt;aside&gt;
  `Lv.4` 에서 해적고양이를 등장시킵니다! 준비하는 것은 튜터와 지금! 함께할께요  

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;해적고양이의 속성&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;normalCat 보다 사이즈가 작음 scale: &lt;code&gt;x:0.8&lt;/code&gt;, &lt;code&gt;y:0.8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;normalCat 보다 빠르게 내려옴 &lt;code&gt;-0.1f&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;해적고양이 준비하기 (튜터와 함께)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;normalCat 프리팹을 가져와서, 오른쪽 키 → unpack 합니다.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f5a69e06-4695-4ff0-8f08-c44e7dce4a48/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;이미지 교체해주고, pirateCat의 scale을 작게하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cfff28b6-4dc5-433b-9f41-f1ed9b4c5206/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;pirateCat의 animator를 떼어내고, 새로 붙여주기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b5429e27-0768-48fe-9559-db7bca23663d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cat.cs&lt;/code&gt; 스크립트 붙이고 &lt;code&gt;type: 2&lt;/code&gt; 로 바꿔두기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/133fe1a7-3a2a-4be9-b63a-fee073bebd3e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;프리팹 화 해두고 과감히 삭제하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5750ac5d-a420-4b1d-b594-0eafae9af46d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;이렇게 되면 완성!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  → 제법 흥미진진하죠?&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1a5e6984-66e3-4f89-b85a-574580facd3b/%EA%B3%A0%EC%96%91%EC%9D%B4_%EB%B0%A5%EC%A3%BC%EA%B8%B0_%EA%B2%8C%EC%9E%84(%EC%88%99%EC%A0%9C).mp4&quot;&gt;고양이 밥주기 게임(숙제).mp4&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;힌트요정 -  &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  [수정해야할 부분]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cat.cs&lt;/code&gt; 의 Update 부분 : type 나오는 곳에 아래를 추가해주기!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; transform.position += new Vector3(0.0f, -0.1f, 0.0f);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 의 makeCat 부분 : &lt;code&gt;level &amp;gt;= 4&lt;/code&gt; 만들어주기!&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;level &amp;gt;= 3&lt;/code&gt; 부분은 &lt;code&gt;level == 3&lt;/code&gt; 으로 바꿔야겠죠?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; else if (level &amp;gt;= 4)
 {
     float p = Random.Range(0, 10);
     if (p &amp;lt; 5) Instantiate(normalCat);

     Instantiate(fatCat);
     Instantiate(pirateCat);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;HW. 3주차 숙제 해설&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 부분&lt;/p&gt;
&lt;p&gt;  → &lt;code&gt;makeCat()&lt;/code&gt; 부분을 보세요&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;
  using UnityEngine.UI;

  public class gameManager : MonoBehaviour
  {
      public GameObject food;
      public GameObject dog;

      public GameObject normalCat;
      public GameObject fatCat;
      public GameObject pirateCat;

      public GameObject retryBtn;

      int level = 0;
      int cat = 0;

      public Text levelText;
      public GameObject levelFront;

      public static gameManager I;
      void Awake()
      {
          I = this;
      }

      // Start is called before the first frame update
      void Start()
      {
          Time.timeScale = 1.0f;
          InvokeRepeating(&amp;quot;makeFood&amp;quot;, 0.0f, 0.1f);
          InvokeRepeating(&amp;quot;makeCat&amp;quot;, 0.0f, 1.0f);
      }

      void makeFood()
      {
          float x = dog.transform.position.x;
          float y = dog.transform.position.y + 2.0f;
          Instantiate(food, new Vector3(x,y,0), Quaternion.identity);
      }

      void makeCat()
      {
          Instantiate(normalCat);

          if (level == 1)
          {
              float p = Random.Range(0, 10);
              if (p &amp;lt; 2) Instantiate(normalCat);
          }
          else if (level == 2)
          {
              float p = Random.Range(0, 10);
              if (p &amp;lt; 5) Instantiate(normalCat);
          }
          else if (level == 3)
          {
              float p = Random.Range(0, 10);
              if (p &amp;lt; 5) Instantiate(normalCat);

              Instantiate(fatCat);
          }
          else if (level &amp;gt;= 4)
          {
              float p = Random.Range(0, 10);
              if (p &amp;lt; 5) Instantiate(normalCat);

              Instantiate(fatCat);
              Instantiate(pirateCat);
          }
      }

      // Update is called once per frame
      void Update()
      {

      }

      public void gameOver()
      {
          retryBtn.SetActive(true);
          Time.timeScale = 0.0f;
      }

      public void addCat()
      {
          cat += 1;
          level = cat / 5;

          levelText.text = level.ToString();
          levelFront.transform.localScale = new Vector3((cat - level * 5) / 5.0f, 1.0f, 1.0f);
      }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;cat.cs&lt;/code&gt; 부분&lt;/p&gt;
&lt;p&gt;  → &lt;code&gt;Update()&lt;/code&gt; 부분을 보세요&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;

  public class cat : MonoBehaviour
  {
      float full = 5.0f;
      float energy = 0.0f;

      bool isFull = false;

      public int type;

      // Start is called before the first frame update
      void Start()
      {
          float x = Random.Range(-8.5f, 8.5f);
          float y = 30.0f;
          transform.position = new Vector3(x, y, 0);

          if (type == 1)
          {
              full = 10.0f;
          }
      }

      // Update is called once per frame
      void Update()
      {
          if (energy &amp;lt; full)
          {
              if (type == 0)
              {
                  transform.position += new Vector3(0.0f, -0.05f, 0.0f);
              }
              else if (type == 1)
              {
                  transform.position += new Vector3(0.0f, -0.03f, 0.0f);
              }
              else if (type == 2)
              {
                  transform.position += new Vector3(0.0f, -0.1f, 0.0f);
              }

              if (transform.position.y &amp;lt; -16.0f)
              {
                  gameManager.I.gameOver();
              }
          }
          else
          {
              if (transform.position.x &amp;gt; 0)
              {
                  transform.position += new Vector3(0.05f, 0.0f, 0.0f);
              }
              else
              {
                  transform.position += new Vector3(-0.05f, 0.0f, 0.0f);
              }
              Destroy(gameObject, 3.0f);
          }
      }

      void OnTriggerEnter2D(Collider2D coll)
      {
          if (coll.gameObject.tag == &amp;quot;food&amp;quot;)
          {
              if (energy &amp;lt; full)
              {
                  energy += 1.0f;
                  Destroy(coll.gameObject);

                  gameObject.transform.Find(&amp;quot;hungry/Canvas/front&amp;quot;).transform.localScale = new Vector3(energy / full, 1.0f, 1.0f);
              }
              else
              {
                  if (isFull == false)
                  {
                      gameManager.I.addCat();
                      gameObject.transform.Find(&amp;quot;hungry&amp;quot;).gameObject.SetActive(false);
                      gameObject.transform.Find(&amp;quot;full&amp;quot;).gameObject.SetActive(true);

                      isFull = true;
                  }
              }
          }
      }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/965</guid>
      <comments>https://comphy.tistory.com/965#entry965comment</comments>
      <pubDate>Tue, 3 Jan 2023 22:44:56 +0900</pubDate>
    </item>
    <item>
      <title># [스파르타코딩클럽] 게임개발 종합반 - 2주차</title>
      <link>https://comphy.tistory.com/964</link>
      <description>&lt;h1&gt;[스파르타코딩클럽] 게임개발 종합반 - 2주차&lt;/h1&gt;
&lt;aside&gt;
&lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; alt=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/3e9b61fa-7a3a-4ebc-b502-407eaf7619ee/css__280x280.png&quot; width=&quot;40px&quot; /&gt; **매 주차 강의자료 시작에 PDF파일을 올려두었어요!**

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PDF 파일&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cc6d2aad-e175-4d07-9a3e-aef2ee9c57c8/%E1%84%89%E1%85%B3%E1%84%91%E1%85%A1%E1%84%85%E1%85%B3%E1%84%90%E1%85%A1%E1%84%8F%E1%85%A9%E1%84%83%E1%85%B5%E1%86%BC%E1%84%8F%E1%85%B3%E1%86%AF%E1%84%85%E1%85%A5%E1%86%B8_%E1%84%80%E1%85%A6%E1%84%8B%E1%85%B5%E1%86%B7%E1%84%80%E1%85%A2%E1%84%87%E1%85%A1%E1%86%AF_%E1%84%8C%E1%85%A9%E1%86%BC%E1%84%92%E1%85%A1%E1%86%B8%E1%84%87%E1%85%A1%E1%86%AB_2%E1%84%8C%E1%85%AE%E1%84%8E%E1%85%A1_211108.pdf&quot;&gt;스파르타코딩클럽_게임개발_종합반_2주차_211108.pdf&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[수업 목표]&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;유니티 기본 사용법 복습해보기&lt;/li&gt;
&lt;li&gt;유명 게임을 완성해보기&lt;/li&gt;
&lt;li&gt;베스트 스코어 기록해보기&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;[목차]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  모든 토글을 열고 닫는 단축키
Windows : `Ctrl` + `alt` + `t` 
Mac : `⌘` + `⌥` + `t`

&lt;/aside&gt;

&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;01. 2주차 오늘 배울 것&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 2주차 수업의 목표와 범위&lt;/p&gt;
  &lt;aside&gt;
    오늘의 키워드! `**기억이 새록새록**`

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1주차에 배웠던 것을 복습 → 복습 → 복습 하는 게 전체적인 수업의 구성이에요!&lt;/p&gt;
&lt;p&gt;  → 이번 주는 조금 더 익숙해진 자신을 볼 수 있을 것이랍니다.&lt;/p&gt;
&lt;p&gt;  → 참고로 &lt;strong&gt;오늘, 쉽습니다!&lt;/strong&gt; (복습이 많기 때문에!  )&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rise Up! 이란 게임을 유사하게 만들어보면서, 유니티의 기초를 다시 다집니다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;실제 게임 모습 보기: 전세계 1억 다운로드 이상의 히트작이랍니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d413c0be-0d6c-447d-b69a-8fc6721a9345/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/046ff653-3483-4084-818b-c5c660b4d193/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 오늘 만들 것 : &lt;code&gt;풍선을 지켜라&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;네모가 풍선에 닿으면 끝! 오래 살아남는 게 목표랍니다.&lt;/p&gt;
&lt;p&gt;  → 새롭게 배우는 것: 마우스로 제어하기, 베스트스코어 기록, 애니메이션 전환&lt;/p&gt;
&lt;p&gt;  &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f4ad2974-6c4c-4664-8330-39e12915d4fb/Untitled.mp4&quot;&gt;Untitled&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 만들 순서&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;기본 씬 구성하기 - 배경, 풍선, 마우스, 네모, 시간&lt;/li&gt;
&lt;li&gt;풍선 애니메이션 더하기&lt;/li&gt;
&lt;li&gt;마우스 움직임 더하기&lt;/li&gt;
&lt;li&gt;시간 가게 하기&lt;/li&gt;
&lt;li&gt;네모 내려오게 하기 + 충돌 구현&lt;/li&gt;
&lt;li&gt;게임 끝내기(1): 판넬 만들기&lt;/li&gt;
&lt;li&gt;게임 끝내기(2): 베스트 스코어 기록하기&lt;/li&gt;
&lt;li&gt;풍선 애니메이션 전환하기&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[기본 씬 구성하기]&lt;/strong&gt;&lt;/p&gt;
&lt;aside&gt;
  `myshield` 라는 프로젝트를 만들고 시작할게요!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 기본 세팅하기 &amp;amp; 배경 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;windows → 2x3 layout 클릭! free aspect → phone 클릭!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6b5d7b1d-3ade-4c79-9a2b-dc126cf9b20d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;배경 색은 &lt;code&gt;rgb ⇒ 20, 20, 80&lt;/code&gt; 으로 할게요! 사이즈는 &lt;code&gt;x: 6, y: 10&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4fd52626-520e-4e97-b600-67b6d8bbc3d5/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 풍선, 마우스 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;간단한 풍선을 만들어둡니다. (balloon)&lt;/p&gt;
&lt;p&gt; → position &lt;code&gt;x: 0, y: -3.2&lt;/code&gt; 에 맞춰두기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1c03eece-4b98-4d7e-a534-3d92e6092496/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;마우스를 만들어둡니다. (shield)&lt;/p&gt;
&lt;p&gt; → scale &lt;code&gt;x: 0.5, y: 0.5&lt;/code&gt; 로 세팅해두기! position은 그대로 둘게요!&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;rgb ⇒ 0, 0, 255&lt;/code&gt; 로 가겠습니다!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/ed3eaa22-b17a-4b23-9d1d-18299b927d7a/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 타이머 만들기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;시간 라벨 만들기 (timeTxt)&lt;/p&gt;
&lt;p&gt;  → UI → Text를 활용!&lt;/p&gt;
&lt;p&gt;  → &lt;code&gt;font size 70&lt;/code&gt; , &lt;code&gt;position x: 0, y: 450&lt;/code&gt; 으로 맞춰주세요!&lt;/p&gt;
&lt;p&gt;  → width: 200, height :200&lt;/p&gt;
&lt;p&gt;  → posY: 450으로 맞춰주세요!&lt;/p&gt;
&lt;p&gt;  → color: 255, 0, 0 (빨강)&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e4b05159-7ed0-4b44-b17c-728054fcb66d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;02. 풍선 &amp;amp; 마우스 만들기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 애니메이션 더하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Animations 폴더 → 애니메이션 만들기 (balloon_idle)&lt;/p&gt;
&lt;p&gt; → 이따가 풍선이 &amp;quot;터지는&amp;quot; 애니메이션도 만들어야 하니, 이것은 idle(기본 상태)로 둘게요!&lt;/p&gt;
&lt;p&gt; → Loop Time에 체크하는 것 잊지 말기!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1bf590a2-03e2-47f0-8b77-bf693598752b/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;풍선에 끌어다 놓고 애니메이션 만들기&lt;/p&gt;
&lt;p&gt; →레코드 (빨간색 동그라미!)를 눌러서 같이 세팅합시다!&lt;/p&gt;
&lt;p&gt; → 0:00, 0:40은 처음 모습 그대로&lt;/p&gt;
&lt;p&gt; → 0:20은 &lt;code&gt;rgb ⇒ 200, 200, 255&lt;/code&gt; 로 세팅하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/28e8f459-17f0-4726-9602-9c42b5368d6f/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Play&lt;/code&gt; 버튼을 눌러서 확인하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e054bff0-d5ea-4b31-b52d-ee78d0631f14/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 마우스에 움직임 더하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Scripts → shield.cs 만들기 + shield에 붙이기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6d0008bd-cc3f-4063-91cb-e5e607b425f1/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;마우스 포인터를 따라 움직이게 하기&lt;/p&gt;
&lt;p&gt; → 외우지 말고, 나중에도 보고 쓰는 코드랍니다. 튜터도 외우고 있지 않아요!&lt;/p&gt;
&lt;p&gt; → mouse 의 좌표계를 카메라 좌표계로 바꾸고, shield의 위치에 넣어주기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Update()
 {
     Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
     transform.position = new Vector3(mousePos.x, mousePos.y, 0);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Play&lt;/code&gt; 해서 움직임 확인하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5e39e519-2dc9-41cc-b46b-1535243832e3/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;03. 네모 만들기&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;[내려오기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 네모 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;sprite → square 로 만들고, 이름 바꿔두기 (square)&lt;/p&gt;
&lt;p&gt; → position &lt;code&gt;x:0, y:3&lt;/code&gt; 에 맞추기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/095f7525-c43d-4d9c-abbf-0519d2ba26b2/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;order in layer 맞추기&lt;/p&gt;
&lt;p&gt; → 네모, 마우스, 풍선 모두 order in layer를 1로 바꿔줍니다! (참고로 배경은 0 !)&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/41513868-9a36-4fee-b394-a90cb2a41c46/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 네모 떨어지게 하기/충돌효과 주기&lt;/p&gt;
  &lt;aside&gt;
    리마인드 - 충돌은 `한쪽에 rigidbody` 를 주고 + `양쪽에 collider` 가 있으면 완성!

  &lt;/aside&gt;

&lt;p&gt;  → rigidbody 2D 와, box collider 2D 를 줍니다.&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/953bf351-0796-469f-98c9-b995e256f23d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 풍선, 마우스에도 충돌효과 주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;풍선, 마우스에 &lt;code&gt;circle collider 2D&lt;/code&gt; 달기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cefd8178-481a-4c72-9a2a-59d996d77ab3/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Play&lt;/code&gt; 버튼을 눌러 마우스와 네모가 부딪히는지 보기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/1f733d4d-349e-4fa6-b498-c8fffb6edf60/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;[나타나기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) gameManager 만들기&lt;/p&gt;
  &lt;aside&gt;
    리마인드 - gameManager는? 가운데서 게임을 관장하는 역할!

  &lt;/aside&gt;

&lt;p&gt;  → gamaManager gameObject 와 script 를 만들고 서로 붙여줍니다!&lt;/p&gt;
&lt;p&gt;  → 이후 이 gameManager에서 네모를 만들어 줄 예정!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/aa226b08-2dc3-4934-bd7a-7e8e67e165d3/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 네모 랜덤으로 나타내기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;square.cs&lt;/code&gt; 만들고, 네모에 붙이기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d916599f-f588-49a3-9d65-c5be3e95dac8/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;랜덤 위치에서 생성하기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;x: -3.0f ~ 3.0f&lt;/code&gt;, &lt;code&gt;y: 3.0f ~ 5.0f&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → 저장 후 &lt;code&gt;Play&lt;/code&gt; 해서 확인하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     float x = Random.Range(-3.0f, 3.0f);
     float y = Random.Range(3.0f, 5.0f);

     transform.position = new Vector3(x, y, 0);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;랜덤 사이즈로 생성하기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;size: 0.5f ~ 1.5f&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → 저장 후 &lt;code&gt;Play&lt;/code&gt; 해서 확인하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     float x = Random.Range(-3.0f, 3.0f);
     float y = Random.Range(3.0f, 5.0f);

     transform.position = new Vector3(x, y, 0);

     float size = Random.Range(0.5f, 1.5f);
     transform.localScale = new Vector3(size, size, 1);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 네모를 prefab으로 만들기&lt;/p&gt;
  &lt;aside&gt;
    리마인드 - 프리팹은? 복제를 위해 미리 만들어둔 아이템

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Prefabs 폴더를 만들고 → 끌어다넣기&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;기존의 square 오브젝트는 과감하게 삭제!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5acf2ef1-ef90-406f-b634-15a55aebd15e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) &lt;code&gt;gameManager.cs&lt;/code&gt; 에서 네모를 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;반복 실행하게 하기&lt;/p&gt;
&lt;p&gt; → 0.5f 마다 makeSquare 함수를 실행!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     InvokeRepeating(&amp;quot;makeSquare&amp;quot;, 0.0f, 0.5f);
 }

 void makeSquare()
 {
     Debug.Log(&amp;quot;반복한다!&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d89b4460-998d-40cf-8ffd-359d44348e0c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;네모 만들기&lt;/p&gt;
&lt;p&gt; → square 프리팹을 받아서, 복제하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject square;

 void makeSquare()
 {
     Instantiate(square);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f99f7219-7561-481a-92c9-0ecef1c51010/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;04. 시간 올라가게 하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 시간 올리기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;UI text 받기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; using UnityEngine.UI;

 public Text timeTxt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;시간 올리기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; float alive = 0f;

 void Update()
 {
     alive += Time.deltaTime;
     timeTxt.text = alive.ToString(&amp;quot;N2&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;05. 게임 끝내기&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;[판넬 만들기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 게임 종료 판넬 만들기&lt;/p&gt;
  &lt;aside&gt;
    Canvas → endPanel 게임오브젝트로 모두 묶습니다.

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Image 만들기&lt;/p&gt;
&lt;p&gt; → 사이즈 &lt;code&gt;x: 450, y: 600&lt;/code&gt;&lt;/p&gt;
&lt;p&gt; → shadow 효과주기 : &lt;code&gt;rgba ⇒ 255, 255, 0, 150&lt;/code&gt; (Add Component로 추가!)&lt;/p&gt;
&lt;p&gt; → 그림자 위치는 &lt;code&gt;x: 15, y: -15&lt;/code&gt; 로 맞추기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/b200f5fd-bcc6-410e-ba7a-c849ccfe4172/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;폰트 가져오기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;[코드스니펫] 배달의민족 주아체&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  http://pop.baemin.com/fonts/jua/BMJUA_ttf.ttf&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    → Fonts 폴더 만들고 끌어다넣기

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf440841-5349-4ef3-b30a-6a4e3cd9a845/Untitled.png)

3. 끝 메시지, 현재 스코어, 최고 스코어 만들기

    → 폰트 사이즈는 메시지는 50, 라벨은 40으로 해주세요!

    → position (-100, 100), (150, 100), (-100, 0), (150, 0)으로 맞춰주세요!

    → ctrl+d (복제) 를 이용하면 무척 편하답니다.

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/e3a20e24-4dfc-4150-8cb4-1ffb415b5aaa/Untitled.png)

4. retry 버튼 만들기

    → retryBtn 오브젝트 안에 만들게요!

    → 이미지 색은 `rgb ⇒ 80, 80, 200` 으로 할게요!

    → width: 300, height: 100

    → posY : -200

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/4d01b527-7cf4-4328-a0b9-fa70ead600d2/Untitled.png)

5. 버튼에 `button` 속성 달고 Image 끌어다놓기

    → 그래야 클릭 할 때 color 틴트가 일어납니다!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/40e041f9-18bc-4402-b2e5-530955df6e1e/Untitled.png)

6. 우선, 판넬 전체를 숨겨둡니다.

    → `SetActive(true)` 로 나중에 켤 것이랍니다!

    ![Untitled](https://s3-us-west-2.amazonaws.com/secure.notion-static.com/44088764-b8e5-4a8d-b560-030ec83fc768/Untitled.png)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;[판넬 나타내기]&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) gameManager 싱글톤 처리하기&lt;/p&gt;
  &lt;aside&gt;
    리마인드 - 싱글톤이란? `너는 딱 하나야!` 라고 할 수 있게 세팅해두는 것
  (그래야 다른데서 나를 부를 수 있습니다!)

  &lt;/aside&gt;

&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  public static gameManager I;

  void Awake()
  {
      I = this;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 게임 종료하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에 종료 함수 만들어두기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public GameObject endPanel;

 public void gameOver()
 {
     Time.timeScale = 0.0f;
     endPanel.SetActive(true);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;square.cs&lt;/code&gt; 네모가 풍선과 부딪히면 게임 종료하기&lt;/p&gt;
&lt;p&gt; → 우선, 풍선에 &amp;quot;balloon&amp;quot;이라는 tag를 주기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void OnCollisionEnter2D(Collision2D coll)
 {
     if (coll.gameObject.tag == &amp;quot;balloon&amp;quot;)
     {
         gameManager.I.gameOver();
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Play&lt;/code&gt; 해서 확인하기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/161c55cb-e545-4ea7-a58f-ac4c2af5b2b9/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;3) 현재 점수 보여주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;thisScoreText 가져오기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public Text thisScoreTxt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;gameOver() 수정하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void gameOver()
 {
     Time.timeScale = 0.0f;
         thisScoreTxt.text = alive.ToString(&amp;quot;N2&amp;quot;);
     endPanel.SetActive(true);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;한걸음 더 : Update() 함수를 멈추게 하기&lt;/p&gt;
&lt;p&gt; → Update()와 gameOver() 간의 약간의 시간차가 있기 때문에, 이것을 제어해보겠습니다.&lt;/p&gt;
&lt;p&gt; → 그래야 &lt;code&gt;timeTxt&lt;/code&gt; 와 &lt;code&gt;thisScoreTxt&lt;/code&gt; 가 같은 값으로 나온답니다!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d153b9e1-56e4-4d09-8888-2659b3cd43dd/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; bool isRunning = true;

 void Update()
 {
     if (isRunning)
     {
         alive += Time.deltaTime;
         timeTxt.text = alive.ToString(&amp;quot;N2&amp;quot;);
     }
 }

 public void gameOver()
 {
     isRunning = false;
     Time.timeScale = 0.0f;
     thisScoreTxt.text = alive.ToString(&amp;quot;N2&amp;quot;);
     endPanel.SetActive(true);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;4) 다시하기 만들기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; - 다시하기 함수 만들기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;using UnityEngine.SceneManagement;

public void retry()
{
    SceneManager.LoadScene(&amp;quot;MainScene&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;시간을 다시 켜주기&lt;/p&gt;
&lt;p&gt; → 이렇게 다시 할 때에는 반드시 &amp;quot;시간&amp;quot;도 되돌려 놓아야 합니다!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; void Start()
 {
     Time.timeScale = 1.0f;
     InvokeRepeating(&amp;quot;makeSquare&amp;quot;, 0.0f, 0.5f);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;다시하기 버튼에 retry() 함수 붙이기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/bc1fa892-73d7-42e4-9646-576fc22cea4d/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 최고 점수 나타내기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 데이터를 보관하는 방법: &lt;code&gt;PlayerPrefs&lt;/code&gt;&lt;/p&gt;
  &lt;aside&gt;
    앱을 껐다 켜도 데이터가 유지되게 - 유니티에서 데이터를 보관하는 방법!

  &lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;데이터 저장하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  PlayerPrefs.SetFloat(&amp;quot;bestScore&amp;quot;, 어떤숫자값);
  PlayerPrefs.SetString(&amp;quot;bestScore&amp;quot;, 어떤문자열);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;데이터 불러오기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  어떤숫자값 = PlayerPrefs.getFloat(&amp;quot;bestScore&amp;quot;);
  어떤문자열 = PlayerPrefs.getString(&amp;quot;bestScore&amp;quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;데이터를 저장했었는지 확인&lt;/p&gt;
&lt;p&gt;  → 있으면 &lt;code&gt;true&lt;/code&gt; 없으면 &lt;code&gt;false&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  PlayerPrefs.HasKey(&amp;quot;bestScore&amp;quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;데이터를 모두 지우기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  PlayerPrefs.DeleteAll();&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 최고 점수 보여주기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;로직 생각하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; if (최고 점수가 없으면)
 {
     최고점수 = 지금점수
 }
 else
 {
     if (최고점수 &amp;lt; 지금점수)
     {
         최고점수 = 지금점수
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;구현하기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void gameOver()
 {
     isRunning = false;
     Time.timeScale = 0.0f;
     thisScoreTxt.text = alive.ToString(&amp;quot;N2&amp;quot;);
     endPanel.SetActive(true);

     if (PlayerPrefs.HasKey(&amp;quot;bestScore&amp;quot;) == false)
     {
         PlayerPrefs.SetFloat(&amp;quot;bestScore&amp;quot;, alive);
     }
     else
     {
         if (PlayerPrefs.GetFloat(&amp;quot;bestScore&amp;quot;) &amp;lt; alive)
         {
             PlayerPrefs.SetFloat(&amp;quot;bestScore&amp;quot;, alive);
         }
     }
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;최고 점수 띄워주기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public Text bestScoreTxt;

 bestScoreTxt.text = PlayerPrefs.GetFloat(&amp;quot;bestScore&amp;quot;).ToString(&amp;quot;N2&amp;quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/72d302dd-060f-4517-aa7d-158615dd721e/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;07. 풍선 애니메이션 전환하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1) 풍선이 &lt;code&gt;터지면서&lt;/code&gt; 끝나게 하기&lt;/p&gt;
  &lt;aside&gt;
    터지는 애니메이션을 만들어두고 → 풍선이 네모와 닿으면 &quot;전환&quot;하게 하는 것이죠!

  &lt;/aside&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;터지는 애니메이션(balloon_die) 만들고 balloon에 끌어다 놓고 add New Clip 해주기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/40d87a58-04b5-48bc-808d-652165ed0278/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;0:20 에 기록하기&lt;/p&gt;
&lt;p&gt; → &lt;code&gt;x:2, y:2&lt;/code&gt; 그리고 &lt;code&gt;rgba ⇒ 255, 0, 0, 125&lt;/code&gt; 으로, 터지는 것처럼!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/920bf7f8-0b1f-4790-8969-c3cfc9a448cd/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;balloon animator를 열어서, idle → die로 transition 만들기&lt;/p&gt;
&lt;p&gt; → 마우스 오른쪽 클릭후 make transition 하면 됩니다!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/15e0b7cf-75b4-4571-8561-d27a9f5a91fd/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Parameters에, bool 형식의 &lt;code&gt;isDie&lt;/code&gt; 를 만들기&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/18ba80cb-5e57-4ab3-99aa-e1efca95285b/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;transition을 클릭하고 아래와 같이 세팅하기&lt;/p&gt;
&lt;p&gt; → has exit time 을 체크 해제해야 : 즉시 전환됩니다!&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5b407dc7-1ba0-41ab-9e39-d0bcf4641939/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;2) 풍선 애니메이션 전환하기&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameManager.cs&lt;/code&gt; 에서 - animator를 받기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public Animator anim;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6f91a668-4879-4361-ae98-fe12f3e5be72/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;gameOver()&lt;/code&gt; 할 때 &lt;code&gt;isDie&lt;/code&gt; 값을 바꿔주기&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void gameOver()
 {
     anim.SetBool(&amp;quot;isDie&amp;quot;, true);

     isRunning = false;
     Time.timeScale = 0.0f;
     thisScoreTxt.text = alive.ToString(&amp;quot;N2&amp;quot;);
     endPanel.SetActive(true);

     if (PlayerPrefs.HasKey(&amp;quot;bestScore&amp;quot;) == false)
     {
         PlayerPrefs.SetFloat(&amp;quot;bestScore&amp;quot;, alive);
     }
     else
     {
         if (PlayerPrefs.GetFloat(&amp;quot;bestScore&amp;quot;) &amp;lt; alive)
         {
             PlayerPrefs.SetFloat(&amp;quot;bestScore&amp;quot;, alive);
         }
     }
     bestScoreTxt.text = PlayerPrefs.GetFloat(&amp;quot;bestScore&amp;quot;).ToString(&amp;quot;N2&amp;quot;);
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;확인하기 : 앗, 안된다!&lt;/p&gt;
&lt;p&gt; → 그 이유는, 애니메이션이 나올 틈이 없이 시간을 멈추기 때문&lt;/p&gt;
&lt;p&gt; → 0.5초 후에 시간을 멈추도록 Invoke 로 처리하기!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt; public void gameOver()
 {
     anim.SetBool(&amp;quot;isDie&amp;quot;, true);

     isRunning = false;
     Invoke(&amp;quot;timeStop&amp;quot;, 0.5f);
     thisScoreTxt.text = alive.ToString(&amp;quot;N2&amp;quot;);
     endPanel.SetActive(true);

     if (PlayerPrefs.HasKey(&amp;quot;bestScore&amp;quot;) == false)
     {
         PlayerPrefs.SetFloat(&amp;quot;bestScore&amp;quot;, alive);
     }
     else
     {
         if (PlayerPrefs.GetFloat(&amp;quot;bestScore&amp;quot;) &amp;lt; alive)
         {
             PlayerPrefs.SetFloat(&amp;quot;bestScore&amp;quot;, alive);
         }
     }
     bestScoreTxt.text = PlayerPrefs.GetFloat(&amp;quot;bestScore&amp;quot;).ToString(&amp;quot;N2&amp;quot;);
 }

 void timeStop()
 {
     Time.timeScale = 0.0f;
 }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;08. 숙제 - 떨어지는 네모를 없애기!&lt;/h2&gt;
&lt;aside&gt;
  화면 밖으로 떨어진 네모들을 `Destroy(gameObject)` 해주기!

&lt;/aside&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;현재 상황&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6bdca597-2c1b-4a97-bee8-3906b2196315/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;p&gt;  → &amp;quot;뜨악&amp;quot; 시간이 지나면 네모가 계속 쌓이고 있었네요..!&lt;/p&gt;
&lt;p&gt;  → 화면을 넘어가면 square를 Destroy 해줄 수 있을까요?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;이렇게 되면 완성!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  → 화면에 보여지는 네모와 &lt;code&gt;square(clone)&lt;/code&gt; 수가 일치하면 완성!&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;https://s3-us-west-2.amazonaws.com/secure.notion-static.com/5acdc715-b144-46d3-89fe-6a3f530fbb1c/Untitled.png&quot; alt=&quot;Untitled&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;힌트요정 -  &lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  → &lt;code&gt;square.cs&lt;/code&gt; 만 수정하면 된답니다!&lt;/p&gt;
&lt;p&gt;  → Update() 안에 딱 세 줄만 넣으면 됩니다! 딱 5분만 더 해보면 될 거예요!&lt;/p&gt;
&lt;p&gt;  → y좌표 구하기 ⇒ &lt;code&gt;transform.position.y&lt;/code&gt; 기억나시죠!&lt;/p&gt;
&lt;p&gt;  → 없애라 ⇒ &lt;code&gt;Destroy(gameObject)&lt;/code&gt; 기억나시죠!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  Update()
  {
      if (만약에 y좌표가 -5.0f 보다 작다면)
      {
          없애라;
      }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;HW. 2주차 숙제 해설&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;square.cs&lt;/code&gt; 코드&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;

  public class square : MonoBehaviour
  {
      // Start is called before the first frame update
      void Start()
      {
          float x = Random.Range(-3.0f, 3.0f);
          float y = Random.Range(3.0f, 5.0f);

          transform.position = new Vector3(x, y, 0);

          float size = Random.Range(0.5f, 1.5f);
          transform.localScale = new Vector3(size, size, 1);
      }

      // Update is called once per frame
      void Update()
      {
          if (transform.position.y &amp;lt; -5.0f)
          {
              Destroy(gameObject);
          }
      }

      void OnCollisionEnter2D(Collision2D coll)
      {
          if (coll.gameObject.tag == &amp;quot;balloon&amp;quot;)
          {
              gameManager.I.gameOver();
          }
      }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>졸리운_곰</author>
      <guid isPermaLink="true">https://comphy.tistory.com/964</guid>
      <comments>https://comphy.tistory.com/964#entry964comment</comments>
      <pubDate>Sat, 24 Dec 2022 14:48:44 +0900</pubDate>
    </item>
  </channel>
</rss>