<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bigwave-cho.log</title>
        <link>https://velog.io/</link>
        <description>주먹구구식은 버리고 Why &amp; How를 고민하며 프로그래밍 하는 개발자가 되자!</description>
        <lastBuildDate>Mon, 03 Jul 2023 14:16:02 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>bigwave-cho.log</title>
            <url>https://velog.velcdn.com/images/bigwave-cho/profile/d95dedc8-e440-4632-bbbb-738b3ae510e6/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. bigwave-cho.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bigwave-cho" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[CSR/SSR with Next.js]]></title>
            <link>https://velog.io/@bigwave-cho/CSRSSR-with-Next.js</link>
            <guid>https://velog.io/@bigwave-cho/CSRSSR-with-Next.js</guid>
            <pubDate>Mon, 03 Jul 2023 14:16:02 GMT</pubDate>
            <description><![CDATA[<h1 id="csrssr-with-nextjs">CSR/SSR with Next.js</h1>
<h2 id="csr--ssr">CSR &amp; SSR</h2>
<p>CSR은 클라이언트에서 웹 페이지 렌더링을 하는 것이고 모든 로직, 데이터 페칭, 라우팅 등을 클라이언트에서 처리한다.</p>
<p>CSR은 한 번에 HTML, JS, CSS 등을 로드해서 부분적 리로드를 하지 않고 사용자와 동적으로 상호작용하는 웹 앱이다. 따라서 모든 컨텐츠가 동적으로 로드되어 브라우저는 앱 내에서 라우터를 이동하더라도 새로운 html을 요청하지 않고 기존에 로드한 소스들을 사용하여 컨텐츠를 업데이트 한다.</p>
<p>하지만 CSR은 초기에 모든 것을 로드하기 때문에 로딩 시간이 길고 검색 엔진의 크롤러가 페이지를 제대로 인식하지 못하기 때문에 SEO에 적합하지 않다.</p>
<p>CSR의 이런 단점을 보완하기 위해 다시 SSR 사용이 우세해지기 시작했다. SSR은 페이지를 서버에서 미리 렌더링하여 사용자나 크롤러에게 전달하고 그 후 JS를 로드하여 HTML과 결합한다.</p>
<p>따라서 빠른 페이지 로드와 SEO최적화, 소셜 공유에 최적화돼있다.</p>
<h2 id="nextjs--npm-start">Next.js- npm start</h2>
<p>npm start는 Next.js로 빌드한 웹 앱을 프로덕션 환경에서 실행하는 명령어이다. </p>
<p>npm start는 build된 앱을 실행하는 것이기 때문에 npm run build 명령어로 빌드가 선행되어야 한다. </p>
<p>추가적으로 개발환경에서 앱을 실행하기 위해서는 npm run dev 명령어를 입력하면 된다.</p>
<ul>
<li>참고 블로그(<a href="https://ajdkfl6445.gitbook.io/study/web/csr-vs-ssr-vs-ssg">https://ajdkfl6445.gitbook.io/study/web/csr-vs-ssr-vs-ssg</a>)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[hook] useCreateFuzzyMatcher]]></title>
            <link>https://velog.io/@bigwave-cho/hook-useCreateFuzzyMatcher</link>
            <guid>https://velog.io/@bigwave-cho/hook-useCreateFuzzyMatcher</guid>
            <pubDate>Tue, 14 Mar 2023 02:03:06 GMT</pubDate>
            <description><![CDATA[<p><a href="https://taegon.kim/archives/9919">https://taegon.kim/archives/9919</a>
위 글 공부용</p>
<pre><code class="language-js">import { escapeRegExp } from &#39;lodash&#39;;

//초성검색
function ch2pattern(ch: string) {
  const offset = 44032; // 44032 : &#39;가&#39; 코드
  // 한국어 음절에 해당한다면?
  if (/[가-힣]/.test(ch)) {
    // ch에서 44032를 빼주고
    const chCode = ch.charCodeAt(0) - offset;

    // 28로 나눠서 떨어지면 중성까지만 있는거고 나머지가 남으면 종성까지 있는거.
    if (chCode % 28 &gt; 0) {
      // 종성이 있는 경우는 ch를 그대로 반환
      return ch;
    }
    //종성이 없을 때는
    const begin = Math.floor(chCode / 28) * 28 + offset; // ex) 돈을 종성 빼버려서 &#39;도&#39;로.
    const end = begin + 27; //  end는 &#39;돟&#39;
    return `[\\u${begin.toString(16)}-\\u${end.toString(16)}]`; // 도~돟에 해당하는 유니코드
  }
  // 한글 자음만 있을 때는
  if (/[ㄱ-ㅎ]/.test(ch)) {
    const con2syl = {
      ㄱ: &#39;가&#39;.charCodeAt(0),
      ㄲ: &#39;까&#39;.charCodeAt(0),
      ㄴ: &#39;나&#39;.charCodeAt(0),
      ㄷ: &#39;다&#39;.charCodeAt(0),
      ㄸ: &#39;따&#39;.charCodeAt(0),
      ㄹ: &#39;라&#39;.charCodeAt(0),
      ㅁ: &#39;마&#39;.charCodeAt(0),
      ㅂ: &#39;바&#39;.charCodeAt(0),
      ㅃ: &#39;빠&#39;.charCodeAt(0),
      ㅅ: &#39;사&#39;.charCodeAt(0),
    };

    //as keyof typeof
    /*
    interface User {
     name: string;
     age: number;
     email: string;
     }

     keyof typeof User

     type UserKeys = &quot;name&quot; | &quot;age&quot; | &quot;email&quot;;
    */
    // ㄱ~ㅅ 까지는 그대로 begin 주고
    // ㅅ 이후로는 해당 유니코드에서 &#39;ㅅ&#39;을 뺀다음 588을 곱하고 다시 ㅅ를 더해서 시작점 설정
    // end 는 중성과 종성을 곱해서 더해주기
    // ex)  ㅎ =&gt; begin : 하  end : 힣
    const begin =
      con2syl[ch as keyof typeof con2syl] ||
      (ch.charCodeAt(0) - 12613) * 588 + con2syl[&#39;ㅅ&#39;]; /* &#39;ㅅ&#39;의 코드 */
    const end = begin + 587;
    return `[${ch}\\u${begin.toString(16)}-\\u${end.toString(16)}]`; // ex) 하-힣 에 대한 유니코드 범위
  }
  // 그 외엔 그대로 내보냄(영어 등등..)
  // escapeRegExp : 특수문자 있는 문자열을 정규표현식으로 바꿔주는 메서드(유니코드의 경우 특수문떄문에
  // 이스케이프 안하면 에러 뜰 수 있기 때문)
  return escapeRegExp(ch);
}

export default function createFuzzyMatcher(input: string) {
  const pattern = input.split(&#39;&#39;).map(ch2pattern).join(&#39;.*?&#39;);
  // 인풋을 하나씩 정규표현식화 시켜서 c a t  이런식으로 만듦.
  return new RegExp(pattern);
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git] 실수로 원치 않는 파일 올렸을 때..]]></title>
            <link>https://velog.io/@bigwave-cho/Git-%EC%8B%A4%EC%88%98%EB%A1%9C-%EC%9B%90%EC%B9%98-%EC%95%8A%EB%8A%94-%ED%8C%8C%EC%9D%BC-%EC%98%AC%EB%A0%B8%EC%9D%84-%EB%95%8C</link>
            <guid>https://velog.io/@bigwave-cho/Git-%EC%8B%A4%EC%88%98%EB%A1%9C-%EC%9B%90%EC%B9%98-%EC%95%8A%EB%8A%94-%ED%8C%8C%EC%9D%BC-%EC%98%AC%EB%A0%B8%EC%9D%84-%EB%95%8C</guid>
            <pubDate>Fri, 03 Feb 2023 00:11:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>내 상황 </p>
</blockquote>
<ul>
<li>초반에 .env 파일을 생성해두고 gitignore에 .env를 등록해두었으나 어떤 이유에서인지 .env도 같이 github에 올라간 상황.</li>
<li>바로 확인 후 .env를 지우고 다시 push했지만 commit 내역에는 수정 기록이 남아있음.</li>
<li>바로 검색했음.</li>
<li><a href="https://minios.tistory.com/47">관련 해결 블로그</a></li>
</ul>
<ol>
<li>git rm [파일명] --cached (git 저장소에 파일 삭제)</li>
<li>gitignore에 .env 삭제</li>
<li>.env 삭제</li>
<li>add push</li>
<li>gitignore에 .env 추가</li>
<li>.env 추가</li>
</ol>
<hr>
<ul>
<li>하지만 아직 commit 내역이 남아 있음.
<code>git filter-branch --tree-filter &#39;rm -rf .env&#39; HEAD</code>
해당 파일의 커밋히스토리를 삭제해주고</li>
<li>git push origin main -f 강제푸시 해주고 저장소 확인해보면 
해당 파일 커밋 내역이 사라짐을 확인할 수 있다.</li>
</ul>
<blockquote>
<p>당연히 .env를 gitignore에 등록해두어서 올라가지 않았을 줄 알았는데
식은땀 흘리며 수정작업을 했다.</br>
설상가상으로 유료 문자 서비스의 토큰과 sid를 넣어둔터라 바로 해당 서비스 회사에서 메일이 날아왔고 당신 토큰이 
public에 노출되었으니 변경처리했고 빨리 바꾸라는 내용이었다.</br>
잘못했으면 애꿎은 무료 크레딧을 다 날릴뻔 했다.
꼭 이런 중요 파일들은 한번 더 체크하는 습관을 가지자. ㅠㅠ</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[라이브러리]React hook form]]></title>
            <link>https://velog.io/@bigwave-cho/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%ACReact-hook-form</link>
            <guid>https://velog.io/@bigwave-cho/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%ACReact-hook-form</guid>
            <pubDate>Tue, 31 Jan 2023 12:56:05 GMT</pubDate>
            <description><![CDATA[<h2 id="react-hook-form">React Hook Form</h2>
<h3 id="설치">설치</h3>
<p><code>npm install react-hook-form</code></p>
<h3 id="써보기">써보기!</h3>
<blockquote>
<p><a href="https://github.com/bigwave-cho/carrot-market/commit/d6cdd0db6f58ab4b0a24aceae79ed4a62e1a3c21?diff=split">커밋 링크 </a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Prisma]]></title>
            <link>https://velog.io/@bigwave-cho/Prisma</link>
            <guid>https://velog.io/@bigwave-cho/Prisma</guid>
            <pubDate>Mon, 30 Jan 2023 14:23:37 GMT</pubDate>
            <description><![CDATA[<h2 id="prisma">Prisma</h2>
<ol>
<li><p>Node.js and Typescript ORM(Object Relational Mapping)
=&gt; JS or TS 와 데이터베이스 사이에 다리를 놓아줌 (기본적으로 번역기의 역할을 한다고 생각하면 됨)</p>
</li>
<li><p>Prisma를 사용하기 위해서는 먼저 Prisma에게 DB가 어떻게 생겼는지, 데이터의 모양을 설명해줘야 함 =&gt; schema.prisma</p>
</li>
<li><p>Prisma가 이런 타입에 관한 정보를 알고 있으면 client를 생성해줄 수 있음. client를 이용하면 TS로 DB와 직접 상호작용 가능, 자동완성 제공.</p>
</li>
<li><p>Prisma Studio : Visual Database Browser, DB를 위한 관리자 패널같은 것.</p>
</li>
</ol>
<h2 id="prisma-만들기">Prisma 만들기</h2>
<blockquote>
<p><a href="https://www.prisma.io/docs/getting-started/quickstart">https://www.prisma.io/docs/getting-started/quickstart</a></p>
</blockquote>
<ol>
<li>VScode에서 prisma extension 설치</li>
<li><code>npm i prisma -D</code></li>
<li><code>npx prisma init</code> -&gt; 루트에 관련 폴더와 .env 생성됨<blockquote>
<p>Next steps: (npx prisma init시 나오는 설명)</p>
<ol>
<li>Set the DATABASE_URL in the .env file to point to your existing database. If your database has no tables yet, read <a href="https://pris.ly/d/getting-started">https://pris.ly/d/getting-started</a></li>
<li>Set the provider of the datasource block in schema.prisma to match your database: postgresql, mysql, sqlite, sqlserver, mongodb or cockroachdb.</li>
<li>Run prisma db pull to turn your database schema into a Prisma schema.</li>
<li>Run prisma generate to generate the Prisma Client. You can then start querying your database.</li>
</ol>
</blockquote>
</li>
</ol>
<blockquote>
<p>세팅 깃허브 커밋 링크</p>
</blockquote>
<h2 id="planet-scale">Planet scale</h2>
<p>PlanetScale
MySQL과 호환되는 Serverless 데이터베이스 플랫폼
<a href="https://planetscale.com/">https://planetscale.com/</a></p>
<p>Vitess
Vitess는 MySQL을 스케일링하기 위한 데이터베이스 클러스터링 시스템
인터넷에서 가장 큰 사이트를 호스팅하는 강력한 오픈 소스 기술입니다.
<a href="https://vitess.io/">https://vitess.io/</a></p>
<p>Vitess를 사용하는 이유</p>
<ol>
<li>수평 스케일</li>
<li>고가용성 (Vitess의 기본 복제본 구성은 예기치 않은 이벤트가 발생할 때 기본에서 복제본으로 원활한 장애 조치를 허용합니다.)</li>
<li>MySQL 호환</li>
<li>쿠버네티스 네이티브</li>
<li>구체화된 뷰</li>
<li>온라인 스키마 마이그레이션</li>
</ol>
<h3 id="install-planet-scale">install planet scale</h3>
<blockquote>
<p><a href="https://github.com/planetscale/cli">https://github.com/planetscale/cli</a></p>
</blockquote>
<p><code>brew install planetscale/tap/pscale</code>
<code>brew install mysql-client</code>
<code>brew upgrade pscale</code>(옵션 최신판 업뎃)</p>
<p>터미널에서 <code>pscale</code> 쳐서 설명 나오면 잘 설치된겨</p>
<ol>
<li><p>pscale auth login (로그인하기)</p>
</li>
<li><p>pscale region list (지역 리스트 확인)</p>
</li>
<li><p>pscale database create carrot-market --region ap-northe
ast (원하는 지역에 [carrot-market] 이름의 데이터베이스 생성(planet scale가면 생성된 것 확인 가능)</p>
</li>
<li><p>관리창에서 connect 클릭하면 username과 비번이 나오는데 이를 굳이 .env에서 관리할 필요가 없다.
터미널에서 <code>pscale connect carrot-market</code>
connect:Create a secure connection to a database and branch for a local client</p>
<ul>
<li>url이 나오는데 플레닛스케일 서버와 연결된 주소임.</li>
<li>.env에 `mysql://[url]/[database이름]</li>
<li>connect중인 터미널은 유지할 것.</li>
</ul>
<ol start="5">
<li>schema.pisma도 설정해주고 <code>npx prisma db push</code>
데이터베이스가 schema와 동기화됨</li>
<li>확인 : 플레닛 사이트가서 schema 탭확인하면 model User 확인.</li>
</ol>
<blockquote>
<p>깃 커밋 링크 (db구조 설명 및 schema와 동기화)</p>
</blockquote>
<h3 id="prisma-client">prisma client</h3>
<p><code>npx prisma studio</code> </p>
<ul>
<li>데이터베이스 관리자 패널창이 뜬다.</li>
<li>스튜디오가 schema.prisma를 읽고 User model을 확인해서 User를 관리할 수 있는 패널을 제공해줌.(추가 수정 가능)</li>
</ul>
<p><code>npm install @prisma/client</code></p>
<ul>
<li>libs 폴더에 client.ts에 PrismaClient객체 export하고</li>
<li>npx prisma generate하면 노드 모듈에 PrismaClient 생성</li>
</ul>
<blockquote>
<p>api이용해서 어떻게 쓰는지 등등 깃허브 커밋 링크!</p>
</blockquote>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NextJS] Redirects & Rewrites]]></title>
            <link>https://velog.io/@bigwave-cho/NextJS-Redirects-Rewrites</link>
            <guid>https://velog.io/@bigwave-cho/NextJS-Redirects-Rewrites</guid>
            <pubDate>Sat, 28 Jan 2023 16:23:30 GMT</pubDate>
            <description><![CDATA[<h2 id="redirects">redirects</h2>
<ul>
<li>redirects는 단순히 user가 해당 source 경로로 페이지 이동을 하게 되면 
그것을 감지하여 destination으로 redirect한다.</li>
<li>순간적으로 원래의 경로가 보이기 때문에 유저는 url의 변화를 눈치챌 수 있다.<pre><code class="language-js">async redirects() {
  return [
    {
      //step1. source 찾기
      source: &#39;/contact&#39;,
      // step2. 감지해서 dest로 보내기
      destination: &#39;/&#39;,
//permanent : 브라우저나 검색엔진이 이 정보를 기억하는지 여부를 결정.
      permanent: false,
    },
    {
      // * 붙여주면 뒤에 붙는 모든 path를 catch 가능.
      source: &#39;/old-blog/:path*&#39;,
      destination: &#39;/new-blog/:path*&#39;,
      permanent: false,
    },
  ];
},</code></pre>
</li>
</ul>
<h2 id="rewrites">rewrites</h2>
<pre><code class="language-js">async rewrites() {
    return [
      {  1번
//http://localhost:3000/api/movies
//여기 들어가보면 해당 api data 확인 가능.
        source: &#39;/api/movies&#39;,
        destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`,
      },
      {
        source: &#39;/api/movies/:id&#39;,
        destination: `https://api.themoviedb.org/3/movie/:id?api_key=${API_KEY}`,
      },
    ];
  },</code></pre>
<ul>
<li>1번 source로 이동한다고 가정
<code>http://localhost:3000/api/movies</code></li>
<li><blockquote>
<p>(rewrites) destination 주소로 감.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-js">// CSR의 경우
  useEffect(() =&gt; {
    (async function get() {
      const res = await (await fetch(`/api/movies`)).json();
      console.log(res.results);
    })();
  }, []);

// SSR의 경우
// 여기 코드는 서버에서만 실행
export async function getServerSideProps() {
  const { results } = await // 절대경로만 지원하기 때문에 URI 다 넣어야 함.
  (await fetch(`http://localhost:3000/api/movies`)).json();
  return {
    props: { results },
  };</code></pre>
<blockquote>
<p>Client side에서 직접 API_KEY를 이용해서 fetch를 하게 되면
API_KEY가 노출될 위험이 있기 때문에 rewrite를 통해서 키를 숨겨 보내기가 가능하다.
하지만 SSR을 이용한다면 rewrite로 굳이 키를 숨기지 않아도 서버단에서 요청이 다 이루어지기 때문에 노출될 위험이 없어진다.</p>
</blockquote>
<ul>
<li>단 CORS 문제를 해결하기 위해서 rewrites를 사용할 수 있다.(react proxy)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NextJS] Carrot Market 초기세팅!]]></title>
            <link>https://velog.io/@bigwave-cho/NextJS-Carrot-Market-%EC%B4%88%EA%B8%B0%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@bigwave-cho/NextJS-Carrot-Market-%EC%B4%88%EA%B8%B0%EC%84%B8%ED%8C%85</guid>
            <pubDate>Sat, 28 Jan 2023 15:53:25 GMT</pubDate>
            <description><![CDATA[<h2 id="create-next-app">Create-next-app</h2>
<ol>
<li><code>npx create-next-app --typescript</code></li>
<li>프로젝트 name 설정</li>
<li>src는 일단 No</li>
<li>code [프로젝트]</li>
</ol>
<h2 id="initial-settings">Initial Settings</h2>
<ol>
<li><p>깃 레포지토리와 연결</p>
</li>
<li><p><code>npm install -D tailwindcss postcss autoprefixer</code></p>
</li>
<li><p><code>npx tailwindcss init -p</code> -&gt; postcss.config &amp; tailwind.config.js 파일 생성됨.</p>
</li>
<li><p>tailwind.config 수정.=</p>
<pre><code class="language-js">module.exports = {
// tailwind가 어디서 사용될 것인지 설정
// pages 폴더 / 모든 디렉토리 / 모든 파일(해당확장자)
content: [
 &#39;./pages/**/*.{js,jsx,ts,tsx}&#39;,
 &#39;./components/**/*.{js,jsx,ts,tsx}&#39;,
],
theme: {
 extend: {},
},
plugins: [],
};</code></pre>
</li>
<li><p>/styles 에서 home.css 삭제, global.css 설정</p>
<pre><code class="language-js">@tailwind base;
@tailwind components;
@tailwind utilities;</code></pre>
</li>
<li><p><code>npm install -D prettier prettier-plugin-tailwindcss</code>
tailwind 스타일 적용 시 순서 알아서 바꿔줌.</p>
</li>
</ol>
<ul>
<li><p>추가적으로 vscode extension </p>
</li>
<li><p><em>Tailwind css Intellisense*</em> 깔면 자동 완성 등의 기능을 제공해줌. 편해 편해~</p>
<blockquote>
<ul>
<li>tailwind 적용 공식문서 참고!
<a href="https://tailwindcss.com/docs/guides/nextjs">https://tailwindcss.com/docs/guides/nextjs</a></li>
<li>각 라이브러리 &amp; 프레임웤에 대한 설치 방법도 제공하고 있음.
<a href="https://tailwindcss.com/docs/installation/framework-guides">https://tailwindcss.com/docs/installation/framework-guides</a></li>
</ul>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] FCM(안드만)]]></title>
            <link>https://velog.io/@bigwave-cho/Flutter-FCM%EC%95%88%EB%93%9C%EB%A7%8C</link>
            <guid>https://velog.io/@bigwave-cho/Flutter-FCM%EC%95%88%EB%93%9C%EB%A7%8C</guid>
            <pubDate>Thu, 26 Jan 2023 01:31:21 GMT</pubDate>
            <description><![CDATA[<h2 id="firebasecloudmessagingfcm">FirebaseCloudMessaging(FCM)</h2>
<blockquote>
<p>FCM은 비용 없이 안정적으로 메시지 전송이 가능한 플랫폼 간 메시징 솔루션.
<a href="https://firebase.google.com/docs/cloud-messaging?hl=ko">(firebase 공식문서)</a></p>
</blockquote>
<ul>
<li>FCM은 새 이메일 또는 기타 데이터를 동기화할 수 있음을 클라이언트 앱에 알릴 수 있음.</li>
<li>알림 메시지를 보내서 사용자 재참여 및 유지를 유도 가능</li>
<li>메시지는 최대 4,000바이트의 페이로드를 클라이언트 앱으로 전송 가능</li>
</ul>
<h2 id="fcm-plugin-설치">FCM plugin 설치</h2>
<p>필요: flutter용 firebase 플러그인 설치 및 초기화</p>
<ol>
<li><p><code>flutter pub add firebase_messaging</code></p>
</li>
<li><p><code>flutter pub add firebase_core</code></p>
</li>
<li><p><code>flutter pub add flutter_local_notifications</code></p>
</li>
<li><p>/android/app/src/main/AndroidManifest.xml</p>
<pre><code class="language-js">         &lt;!--애널리스틱스 수집, FCM 자동 초기화 비활성화--&gt;
     &lt;meta-data
         android:name=&quot;firebase_messaging_auto_init_enabled&quot;
         android:value=&quot;false&quot; /&gt;
     &lt;meta-data
        android:name=&quot;firebase_analytics_collection_enabled&quot;
        android:value=&quot;false&quot; /&gt;</code></pre>
<p>추가했음.</p>
</li>
<li><p>빌드</p>
<blockquote>
<p>❗️빌드 시 <strong>sdk version 오류</strong>가 떴음.
하라는 대로 app/build.gradle 찾아가서 
버전 바꿔주고 다시 빌드하니 에러 해결됨.</p>
</blockquote>
</li>
</ol>
<h3 id="테스트-알림-메시지-보내기">테스트 알림 메시지 보내기</h3>
<p>일단 아래와 같은 함수 파일을 하나 만들어 줬음.</p>
<pre><code class="language-js">import &#39;package:firebase_messaging/firebase_messaging.dart&#39;;
import &#39;dart:io&#39;;

Future&lt;String?&gt; getFcmToken() async {
  if (Platform.isIOS) {
    String? fcmKey = await FirebaseMessaging.instance.getToken();
    return fcmKey;
  }
  String? fcmKey = await FirebaseMessaging.instance.getToken();
  return fcmKey;
}
</code></pre>
<p>Home_screen을 StatefulWidget으로 변환했고 위 함수를 import
async 함수를 만들고 내부에서 getFcmToken함수를 호출 후 토큰 리턴 -&gt; initState에 넣어줌.</p>
<pre><code class="language-js">  void getToken() async {
    String? fcmkey = await getFcmToken();
    print(fcmkey); //fcmkey를 test에 추가해주고 쏘면 됨.
  }

  @override
  void initState() {
    super.initState();
    getToken();
  }</code></pre>
<p>print된 토큰을 테스트메시지에 추가해주고 테스트를 누르면 해당 기기로 메시지가 전송됨.</p>
<blockquote>
<p>참고 <a href="https://www.youtube.com/watch?v=jUpj13dZQQc">영상-background</a></p>
</blockquote>
<p><a href="https://www.youtube.com/watch?v=BVeLbS17G9A&amp;t=5s">foreground 영상</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] Flutter Firebase 연결]]></title>
            <link>https://velog.io/@bigwave-cho/Flutter-Flutter-Firebase-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@bigwave-cho/Flutter-Flutter-Firebase-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Wed, 25 Jan 2023 06:41:44 GMT</pubDate>
            <description><![CDATA[<h2 id="firebase-설치">Firebase 설치</h2>
<p><a href="https://firebase.google.com/docs/cli?hl=ko#mac-linux-auto-script">firebase docs - 설치</a></p>
<blockquote>
<ol>
<li>처음엔 npm으로 설치했지만 계속 zsh not found 에러가 떠서 자동설치 스크립트를 이용하여 설치했다.
<code>curl -sL https://firebase.tools | bash</code></li>
<li><code>firebase --version</code> 으로 설치 확인</li>
<li><code>firebase login</code> 후 계정 로그인</li>
<li><code>firebase projects:list</code>으로 생성된 프로젝트 리스트를 확인할 수 있다.</li>
</ol>
</blockquote>
<p>완료</p>
<h2 id="firebase-시작">Firebase 시작</h2>
<h3 id="flutter에-firebase-사용">flutter에 Firebase 사용</h3>
<blockquote>
<ul>
<li><a href="https://firebase.google.com/docs/flutter/setup?hl=ko&amp;platform=ios">공식문서 참고</a></li>
</ul>
</blockquote>
<ul>
<li><a href="https://kanoos-stu.tistory.com/72">참고 블로그 글</a></li>
</ul>
<h4 id="1단계--필요한-커맨드-라인-툴-설치">1단계 : 필요한 커맨드 라인 툴 설치</h4>
<ol>
<li>firebase CLI 설치는 위와 같이 하면 됨.</li>
<li><code>firebase login</code> - 로그인</li>
<li><code>dart pub global activate flutterfire_cli</code>
flutterfire 설치하고 flutterfire를 호출해봤는데 <strong>command not found 발생</strong>.</li>
</ol>
<ul>
<li><p>flutterfire not found
<a href="https://stackoverflow.com/questions/73835435/zsh-command-not-found-flutterfire">해결 참고 stackoverflow</a></p>
<ul>
<li>.zshrc에 넣으라는 export~~ 를 넣고 터미널을 재실행하고 이것저것 했는데도
flutterfire not found가 뜸.</li>
<li>그래서 해당 디렉토리에서 export~~ 를 입력하고 flutterfire를 입력하니
작동됨. 왜그럴까..?
<code>export PATH=&quot;$PATH&quot;:&quot;$HOME/.pub-cache/bin&quot;</code></li>
</ul>
<blockquote>
<p><strong>2시간 정도의 삽질</strong>을 통해 겨우 알아냈다.
<img src="https://velog.velcdn.com/images/bigwave-cho/post/5745a61f-158d-4fc2-952b-c0a985bb0b89/image.png" alt="">zshell을 사용한다면 .zshrc 파일을 통해 PATH(환경변수)를 관리하게 된다. 파일의 맨 밑 줄에 환경변수가 선언되어 있는데 환경변수는 
<code>export PATH=경로1:경로2</code> 이런식으로 여러개를 등록할 수 있다.
요약</p>
<ol>
<li><code>export PATH=$PATH:$HUME/.pub-cache/bin</code> 을 맨 밑에 추가</li>
<li>터미널) echo $PATH 
아래처럼 기존 경로에 경로가 추가된 결과가 나옴.
<code>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/jhmb/.pub-cache/bin</code> </li>
<li>flutterfire 쳐보면 명령어가 잘 실행됨을 알 수 있음.</li>
</ol>
<hr>
<p>❓❓<em><strong>직접 .zshrc를 만지지 않고 터미널 상에서 path등록 할 경우?</strong></em></p>
<ul>
<li><p>최우선순위로 등록
export PATH=&quot;/NEW/PROGRAM/RUN/PATH:$PATH&quot;</p>
</li>
<li><p>최하위 우선순위로 등록
export PATH=&quot;$PATH:/NEW/PROGRAM/RUN/PATH&quot;
</br>최우선은 경로 맨 앞에 최하위는 경로 맨 뒤에 추가되게 되는데
주의할 점은 이렇게 하면 <strong>임시적으로 등록</strong>하는 것이기 때문에 터미널을 껏다 키게 되면 <strong>환경변수가 초기화</strong>되기 때문에 명령어가 먹지 않는다는 점이다.
<a href="https://passwd.tistory.com/entry/PATH-%EC%B6%94%EA%B0%80-%EB%93%B1%EB%A1%9D">환경변수에 대한 참고글 링크</a></p>
</li>
</ul>
</blockquote>
</li>
</ul>
<h4 id="2단계--fb-사용하기-위해-앱-구성configure하기">2단계 : FB 사용하기 위해 앱 구성(configure)하기</h4>
<p><code>flutterfire configure</code></p>
<ul>
<li>기존 Firebase project를 사용하거나 새 Firebase 프로젝트를 생성하도록 선택 가능.</li>
<li>기존 firebase 프로젝트에 이미 등록된 앱이 있으면 Flutter CLI는 현재 Flutter 프로젝트 구성을 기반으로 앱을 일치시키려 시도함.</li>
</ul>
<blockquote>
<p><a href="https://github.com/invertase/flutterfire_cli/issues/127">https://github.com/invertase/flutterfire_cli/issues/127</a>
이런 에러가 발생. ios 설정을 안해줘서 그렇다는데.. xcode 깔아보자.
-&gt; xocode, cocoapods 깔고 했는데 똑같음.
-&gt; 밑에 쓰레드 보니까 <code>sudo gem install xcodeproj</code>해서 해결됐다고 함. -&gt; 해결. ㅠㅠ</p>
</blockquote>
<h4 id="3단계--앱에서-firebase-시작하기">3단계 : 앱에서 Firebase 시작하기</h4>
<ol>
<li><p>핵심 플러그인 설치</p>
<ul>
<li><code>flutter pub add firebase_core</code></li>
</ul>
</li>
<li><p>Firebase 구성 최신 확인</p>
<ul>
<li><code>flutterfire configure</code></li>
</ul>
</li>
<li><p>lib/main.dart 파일에 import</p>
<pre><code class="language-js">import &#39;package:firebase_core/firebase_core.dart&#39;;
import &#39;firebase_options.dart&#39;;</code></pre>
</li>
<li><p>main.dart에 코드 추가</p>
<pre><code class="language-js">void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 위 코드는 문서에 없는 코드.. 바인딩 에러 뜸.
await Firebase.initializeApp(
 options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const App());
}</code></pre>
<p><a href="https://stackoverflow.com/questions/57689492/flutter-unhandled-exception-servicesbinding-defaultbinarymessenger-was-accesse">여기서 해결법 찾음.</a></p>
</li>
</ol>
<h4 id="4단계--플러그인-추가-사용-가능">4단계 : 플러그인 추가 사용 가능!</h4>
<p><a href="https://firebase.google.com/docs/flutter/setup?platform=ios&amp;hl=ko#add-plugins">사용방법 및 플러그인 종류</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] Toonflix]]></title>
            <link>https://velog.io/@bigwave-cho/Flutter-Toonflix</link>
            <guid>https://velog.io/@bigwave-cho/Flutter-Toonflix</guid>
            <pubDate>Tue, 24 Jan 2023 06:58:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://github.com/bigwave-cho/toonflix"><strong>완성본 깃허브 링크</strong></a></p>
</blockquote>
<h2 id="appbar">AppBar</h2>
<h3 id="scaffold">Scaffold</h3>
<ul>
<li>screen을 위한 기본적 레이아웃과 설정을 제공한다.</li>
</ul>
<h2 id="data-fetching">Data Fetching</h2>
<blockquote>
<p>flutter에서 fetch를 하기 위해서는 http라는 패키지를 설치해야 한다.</p>
</blockquote>
<ul>
<li><p>설치방법</p>
<ul>
<li><a href="https://pub.dev/">https://pub.dev/</a></li>
<li>http 검색해서 해당 페이지 가보면</li>
<li><a href="https://pub.dev/packages/http">https://pub.dev/packages/http</a>
어떻게 설치하고 쓰는지 잘 나와있음.</li>
</ul>
</li>
<li><p>pubspec.yaml 으로 설치하는 방법을 해보자</p>
<ul>
<li>위 파일은 package.json 같은거라 보면 됨.</li>
<li><code>dependencies:
http: ^0.13.5</code> 디펜던시에 복붙, 저장</li>
<li>저장했는데 자동 다운로드 안되면?</li>
<li><blockquote>
<p>아래 사진에 패키지 다운로드 버튼 클릭.
<img src="https://velog.velcdn.com/images/bigwave-cho/post/11c904cd-4529-4c06-b3bc-efdc8dfcbcb4/image.png" alt=""></p>
</blockquote>
</li>
</ul>
<p>🪓 계속 <code>Flutter http package does not exist</code> 에러가 떴는데 vscode와 안드로이드 스튜디오 재실행하니 사라졌음.</p>
<h3 id="get-data">get data</h3>
</li>
</ul>
<pre><code class="language-js">import &#39;dart:convert&#39;;

import &#39;package:http/http.dart&#39; as http;
import &#39;package:webflix/models/webtoon_model.dart&#39;;

class ApiService {
  final String baseUrl = &#39;https://webtoon-crawler.nomadcoders.workers.dev&#39;;
  final String today = &quot;today&quot;;

  Future&lt;List&lt;WebtoonModel&gt;&gt; getTodaysToons() async {
    List&lt;WebtoonModel&gt; webtoonInstances = [];

    final url = Uri.parse(&#39;$baseUrl/$today&#39;);
    //get은 future를 반환(Pormise) - async await
    // Future&lt;Response&gt; get()
    // get함수는 Future타입을 반환; Future는 지금이 아닌 나중에 완료된다는 뜻
    // 완료되면 Response 타입을 반환할 예정.
    // http 요청 완료 기다리게 하기.
    final response = await http.get(url);
    if (response.statusCode == 200) {
      //response.body는 배열로 들어오며 요소 타입은 확실치 않아서 dynamic

      final List&lt;dynamic&gt; webtoons = jsonDecode(response.body);

      for (var webtoon in webtoons) {
        //webtoonModel클래스의 fromJson 컨트트럭터에 의해
        // 인스턴스화 된 데이터들이 차례대로 webtoonInstatnces 배열에 추가됨.
        webtoonInstances.add(WebtoonModel.fromJson(webtoon));
      }
      return webtoonInstances;
    }
    throw Error();
  }
}</code></pre>
<blockquote>
<p>데이터 정제 하기</p>
</blockquote>
<pre><code class="language-js">class WebtoonModel {
  final String title, thumb, id;

// named constructor 이용해서 받아온 json 데이터로 위 프로퍼티들 초기화.
  WebtoonModel.fromJson(Map&lt;String, dynamic&gt; json)
      : title = json[&#39;title&#39;],
        thumb = json[&#39;thumb&#39;],
        id = json[&#39;id&#39;];
}</code></pre>
<h3 id="home_screen에서-써먹기">home_screen에서 써먹기</h3>
<pre><code class="language-js">// StatefulWidget 인 상태
List&lt;WebtoonModel&gt; webtoons = [];
  bool isLoading = true;

  void waitForWebtoons() async {
    //1. ApiService클래스의 getTodaysToons() 메서드를 호출하여 webtoons에 할당
    //2. isLoaidng을 false로 그리고 setState로 build호출
    webtoons = await ApiService.getTodaysToons();
    isLoading = false;
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    waitForWebtoons();
  }</code></pre>
<blockquote>
<p>하지만 isLoading 등을 수동으로 할당해줘야 하고 state를 사용하는 것을 최대한 지양해야 하기 때문에 리팩터링이 필요함.</p>
</blockquote>
<pre><code class="language-js">//StatelessWidget으로 다시 변경
 Future&lt;List&lt;WebtoonModel&gt;&gt; webtoons = ApiService.getTodaysToons();

  @override
  Widget build(BuildContext context) {
    // print(webtoons); //Instance of &#39;Future&lt;List&lt;WebtoonModel&gt;&gt;
//원래라면 Future타입은 asnyc/await으로 기다려야 하지만 이를 위한 widget 존재
// &#39;FutureBuilder&#39;

---Scaffold에 body추가
body: FutureBuilder(
          //future 기다려라
          future: webtoons,
          builder: (context, snapshot) {
            //snapshot : Future상태 감지
            if (snapshot.hasData) {
              return const Text(&quot;There is data!&quot;);
            }
            return const Text(&#39;Loading...&#39;);
          },
</code></pre>
<h3 id="listview-widget">ListView Widget</h3>
<pre><code class="language-js"> return ListView(
   children: [
    for (var webtoon in snapshot.data!) Text(webtoon.title)
                ],
              );</code></pre>
<ul>
<li>ListView는 요소들을 목록으로 나타냄.</li>
<li>하지만 최적화가 안되어있다.(한 번에 모든 아이템 로딩)</li>
</ul>
<h4 id="listviewbuilder">ListView.builder</h4>
<pre><code class="language-js"> body: FutureBuilder(
          //future 기다려라
          future: webtoons,
          builder: (context, snapshot) {
            //snapshot : Future상태 감지
            if (snapshot.hasData) {
              // ListView.builder는 사용자가 보는 것만 로드하고
              //안보는 것들은 메모리에서 제외시킨다.
              return ListView.builder(
                itemBuilder: (context, index) {
                  //print(index); // 스크롤해보면 해당 아이템의 index가 디버그 콘솔에 나타남.
                  //index: 어떤 아이템이 빌드되는지 알 수 있는 요소
                  var webtoon = snapshot.data![index];

                  return Text(webtoon.title);
                },
                scrollDirection: Axis.horizontal,
                itemCount: snapshot.data?.length,
              );</code></pre>
<h4 id="listviewseparated">ListView.separated</h4>
<ul>
<li>separated는 <code>separatorBuilder</code>를 필수로 가짐.</li>
<li>separatorBuilder는 리스트 사이에 추가해줄 위젯을 리턴.</li>
</ul>
<pre><code class="language-js">separatorBuilder: (context, index) {
      return const SizedBox(
                    width: 20,
                  );
                },</code></pre>
<h4 id="detail-screen">Detail Screen</h4>
<ul>
<li>detail_screen.dart<pre><code class="language-js">//&#39;id&#39;를 사용할 수 없다.
//ApiService에서 id를 사용하려할 때 id 또한 이니셜라이징되기 때문.
// 그래서 id가 초기화되기를 기다리고 난 후 에 id를 사용할 수 있다.
// 그래서 statfulWidget으로 변경!
Future&lt;WebtoonDetailModel&gt; webtoon = ApiService.getToonById(id);</code></pre>
</li>
</ul>
<h3 id="url-launcher"><a href="https://pub.dev/packages/url_launcher">URL launcher</a></h3>
<ul>
<li>앱에서 링크 클릭 시 웹사이트 이동.</li>
</ul>
<h3 id="shared_preferences"><a href="https://pub.dev/packages/shared_preferences">shared_preferences</a></h3>
<ul>
<li>중요데이터 말고 간단한 데이터를 기기에 저장해놓고 읽을 수 있게 하는 패키지.</li>
</ul>
<h3 id="flutter-structure에-대한-설명"><a href="https://www.geeksforgeeks.org/flutter-file-structure/">flutter structure에 대한 설명</a></h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] Pomodoro 앱 만들기]]></title>
            <link>https://velog.io/@bigwave-cho/Flutter-Pomodoro-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@bigwave-cho/Flutter-Pomodoro-%EC%95%B1-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 23 Jan 2023 07:53:56 GMT</pubDate>
            <description><![CDATA[<h3 id="ui">UI</h3>
<h4 id="flexible">flexible</h4>
<ul>
<li>픽셀로 지정하면 기기마다 유연한 UI 대응을 하지 못하기 때문에 flexible을 이용하면 알아서 비율을 맞춰준다.</li>
<li>flex: 로 비율 정해주기 가능.<pre><code class="language-js">Widget build(BuildContext context) {
  return Scaffold(
    body: Column(
      children: [
        //Flexible UI를 비율에 맞춰 유연하게 만들어 줌.
        Flexible(
          flex: 1,
          child: Container(
            decoration: const BoxDecoration(
              color: Colors.red,
            ),
          ),
        ),
        Flexible(
          flex: 3,
          child: Container(
            decoration: const BoxDecoration(
              color: Colors.green,
            ),
          ),
        ),
        Flexible(
          flex: 1,
          child: Container(
            decoration: const BoxDecoration(
              color: Colors.blue,
            ),
          ),
        )
      ],
    ),
  );
}</code></pre>
</li>
</ul>
<h4 id="expanded">Expanded</h4>
<ul>
<li>영역을 꽉 채우도록 하는 위젯</li>
</ul>
<h3 id="timer">Timer</h3>
<pre><code class="language-js">  int totalSeconds = 1500;

  //dart:asnyc 클래스임.-&gt;  import &#39;dart:async&#39;;
  late Timer timer;

  void onTick(Timer timer) {
    setState(() {
      totalSeconds--;
    });
  }

  void onStartPressed() {
    timer = Timer.periodic(
      //1초마다 onTick을 실행
      const Duration(seconds: 1),
      onTick,
    );
  }</code></pre>
<h3 id="pause-play">pause play</h3>
<pre><code class="language-js">  bool isRunning = false;


  void onStartPressed() {
    setState(() {
      isRunning = true;
    });
    timer = Timer.periodic(
      const Duration(seconds: 1),
      onTick,
    );
  }

  void onPausePressed() {
    // timer 취소 메서드
    timer.cancel();

    setState(() {
      isRunning = false;
    });
  }</code></pre>
<p>isRunning에 따라 작동 함수와 표시 아이콘을 삼항조건문으로 처리.</p>
<pre><code class="language-js">              onPressed: () {
                  isRunning ? onPausePressed() : onStartPressed();
                },
                icon: Icon(isRunning
                    ? Icons.pause_circle_outline
                    : Icons.play_circle_outline),
              ),</code></pre>
<h3 id="date-format">Date Format</h3>
<ul>
<li>0초되면 리셋<pre><code class="language-js">static const twentyFiveMinutes = 1500;
int totalSeconds = twentyFiveMinutes;
</code></pre>
</li>
</ul>
<p>void onTick(Timer timer) {
    if (totalSeconds == 0) {
      setState(() {
        totalPomodoros++;
        isRunning = false;
        totalSeconds = twentyFiveMinutes;
      });
      timer.cancel();
    } else {
      setState(() {
        totalSeconds--;
      });
    }
  }</p>
<pre><code>#### date Format
- 1500을 25:00으로 바꾸기

```js
// 기존 
child: Text(
                &#39;$totalSeconds&#39;,
                style: TextStyle(
                  color: Theme.of(context).cardColor,
                  fontSize: 89,
                  fontWeight: FontWeight.w600,
                ),

</code></pre><pre><code class="language-js">$totalSeconds-&gt;
 child: Text(
         format(totalSeconds),
         style: TextStyle(  
  --- format 함수 ----

    String format(int seconds) {
    var duration = Duration(seconds: seconds);
    var minutes = duration.toString().split(&#39;.&#39;)[0].split(&#39;:&#39;)[1];
    var second = duration.toString().split(&#39;.&#39;)[0].split(&#39;:&#39;)[2];
    return &#39;$minutes:$second&#39;;
  }
</code></pre>
<blockquote>
<p> <a href="https://github.com/bigwave-cho/start-flutter">완성본 깃헙 링크</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] Stateful Widget]]></title>
            <link>https://velog.io/@bigwave-cho/Flutter-Stateful-Widget</link>
            <guid>https://velog.io/@bigwave-cho/Flutter-Stateful-Widget</guid>
            <pubDate>Mon, 23 Jan 2023 04:56:55 GMT</pubDate>
            <description><![CDATA[<h2 id="stateful-widget">Stateful Widget</h2>
<blockquote>
<p>Stateful Widget은 변경 가능한 위젯이다.</p>
</blockquote>
<ul>
<li><p>state는 위젯이 빌드될 때 동기적으로 읽을 수 있고 위젯의 lifecycle동안 변경 가능한 정보이다.</p>
</li>
<li><p>Stateful 위젯은 두 가지로 나뉜다.</p>
</li>
</ul>
<ol>
<li>상태가 없는 위젯</li>
<li>state 위젯은 위젯에 들어갈 데이터와 UI를 넣는 곳
(데이터가 변경되면 해당 위젯도 변경된다.)</li>
</ol>
<p>기본 코드 구성</p>
<pre><code class="language-js">@immutable
class App extends StatefulWidget {
  const App({super.key});

  @override
  State&lt;App&gt; createState() =&gt; _AppState();
}

class _AppState extends State&lt;App&gt; {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: const Color(0xFFF4EDDB),
        body: Container(),
      ),
    );
  }
}</code></pre>
<blockquote>
<p>Stateful로 바꾸고 
<img src="https://velog.velcdn.com/images/bigwave-cho/post/ed61e8d7-15a9-4547-a850-3390288ffc96/image.png" alt="">
이런 에러가 발생했다. 
<a href="https://nkaushik.com/flutter/flutter-type-myapp-is-not-subtype-of-type-statelesswidget/#:~:text=If%20you%20make%20changes%20in%20the%20main()%20or%20initState()%20method%2C%20hot%20reload%20will%20fail.%20These%20methods%20are%20not%20re%2Dexecuted%20with%20hot%20reload.%20Hence%2C%20it%20will%20fail.%20You%20need%20a%20hot%20restart.">이 글을 보고 해결했음.</a></p>
</blockquote>
<ul>
<li>main()이나 initState() 메서드 내에서 변경을 했을 때 핫 리로드가 실패하는 현상으로 VM을 재부팅하여 다시 앱을 실행하면 해결됨.</li>
</ul>
<h3 id="setstate">setState()</h3>
<pre><code class="language-js">  int counter = 0;

  void onCliked() {
    // 단순히 아래처럼 counter의 값을 변경하는 것은
    // 플러터에서 알아차리지 못함.
    // counter = counter + 1;

// react처럼 setState 함수를 이용해서 알려줘야 함.
// 무조건 counter ++; 이 setState 내에 있어야 하는 것은 아니고
// setState함수가 실행되면서 build메서드를 실행시킴.
    setState(() {
      counter++;
    });
  }</code></pre>
<h3 id="recap">Recap</h3>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/dc9f9659-b6bb-4ebe-a5ea-248548c2cdeb/image.png" alt=""></p>
<pre><code class="language-js">  List&lt;int&gt; numbers = [];
  void onCliked() {
    setState(() {
      numbers.add(numbers.length);
    });
  }

  //build
  for (var n in numbers) Text(&#39;$n&#39;),</code></pre>
<blockquote>
<p>flutter에서는 생각보다 state가 많이 사용되지 않음.</p>
</blockquote>
<h3 id="build-context">build context</h3>
<ul>
<li>context는 부모 요소들의 모든 정보를 담고 있다.</li>
<li>buildContext는 위젯 트리에서 위젯의 위치를 제공</li>
<li>이를 통해 상위 요소 데이터에 접근 가능<pre><code class="language-js">Widget build(BuildContext context) {
  return MaterialApp(
    theme: ThemeData(
      textTheme: const TextTheme(
        titleLarge: TextStyle(
          color: Colors.red,
        ),
      ),
    ),

</code></pre>
</li>
</ul>
<p>// 자식 MyLargeTitle
//MyLargeTitle은 MaterialApp의 자식이고
//부모의 ThemeData를 사용하기 위해서 context를 사용.
  Widget build(BuildContext context) {
    return Text(
      &#39;My Large title&#39;,
      style: TextStyle(
        fontSize: 30,
        // context로 부모 요소에 접근하여 데이터를 가져오는 방법.
        color: Theme.of(context).textTheme.titleLarge?.color,
      ),
    );</p>
<pre><code>
### Widget LifeCycle
#### initState
- 위젯이 처음 생성될 때 한 번 호출되는 메서드

```js
class _MyLargeTitleState extends State&lt;MyLargeTitle&gt; {
  //initState를 사용하지 않고 아래처럼 해도 됨.
  //근데도 initState를 사용하는 상황
  // 부모 요소에 의존하는 데이터를 초기화해야 하는 경우
  // API에서 데이터를 받아와 이니셜라이즈.
  // 그 때 build 메서드보다 initState가 항상 먼저 호출 됨.
  int count = 0;

  //상태를 초기화하기 위한 메서드(옵션)
  @override
  void initState() {
    super.initState();
    print(&#39;initState!&#39;);
  }

  @override
  Widget build(BuildContext context) {
    print(&#39;build!&#39;);

    return Text(
      &#39;My Large title&#39;,
      style: TextStyle(
        fontSize: 30,
        // context로 부모 요소에 접근하여 데이터를 가져오는 방법.
        color: Theme.of(context).textTheme.titleLarge?.color,
      ),
    );
  }
}</code></pre><ul>
<li>initState가 먼저 호출되는 것 증명
<img src="https://velog.velcdn.com/images/bigwave-cho/post/521b4e36-9550-4a9f-b259-b1aa58c68893/image.png" alt=""></li>
</ul>
<h4 id="dispose">dispose</h4>
<ul>
<li>위젯이 제거될 때 호출되는 메서드<ul>
<li>API업데이트나 이벤트 리스너 구독을 취소하거나</li>
<li>form의 리스너로부터 벗어나고 싶을 때 사용.</li>
</ul>
</li>
</ul>
<blockquote>
<p>위젯이 제거되기 전에 실행하고 싶은 코드를 기입.</p>
</blockquote>
<pre><code class="language-js">// 토글 만들어주고.
class _AppState extends State&lt;App&gt; {
  bool showTitle = true;

  void toggleTitle() {
    setState(() {
      showTitle = !showTitle;
    });

// 
showTitle ? const MyLargeTitle() : const Text(&#39;nothing&#39;),
              IconButton(
                  onPressed: toggleTitle,
                  icon: const Icon(Icons.remove_red_eye))
  }</code></pre>
<p>토글 눌러서 MyLargeTitle()이 제거될 때 디버그 콘솔에 dispose가 출력.</p>
<blockquote>
<p>요약
stateful widget은 lifecycle을 가지고 있다.
가장 중요한 메서드는 initState와 dispose, build이다.</p>
</blockquote>
<ul>
<li>build: UI 생성</li>
<li>initState : build 이전에 호출되는 메서드</li>
<li>dispose : 위젯이 위젯트리에서 제거될 때 호출</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] Stateless Widget]]></title>
            <link>https://velog.io/@bigwave-cho/Flutter-%ED%94%8C%EB%9F%AC%ED%84%B0-%EC%8B%9C%EC%9E%91</link>
            <guid>https://velog.io/@bigwave-cho/Flutter-%ED%94%8C%EB%9F%AC%ED%84%B0-%EC%8B%9C%EC%9E%91</guid>
            <pubDate>Mon, 23 Jan 2023 02:58:15 GMT</pubDate>
            <description><![CDATA[<h2 id="hello-world">Hello World.</h2>
<pre><code class="language-js">void main() {
  //runApp은 void이며 하나의 Widget타입 인자를 필요로 함.
  //Widget은 react의 컴포넌트라고 보면 됨.
  //플러터 docs에는 widget of the week라는 카테고리에서
  // 여러 공식 위젯을 소개해줌.

  // App은 root widget이라고 보면 됨.
  runApp(App());
}</code></pre>
<h3 id="widget을-만드는-방법">Widget을 만드는 방법</h3>
<ul>
<li><p>가장 기초적 Widget: StatelessWidget</p>
</li>
<li><p>필요 개념 <a href="https://velog.io/@bigwave-cho/Dart-%EB%8B%A4%ED%8A%B8-%EA%B8%B0%EC%B4%88#:~:text=%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4%20%EB%90%A8.-,Classes,-class%20%EA%B8%B0%EB%B3%B8"><strong>classes 링크</strong></a></p>
<pre><code class="language-js">class App extends StatelessWidget {
// StatelessWidget의 build 메서드를 구현하라는 에러
//build메서드가 return하는 것을 화면에 보여줌.

@override
Widget build(BuildContext context) {
  //root widget은 두 개의 옵션 중 하나를 선택해야 함.
  // 1. material 앱을 return(구글 디자인시스템)
  //2. cupertino 앱을 return(애플 디자인시스템)

  // scaffold (구조를 정렬해줌.)
  return MaterialApp(
    //home: 사용자가 앱의 home에 있을 때 보여짐
    home: Scaffold(
      appBar: AppBar(
        // named parameter 사용하고 있는 것.
        centerTitle: true,
        title: Text(&#39;Hello flutter!&#39;),
  //Text의 data 프로퍼티만 필수이기 때문에 positional parameter 사용중.
 //(Text에 커서 올려보면 나머지는 옵셔널 표시)
      ),
      body: Center(
        child: Text(&quot;hello world!&quot;),
      ),
    ),
  );
}
}</code></pre>
<blockquote>
<p>❗️😽<strong>중요 포인트</strong>
모든 Widget은 클래스의 인스턴스!
각 위젯에 커서를 올려보며 required와 optional을 볼 수 있으며 required 파라미터를 넣지 않으면 에러가 뜬다.</p>
</blockquote>
</li>
</ul>
<h2 id="ui-구성해보기">UI 구성해보기!</h2>
<h3 id="header">Header</h3>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/93381eb3-0f25-45f8-88bd-25ecac1ecca8/image.png" alt=""></p>
<blockquote>
</blockquote>
<ul>
<li>padding</li>
<li>row, column</li>
<li>sizedBox</li>
<li>Text</li>
</ul>
<pre><code class="language-js">class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(

////Color.fromRGBO(255, 255, 255, 0.8),
          backgroundColor: Color(0xFF181818),
          // Padding 위젯
          body: Padding(
            // EdgeInsets.all(모두) , only(r,b...)
            padding: EdgeInsets.symmetric(
              horizontal: 40,
            ),
            child: Column(
              children: [
                //SizedBox 공간 창출(padding 같음)
                SizedBox(
                  height: 80,
                ),
                Row(
                  //mainAxisAlignment: Row의 main은 수평
                  // colum의 메인은 수직 방향.
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: [
                    Column(
                      crossAxisAlignment: CrossAxisAlignment.end,
                      children: [
                        Text(
                          &quot;Hey,Selena&quot;,
                          style: TextStyle(
                            fontSize: 28,
                            fontWeight: FontWeight.w800,
                            color: Colors.white,
                          ),
                        ),
                        Text(
                          &quot;welcome back&quot;,
                          style: TextStyle(
                            color: Colors.white.withOpacity(0.8),
                            fontSize: 18,
                          ),
                        ),
                      ],
                    )
                  ],
                )
              ],
            ),
          )),
    );
  }
}</code></pre>
<h3 id="developer-tools">Developer Tools</h3>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/e17837c4-6e01-460b-8c46-c16c575fa8c2/image.png" alt="">
레이아웃이 어떻게 적용되고 있는지 궁금할 헷갈릴 때는 파란색 돋보기를 누르면 widget inspector 창 나옴</p>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/d1ff26c8-2656-4d19-8895-48e349fc1ba9/image.png" alt=""></p>
<ul>
<li>맨 왼쪽 커서 : 누르고 에뮬레이터 요소 클릭하면 해당 요소 검사 가능</li>
<li>|&lt;-&gt;| : 전체적 레이아웃 표시해줌.</li>
</ul>
<h3 id="buttons">Buttons</h3>
<pre><code class="language-js">    Row(
             children: [
               Container(
                 decoration: BoxDecoration(
                     color: Colors.amber,
                       borderRadius: BorderRadius.circular(45)),
                      child: Padding(
                        padding: EdgeInsets.symmetric(
                          vertical: 20,
                          horizontal: 45,
                        ),
                        child: Text(
                          &#39;Transfer&#39;,
                          style: TextStyle(
                            fontSize: 20,
                          ),
                        ),
                      ),
                    )
                  ],
                )</code></pre>
<h3 id="vscode-세팅">VSCODE 세팅</h3>
<blockquote>
<p>prefer const with constant constructors</p>
</blockquote>
<ul>
<li><a href="https://velog.io/@bigwave-cho/Dart-%EB%8B%A4%ED%8A%B8-%EA%B8%B0%EC%B4%88#:~:text=%EB%B0%A9%EC%A7%80%EC%97%90%EB%8F%84%20%EC%9C%A0%EC%9A%A9%ED%95%98%EB%8B%A4.-,Constant%20Variables,-JS%EC%9D%98%20const">const 고정된 상수(이미 알고 있는)</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/4b12c2a0-aed1-43ef-99bc-84e534c2cbab/image.png" alt="">
=<img src="https://velog.velcdn.com/images/bigwave-cho/post/d0b10c3a-4a71-4dad-9d16-ed3803ca8dfa/image.png" alt=""></p>
<ul>
<li>하지만 모든 것을 const 지정해주는 것은 실수를 유발할 수 있다. 따라서 세팅을 해보자.</li>
</ul>
<blockquote>
<ol>
<li>const 자동 지정</li>
</ol>
</blockquote>
<pre><code class="language-js">// (command palette) open user setting
// 아래 코드 추가
  &quot;editor.codeActionsOnSave&quot;: {
    &quot;source.fixAll&quot;: true
  }</code></pre>
<ul>
<li>파란줄 떴던 것들 앞에 const 타입 지정이 알아서 추가됨.</li>
</ul>
<blockquote>
<ol start="2">
<li>부모/자식 위젯 표시</li>
</ol>
</blockquote>
<pre><code class="language-js"> &quot;dart.previewFlutterUiGuides&quot;: true</code></pre>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/41c91352-f525-4b8c-887c-a70850a407a9/image.png" alt=""></p>
<h3 id="code-actions">Code Actions</h3>
<blockquote>
<p>코드 왼쪽 전구 모양 클릭하면!</p>
</blockquote>
<pre><code class="language-js">//
Text(
          &#39;Total Balance&#39;,
             style: TextStyle(
             fontSize: 22,
             color: Colors.white.withOpacity(0.8),
                  ),
        ),</code></pre>
<p>Text위젯을 Container 위젯으로 감싸고 싶다거나 할 때 클릭만으로 간단하게 자동으로 변경해준다.</p>
<pre><code class="language-js">Container(
                  child: Text(
                    &#39;Total Balance&#39;,
                    style: TextStyle(
                      fontSize: 22,
                      color: Colors.white.withOpacity(0.8),
                    ),
                  ),
                ),</code></pre>
<ul>
<li>부모 위젯 없애는 remove widget도 가능.</li>
</ul>
<h3 id="재사용-가능한-위젯">재사용 가능한 위젯</h3>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/9d1da63f-a25b-4fee-a2a7-4e258423d636/image.png" alt=""></p>
<ul>
<li>중복되는 두개의 Container가 있고 이 Container를 두 번 반복하여 사용하는 것은 코드 낭비.</li>
<li>따라서 위젯으로 만들어서 재사용할 수 있도록 하자</li>
</ul>
<ol>
<li>(자동)Container의 전구를 클릭하여 <em><strong>Extract Widget</strong></em>
<img src="https://velog.velcdn.com/images/bigwave-cho/post/9401d37a-b46c-429a-bc99-16c245d66551/image.png" alt=""></li>
</ol>
<ul>
<li>그럼 이렇게 위젯으로 만들어 줌.</li>
</ul>
<ol start="2">
<li>직접 만들기</li>
</ol>
<ul>
<li>lib폴더에 widget폴더를 만들고 button.dart파일을 생성하자.</li>
</ul>
<ol start="3">
<li><p>위젯 생성!
<img src="https://velog.velcdn.com/images/bigwave-cho/post/636955cc-40d4-4d9b-b862-4fc3d6da201d/image.png" alt=""></p>
</li>
<li><p>main.dart에서 Button 입력하면 자동으로 받아와줌.
<img src="https://velog.velcdn.com/images/bigwave-cho/post/c28c9a7d-d9b4-402f-b09d-ab62c7eb4c87/image.png" alt=""></p>
</li>
</ol>
<h3 id="icons--transform">Icons &amp; Transform</h3>
<ul>
<li><p>Icon 위젯</p>
<pre><code class="language-js">    const Icon(
      //아이콘 이름
           Icons.euro_symbol_rounded,
      // 아이콘 색깔
           color: Colors.white,
      //아이콘 사이즈
           size: 98,
       )</code></pre>
<p>사이즈를 키우면 사이즈에 따라 카드의 넓이도 함께 증가한다. 카드 넓이는 그대로 아이콘의 크기만 키우고 싶을 때 <strong>Transform을</strong> 사용한다.</p>
</li>
<li><p>Transform 적용
<img src="https://velog.velcdn.com/images/bigwave-cho/post/850f08e0-130b-4efb-afc2-18e6b42e0e74/image.png" alt=""></p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/3225fe2f-a55b-4cdd-957b-47ba48279348/image.png" alt=""></p>
<ul>
<li>Transform.scale</li>
<li>Transform.translate</li>
</ul>
<p><img src="blob:https://velog.io/3b3981b9-cbc4-433a-bd51-48d9ec7d0c7d" alt="업로드중.."></p>
<blockquote>
<p>stateless 위젯 완성본!
<a href="https://github.com/bigwave-cho/start-flutter">코드 참고하기 Github Link</a></p>
</blockquote>
<p><img src="blob:https://velog.io/e7bffbe1-6e1d-41ba-a933-03bdd0b9a051" alt="업로드중.."></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Flutter] 개요와 설치방법]]></title>
            <link>https://velog.io/@bigwave-cho/Flutter-%EA%B0%9C%EC%9A%94%EC%99%80-%EC%84%A4%EC%B9%98%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@bigwave-cho/Flutter-%EA%B0%9C%EC%9A%94%EC%99%80-%EC%84%A4%EC%B9%98%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sun, 22 Jan 2023 10:17:57 GMT</pubDate>
            <description><![CDATA[<h2 id="플러터가-어떻게-작동하는가">플러터가 어떻게 작동하는가?</h2>
<ul>
<li>다트는 네이티브 언어가 아니다.</li>
<li>네이티브 언어는 안드로이드나 IOS의 네이티브 위젯 세트에 직접 상호작용하며 앱을 구축한다.</li>
<li>하지만 플러터는 자체의 고성능 렌더링 엔진을 사용하여 위젯을 렌더한다.</li>
</ul>
<blockquote>
<p>플러터의 작동 순서
앱에 엔진 및 다트코드를 컴파일하여 넣어둔다.
-&gt; 앱을 실행하면 각 기기 OS 별로 최적화된 runner가 엔진을 실행-&gt; 엔진은 컴파일된 코드를 이용하여 렌더링</p>
</blockquote>
<ul>
<li>즉, 호스트(ios, android 운영체제)가 직접 소통하며 렌더링하는 것이 아닌 호스트는 러너를 실행시킬 뿐 렌더링은 러너가 실행시킨 엔진(가상 머신)에 의해 이루어 진다고 보면 된다.</li>
</ul>
<h2 id="플러터와-리액트-네이티브">플러터와 리액트 네이티브</h2>
<ul>
<li>결국 플러터는 게임엔진들과 마찬가지로 OS에게 직접 렌더링에 대한 요청을 하지 않고 자체 엔진을 이용하여 렌더링 함.</li>
</ul>
<h3 id="차이">차이</h3>
<ul>
<li>리액트 네이티브는 자바스크립트를 이용하여 운영체제와 소통을 통해 렌더링을 하기 때문에 같은 코드라도 IOS와 Android에서 다른 모습으로 작동함(네이티브 위젯을 사용)</li>
<li>플러터는 각 운영체제의 네이티브와 똑같이 만들고 싶으면 그냥 비슷하게 만드는 수 밖에 없음.</li>
</ul>
<h3 id="결론">결론</h3>
<ul>
<li>RN : 크로스플랫폼 앱을 만들면서 네이티브 위젯을 사용하고 싶고 IOS나 Android 앱처럼 보이고 싶을 때</li>
<li>Flutter : 세밀한 디자인 요구사항이 있거나 커스터마이징이 필요하며 외부 패키지 도움 없이 고수준의 애니메이션을 구현하고 싶을 때</li>
</ul>
<blockquote>
<p>참고 : 플러터 아키텍쳐 개요.
<a href="https://docs.flutter.dev/resources/architectural-overview">https://docs.flutter.dev/resources/architectural-overview</a></p>
</blockquote>
<h2 id="설치-방법">설치 방법</h2>
<ol>
<li>homebrew에서 flutter 검색</li>
<li>터미널에서 <code>brew install --cask flutter</code>  실행</li>
</ol>
<h3 id="os-별-setup-설치-방법">OS 별 setup 설치 방법</h3>
<p><a href="https://docs.flutter.dev/get-started/install/macos#ios-setup">Flutter Docs 참고해서 설치하기!</a></p>
<h3 id="flutter-doctor">flutter doctor</h3>
<p><code>flutter doctor</code> 명령어를 터미널에 실행시키면 어떤 것들이 설치되지 않았는지 등을 체크해준다.</p>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/71f82125-1e49-4773-b9c3-d6ec3bc1866e/image.png" alt="flutter doctor"></p>
<h4 id="unable-to-find-bundled-java-version-에러-해결">Unable to find bundled Java version 에러 해결</h4>
<blockquote>
<p>안드로이드 스튜디오를 설치했는데 위와 같은 에러가 발생했다.</p>
</blockquote>
<ol>
<li>finder의 Application에서 안드로이드 스튜디오를 우클릭하여 패키지보기 클릭.</li>
<li>jre 파일 생성</li>
<li>jbr의 Contents 파일 복사</li>
<li>jre 파일에 붙여넣기.</li>
<li>터미널 재실행 후 flutter doctor 하면 해결.
<a href="https://github.com/flutter/flutter/issues/118502#issuecomment-1383215722">해결법 링크</a></li>
</ol>
<ul>
<li>추가: <a href="https://msyu1207.tistory.com/m/entry/Mac%EC%97%90%EC%84%9C-Flutter-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0feat-android-studio-xcode">toolchain 설치</a></li>
</ul>
<blockquote>
<p>ios 용 Xcode의 경우 필요할 때 설치할 예정.</p>
</blockquote>
<h2 id="create-flutter-app">create flutter app</h2>
<ul>
<li>CRA와 마찬가지로 터미널로 프로젝트를 만들고 싶은 경로로 가기!</li>
<li><code>flutter create [프로젝트명]</code></li>
</ul>
<h3 id="vscode-익스텐션-설치">VSCODE 익스텐션 설치</h3>
<blockquote>
<p>flutter를 vscode에서 실행하기 위해 필요함.</p>
</blockquote>
<ol>
<li>Dart</li>
<li>Flutter</li>
<li>lib파일의 main 클릭해보면 하단에 devtools 뜨는 것까지 확인!</li>
</ol>
<h3 id="에뮬레이터-실행">에뮬레이터 실행</h3>
<ul>
<li>하단 macOS(darwin) 클릭해서 각 기기 에뮬 실행가능.</li>
<li>안드로이드 스튜디오에서 에뮬레이터 하나 생성해주고</li>
<li>하단에서 에뮬레이터 실행 후 상단 디버깅 모드 스타트해주면 </li>
<li>에뮬레이터에서 플러터 앱 실행됨.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Dart] 다트 기초]]></title>
            <link>https://velog.io/@bigwave-cho/Dart-%EB%8B%A4%ED%8A%B8-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@bigwave-cho/Dart-%EB%8B%A4%ED%8A%B8-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Thu, 19 Jan 2023 14:00:08 GMT</pubDate>
            <description><![CDATA[<h2 id="variables">Variables</h2>
<h3 id="hello-dart">Hello Dart!</h3>
<ul>
<li>모든 Dart 앱은 main() 함수를 시작점으로 가지고 있음.</li>
<li>콘솔에 텍스트를 표시하기 위해선 print()함수를 사용.<pre><code class="language-js">void main() {
print(&#39;Hello, World!&#39;);
}</code></pre>
</li>
</ul>
<h3 id="var">var</h3>
<ul>
<li>Dart에서 변수는 var 키워드를 사용하여 자동 타입 지정 가능</li>
<li>명시적 타입 지정도 가능 (예: String)</li>
<li>세미콜론(;)을 작성하는 것을 주의 (자동 포매팅 없음)</li>
</ul>
<pre><code class="language-js">// hello world
// dart의 main은 entry
// dart는 세미콜론을 자동으로 포매팅해주지 않기 때문에
// 세미콜론을 작성하는 것을 주의해라.
// void main() {
//   print(&#39;hello world&#39;);
// }

void main() {
  var name = &#39;이름&#39;; // 자동 타입 지정
  String na = &#39;me&#39;; // 명시적 타입 지정
  print(na);

  na = &#39;you&#39;; // 업데이트 가능
}</code></pre>
<blockquote>
</blockquote>
<p> 보통 <strong>class내</strong>에서 변수나 프로퍼티를 선언할 땐 <strong>type 지정</strong>을 해주고
 <strong>함수나 메서드</strong> 내에서 지역변수 등을 선얼할 때는 <strong>var를 쓰는 것을 권장</strong>한다.</p>
<h3 id="dynamic-type">Dynamic Type</h3>
<ul>
<li>선언방법<ul>
<li><code>dynamic 식별자 or var 식별자</code></li>
</ul>
</li>
</ul>
<pre><code class="language-js">void main() {
  var name; //dynamic name
  // 명시적 지정도 가능 
  // dynamic name;

  name = &#39;이름&#39;;
  name = 12;
  name = true;
  // 이렇게 다른 타입을 할당해도 에러가 발생하지 않는다.

  name. 
  // dart가 정확히 name의 타입이 지정되지 않음을 알고 
  // 관련 메서드를 나타내지 않는다.

  if(name is String){
    name. // string타입에 관한 메서드가 나옴.
  }
}</code></pre>
<blockquote>
<p>다이내믹 타입은 <strong>정말 필요할 때만 사용</strong>하는 것을 권장
🤷🏻 var과 차이가 뭔데?
answer: var은 선언 후 값이 할당되면 타입이 고정된다.
하지만 dynamic은 타입이 고정되지 않는다.</p>
</blockquote>
<h3 id="nullable-variables">Nullable Variables</h3>
<ul>
<li>Null Safety는 개발자가 null 값을 참조하지 못하도록 하는 것</li>
<li>null 값을 참조하면 런타임 에러가 발생함.</li>
</ul>
<h4 id="without-null-safety"><strong>Without null safety</strong></h4>
<pre><code class="language-js">// 컴파일러가 에러를 캐치하지 못하고 
// 런타임 때 에러가 발생하게 됨.
bool isEmpty(String string) =&gt; string.length == 0;

main(){
  isEmpty(null); 
  // err: NoSuchMethodError
  //null이 인자로 보내지고 null.length가 실행되는데
  // null에는 length 메서드가 없기 때문에 
  // 위와 같은 에러가 발생하게 된다.
}</code></pre>
<blockquote>
<p>null은 아무 값도 없음을 나타내는 중요한 값.
따라서 dart에서는 null safety를 도입함.</p>
</blockquote>
<h4 id="with-null-safety"><strong>with null safety</strong></h4>
<pre><code class="language-js">bool isEmpty(String string) =&gt; string.length == 0;

main() {
  // &#39;?&#39; String 또는 null 입니다.
  String? nico = &#39;이름&#39;;
  nico = null;

  if(nico != null){
    nico.isNotEmpty; // true
  }

  // 또는 옵셔널 연산자를 사용해도 좋다
  nico?.isEmpty;
}</code></pre>
<blockquote>
<p>기본적으로 <strong>모든 변수는 non-nullable</strong>
 변수가 null도 될 수 있음을 알려주기 위해 <strong>&#39;?&#39;</strong>를 사용
 </br><strong>조건문</strong>으로 null safety를 적용할 수 있지만 <strong>옵셔널 연산자</strong>를 이용하면
 훨씬 간단하게 구현 가능하다.</p>
</blockquote>
<h3 id="finalconst">final(const)</h3>
<pre><code class="language-js">void main() {
  // JS의 const 선언과 같다.
  final name = &quot;이름&quot;;
  // 타입 지정도 가능 final String name = &#39;이름&#39;
  // 필수는 아님.

  name = &#39;창씨개명&#39;; // error
}</code></pre>
<h3 id="late-variable">late Variable</h3>
<ul>
<li><p>값 할당을 미루어 준다.</p>
<pre><code class="language-js">void main() {
late final String name;
//사용예) 무언가 실행되면 api에서 데이터를 불러와 값을 할당

//할당 하기 전 참조 시 할당되지 않음을 이유로 에러 발생
print(name);

name = &#39;api에서 불러온 이름&#39;;
name = &#39;dd&#39;; // 마찬가지로 상수이므로 에러 발생
</code></pre>
</li>
</ul>
<p>}</p>
<pre><code>
&gt;대부분의 경우 필요한 데이터를 알고 있기 때문에 
**`final name = &#39;이름&#39;;`** 의 경우처럼 초기에 값을 할당할 것이다.
하지만 **그렇지 않은 경우**도 있기 때문에 이 때 **`late`가 유용**하며,
할당 전까지 접근이 불가하기 때문에 **에러 방지에도 유용**하다.



### Constant Variables
- **JS의 const** 키워드는 Dart의 **final과 같고** Dart의 **const 키워드는 약간 다름**
- Dart의 const는 **compile-time constant**를 생성한다.

```js
void main(){
  const name = &#39;이름&#39;;
  name = &#39;11&#39;; //final과 마찬가지로 상수

 // 중요한 점: const는 compile-time에 알고 있는 값이어야 함.

 // API를 예로 들어보면
 const API = &#39;1231318151&#39;;
  // API 값은 절대 변하지 않고 컴파일 된 시점(유저가 사용하는)에도 유지됨.
  // 앱스토어에 배포하기 전에 알고 있는 상수

 final API2 = fetchAPI();
  // 이 경우 컴파일 된 시점에 API2의 값이 fetchAPI로 데이터를 받아오거나
  // 유저가 입력한 것에 따라 바뀌기 때문에 final나 var를 사용하는 것이 옳다.
  // 앱스토어에 배포한 후 상화에 따라 변화되는 상수


// -------- 예시2 --------- 
  const max_allowed_price = 120000;
// 상황에 따라 변하지 않고 고정된 제한 값(상수) 이용
}</code></pre><blockquote>
</blockquote>
<ul>
<li>compile 되기 전에 상수 값이 확실하다? -&gt; const</li>
<li>compile 된 후 상수 값이 유저의 상호작용이나 data fetch에 따라 할당되기 때문에 불확실하다 -&gt; final(상수) or var(변수)</li>
</ul>
<h2 id="data-types">Data Types</h2>
<h3 id="basic-data-types">Basic Data Types</h3>
<ul>
<li><p>String, bool, int, double, num</p>
<pre><code class="language-js">void main(){
String name = &#39;이름&#39;;
bool alive = false;
int age = 300;
// 정수
double money = 5.55;
// 부동소수점 숫자

num x = 12;
x = 1.1;
// num클래스는 int와 double의 부모클래스기 때문에
// num 변수에는 int와 double 모두 할당 가능하다.
}</code></pre>
<blockquote>
<p>bool, String는 모두 super class (부모 클래스)
int, double은 num 클래스 자료형의 자식 클래스!</p>
</blockquote>
</li>
</ul>
<h3 id="lists">Lists</h3>
<pre><code class="language-js">void main() {
  var giveMeFive = true;
  var numbers = [1, 2, 3, 4];
  //List&lt;int&gt; numbers

  //위와 같다.
  List&lt;int&gt; numbers2 = [
    1,
    2,
    3,
    4,
    if(giveMeFive) 5,
  ];

  numbers2.add(1); //[1, 2, 3, 4, 1]
  print(numbers2);

  numbers2.first; //첫 요소 //or last 마지막 요소 반환
}</code></pre>
<h4 id="collection-if">collection if</h4>
<pre><code class="language-js">//### Lists(배열)

void main() {
  var giveMeFive = true;

  List&lt;int&gt; numbers2 = [
    1,
    2,
    3,
    4,
    if(giveMeFive) 5,
  ];

  //아래 코드와 같음. 
  if(giveMeFive){
  numbers2.add(1); //[1, 2, 3, 4, 1]
  }
}</code></pre>
<h3 id="string-interpolation">String Interpolation</h3>
<pre><code class="language-js">void main(){
  var name = &#39;이름&#39;;

  var greeting = &#39;Hello everyone, my name is $name&#39;;

  print(greeting);
  //Hello everyone, my name is 이름
}</code></pre>
<h3 id="string-interpolation-1">String Interpolation</h3>
<ul>
<li>단순 변수 : $변수명</li>
<li>계산 등.. : ${변수명 + 2}</li>
<li>JS는 템플릿 리터럴 이용하지만 다트는 &quot;&quot; or &#39;&#39; 상관없음</li>
</ul>
<pre><code class="language-js">void main(){
  var name = &#39;이름&#39;;

  var age = 10;


  var greeting = &quot;Hello everyone, my name is $name and I&#39;m ${age + 2}&quot;;

  print(greeting);
  //Hello everyone, my name is 이름 and I&#39;m 12
}</code></pre>
<blockquote>
<p>추가 팁 ) 작은 따옴표 안에 작은 따옴표 쓰려면 이스케이프 사용
<strong><code>&#39;i\&#39;m $변수명</code></strong></p>
</blockquote>
<h3 id="collection-for">Collection For</h3>
<ul>
<li>배열에 다른 배열 변형 추가 가능.<pre><code class="language-js">void main() {
var oldFriends = [&#39;nico&#39;, &#39;lynn&#39;];
var newFriends = [
  &#39;lewis&#39;,
  &#39;ralph&#39;,
  &#39;darren&#39;,
  for (var friend in oldFriends) &#39;🪓 $friend&#39;,
];
print(newFriends);
[lewis, ralph, darren, 🪓 nico, 🪓 lynn]
}
</code></pre>
</li>
</ul>
<pre><code>
&gt;collection if / collection for 은 아주 혁명적임.


### Maps
- 객체의 키와 밸류 타입을 지정해줄 수 있다.
- 배열에도 적용 가능
```js
void main() {
  var player = {
    &#39;name&#39;: &#39;nico&#39;,
    &#39;xp&#39;: 19.99,
    &#39;superpower&#39;: false,
  };
//Map&lt;String, Object&gt; player
// 키: string, 값: object(any와 같은 의미)

  var player2 = {
    &#39;name&#39;: &#39;nico&#39;,
    &#39;xp&#39;: &#39;19.99&#39;,
    &#39;superpower&#39;: &#39;false&#39;,
  };
  //Map&lt;String, String&gt; player2

  Map&lt;int, bool&gt; player = {
    1: true,
    2: false,
  };
  // 객체의 키와 밸류의 타입을 지정해줄 수 있다.
  // var를 사용해도 알아서 타입을 유추해준다.

  Map&lt;List&lt;int&gt;, bool&gt; player3 = {
    [1, 2]: true,
    [3, 4]: false,
  };

  List&lt;Map&lt;String, Object&gt;&gt; players = [
    {
      &#39;name&#39;: &#39;이름&#39;,
      &#39;xp&#39;: 200,
    },
    {
      &#39;name&#39;: &#39;이름&#39;,
      &#39;xp&#39;: 200,
    }
  ];
}</code></pre><blockquote>
<p>위와 같은 방법보다 더 효율적인 class가 있으므로 후에 배워보자.</p>
</blockquote>
<h3 id="sets">Sets</h3>
<ul>
<li><p>요소의 중복없는(unique) 배열을 만들기 위한.</p>
</li>
<li><p>JS의 Set과 같네</p>
<pre><code class="language-js">void main() {
var numbers = {1,2,3,4};
//Set&lt;int&gt; numbers = {1,2,3,4}
//Set에 속한 아이템들은 unique함.
numbers.add(1);
numbers.add(5);

print(numbers);//{1, 2, 3, 4, 5}

// 배열의 경우
List&lt;int&gt; numbers2 = [1,2,3,4];

numbers2.add(1);
numbers2.add(1);
print(numbers2); //[1, 2, 3, 4, 1, 1]
}</code></pre>
<blockquote>
<p>Set은 순서가 있고(sequence)
List와 같지만 모든 요소가 유니크(중복없음)해야 한다.</p>
</blockquote>
<h2 id="function">Function</h2>
<h3 id="함수-정의하기">함수 정의하기</h3>
<pre><code class="language-js">void sayHello(String name) {
print(&quot;Hello $name nice to meet you!&quot;);
}
</code></pre>
</li>
</ul>
<p>String sayHello2(String name) {
  return &quot;Hello, $name&quot;;
}</p>
<p>String sayHello3(String name) =&gt; &quot;hello, $name&quot;;</p>
<p>void main() {
  print(sayHello3(&#39;이름&#39;));
  //hello, 이름
}</p>
<pre><code>- 사용 예
```js
// 1)
String sayHello(String name) {
  //call api
  // perform cal
  return &#39;result&#39;;
}

------
// 2)
num plus(num a, num b) =&gt; a + b;</code></pre><h3 id="named-parameter">Named Parameter</h3>
<ul>
<li>기존<pre><code class="language-js">String sayHello(String name, int age, String country){
return &quot;Hello $name, you are $age, from $country&quot;;
}
</code></pre>
</li>
</ul>
<p>void main() {
  // 기존 파라미터 전달 방법.
  print(sayHello(&#39;이름&#39;, 100, &quot;N.Korea&quot;));
}</p>
<pre><code>- name parameter
```js

// 1. 중괄호로 감싸고 default 값 부여하기
String sayHello({String name = &quot;익명&quot;, int age = 99, String country=&quot;usa&quot;}) {
  return &quot;Hello $name, you are $age, from $country&quot;;
}
// 2. 또는 required 적용
String sayHello2({required String name ,required int age ,required String country}) {
  return &quot;Hello $name, you are $age, from $country&quot;;
}


void main() {
  //리액트 props 전달할 때 {} 객체로 받는 것 개념 비슷

  //named parameter
  // 인수를 키와 값으로 전달.

  // default value
  print(sayHello(age: 12, country: &quot;korea&quot;, name: &quot;이름&quot;));

  //required
  print(sayHello2(age:12,country:&#39;kore&#39;, name:&#39;ㅇㅇ&#39;));
}</code></pre><h3 id="optional-positional-parameter">optional positional parameter</h3>
<ul>
<li>대괄호 감싸기 -&gt; 디폴트 값 설정<pre><code class="language-js">String sayHello(String name, int age, [String country = &#39;쿠바&#39;]) =&gt;
  &quot;hello $name $age $country&quot;;
</code></pre>
</li>
</ul>
<p>void main() {
  print(sayHello(&quot;이름&quot;, 12));
  // hello 이름 12 쿠바</p>
<p>  print(sayHello(&quot;이름&quot;, 12,&quot;한국&quot;));
  //hello 이름 12 한국
}</p>
<pre><code>
### QQ operator
&gt; - 기존 방식

```js
String upperName(String? name) {
  if (name != null) {
    return name.toUpperCase();
  }
  return &quot;no null&quot;;
}

void main() {
  print(upperName(&quot;lower&quot;));// LOWER

  print(upperName(null));// no null
}</code></pre><blockquote>
<ul>
<li>개선 방식 ( <strong>&quot;??&quot;, &quot;?&quot;</strong>)</li>
</ul>
</blockquote>
<pre><code class="language-js">String upperName(String? name) {
  //return name != null ? name.toUpperCase() : &#39;No null&#39;;
  return name?.toUpperCase() ?? &quot;No null&quot;;
// A ?? B -&gt; B
// A가 null이면 B
}</code></pre>
<p>훨씬 짧아졌넹.</p>
<ul>
<li>추가로 알아보기<pre><code class="language-js">String upperName(String? name) {
// return name != null ? name.toUpperCase() : &#39;No null&#39;;
return name?.toUpperCase() ?? &quot;No null&quot;;
//name? : name은 String or null이다. 
// ??   :  좌항의 name이 null인 경우 우항을 리턴
}
</code></pre>
</li>
</ul>
<p>void main() {
  String? name;
  name ??= &#39;이름&#39;;</p>
<p>  // 좌변 name은 절대 null이 될 수 없기 때문에
  //우변 값이 할당 될 수 없음.</p>
<p>  name ??= &#39;dd&#39;; </p>
<p>  //따라서 &#39;dd&#39;가 할당되지 않음.
  print(name); // 이름</p>
<p>  //다시 null 을 부여하면 할당 됨.
  name = null;
  name ??= &quot;이러면 됨&quot;;
  print(name); // 이러면 됨
}</p>
<pre><code>
### typedef
- 타입스크립트 처럼 타입 만들기 쌉가능.

```js
// 기존
List&lt;int&gt; reverseListOfNumbers(List&lt;int&gt; list){
  var reversed = list.reversed;
  return reversed.toList();
}

void main() {
  print(reverseListOfNumbers([1,2,3,4,]));
  // [4, 3, 2, 1]
}

// typedef 이용하기
typedef ListOfInts = List&lt;int&gt;;

ListOfInts reverseListOfNumbers(ListOfInts list){
  var reversed = list.reversed;
  return reversed.toList();
}</code></pre><ul>
<li>객체도 마찬가지로 가능<pre><code class="language-js">String sayHi(Map&lt;String, String&gt; userInfo){
return &quot;Hi, ${userInfo[&#39;name&#39;]}&quot;;
}
</code></pre>
</li>
</ul>
<p>void main(){
  sayHi({&quot;name&quot;:&quot;뿅뿅&quot;});
}
---- 객체 typedef 지정</p>
<p>typedef UserInfo = Map&lt;String, String&gt;;</p>
<p>String sayHi(UserInfo userInfo){
  return &quot;Hi, ${userInfo[&#39;name&#39;]}&quot;;
}</p>
<pre><code>
&gt;좀 더 키 식별자 등을 정형화한 객체를 만들고 싶으면 
class를 사용하면 됨.

## Classes
### class 기본
```js
class Player {
  String name = &quot;이름&quot;;
  int xp = 1500;

  void sayHello(){
    print(&quot;Hi my name is $name&quot;);
  }

  //this 키워드 안써도 되는데 굳이 써야하는 상황은
  void sayBye(){
    var name = &quot;카ㅋㅋ&quot;;
    // 메서드 내에 프로퍼티와 같은 이름의 변수가 있는 경우.

    //속한 클래스의 변수 참조
    print(&quot;Bye, ${this.name}&quot;); // &#39;이름&#39;

    //메서드의 변수 참조
    print(&quot;$name&quot;); // &#39;카ㅋㅋ&#39;

  }
}

void main(){
  var player = Player();
  //JS와 달리 new player도 가능하지만 없어도 됨.
  print(player);// instance of &#39;Player&#39;
  print(player.name); //이름

  player.name = &#39;바까&#39;;
  print(player.name);//&#39;바까&#39;
  // 못 바꾸게 하려면 final 적용하기

  player.sayHello();
  //Hi my name is 바까
}</code></pre><blockquote>
<p>메서드가 프로퍼티 참조할 때 this 는 굳이 필요 없다.
필요한 경우는 메서드의 변수와 프로퍼티의 이름이 같을 경우 정도.</p>
</blockquote>
<h3 id="constructor">constructor</h3>
<ul>
<li><p>JS의 컨스트럭터와 비슷한데 약간 다르네</p>
<pre><code class="language-js">class Player{
String name;
int xp;

//컨스트럭터 이용해서 이름 xp 유동적 바꾸기.
Player(this.name, this.xp);

// 중괄호 + 기본값 부여하는 방식
//   Player({this.name=&#39;기본&#39;, this.xp= 0});
</code></pre>
</li>
</ul>
<p>}</p>
<p>void main(){
  // 컨스트럭터 이용
  var player = Player(&#39;이름&#39;, 123);
  print(player.name);// 이름</p>
<p>  // 두 번째 방식 중괄호 기본값인 경우
  var player2 = Player(name: &#39;하하&#39;, xp: 120);
  print(player2.name); //하하
}</p>
<pre><code>
### Named Constructor Parameter
- class가 커져서 관리가 어려울 때

```js
//기존 클라스
class Player{
  String name;
  int xp;
  String team;
  int age;

 Player(this.name, this.xp, this.team, this.age);
}

void main(){

  // 인자가 무엇을 의미하는지 알기 어려워짐.
  var player = Player(&#39;이름&#39;, 123,&#39;red&#39;, 12);
}</code></pre><ul>
<li>네임드 컨스트럭터 파람 적용<pre><code class="language-js">class Player {
String name;
int xp;
String team;
int age;
// 1. 중괄호 쳐주기
Player({required this.name, required this.xp, required this.team, required this.age});
}
</code></pre>
</li>
</ul>
<p>void main() {
  // 2. 키밸류 쌍으로 파라미터 전달.
  var player = Player(
    name: &#39;이름&#39;,
    xp: 123,
    team: &#39;red&#39;,
    age: 12,
  );
}</p>
<pre><code>
### Named Constructor

```js
class Player {
  String name, team;
  int xp, age;

  Player(
      {required this.name,
      required this.xp,
      required this.team,
      required this.age});
  // named const param은 파라미터값을 프로퍼티에 할당해주는 역할

  // 바로 값 부여하는 방식(constructor)
  Player.createBluePlayer(this.name, this.age)
      : team = &#39;blue&#39;,
        xp = 0;

  // 중괄호에 기본값 주는 방식(Named Constructor Parameter)
  Player.createRedPlayer({this.name = &#39;2&#39;, this.age = 1})
      : team = &#39;red&#39;,
        xp = 0;
}

void main() {
  //named 문법 사용하는 경우
  var player = Player.createBluePlayer(
    &#39;이름&#39;,
    12,
  );
  print(player.name); //이름

  //positional 문법 사용하는 경우.
  var player2 = Player.createRedPlayer(name: &#39;1&#39;, age: 12);
  print(player2.name); // 1
  print(player2.age);  // 12
  print(player2.team); //red
}</code></pre><h3 id="복습-named-constructor">복습 named constructor</h3>
<pre><code class="language-js">class Player {
  final String name;
  int xp;
  String team;

  //fromJson - named constructor
  // playerJson 객체의 프로퍼티 값을 부여
 Player.fromJson(Map&lt;String, dynamic&gt; playerJson) :
  name = playerJson[&#39;name&#39;],
  xp = playerJson[&#39;xp&#39;],
  team = playerJson[&#39;team&#39;];


 void sayHello(){
   print(&#39;hi, $name, $xp, $team&#39;);
 }
}

void main() {
  var apiData = [
    {
      &quot;name&quot;: &#39;일번&#39;,
      &quot;team&quot;: &quot;red&quot;,
      &quot;xp&quot;: 0,
    },
    {
      &quot;name&quot;: &#39;이번&#39;,
      &quot;team&quot;: &quot;red&quot;,
      &quot;xp&quot;: 0,
    },
    {
      &quot;name&quot;: &#39;삼번&#39;,
      &quot;team&quot;: &quot;red&quot;,
      &quot;xp&quot;: 0,
    },
  ];

  apiData.forEach((playerJson){
    var player = Player.fromJson(playerJson);
    player.sayHello();
  });
  /*
   *  hi, 일번, 0, red
      hi, 이번, 0, red
      hi, 삼번, 0, red
   * */
}</code></pre>
<h3 id="cascade-notation">Cascade Notation</h3>
<ul>
<li><p>인스턴스 수정을 쉽게 할 수 있다.</p>
<pre><code class="language-js">class Player {
String name;
int xp;
String team;

//fromJson - named constructor
Player({required this.name, required this.xp, required this.team});

void sayHello() {
  print(&#39;hi, $name, $xp, $team&#39;);
}
}
</code></pre>
</li>
</ul>
<p>void main() {
  // 기존 인스턴스 값 바꾸는 방법
  var CJ = Player(name: &#39;CJ&#39;, xp: 1200, team: &#39;red&#39;);
  CJ.name = &#39;wf&#39;;
  CJ.xp = 10;
  CJ.team = &#39;blue&#39;;</p>
<p>  // Cascade Notation 방법
  var BJ = Player(name: &#39;BJ&#39;, xp:1000, team: &#39;red&#39;)
    ..name=&#39;WW&#39;
    ..xp = 1000
    ..team = &#39;blue&#39;;</p>
<p>  BJ.sayHello(); //hi, WW, 1000, blue</p>
<p>  // 예시 하나 더
  var HJ = BJ
    ..name = &#39;hoho&#39;
    ..xp = 99
    ..team = &#39;white&#39;
    ..sayHello(); //hi, hoho, 99, white
}</p>
<pre><code>
### enum

- enum은 리터럴 타입 지정과 비슷하다.
- string으로 쓸 필요 없이 아래처럼 constrain하면 된다.
```js
enum Team {red, blue}

enum XPLevel {beginner,medium,pro}

class Player {
  String name;
  XPLevel xp;
  Team team;
 // String team;

  //fromJson - named constructor
  Player({required this.name, required this.xp, required this.team});

  void sayHello() {
    print(&#39;hi, $name, $xp, $team&#39;);
  }
}

void main() {
  // 기존 인스턴스 값 바꾸는 방법
  var cj = Player(name: &#39;CJ&#39;, xp: XPLevel.beginner, team: Team.blue);
  cj.name = &#39;wf&#39;;
  cj.xp = XPLevel.medium;
  cj.team = Team.red;

  // Cascade Notation 방법
  var bj = Player(name: &#39;BJ&#39;, xp:XPLevel.beginner, team: Team.red)
    ..name=&#39;WW&#39;
    ..xp = XPLevel.pro
    ..team = Team.blue;

  bj.sayHello(); //hi, WW, XPLevel.pro, Team.blue

  // 예시 하나 더
  var hj = bj
    ..name = &#39;hoho&#39;
    ..xp = 99 // 에러
    ..team = &#39;white&#39; // 에러 Team.red나 Team.blue만 올 수 있음.
    ..sayHello(); //hi, hoho, 99, white

}</code></pre><h3 id="abstract-class">abstract class</h3>
<ul>
<li>abstract class는 자식 클라스가 항상 가져야 하는 메서드를 정의할 수 있다.</li>
<li>abstract class의 메서드는 불변이 아니므로 자식클래스에서 마음대로 구현이 가능하다.<pre><code class="language-js">abstract class Human{
void walk();
}
</code></pre>
</li>
</ul>
<p>enum Team {red, blue}</p>
<p>enum XPLevel {beginner,medium,pro}</p>
<p>// extends하니 Human의 walk메서드가 없다고 에러 띄움.
class Player extends Human {
  String name;
  XPLevel xp;
  Team team;</p>
<p>  Player({required this.name, required this.xp, required this.team});</p>
<p>  void walk(){
    print(&#39;im walking&#39;);
  }</p>
<p>  void sayHello() {
    print(&#39;hi, $name, $xp, $team&#39;);
  }
}</p>
<p>class coach extends Human{
  void walk(){
    print(&#39;the coach is walking&#39;);
  }
}</p>
<pre><code>

### 클래스 상속(inheritance)
```js
class Human {
  final String name;

  Human({required this.name});
  //방식2 :  또는 Human(this.name)

  void sayHello() {
    print(&#39;Hi my name is $name&#39;);
  }
}

enum Team { blue, red }

class Player extends Human {
  final Team team;

  Player({
    required this.team,
    required String name,
  }) : super(name: name);
  // 방식2 : 또는 super(name);
  // super()가 부모클라스의 Human constructor를 작동시킴

  @override //부모가 상속한 sayHello를 대체하겠다.
  void sayHello() {
    // super는 상속한 부모 클래스의 프로퍼티에 접근이나 메서드 호출을 가능하게 한다.
    super.sayHello();
    print(&#39;and I play for ${team}&#39;);
  }
}

void main() {
  var player = Player(
    team: Team.red,
    name: &#39;이름&#39;,
  );

  //override 하지 않으면 부모의 sayHello를 호출(체이닝?)
  //Hi my name is 이름

  // override를 할 경우 해당 인스턴스의 sayHello()가 호출됨.
  player.sayHello();
  //Hi my name is 이름
  //and I play for Team.red
}</code></pre><h3 id="mixins">Mixins</h3>
<h3 id="mixin">mixin</h3>
<ul>
<li>생성자 없는 컨스터럭터를 의미.</li>
<li>재사용 하는 메서드나 프로퍼티를 넣어두면 좋다.</li>
</ul>
<pre><code class="language-js">class Strong {
  final double strengthLevel = 1500.99;
}

class QuickRunner {
  void runQuick(){
    print(&#39;run&#39;);
  }
}

class Tall {
  final double height = 1.99;
}

class Horse with Strong, QuickRunner{}

class Kid with QuickRunner{}

enum Team { blue, red }

// with : 다른 클래스의 프로퍼티와 메서드를 긁어온다.
class Player with Strong, QuickRunner, Tall {
  final Team team;

  Player({
    required this.team,
  });
}

void main() {
  var player = Player(team:Team.blue);
  print(player.team); //Team.blue
  player.runQuick(); //run
  print(player.strengthLevel);  //1500.99
}</code></pre>
<ul>
<li>mixin들의 메서드와 프로퍼티를 가져온 모습.
<img src="https://velog.velcdn.com/images/bigwave-cho/post/f6cf9664-11a5-49fc-a305-407789f54102/image.png" alt=""><blockquote>
<p>주의점 : mixin은 <strong>컨스트럭터 없는 class</strong>여야 함.
또한 부모 자식 <strong>상속 관계가 아닌 단순히 프로퍼티와 메서드를 긁어오는 것</strong>임.</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 25장 클래스]]></title>
            <link>https://velog.io/@bigwave-cho/JS-25%EC%9E%A5-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@bigwave-cho/JS-25%EC%9E%A5-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Tue, 17 Jan 2023 15:10:19 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>Deepdive 공부 정리용 글입니다.</strong></p>
</blockquote>
<h2 id="251_-클래스란">25.1_ 클래스란</h2>
<ul>
<li><p>JS는 프로토타입 기반 객체지향 언어</p>
</li>
<li><p>프로토타입 기반 객체지향 언어는 클래스가 필요 없는 객체지향 언어!</p>
</li>
<li><p>ES5의 함수와 프로토타입을 통한 객체지향 언어의 상속 구현</p>
<pre><code class="language-js">// ES5 생성자 함수
var Person = (function () {
// 생성자 함수
function Person(name) {
  this.name = name;
}

// 프로토타입 메서드
Person.prototype.sayHi = function () {
  console.log(&#39;Hi! My name is &#39; + this.name);
};

// 생성자 함수 반환
return Person;
})();
</code></pre>
</li>
</ul>
<p>// 인스턴스 생성
var me = new Person(&#39;Lee&#39;);</p>
<p>me.sayHi(); // Hi! My name is Lee
console.log(me);
// Person {name: &#39;Lee&#39;}</p>
<pre><code>
- ES6에서 도입된 class는 클래스 기반 **객체지향 프로그래밍 언어와 매우 비슷한** **새로운 객체 생성 메커니즘** 제시.
- 하지만 이는 새로운 객체지향 모델이 아닌 기존 프로토타입 기반 패턴을 클래스 기반 패턴처럼 사용 가능하도록 하는 **문법적 설탕**임이라고 본다.. 하지만?(곧 설명)
- 클래스와 생성자 함수는 비슷하지만 클래스가 더 엄격하며 생성자 함수가 제공하지 않는 기능 또한 제공한다.
&gt; 클래스와 생성자함수의 차이점
1. new 연산자 없이 호출 -&gt; class(에러) &amp; 생성자 함수(일반함수로 호출)
2. 클래스는 extends와 super 키워드 제공
3. 호이스팅 -&gt; class(발생하지 않는 것처럼) &amp; 생성자함수(호이스팅), 함수표현식(변수 호이스팅)
4. strict mode -&gt; class(해제불가 지정) &amp; 생성자함수(암묵적 지정X)
5. 클래스의 constructor, 프로토타입 메서드, 정적 메서드는 모두 프로퍼티 어트리뷰트 [[Enumerable]] 값이 false로 열거되지 않는다.

- 생성자 함수와 클래스는 프로토타입 기반의 객체지향을 구현하는 것은 비슷하지만 클래스가 생성자 함수보다 견고하고 명료하다. 
- 클래스의 extends와 super 키워드는 상속 관계 구현을 더욱 간결하고 명료하게 함.
- 따라서 클래스를 문법적 설탕보다는 새로운 객체 생성 메커니즘으로 보는 것이 합당함.

25.2_ 클래스 정의
- 클래스는 class 키워드를 사용하여 정의.
- 클래스 이름은 생성자함수와 마찬가지로 Pascal case
`class Person {}`
- 표현식도 가능
```js
const Person = class{};
const Person = class Myclass{};</code></pre><ul>
<li><p>즉 클래스는 표현식으로 정의 가능하므로 일급 객체라는 의미.</p>
<ul>
<li>🔎 일급 객체의 특징
무명의 리터럴로 생성 가능(런타임 생성)
변수나 자료구조(객체, 배열)에 저장 가능
함수의 매개변수에 전달 가능
함수의 반환값으로 사용 가능</li>
</ul>
</li>
<li><p>클래스는 컨스트럭터, 프로토타입 메서드, 정적 메서드 정의 가능.</p>
</li>
<li><p>생성자 함수와 클래스의 비교</p>
<pre><code class="language-js">//생성자 함수
var Person = (function(){
function Person(name){
  this.name = name;
}

// 프로토타입 메서드
Person.prototype.sayHi = function(){
  console.log(&#39;Hi, My name is&#39; + this.name);
}

// 정적 메서드
Person.sayHello = function () {
  console.log(&#39;Hello!&#39;);
}

//생성자 함수 반환
return Person;
}());</code></pre>
</li>
</ul>
<hr>
<p>// 클래스 선언문
class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name; // name 프로퍼티는 public하다.
  }</p>
<p>  // 프로토타입 메서드
  sayHi() {
    console.log(<code>Hi! My name is ${this.name}</code>);
  }</p>
<p>  // 정적 메서드
  static sayHello() {
    console.log(&#39;Hello!&#39;);
  }
}</p>
<p>// 인스턴스 생성
const me = new Person(&#39;Lee&#39;);</p>
<p>// 인스턴스의 프로퍼티 참조
console.log(me.name); // Lee
// 프로토타입 메서드 호출
me.sayHi(); // Hi! My name is Lee
// 정적 메서드 호출
Person.sayHello(); // Hello!</p>
<pre><code>
### 정적 메서드?
- 정적 메서드는 prototype이 아닌 클래스 함수 자체에 메서드를 설정하는 것
- 정적 메서드는 특정 클래스 인스턴스가 아닌 클래스 &#39;전체&#39;에 필요한 기능을 만들 때 사용 가능
```js
class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static createTodays() {
    // this는 Article입니다.
    return new this(&quot;Today&#39;s digest&quot;, new Date());
  }
}

let article = Article.createTodays();

alert( article.title ); // Today&#39;s digest</code></pre><h2 id="253_-클래스-호이스팅">25.3_ 클래스 호이스팅</h2>
<ul>
<li>클래스는 평가과정에서 평가되어 함수 객체를 생성</li>
<li>주의! 호이스팅은 발생하지만 런타임 정의 이전에 참조는 불가하다</li>
<li>let const와 비슷<pre><code class="language-js">const Person = &#39;&#39;;
</code></pre>
</li>
</ul>
<p>{
  // 호이스팅이 발생하지 않는다면 &#39;&#39;이 출력되어야 한다.
  console.log(Person);
  // ReferenceError: Cannot access &#39;Person&#39; before initialization</p>
<p>  // 클래스 선언문
  class Person {}
}</p>
<pre><code>
## 25.4_ 인스턴스 생성
- 클래스는 생성자 함수로 new 연산자와 함께 호출되어 인스턴스를 생성
```js
class Person {}

const me = new Person{}</code></pre><h2 id="255_-메서드">25.5_ 메서드</h2>
<ul>
<li><p>클래스 몸체에서 정의 가능한 메서드</p>
<ul>
<li>constructor(생성자)</li>
<li>프로토타입 메서드</li>
<li>정적 메서드</li>
</ul>
<h3 id="2551_-constructor">25.5.1_ constructor</h3>
<ul>
<li>constructor는 인스턴스를 생성하고 초기화하기 위한 특수 메서드</li>
</ul>
</li>
</ul>
<pre><code class="language-js">class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name;
  }
}

// 클래스는 함수다.
console.log(typeof Person); // function
console.dir(Person);


// 인스턴스 생성
const me = new Person(&#39;Lee&#39;);
console.log(me);//Person {name: &#39;Lee&#39;}</code></pre>
<ul>
<li>생성자 함수와 마찬가지로 constructor 내부에서 this에 추가한 프로퍼티는 인스턴스 프로퍼티가 된다.</li>
</ul>
<pre><code class="language-js">// 클래스
class Person {
  // 생성자
  constructor(name) {
    // 인스턴스 생성 및 초기화
    this.name = name;
  }
}

// 생성자 함수
function Person(name) {
  // 인스턴스 생성 및 초기화
  this.name = name;
}</code></pre>
<blockquote>
<h4 id="프로토타입의-constructor와-class의-constructor차이">프로토타입의 constructor와 class의 constructor차이</h4>
<p>관련 없음. 
프로토타입의 컨스트럭터는 모든 프로토타입이 가지고 있는 프로퍼티로 생성자 함수를 가리킴.</p>
</blockquote>
<ul>
<li>인스턴스 생성 시 클래스 외부에서 인스턴스 프로퍼티의 초기값 전달하기<pre><code class="language-js">class Person {
constructor(name, address) {
  // 인수로 인스턴스 초기화
  this.name = name;
  this.address = address;
}
}
</code></pre>
</li>
</ul>
<p>// 인수로 초기값을 전달한다. 초기값은 constructor에 전달된다.
const me = new Person(&#39;Lee&#39;, &#39;Seoul&#39;);
console.log(me); // Person {name: &quot;Lee&quot;, address: &quot;Seoul&quot;}</p>
<pre><code>
### 25.5.2_ 프로토타입 메서드

```js
// 생성자 함수
function Person(name) {
  this.name = name;
}

// 프로토타입 메서드
Person.prototype.sayHi = function () {
  console.log(`Hi! My name is ${this.name}`);
};

// 프로토타입 지정 안해도 기본적으로 프로토타입 메서드가 됨.
sayBye = function(){
  console.log(`Bye, ${this.name}`);

const me = new Person(&#39;Lee&#39;);
me.sayHi(); // Hi! My name is Lee</code></pre><ul>
<li>생성자 함수와 마찬가지로 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 됨.<pre><code class="language-js">// me 객체의 프로토타입은 Person.prototype이다.
Object.getPrototypeOf(me) === Person.prototype; // -&gt; true
me instanceof Person; // -&gt; true
</code></pre>
</li>
</ul>
<p>// Person.prototype의 프로토타입은 Object.prototype이다.
Object.getPrototypeOf(Person.prototype) === Object.prototype; // -&gt; true
me instanceof Object; // -&gt; true</p>
<p>// me 객체의 constructor는 Person 클래스다.
me.constructor === Person; // -&gt; true</p>
<pre><code>
### 25.5.3_ 정적 메서드
- 정적 메서드는 인스턴스를 생성하지 않아도 호출 가능한 메서드

- 생성자 함수의 정적 메서드 생성 방법
  - 명시적으로 생성자 함수에 메서드 추가
```js
// 생성자 함수
function Person(name){
  this.name = name;
}

// 정적 메서드
Person.sayHi = function () {
  console.log(&#39;Hi!&#39;);
};

Person.sayHi(); //Hi!</code></pre><ul>
<li>클래스의 정적 메서드 생성 방법</li>
</ul>
<pre><code class="language-js">class Person {
  //생성자
  constructor(name) {
    //인스턴스 생성 및 초기화
    this.name = name;
  }

  static sayHi() {
    console.log(&#39;Hi&#39;);
  }
}</code></pre>
<ul>
<li><p>정적 메서드는 프로토타입 메서드처럼 인스턴스로 호출하지 않고 클래스로 호출</p>
<pre><code class="language-js">// 정적 메서드는 클래스로 호출한다.
// 정적 메서드는 인스턴스 없이도 호출할 수 있다.
Person.sayHi(); // Hi!</code></pre>
</li>
<li><p>정적 메서드는 인스턴스로 호출 불가.</p>
</li>
<li><p>정적 메서드가 바인딩된 클래스는 인스턴스의 프로토타입 체인 상에 존재하지 않음.</p>
</li>
<li><p>즉, 상속되지 않음</p>
</li>
</ul>
<pre><code class="language-js">// 인스턴스 생성
const me = new Person(&#39;Lee&#39;);
me.sayHi(); // TypeError: me.sayHi is not a function</code></pre>
<h3 id="2554_-정적-메서드와-프로토타입-메서드의-차이">25.5.4_ 정적 메서드와 프로토타입 메서드의 차이</h3>
<blockquote>
</blockquote>
<ol>
<li>정적 메서드와 프로토타입 메서드는 속해있는 프로토타입 체인이 다름</li>
<li>정적 메서드는 클래스로 호출, 프로토타입 메서드는 인스턴스로 호출</li>
<li>정적 메서드는 인스턴스 프로퍼티를 참조 불가 / 프로토타입 메서드는 인스턴스 프로퍼티를 참조 가능</li>
</ol>
<ul>
<li>메서드 내부의 this는 메서드를 소유한 객체가 아닌 메서드를 호출한 객체에 바인딩됨( 메서드 이름 앞의 마침표 &#39;.&#39; 연산자 앞에 기술한 객체에 바인딩)</li>
<li>프로토타입 메서드는 위와 같이 바인딩되지만 정적 메서드는 class에 바인딩 되는 것이 다름.</li>
</ul>
<blockquote>
</blockquote>
<ul>
<li><p>사용 예)</p>
<pre><code class="language-js">// 표준 빌트인 객체의 정적 메서드
Math.max(1, 2, 3);          // -&gt; 3
Number.isNaN(NaN);          // -&gt; true
JSON.stringify({ a: 1 });   // -&gt; &quot;{&quot;a&quot;:1}&quot;
Object.is({}, {});          // -&gt; false
Reflect.has({ a: 1 }, &#39;a&#39;); // -&gt; true</code></pre>
<h3 id="2555_-클래스에서-정의한-메서드의-특징">25.5.5_ 클래스에서 정의한 메서드의 특징</h3>
<blockquote>
<p>클래스에서 정의한 메서드의 특징</p>
</blockquote>
<ol>
<li>function 키워드를 생략한 메서드 축약 표현 사용</li>
<li>객체 리터럴과 다르게 클래스에 메서드 정의 시 콤마 불필요</li>
<li>암묵적으로 strict mode로 실행</li>
<li>for...in 문이나 Objcet.keys 메서드 등으로 열거 불가</li>
<li>내부 메서드 [[Construct]]를 갖지 않는 non-constructor.
(즉, new 연산자와 함께 호출 불가)</li>
</ol>
</li>
</ul>
<h2 id="256_-클래스이-인스턴스-생성-과정435p">25.6_ 클래스이 인스턴스 생성 과정(435p)</h2>
<ol>
<li>인스턴스 생성과 this 바인딩</li>
<li>인스턴스 초기화</li>
<li>인스턴스 반환</li>
</ol>
<h2 id="257_-프로퍼티">25.7_ 프로퍼티</h2>
<h3 id="2571_-인스턴스-프로퍼티">25.7.1_ 인스턴스 프로퍼티</h3>
<ul>
<li>인스턴스 프로퍼티는 constructor 내부에서 정의<pre><code class="language-js">class Person {
constructor(name) {
  // 인스턴스 프로퍼티
  this.name = name;
}
}
</code></pre>
</li>
</ul>
<p>const me = new Person(&#39;Lee&#39;);
console.log(me); // Person {name: &quot;Lee&quot;}</p>
<pre><code>constructor 내부 코드 실행되기 이전에 constructor 내부 this에는 이미 클래스가 암묵적으로 생성한 인스턴스인 빈 객체가 바인딩 됨
-&gt; 이로써 클래스가 암묵적 생성한 빈 객체(인스턴스)에 프로퍼티가 추가되어 인스턴스 초기화

### 25.7.2_ 접근자 프로퍼티
접근자 프로퍼티는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티

- 생성자함수로
```js
const person = {
  // 데이터 프로퍼티
  firstName: &#39;Ungmo&#39;,
  lastName: &#39;Lee&#39;,

  // fullName은 접근자 함수로 구성된 접근자 프로퍼티다.
  // getter 함수
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  // setter 함수
  set fullName(name) {
    // 배열 디스트럭처링 할당: &quot;36.1. 배열 디스트럭처링 할당&quot; 참고
    [this.firstName, this.lastName] = name.split(&#39; &#39;);
  }
};

// 데이터 프로퍼티를 통한 프로퍼티 값의 참조.
console.log(`${person.firstName} ${person.lastName}`); // Ungmo Lee

// 접근자 프로퍼티를 통한 프로퍼티 값의 저장
// 접근자 프로퍼티 fullName에 값을 저장하면 setter 함수가 호출된다.
person.fullName = &#39;Heegun Lee&#39;;
console.log(person); // {firstName: &quot;Heegun&quot;, lastName: &quot;Lee&quot;}

// 접근자 프로퍼티를 통한 프로퍼티 값의 참조
// 접근자 프로퍼티 fullName에 접근하면 getter 함수가 호출된다.
console.log(person.fullName); // Heegun Lee

// fullName은 접근자 프로퍼티다.
// 접근자 프로퍼티는 get, set, enumerable, configurable 프로퍼티 어트리뷰트를 갖는다.
console.log(Object.getOwnPropertyDescriptor(person, &#39;fullName&#39;));
// {get: ƒ, set: ƒ, enumerable: true, configurable: true}</code></pre><ul>
<li><p>클래스의 경우</p>
<pre><code class="language-js">class Person {
constructor(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

// fullName은 접근자 함수로 구성된 접근자 프로퍼티다.
// getter 함수
get fullName() {
  return `${this.firstName} ${this.lastName}`;
}

// setter 함수
set fullName(name) {
  [this.firstName, this.lastName] = name.split(&#39; &#39;);
}
}
</code></pre>
</li>
</ul>
<p>const me = new Person(&#39;Ungmo&#39;, &#39;Lee&#39;);</p>
<p>// 데이터 프로퍼티를 통한 프로퍼티 값의 참조.
console.log(<code>${me.firstName} ${me.lastName}</code>); // Ungmo Lee</p>
<p>// 접근자 프로퍼티를 통한 프로퍼티 값의 저장
// 접근자 프로퍼티 fullName에 값을 저장하면 setter 함수가 호출된다.
me.fullName = &#39;Heegun Lee&#39;;
console.log(me); // {firstName: &quot;Heegun&quot;, lastName: &quot;Lee&quot;}</p>
<p>// 접근자 프로퍼티를 통한 프로퍼티 값의 참조
// 접근자 프로퍼티 fullName에 접근하면 getter 함수가 호출된다.
console.log(me.fullName); // Heegun Lee</p>
<p>// fullName은 접근자 프로퍼티다.
// 접근자 프로퍼티는 get, set, enumerable, configurable 프로퍼티 어트리뷰트를 갖는다.
console.log(Object.getOwnPropertyDescriptor(Person.prototype, &#39;fullName&#39;));
// {get: ƒ, set: ƒ, enumerable: false, configurable: true}</p>
<pre><code>
### 25.7.3_ 클래스 필드 정의 제안(439)
- 클래스 필드란 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어.

- JS도 클래스 기반처럼 작동하도록 바뀜
```js
class Person{
  //클래스 필드 정의
  name = &#39;Lee&#39;;

// this에 클래스 필드 바인딩 X
  this.age = 12; (X)

//참조하는 경우에는 this 필요
    constructor() {
          console.log(name); //refer error
    }

 gender;// 할당안하면 undefined
}

const me = new Person(&#39;Lee&#39;);
// 버전 12 이후 node.js에서는 정상 작동.</code></pre><ul>
<li>인스턴스 초기화 필요 있을 시에는</li>
</ul>
<pre><code class="language-js">class Person {
  //자동으로 추가되기 때문에 클래스 필드 정의할 필요 없다.
  constructor(name) {
    this.name = name;
  }
}

const me = new Person(&#39;Lee&#39;);
console.log(me); // Person {name: &quot;Lee&quot;}</code></pre>
<ul>
<li><p>함수는 일급객체로 함수를 클래스 필드에 할당 가능</p>
<pre><code class="language-js">class Person {
// 클래스 필드에 문자열을 할당
name = &#39;Lee&#39;;

// 클래스 필드에 함수를 할당
getName = function () {
  return this.name;
}
// 화살표 함수로 정의할 수도 있다.
// getName = () =&gt; this.name;
}
</code></pre>
</li>
</ul>
<p>const me = new Person();
console.log(me); // Person {name: &quot;Lee&quot;, getName: ƒ}
console.log(me.getName()); // Lee</p>
<pre><code>위처럼 클래스 필드에 함수를 할당하면 이 함수는 프로토타입 메서드가 아닌 인스턴스 메서드가 됨.
(모든 클래스 필드는 인스턴스 프로퍼티가 되므로 클래스필드에 함수 할당은 권장 X)


### 25.7.4 private 필드 정의
- 인스턴스 프로퍼티는 언제나 외부 참조 가능한 public

- TC39 프로세스 stage3에서 제안 : private 필드 정의는 선두에 &#39;#&#39;

- Typescript는 private, public, protected 모두 지원

```js
class Person {
  // private 필드 정의
  #name = &#39;&#39;;

  constructor(name) {
    // private 필드 참조
    this.#name = name;
  }
}

const me = new Person(&#39;Lee&#39;);

// private 필드 #name은 클래스 외부에서 참조할 수 없다.
console.log(me.#name);
// SyntaxError: Private field &#39;#name&#39; must be declared in an enclosing class</code></pre><ul>
<li><p>접근 가능성 </p>
<blockquote>
<p>접근 여부-&gt; 클래스내부/자식클래스내부/클래스인스턴스통한접근
public / O / O / O
private / O / X / X</p>
</blockquote>
</li>
<li><p>private 필드 정의하는 방법</p>
</li>
</ul>
<pre><code class="language-js">class Person {
  // private 필드 정의
  // 반드시 클래스 몸체에 정의!
  #name = &#39;&#39;;

  // 만약 몸체에 정의 없이 아래의 constructor로만 정의하면 에러가 발생할 것이다.
  constructor(name) {
    this.#name = name;
  }

  // name은 접근자 프로퍼티다.
  get name() {
    // private 필드를 참조하여 trim한 다음 반환한다.
    return this.#name.trim();
  }
}

const me = new Person(&#39; Lee &#39;);
console.log(me.name); // Lee</code></pre>
<h3 id="2575_-static-필드-정의">25.7.5_ static 필드 정의</h3>
<pre><code class="language-js">class MyMath {
  // static public 필드 정의
  static PI = 22 / 7;

  // static private 필드 정의
  static #num = 10;

  // static 메서드
  static increment() {
    return ++MyMath.#num;
  }
}

console.log(MyMath.PI); // 3.142857142857143
console.log(MyMath.increment()); // 11</code></pre>
<h2 id="258_-상속에-의한-클래스확장extends">25.8_ 상속에 의한 클래스확장(extends)</h2>
<blockquote>
<p>기존 클래스를 상속받아 새로운 클래스를 확장(extends)</p>
</blockquote>
<h3 id="2581_-클래스-상속과-생성자-함수-상속">25.8.1_ 클래스 상속과 생성자 함수 상속</h3>
<p>p449 animal 예시 참고!</p>
<pre><code class="language-js">class Animal {
  constructor(age, weight) {
    this.age = age;
    this.weight = weight;
  }

  eat() { return &#39;eat&#39;; }

  move() { return &#39;move&#39;; }
}

// 상속을 통해 Animal 클래스를 확장한 Bird 클래스
class Bird extends Animal {
  fly() { return &#39;fly&#39;; }
}

const bird = new Bird(1, 5);

console.log(bird); // Bird {age: 1, weight: 5}
console.log(bird instanceof Bird); // true
console.log(bird instanceof Animal); // true

console.log(bird.eat());  // eat
console.log(bird.move()); // move
console.log(bird.fly());  // fly</code></pre>
<h3 id="2582_-extends-키워드">25.8.2_ extends 키워드</h3>
<pre><code class="language-js">class Base {}

class Derived extends Base{}</code></pre>
<h3 id="2583_-동적-상속">25.8.3_ 동적 상속</h3>
<ul>
<li>생성자 함수도 상속 가능<pre><code class="language-js">// 생성자 함수
function Base(a) {
this.a = a;
}
</code></pre>
</li>
</ul>
<p>// 생성자 함수를 상속받는 서브클래스
class Derived extends Base {}</p>
<p>const derived = new Derived(1);
console.log(derived); // Derived {a: 1}</p>
<pre><code>- 동적 상속

```js
function Base1() {}

class Base2 {}

let condition = true;

// 조건에 따라 동적으로 상속 대상을 결정하는 서브클래스
class Derived extends (condition ? Base1 : Base2) {}

const derived = new Derived();
console.log(derived); // Derived {}

console.log(derived instanceof Base1); // true
console.log(derived instanceof Base2); // false</code></pre><h3 id="2584_-서브클래스의-constructor">25.8.4_ 서브클래스의 constructor</h3>
<ul>
<li>class에 constructor 생략 시 클래스에는 비어있는 constructor가 암묵적으로 정의됨</li>
<li>자식 클래스에서 생략시 다음과 같은 constructor가 암묵적 생산</li>
</ul>
<pre><code class="language-js">constructor(...args){super(...args);
 //super은 부모 클래스의 constructor를 호출하여 인스턴스 생산</code></pre>
<ul>
<li>부모 자식 둘 다 생략 시<pre><code class="language-js">// 수퍼클래스
class Base {}
</code></pre>
</li>
</ul>
<p>// 서브클래스
class Derived extends Base {}
----- constructor 암묵적 생산 --- 
// 수퍼클래스
class Base {
  constructor() {}
}</p>
<p>// 서브클래스
class Derived extends Base {
  constructor() { super(); }
}</p>
<p>const derived = new Derived();
console.log(derived); // Derived {}</p>
<pre><code>
### 25.8.5_ super 키워드

- super는 함수처럼 호출도, this 처럼 참조할 수 있는 특수 키워드
   - super를 호출하면 부모클래스의 constructor 호출
   - super 참조하면 수퍼클래스의 메서드 호출 가능


- **super 호출**
&lt;부모 클래스 constructor 호출&gt;

```js
// 수퍼클래스
class Base {
  constructor(a, b) { // ④
    this.a = a;
    this.b = b;
  }
}

// 서브클래스
class Derived extends Base {
  constructor(a, b, c) { // ② 인자 받음
    super(a, b); // ③ 부모의 cons 호출
    this.c = c; // 따로 c 생성
  }
}

const derived = new Derived(1, 2, 3); // ①
console.log(derived); // Derived {a: 1, b: 2, c: 3}</code></pre><blockquote>
<p>주의!</p>
</blockquote>
<ol>
<li>constructor 생략 안하는 경우 반드시 super호출하기.<pre><code class="language-js">class Base {}
&gt;
class Derived extends Base {
constructor() {
 // ReferenceError: Must call super constructor in derived class before accessing &#39;this&#39; or returning from derived constructor
 console.log(&#39;constructor call&#39;);
}
}
&gt;
const derived = new Derived();</code></pre>
</li>
<li>자식클래스의 constructor에서 super 호출 전 this 참조 불가<pre><code class="language-js">class Base {}
&gt;
class Derived extends Base {
constructor() {
 // ReferenceError: Must call super constructor in derived class before accessing &#39;this&#39; or returning from derived constructor
 this.a = 1;
 super();
}
}
&gt;
const derived = new Derived(1);</code></pre>
</li>
<li>super는 반드시 자식 클래스의 constructor에서만 호출!</li>
</ol>
<ul>
<li><strong>super 참조</strong>
메서드 내에서 super를 참조하면 수퍼클래스의 메서드 호출 가능</li>
</ul>
<blockquote>
<ol>
<li>super.sayHi의 참조</li>
</ol>
</blockquote>
<pre><code class="language-js">// 수퍼클래스
class Base {
  constructor(name) {
    this.name = name;
  }
&gt;
  sayHi() {
    return `Hi! ${this.name}`;
  }
}
&gt;
// 서브클래스
class Derived extends Base {
  sayHi() {
    // super.sayHi는 수퍼클래스의 프로토타입 메서드를 가리킨다.
    return `${super.sayHi()}. how are you doing?`;
  }
}
&gt;
const derived = new Derived(&#39;Lee&#39;);
console.log(derived.sayHi()); // Hi! Lee. how are you doing?</code></pre>
<blockquote>
</blockquote>
<ol start="2">
<li>자식 클래스의 정적 메서드 내에서 suer.sayHi는 부모 클래스의 정적 메서드 sayHi 가리킴<pre><code class="language-js">// 수퍼클래스
class Base {
static sayHi() {
 return &#39;Hi!&#39;;
}
}
&gt;
// 서브클래스
class Derived extends Base {
static sayHi() {
 // super.sayHi는 수퍼클래스의 정적 메서드를 가리킨다.
 return `${super.sayHi()} how are you doing?`;
}
}
&gt;
console.log(Derived.sayHi()); // Hi! how are you doing?</code></pre>
</li>
</ol>
<h3 id="2586_-상속-클래스의-인스턴스-생성-과정">25.8.6_ 상속 클래스의 인스턴스 생성 과정</h3>
<pre><code class="language-js">// 수퍼클래스
class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }
&gt;
  getArea() {
    return this.width * this.height;
  }
&gt;
  toString() {
    return `width = ${this.width}, height = ${this.height}`;
  }
}
&gt;
// 서브클래스
class ColorRectangle extends Rectangle {
  constructor(width, height, color) {
    super(width, height);
    this.color = color;
  }

  // 메서드 오버라이딩
  toString() {
    return super.toString() + `, color = ${this.color}`;
  }
}
&gt;
const colorRectangle = new ColorRectangle(2, 4, &#39;red&#39;);
console.log(colorRectangle); // ColorRectangle {width: 2, height: 4, color: &quot;red&quot;}
&gt;
// 상속을 통해 getArea 메서드를 호출
console.log(colorRectangle.getArea()); // 8
// 오버라이딩된 toString 메서드를 호출
console.log(colorRectangle.toString()); // width = 2, height = 4, color = red</code></pre>
<ul>
<li><p>과정</p>
<blockquote>
<ol>
<li>자식 클래스의 super 호출
부모는 내부슬롯 값이 &quot;base&quot; 자식은 &quot;derived&quot;로 설정됨.
이 때문에 new 연산자 호출 시 둘은 구별된다.
</br>부모는 new 연산자 호출 시 암묵적으로 빈 객체를 생성하여 이를 this에 바인딩
하지만 자식은 직접 인스턴스를 생성하지 않고 부모에게 인스턴스 생성을 위임하며 이것이 super를 호출해야하는 이유!
따라서 자식이 super 호출 안하면 에러가 나는 이유는 인스턴스 생성의 주체가 부모클래스이기 때문이다.
</br>2. 부모클래스의 인스턴스 생성과 this 바인딩
constructor 내부 코드가 실행되기 이전 빈 객체(인스턴스)가 생성되고 이 객체는 this에 바인딩 된다. 따라서 constructor 내부의 this는 인스턴스를 가리킨다.</br><pre><code class="language-js">// 수퍼클래스
class Rectangle {
constructor(width, height) {
// 암묵적으로 빈 객체, 즉 인스턴스가 생성되고 this에 바인딩된다.
console.log(this); // ColorRectangle {}
// new 연산자와 함께 호출된 함수, 즉 new.target은 ColorRectangle이다.
console.log(new.target); // ColorRectangle
...</code></pre>
이 때 new.target은 ColorRectangle을 가리키는 것을 통해 new 연산자를 통해 생성된 인스턴스는 new target이 가리키는 자식 클래스가 생성한 것으로 처리됨. 따라서 생성된 인스턴스의 프로토타입은 부모의 프로토타입(Rectagle.prototype)이 아닌 자식의 ColorRectangle.prototype 이다.</li>
</ol>
</blockquote>
<pre><code class="language-js">// 수퍼클래스
class Rectangle {
constructor(width, height) {
  // 암묵적으로 빈 객체, 즉 인스턴스가 생성되고 this에 바인딩된다.
  console.log(this); // ColorRectangle {}
  // new 연산자와 함께 호출된 함수, 즉 new.target은 ColorRectangle이다.
  console.log(new.target); // ColorRectangle
&gt;
  // 생성된 인스턴스의 프로토타입으로 ColorRectangle.prototype이 설정된다.
  console.log(Object.getPrototypeOf(this) === ColorRectangle.prototype); // true
  console.log(this instanceof ColorRectangle); // true
  console.log(this instanceof Rectangle); // true
...</code></pre>
<blockquote>
<ol start="3">
<li>수퍼클래스의 인스턴스 초기화</li>
</ol>
</blockquote>
<pre><code class="language-js">// 수퍼클래스
class Rectangle {
constructor(width, height) {
  // 암묵적으로 빈 객체, 즉 인스턴스가 생성되고 this에 바인딩된다.
  console.log(this); // ColorRectangle {}
  // new 연산자와 함께 호출된 함수, 즉 new.target은 ColorRectangle이다.
  console.log(new.target); // ColorRectangle

  // 생성된 인스턴스의 프로토타입으로 ColorRectangle.prototype이 설정된다.
  console.log(Object.getPrototypeOf(this) === ColorRectangle.prototype); // true
  console.log(this instanceof ColorRectangle); // true
  console.log(this instanceof Rectangle); // true
&gt;
  // 인스턴스 초기화
  this.width = width;
  this.height = height;

  console.log(this); // ColorRectangle {width: 2, height: 4}
}
...</code></pre>
<p>~468 까지 책 읽어보는 게 낫겠다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS] 타입은 집합이다!]]></title>
            <link>https://velog.io/@bigwave-cho/TS-%ED%83%80%EC%9E%85%EC%9D%80-%EC%A7%91%ED%95%A9%EC%9D%B4%EB%8B%A4</link>
            <guid>https://velog.io/@bigwave-cho/TS-%ED%83%80%EC%9E%85%EC%9D%80-%EC%A7%91%ED%95%A9%EC%9D%B4%EB%8B%A4</guid>
            <pubDate>Tue, 17 Jan 2023 01:53:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>중점: 타입스크립트의 타입은 속성에 대한 집합이 아닌 <strong>값들의 집합</strong>이다.</p>
</blockquote>
<h2 id="타입은-집합">타입은 집합</h2>
<ul>
<li><strong><code>number</code></strong>, <strong><code>string</code></strong> : 무한집합</li>
<li><strong><code>A|B</code></strong> : A와 B의 합집합</li>
<li><strong><code>A&amp;B</code></strong> : A와 B의 교집합</li>
<li><strong><code>unknown</code></strong> : 모든 집합의 상위 집합(superset) ; 전체집합</li>
<li><strong><code>never</code></strong> : 모든 집합의 부분 집합(subset) ; 공집합<ul>
<li>A | never -&gt; A</li>
<li>A &amp; never -&gt; never   </li>
</ul>
</li>
<li><strong><code>any</code></strong> : 모든 타입의 부분 집합 and 상위집합<ul>
<li>A | any -&gt; any</li>
<li>A &amp; any -&gt; any</li>
</ul>
</li>
</ul>
<h3 id="뇌정지-예제">뇌정지 예제</h3>
<pre><code class="language-js">// script1
type StringOrNull = string | null

type Person = {
    name: string;
};

type HavingBirth = {
  birth: Date;
};

type LifeSpan = Person &amp; HavingBirth;
// { name: string; birth: Date; }</code></pre>
<ul>
<li><p>타입은 집합임을 이해하고 있지 않다면 A&amp;B를 
type A와 B의 공통되는 속성으로 생각하고 결과를 never로 생각하게 된다.</p>
</li>
<li><p>어떻게 위와 같은 결과가 나오는지 공부해보자!</p>
</li>
</ul>
<h3 id="개념-이해">개념 이해</h3>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/515879ef-7f0d-4b4d-bcd3-833f05dbf694/image.png" alt="">&lt;출처:<a href="https://blog.hwahae.co.kr/all/tech/tech-tech/9954&gt;">https://blog.hwahae.co.kr/all/tech/tech-tech/9954&gt;</a></p>
<h4 id="intersection--union">intersection &amp; union</h4>
<blockquote>
<p>Intersection 예시</p>
</blockquote>
<ul>
<li>intersection은 A도 만족하고 B도 만족해야하는 교집합</li>
<li>만약 공통 속성이 없다면 위 이미지의 두 집합은 교집합 부분이 없어지기 때문에
결과는 <strong><code>never</code></strong>!<pre><code class="language-js">interface Person {
name: string;
birth: Date;
}
</code></pre>
</li>
</ul>
<p>interface Lifespan {
  birth: Date;
  death?: Date;
}
// keyof : 객체의 키를 타입으로 만들어준다.
type K = keyof (Person &amp; Lifespan); 
// &quot;name&quot; | &quot;birth&quot; | &quot;death&quot;</p>
<pre><code>---
&gt;Union 예시

- Union은 A이거나 B인 타입을 의미함.
```js
interface Person {
  name: string;
}

interface Lifespan {
  birth: Date;
  death?: Date;
}

type K = keyof (Person | Lifespan); //never


----

interface Person {
  name: string;
  birth: Date;
}

interface Lifespan {
  birth: Date;
  death?: Date;
}

type K = keyof (Person | Lifespan); // birth</code></pre><h3 id="문제-통해-개념-정리하기">문제 통해 개념 정리하기</h3>
<pre><code class="language-js">type A = {
  one: string;
  two: number;
};
type B = {
  two: string;
};
type C = {
  two: string[];
  three: string;
};

type AandB = A &amp; B; // {one:string; two:number &amp; string; } two is never
type AandC = A &amp; C; // {one:string; two:number &amp; string[]; three:string;} two is never
type BandC = B &amp; C; // { two:string &amp; string[]; three:string;} two is never
type AandBandC = A &amp; B &amp; C; // {one:string; two:number &amp; string &amp; string[]; three:string;} two is never

type AorB = A | B; 
type AorC = A | C; 
type BorC = B | C; 
type AorBorC = A | B | C;


// 아래 변수 1~11중에 타입에러를 내뿜는 변수는 무엇일까요?
const A_OR_B1: AorB = {} as A;
// AorB 에는 A가 포함
const A_OR_B2: AorB = {} as B;
// AroB에는 B가 포함
const A_OR_B3: AorB = {} as C;
// Err - AorB에는 C가 포함X
const A_OR_B4: AorB = {} as AandB;
// AorB에는 A와 B의 교집합 포함
const A_OR_B5: AorB = {} as AandC;
// AorB.. A에는 A와 C의 교집합이 포함
const A_OR_B6: AorB = {} as BandC;
// 위와 마찬가지
const A_OR_B7: AorB = {} as AandBandC;
// 마찬가지
const A_OR_B8: AorB = {} as AorB;
// 당연히 포함
const A_OR_B9: AorB = {} as AorC;
// Err AorB에는 AorC(A는 포함 교집합 제외한 C는 포함하지 않으므로)
const A_OR_B10: AorB = {} as BorC;
// Err 위와 동일
const A_OR_B11: AorB = {} as AorBorC;
// Err A와 B와 교집합인 부분을 제외한 C는 A와 B에 포함되지 않으므로</code></pre>
<ul>
<li><a href="https://velog.io/@dltlsgh5/typescript%ED%83%80%EC%9E%85%EC%9D%84-%EC%A7%91%ED%95%A9%EC%9C%BC%EB%A1%9C-%EC%83%9D%EA%B0%81%ED%95%98%EA%B8%B0#:~:text=%EB%AA%85%ED%99%95%ED%95%98%EA%B2%8C%20%EC%9D%B4%ED%95%B4%ED%95%9C%20%EA%B2%83%EC%9E%85%EB%8B%88%EB%8B%A4.-,%EC%8B%AC%ED%99%942%3A%ED%9E%8C%ED%8A%B8,-SINHOLEE">답은 링크의 힌트 이미지를 보면 확실히 이해할 수 있다.</a></li>
</ul>
<blockquote>
<p>참고
 <a href="https://blog.hwahae.co.kr/all/tech/tech-tech/9954">참고1 화해팀 설명글</a>
 <a href="https://velog.io/@dltlsgh5/typescript%ED%83%80%EC%9E%85%EC%9D%84-%EC%A7%91%ED%95%A9%EC%9C%BC%EB%A1%9C-%EC%83%9D%EA%B0%81%ED%95%98%EA%B8%B0">참고2 적절 예시</a>
 <a href="https://velog.io/@gomjellie/You-dont-know-type">참고3</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CSS]Grid]]></title>
            <link>https://velog.io/@bigwave-cho/CSSGrid</link>
            <guid>https://velog.io/@bigwave-cho/CSSGrid</guid>
            <pubDate>Mon, 16 Jan 2023 13:55:48 GMT</pubDate>
            <description><![CDATA[<h2 id="grid">GRID</h2>
<blockquote>
</blockquote>
<p>부모 컨테이너에 display : grid 지정!
=&gt; 자식 요소(grid cell)</p>
<ul>
<li><p>부모</p>
<ul>
<li>grid-template-columns : 열</li>
<li>grid-template-rows    :행</li>
<li>grid-template-areas</li>
<li>grid-gap(행, 열 각각 적용 가능)</li>
</ul>
</li>
<li><p>자식</p>
<ul>
<li>grid-column-start</li>
<li>grid-column-end</li>
<li>grid-row-start</li>
<li>grid-row-end</li>
<li>grid-area</li>
</ul>
</li>
</ul>
<h3 id="사용해보기">사용해보기</h3>
<blockquote>
<p>기본 사용 예)</p>
</blockquote>
<pre><code class="language-js">&lt;div class=&quot;container&quot;&gt;
    &lt;div class=&quot;item color1&quot;&gt;Item1&lt;/div&gt;
    &lt;div class=&quot;item color2&quot;&gt;Item2&lt;/div&gt;
    &lt;div class=&quot;item color3&quot;&gt;Item3&lt;/div&gt;
    &lt;div class=&quot;item color4&quot;&gt;Item4&lt;/div&gt;
    &lt;div class=&quot;item color5&quot;&gt;Item5&lt;/div&gt;
    &lt;div class=&quot;item color1&quot;&gt;Item6&lt;/div&gt;
    &lt;div class=&quot;item color2&quot;&gt;Item7&lt;/div&gt;
    &lt;div class=&quot;item color3&quot;&gt;Item8&lt;/div&gt;
    &lt;div class=&quot;item color4&quot;&gt;Item9&lt;/div&gt;
    &lt;div class=&quot;item color5&quot;&gt;Item10&lt;/div&gt;
  &lt;/div&gt;
// css
.container {
  display: grid;
  grid-template-columns: repeat(5, 100px);
  //아래와 같음.
  //grid-template-columns: 100px 100px 100px 100px 100px;
  grid-template-rows: 100px 200px 100px 100px;
  //아래와 같음
  //grid-template-rows: 100px 200px repeat(2, 100px);
}</code></pre>
<blockquote>
<p>반응형을 위한 리팩토링</p>
</blockquote>
<pre><code class="language-js">// 전체를 5분의 1씩!
grid-template-columns: repeat(5, 1fr);

//넓이의 10%씩 넓이
grid-template-columns: repeat(5, 10%);</code></pre>
<pre><code class="language-js">//몇 줄인지 모르겠다만 행 높이 지정
grid-auto-rows: 150px;                          </code></pre>
<ul>
<li>아래와 같이 컨텐츠가 잘리지 않도록
<img src="https://velog.velcdn.com/images/bigwave-cho/post/9a9907c3-842b-4bbf-a1a7-bbc2e0ddb2b5/image.png" alt=""></li>
</ul>
<pre><code class="language-js">grid-auto-rows: minmax(150px, auto);
//기본값 150px, 컨텐츠에 따라 증가</code></pre>
<ul>
<li><p>gap을 주고 싶다</p>
<pre><code class="language-js">gap
column-gap
row-gap</code></pre>
</li>
<li><p>특정 grid cell 영역 늘리기
<img src="https://velog.velcdn.com/images/bigwave-cho/post/f121ae65-8ad5-44b8-85b6-2462053b3b43/image.png" alt="드림코딩">출처) 드림코딩(<a href="https://www.youtube.com/watch?v=nxi1EXmPHRs&amp;t=168s">https://www.youtube.com/watch?v=nxi1EXmPHRs&amp;t=168s</a>)
row도 마찬가지로 적용!</p>
</li>
</ul>
<pre><code class="language-js">//index.html
// 3번 요소한테 item2 적용
&lt;div class=&quot;item item2 color3&quot;&gt;Item3&lt;/div&gt;

//grid.css
.item2 {
  각각 아래와 동일
  grid-column: 2/4;
  //grid-column-start: 2;
  //grid-column-end: 4;

  grid-row: 1/3;
  //grid-row-start: 1;
  //grid-row-end: 3;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/6e969b29-332c-40a6-b6dd-7e71786999b6/image.png" alt=""></p>
<blockquote>
<p>추가</p>
</blockquote>
<pre><code class="language-js">grid-column: 2/span3 // 2부터 3개의 스팬만큼
grid-column: 2/ -1 // 2부터 끝까지</code></pre>
<h3 id="grid-area">grid-area</h3>
<pre><code class="language-js">&lt;img
      src=&quot;https://images.unsplash.com/photo-1661933021480-75b708d5648f?ixlib=rb-1.2.1&amp;ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwzfHx8ZW58MHx8fHw%3D&amp;auto=format&amp;fit=crop&amp;w=500&amp;q=60&quot;
      alt=&quot;img&quot; class=&quot;image image1&quot; /&gt;
    &lt;img
      src=&quot;https://images.unsplash.com/photo-1661933021480-75b708d5648f?ixlib=rb-1.2.1&amp;ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwzfHx8ZW58MHx8fHw%3D&amp;auto=format&amp;fit=crop&amp;w=500&amp;q=60&quot;
      alt=&quot;img&quot; class=&quot;image image2&quot; /&gt;

// css
.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 150px;
  gap: 1rem;
  grid-template-areas:
    &#39;a a a&#39;
    &#39;b c c&#39;
    &#39;b d g&#39;
    &#39;e f g&#39;;
}

.image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.image1 {
  grid-area: a;
}

.image2 {
  grid-area: b;
}

.image3 {
  grid-area: c;
}

.image4 {
  grid-area: d;
}

.image5 {
  grid-area: e;
}

.image6 {
  grid-area: f;
}

.image7 {
  grid-area: g;
}        
</code></pre>
<h3 id="grid-area-실습">grid area 실습</h3>
<p><img src="https://velog.velcdn.com/images/bigwave-cho/post/b1ae9392-f5e1-4d59-b4d0-5398f393ee8b/image.png" alt=""></p>
<pre><code class="language-js">//html
&lt;body&gt;
  &lt;header&gt;header&lt;/header&gt;
  &lt;main&gt;main&lt;/main&gt;
  &lt;aside&gt;aside&lt;/aside&gt;
  &lt;footer&gt;footer&lt;/footer&gt;
&lt;/body&gt;

// css
body {
  display: grid;
  grid-template-columns: 3fr 1fr;
  grid-template-areas:
    &#39;header header&#39;
    &#39;main aside&#39;
    &#39;footer footer&#39;;
  grid-template-rows: 100px auto 50px;
}

header {
  grid-area: header;
  background-color: black;
  color: wheat;
}

main {
  grid-area: main;
}

aside {
  grid-area: aside;
  background-color: yellow;
}

footer {
  grid-area: footer;
  background-color: black;
  color: aliceblue;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Context API]]></title>
            <link>https://velog.io/@bigwave-cho/React-Context-API</link>
            <guid>https://velog.io/@bigwave-cho/React-Context-API</guid>
            <pubDate>Fri, 13 Jan 2023 01:16:54 GMT</pubDate>
            <description><![CDATA[<h2 id="context-api">Context API</h2>
<h3 id="왜-사용할까요">왜 사용할까요?</h3>
<ul>
<li>리액트에서는 보통 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달할 때
속성값을 이용합니다.</li>
<li>간단한 구조에서는 이 방법으로 충분하지만 구조가 복잡하다면 번거로워지고 관리가 힘들어지게 됩니다.</li>
<li>그러나 Context API를 이용하면 컴포넌트의 중첩 구조가 복잡하더라도 비교적 쉽게 데이터를 전달할 수 있습니다.</li>
</ul>
<h3 id="props과-context-api의-비교">props과 Context API의 비교</h3>
<blockquote>
</blockquote>
<p>Prop : 조부모(data전달) -&gt; 부모(data 사용X 전달) -&gt; 자식(사용)
Context API : 조부모 -&gt; 자식(사용)</p>
<h3 id="작동원리-이해하기">작동원리 이해하기</h3>
<pre><code class="language-js">//1. createContext를 import
import {createContext} from &#39;react&#39;


// 상위 컴포넌트
// 2. Context 객체 생성
const UserContext = createContext(초기값)
// createContext(초기값)은 {Provider, Consumer} 객체를 반환
// createContext(defaultValue) =&gt; {Provider, Consumer}
function App() {
  return (
    &lt;div&gt;
        &lt;UserContext.Provider value=전달할값&gt;
            &lt;Profile /&gt;
        &lt;/UserContext.Provider&gt;
    &lt;/div&gt;
    );
}
.....
// Profile 컴포넌트의 자아아아~~식 컴포넌트
// 3. 사용할 컴포넌트에서 Consumer로 감싼다.
function 자아아아식(){
  return (
    &lt;UserContext.Consumer&gt;
      {value =&gt; &lt;p&gt;{`${value}`}&lt;/p&gt;}
    &lt;/UserContext.Consumer}
    );
}</code></pre>
<ul>
<li>상위 컴포넌트에서 Provider로 데이터를 전달하고 하위 컴포넌트에서는 Consumer로 데이터를 사용한다.</li>
<li>Consumer 컴포넌트는 데이터를 찾기 위해 상위로 올라가면서 가장 가까운 Provider를 찾고 최상위에도 Provider 컴포넌트가 없다면 Context객체 생성시 설정한 기본값을 사용한다.<blockquote>
<p>알아두기! : Provider 컴포넌트에 새로운 데이터가 입력되면 모든 Consumer 컴포넌트는 부모 컴포넌트에 memo등이 적용되는 등의 중간 컴포넌트의 렌더링 여부와 상관없이 다시 렌더링된다.</p>
</blockquote>
</li>
</ul>
<h3 id="활용-여러-context-중첩-사용">활용) 여러 context 중첩 사용</h3>
<pre><code class="language-js">const UserContext = createContext(&quot;&quot;)
const ThemeContext = createContext(&#39;dark&#39;)
// createContext(초기값)은 {Provider, Consumer} 객체를 반환
// createContext(defaultValue) =&gt; {Provider, Consumer}
function App() {
  return (
    &lt;div&gt;
     &lt;ThemeContext.Provider value=&#39;light&#39;&gt;
        &lt;UserContext.Provider value=전달할값&gt;
            &lt;Profile /&gt;
        &lt;/UserContext.Provider&gt;
     &lt;/ThemeContext.Provider&gt;
    &lt;/div&gt;
    );
}
..... 자아아식

function 자아아아식(){
  return (
    &lt;ThemeContext.Consumer&gt;
     {theme =&gt; (
       &lt;UserContext.Consumer&gt;
        {value =&gt; &lt;p style={{color: theme}}&gt;{`${value}`}&lt;/p&gt;}
       &lt;/UserContext.Consumer&gt;
      )}
     &lt;/ThemeContext.Consumer&gt;
    );
 }</code></pre>
<h3 id="하위-컴포넌트에서-데이터-수정하기">하위 컴포넌트에서 데이터 수정하기!</h3>
<pre><code class="language-js">const UserContext = createContext({username:&#39;&#39;, helloCount:0});
const SetUserContext = createContext(()=&gt;{});

function App(){
  const [user, setUser] =&gt; useState({username:&#39;mike&#39;, helloCount:0)};

  return (
           &lt;div&gt;
             &lt;SetUserContext.Provider value={setUser}&gt;
                &lt;UserContext.Porvider value={user}&gt;
                  &lt;Profile /&gt;
                &lt;/UserContext.Provider&gt;
             &lt;/SetUserContext.Provider&gt;
            &lt;/div&gt;
        );
  }

</code></pre>
<ul>
<li>Consumer은 중첩사용에서 쓴 코드처럼 쓰면 된다!</li>
<li>원시값 수정은 useState가 편리하지만 위 코드처럼 객체를 변경하는 훅은
useReducer를 사용하는 것이 편리하다. <a href="https://github.com/bigwave-cho/learnFirebase/blob/main/src/context/AuthContext.js#:~:text=const%20%5Bstate%2C%20dispatch%5D%20%3D%20useReducer(authReducer%2C%20%7B">useReducer 사용 예 링크</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS] Part2 - 살짝 심화]]></title>
            <link>https://velog.io/@bigwave-cho/TS-Part2-%EC%82%B4%EC%A7%9D-%EC%8B%AC%ED%99%94%EC%9E%91%EC%84%B1%EC%A4%91</link>
            <guid>https://velog.io/@bigwave-cho/TS-Part2-%EC%82%B4%EC%A7%9D-%EC%8B%AC%ED%99%94%EC%9E%91%EC%84%B1%EC%A4%91</guid>
            <pubDate>Wed, 11 Jan 2023 13:29:06 GMT</pubDate>
            <description><![CDATA[<h2 id="함수-rest-파라미터-destructuring-타입-지정">함수 rest 파라미터, destructuring 타입 지정</h2>
<h3 id="rest-parameter">rest parameter</h3>
<pre><code class="language-js">function 함수(...a: number[]) {
  // 여러 파라미터가 배열로 들어옴
  console.log(a);
}

함수(1, 2, 3); //[ 1, 2, 3 ]</code></pre>
<h3 id="spread-operator">spread operator</h3>
<pre><code class="language-js">let arr = [1, 2, 3];
let arr2 = [4, 5];
let arr3 = [...arr, ...arr2];
console.log(...arr3); //1 2 3 4 5

let obj = { a: &#39;b&#39;, b: &#39;c&#39; };
console.log({ ...obj }); //{ a: &#39;b&#39;, b: &#39;c&#39; }</code></pre>
<blockquote>
<p>MDN spread 문법
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Spread_syntax</a></p>
</blockquote>
<h3 id="destructuring">destructuring</h3>
<ul>
<li>배열이나 객체의 값들을 변수에 쉽게 할당해준다.</li>
<li>let 변수1 = 배열[0]; 이렇게 안해도 되겠지.<pre><code class="language-js">let [변수1, 변수2] = [&#39;안녕&#39;, 100];
console.log(변수1, 변수2); //안녕 100
</code></pre>
</li>
</ul>
<p>// let { student, age } = { student: true, age: 20 };
//사실 정확히는
let { student: student, age: age } = { student: true, age: 20 };
//student변수에는 오른쪽 객체의 student프로퍼티의 값을 할당해주세요~ 이런 의미
console.log(student, age); //true 20</p>
<p>// 응용
interface Info {
  student: boolean;
  age: number;
}</p>
<p>function 함슈({ student, age }: Info) {
  console.log(student, age); //true 20
}</p>
<p>함슈({ student: true, age: 20 });</p>
<pre><code> ### 연습해보기
 - 숫자 넣으면 가장 큰 수 구하기
 Math.max 금지
```js
type FindMax = (...x: number[]) =&gt; number;

const findMax: FindMax = (...nums) =&gt; {
  let maxNum: number = 0;
  nums.forEach((num) =&gt; {
    if (maxNum &lt;= num) maxNum = num;
  });
  return maxNum;
};

findMax(5, 2, 3, 4);</code></pre><h3 id="연습2">연습2</h3>
<pre><code class="language-js">const 연습2 = ({
  user,
  comment,
  admin,
}: {
  user: string;
  comment: number[];
  admin: boolean;
}) =&gt; {
  console.log(user, comment, admin);
};

 연습2({ user: &#39;kim&#39;, comment: [3, 5, 4], admin: false });</code></pre>
<h3 id="연습3">연습3</h3>
<pre><code class="language-js">const 연습3 = ([a, b, c]: (number | string | boolean)[]) =&gt; {
  console.log(a, b, c);
};
연습3([40, &#39;wine&#39;, false]);</code></pre>
<hr>
<hr>
<h2 id="narrowing-더-알아보기">Narrowing 더 알아보기</h2>
<h3 id="연산자로-null--undefined-체크">&amp;&amp; 연산자로 null &amp; undefined 체크!</h3>
<pre><code class="language-js">function 함수(a: string | undefined) {
  // a 가 undefined가 아니고 스트링임을 한 줄로.
  if (a &amp;&amp; typeof a === &#39;string&#39;) {
  }
}</code></pre>
<h3 id="in-키워드">in 키워드</h3>
<pre><code class="language-js">type Fish = { swim: string };
type Bird = { fly: string };

function 함슈(animal: Fish | Bird) {
  // animal.swim -&gt; 아직 확실하지 않아서 에러.
  if (&#39;swim&#39; in animal) {
    console.log(animal.swim);
  } else if (&#39;fly&#39; in animal) {
    console.log(animal.fly);
  }
}</code></pre>
<h3 id="인스턴스객체-instanceof-부모class">인스턴스(객체) instanceof 부모class</h3>
<blockquote>
<p>참고 인스턴스란?
 생성자 함수 또는 클래스가 만들어낸 객체를 의미.</p>
</blockquote>
<pre><code class="language-js">let 날짜 = new Date();

if (날짜 instanceof Date) {
  console.log(&#39;날짜는 Date의 자식이에요!&#39;);
} //날짜는 Date의 자식이에요!</code></pre>
<h3 id="비슷한-객체-타입이-많을-때">비슷한 객체 타입이 많을 때?</h3>
<ul>
<li>타입에 literal type을 강제로 만들어두면 도움됨.</li>
</ul>
<pre><code class="language-js">type Car = {
  wheel: &#39;4개&#39;;
  color: string;
};
type Bike = {
  wheel: &#39;2개&#39;;
  color: string;
};

function 예제3(x: Car | Bike) {
  if (x.wheel === &#39;4개&#39;) {
    console.log(&#39;차에요&#39;);
  } else if (x.wheel === &#39;2개&#39;) {
    console.log(&#39;바이크에요&#39;);
  }
}</code></pre>
<hr>
<hr>
<h2 id="함수의-never">함수의 never</h2>
<blockquote>
<p><strong>never</strong></p>
</blockquote>
<ol>
<li>절대 return 하지 않는다.</li>
<li>함수 실행이 끝나지 않는다(endpoint 존재 X)<blockquote>
</blockquote>
함수에 never지정해주면 타입 에러가 뜸.
모든 함수에는 return을 직접 지정해주지 않으면
기본적으로 return undefined를 하고 있기 때문.</li>
</ol>
<h3 id="throw-new-error">throw new Error()</h3>
<pre><code class="language-js">function 함수(): never {
  throw new Error();
  // 다음 코드를 실행하기 전에 Error로 인해 멈춤.
}</code></pre>
<h3 id="while">while</h3>
<pre><code class="language-js">function 함수2(): never {
  while (true) {
    //코드
  }
  // 영원히 끝나지 않는 while 문.
}</code></pre>
<h3 id="어디-써요">어디 써요?</h3>
<ul>
<li>보통 void로 해결되기 때문인데..</li>
</ul>
<h4 id="never-등장-case-1">never 등장 case 1</h4>
<ul>
<li>그런 경우가 없어!</li>
</ul>
<pre><code class="language-js">function 함수3(param: string) {
  if (typeof param === &#39;string&#39;) {
    console.log(param);
  } else {
    console.log(param); // never
  }
  // param은 스트링만 들어올 수 있으니까 never
}</code></pre>
<h4 id="never-등장-case-2">never 등장 case 2</h4>
<ul>
<li>함수 표현식</li>
</ul>
<pre><code class="language-js">let 함수4 = function () {
  throw new Error();
};</code></pre>
<blockquote>
<p>*<em>결론 *</em></p>
</blockquote>
<ul>
<li>굳이 사용되는 경우는 없고 never가 뜨면 왜 그런지 이해만</li>
</ul>
<hr>
<hr>
<h2 id="public-private">public, private</h2>
<h3 id="class-요약-정리">class 요약 정리</h3>
<pre><code class="language-js">class Case1 {
  name = &#39;kim&#39;;
}

class Case2 {
  name;

  constructor() {
    this.name = &#39;kim&#39;;
  }
}
// Case1, Case2 는 똑같다.
// 하지만 constructor의 파라미터를 이용해서
// 생성되는 인스턴스들의 속성 변경 가능.</code></pre>
<h3 id="public">public</h3>
<ul>
<li>모든 인스턴스들이 사용 가능.</li>
</ul>
<pre><code class="language-js">class User {
  public name = &#39;kim&#39;;

  constructor(a: string) {
    this.name = a;
  }

  public 프로토타입에추가되는함수() {
    console.log(&#39;프로토타입함수&#39;);
  }
}

let 유저1 = new User(&#39;park&#39;);

유저1.name = &#39;kaka&#39;;
console.log(유저1); // kaka</code></pre>
<h3 id="private-키워드">private 키워드</h3>
<blockquote>
</blockquote>
<ul>
<li>특정 속성을 보호하고 싶을 때</li>
<li>생성자 class에서 직접 조회는 가능함.</li>
</ul>
<pre><code class="language-js">class User2 {
  private name = &#39;kim&#39;;
  constructor(a: string) {
    this.name = a;
  }
}

let 유저2 = new User2(&#39;park&#39;);
console.log(유저2.name);
// (에러문구) property name은 private이라
// User2 클래스 내에서만 수정, 사용이 가능하다.</code></pre>
<blockquote>
<p>사용 예시</p>
</blockquote>
<pre><code class="language-js">class Example {
  name: string;
  private familyName: string = &#39;kim&#39;;
&gt;
  constructor(a: string) {
    this.name = a + this.familyName;
  }
}
&gt;
let 예시 = new Example(&#39;호호&#39;);
예시.name; // 호호KIM
&gt;
// familyName은 다른 사람이 변경하면 안될 일!
&gt;
// 중요 프로퍼티 등은 private를 이용해서
// 수정 등을 방지</code></pre>
<h4 id="class-밖에서-수정가능하게-할-수-있다">class 밖에서 수정가능하게 할 수 있다?</h4>
<ul>
<li>전국민의 성을 바꾸라는 명령</li>
</ul>
<pre><code class="language-js">class Example2 {
  name: string;
  private familyName: string = &#39;kim&#39;;

  constructor(a: string) {
    this.name = a + this.familyName;
  }

  성씨변경함수(a: string) {
    //프로토타입
    this.familyName = a;
  }
}

let 시민 = new Example2(&#39;봄봄&#39;);
시민.성씨변경함수(&#39;park&#39;);
console.log(시민);
//Example2 { familyName: &#39;park&#39;, name: &#39;봄봄kim&#39; }</code></pre>
<h3 id="public-쓰면-this-생략-가능">public 쓰면 this. 생략 가능</h3>
<ul>
<li><p>기존</p>
<pre><code class="language-js">class Person {
name;
constructor() {
  this.name = &#39;KAK&#39;;
}
}</code></pre>
</li>
<li><p>public 이용</p>
<pre><code class="language-js">class Person2 {
constructor(public name: string) {
  // 이 자리의 파라미터는 자식의 name 속성에 할당해주세요
  // =&gt; feild에 name과 컨스트럭터에 this.name = name 생략 가능.
}
}
</code></pre>
</li>
</ul>
<p>let person1 = new Person2(&#39;이름&#39;);
console.log(person1);
//Person2 { name: &#39;이름&#39; }</p>
<pre><code>---
---
## class extends &amp; protected, static

 ### extends
```js
class User {
  x = 10;
}

class Copy extends User {}

const CopyUser = new Copy();
console.log(CopyUser); //Copy { x: 10 }</code></pre><h3 id="protected">protected</h3>
<ul>
<li>extends 된 클래스는 사용가능</li>
<li>생성된 인스턴스는 사용 불가능!</li>
</ul>
<pre><code class="language-js">// private은 클래스나 인스턴스나 사용 불가능.
class Private {
  private x = 10;
}
// extends 클래스에서 x 사용 불가.
class NewPrivate extends Private {}

class Protect {
  protected x = 10;
}
// extends 클래스 내에서 x 사용 가능.
class NewProtec extends Protect {
  doThis() {
    this.x = 20;
  }
}</code></pre>
<h3 id="static">static</h3>
<ul>
<li>static은 부모 클래스에 직접 부여되며 인스턴스에 물려주지 않음.</li>
</ul>
<pre><code class="language-js">class Uer {
  static x = 10;
  y = 20;
}

let 자식 = new Uer();
console.log(자식); // Uer { y: 20 }</code></pre>
<ul>
<li><p>x 는 부모만
<code>console.log(Uer.x); //10</code></p>
</li>
<li><p>y는 부모가 못쓰고 자식만.
<code>console.log(Uer.y); //undefined</code></p>
</li>
<li><p>private / protected / public 과 동시 사용 가능</p>
</li>
</ul>
<pre><code class="language-js">class Uer2 {
  private static x = 10;
  y = 20;
}

class Apply {
  static skill = &#39;JS&#39;;
  intro = `${Apply.skill} 전문가입니다.`;
  // this.skill 에러
  // static은 부모만의 프로퍼티
}

const 철수 = new Apply();
console.log(철수);
// Apply { intro: &#39;JS 전문가입니다.&#39; }

Apply.skill = &#39;TS&#39;;

const 영희 = new Apply();
console.log(영희);
// Apply { intro: &#39;TS 전문가입니다.&#39; }</code></pre>
<h3 id="연습-1">연습 1</h3>
<pre><code class="language-js">class 연습 {
  private static x = 10;
  public static y = 20;
  protected z = 30;
}
/*
1. 필드값은 원래는 모든 User의 자식들에게 물려주는 속성이지만 

x와 y에는 static 키워드가 붙었기 때문에 User.x 이런 식으로만 접근해서 쓸 수 있습니다.

User의 자식들은 x와 y를 쓸 수 없습니다.



2. private static x는 class 내부에서만 수정가능합니다. 



3. public static y는 class 내부 외부 상관없이 수정가능합니다. public 키워드 지워도 똑같이 동작할 듯 



4. protected z는 private 키워드와 유사하게 class 내부에서만 사용이 가능한데 

약간 범위가 넓어서 extends로 복사한 class 내부에서도 사용할 수 있습니다. 
*/
class 연습확장 extends 연습 {}

const practice = new 연습();
console.log(연습);
console.log(practice);</code></pre>
<h3 id="연습2-1">연습2</h3>
<pre><code class="language-js">class 연습2 {
  private static x = 10;
  public static y = 20;

  addOne(num: number) {
    연습2.x += num;
  }
  printX() {
    console.log(연습2.x);
  }
}</code></pre>
<h3 id="연습3-1">연습3</h3>
<pre><code class="language-js">class Square {
  x;
  y;
  color;
  constructor(a: number, b: number, c: string) {
    this.x = a;
    this.y = b;
    this.color = c;
  }
  draw() {}
}
let 네모 = new Square(30, 30, &#39;red&#39;);

// 답
class Square {
  constructor(
    public width: number,
    public height: number,
    public color: string
  ) {}
  draw() {
    let a = Math.random();
    let square = `&lt;div style=&quot;position:relative; 
      top:${a * 400}px; 
      left:${a * 400}px; 
      width:${this.width}px; 
      height : ${this.height}px; 
      background:${this.color}&quot;&gt;&lt;/div&gt;`;
    document.body.insertAdjacentHTML(&#39;beforeend&#39;, square);
  }
}

let 네모 = new Square(30, 30, &#39;red&#39;);
네모.draw();
네모.draw();
네모.draw();
네모.draw();</code></pre>
<hr>
<hr>
<h2 id="import-export-namespace">import export namespace</h2>
<pre><code class="language-js">//export(A.ts)
export let 변수 = &#39;kim&#39;;
export let 변수2 = 20;

export type Name = string;
export interface 인터페이스 {}

namespace 네임스페이스 {
  export type Name = string | number;
}

module 네임스페이스와같음 {

}

// import(index.ts)
import { 변수, 변수2, Name} from &#39;./a&#39;;

console.log(변수, 변수2); //kim 20

let 이름: Name = &#39;park&#39;;

// import, export가 있기 전엔
// &lt;script src=&#39;a.js /&gt;
// 이런식으로 불러왔음.
// 해당 소스 파일의 모든 코드를 불러오기 때문에
// 변수가 겹치는 등의 문제가 발생함.
// 그래서 그 당시엔 namespace 문법을 사용함.

///&lt;referenc path=&#39;./a.ts&#39; /&gt;
네임스페이스.Name;
</code></pre>
<hr>
<hr>
<h2 id="generic">Generic</h2>
<pre><code class="language-js">function 함수(x: unknown[]) {
  return x[0];
}

let a = 함수([4, 2]);
// a의 타입 unknown
// return이 숫자라도 자동 타입 변환하지 않음.
console.log(a + 1);
// error -&gt;narrowing 해결 but 불편함.</code></pre>
<h3 id="generic-사용-예">generic 사용 예</h3>
<pre><code class="language-js">function 제네릭&lt;아무거나&gt;(x: 아무거나[]): 아무거나 {
  return x[0];
}
//파라미터로 타입을 지정 가능.
let b = 제네릭&lt;number&gt;([4, 2]);
console.log(b + 1); // 해결

let c = 제네릭&lt;string&gt;([&#39;4&#39;, &#39;2&#39;]);

let d = 제네릭([true, false]);
// typeof d === boolean (지정안해줘도 알아서 해줌.)</code></pre>
<h3 id="constraints제네릭의-타입-제한하기">constraints(제네릭의 타입 제한하기.)</h3>
<pre><code class="language-js">function 함슈&lt;MyType&gt;(x: MyType) {
  return x - 1;
  // 미리 x는 any, bigInt, number 등의 타입만 가능하다고
  // 에러를 띄워줌.
}
// narrowing 번거로우니
function 함슈&lt;MyType extends number | bigint&gt;(x: MyType) {
  return x - 1;
}
let result = 함슈&lt;number&gt;(100);</code></pre>
<h4 id="커스텀-타입도-constraints-가능">커스텀 타입도 constraints 가능</h4>
<pre><code class="language-js">interface LengthCheck {
  length: number;
}

function lengthChecker&lt;MyType extends LengthCheck&gt;(x: MyType) {
  return x.length;
}
let fw = lengthChecker&lt;string&gt;(&#39;hi&#39;);
let f = lengthChecker&lt;number&gt;(1234);

class 제너릭&lt;MyType&gt; {
  a: MyType;
  constructor(x: MyType) {
    this.a = x;
  }
}
const 뉴클 = new 제너릭&lt;number&gt;(2);

console.log(뉴클);
//제너릭 { a: 2 }</code></pre>
<h4 id="연습1">연습1</h4>
<p>//문자를 집어넣으면 문자의 갯수, array를 집어넣으면 array안의 자료 갯수를 콘솔창에 출력해주는 함수</p>
<pre><code class="language-js">function 함수&lt;T extends string | string[]&gt;(x: T) {
  console.log(x.length);
}</code></pre>
<h4 id="연습2-2">연습2</h4>
<pre><code class="language-js">interface Animal {
  name: string;
  age: number;
}

let data = &#39;{&quot;name&quot; : &quot;dog&quot;, &quot;age&quot; : 1 }&#39;;

function convert&lt;T extends Animal&gt;(x: string): T {
  return JSON.parse(x);
}
// 또는 extends 안쓰고
convert&lt;Animal&gt;(data);</code></pre>
<h4 id="연습3-2">연습3</h4>
<pre><code class="language-js">class Person&lt;T&gt; {
  name;
  constructor(a: T) {
    this.name = a;
  }
}
let a = new Person&lt;string&gt;(&#39;어쩌구&#39;);
a.name; //any 타입이 아닌 제너릭에 따라 타입지정되도록.</code></pre>
<hr>
<hr>
<h2 id="tuple-type">tuple type</h2>
<pre><code class="language-js">let dog: (string | boolean)[] = [&#39;dog&#39;, true];
// 첫 인덱스 요소는 스트링 두번째는 불리언!
let dog1: [string, boolean] = [&#39;dog&#39;, true];

//옵셔널도 가능.
let cat: [string, boolean?] = [&#39;dog&#39;]; //에러 X

//주의
let cat1: [string, boolean?, number];
// 요소 순서가 불확실해져서 옵셔널은 마지막 요소만</code></pre>
<h3 id="함수-tuplerest---parameter">함수 tuple(rest - parameter)</h3>
<pre><code class="language-js">// number[] 대신 tuple도 가능.
function 함수(...x: [number, number, number, number]) {
  console.log(x);
}
함수(1, 2, 3, 4);</code></pre>
<h3 id="array-합칠-때spread-operator">array 합칠 때(spread operator)</h3>
<pre><code class="language-js">let arr = [1, 2, 3];
let arr2 = [4, 5];
let combine = [...arr, ...arr2];

//이렇게 쓰면 됨.
let tuple: [number, number, ...number[]] = [4, 5, ...arr];</code></pre>
<hr>
<hr>
<h2 id="declare--ambient-module">declare &amp; ambient module</h2>
<blockquote>
<p>.js 파일의 변수를 .ts에서 이용하기</p>
</blockquote>
<h3 id="declare">declare</h3>
<ul>
<li>어딘가 분명 변수 a가 있으니 에러내지 말아달라는 의미.</li>
<li>변수 재정의가 가능하다.</li>
<li>declare는 js로 컴파일되지 않음.</li>
<li>js로 만든 라이브러리를 TS에서 사용할 때 많은 에러가 발생하기 때문에
변수나 함수를 declare로 재정의 하는 경우가 많음.</li>
</ul>
<pre><code class="language-js">// .js
var a = 10;
var b = { name: &#39;kim&#39; };

// .ts
declare let a: number;
console.log(a + 1);</code></pre>
<h3 id="ambient-module글로벌-모듈">ambient module(글로벌 모듈)</h3>
<h4 id="ts---ts는">ts -&gt; ts는?</h4>
<ul>
<li>import export하면 됨.</li>
<li>ts의 특징 : 모든 ts 파일은 ambient module(글로벌 모듈)</li>
<li>그래서 import export 없어도 global 변수선언이 되어</li>
<li>ts 끼리 서로 변수를 사용 가능함.</li>
</ul>
<blockquote>
<p><code>let name // error</code> 뜨는 이유
ts기본 파일에 이미 let name이 있기 때문.</p>
</blockquote>
<p>ambient module 예시</p>
<pre><code class="language-js">//data.ts
var a = 10;

// index.ts
console.log(a); // error X

let a; // cannot redeclare error</code></pre>
<h4 id="로컬-모듈로-만들어버리기">로컬 모듈로 만들어버리기</h4>
<pre><code class="language-js">//data.ts
var a = 10;
export {}
// index.ts

let a; //good</code></pre>
<h4 id="로컬모듈에-글로벌-변수도-쓰고싶다">로컬모듈에 글로벌 변수도 쓰고싶다</h4>
<ul>
<li>declare global {}<pre><code class="language-js">// data.ts
let a = 10;
declare global {
type Dog = string;
}
</code></pre>
</li>
</ul>
<p>export { a };
// index.ts
import { a } from &#39;./data.js&#39;;
console.log(a + 1);</p>
<p>let b: Dog = &#39;kim&#39;;</p>
<pre><code>

## d.ts 파일 이용하기
### &lt;파일명&gt;.d.ts 파일의 사용처
1. 타입정의만 따로 저장해서 import해서 사용
2. 프로젝트에서 사용하는 타입을 쭉 정리할 레퍼런스용

#### import해서 사용하기
```js
// ## 파일명.d.ts
// 타입정의 보관용 파일

export type Age = number;
export interface Person {
  name: string;
}

// ## index.ts

import { Age } from &#39;./test&#39;;
// 또는
import * as 변수명 from &#39;./test&#39;;

let age: Age;</code></pre><h4 id="dts-파일을-레퍼런스용으로-사용하기">d.ts 파일을 레퍼런스용으로 사용하기</h4>
<ul>
<li>declaration 옵션을 true로 설정하면
ts파일 저장시 자동으로 d.ts파일이 생성됨.</li>
</ul>
<pre><code class="language-js">// tsconfig.json
{
  &quot;compilerOptions&quot;: {
    &quot;target&quot;: &quot;es5&quot;,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;noImplicitAny&quot;: true,
    &quot;strictNullChecks&quot;: true,
    &quot;declaration&quot;: true
  }
}</code></pre>
<hr>
<p>이렇게 저장된다.</p>
<pre><code class="language-js">//(index.ts)

let 이름 :string = &#39;kim&#39;;
let 나이 = 20;
interface Person { name : string } 
let 사람 :Person = { name : &#39;park&#39; }
--
//(index.d.ts)

declare let 이름: string;
declare let 나이: number;
interface Person {
    name: string;
}
declare let 사람: Person;</code></pre>
<blockquote>
</blockquote>
<p>주의!</p>
<ul>
<li>d.ts파일은 글로벌 모듈이 아니다.</li>
<li>글로벌 모듈로 사용하고 싶다면<pre><code class="language-js">{
&quot;compilerOptions&quot;: {
  &quot;target&quot;: &quot;es5&quot;,
  &quot;module&quot;: &quot;commonjs&quot;,
  &quot;noImplicitAny&quot;: true,
  &quot;strictNullChecks&quot;: true,
  &quot;declaration&quot;: true,
  &quot;typeRoots&quot;: [&quot;./types&quot;]
}
}</code></pre>
</li>
<li>typeRoots 폴더 내의 d.ts파일 타입들을 전역으로 사용가능하게 하는 옵션.</li>
</ul>
<pre><code class="language-js">// types폴더의 common폴더에 위치한 test.d.ts 파일.

type Age = number;

// index.ts

let 나이: Age = 30;</code></pre>
<h4 id="외부-라이브러리-등을-사용할-때">외부 라이브러리 등을 사용할 때</h4>
<p>예)
<code>npm i -save @types/jquery</code>
node modules 파일의 @types에 추가됨.</p>
<p>주의할 점은 typeRoots 옵션을 설정해두면 자동으로 적용이 안되기 때문에
해당 설정을 끄는 것을 권장.</p>
<h2 id="implements">implements</h2>
<blockquote>
</blockquote>
<p>iterface는 객체의 타입을 지정할 때 사용한다.
또한 class의 타입을 확인할 때도 사용하는데 이 때 implements가 필요!</p>
<h3 id="implements-1">implements</h3>
<pre><code class="language-js">interface CarType {
  model: string;
  price: number;
}

class Car implements CarType {
  model: string;
  constructor(a: string) {
    this.model = a;
  }
}
error: Class &#39;Car&#39; incorrectly implements interface &#39;CarType&#39;.
  Property &#39;price&#39; is missing in type &#39;Car&#39; but required in type &#39;CarType&#39;.</code></pre>
<blockquote>
<p>!!
implements는 타입을 지정해주지 않고 <strong>속성 확인만</strong>을 해준다!</p>
</blockquote>
<pre><code class="language-js">interface CarType {
  model : string,
  tax : (price :number) =&gt; number;
}

class Car implements CarType {
  model;   ///any 타입됨
  tax (a){   ///a 파라미터는 any 타입됨 
    return a * 0.1
  }
}</code></pre>
<h2 id="object-index-signatures">object index signatures</h2>
<pre><code class="language-js">// 기존
interface String {
  name: string;
  age: string;
  location: string;
}

//모든 속성을 한번에 타입지정하고 싶을 때

interface String2 {
  [key: string]: string | number;
}</code></pre>
<h4 id="에러나는-경우">에러나는 경우</h4>
<pre><code class="language-js">interface StringOnly {
  age : number,   ///에러
  [key: string]: string,
}

interface StringOnly {
  age : string,   ///가능  
  [key: string]: string,
}

  interface StringOnly {
  age : number,   ///가능
  [key: string]: string | number,
}</code></pre>
<h4 id="array-경우">array 경우</h4>
<pre><code class="language-js">interface StringOnly {
  [key: number]: string,
}

let obj :StringOnly = {
  0 : &#39;kim&#39;
  1 : &#39;20&#39;,
  2 : &#39;seoul&#39;
}
// string으로 지정해도 에러 안남.
interface StringOnly {
  [key: string]: string;
}
</code></pre>
<h3 id="recursive-index-signatures">Recursive Index Signatures</h3>
<pre><code class="language-js">// 기존
interface MyType {
  &#39;font-size&#39;: {
    &#39;font-size&#39;: {
      &#39;font-size&#39;: number;
    };
  };
}

// Recursive Index Signatures
interface MyType {
  &#39;font-size&#39;: MyType | number;
}

let css: MyType = {
  &#39;font-size&#39;: {
    &#39;font-size&#39;: {
      &#39;font-size&#39;: 14,
    },
  },
};</code></pre>
<h3 id="사용해보기">사용해보기!</h3>
<ul>
<li>index signatures<pre><code class="language-js">interface MyObj {
[key: string]: string | number;
}
</code></pre>
</li>
</ul>
<p>let obj: MyObj = {
  model: &#39;k5&#39;,
  brand: &#39;kia&#39;,
  price: 6000,
  year: 2030,
  date: &#39;6월&#39;,
  percent: &#39;5%&#39;,
  dealer: &#39;김차장&#39;,
};</p>
<pre><code>- Recursive
```js
interface MyObj {
  &#39;font-size&#39;: number;
  [key: string]: MyObj | number;
}

let obj: MyObj = {
  &#39;font-size&#39;: 10,
  secondary: {
    &#39;font-size&#39;: 12,
    third: {
      &#39;font-size&#39;: 14,
    },
  },
};</code></pre><h2 id="object-타입-변환">object 타입 변환</h2>
<h3 id="objectkeysobj">Object.keys(obj)</h3>
<pre><code class="language-js">let obj = {
  name: &#39;kim&#39;,
};

console.log(Object.keys(obj));
// 키를 배열로 반환.
//[ &#39;name&#39; ]</code></pre>
<h3 id="keyof">keyof</h3>
<p>object의 키를 이용해서 union type을 만들어줌</p>
<pre><code class="language-js">interface Person {
  age: number;
  name: string;
}

type PersonKeys = keyof Person
 // &#39;age&#39; 또는 &#39;name&#39; 타입 지정

let a: PersonKeys = &#39;age&#39;;// &#39;age2&#39; 불가
let b: PersonKeys = &#39;name&#39;;</code></pre>
<h4 id="index-signature">index signature?</h4>
<pre><code class="language-js">interface Person {
  [key: string]: number;
}

type PersonKeys = keyof Person;
// string | number
// ? key를 string으로 지정해뒀는데..
// obj 키에 number 넣어도 자동으로 스트링으로 변환되기 때문에</code></pre>
<h3 id="타입-변환기-만들기">타입 변환기 만들기!</h3>
<pre><code class="language-js">// 아래 타입을 모두 string 타입으로 바꾸고 싶을 때?
type Car = {
  color: boolean;
  model: boolean;
  price: boolean | number;
};

// 변환기 만들기
type TypeChanger&lt;MyType&gt; = {
  [key in keyof MyType]: string;
};

type NewType = TypeChanger&lt;Car&gt;;

let a: NewType = {
  color: false, // 에러
};</code></pre>
<blockquote>
<p>[key in keyof MyType] : string
keyof MyType // &#39;color&#39; | &#39;model&#39; | &#39;price&#39;
키값이 오른쪽 유니온 타입에 포함된다면 string으로 타입지정해주세요!</p>
</blockquote>
<h4 id="모두-적용-아닌-원하는-타입으로-적용하고-싶을-때">모두 적용 아닌 원하는 타입으로 적용하고 싶을 때</h4>
<pre><code class="language-js">type Bus = {
  color : string,
  model : boolean,
  price : number
}

type TypeChanger &lt;MyType, T&gt; = {
  [key in keyof MyType]: T;
};

type NewBus = TypeChanger&lt;Bus, boolean&gt;;
type NewBus2 = TypeChanger&lt;Bus, string[]&gt;</code></pre>
<h2 id="조건부-타입-만들기--infer">조건부 타입 만들기 &amp; infer</h2>
<h3 id="삼항조건식">삼항조건식</h3>
<pre><code class="language-js">type Age&lt;T&gt; = T extends string ? T : unknown;
// extends : T 가 string 타입인지 확인

let a: Age&lt;string&gt;; // string 타입
let b: Age&lt;number&gt;; // unknown 타입</code></pre>
<h4 id="활용해보기">활용해보기</h4>
<pre><code class="language-js">// 파라미터로 array 타입 입력하면 array의 첫 인덱스 타입을
// 다른 거 입력하면 any를 타입으로.
type FisrtItem&lt;T&gt; = T extends any[] ? T[0] : any;

let c: FisrtItem&lt;string[]&gt; = &#39;스트링&#39;;
let d: FisrtItem&lt;number&gt;; //any</code></pre>
<h3 id="infer">infer</h3>
<pre><code class="language-js">//  기존 : T가 스트링타입이면 T를 아니면 unknown으로 타입지정
type Person&lt;T&gt; = T extends string ? T : unknown;

// infer

type Person2&lt;T&gt; = T extends infer R ? R : unknown;
// T에서 타입을 추출해라

let a: Person2&lt;string&gt;;

// 실용예제- array 내부 타입 추출
type 타입추출&lt;T&gt; = T extends (infer R)[] ? R : unknown;

type a = 타입추출&lt;string[]&gt;;
// T는 string[]으로 들어왔고 R[]이 T를 추출해서 R이 string이 됨.
// a의 타입이 string이 됨.

// 예제2 - 함수 추출
type 타입추출2&lt;T&gt; = T extends () =&gt; infer R ? R : unknown;
type b = 타입추출2&lt;() =&gt; void&gt;;
// b 타입 void</code></pre>
<blockquote>
</blockquote>
<pre><code class="language-js">이거 써도 됨!
// #### ReturnType 
type c = ReturnType&lt;() =&gt; void&gt;;</code></pre>
<h4 id="연습해보기">연습해보기</h4>
<blockquote>
</blockquote>
<p>1번</p>
<ul>
<li>array 타입 입력 -&gt; array 첫 자료가 string이면 string타입</li>
<li><blockquote>
<p>아니면 unknown</p>
</blockquote>
<pre><code class="language-js">type 추출&lt;T&gt; = T extends [string, ...any] ? T[0] : unknown;
&gt;
&gt;
let age1: 추출&lt;[string, number]&gt;; // string
let age2: 추출&lt;[boolean, number]&gt;; // unknown</code></pre>
<blockquote>
</blockquote>
2번</li>
<li>함수의 파라미터 타입을 추출<pre><code class="language-js">type 추출&lt;T&gt; = T extends (x: infer R) =&gt; any ? R : any;
&gt;
let a: 추출&lt;(x: number) =&gt; void&gt;;
// number
let b: 추출&lt;(x: string) =&gt; void&gt;;
// string
&gt;</code></pre>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>