<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lee_moi.log</title>
        <link>https://velog.io/</link>
        <description>https://mo-i-programmers.tistory.com/</description>
        <lastBuildDate>Mon, 13 May 2024 08:13:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>lee_moi.log</title>
            <url>https://velog.velcdn.com/images/lee_moi/profile/e6b2e0e3-d7d7-4bf7-8561-1f171691e35f/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. lee_moi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/lee_moi" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[docker로 mysql띄우고 Next.js로 연결해보기(prisma)-2]]></title>
            <link>https://velog.io/@lee_moi/docker%EB%A1%9C-mysql%EB%9D%84%EC%9A%B0%EA%B3%A0-Next.js%EB%A1%9C-%EC%97%B0%EA%B2%B0%ED%95%B4%EB%B3%B4%EA%B8%B0prisma-2</link>
            <guid>https://velog.io/@lee_moi/docker%EB%A1%9C-mysql%EB%9D%84%EC%9A%B0%EA%B3%A0-Next.js%EB%A1%9C-%EC%97%B0%EA%B2%B0%ED%95%B4%EB%B3%B4%EA%B8%B0prisma-2</guid>
            <pubDate>Mon, 13 May 2024 08:13:10 GMT</pubDate>
            <description><![CDATA[<p>이전 글에서 <code>docker</code>를 이용해 <code>mysql</code>을 띄웠고 이후 workbench를 이용해 데이터 베이스에 쉽게 접속할 수 있도록 만들었다.</p>
<p>그럼 이제 이를 실제로 코드상에서 사용해보려 하는데 <code>Next.js</code>와 <code>Prisma</code>를 이용해서 만들어보려고 한다.<br>이를 통해 <code>prisma</code>의 사용법을 간단하게 보고 특히 <code>Next.js</code>의 Server action을 잘 익혀볼 수 있을 것 같다.</p>
<hr>
<h1 id="프로젝트-설정">프로젝트 설정</h1>
<pre><code>npx create-next-app@latest</code></pre><p>를 통해 간단한 <code>next-app</code>을 만든다. 이름은 <code>sql_test</code>로 만들었다.</p>
<p>성공적으로 <code>sql_test</code>가 설치 되었다면 추가적으로 <code>prisma</code>를 설치하자</p>
<pre><code>cd sql_test
npm i prisma --save-dev
npx prisma init --datasource-provider mysql</code></pre><p>혹시 <code>sql_test</code> 폴더가 아닌 바깥쪽에서 설치할 까 <code>cd sql_test</code>도 넣었다.<br>무사히 잘 설치 했다면 <code>sql_test</code>폴더 안에 <code>prisma</code>라는 폴더가 생긴 걸 확인할 수 있다</p>
<blockquote>
<p>이때 주의 할 점으로 만약 <code>git</code>으로 해당 프로젝트를 올릴 생각을 하고 있다면 방금 <code>prisma</code>와 함께 생긴 <code>.env</code>파일을 되도록 git remote 에는 올리지 말아야 하는데 <code>.gitignore</code>에 들어가서 <code>.env</code>를 추가하자.<br>그럼 git의 추적이 끊어진 걸 볼 수 있다</p>
</blockquote>
<p><code>prisma</code> 폴더 안에는 <code>schema.prisma</code>라는 파일이 하나 같이 생성되어 있는데 내부를 살펴보면</p>
<pre><code>// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = &quot;prisma-client-js&quot;
}

datasource db {
  provider = &quot;mysql&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}</code></pre><p>이렇게 만들어져 있다. Prisma클라이언트를 생성하고 아래는 db를 연결하는데 <code>mysql</code>이며 <code>url</code>부분은 <code>env()</code>를 통해 <code>.env</code>파일에 접근 하여 <code>DATABASE_URL</code>을 가져온다.</p>
<p>나는 <code>.env</code>와 <code>DATABASE_URL</code>을 만든적이 없는데 어떻게 된걸까? 프로젝트 루트 폴더에서 잘 살펴보면 <code>.env</code>파일이 만들어져 있고 내부를 살펴보면</p>
<pre><code># Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL=&quot;mysql://johndoe:randompassword@localhost:3306/mydb&quot;</code></pre><p>이렇게 존재하는 걸 확인할 수 있다.<br>url의 형식은 내가 사용하는 데이터베이스마다 형식이 다르니 각자 참고해서 넣어보자 <code>MySQL</code>같은 경우는 아래와 같다.</p>
<pre><code>mysql://USER:PASSWORD@HOST:PORT/DATABASE</code></pre><p>임으로</p>
<ul>
<li><code>USER</code> : 데이터베이스 사용자의 이름</li>
<li><code>PASSWORD</code> : 데이터베이스 사용자의 비밀번호</li>
<li><code>PORT</code> : 데이터베이스 서버가 실행중인 포트(일반적으로 MySQL의 경우 <code>3306</code>이다)</li>
<li><code>DATABASE</code> : 데이터베이스 이름</li>
</ul>
<p>우리는 저번에 만든 걸 넣어본다면</p>
<pre><code>DATABASE_URL=&quot;mysql://dev:1234@localhost:3306/test&quot;</code></pre><p>가 될 것이다.</p>
<p>이러면 Prisma가 데이터 베이스에 접근할 수 있도록 성공적으로 작성했다. 이따가 테스트를 통해 잘 들어가는지 확인을 해보자</p>
<p>내가 넣고 싶은 건 간단하게 제목과 내용을 가진 글목록이고 이를 테이블로 만들어 보고 싶다.</p>
<pre><code>
generator client {
  provider = &quot;prisma-client-js&quot;
}

datasource db {
  provider = &quot;mysql&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}

model Post {
  id Int @id @default(autoincrement())
  createdAt DateTime @default(now())
  title String @db.VarChar(255)
  content String?
}</code></pre><p><code>Post</code>라는 테이블에 다음과 같은 컬럼을 만든다</p>
<ul>
<li><code>id</code> : <code>Int</code>타입이며 <code>@id</code>역할을 하고 <code>@default</code>기본적으로 <code>autoincrement</code>자동 증가하게 해준다. 즉, 자동적으로 증가하는 아이디가 붙을거란 것</li>
<li><code>createdAt</code> : 생성 날짜인데 <code>DateTime</code> 타입이며 기본적으로 <code>now</code>현재 시간으로 넣을것이다</li>
</ul>
<p>등 아래 <code>title</code>과 <code>content</code>는 찾아보며 유추 가능할 것이라 생각한다.</p>
<p>이렇게 prisma를 작성하고 다시 명령어 창으로 돌아와 아래의 명령어를 입력해주면?</p>
<pre><code>npx prisma migrate dev --name init</code></pre><p>우리가 작성한 prisma 모델이 올라가게 되고 <code>migration</code>기록이 db에 함께 저장되게 된다!</p>
<p>우리 프로젝트에도 새로운 폴더와 파일이 생성되는데</p>
<pre><code>prisma/migrations/2024...(마이그레이션시간)/migration.sql</code></pre><p>이 만들어지게 된다. 이 내부를 확인해보면</p>
<pre><code>CREATE TABLE `Post` (
    `id` INTEGER NOT NULL AUTO_INCREMENT,
    `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    `title` VARCHAR(255) NOT NULL,
    `content` VARCHAR(191) NULL,

    PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;</code></pre><p>&#39;이처럼 되어 있는데 우리가 아까 prisma 파일에서 선언한 model을 prisma가 자동적으로 sql문으로 변경하여 데이터 베이스 서버에 날려주는 것이다.</p>
<p>고로 데이터 베이스를 확인하러 workbench로 들어가보면?</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/8e3583b3-622c-4451-821c-c53b005c0629/image.png" alt=""></p>
<p>내가 설정한 model이 다 그대로 들어간 걸 확인할 수 있다. 여기까지 오면 굉장히 신기하고 또 재밋게 느껴졌다.<br>그럼 이 다음에는 클라이언트 쪽에서 무언가를 할 수 있어야 하는데..</p>
<p><strong>여기서<br>공식문서에 따르면 <code>prisma@client</code>를 새로 설치하라고 나오는데 설치하지 않고도 사용할 수 있었다. 아마 자동 설치되는 부분이 있는 것 같다.<br>그러면 아래의 설치를 따라하지 않고 한번 진행해보자</strong></p>
<pre><code>npm install @prisma/client</code></pre><h1 id="데이터-베이스-쿼리">데이터 베이스 쿼리</h1>
<p>데이터 베이스에서 데이터를 읽고 쓰기 위한 쿼리 작성을 시작할 수 있는데<br>일단 prisma 의 인스턴스를 생성해 줘야 한다</p>
<p><code>lib/prisma.ts</code>라는 파일을 만들어 아래의 코드를 추가해주자</p>
<pre><code>// lib/prisma.ts
import { PrismaClient } from &quot;@prisma/client&quot;;
declare global {
  let prisma: PrismaClient | undefined;
}
const client = prisma || new PrismaClient();
if (process.env.NODE_ENV !== &quot;production&quot;) prisma = client;
export default client;</code></pre><p>이는 <code>prisma@client</code>를 따로 <code>import</code> 하지 않고 전역적으로 사용하기 위해 작성된 코드로 <code>declare global</code>을 통해 글로벌 변수속 <code>prisma</code>를 선언, 이후 <code>client</code>를 만들어 만약 전역 변수 <code>prisma</code>가 없다면 새로운 <code>prisma@client</code>를 가지도록 하였고 아니라면 기존 글로벌 변수에 있는 <code>prisma</code>를 사용하도록 한다.<br>이때 만약 노드의 상태가 프로덕션이면 <code>client</code>는 <code>prisma</code> 전역변수에 할당하여 동일화 시킨 후 <code>client</code>는 <code>export</code>하여 전역적으로 사용할 수 있게 한다.</p>
<p>이를 통해 다른 곳에서 <code>client</code>를 통해 <code>prisma</code>가 제공하는 데이터베이스 쿼리를 사용할 수 있게 된다.</p>
<p>이는 다음 글에서 Next.js의 server action과 더불어 함께 다뤄보자</p>
<p>Reference : <a href="https://www.prisma.io/">https://www.prisma.io/</a> , <a href="https://www.yujiseok.blog/post/nextjs-server-actions">https://www.yujiseok.blog/post/nextjs-server-actions</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[docker로 mysql띄우고 Next.js로 연결해보기(prisma)-1]]></title>
            <link>https://velog.io/@lee_moi/docker%EB%A1%9C-mysql%EB%9D%84%EC%9A%B0%EA%B3%A0-Next.js%EB%A1%9C-%EC%97%B0%EA%B2%B0%ED%95%B4%EB%B3%B4%EA%B8%B0prisma-1</link>
            <guid>https://velog.io/@lee_moi/docker%EB%A1%9C-mysql%EB%9D%84%EC%9A%B0%EA%B3%A0-Next.js%EB%A1%9C-%EC%97%B0%EA%B2%B0%ED%95%B4%EB%B3%B4%EA%B8%B0prisma-1</guid>
            <pubDate>Mon, 13 May 2024 06:16:21 GMT</pubDate>
            <description><![CDATA[<p>제목만 보면 뭔가 되게 많아 보이는데 실은 별거 없다.</p>
<p>docker를 이용해서 가상 컨테이너로 mysql 서버를 만들고 Next.js와 prisma를 사용해서 쉽게 mysql에 내가 원하는 타입과 결과를 넣어보려고 하는거다.</p>
<p>docker와 next.js의 server action, prisma를 연습하기 위해 한번 실행해 봤다.</p>
<p><strong>본 작성글은 window 환경에서 작업한 작성물입니다.</strong></p>
<h1 id="docker로-mysql-컨테이너-올리기">docker로 mySQL 컨테이너 올리기</h1>
<p>먼저 <code>docker</code>가 있어야 하지 않을까? 해당 링크로 가서 <code>docker</code>를 먼저 설치하자</p>
<p><a href="https://www.docker.com/get-started/">https://www.docker.com/get-started/</a></p>
<p>무사히 설치가 됐다면 <code>cmd</code> 창을 열자 <code>git bash</code>나 <code>window powershell</code>도 괜찮습니다.<br>저는 <code>window powershell</code>에서 실행했습니다.</p>
<pre><code>docker pull mysql:8.0</code></pre><p>먼저 입력해주자. 하나씩 살펴보면 <code>docker</code>는 <code>docker</code>명령어들을 실행하기 위한 것이고 우리가 라이브러리 설치하려 할 때 앞에 <code>npm</code>을 붙여 <code>npm</code>을 사용하는 것과 같다.<br><code>pull</code>은 다운로드 받는 걸 뜯하고 <code>mysql:8.0</code>은 어떤걸 다운 받은 건가 이다.<br><code>npm install mysql:8.0</code> 과 똑같이 생각하자.<br><strong>이때 <code>mysql</code>만 해도 되는데 뒤에 <code>:8.0</code>은 뭐지? 할 수 있는데 이건 <code>mysql workbench</code>를 사용하는데 있어 버전을 맞추기 위해 뒤에 버전을 따로 <code>tag</code>한 것이다.<br>이는 이따 다른 명령어들을 실행할 때도 주의해서 작성해주자</strong><br>무사히 <code>mysql:8.0</code>이 설치 되었다면 한번 확인해보자</p>
<pre><code>docker images</code></pre><p>를 입력시 내가 다운 받은 이미지들을 전부 띄우는 것으로 입력히 아까 다운받은 <code>mysql</code>이 떠야한다.</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/7357e0d0-a92c-4cbb-921d-9571355505cc/image.png" alt=""></p>
<p>위처럼<code>mysql</code>이란 이름과 <code>tag</code>부분에 <code>8.0</code>으로 들어가 있으면 성공적으로 <code>mysql:8.0</code>버전으로 이미지가 설치 된 것이다.</p>
<p>이후 컨테이너를 설정해서 올려야 한다.</p>
<blockquote>
<p>컨테이너와 이미지의 차이점은 docker 공식문서나 여러 블로그들을 참고해서 한번 알아보자.</p>
</blockquote>
<pre><code>docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=dev --name mysql_test mysql:8.0</code></pre><ul>
<li><code>docker run</code> :도커로 실행하겠다</li>
<li><code>-d</code> :<code>detach</code>모드의 약자로 컨테이너가 백그라운드에서 실행된다 생각하면 된다. 우리가 어떤 다른 작업을 한다해도 docker container는 계속 올라가서 유지되고 있다는 것</li>
<li><code>-p &lt;호스트 포트&gt; &lt;컨테이너 포트&gt; :</code>p 3306:3306`으로 포트를 서로 연결한다.</li>
<li><code>-e</code> : 컨테이너에서 사용할 환경변수를 설정한다. 우리가 개발 할 때 <code>.env</code>파일에 비밀 변수들을 추가하는 것이라 생각하면 된다<br>여기선 <code>MYSQL_ROOOT_PASSWORD</code>라는 이름의 환경변수를 <code>dev</code>로 저장했다.<br>이는 이따 컨테이너에서 mySQL에 접속할 때 사용할 비번을 설정하는 것!</li>
<li><code>--name &lt;container name&gt;</code> 컨테이너의 이름을 설정한다. 우리가 만든 컨테이너는 <code>mysql_test</code>이다</li>
<li><code>mysql:8.0</code> 컨테이너를 어떤 이미지를 사용해서 띄울건지로 우리는 mysql:8.0으로 컨테이너를 띄웠다</li>
</ul>
<blockquote>
<p>여기서 한번 궁금할텐데 port는 왜 3306이어야 할까 한다.<br><a href="https://www.cbtnuggets.com/common-ports/what-is-port-3306">https://www.cbtnuggets.com/common-ports/what-is-port-3306</a><br>이 페이지를 참고해보자!  </p>
</blockquote>
<p>이후 컨테이너가 정상적으로 생성되고 돌아가고 있는지 확인하려면</p>
<pre><code>docker ps -a</code></pre><p>를 입력해서 확인 해 보자.</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/81a44674-d735-425b-a0ab-18602895d9f3/image.png" alt=""></p>
<p>정상적으로 떠있는 걸 확인할 수 있다.</p>
<p>이때 <code>STATUS</code>부분을 확인해 보면 되고 <code>UP</code>이면 컨테이너가 올라가 있다. 즉, 사용할 수 있다는 뜻이고 멈추고 싶다면 <code>docker container stop [컨테이너ID]</code>를 입력해주자<br>입력 후 다시 <code>docker ps -a</code>를 작성해보면 방금과 동일하게 뜨지만 <code>STATUS</code>부분이 <code>EXITED</code>로 변경된 부분을 확인 할 수 있다</p>
<p>다시 docker를 올리고 싶다면 <code>run</code>이나 <code>start</code>를 쓰면 되는데 <code>run</code>은 <code>attach</code>모드가 디폴트이고 <code>start</code>는 <code>detached</code>모드가 디폴트이니 우리는 <code>detached</code>모드를 부여했음으로 <code>start</code>를 쓰자<br><code>docker start mysql_test</code>를 입력해주고 다시 <code>docker ps -a</code>를 입력하면 정상적으로 <code>STATUS</code>가 <code>UP</code>으로 바뀐 걸 볼 수 있다.</p>
<h1 id="mysql-컨테이너에-접속하여-설정하기">mySQL 컨테이너에 접속하여 설정하기</h1>
<p>무사히 컨테이너가 올라갔다면 이제 접속해서 권한 설정을 해보자</p>
<pre><code>docker exec -it mysql_test bash</code></pre><p>이는 <code>docker exec</code>가 컨테이너에 특정 명령을 실행할 수 있는 명령인데 이를 이용해서 컨테이너에 접속 할 수 있다.<br>뒤에 <code>-it</code>는 본래 <code>-i -t</code> 의 명령어로 표준 입력 활성화와 컨테이너와 연결되지 않더라도 표준 입력을 유지한다. <code>TTY</code>모드로 Bash를 사용하려면 해당 옵션을 사용하라고 되어 있다. 상호 입출력과 bash를 사용하기 위해 넣는다고 간단하게 일단 생각하고 넘어가자.</p>
<p>이후 컨테이너 이름을 적어주고 뒤에 <code>bash</code>를 사용해 <code>bash</code>환경으로 실행하자</p>
<p>그러면 이제 창에 명령 입력 부분이 이렇게 변할 것이다.</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/743c90a7-b96d-4bfa-8425-6846b1d69913/image.png" alt=""></p>
<p>현재 상태는 컨테이너에 접속하였고 bash 명령어들을 입력할 수 있게 되었다.<br>그러면 mysql에 접속해보자</p>
<pre><code>mysql -u root -p</code></pre><p>를 입력시</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/b8de32ec-0ecd-45ef-a2f1-f84bb154d562/image.png" alt=""></p>
<p>이렇게 비밀번호를 입력하라고 나오는데 우리가 위에서 환경변수로 만든 <code>MYSQL\_ROOT\_PASSWORD</code> 에 입력한 값을 넣자.</p>
<p>키보드로 입력하는데 화면에 아무것도 안나온다고 걱정하지 말자. 보안상 나타나지 않는 것이고 작성되고 있음으로 정확한 비밀번호를 입력하고 엔터로 입력시 무사히 mySQL에 접속 된 걸 확인할 수 있다.</p>
<p>여기서 <code>mysql -u root -p</code> 부분은 보통 우리가 <code>root</code>의 경우 관리자 계정이고 관지라 계정으로 접속한다는 뜻으로 이해하자</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/2f8939f3-7331-43e5-9e2f-962f8435adeb/image.png" alt=""></p>
<p>위와 같은 화면이 뜰 경우 성공적으로 접속했다.<br>그럼 항상 관리자 모드로 접속 할 수는 없으니 우리가 사용할 유저 하나를 만들고 권한을 부여하자</p>
<pre><code>CREATE USER &#39;dev&#39;@&#39;%&#39; IDENTIFIED BY &#39;1234&#39;;</code></pre><p><code>mysql</code>문법을 사용해서 입력해주고 끝에 꼭 세미콜론(<code>;</code>)은 잊지말고 작성해주자.<code>mysql</code>의 기본임으로!  
<code>CREATE USER</code> 유저를 생성하겠다 <code>&#39;dev&#39;</code> dev란 이름으로 이때 <code>&#39;%&#39;</code>외부에서 접근을 허용 하겠다.<br>이후 뒤는 비밀번호 설정이다.</p>
<p>이렇게 권한을 부여한 <code>dev</code>란 유저를 만들었다. 그럼 잘 만들어졌는지 확인해 보고 싶은데 어떻게 할까?</p>
<pre><code>use mysql;
select host,user from user;</code></pre><p>이렇게 입력시 <code>mysql</code>이라는 기본적으로 <code>mysql</code>이 가지고있는 데이터베이스로 접근할 수 있고 이후 <code>select</code>문을 사용해서 user 부분을 가져올 경우</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/b87ddf77-2477-407b-9b74-6fb3a07eab43/image.png" alt=""></p>
<p>이렇게 내가 만든 `dev`를 확인할 수 있다</p>
<p>물론 현재는 유저만 만든것이기에 따로 권한이 없어 mysql 작업을 못하니 이제 권한을 부여해보자<br>다시 입력창에</p>
<pre><code>GRANT ALL PRIVILEGES ON *.* TO &#39;dev&#39;@&#39;%&#39;;</code></pre><p>이 명령어는 <code>dev</code>에게 모든 권한을 부여했음을 뜻하며 권한을 따로따로 설정별로 주고 싶을 경우 mySQL문서에서 찾아서 권한을 따로 부여할 수 있도록 연습해보자.<br>이후 우리는 꼭 <code>root</code>가 아닌 <code>dev</code>로 mysql에 접속할 수 있다.</p>
<pre><code>quit
mysql -u dev -p</code></pre><p><code>quit</code>를 통해 mysql에서 나온 뒤 다시 <code>mysql -u dev -p</code>를 입력해서 내가 만든 유저로 무사히 접속 되는지 확인해보자.</p>
<p><strong>만약 접속한 컨테이너 환경에서 나오고 싶을 경우 명령어 <code>exit</code>를 입력시 나올 수 있다</strong></p>
<p>그럼 우리가 테스트 할 데이터 베이스를 하나만 만들고 컨테이너에서 나와보자<br><code>mysql -u dev -p</code>로 다시 <code>mysql</code>에 접속하고</p>
<pre><code>CREATE DATABASE test;</code></pre><p>를 통해 <code>test</code>라는 데이터 베이스를 만들고 잘 만들어 졌는지 확인해보자</p>
<pre><code>show databases;</code></pre><p><img src="https://velog.velcdn.com/images/lee_moi/post/206e2ff0-2348-429b-8886-8e826b363d4b/image.png" alt=""></p>
<p>아래 보면 <code>test</code>라고 무사히 database가 만들어진 걸 확인해 볼 수 있다.<br>그럼 아까 <code>mysql</code>데이터 베이스에서 <code>user</code>를 찾았던 것처럼 <code>test</code>라는 데이터베이스에 접속하려면 어떻게 해야할까?</p>
<pre><code>use test</code></pre><p>로 <code>test</code>데이터 베이스에 접속할 수 있다!</p>
<hr>
<p>이로써 docker 컨테이너를 이용해서 mysql 서버를 하나 만들어 보았다. 그럼 마지막으로 mysql workbench에만 한번 등록을 해보고 끝내보자</p>
<h1 id="mysql-workbench에-등록하기">mySQL workbench에 등록하기</h1>
<p>먼저 mySQL workbench는 다운로드 하자. mySQL홈페이지로 들어가 설치 하면 된다.<br><a href="https://dev.mysql.com/downloads/workbench/">https://dev.mysql.com/downloads/workbench/</a></p>
<p>무사히 실행이 완료 되었다면 아래와 같은 화면일 텐데 My SQL Connections부분에 + 버튼을 눌러 데이터 베이스를 연결해보자</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/a41240f0-93e7-4d40-9c9d-e8eeae4c7497/image.png" alt=""></p>
<p>+버튼을 클릭할 경우 아래와 같은 창이 나올텐데 순서대로 입력해주자</p>
<p><code>connection name</code>은 내가 만들었던 db이름으로 적어주고(본인이 우너하는 대로 적어도 상관없긴 하지만 맞춰서 적어줘야 직관적이다!)</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/193897c8-c901-409c-82dd-eb2b6eb18a7b/image.png" alt=""></p>
<p><code>Hostname</code>은 <code>docker</code>로 올렸기 때문에 <code>localhost</code>를 작성해주고 <code>port</code>는 mysql 포트인 <code>3306</code> 그대로 두고</p>
<p><code>username</code>은 아까 우리가 만든 user의 이름인 <code>dev</code>를 집어 넣는다.</p>
<p><code>Default Schema</code>는 우리가 기본으로 사용할 스키마를 고르는 것으로 아까 만든 <code>test</code>라는 이름의 <code>database</code>를 적어넣어서 기본적으로 <code>workbench</code>에서 <code>mysql_test</code>라는 데이터베이스에 접속시 <code>test</code> 데이터베이스로 들어갈 수 있도록 한다</p>
<p>이후 바로 들어가는게 아닌 <code>password</code> 부분은 <code>Store in Vault</code>를 통해 비밀번호를 입력하자!  
클릭할 시</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/b4bd3f2d-dbee-4ccf-bf37-91fb5ced0a9e/image.png" alt=""></p>
<p>이런 창이 나오는데 아까 우리가 아까 도커 컨테이너의 mysql에 들어가서 <code>CREATE USER</code> 하면서 같이 적었던 비밀번호를 입력해주자</p>
<p><code>OK</code>를 누른 뒤 우측 하단의 <code>Test Connection</code>으로 정상적으로 연결 되었는지 확인해보자!</p>
<p><code>Successfully</code>로 뜰경우 성공적으로 연결된 것이니 <code>ok</code>누르고 다시 우측 하단 <code>ok</code>를 눌러서 무사히</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/248f332d-0e38-4865-977b-47d9973d83db/image.png" alt=""></p>
<p>이러한 데이터베이스가 생겼는지 확인해보자! 더블클릭 할 경우 우리가 잘 만든 <code>mysql_test</code> 서버에 들어왔고 <code>test</code> 데이터베이스가 <code>default</code>로 되어 있는 걸 확인 할 수 있다</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/22e724d8-c21d-43ad-946f-2b5dff058eb1/image.png" alt=""></p>
<p>여기까지 모두 성공했다면 무사히 docker를 통해 mysql서버를 컴퓨터로 띄울 수 있었고 데이터베이스도 사용할 수 있게 된 것이다.<br>그러면 다음 글에서 <code>next.js</code> 와 <code>prisma</code> 를 통해 데이터 베이스에 접근하고 사용할 수 있는지 테스트 해보자!</p>
<p>Reference: <a href="https://mungto.tistory.com/328">https://mungto.tistory.com/328</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ember -Tutorial (Service & Form input)]]></title>
            <link>https://velog.io/@lee_moi/Ember-Tutorial-Service-Form-input</link>
            <guid>https://velog.io/@lee_moi/Ember-Tutorial-Service-Form-input</guid>
            <pubDate>Mon, 29 Apr 2024 06:48:32 GMT</pubDate>
            <description><![CDATA[<h1 id="service">Service</h1>
<p>엠버의 서비스는 애플리케이션이 동작하는 동안 유지되는 숫자 개체이며 애플리케이션의 다른부분에서 사용할 수 있다.<br>번역 본이라 조금 이상한데 여러개의 컴포넌트에서 <code>service1</code>을 사용할 수 있다는 뜻.</p>
<p>예시로 로그인이라는 서비스를 통해 로그인 페이지에서 로그인 서비스로 로그인을 보낸뒤 사용자 페이지에서 받을 수 있도록 할 수 있다.<br>개념적으로만 보면 전역 상태가 생각되는데 한번 사용 예시를 봐봐야 겠다</p>
<p>먼저 service를 만드려면 동일하게 emberCLI로 만드는데</p>
<pre><code>
ember g service shopping-cart</code></pre><p>이러면 <code>app/services/shopping-cart.js</code> 파일이 하나 만들어 진다</p>
<pre><code>
// app/services/shopping-cart.js

import Service from &#39;@ember/service&#39;

export default class ShoppingCartService extends Service {
  itemList = [
  {name: &#39;1&#39;},
  {name: &#39;2&#39;}
 ]
}</code></pre><p>일단 <code>shopping-cart</code>라는 <code>service</code>에 <code>itemList</code>라는 더미 데이터를 만들어 놓는다.<br>이후 <code>controllers/cart.js</code>로 가서 서비스를 주입한다</p>
<p>현재 보고 있는 ember tutorial은 2022 영상이지만 현재 2024 docs에선 변경점이 있다<br><code>service</code>를 <code>import</code>할 때 2022 영상에선 <code>import {inject as service} from &#39;@ember/service&#39;</code>처럼 사용하는 걸 볼 수 있는데</p>
<p><code>inject</code>를 <code>as</code>를 통해 변경해서 사용한다.</p>
<p>하지만 최근 docs를 보면 업데이트 되었는지 <code>component</code>에서 사용할 때 <code>import {service} from &#39;@ember/service&#39;</code>로 사용되고 있다.</p>
<p>여기까지 <code>import</code>에 대한 얘기이고 사용에 관해서는 데커레이터를 사용하면 된다</p>
<pre><code>// controllers/cart.js

export default class CartController extends Controller {
  @service shoppingCart;

  //...somecode
}</code></pre><p>그리고 <code>shopping-cart</code>에서 선언한 더미데이터를 쓰기 위해 <code>template</code>으로 가서</p>
<pre><code>//...some code

{{#each this.shoppingCart.itemList as |item|}}
  //...some code
{{/each}}
///...some code</code></pre><p>이렇게 사용하면 된다.</p>
<p><strong>만약 <code>service</code>를 사용할 때 파일 이름이 아닌 현재 컨트롤러 안에서 내가 이름을 재정의 하고 싶다면 <code>service</code> 데커레이터 사용하는 부분을 <code>@service shoppingCart</code> 에서 <code>@service(&#39;shopping-cart&#39;) cart;</code> 로 변경 해주면 된다.</strong></p>
<p>이는 함수 부분도 <code>service</code> class 내에서 선언한뒤 사용부분에서 데커레이터를 통해 가져오고 똑같이 사용하면 된다.</p>
<h1 id="form-input">Form Input</h1>
<p>우리가 하나의 <code>input</code>창을 가지고 있고 이걸 변경하고 싶다.</p>
<p>예시로 장바구니에 상품이 있고 상품 개수를 수동으로 조절할 수 있는 input이 있는데 작성해보자</p>
<pre><code>&lt;!-- cart.hbs --&gt;
{{#each this.shoppingCart.itemList as |item|}}
  &lt;input type=&quot;number&quot; value={{item.count}} {{on &quot;input&quot; (fn this.updateItemCount item)}}/&gt;
{{/each}}</code></pre><p>이후 <code>controllers/cart</code>로 이동해서 <code>@action</code>하나를 추가한다.</p>
<pre><code>//controllers/cart.js

//...some code
@action
updateItemCount(item, event) {
  const count = event.target.value;
  item.count = count;
}</code></pre><p>여기서 전달되는 <code>item</code>은 <code>input</code>에서 보내는 <code>item</code>이며 2번째 매개변수인 <code>event</code>는 말그대로 이벤트이다.<br>따라서 js 기본적으로 실제 값 얻는 방식을 그대로 사용하면편한데 <code>event.target.value</code> 사용하면 된다.<br>이후 전달받은 <code>item</code>의 <code>count</code>를 현재입력된<code>count</code>로 변경하면 된다.</p>
<blockquote>
<p>이때 주의할 점으론 <code>tracked</code>되고 있는 놈은 <code>itemList</code>로 ember는 <code>itmeList</code> 내부의 <code>count</code>가 변경 되었는지 확인할 수 없다.<br>따라서 <code>itemList</code>에 들어갈 <code>item</code>을 하나의 <code>class</code>로 만들고 거기서 <code>count</code>를 <code>@tracked</code>를 사용해 트랙킹 하고 이를 <code>itemList</code>에 들어갈 <code>item</code>들을 모두 <code>new Item</code>을 통해 각각의 <code>item</code>의 <code>count</code>가 추적되고 있다면 값의 변함을 트랙킹하고 이를 실제로 웹에 반영한다.<br>이를 통해 총 합을 구하는 부분에서도 연관된 부분이 <code>@tracked</code>되어 같이 업데이트 되기 때문에 잘 고려해서 만들어보자</p>
</blockquote>
<p>우리가 주의할 점은 <code>updateItemCount</code>를 사용하는 부분에서 우리는 인자로 <code>item</code> 하나만 넘겼는데 <code>class</code>에서는 <code>event</code> 매개변수가 추가로 있다.<br>이를 통해 <code>on</code>도우미를 사용하고 이벤트를 호출하는 경우 기본인수로 <code>event</code>가 전달된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[	
Ember -Tutorial (Helper & custom Helper)]]></title>
            <link>https://velog.io/@lee_moi/Ember-Tutorial-Helper-custom-Helper</link>
            <guid>https://velog.io/@lee_moi/Ember-Tutorial-Helper-custom-Helper</guid>
            <pubDate>Mon, 29 Apr 2024 04:56:08 GMT</pubDate>
            <description><![CDATA[<h1 id="helper">Helper</h1>
<p>ember에는 helpler라는 개념이 있는데 이는 이전 글에서 <code>action</code>을 사용할 때 이용했던 <code>on</code>,<code>fn</code> 등이 이런 helper에 해당된다</p>
<p>그런데 한가지 다른게 보이는데 <code>on</code>을 사용할 때는<code>{{}}</code>안에서 사용했고 <code>fn</code>은 <code>()</code>안에서 사용됐다<br>이는 최상위 수준에서의 도우미가 <code>{{}}</code>로 시작하기 때문이다.<br><code>{{helper1, (helper2 ... (helper3 ....))}}</code> 가장 최상위는 <code>{{}}</code>로 시작하고 이후 중첩되는 도우미들은 <code>()</code>로 둘러싼다.</p>
<h2 id="built-in-helper">Built-in Helper</h2>
<p>ember에 내장된 도우미들 몇개만 살펴보자면</p>
<ul>
<li><code>{{on</code></li>
<li><code>{{concat</code> :여러 문자열을연결하는데 도움이 된다. `class={{concat &quot;active-&quot; @color}}</li>
<li><code>{{get</code> : 가져오는 것으로 <code>{{get this.product &quot;name&quot;}}</code>. 첫번째 인수는 해당 제품의 객체이고 2번째 인수는 그 객체의 키값이다. 객체의 키값으로 찾는 <code>product.name</code>과 동일하고 뒤에 오는 <code>&#39;name&#39;</code>은 동적으로도 가능하다</li>
<li><code>{{hash</code> : <code>&lt;Child @user ={{hash firstName=&quot;Lee&quot; lastName=&quot;Moi&quot;}}</code>처럼 주어진 키와 값 쌍으로 해시를 생성하는 도우미이다. 이는 내부적으로 <code>user = {firstName:&#39;Lee&#39;, lastName:&#39;Moi&#39;}</code>와 동일하다.</li>
<li><code>{{let</code> : <code>{{let (concat this.firstName this.lastName) as |fullName|}}</code> 처럼 사용하면 템플릿 내부에 임시 변수로서 사용할 수 있게 만드는 도우미로 <code>concat</code>도우미를 활용해 이름을 연결하여 <code>LeeMoi</code>라는 문자열을 <code>fullName</code>이라는 이름으로 사용할숭 있게 만드는 것이다.</li>
<li><code>{{if</code> : 프로그래밍에서 자주 보는 것으로 똑같다 <code>if</code> 조건절로 <code>class={{if this.isRed &#39;red&#39; &#39;black&#39;}}</code>이렇게 사용될 수 있다. 이는 <code>this.isRed</code>가 조건이며 <code>red</code>가 <code>true</code>일 때, <code>black</code>이 <code>false</code>일 때 출력되는 값이다.</li>
<li><code>{{unless</code> : <code>class={{unless this.isRed &#39;black&#39; &#39;red}}</code> <code>if</code>와 반대로 조건이 거짓인 경우 첫번째를 반환하고 참일경우 2번째를 반환한다. 우리가 자주 사용한 <code>!</code>를 붙여 <code>false</code>가 <code>true</code>일때를 쓰는 방식. <code>unless</code>도 똑같이 <code>else</code>를사용할 수 있다(열린태그에서)</li>
<li><code>{{each</code> : React에서 가장 많이 사용했던 <code>map</code>이라고 생각하자 <code>each</code>를 통해 반복을 돌리고 하나씩 꺼내서 렌더링 하는데 도움을 준다<br>아래 개방태그에서 마지막 예시를 확인해보자</li>
</ul>
<p>등이 있고 개방태그에서 사용할 수 있는 예시를 하나씩 보면 이해하기 편하다.</p>
<blockquote>
<p>주의할 점으로 요소의 attribute 영역에서 사용하는 것을 제외하고 열려있는 구역에서 사용할 시 <code>#</code>과 <code>/</code>을 통해 꼭 표시를 해줘야한다.</p>
</blockquote>
<pre><code>{{#let (concat this.firstName this.lastName) as |fullName|}}
  &lt;h1&gt;{{fullName}}&lt;/h1&gt;
{{/let}}

{{#if this.isRed}}
  &lt;h1&gt;Red&lt;/h1&gt;
{{/if}}

{#if this.isRed}}
  &lt;h1&gt;Red&lt;/h1&gt;
{{else}}
  &lt;h1&gt;Black&lt;/h1&gt;
{{/if}}

{#unless this.isRed}}
  &lt;h1&gt;Black&lt;/h1&gt;
{{else}}
  &lt;h1&gt;Red&lt;/h1&gt;
{{/unless}}

{{#each list as |item|
  &lt;h1&gt;{{item.name}}&lt;/h1&gt;
  &lt;h2&gt;{{item.description}}&lt;/h2&gt;
{{/each}}</code></pre><p>여기서 <code>each</code>에 대한 설명을 조금만 더 붙이자면 <code>each</code>를 <code>for</code>와 동일하게 보며 <code>list</code>는 반복을 돌릴 객체이며 <code>as |item|</code>부분은 <code>let</code>을 사용한 부분처럼 반복을 돌면서 나올 하나하나의 <code>item</code>을 <code>item</code>이라는 변수명으로 사용하겠다는 얘기이다.<br>그리고 꼭 끝났다는 {{/each}}를 집어넣어 마무리 하자</p>
<h1 id="custom-helper">custom helper</h1>
<p>사용자 지정 도우미를 만들 수 있는데 이도 똑같이 emberCLI를 통해 만들 수 있다.<br>또 나오긴 하지만 React에서 custom hook과 같은 느낌이 처음에 강하게 든 개념이다</p>
<pre><code>ember g helper currency</code></pre><p>라고 저번에 만들었던 샵과 장바구니에서 가격을 나타내는 데 $가격으로 나타내고 싶은 것을 도우미로 만들어보려한다.<br>처음 CLI를 입력하면 <code>currency.js</code>라는 파일이 생성되는데 첫 내부를 살펴보면</p>
<pre><code>import {helper} from &#39;@ember/component/helper&#39;

export default helper(function currency(params/*, hash*/) {
  return parmas
}</code></pre><p>이렇게 생겼다.</p>
<p>여기서 helper라는 한수에 인자로 우리가 CLI로 만들때 만든 이름인 <code>currency</code>라는 함수를 만들어 넘긴다.<br>이때 첫번째 인수인 <code>params</code>는 도우미 블록에서 전달되는 매개변수 목록이다.<br>즉, <code>{{currency 25}}</code>를 작성할경우 <code>params</code> 배열 내부의 첫번째 매개변수로 들어오게 된다.<br><code>const [number] = params;</code> 처럼 구조파괴 하여 사용가능하고 이를 통해 <code>number</code>에는 <code>25</code>라는 값이 담기게 된다.</p>
<p>그럼 주석 처리 되어 있는 <code>hash</code>부분은 2번째 매개변수로 무엇일까??</p>
<p>키가 정의되어 helper에게 값을 전달 할 수 있는 해시이다.</p>
<p>이또한 어떻게 사용하는지 바로 보자면 <code>{{currency 25 sign=&quot;$&quot;}}</code> 처럼 사용할 수 있는데 이 또한 <code>hash</code>를 구조분해 할당하여</p>
<p><code>const {sign} = hash</code>를 통해 안에 <code>sign</code>이라는 키값으로 보내진 값 <code>$</code>를 사용할 수 있게 된다!</p>
<p>뭐 기본값을 설정하거나 하는 등의 테크닉은 Javascript를 안다면 충분히 해볼 수 있다.<br>매개변수를 주지 않고 <code>currency</code>의 함수에서 <code>=</code>등을 사용해 기본값을 설정해서 사용한다면 고정적인 값들을 사용할 수 있게 된다.</p>
<p><strong>근데 꼭 함수 기반으로 해야할까?</strong> 이는 함수형 프로그래밍인 Javascript이긴 하지만 ember는 class가 대부분이다<br>이<code>curreny</code>도 비슷하게 갈 수 있지 않을까??</p>
<h2 id="class-helper로-변경">class helper로 변경</h2>
<pre><code>import Helper from &#39;@ember/component/helper&#39;

export default class currency extends Helper {
  compute(params,hash) {
    //...some code
    return /*...some code*/
  }
}</code></pre><p>내장되어 있는 <code>compute</code> 메서드를 사용하여 활용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ember - Tutorial(Component#2-Tracked,Getter,Actions) ]]></title>
            <link>https://velog.io/@lee_moi/Ember-TutorialComponent2-TrackedGetterActions</link>
            <guid>https://velog.io/@lee_moi/Ember-TutorialComponent2-TrackedGetterActions</guid>
            <pubDate>Fri, 26 Apr 2024 06:15:48 GMT</pubDate>
            <description><![CDATA[<p>제목에 적어놓은 Tracked, Getter, Actions는 컴포넌트에만 존재하는 것이 아니라 Controller에서 사용된다.</p>
<p>뿐만아니라 관련해서는 현재 튜토리얼 페이지에서 따라해보면 Route에서도 사용되는 것 같다.</p>
<h1 id="tracked-properties--getter">Tracked properties &amp; Getter</h1>
<p>먼저 컴포넌트의 구성부터 살펴보자면</p>
<pre><code>import Component from &#39;@glimmer/component&#39;
import {tracked} from &#39;@glimmer/trancking&#39;

export default class MyComponent extends Component {
  @tracked firstName = &#39;Shawn&#39;;

  @tracked lastName = &#39;Chen&#39;

  get fullName() {
    return this.firstName + this.lastName;
  }
}</code></pre><p><code>glimmer</code>에서 <code>import</code>시킨 <code>tracked</code>는 데코레이터로 속성 앞에 작성할 경우 동작한다.<br>어떤 것에 대한 동작이냐?<br>간단히 설명하면 React에서 사용했던 useState같은 느낌과 비슷하다.</p>
<p>우리가 React를 사용할 때 상태가 변경됨을 체크하려면 useState를 썼던 것처럼 속성이 변경될 때 알림을 받으려면 <code>@tracked</code> 즉, <code>GIT</code>을 할 때 한번쯤 들었던 것처럼 트랙킹,추적을 해야한다.</p>
<p>이경우 <code>hbs</code> 파일에서</p>
<pre><code>&lt;h1&gt;{{this.fullName}}&lt;/h1&gt;</code></pre><p>사용했을 때 fullName을 렌더링 해야한다면 일단 처음 만들어질 때 초기화 된 <code>Shawn Chen</code>을 출력할 것이다.<br><code>tracked</code>되어 있기 때문에 아마 변경이 된다면 최신 정보를 출력할 것이다.</p>
<p>그럼 최신으로 바꾸기 위해 필요한 <code>Action</code>부분을 살펴보자</p>
<h1 id="action">Action</h1>
<pre><code>import Component from &#39;@glimmer/component&#39;
import {tracked} from &#39;@glimmer/trancking&#39;
import {action} from &#39;@ember/object&#39;

export default class MyComponent extends Component {
  @tracked number = 0;

  @action
  addNumber() {
    this.number = this.number + 1;
  }
}</code></pre><p><code>action</code>데커레이터를 통해서 숫자를 증가시키는 간단한 액션을 추가한다. 이후 <code>template</code>부분에서 보자면</p>
<pre><code>&lt;h1&gt;Number : {{this.number}}&lt;/h1&gt;
&lt;button {{on &quot;click&quot; this.addNumber}}&gt;Add&lt;/button&gt;</code></pre><p>다른 React 등을 보다 보면 되게 생소할 수 잇다. 저게 함수인가?? 뭐지? 하는 느낌이 나도 처음 들었는데 일단 하나씩 살펴보자면..</p>
<p><code>on</code>부분은 이벤트를 트리거 할 수 있도록 도와주는 ember helper인데 이 부분인데 정확한 설명은 없고 일단 버튼에 이벤트를 트리거 하기 위해선 첫번째로 on이 들어가는 구나를 일단 인식해보자</p>
<p>2번째 인수로 들어간 <code>&quot;click&quot;</code>은 이벤트의 이름을 알려주고 클릭임으로 클릭이벤트를 트리거 한다라고 알려주는 것이다.</p>
<p>그리고 마지막 이벤트는 우리가 정의한 액션을 집어 넣는 것이다.</p>
<p><code>on</code>을 통해 ember helper를 불러서 ember가 이벤트를 트리거 할 수 있도록 부르고 <code>&quot;click&quot;</code>어떤 이벤트인지 알려주고, <code>this.addNumber</code>실행할 이벤트를 적는 것이다.<br><code>onClick={clickHandler}</code>를 <code>on/Click/={clickHandler}</code>react에서 사용된 클릭 이벤트를 저렇게 나눠 쓴다고 생각하면 바로 이해가 온다</p>
<p>그럼 이걸 이전에 만든 컴포넌트들에 적용시키려면 어떻게 해야할까?</p>
<p>똑같이 emberCLI를 이용한다면 만들기 쉽다</p>
<pre><code>ember g component-class product</code></pre><p>를 입력할 경우 <code>product</code>의 <code>class</code>가 담긴 <code>app/components/product.js</code>가 생성되게 된다.<br>해당 파일에서 <code>tracked</code>와 <code>action</code>을 이용해 다양한 함수 처리를 해보고 익혀보자</p>
<p>그렇다면 속성과 인수부분은 어떻게 될까??</p>
<h1 id="properties--arguments">Properties &amp; Arguments</h1>
<p>여기서 속성은 현재 정의된 변수라 보면 편한데</p>
<pre><code>export default class Child extends Component {
  propA = 1;
}

export default class Parent extends Component {
  propB = 2;
}</code></pre><p>클래스와 인수는 상위 구성요소에서 전달된 것이므로 예시로 바로 확인해보자</p>
<pre><code>&lt;!-- parent.hbs --&gt;
&lt;Child @propB={{this.propB}} /&gt;

&lt;!-- child.hbs --&gt;
&lt;h1&gt;Child : {{this.propA}}&lt;/h1&gt;

&lt;h1&gt;Parent : {{@propB}}&lt;/h1&gt;</code></pre><p>이렇듯 상위에 있는 <code>parent.hbs</code>에서 <code>Child</code>컴포넌트에게 React처럼 <code>props</code>를 보내고 싶을 때 <code>@</code>를 사용해서 보내면 된다<br><code>@</code>뒤에 보낼 prop에 이름을 명시하고 <code>={{this.propB}}</code>으로 <code>Parent</code>컴포넌트가 렌더링 될 당시 실행될 <code>class Parent</code>에 있는 속성 <code>propB</code>의 값 <code>2</code>를 <code>Child</code>에게 보낸다</p>
<p>이러면 <code>Child.hbs</code>에선 <code>propB</code>라는 이름으로 <code>2</code>라는 값을 받아 사용할 수 있게 되고 그사용되는 부분이 <code>{{@propB}}</code>이다.</p>
<p>React에서 props로 어떠한 값을 보낼대 사용한 <code>&lt;Child data={user} /&gt;</code>에서 <code>@</code>와 <code>{}</code>만 더 추가된 느낌이라 크게 어려울 것은 없다.</p>
<blockquote>
<p>css에서도 똑같이 동적인클래스를 만들 수 있다<br><code>@tarcked</code>된 속성을 변경됨에 따라 클래스를 변경할 수 있는데, 이는 <code>class</code>에서도 똑같이 <code>{{}}</code>를 사용해서 classname을 동적으로 줄 수 있다.<br>이는 만약 텍스트 샊깔을 바꾼다 했을 때 기본 프로퍼티로는 <code>@tracked color = black;</code>을 설정해놓은 상태다<br>그리고 변경버튼을 누를 경우 <code>red</code>색깔로 바뀐다고 가정했을 때 변경될 곳에<br><code>class=&quot;text-{{this.color}}</code>식으로 넣을경우 동적으로 클래스 네임을 변경할 수 있게 된다.  </p>
</blockquote>
<p>그럼 클릭을 눌렀을 때 어떠한 매개변수나 인자를 받고 싶다면 어떻게 하는게 좋을까?<br><code>{{on &#39;click&#39; this.changeColor}}</code> 이렇게 사용되는 걸 <code>{{on &#39;click&#39; this.changeColor(&#39;red&#39;)}}</code> 이렇게 바꾸면 될까?<br>이때는 함수도우미를 사용하면 인자를 넘겨줄 수 있게 된다</p>
<pre><code>&lt;button {{on &quot;click&quot; (fn this.changeColor &quot;red&quot;)}}&gt;ChangeRed&lt;/button&gt;</code></pre><p>이런 식으로 함수 도우미인 <code>fn</code>을 사용하고 함수명 뒤에 적는 것이 인자로 넘어가게 된다.<br>여러개라면 <code>{{on &quot;click&quot; (fn this.changeColor 인자1 인자2 인자3)}}</code>으로 넘겨주면 동일하게 동작한다.<br><strong>주의할 점으론 javascript 함수 법칙에 따라 인자와 매개변수의 위치에 대해 주의하자. 이는 typescript를 쓴다면 명확하게 코딩을 할 때 잡을 수 있는 부분이기도 하다</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ember - Tutorial(Component#1)]]></title>
            <link>https://velog.io/@lee_moi/Ember-TutorialComponent1</link>
            <guid>https://velog.io/@lee_moi/Ember-TutorialComponent1</guid>
            <pubDate>Thu, 25 Apr 2024 08:25:49 GMT</pubDate>
            <description><![CDATA[<h1 id="component">Component</h1>
<p>재사용 가능한 독립된 모듈로 주로 React를 한번쯤은 다뤄봤다면 아주 흔한 개념이다.
이는 Ember에서도 동일하게 있는데 한번 알아보자</p>
<p>라우터 내부나 다른 컴포넌트 또 그 컴포넌트 안에 컴포넌트 이렇게 React처럼 중첩이 가능하다.</p>
<h2 id="예시">예시</h2>
<p>우리가 index route에 있다고 가정했을 때 제품목록 안에 2개의 제품이 있다
그럼 각 제품 카드들의 프로덕트 모양은 동일할텐데 이미지 관련 로직이나 다른 구성들이 동일하게 동작한다면 이는 중복됨으로 하나의 컴포넌트로 처리하는 것이 맞다.</p>
<p>직접적인 전체 코드를 가져오진 않고 <code>component</code>를 어떻게 만들고 어떻게 사용하는 지에대해서 간단하게 다루고 넘어가보자</p>
<p>컴포넌트를 처음 만들때도 동일하게 emberCLI를 사용해 만들 예정이다</p>
<pre><code class="language-cli">ember g component general-container</code></pre>
<p>이러면 <code>app/components/general-container.hbs</code>라는 파일이 생성되게 된다.
이를 통해 <code>general-container.hbs</code>라는 컴포넌트를 만들고 다른 곳에서도 동일하게 컴포넌트를 통해 동일한 로직과 기능을 재사용 할 수 있게 된다.</p>
<p>일단 저번 글에서 만들었던 장바구니로 가는 버튼을 컴포넌트로 만든다면</p>
<pre><code class="language-html">&lt;!-- app/components/general-container.hbs--&gt;

&lt;LinkTo @route=&quot;cart&quot; class=&quot;cart_link&quot;&gt;
  cart
&lt;/LinkTo&gt;

&lt;main class=&quot;container mt-5&quot;&gt;
  {{yield}}
&lt;/main&gt;</code></pre>
<p>이런식으로 추가 하게 됐는데 아까 <code>index</code>에서 사용하던 페이지 모양하고 동일하며 코드 중간을 보면 <code>{{yield}}</code>라는 특이한 게 있다.
이는 쉽게 말하면 React에서 사용하던 <code>{children}</code>과 같은 것으로 외부에서 해당 위치의 구성요소를 직접적으로 컨트롤 할 수 있게 되는 것이다.
이를 통해 공통적인 부분은 두고 나머지 변할 수 있는 부분의 확장성을 고려한 컴포넌트를 작성할 수 있다.</p>
<p>그럼 이걸 어떻게 사용해야 하는가?</p>
<p>우리가 React를 사용할 때는 함수형 컴포넌트를 만들고 이를 <code>export</code>시킨 후 <code>import</code>하여 사용하였는데 ember는 따로 import하는 부분이 존재하진 않는다</p>
<pre><code class="language-html">&lt;!-- app/templates/index.hbs --&gt;

&lt;GeneralContainer&gt;
  &lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot;&gt;Product 1&lt;/LinkTo&gt;
  &lt;LinkTo @route=&quot;item&quot; @model=&quot;2&quot;&gt;Product 2&lt;/LinkTo&gt;
&lt;/GeneralContainer&gt;</code></pre>
<p><code>import</code>도 없이 그냥 코드 자체를 이렇게 작성할 경우 이전에 작성한 코드랑 동일하게 동작하는 것을 볼 수 있다.
작성된 코드를 보면 굉장히 특이한데 component의 파일 명은 <code>general-container</code>인데 사용되는 부분은 <code>&lt;GeneralContainer&gt;</code>로 사용되고 있다.
이를 통해 코드 베이스 내부에 대시 케이스가 있더라도 엠버 구성요소 이름이 어떻게 되어 있는지 알 수 있다.(첫글자는 대글자여야 하고 카멜케이스로 작성해야한다)
이를 통해 <code>item</code> 페이지도 변경하자면</p>
<pre><code class="language-html">&lt;!-- app/tmeplates/item.hbs --&gt;
&lt;GeneralContainer&gt;
  ITEM :{{this.model}}
  product
&lt;/GeneralContainer&gt;</code></pre>
<p>이도 <code>index</code>처럼 동일하게 동작하는 걸 볼 수 있다</p>
<p>그럼 <code>{{yeild}}</code> 말고 React에서 자식 컴포넌트에게 필요한 데이터를 넘겨주던 <code>props</code>방식은 어떻게 사용할까??
이는 Product 상세페이지로 이동하는 링크 컴포넌트도 하나 만들어서 테스트해보자
똑같이 emberCLI를 사용해 component를 하나 만든다</p>
<pre><code class="language-cli">ember g component product</code></pre>
<pre><code class="language-html">&lt;!-- app/coomponents/product --&gt;
&lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot; class=&quot;product&quot;&gt;
  &lt;div class=&quot;product-image&quot;&gt;
    &lt;img src=&quot;/assets/images/headphone.png&quot; alt=&quot;&quot; /&gt;
  &lt;/div&gt;
  &lt;div class=&quot;product-details&quot;&gt;
    &lt;h3&gt;title&lt;/h3&gt;
  &lt;/div&gt;
&lt;/LinkTo&gt;</code></pre>
<p><code>product</code>컴포넌트를 만들다 보니 <code>image</code>가 들어가는 부분이 생기는데 이 부분도 컴포넌트를 하나 만들어보자. 이때의 컴포넌트는 <code>product</code>의 컴포넌트로 사용하기 위해</p>
<pre><code class="language-cli">ember g component product/image</code></pre>
<p>이러면 파일 구조가 <code>components/product/image.hbs</code>로 하나가 생기는데 이러면 똑같이 <code>&lt;Image/&gt;</code> 이런식으로 사요하면 될까?
조금 다르다
일단 <code>product</code>에서 작성한 <code>img</code>태그 부분을 잘라내서 Image 컴포넌트에 옮긴 뒤 실제로 사용할 때는</p>
<pre><code class="language-html">&lt;!-- app/coomponents/product --&gt;

&lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot; class=&quot;product&quot;&gt;
  &lt;Product::Image /&gt;
  &lt;div class=&quot;product-details&quot;&gt;
    &lt;h3&gt;title&lt;/h3&gt;
  &lt;/div&gt;
&lt;/LinkTo&gt;</code></pre>
<p>이런 식으로 <code>&lt;Produt::Image/&gt;</code> <code>::</code>를 사용해서 접근할 수 있다</p>
<p>이걸 동일하게 detail 부분에도 사용한다면 emberCLI통해 component를 만들고 <code>Product::Detail</code>을 통해 컴포넌트를 사용할 수 있다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[	
Ember - Tutorial(Router & controller)]]></title>
            <link>https://velog.io/@lee_moi/Ember-TutorialRouter-controller</link>
            <guid>https://velog.io/@lee_moi/Ember-TutorialRouter-controller</guid>
            <pubDate>Thu, 25 Apr 2024 05:57:49 GMT</pubDate>
            <description><![CDATA[<p>라우팅 시스템과 Router와 controller는 밀접한 연관이 있으니 바로 다뤄보는 게 좋을 것 같다.</p>
<p>유튜브 ember tutorial에서 만드는 페이지로 연습을 같이 해보려고 하는데 일단 이전에 만든 필요없는 경로를 삭제해보자</p>
<hr>
<h1 id="router-삭제">router 삭제</h1>
<p>당연하게도 이를 도와주는 emberCLI가 존재하는데</p>
<pre><code>
ember destroy route clothes/index &amp;&amp; ember destroy route clothes/t-shirt &amp;&amp; ember destroy route clothes</code></pre><p><code>clothes</code>경로에는 중첩된 부분이 많기 때문에 중첩된걸하나하나 지워가며 마지막에 가장 큰 경로를 삭제하는 게 좋다.</p>
<p>바로 큰 경로를 삭제해봐야 폴더 내부는 안지워지고 `clothes.hbs`와 테스트 등 `clothes` 경로에 관한것만 지워지지 `clothes`폴더에 있는 중첩된 경로들까지 삭제해주진 않는다.</p>
<h2 id="새-router-추가">새 router 추가</h2>
<p>이전에 쓸모없는 경로들을 삭제했으니 이제 웹 페이지에 들어갈만한 경로들을 만들자.</p>
<p>먼저 홈페이지인 애플리케이션 인덱스 페이지 경로인 인덱스를 생성하고 장바구니로 쓸 cart부분을 만들자</p>
<p>물론 emberCLI로 만든다</p>
<pre><code>ember g route index &amp;&amp; ember g route cart</code></pre><p>그리고 <code>cart</code>경로의 경우 실제 경로 이름에선 좀 더 명확하게 해주기 위해서 다룰 파일의 이름은 쉽고 경로 이름은 명확하게 서로 다른 옵션을 사용하기 위해 <code>router.js</code>에서 변경해준다</p>
<pre><code>Router.map(function () {
  this.route(&#39;item&#39;, { path: &#39;item/:item_id&#39; });
  this.route(&#39;not-found&#39;, { path: &#39;/*path&#39; });
  this.route(&#39;cart&#39;, { path: &#39;shopping-cart&#39; });
});</code></pre><p>이처럼 입력할 경우 경로는 <code>/shopping-cart</code>가 되지만 실제 동작은 <code>cart</code>가 받아서 하게된다</p>
<blockquote>
<p><strong>이때 index route는 왜 없나요?? 할 수 있다</strong><br>이는 index일 경우 default 페이지 역할을 하기 때문에 최상단 <code>application.hbs</code> 와 동일한 뎁스에 <code>index.hbs</code>를 가졌음으로 맨 처음 뒤에 어떠한 추가 경로도 적지 않고 말 그대로 홈페이지에 접속할 경우 이 해당 <code>index.hbs</code>로 접속할 수 있다.<br>현재 로컬에선 <code>http://localhost:4200</code>으로 접속시 <code>application.hbs</code>의 <code>{{outlet}}</code>부분에 <code>index.hbs</code>가 뜨게 되는 것이다.</p>
</blockquote>
<h2 id="navigate-pages">Navigate pages</h2>
<p>이제 홈페이지로 표현될 index.js에 상품 목록들을 보이게 하고 상품 목록들을 클릭하면 상품 상세보기로 점프 할 수 있게 하려고 한다.</p>
<p>먼저 application.hbs에 있는 코드들을 {{outlet}}을 제외하고 모두 지운 뒤 index.js에서 원하는 목록모양으로 HTML작성한다</p>
<pre><code>&lt;main class=&quot;container mt-5&quot;&gt;
  &lt;div&gt;Product 1&lt;/div&gt;
  &lt;div&gt;Product 2&lt;/div&gt;
&lt;/main&gt;</code></pre><p>일단은 이렇게 작성하였는데 우리가 원하는 건 저 Product 1부분을 클릭했을 때 해당 제품의 상세페이지로 점프하는 것이다.</p>
<p>이를 어떻게 만들 수 있을까?<br>Next.js로 할땐 Link라는 녀석이 있었는데 ember도 비슷한 놈이 있다</p>
<p><strong>바로 LinkTo이다</strong></p>
<p>그러면 Next의 Link 처럼 href로 경로를 나타내냐? 이건 또 아니고 @route라는 걸 사용한다</p>
<pre><code>&lt;main class=&quot;container mt-5&quot;&gt;
  &lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot;&gt;Product 1&lt;/LinkTo&gt;
  &lt;LinkTo @route=&quot;item&quot; @model=&quot;2&quot;&gt;Product 2&lt;/LinkTo&gt;
&lt;/main&gt;</code></pre><p>이 경우 제품을 클릭할 때 이동되는 경로는 item이 되고 이동할때 @model=&quot;1&quot;을 통해 id값을 넘겨주게 된다</p>
<p>이처럼 LinkTo로 바꾼 페이지 모양은 html변환시 a태그와 동일하다</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/2032b2e8-1e2f-4569-9576-e7b315f13784/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/5d636716-b8a8-4cbb-b8e6-b3576fb5c4a0/image.png" alt=""></p>
<p>이런식으로 <code>LinkTo</code>를 통해 각 경로를 이동할 수 있고 <code>.hbs</code>에 원하는<code>hmtl</code>을 작성하여 템플릿을 렌더링 할 수 있다.</p>
<p>이걸 통해 각 cart와 product부분을 만든 후 <code>cart.hbs</code>에서 결제를 시킬 <code>check out</code>이라는 버튼을 한번 만들어보자.</p>
<pre><code>  &lt;button type=&quot;button&quot; class=&quot;btn btn-success float-right&quot;&gt;Check out&lt;/button&gt;</code></pre><p>이렇게 일단 button 은 만들어 놓고 버튼이 클릭 되었을 때 동작해야할 기능들은 어디에 적어야 할까?<br>기존 react나 next같은 경우 현재 함수형 컴포넌트들을 통해 같은 함수 스코프 안에 return 상단에 여러 기능들을 하는 함수들을 적어서 <code>onClick</code>에 넣는 식으로 사용했었는데 ember는 좀 모양새도 많이 달랐다</p>
<p>이는 먼저 Route와 Controller를 알아보고 추후에 추가해보자</p>
<hr>
<h1 id="router와-controller">Router와 Controller</h1>
<p>라우터와 컨트롤러의 차이점은 동일한 URL에 대해 작동하고 이름도 같지만 다른 폴더에 존재한다.</p>
<ul>
<li>Route<ul>
<li>route/cart.js</li>
</ul>
</li>
<li>Controller<ul>
<li>controller/cart.js</li>
</ul>
</li>
</ul>
<p>이렇게 서로 <code>cart.js</code>라는 같은 이름을 가지지만 각각 <code>Route</code>와 <code>Controller</code>라는 다른 폴더에 존재한다.<br>둘 다 app 경로 아래에 존재하며 <code>template/cart.hbs</code>를 렌더링한다.</p>
<p>Route에선 controller에 모델을 관여할 수 있으며 다양한 Methods들이 존재하는데 이들을 사용할 수 있다.</p>
<p>한번 다이나믹 라우팅을 사용하는 item은 product <code>LinkTo</code>로 이동할 때 <code>id</code>도 보내는데 이를 어떻게 받을까?<br>지난 글에서 썼었던? <code>route/item.js</code>를 활용해보자</p>
<pre><code>// app/route/item.js
import Route from &#39;@ember/routing/route&#39;;

export default class ItemRoute extends Route {
  model(params) {
    const { item_id } = params;

    return item_id;
  }
}

// app/templates/item.hbs
&lt;h2&gt;{{this.model}} product&lt;/h2&gt;</code></pre><p><code>this.model</code>처럼 사용할 수 있는 건데 이걸 통해 <code>routes/cart.js</code>도 변경해보자</p>
<pre><code>import Route from &#39;@ember/routing/route&#39;;

export default class CartRoute extends Route {
  model() {
    const items = [{ price: 10 }, { price: 15 }];
    return items;
  }
}</code></pre><p>만약 이런식으로 여러 가격을 가지고 있는 상품들이 있다고 생각하고 작성해보았다<br>그리고 controller를 사용해보려고 하는데 우리는 아직 만들지 않았으니 ember CLI로 만들어보자.</p>
<pre><code>ember g controller cart</code></pre><p>이렇게 <code>controller</code>에 <code>cart</code>를 만든후 템플릿에 전달할 몇가지 사용자 정의 속성을 만들어보자</p>
<pre><code>// app/contorollers/cart.js
import Controller from &#39;@ember/controller&#39;;

export default class CartController extends Controller {
  subtotla = 0;
  tax = 0;
  total = 0;
}</code></pre><p><code>cart</code>라는 장바구니 페이지에서 사용될 상품가격, 세금, 총 가격을 <code>CartController</code>에 기본적으로 정의하고 <code>templates</code>에 있는 <code>cart.hbs</code>에 가서 정의된 속성들을 사용해보자</p>
<pre><code> &lt;section class=&quot;w-50 ml-auto text-right mb-5&quot;&gt;
    &lt;div class=&quot;row&quot;&gt;
      &lt;span class=&quot;col&quot;&gt;Subtotal&lt;/span&gt;
      &lt;span class=&quot;col&quot;&gt;{{this.subtotal}}&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;row&quot;&gt;
      &lt;span class=&quot;col&quot;&gt;Tax&lt;/span&gt;
      &lt;span class=&quot;col&quot;&gt;{{this.tax}}&lt;/span&gt;
    &lt;/div&gt;
    &lt;div class=&quot;row&quot;&gt;
      &lt;span class=&quot;col&quot;&gt;Total&lt;/span&gt;
      &lt;span class=&quot;col&quot;&gt;{{this.total}}&lt;/span&gt;
    &lt;/div&gt;
  &lt;/section&gt;</code></pre><p>여기서 <code>{{this.subtotal}}</code>사용 된 <code>this</code>는 현재 컨트롤러 또는 현재 라우팅을 가리키고 이를 저장하고 앱을 실행하면 표시되는 페이지가</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/c68fc358-ac4e-4799-b9ee-ceaec6feda27/image.png" alt=""></p>
<p>이렇게 표시되는 것을 볼 수 있다.<br>이 숫자 0들은 우리가 <code>Contoroller</code>에서 정의한 값들이다. 하지만 아직 정적인 값들이니 이 값들을 실제로 관리해보기 위해 <code>route/cart.js</code>로 이동해서 코드를 추가해보자</p>
<pre><code>// routes/cart.js

import Route from &#39;@ember/routing/route&#39;;

export default class CartRoute extends Route {
  model() {
    const items = [{ price: 10 }, { price: 15 }];
    return items;
  }

  setupController(controller, model) {
    super.setupController(controller, model);
    const subtotal = model.reduce((acc, item) =&gt; acc + item.price, 0);
    controller.set(&#39;subtotal&#39;, subtotal);
  }
}</code></pre><p>이는 먼저 <code>setupController</code>를 통해 컨트롤러 기능을 재정의 하는 메서드를 사용한다.<br>이때 <code>setupController</code>는 이미 있는 걸로 재정의 할 때 매개변수는 <code>controller,model</code> 을 사용할 것이다.<br>그리고 <code>super</code>를 사용해 <code>super.setupController()</code>로 상속된 모든 항목이 호출됐는지 확인해본다</p>
<p>이후 <code>subtotal</code>을 계산하기 위해 <code>model</code>을 가져온다. 이때 모델은 위에서 return 된 items가 된다.<br>이후 <code>controller.set</code>을 통해 <code>controller</code>에서 정의된 <code>subtotal</code>의 값을 변경한다</p>
<p>하지만 컨트롤러가 모델에 직접적으로 엑세스 할 수 있는 걸 발견했으니 굳이 <code>route</code>에서 작업하지 않고 바로 <code>controller</code>에서 작업이 가능하단 걸 깨달을 수 있다.<br>작업한 결과물을 잘라내서 <code>controller</code>에 붙여넣어보자</p>
<pre><code>// controllers/cart.js

import Controller from &#39;@ember/controller&#39;;

export default class CartController extends Controller {
  get subtotal() {
    return this.model.reduce((acc, item) =&gt; acc + item.price, 0);
  }
  get tax() {
    return 0.09 * this.subtotal;
  }
  get total() {
    return this.subtotal + this.tax;
  }
}</code></pre><p>이처럼 getter를 통해서 각각의 <code>subtotal, tax, total</code>등을 계산하고 정의했고 이는 화면에 똑같이 출력되는 걸 확인해볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/7d8a17fe-abff-4152-bf49-55f25e45eaa1/image.png" alt=""></p>
<p>이렇게 controller와 route를 통해서 우리가 사용하는 render에 여러 가지를 동적으로 집어 넣을 수 있게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ember - Tutorial(Routing System)]]></title>
            <link>https://velog.io/@lee_moi/Ember-TutorialRouting-System</link>
            <guid>https://velog.io/@lee_moi/Ember-TutorialRouting-System</guid>
            <pubDate>Wed, 24 Apr 2024 08:18:59 GMT</pubDate>
            <description><![CDATA[<p>업무 진행을 위해 Ember를 처음부터 배워보고 있는데 단순히 보고 Tutorial 을 따라한 것으로는 부족한 것 같아 정리 차 작성해보려 한다</p>
<p>모든 내용은 Ember 공식문서의 Tutorial과 예전 버전이긴 하지만 2022 ember beginners라는 외국 유튜브를 보고 적절히 섞어 작성하였다.<br>물론 Test에 관련된 부분은 적당히 제거했다</p>
<p>먼저 tutorial에서 만들어지는 웹사이트는 공식문석에 실제로 netlify를 통해 배포되어 있으니 직접 확인해 봐도 된다<br><a href="https://ember-super-rentals.netlify.app/">Tutorial완성페이지 확인하기</a></p>
<hr>
<h1 id="ember-cli-설치">Ember CLI 설치</h1>
<p>먼저 Ember를 쉽게 사용하기 위한 CLI를 먼저 설치 했다</p>
<pre><code>npm install -g ember-cli</code></pre><p>설치가 잘 완료되었는지 확인해보자</p>
<pre><code>ember --version</code></pre><p>입력시 이런식으로 출력이 잘 되는지 확인해보자</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/8d2427dc-1660-4754-9737-e7b1ff28be52/image.png" alt=""></p>
<h2 id="embercli로-새로운-ember앱-만들기">EmberCLI로 새로운 Ember앱 만들기</h2>
<p>EmberCLI 명령을 사용해서 새 프로젝트를 사용할 수 있다. <code>create react-app</code>처럼 간단히 만들 수 있는 명령어인데 패턴을 살펴보자면<br><code>ember new &lt;project-name&gt;</code>으로 project-name 부분에 원하는 프로젝트 이름을 작성시 자동적으로 만들어 진다.<br>이때 공식문서에는 나와있는데로 뒤에 <code>--lane en</code>을 통해 앱의 기본 언어를 영어롤 설정시키자. 웹 사이트의 접근성이 향상된다</p>
<pre><code>ember new test-project --lang en</code></pre><p>그렇다면 새로 생긴 <code>test-project</code>폴더에 ember 프로젝트가 생긴 걸 확인할 수 있다.<br>이후 해당 폴더로 이동하여 <code>npm start</code>를 실행할 경우 아주 기초적인 웹사이트가 하나 뜨는 걸 확인할 수 있다<br>주소는 <a href="http://localhost:4200">http://localhost:4200</a> 으로 기본 설정 되어 있다</p>
<hr>
<h1 id="파일-구조-살펴보기">파일 구조 살펴보기</h1>
<p>처음 만들어질 때 파일 구조를 잘 살펴보면 아래와 같이 만들어져 있을 것이다.</p>
<pre><code>test-project
├── .github
│   └── workflows
│       └── ci.yml
├── app
│   ├── components
│   │   └── .gitkeep
│   ├── controllers
│   │   └── .gitkeep
│   ├── helpers
│   │   └── .gitkeep
│   ├── models
│   │   └── .gitkeep
│   ├── routes
│   │   └── .gitkeep
│   ├── styles
│   │   └── app.css
│   ├── templates
│   │   └── application.hbs
│   ├── app.js
│   ├── index.html
│   └── router.js
├── config
│   ├── ember-cli-update.json
│   ├── environment.js
│   ├── optional-features.json
│   └── targets.js
├── public
│   └── robots.txt
├── tests
│   ├── helpers
│   │   └── index.js
│   ├── integration
│   │   └── .gitkeep
│   ├── unit
│   │   └── .gitkeep
│   ├── index.html
│   └── test-helper.js
├── .editorconfig
├── .ember-cli
├── .eslintcache
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── .stylelintignore
├── .stylelintrc.js
├── .template-lintrc.js
├── .watchmanconfig
├── README.md
├── ember-cli-build.js
├── package.json
├── package-lock.json
└── testem.js

16 directories, 37 files</code></pre><p>하나씩 살펴보자면</p>
<ul>
<li>app 폴더<ul>
<li>기본 폴더로 우리가 주로 봐야할 소스 파일들이 존재하는 곳이다(templates,components 등)</li>
</ul>
</li>
<li>config 폴더<ul>
<li>환경 변수가 여기에 설정되는 것과 같은 일부 구성 정보가 포함되어 있다</li>
</ul>
</li>
<li>public 폴더<ul>
<li>이미지나 일부 문서와 같은 모든 정적파일을 저장하는 공용 폴더</li>
</ul>
</li>
<li>tests 폴더<ul>
<li>모든 test파일</li>
</ul>
</li>
</ul>
<hr>
<h1 id="라우팅-시스템">라우팅 시스템</h1>
<p>ember에는 브라우저 URL과 통합되는 라우팅 시스템이 함께 제공된다.<br>예를 들면 우리가 localhost:4200/cart로 가려 한다면 실제 <code>app/router.js</code> 내부에서 카트경로를 검색 하기 시작한다.</p>
<pre><code>// ...app/router.js

Router.map(function () {
  this.route(&#39;cart&#39;);
});</code></pre><p>이때 찾고있던 관련 항목을 찾으면 전체 앱이 어떻게 구성되어 있는지 빠르게 확인할 수 있도록 사이트맵 역할을 한다<br>이때 구동되는게 <code>app/routes/cart.js</code>와 <code>app/controllers/cart.js</code>이고 이를 통해 우리가 만든 <code>app/templates/cart.hbs</code>가 템플릿 렌더링을 시작하게 된다.</p>
<p>라우팅에도 여러가지가 있는데</p>
<ul>
<li>Application route</li>
<li>Basic route</li>
<li>Nested route</li>
<li>Index route</li>
<li>Dynamic route</li>
<li>404 not found route</li>
</ul>
<p>등이 있다.<br>하나씩살펴보자</p>
<h2 id="application-route">Application route</h2>
<p>먼저 ember에는 HCBS의 <code>app/template/application.hbs</code> 으로 시작되는 기본 애플리케이션 경로가 있다<br>이는 웹 앱의 시작점이기도 한데 최상위에 존재하는 react로 보자면 main이나 next의 최상위 layout이라고 생각하면 편할 것 같다.<br>모든 곳에서 쓰이는 부분을 이곳에 적용시키면 모두 적용된다.<br>Tutorial에선 <code>nav-bar</code>를 <code>application.hbs</code>에 적용한 다음 <code>{{outlet}}</code>을 통해 하위 요소들을 렌더링 시킨다.<br>마치 react 최상위 요소에서 <code>navbar</code>컴포넌트를 넣고 아래에 <code>{children}</code>을 통해 각 페이지들이 렌더링 되도록 하는 것과 같았다</p>
<h2 id="basic-route">Basic route</h2>
<p>기본 경로이다.<br>ember cli를 통해 만들어 낼 수도 있고 직접 파일을 만들 수도 있는데 ember cli가 편한 방법 같기도 하다.<br>아직 ember에 대해 부족한 부분이 많아 CLI로 만드는게 좋은것인지 직접 만드는 게 좋은것인지 잘 모르겠지만 일단 Tutorial과 beginner 강의가 알려준대로 따라가보자.</p>
<pre><code>ember g route clothes
// 또는
ember generate route clothes</code></pre><p>이렇게 작성할 경우 저절로 template에 관련 파일이 생성되고 <code>app/router.js</code>의 <code>Router.map</code> 함수 안에도 저절로 입력되어 있을것이다.</p>
<pre><code>//...app/router.js
Router.map(function(){
    this.route(&#39;clothes&#39;)
})</code></pre><p>이러면 <code>localhost:4200/clothes</code>로 이동할 경우 <code>app/templates/clothes.hbs</code> 에 입력한 요소들이 출력되게 된다.</p>
<h2 id="nested-route">Nested route</h2>
<p>중첩된 경로로 <code>/clothes/t-shirt</code>와 같이 경로에 경로로 들어가는 경우이다.<br>예로 현재 clothes라는 옷 경로를 사용중인데 가장 가까운 하위 카테고리로 티셔츠로 들어가야 한다. 이때는 어떻게 구성을 하고 만들어야 할까??<br>생각보다 간단한데 이전 <code>clothes</code>경로를 만들 때처럼 ember CLI를 사용하면 된다</p>
<pre><code>ember g route clothes/t-shirt</code></pre><p>clothes는 이미 만들어져 있고 그에 중첩된 경로를 <code>/</code>를 통해 작성해주면 되는데 이러면 <code>app/tmeplates</code>폴더에 <code>clothes</code>라는 폴더가 생성되게 되고 그안에</p>
<p>그리고 <code>router.js</code>내부 사항도 변경되게 되는데</p>
<pre><code>Router.map(function(){
    this.route(&#39;clothes&#39;,function(){
      this.route(&#39;t-shirt&#39;)
    })
})</code></pre><p>처럼 변하게 된다.</p>
<p>상위 루트의 복사 기능 내에 루트 설정이 추가되는 것이다.(말이 번역본이라 어렵긴 한데 javascript 문법적으로만 보자면 <code>Router</code>라는 class는 <code>app/router.js</code> 안에서 선언된 클래스로 <code>EmberRouter</code>를 상속받고 있다.<br>그리고 Router 내부에서는 위에서 얘기했던 <code>config</code>폴더의 환경변수 파일 <code>environment.js</code>에서 <code>locationtype</code>과 <code>rootURL</code>을 덮어 씌우고 있다.</p>
<p>이후 <code>Router</code>클래스의 <code>map</code>메서드를 실행시키는데 map 메서드는 <code>RouterDSL</code>이라는 콜백함수를 받는다. 해당 콜백함수 안에는 <code>route</code>라는 메서드가 존재하고 <code>route</code>메서드는 우리가 연결한 <code>url</code>경로 이름을 첫번째 매개변수로 받고 <code>option</code>(경로 이름과 파일 이름을 서로 다른걸 사용하고 싶을때)과 또 같은 <code>RouterDSL</code>을 콜백함수로 가지는 콜백함수를 옵셔널로 실행할 수 있다.</p>
<p>여기서 중첩 라우팅일 경우 route안에 route로 중첩해서 사용하는 방식으로 <code>this.route</code>를 통해 <code>clothes</code> 경로를 만든뒤 바로 위에서 설명한 옵셔널한 콜백함수를 다시 작성하여 <code>this.route</code> <code>t-shirt</code>를 넣는것이다.</p>
<p>이후 <code>app/templates/clothes/t-shirt</code>로 들어가면 <code>t-shirt</code>에서 작성한 HTML 요소들이 보일 것이다.</p>
<h2 id="index-route">Index route</h2>
<p>각 슬래시 마다 기본적으로 보여주는 페이지들이 있는데 이를 index를 통해 관리 할 수 있다.<br>예를 들어 <code>clothes</code>라는 경로로 이동했을 때 물론 <code>clothes.hbs</code>라는 파일 내에서도 작성할 수 있지만 무언가 기본적으로 표시되고 싶은 걸 파일로 나누고 싶다면 <code>index.hbs</code>를 <code>templates/clothes</code>폴더안에 생성할 경우 자동적으로 <code>clothes.hbs</code>의 <code>{{outlet}}</code>부분에 표시된다.<br>이때 경로를 <code>clothes/t-shirt</code>로 들어갈 경우 <code>index.hbs</code>에서 작성한 요소는 표기되지 않고 <code>t-shirt.hbs</code>에서 작성한 요소만 표기되며<br><code>/clothes</code>만 들어갈 경우 <code>clothes.hbs</code>의 <code>{{outlet}}</code>부분에 <code>index.hbs</code>의 요소들이 자동적으로 표기되게 된다</p>
<p>각 라우팅들에서 표시될 수 있는 기본 값이라고 생각하면 편할 것 같다.</p>
<h2 id="dynamic-route">Dynamic route</h2>
<p>동적 라우팅으로 동적실행 또는 동적 세그먼트이다</p>
<p>주로 사용 되는게 게시물이나 상품의 <code>ID</code>를 경로에 넣는 것이 있다. <code>item/123</code>이면 123번의 item을 보여달라는 것과 같다<br>지금 <code>item/123</code>의 경우 URL의 마지막 세그먼트 인스턴트인데 한번만들어보자<br>똑같이 emberCLI를 통해 만든다면</p>
<pre><code>ember g route item</code></pre><p>으로 한번 만들어보자 이경우 위에서 <code>router.js</code> 에 새로 만들어지는 것처럼</p>
<pre><code>Router.map(function(){
    this.route(&#39;clothes&#39;,function(){
      this.route(&#39;t-shirt&#39;)
    })
    this.route(&#39;item&#39;)
})</code></pre><p>가 생성되는데 조금 변경시켜보자</p>
<pre><code>Router.map(function(){
    this.route(&#39;clothes&#39;,function(){
      this.route(&#39;t-shirt&#39;)
    })
    this.route(&#39;item&#39;,{path:&quot;/item/:item_id&quot;})
})</code></pre><p>아까 말했던 옵셔널로 주어지는 인자중에 표기와 이름을 다르게 하는 옵션인자를사용해 <code>{path:&quot;/item/:item_id&quot;}</code>를 통해 실제 url은 <code>item/</code>과 마지막에 <code>:item_id</code>가 동적 세그먼트로 들어올수 있도록 한다</p>
<p>이후 실제로 <code>/items/1</code>으로 경로를 이동해서 실제테스트 해보자.</p>
<p>화면은 잘 뜨는데 그럼 동적 라우팅한 값은 어디서 얻을 수 있을까?</p>
<p><code>app/routes</code> 폴더로 이동해보면 emberCLI를 통해 <code>generate</code>로 만들 때 함께 만들어진 <code>clothes.js</code>와 <code>item.js</code>가 자동적으로 만들어져 있을 것이다.<br>이때 <code>item</code>에 대해서 동적으로 받는 것에 대한 모델링이 필요함으로</p>
<pre><code>import Route from &#39;@ember/routing/route&#39;

export default class ItemRoute extends Route {
    model(params) {
      const {item_id} = params;
      return item_id;
    }
}</code></pre><p>이렇게 작성하면 이젠 정상적으로 렌더링 되는 것을 확인할 수 있다<br>그럼 이 모델에서 <code>return</code>한 <code>item_id</code>는 어디서 어떻게 받을 수 있을까? 하고 의문을 가지는데 이는 <code>item.hbs</code>에서 사용할 수 있다</p>
<pre><code>// app/templates/item.hbs
&lt;h1&gt; {{this.model}} &lt;/h1&gt;</code></pre><p>이렇게 작성할 경우 <code>this.model</code>부분이 아까 <code>return item_id</code>부분이 되게 된다.</p>
<p>이를 살펴볼때 위에서 설명한 ember의 라우팅 부분에서 마지막 <code>hbs</code>파일에 도달해서 렌더링 하기 전에 route폴더와 controller 폴더에서 해당되는 js들을 실행시키고 오는 것 같다.<br>마치 class형 react를 했을 때 처럼 사용되는 함수들은 route폴더 안의 js파일에 메서드들로 작성하고 실행은 hbs에서 <code>{{this.함수명}}</code>으로 실행시키는 것이다.</p>
<p><strong>이때 주의할 것으로 실제로 에러를 겪었던 부분인데 이름의 중요성이다. <code>item</code>이라는 경로를 사용 했는데 templates에서도 <code>item.hbs</code>여야 하고 routes에서도 <code>item.js</code> 그리고 실행되는 class도 ItemRoute로 대소문자까지 잘 지켜줘야 실행되는 걸 볼 수 있다.<br>때문에 직접 만들 경우 오타의 문제가 생길지 모르니 emberCLI를 사용하는 게 더 좋은 것 같다는 생각이 든다</strong></p>
<h2 id="404-not-found-route">404 not found route</h2>
<p>우리가 흔히 보는 에러페이지로 next 등에는 <code>404.js</code>사용하는 것처럼 사용할 수 있다.<br>예를 들어 우리가 지정하지 않은 경로로 사용자가 이동 하였을 때 사용자는 개발자가 아니기에 콘솔창을 확인해 보지 않는다.<br>이로 인해 비어 있는 페이지가 사용자를 반기게 되고 사용자는 페이지가 로딩중인지 에러가 났는지 전혀 알 수가 없는 상태가 된다.<br>이러면 유저 입장을 전혀 고려하지 않은 설계가 됨으로 이러한 에러 페이지는 필수라고 생각한다.</p>
<p>ebmerCLI를 통해 만들어보자</p>
<pre><code>ember g route not-found</code></pre><p>이후 작성된 경로부분을 수정한다.</p>
<pre><code>Router.map(function(){
  this.route(&#39;clothes&#39;,function(){
    this.route(&#39;t-shirt&#39;);
  });
  this.route(&#39;item&#39;,{path:&quot;/item/:item_id&quot;});
  this.route(&#39;not-found&#39;,{path:&#39;/*path&#39;});
});</code></pre><p>이렇게 저장할 경우 허용되지 않은 URL로 들어간다 하더라도 콘솔 오류는 사라지고 <code>application.hbs</code>에서 작성되어 있는 요소는 그대로 출력되는 것을 확인할 수 있다.</p>
<p>이후 아래엔 <code>not-found.hbs</code>에 작성한 에러임을 알리는 요소들이 출력되는 것을 볼 수 있다.</p>
<p>이에 에러 페이지에 다시 되돌아가는 버튼을 만들어 놓는다면 사용자 측면에서 이 페이지가 에러고 다시 돌아가면 되겠다라는걸 명확하게 보여줄 수 있게된다.</p>
<hr>
<p>여기까지가 ember에 있는 라우팅 시스템이고 더욱 자세하게 알아본다면 라우팅 시스템 안에 리디렉션과 전환 방지나 재시도 등 여러가지가 있는데 tutorial이 끝나면 또 하나씩 정리 해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[전역 상태관리 툴 없이Toast만들기(custom hook으로)]]></title>
            <link>https://velog.io/@lee_moi/%EC%A0%84%EC%97%AD-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%ED%88%B4-%EC%97%86%EC%9D%B4Toast%EB%A7%8C%EB%93%A4%EA%B8%B0custom-hook%EC%9C%BC%EB%A1%9C</link>
            <guid>https://velog.io/@lee_moi/%EC%A0%84%EC%97%AD-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%ED%88%B4-%EC%97%86%EC%9D%B4Toast%EB%A7%8C%EB%93%A4%EA%B8%B0custom-hook%EC%9C%BC%EB%A1%9C</guid>
            <pubDate>Wed, 17 Apr 2024 05:26:52 GMT</pubDate>
            <description><![CDATA[<h1 id="toast-만들기">Toast 만들기</h1>
<p>업무중 atomic component로 사용될 기본 컴포넌트 들을 만들다 Toast의 차례가 왔다<br>기본적으로 Toast는</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/60709469-710c-4f5e-9ade-319dd956604f/image.png" alt=""></p>
<p>이런식으로 어떠한 동작을 했거나 실패 등 여러 상황에서 alert창이 아닌 쉽게 말해 서비스 디자인 등과 맞춰 제작된 자체적인 알림창이라 봐도 무방하다.</p>
<p>React에서 이를 제작할 경우 일단 모든 컴포넌트에서 이 Toast 알림창이 화면에 띄워질 수 있도록 해당 Toast 컴포넌트에 관여 할 수 있어야 한다.</p>
<p>게시판 글이나 뭐 회원가입 버튼이나 각종 여러 군데에서 이 Toast 알림을 띄울 수 있기 때문에 첫번째로 고안한 방법은 </p>
<p>Context API와 같은 전역 상태를 활용해서 가장 최상위 루트에서 감싸주는 것이다.</p>
<p>그럴경우 최상위에서 감싸진 ToastContext에서 useToast라는 함수를 내보내게 되고 이는 Toast가 필요한 컴포넌트에서 이 Toast 알림창을 띄울 수 있는 로직을 사용할 수 있게 된다.</p>
<p><strong>하지만 문제점이 하나 생기는데..</strong></p>
<p>다른 라이브러리들은 이 현상을 해결했다고 하지만 Compound component 디자인 패턴등을 사용하다 보니 ContextAPI를 많이 사용했고 이 Toast 또한 ContextAPI로 전역 상태를 감쌌더니 Toast가 뜰때마다 모든 컴포넌트들이 리렌더링 되는 일이 발생했다.</p>
<p>이 때문에 Recoil이나 redux, zustand와 같은 전역 상태관리 라이브러리들이 생겨났다고도 읽었는데 확실히 사용자가 버튼을 연속해서 누를 경우도 있고 그 때는 엄청난 리렌더링이 일어나 성능적으로 매우 부적절하다 생각했다.</p>
<p>이를 고치기 위해선 모든 컴포넌트에 memo를 씌운다면 가능하겠지만... <strong>합리적이지 못하다</strong></p>
<hr>
<h4 id="어떻게-해결했나">어떻게 해결했나?</h4>
<p>우선 Toast들을 사용하고 있는 UI 라이브러리들을 뒤졌다.</p>
<p>import해서 사용하고 있는 라이브러리들은 내부적으로 보기가 까다로웠지만 그 중 shadcn 이라는 라이브러리를 찾았는데 manual 부분과 직점 install하면 사용되는 코드를 볼 수가 있어 직접 코드를 분석해봤다.</p>
<p><a href="https://ui.shadcn.com/">https://ui.shadcn.com/</a></p>
<p> </p>
<p>shadcn 페이지에 들어가서 docs에 Toast를 살펴보면 알겠지만 shadcn의 Toast를 설치하면 약 3개의 파일을 다운받게 되는데</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/73c6e3ba-9d0c-4967-ab44-863da6c7c048/image.png" alt=""></p>
<p>이렇게 3개의 파일이 만들어지게 된다.</p>
<p>각각 간단히 살펴보면</p>
<ul>
<li>Toast는 띄워지는 알림창의 component이고 </li>
</ul>
<p>-  Toaster는 알림창이 띄워지기 위한 레이아웃이다</p>
<ul>
<li>use-toast는 toast를 각 컴포넌트에서 사용하기 위한 toast 라는 함수와 Toaster에서 관리하기 위한 useToast를 관리하고 있다.</li>
</ul>
<p>이 shadcn에서 사용되는 방법을 간단히 소개하면</p>
<p>1. <Toaster/>라는 toast 레이아웃을 최상위 루트에서 잡아놓는다. 이는 Toast 알림창이 하나만 뜨게 할것인지 여러개 뜨게 할 수 있을것인지는 직접 CSS와 Toast 상태값을 조절하면 된다.(일단 Shadcn은 Toast를 하나만 뜨도록 만들어놨다)</p>
<p>2. <Toaster /> 내부에서 useToast를 사용하 toasts 즉, 뜨게할 알림창들을 배열로 가져온 다음 map을 돌려 <Toast/>로 화면에 표시한다.</p>
<p>3. use-toast에는 export를 통해 Toaster에 보낼 toasts와 각 컴포넌트에서 사용된 toast함수를 내보내는데 이 toast 함수를 통해 각 컴포넌트에서 </p>
<pre><code class="language-javascript">toast({
    title:&#39;test&#39;,
    description:&#39;test toast&#39;
    })</code></pre>
<p>이러한 함수식을 버튼의 onClieck 과 같은 곳에 실행되도록 하여 use-toast에서의 toast함수를 실행되도록 한다</p>
<p>처음 이걸 봤을 때 이게 그래서 어떻게 전역적으로 상태가 관리된다는 것인가? 궁금할 수 있다.</p>
<p><strong>보통 우리가 자주 보는 custom hook은 각 컴포넌트에서 useCustomHook과 같이 사용할 때 각 컴포넌트 별로 상태들이 만들어지고 각각은 독립적인 상태가 된다</strong> 는게 보통 많이 접했을 것이다.</p>
<p>shadcn은 독특하게 hook이 불려질 때 해당 hook파일 자체적으로 변수를 가지고 있도록 파일 즉 해당 모듈에서의 전역변수를 만든다.</p>
<p>그리고 그 변수에 hook이 불릴때마다 만들어지는 state들을 관리할 수 있는 setState들을 배열로 집어넣고 toast라는 함수가 발동할 때마다 이 setState들을 순환하여 모든 state들을 변경할 수 있게 만든다.</p>
<p>이는 해당 hook이 사용된 컴포넌트들만 리렌더링 됨으로 전역적인 리렌더링을 방지할 수 있다.</p>
<hr>
<h3 id="의문점">의문점</h3>
<p>그대로 사용하기엔 의문점이 있다.</p>
<p>toast알림창들의 layout으로 잡고 있는 Toaster에서만 리렌더링 되면 안되나? 왜 굳이 배열에 넣고 반복문을 돌리지? 라고 생각이 들 수 있다.</p>
<p>다시 생각해보면 shadcn은 공통적으로 사용할 수 있게 라이브러리로 나온 상황이라 사용자가 <Toaster/>를 루트 한군데에서만 사용한다는 보장이 없다.</p>
<p>그렇기에 <Toaster/>를 컴포넌트별로 분할시켜 사용한다면 각각의 상태를 다 업데이트 해야 하기 때문에 배열로 만들었다 생각하고 내가 만들 Toaster는 루트에서 한번만 관리할 것이기 때문에 쓸데없는 부분은 삭제시키고 새로 만들었다.</p>
<p>ToastLayout.tsx</p>
<pre><code class="language-javascript">import { memo } from &#39;react&#39;;
import { useToast } from &#39;../../hooks/useToast&#39;;
import { cn } from &#39;../../lib/utils&#39;;

const ToastLayout = () =&gt; {
  const [toastArr, closeToast] = useToast();

  return (
    &lt;div className=&#39;fixed bottom-2 right-2 z-100 flex flex-col&#39;&gt;
      {toastArr.length !== 0 &amp;&amp;
        toastArr.map((toast) =&gt; (
          &lt;Toast key={toast.id} closeToast={closeToast} {...toast} /&gt;
        ))}
    &lt;/div&gt;
  );
};</code></pre>
<p>useToaste.tsx</p>
<pre><code class="language-javascript">import { Dispatch, SetStateAction, useEffect, useState } from &#39;react&#39;;

type ToastType = {
  id: number;
  content: string;
  variant?: ColorVariants;
  soft?: boolean;
  className?: string;
};
const TOASTCLOSEDELAY = 3000;
let toastListener: Dispatch&lt;SetStateAction&lt;ToastType[]&gt;&gt;;
let toastId = 0;

type ToastFunctionType = Omit&lt;ToastType, &#39;id&#39;&gt;;

const openToast = ({
  content,
  variant,
  soft,
  className,
}: ToastFunctionType) =&gt; {
  toastId = (toastId + 1) % Number.MAX_SAFE_INTEGER;
  toastListener((prev) =&gt; [
    ...prev,
    { id: toastId, content, variant, soft, className },
  ]);
  setTimeout(() =&gt; {
    toastListener((prev) =&gt; prev.slice(1));
  }, TOASTCLOSEDELAY);
};

const useToast = () =&gt; {
  const [toastArr, setToastArr] = useState&lt;ToastType[]&gt;([]);

  const closeToast = (id: number) =&gt;
    setToastArr((prev) =&gt; prev.filter((toast) =&gt; toast.id !== id));

  useEffect(() =&gt; {
    toastListener = setToastArr;
  }, []);

  return [toastArr, closeToast] as const;
};

export { useToast, openToast };</code></pre>
<p>일단 테스트 용으로 만든 Toast이며 <ToasteLayout/>이라는 Toast들이 뜰 공간을 잡아놓고 useToast를 통해 상태를 하나 잡아놓는다</p>
<p>그리고 openToast라는 함수를 통해 각 컴포넌트에서 사용하고 openToast가 실행될 경우 useToast란 파일에서 잡아놓은 toastLitener변수가 Toaster가 가지고 있는 toastArr이란 상태를 업데이트 할 수 있기 때문에 Toaster가 리렌더링 되며 toast가 화면에 나타나게 된다.</p>
<p>이후 적절히 사용되는 Toast에도 memo를 걸어줄 경우 Toast알림창이 뜰경우 리렌더링이 굉장히 최소화 되는 부분을 확인해 볼 수 있다</p>
<p>실제로 toast알림창이 뜰때 화면 우측 하단의 ToastLayout과 새롭게 만들어지고 사라지는 toast들만 리렌더링 되는지 체크해보자</p>
<p>코드에 정답이 없다지만 이렇게 useToast안에 모듈 전역변수를 두는 게 좋은 방식인가? 는 의문이 들기도 하여 추후에 더 좋은 방법이 있다면 실행해 볼만 하다.</p>
<p>아니면 아싸리 전역 상태 관리 라이브러리에서 Toast를 관리하는 것도 훨씬 좋은 방안이라 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비전공자의 청년취업사관학교(SeSAC, 새싹)-풀스택 프로젝트 과정 수료 및 취업후기]]></title>
            <link>https://velog.io/@lee_moi/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%9D%98-%EC%B2%AD%EB%85%84%EC%B7%A8%EC%97%85%EC%82%AC%EA%B4%80%ED%95%99%EA%B5%90SeSAC-%EC%83%88%EC%8B%B9-%ED%92%80%EC%8A%A4%ED%83%9D-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B3%BC%EC%A0%95-%EC%88%98%EB%A3%8C-%EB%B0%8F-%EC%B7%A8%EC%97%85%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@lee_moi/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EC%9D%98-%EC%B2%AD%EB%85%84%EC%B7%A8%EC%97%85%EC%82%AC%EA%B4%80%ED%95%99%EA%B5%90SeSAC-%EC%83%88%EC%8B%B9-%ED%92%80%EC%8A%A4%ED%83%9D-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B3%BC%EC%A0%95-%EC%88%98%EB%A3%8C-%EB%B0%8F-%EC%B7%A8%EC%97%85%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Thu, 11 Apr 2024 06:31:48 GMT</pubDate>
            <description><![CDATA[<p>먼저 저는 이 과정을 듣기 전에 광고를 많이 하는 국비 교육을 한번 받은 상태로 수업을 들었고</p>
<p>또 현재는 취업한 상태입니다. 인턴이지만😉</p>
<p>아무튼 원래 후기를 잘 안쓰는 편이기도 한데 이번에 새롭게 모집하는 걸 보고 같은 내용이지만 또 듣고 싶은 마음에 지원하려 했다가 다른 개발 막 시작하는 분들에게 양보하고자 아무도 안볼 것 같지만 후기를 남깁니다</p>
<hr>
<p>제가 SeSAC에서 들었던 과정은
<img src="https://velog.velcdn.com/images/lee_moi/post/cf1ca68f-b5b3-4bfe-8f3f-9872cd44dd41/image.png" alt=""></p>
<h2 id="풀스택-프로젝트-실무과정---전성호">풀스택 프로젝트 실무과정 - 전성호</h2>
<p>강의로 전성호 강사님의 개인 수업 클래스입니다. </p>
<p>물론 SeSAC에서 운영하는거라 무료로 들을 수 있고 건물도 무료고 개인 노트북만 준비해간다면 6개월 동안 바싹 공부만 집중 할 수 있습니다.</p>
<p>저는 이거 통해서 SeSAC 건물 통해서 스터디랑 공부 장소 다 이용했고 국취제에서도 인정되다보니 매달 국취제 50씩 받으면서 들었습니다!</p>
<p>제가 들었던 건 금천에서 열렸던 걸 들었는데 지금은 영등포에서 하네요!</p>
<p>금천보단 영등포가 건물이 훨씬 큽니다😏(매칭 데이때 처음으로 갔는데 시설이 훨씬 좋다고 느낀..)</p>
<p>사족은 그만하고 후기를 남기자면...</p>
<hr>
<h3 id="강의-시작전의-나">강의 시작전의 나</h3>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/e0db4544-9e96-43f3-a712-187c94adc485/image.png" alt=""></p>
<p>코드를 표현하자면 위 움짤이 내가 짠 코드들이지 않을까 싶다.</p>
<p>국비를 듣기도 했고 개인적으로 프로그래머스 문제도 0~1단계까지 다 풀었고 2단계도 난이도 쉬운것부터 풀어내고 있었다</p>
<p>그 와중에 개인 프로젝트도 하나 해서 실제로 배포까지 완성했다</p>
<p>다이어트 공유 SNS였는데 공공 API를 활용해서 내가 먹은 음식을 검색해서 클릭하면 내가 올린 글의 사진에 식단에 대한 칼로리와 탄단지가 공유되는 SNS였다.</p>
<p>물론 쉽지 않았고 많은 시간을 투자했다</p>
<p>이후 완성된 프로젝트를 가지고 여러 회사에 지원했지만 그 어느 한군데도 통과하지 못했다. (약 6~70개 정도 지원한 듯..)</p>
<p>코드는 떡이었고 잘못 된 부분을 알아차렸을 땐 이미 스파게티처럼 얽힌 코드는 어디서부터 풀어내야 할지 모를 정도인 코드는 막상 페이지 보고 오~ 했다 해도 내가봐도 엉망인데 잘하시는 분들이 볼땐 얼마나 더 잘 보일까 했다</p>
<p>그렇게 몇십번을 떨어지고 나니까 자존감은 낮아졌고 개발자는 그만두고 가서 부모님 가게나 물려받을까 생각했다
<img src="https://velog.velcdn.com/images/lee_moi/post/e8ed033e-d579-470d-85c5-ea20fd42d028/image.png" alt=""></p>
<p>고민중에 지인이 알려준 SeSAC이 생각나서 들어갔다</p>
<p>국비에선 정말 얻은것이 없다 할 정도였고 기업에서 하는 건 더이상 믿지 않게 된 상황에서 딱 10명만 모집하는데다가 카카오 개발팀장이신 분의 강의라니까 뭔가 지원해 보고 싶었었다</p>
<hr>
<h3 id="선발-과정">선발 과정</h3>
<p>근데 선발 절차가 온라인 시험 - 오프라인 시험(손코딩) - 면접 - 선발 이었는데 처음 봤을 땐 </p>
<p>와... 면접본 회사도 지원서 - 면접 - 합격통지 인데 엄청 빡센데?🤨 하는 생각으로 지원을 망설였다</p>
<p>고민고민 하다 진짜 마지막에 와서 지원하고 온라인 시험을 봤는데 생각보다 어려운 부분도 있었고 개념에 대한걸 물어보는 게 강했다.</p>
<p>그리고 그때 뭔가 확신이 들어가고 있었고 이는 오프라인 시험하고 면접에서 진짜 이 수업을 듣고싶다는 확신이 생겼다</p>
<p>이유는..</p>
<p><strong>국비 교육의 대부분은 당장 취업률에만 급급하기 때문에 언어는 대충 사용할 수 있는 것들만 가르치고 넘어간 다음 프레임워크 사용법만 후다닥 가르쳐주고 팀 프로젝트 하나 만들게 던져주고 취업연계로 아무 회사 던져주고 끝나는 게 대부분이었는데...</strong></p>
<p><strong>오프라인 손코딩이 생소했지만 내가 이 프로그래밍 흐름과 문법들을 알고 쓰는건지 확인하는 시간이었고 특히 면접에서 하시는 기술적인 질문들은 그 어떤 국비나 강의를 들어도 한번도 들어보지 못한 개념들을 물어보셨다.</strong></p>
<p>물론 면접 질문의 대답은 엉망이었고 그냥 열심히 했고 간절히 봤다.</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/a7514b47-0009-4100-b780-3d5aec2f3dcb/image.png" alt=""></p>
<p>원래 술을 안먹는데 진짜 엄청 오랜만에 술을 마시고 싶다는 생각이 들었다</p>
<p>끝나고 나오는 길에 부모님한테 전화를 드렸을 때 열심히 하긴 했는데 너무 어려웠다.. 떨어진 것 같다 라고 말씀드렸었다</p>
<p><strong>내가 국비 교육도 한번 다 듣고 거진 1년에 가깝게(물론 프로그래밍이 아닌 다른 일을 하긴 했지만) 준비하면서 강의도 몇개씩 듣고 했는데 정작 기초 개념에 대한 질문을 하나도 제대로 된 대답을 못했다는게.. 현타가 많이 왔다😭</strong></p>
<p>javascript의 클로져 개념 부터 기본적인 자료구조인 큐 스택, 해시 등 그냥 면접 질문에 대한 답으로만 외워놓은 개념정리는 아무 도움이 안됐다.</p>
<p>해당 개념을 이해를 하고 대답하는지 외운 개념을 말하는지는 정말 내가 봐도 보일 정도긴 했다</p>
<p>그런데..</p>
<p><strong>합격함😏</strong></p>
<hr>
<h3 id="교육과정">교육과정</h3>
<p>간절함이 통했는지 다행히 10명 중 1명으로 합격해서 교육을 들었는데.. 결론부터 말하면 굉장히 어려웠고 또 좋았다</p>
<p>OT를 잠깐하고 첫시간에 컴퓨터 아키텍처부터 들어갔는데 어느 강의를 들어도 수업 시작에 컴퓨터 아키텍처를 하는 곳은 없었던 것 같다.</p>
<p>생각해보니 내가 컴퓨터로 프로그래밍을 하겠다고 다짐하고 온 사람인데 적어도 컴퓨터가 뭔지는 알아야 한다는 걸 전혀 생각하지 않고 있었다.</p>
<p>요리도 그냥 재료만 때려넣어도 먹을만한데 재료의 성질과 요리 방법의 세분화한다면 맛이 극대화 되는 것처럼 단순히 내가 누가 만든 레시피를 그냥 가져다가 쓰는 사람이 아닌 내가 레시피를 개발할 수 있는 사람을 목표로 교육하신다는 게 바로 느껴졌다.</p>
<p>이후 실제로 javascript 부터 시작해 typescript react next nest 로 쭉 갔는데 후반 부의 프레임 워크는 시간이 부족해 제대로 못다뤘지만 이를 사용하는 언어인 javascript와 typescript만큼은 진짜 누구 못지 않게 깊게 배운 느낌이다.</p>
<p>내가 쓰는 문법들이 어떻게 내부적으로 동작하는데 그래서 이 변수와 함수들은 어떻게 되고 이 고차 함수들은 뭘 어떻게 사용해서 이러한 return을 주는지, 실행 컨텍스트 와 호이스팅, 클로저... 제네레이터.. 진짜 이전에 공부하는 동안 한번도 들어보지 못한 개념들을 이 교육 과정에서 다 배우면서 지나갔다</p>
<p>물론 그 과정 자체가 매우 어려웠고 같이 공부하는 분들도 대부분..</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/8c2fd64c-086e-44b0-8449-c48c4883b0f5/image.png" alt=""></p>
<p>이렇게 지나간 것 같다 😂</p>
<p>만약 이 교육 과정을 듣게 되신다면 질문을 한다는 것에 대해 좋아하시고 또한 질문을 함으로써 수업 이상의 것을 얻을 수 있으니 꼭 민폐된다 하지 말고 무조건 모르겠으면 질문하자.</p>
<p><strong>아마 당신이 모른다면 이 교육과정에서 모르는 사람은 10에 8~9일 것이 분명하다</strong></p>
<p>아무튼 매일 매일 책을 가지고 스터디 하고 개념에대해 다시 공부하면서 지금은 어느 단계에 들어왔냐면..</p>
<p>react로 만든 어떤 라이브러리를 보고 어떻게 만들었는지 분석하면서 내가 다시 똑같이 필요한 부분만 빼내고 불필요한 부분은 덜어내어 다시 만드는 정도까지 올라올 수 있었다.</p>
<hr>
<h3 id="수료-후기">수료 후기</h3>
<p>현재는 취업 했고 면접 질문등을 기억했을 때 내가 배운대로만 대답하면 굉장히 연계질문이 많이 들어오는데 충분히 알고있는 선에서 대답할 수 있었다.</p>
<p>질문이 꼬리처럼 달리며 엄청 어렵게 온다해도 괜찮다. 오히려 &#39;오 여기까지 알아? 확실히 아는데? 좀 더 아나?&#39;라는 흥미때문에 질문하는 것이기 때문에 겁먹지 말자. 배운대로만 하면 취업까지 충분히 가능하다.</p>
<p>단순히 사용법만 배운게 아니라 나는 언어 자체를 확실하게 배웠다는 자신감이 있었고 프로그래밍의 흐름을 이해하고 배우고 있기때문에 어떤 언어라도 새롭게 배우는 것에 두려움이 없어졌다.</p>
<p>생각의 폭이 정말 전 후로 차이가 날 정도로 많이 달라졌으며 물론 전적으로 강의에만 몰두하는 것이 아닌 스스로도 많은 공부와 고민등을 한다면 정말 이 교육 과정 안에서 많은 점프를 할 수 있을 것이라 생각한다.</p>
<p>나와 같은 혹 우리같은 비전공자는 출발선이 다르다. 그렇기에 멀리가기 위해선 준비가 필요하다.</p>
<p>단순히 1년 2년 하고 더이상 쫓아가지 못해 그 때 언어에 대해 더 공부할 걸 후회하지 않기 위해... 혹시나 시작하려고 한다거나 공부를 했는데 스스로 많이 부족하다고 느낀다면 꼭 이 강의를 들었으면 좋겠다.</p>
<p>심지어 나는 취업을 했지만 강의를 또 여신다는 소식을 듣고 한번 더 듣고 싶다 얘기를 드렸는데 강의 영상 올리는 거로 들으라고 빠꾸 먹었따😂😂😂</p>
<p>새로운 시작을 하시는 분들을 위해 한자리 남겨드릴 테니 고민 한다면 지원은 공짜니 꼭 지원하시길</p>
<hr>
<p><a href="https://url.kr/wcxql7">https://url.kr/wcxql7</a> - 과정을 살펴볼 수 있는 페이지입니다!</p>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/9560e450-2d8b-4ae9-9db2-b3fba3a01b96/image.png" alt=""></p>
<h4 id="할수-있습니다">할수 있습니다</h4>
]]></description>
        </item>
        <item>
            <title><![CDATA[모노레포(monorepo)]]></title>
            <link>https://velog.io/@lee_moi/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%ACmonorepo</link>
            <guid>https://velog.io/@lee_moi/%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%ACmonorepo</guid>
            <pubDate>Thu, 18 Jan 2024 12:39:59 GMT</pubDate>
            <description><![CDATA[<h1 id="모노레포---monorepo">모노레포 - Monorepo</h1>
<p>일단 모노레포가 무엇인지 정의를 두자면 위키에선
** 모노레포란 버전 관리 시스템에서 두 개 이상의 프로젝트 코드가 동일한 저장소에 저장되는 소프트웨어 개발 전략** 
이라고 기재되어 있다</p>
<p>이전 개발 전략은 모놀리식 애플리케이션으로
<strong>모놀리식 애플리케이션은 모듈화 없이 설걔된 소프트웨어 애플리케이션</strong> 이라 되어있다</p>
<p>즉, 어떤 하나의 서비스를 개발할 때 모듈화 없이 개발된다면 코드가 서로 직접적으로 의존되며 하나의 버전으로 관리되면서 분리가 어려워지고 새로운 설계 리팩터링 등의 작업들을 진행할 때마다 작업 단위가 거대해진다.</p>
<p>리액트를 배우고 프로그래밍을 배울 수록 딱 봐도 비효율적인 부분을 찾아볼 수 있는데 그렇기에 우리는 모듈화와 재사용성을 신경쓴다</p>
<h3 id="모듈과-재사용성">모듈과 재사용성</h3>
<p>모듈(module)이란 <strong>다른 프로그램에 링크되어 특정한 기능을 수행할 수 있도록 작성된 독립적인 프로그램의 단위</strong>로 우리가 프로젝트를 진행하며 모듈식 프로그래밍을 할 경우 반복되는 로직을 모듈화하여 재사용할 수 있고 애플리케이션의 일부를 교체해야 할 때도 해당 모듈인 일부만 수정하거나 교체하면 되니 유지 관리가 용이해진다</p>
<p>그럼 이런 모듈들을 다른 애플리케이션에 사용되게 하고 싶다면 소스를 어디에 위치 시킬까?
해당 모듈을 독자적인 저장소에 관리하고 싶다면 해당 구조가 멀티레포가 된다</p>
<h3 id="멀티레포">멀티레포</h3>
<p>폴리레포 구조라고도 불리며 앞서 분리된 각 모듈을 먼티레포 구조에서 고유한 저장소가 있는 독자적인 프로젝트가 되는 것이다.
이러면 각 프로젝트는 자율성이 높으며 독립적인 개발이 가능하게 된다
마치 todoApp 하나 calendarapp하나 todoApi 하나 등 여러 저장소를 가지고 되게 각자 팀의 자율성으로 개발이 가능해진다.</p>
<p>그런데 왜 모노레포란게 생겼을까?</p>
<h3 id="멀티레포의-문제">멀티레포의 문제</h3>
<ul>
<li>계속되는 프로젝트 생성
새로운 공유 패키지를 생성할 때마다 npm에 들어간 패키지를 각각 다시 다운받고 환경 구축하고 CI/CD구축하는 등 번거로운 작업이 반복된다</li>
<li>패키지의 중복 코드 가능성
프로젝트에서 공통 구성요소를 자체적으로 작성한다면 초기에 시간을 아낄 수 잇겠지만 시간이 지날수록 보안 및 품질 관리 부담이 증가된다</li>
<li>관리 포인트 증가
프로젝트가 늘어나면서 당연시 관리해야할 포인트도 늘어난다.</li>
<li>일관성이 사라지는 개발자 경험
각 프로젝트는 고유한 명령 집합을 사용할 수 있고 이런 불일치는 프로젝트가 늘어날수록 명령어를 기억해야하는 불편함이 생긴다</li>
<li>다른 패키지의 변경사항 파악
사용되는 관련 패키지의 변화를 지켜보거나 통지받지 못하면 예상치 못한 문제가 발생할 수 있다</li>
<li>교차 저장소의 리팩터링 비용
관련 패키지 변화가 있을 때 여러 저장소에 걸쳐 변화를 반영해야 하는 게 쉬운 것이 아님!</li>
</ul>
<p>그럼 모듈도 적절히 분리하고 동시에 분리된 모듈을 쉽게 참조하고 테스트 빌드 배포 등도 한번에 되는 게 없을까? 했을 때 등장된게 모노레포이다</p>
<h2 id="모노레포의-특징">모노레포의 특징</h2>
<p>모노레포 구조는 2개 이상의 프로젝트가 동일한 저장소에 저장되는 소프트웨어 개발 전략으로 앞에서 말한 TODOAPP이나 CALENDER등이 독자 프로젝트로 존재하지만 저장소는 같은 그런 개념이다</p>
<p>중요한 특징 중 하나는 프로젝트 간의 관계로 단순히 여러 프로젝트가 하나의 저장소를 사용한다 해서 모노레포 구조가 아니라 프로젝트 사이에 의존성이 존재하거나 같은 제품군이거나 하는 정의된 관계가 존재한다.
그런 관계를 효율적으로 관리해주는 도구들이 있다</p>
<h4 id="오해">오해</h4>
<ul>
<li>다른 팀이 내가 모르는 사이에 내 코드를 변경할 수 있지 않나?
GitHub에는 CODEOWNERS와 같은 기능을 사용해서 폴더 기반으로 소유권을 구성할 수 있다
따라서 해당 저장소에 대한 모든 PR을 소유자에게 리뷰 받아야 머지할 수 있는 것이다</li>
<li>모노레포가 멀티레포보다 항상 나은 방법인가?
멀티레포의 단점이 모노레포의 장점이고 장단점이 각각 교차하기 때문에 적절한 상황에서 사용해야 한다
모노레포의 핵심은 프로젝트 사이의 관계로<ul>
<li>유사한 제품의 집합</li>
<li>여러 프로젝트의 변화를 한 눈에 파악해야 할 때</li>
<li>호스트 애플리케이션을 플러그인 등으로 확장할 때</li>
<li>공통 기능을 재사용하는 관련된 프로젝트의 집합</li>
<li>유사한 DevOps로 구성된 프로젝트의 집합</li>
</ul>
</li>
</ul>
<h3 id="모노레포-구축할-때-고려할-것">모노레포 구축할 때 고려할 것</h3>
<h4 id="관리">관리</h4>
<ul>
<li>코드 공유 
서로 다른 프로젝트 간에 쉽게 소스코드 공유</li>
<li>일관성 있는 도구 
서로 다른 프로젝트들 (다른 프레임워크를 사용하더라도)에서 일관된 개발 경험 제공</li>
<li>스케폴딩
새로운 프로젝트를 생성할 때 초기 코드를 쉽게 생성</li>
<li>프로젝트 제약 및 가시성
저장소 내에서 의존 관계를 제한하는 규칙 정의 지원<h4 id="속도">속도</h4>
</li>
<li>로컬 캐싱
같은 머신에서 같은것을 두 번 빌드,테스트 하지 않음</li>
<li>분산 캐싱
다양한 환경에서 캐시 아티팩트 공유. 즉, 조직단위로 여러 CI환경에 걸쳐 같은 것을 두 번 빌드,테스트하지 않음</li>
<li>로컬 작업 오케스트레이션
빌드 및 테스트 등의 작업을 순서에 맞게 병렬로 실행</li>
<li>분산 작업 실행
단일 시스템에서 실행되어 여러 시스템에 명령을 전달</li>
<li>변화에 영향을 받는 프로젝트 감지
변경 영향을 받을 수 있는 항목을 결정해서 영향을 받는 프로젝트만 빌드/테스트<h4 id="구조파악">구조파악</h4>
</li>
<li>워크 스페이스 분석
추가 구성 없이 주어진 워크 스페이스의 의존성 관계를 분석</li>
<li>의존성 그래프 시각화
프로젝트 및 작업 간의 종속 관계를 시각화</li>
</ul>
<hr>
<p>글 참조 : <a href="https://d2.naver.com/helloworld/0923884">https://d2.naver.com/helloworld/0923884</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - Function]]></title>
            <link>https://velog.io/@lee_moi/JS-Object</link>
            <guid>https://velog.io/@lee_moi/JS-Object</guid>
            <pubDate>Tue, 28 Nov 2023 14:08:07 GMT</pubDate>
            <description><![CDATA[<h1 id="function">Function</h1>
<p><a href="https://github.com/hoinlee-moi/fullstack5/blob/main/trythis/function/onceFunction.js">함수 연습 문제 - once함수 만들기</a>
<a href="https://github.com/hoinlee-moi/fullstack5/blob/main/trythis/function/purefn_week_name.js">함수 연습 문제 - closure를 활용한 요일 출력 함수</a>
<a href="https://github.com/hoinlee-moi/fullstack5/blob/main/trythis/function/reducer.js">함수 연습 문제 - reduce 함수 직접 만들기</a></p>
<hr>
<p>함수는 하나의 단위로 실행되는 문(statement)들의 묶음이다.<br>
함수도 객체이며 javascript는 함수형 프로그래밍 언어로 핵심적인 부분이라 할 수 있다.<br>
그렇다보니 인터프리터 언어인 javascript는 모든 함수들이 사용될지 안될지 모르는 상황에서 모두 컴파일을 진행할 수는 없으니 먼저 선언시 function object에만 등록 해놓은 상태고 나중에 호출(call, invoke)되었을 때 평가하고 실행한다<br></p>
<p>이는 javascript에서 class를 사용할 때도 볼 수 있는데 class는 function일까??<br>
class도 function으로 즉 constructor function 이라고 볼 수 있다<br>
그렇기에 <code>dog = new Dog()</code>를 실행할 경우 <code>Dog</code>라는 클래스의 constructor 함수를 호출하여 실행하는 것이다.</p>
<pre><code class="language-javascript">class Dog {
  name;
  constructor(name) {
    this.name = name;
  }
}
console.log(typeof Dog); // function</code></pre>
<p>이처럼 <code>class</code>를 만들어서 <code>typeof</code>연산자를 통해 확인해보니 function라고 뜬다.<br>
이처럼 javascript는 함수형 프로그래밍으로 객체지향 프로그래밍 처럼 class를 사용한다 하더라도 함수가 얼마나 중요한지 간접적으로 알 수 있다.</p>
<h2 id="함수의-매개변수-arguments---값-parameter---변수">함수의 매개변수 (arguments - 값, parameter - 변수)</h2>
<p>매개변수는 함수 실행 컨텍스트(Function Environment Record)에 별도 생성된다.<br>
이전 실행컨텍스트 그림을 확인하면 알 수 있듯이 함수가 function Object에는 등록되지만 평가가 됐다거나 그런 건 아니고 콜 되는 순간 실행 스택에 올라가고 Environment Record가 생겨나며 호이스팅부터 할당등의 평가 실행 들이 일어나게 된다.<br></p>
<p>이때 함수 매개변수는 해당 Record에 생성되게 되고 함수가 종료될 시 G.C가 돌며 해당 Record가 사라질때 함께 사라지게 된다
<br></p>
<p>매개변수또한 destructuring과 spread 연산자를 사용할 수 있따</p>
<pre><code class="language-javascript">function ds1({ id, name }) {
  //...some code
}
const obj = { id: 1, name: &quot;lee&quot; };

ds1(obj);</code></pre>
<p>javascrpit에서는 오버로딩과 오버라이딩이란 개념이 없는데 fucntion의 자유로운 환경 때문이라고 한다.<br>
기본적으로 javascrpit function은 실제로 작성될 때 필요한 매개변수는 1개라고 하더라도 0개를 보낼수도 있고 5개를 추가로 보낼 수도 있다.<br>
이처럼 자유로운 언어 특성때문인 것 같다.<br></p>
<p>아무튼 javascript는 함수 선언할 때 같은 이름으로 선언할 경우 덮어 쓰게 되는 점이 있으므로 주의하자</p>
<h2 id="함수-생성">함수 생성</h2>
<p>함수의 생성 방식은 크게 약 3가지로 볼 수 있는데<br></p>
<ul>
<li>함수 선언식</li>
<li>함수 표현식</li>
<li>Function 생성자 함수</li>
</ul>
<p>이다.
함수 선언문은 <code>function f() {...}</code>로 javascript를 공부했다면 쉽게 알 수 있는 함수다 <code>function () {...}</code>로 작성할 시 익명함수로 사용할 수 있다.<br></p>
<p>함수 표현식은 <code>const f1 = function ff() {}</code>처럼 사용할 수 있는데
여기서 이상한 점을 찾는다면 <code>f1</code>과 <code>ff</code>가 2개가 있는데 함수를 실행하려면 <code>f1</code>을 불러야 할까, <code>ff</code>를 불러야 할까?<br></p>
<pre><code class="language-javascript">const f1 = function ff() {
  //...some code
};
f1(); // OK!
ff(); // ff is not defiend!
console.log(f1); // [Function:ff]</code></pre>
<p>이처럼 실행할 땡 <code>f1</code>을 사용하지만 이 함수는 <code>f1</code>함수가 아닌 <code>ff</code>란 함수이름을 가지고 있다.<br>
식별자 우선순위때문인데 만약 만드는 게 재귀함수라 <code>ff</code>함수안에 다시 스스로를 호출해야 한다면 이때는 <code>ff</code>로 실행 가능하다</p>
<h4 id="즉시호출-함수">즉시호출 함수</h4>
<p>외로 즉시 호출 함수라는 게 있는데 주로 익명함수를 사용하고 언어 그대로 함수를 바로 실행하는 특징을 가지고 있다.<br></p>
<pre><code class="language-javascript">(function () {
  //...some code
})();</code></pre>
<p>이러면 실행문이 해당 코드를 만나면 곧바로<code>()</code>로 인해 실행하게 되고 이를 통해 불필요한 전역 변수와 메모리 낭비를 줄일 수 있으며 Closure나 부분적인 await가 활용 가능해진다
(최근 ECMA에선 최상위 코드에서 <code>await</code>가 가능해지기도 했다)</p>
<h2 id="화살표-함수">화살표 함수</h2>
<ul>
<li>object method와 같은 <strong>non-constructor!</strong> 이다<br></li>
<li>arguments 객체(배열)가 없다</li>
<li>함수를 반환하는 고차 함수에 사용하기 좋다</li>
</ul>
<p>화살표 함수 사용법은 <code>const f3 = ()=&gt; {...}</code> 이렇게 사용한다.<br>
다만 화살표 함수의 특징중 하나로 함수가 바로 return을 사용할 경우 굳이 <code>{}</code>로 감싸고 <code>return</code>을 적을필요 없이 <code>const f3 = () =&gt; ...</code>식으로 바로 반환해도 된다. <code>=&gt;</code>가 <code>{return}</code>을 품고 있다라고 보면 좋을 것 같다.<br>
물론 statement가 2개 이상으로 늘어날 경우 <code>{}</code>을 꼭 적고 그 속에서 반환할 게 있다면 <code>return</code>을 적어주자<br>
또한 매개변수도 1개라면 매개변수를 감싸고 있는 괄호도 생략할 수 있다<br>
<code>const f3 = arg =&gt; ...</code><br></p>
<p>또 하나의 특징으로 내부/콜백 함수 시 <code>this</code>가 외부(상위/전역)객체를 가르킨다는 것이다.<br>
즉 나의 부모의 <code>this</code>를 본다라고 생각하면 좋다.<br>
또한 원래 선언문 같은 경우 같은 이름으로 <code>function</code>을 이용해 선언할 경우 덮어쓰는 방식이 사용됐지만 <code>const</code>를 사용하다보니 같은 이름으로 화살표 함수를 사용할 경우 에러가 발생한다!!</p>
<h2 id="함수의-호출-방식과-this">함수의 호출 방식과 this</h2>
<p>호출 방식은 총 3가지 정도로 일반함수 | 메소드 | 생성자 함수가 있다<br>
각각 호출 할 때의 this가 정해지는 데 기본적으로 일반함수의 this는 global을 가르키게 되고 메소드 | 생성자 함수 는 생성된 instance를 가르키게 된다</p>
<pre><code class="language-javascript">const obj = {
  name: &quot;ObjName&quot;,
  bark() {
    // good!(호출한 객체)
    console.log(&quot;bark=&quot;, this.name);
  },
  bark2: () =&gt;
    // bad!! (this = 전역)
    console.log(&quot;bark2=&quot;, this.name),
};

obj.bark();
obj.bark2();</code></pre>
<h3 id="call--apply">call , apply</h3>
<p>호출 방식으로는 <code>call</code>, <code>apply</code>, <code>bind</code> 가 있다.<br>
먼저 우리가 기본적으로 선언된 함수를 사용하는 방법은 대부분 <code>()</code>를 사용하기에 <code>apply</code>와 <code>call</code>은 처음 들어본 경우가 많다.<br>
나도 처음 들었을 때 똑같은 거 아닌가 싶었지만 직접 사용법을 보면 다르다는 것을 확인할 수 있다<br></p>
<pre><code class="language-javascript">const example = function (a, b, c) {
  return a + b + c;
};

example(1, 2, 3);
example.call(null, 1, 2, 3);
example.apply(null, [1, 2, 3]);</code></pre>
<p><code>call</code>과 <code>apply</code>둘 다 arguments 로 기본 <code>()</code>사용법과 똑같이 전달한다.<br>
다른 점이 있다면 맨 처음의 <code>null</code>이 있는데 이는 함수의 <code>this</code>를 대체하는 것이다.</p>
<p>만약 <code>call</code>과 <code>apply</code>를 사용할 때 <code>null</code> 자리에 <code>this</code>로 사용하고 싶은 객체를 입력할 경우 그 함수가 실행될 때 자리는 입려한 객체가 <code>this</code>로 된다.<br>
<strong>즉, call이나 apply를 사용해서 this를 정의해주면 다른 객체의 파라미터나 메소드를 가져와 사용할 수 있다</strong></p>
<h3 id="특징">특징</h3>
<p>주로 함수의 <code>arguments</code>에 사용된다고 하는데 화살표 함수에는 <code>arguments</code>말고 매개변수 위치에서 <code>...</code>을 사용해 받지만 이전 기존 함수 선언식은 arguments라고 해서 함수에 들어온 인자를 배열 형식으로 받을 수 있는 게 있다<br>
다만 배열 형식이지 정식 배열은 아니다.<br>
유사 배열로 배열의 형식을 뛰고 index로 값도 가져올 수 있지만 배열이 가진 메서드를 가질수는 없다.<br>
이럴 때 <code>call</code>과 <code>apply</code>를 사용해서</p>
<pre><code class="language-javascript">function example3() {
  console.log(Array.prototype.join.call(arguments));
}
example3(1, &quot;string&quot;, true); // 1,string,true</code></pre>
<p>이런식으로 다른 곳의 메서드들을 끌고와서 <code>this</code>를 할당해 줌으로써 빌려사용할 수 있다.<br></p>
<h3 id="다른-bind">다른 bind</h3>
<p><code>bind</code>는 call과 apply와는 다르게 함수가 가리키는 <code>this</code>만 바꾸고 호출하지는 않는다.<br>
또 그 함수를 가져와서 사용하면서 <code>this</code>만 바꾸는게 아닌 <code>this</code>를 정의하고 그 함수를 복사해 새로운 함수를 만들어 리턴해준다.</p>
<pre><code class="language-javascript">function f() {
  console.log(&quot;new function&quot;);
}
console.log(f === f.bind(this)); // false</code></pre>
<p>둘이 다른 것을 확인할 수 있듯이 같은 함수가 아닌 새로운 함수를 반환하는 것이 <code>bind</code>이다.<br></p>
<hr>
<p>최종적으로 <code>call</code>,<code>apply</code>, <code>bind</code>는 참조하는 함수 조작이다.<br>
<code>this</code>를 바꿔서 마치 해당 함수가 어느 객체 안에 있게 할 수 있는것이다.</p>
<p><code>call</code>과 <code>apply</code>는 바로 적용하면 되지만 <code>bind</code>는 반환이기 때문에 어떤 변수에 저장하고 <code>()</code>로 실행하거나 <code>.bind()()</code>로 바로 실행하면 된다.</p>
<p><code>call</code>과 달리 <code>apply</code>는 배열로 받는 <code>call</code>과 같다.</p>
<h2 id="순수함수">순수함수</h2>
<p>함수에 대해서 가장 중요한 개념이 있는데 바로 순수 함수 개념이다.
순수함수를 설명하자면</p>
<ul>
<li>함수로서의 함수</li>
<li>수학적 함수</li>
<li><strong>입력이 같으면 결과도 같다</strong></li>
<li><strong>부수 효과(side effect)가 없다</strong></li>
</ul>
<p>함수란 어떤 하나의 동작을 하는 어떠한 특별한 목적의 작업을 수행하기 위해 독립적으로 설계된 코드의 집합이다<br>
그렇기에 최대한 side effect가 적고 동일한 입력 동일한 결과를 나오게 해야 매우 효율적이다 라고 보고 실제로 그렇게 생각한다.</p>
<h2 id="콜백-함수-와-고차함수">콜백 함수 와 고차함수</h2>
<p>콜백은 간단한 함수(너무 복잡하지 않은)로 다른 함수의 value 즉 arguments로 전달되는 함수이고 실행되는 것은 오직 이벤트가 발생되었을 때 하는 함수를 콜백함수라고 한다.<br></p>
<p>여기서 이벤트란 뭐 클릭이나 서버에서 데이터가 도착했을 때, 로드 될 때 등 들이 이벤트라고 볼 수 있다.<br></p>
<p>뚜렷하게 고차함수와는 다른 것을 볼 수 있는데...고차 함수는</p>
<ul>
<li>인수(매개 변수)로서의 함수</li>
<li>반환 값으로서의 함수(closure)</li>
<li>식별자로서의 함수 (1급 객체)</li>
<li>배열 안 요소 함수(배열 원소)</li>
</ul>
<p>즉 ,1급 객체로서의 함수이다.<br>
여기서 1급 객체란 다른 객체들에 일반적으로 적용 간으한 연산을 모두 지원하는 객체를 가리킨다<br>
특징으로는</p>
<ul>
<li>변수에 할당할 수 있다</li>
<li>다른 함수를 인자로 전달받는다</li>
<li>다른 함수의 결과로서 리턴될 수 있다
인데 고차함수의 개념과 매우 동일하기에 1급 객체로서의 함수라고 볼 수 있는 것이다.</li>
</ul>
<pre><code class="language-javascript">const f1 = (f, val) =&gt; f(val);
f1(console.log, &quot;f1&quot;);</code></pre>
<p>위에서 f1에 console.log라는 함수가 전달되긴 함으로 콜백 함수의 첫번째 조건을 만족하지만 2번째인 이벤트가 발생되었을 때 실행되는 함수가 아니므로 콜백함수가 아니다.<br>
따라서 전달되는 console.log를 고차함수라고 부른다.<br></p>
<h2 id="unary-함수">unary 함수</h2>
<p>고차함수에서 인수(매개 변수)의 개수를 1개로 제한하여 실행하는 함수로 인자를 단 하나만 받는 함수를 말한다!</p>
<pre><code class="language-javascript">const arr = [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;];

const rets = arr.map(parseInt);
console.log(rets); // [ 1, NaN, NaN ]

const unary = (fn) =&gt; (fn.length === 1 ? fn : (arg) =&gt; fn(arg));

const rets2 = arr.map(unary(parseInt));
console.log(rets2); // [ 1, 2, 3 ]</code></pre>
<p>를 보면 실제로 parseInt를 할경우 parseInt에 map의 특성상 3개의 인자를 주다보니 parseInt가 실행되는 조건이 안맞아서 <code>NaN</code>이 뜨게 된다.<br></p>
<p>그래서 아래의 <code>unary</code>함수를 이용해서 <code>fn.length</code>는 전달받는 매개변수의 개수를 알려주기에 1개일때는 그대로 실행하고 그게 아니라면 매개변수를 하나만 받는 함수를 만들어 parseInt를 실행하도록 한다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - Destructuring]]></title>
            <link>https://velog.io/@lee_moi/JS-Destructuring</link>
            <guid>https://velog.io/@lee_moi/JS-Destructuring</guid>
            <pubDate>Sat, 25 Nov 2023 09:30:13 GMT</pubDate>
            <description><![CDATA[<h2 id="구조-분해-할당확장spread-연산자-나머지-연산자">구조 분해 할당,확장(spread) 연산자, 나머지 연산자</h2>
<p>구조 분해 할당 구문은 배열이나 객체의 속성을 해체해서 그 값을 개별 변수에 담을 수 있게 하는 표현이다.</p>
<p>구조분해 할당에 대해선 설명보다 사용법을 보는게 훨씬 빠르게 익혀지는 것 같다<br>
몇가지 예시를 살펴보자</p>
<pre><code class="language-javascript">let x, y;  x = y = 9;  // x = 9, y = 9
const z = (y++, x + y);  // z = 19
x += y; x %= y; x &amp;= y; x ^= y; // x = x + y; x = x &amp; y;
const u = {id: 1, name: &#39;hong&#39;, age: 29};   // object
let {id, name, addr} = u; // id = 1, name = &#39;hong&#39;, addr = undefined, age = ?
let {id, ...info} = u; // id = 1, info = { name: &#39;hong&#39;, age: 29 }
let id, name;
{id, name} = u;  // Error! ⇒ ({id, name} = u);
const arr = [1, 2, 3, 4, 5];
let [a, b, ...c] = arr; // a=1,b=2,c=[3,4,5]
let [x,,y,,z] = arr; // x=1 , y=3 , z=5
[a, b] = [b, a];       // a=2 , b=1</code></pre>
<p>위에서 부터 살펴보면 <code>=</code>이라는 할당연산자가 연속해서 붙을 경우 변수에 할당되는 값들은 가장 마지막에 있는 값으로 할당된다.<br>
중요한 부분은 객체를 구조분해 할당하는 <code>u</code>부분이다<br></p>
<p>객체를 구조분해 할당할 때 주의할 점은 할당받을 변수를 분해할 객체의 키값과 동일하게 작성해야 한다는 것이다<br>
할당받을 변수의 이름을 통해 분해할 객체의 키를 검색하여 그대로 할당하여 줌으로 변수 이름을 키와 맞지 않게 입력할 경우 <code>let a;</code>와 같이 undefined로 할당된다.<br>
<strong>이때는 const로 구조분해 할당하더라도 원래 <code>const a;</code>로 입력할 경우 생기는 에러가 이때는 생기지 않으니 꼭 주의하자!</strong><br></p>
<p>배열의 경우 키값이 없지만 Javascript에서의 배열은 객체와도 같다.<br>
객체로 변환되면 키는 index를 통해 나오는데 이와 같이 각각의 index위치에 맞게 구조분해 할당되어 각각 변수에 들어가게 된다.<br>
이때 만약 하나의 인덱스를 뛰어 넘고 싶다면 해당 index 위치는 <code>,,</code>를 입력하여 뛰어 넘으면 된다<br></p>
<p>그리고 같이 알아야할 나머지 연산자로 <code>...</code>가 존재하는데 구조분해 할당을 할때 입력되는 <code>...</code>는 스프레드 연산자가 아니라는 걸 기억하자<br></p>
<h3 id="전개확장-연산자-spread-operator">전개(확장) 연산자 (spread operator)</h3>
<p>여러개의 변수가 들어갈 자리에 배열의 원소들을 분포시킬 때 사용한다.
사용 주의 사항으론 iterable한지가 중요하니 만약 iterable하지 않은지 꼭 확인하자<br>
현재는 객체에서도 사용 가능하다</p>
<pre><code class="language-javascript">const arr = [1, 2, 3, 4, 5];
const arr2 = [...arr];
const arr3 = arr;
console.log(arr === arr3); // true
console.log(arr === arr2); // false</code></pre>
<p>여기서 눈여겨 봐야할 것은 <code>arr3=arr</code>처럼 메모리 주소를 그대로 할당하는 것이 아닌 복사하여 새로운 메모리 주소에 할당된다는 것이다.<br>
이로인해 <code>arr</code>의 내용물을 바꾼다 하여도 <code>arr2</code>에는 전혀 영향이 없다.<br></p>
<p>하지만 주의할 점으론 만약 객체 배열일 경우 배열의 값들은 객체겠지만 실제론 각 객체들의 메모리 주소를 배열로 가지고 있는 상태로 spread 연산자를 사용해서 복사한다 하더라도 메모리 주소가 그대로 복사됨으로 각 객체들까지 독립적으로 복사되진 않는다<br>
이로인해 얕은 복사라고 불리기도 한다<br></p>
<h3 id="나머지-연산자-rest-operator">나머지 연산자 (rest operator)</h3>
<p>비 구조화 할당을 사용하되, 배열의 일부 부분을 다른 변수에 배열로 할당하고자 할 때 나머지 연산자를 사용할 수 있다.<br></p>
<pre><code class="language-javascript">const arr = [1, 2, 3, 4, 5];
let [a, b, ...c] = arr; // a=1,b=2, c=[3,4,5]

const u = { id: 1, name: &quot;lee&quot;, id: 27 };
let { id, ...u2 } = u; // id =1 , u2 = {name:&#39;lee&#39;,id:27}</code></pre>
<p>위에서 본 예시와 똑같은데 할당되고 남은 나머지 값들을 객체 또는 배열로 묶어서 반환해준다.<br>
화살표 함수를 사용할 때 들어오는 매개변수의 값도 나머지 연산자를 자주 사용할 수 있으니 꼭 참고하자</p>
<h3 id="변수명-변경과-기본값-할당">변수명 변경과 기본값 할당</h3>
<p>만약 키 값과 동일하게 변수를 사용하고 싶지 않고 다른 변수명을 사용하고 싶을때는 어떻게 할까?</p>
<pre><code class="language-javascript">const u = { id: 1, name: &quot;lee&quot;, id: 27 };
let { id, name: nickName, id } = u;</code></pre>
<p>이렇듯 간단하게 <code>:</code> 를 사용하고 뒤에 원하는 변수 명을 쓸 경우 <code>name</code>을 통해 키값을 찾아 구조분해 한 뒤 <code>:</code>뒤에 입력된 <code>nickName</code>이란 이름의 변수로 값을 할당한다.</p>
<pre><code class="language-javascript">const u = { id: 1, name: &quot;lee&quot;, id: 27 };
let { id, addr = &quot;seoul&quot; } = u;</code></pre>
<p>위는 <code>addr</code>이라는 <code>u</code>라는 객체에 존재하지 않는 키값을 찾고 있다. <br>
이 경우 원래대로 라면 <code>undefined</code>가 할당되어야 하지만 <code>=</code>할당 연산자를 통해 &#39;seoul&#39;이라는 기본 값을 할당함으로 <code>undefined</code>가 아닌 <code>&#39;seoul&#39;</code>값이 할당되게 된다.<br>
만약 객체에 존재하는 키값이라 하면 당연히 기본값 <code>&#39;seoul&#39;</code>은 무시되고 구조분해 할당받은 값이 <code>addr</code>에 들어오게 된다
<br><br></p>
<p>이처럼 구조분해 할당과 나머지 연산자를 쓸 경우 간단한 얕은 복사와 배열들을 처리할 수 있게 되고 후에 프레임워크를 사용할 때 자주 사용할 테니 여러번 반복해서 사용해봐야겠다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - Object & Property]]></title>
            <link>https://velog.io/@lee_moi/JS-Object-Property</link>
            <guid>https://velog.io/@lee_moi/JS-Object-Property</guid>
            <pubDate>Sun, 12 Nov 2023 17:08:46 GMT</pubDate>
            <description><![CDATA[<h2 id="object">Object</h2>
<p>우리가 흔히 알고 있는 객체를 Object라고 하는데 기본적으로 함수도 따지고 보면 object이고 배열도 object이고 우리가 실행하는 js의  global도 object이다.
이렇게 object는 광범위한 개념을 가지고 있는데 우리가 주로 객체라고 부르며 사용하게 될 것은 아래처럼</p>
<pre><code class="language-javascript">const user = {
  &#39;&#39;: 1,        
  &#39; &#39;: 1,       // &#39;id&#39;: 1, &#39;0y&#39;: 2 모두 OK!
  123: 1,       // user[123], user[&#39;123&#39;] OK, but user.123 is SyntaxError!!
  12345n: 2,    // user[12345], user[12345n], user[&#39;12345&#39;] OK, but user[&#39;12345n&#39;] is undefined!
  true: 1,      // OK  user[true]  user.true
  id: 2,          
  [`name`]: &#39;Hong&#39;,  // But, `name`: &#39;Hong&#39;은 SyntaxError: Unexpected template string!
  [Symbol()]: &#39;Hong&#39;,   // OK But, Symbol(): &#39;Hong&#39;은 SyntaxError: Unexpected token &#39;:&#39;
  [`${new Date()}`]: 365,    // OK! &#39;Sun Jul …&#39;: 365
  &#39;my-friends&#39;: [&#39;Han&#39;, &#39;Kim&#39;],
  getInfo: () =&gt; `${this.id}-${this.name}`,       // OK! But, this is not user!
  getInfo2() { return `${this.id}-${this.name}`; }, // OK! getInfo의 최종 &lt;f.o&gt;
}</code></pre>
<p>와 같다. <code>user</code>라는 object 즉 객체가 있고 이 안에 포함되어 있는 값들은 property라고 부른다.
이 property라는 개념이 처음 들을 땐 이해가 안될 수 있는데 그냥 object가 가지고 있는 속성들로 위의 예시에서 보자면 <code>user</code>라는 녀석은 object를 가지고있다.(식별자)
이때 user 안의 <code>&quot;&quot;:1</code>과 <code>&quot; &quot;:1</code>, <code>getInfo(){...}</code>,<code>[Symbol()]:&#39;Hong&#39;</code>들 하나하나가 user의 속성, 즉 프로퍼티라고 생각하면 된다.</p>
<p>이때 위의 예시처럼 객체의 프로퍼티로는 다양한 값들이 들어올 수 있는데 단순한 원시타입부터 객체, 배열, 함수, 추가로 심볼까지 들어올 수 있다.</p>
<h2 id="method">method</h2>
<p>일명 메서드로 객체 프로퍼티에서 사용되는 함수들을 통칭 메서드라 부르고 최근에는 조금 더 구분해서 부르고 있다
<code>user</code> 객체에서 함수가 2종류가 있는데 하나는 <code>getInfo</code>와 <code>getInfo2()</code>2가지가 있다
2종류모두 method라 할 수 있지만 정확히 더 구분하자면 <code>getInfo</code>는 함수 리터럴(값)을 가지는 key이고 <code>getInfo2()</code>가 method의 성격에 더 가깝다 생각한다.</p>
<p>보통 객체 내부의 함수를 작성할 때 <code>this</code>를 많이 사용하는데 이때 <code>getInfo2</code>와 같은 메서드는 사용시 무조건 <code>user.getInfo2()</code>와 같이 사용할 수 밖에 없고 어디 변수에 할당한다 하더라도 <code>getInfo2()</code>가 <code>return</code>하는 값이 담길 뿐이다.
그렇기에 <code>getInfo2</code>가 사용되는 <code>this</code>는 객체 자신이 되는 것인데 그러면 객체 리터럴을 가진 <code>getInfo</code>도 <code>user.getInfo()</code>로 사용하지 않나? 생각할 수 있다.</p>
<p>정확히는 <code>getInfo</code>를 그대로 사용할 때 <code>user.getInfo()</code>처럼 사용한다면 자기 자신이 바인딩 되어 <code>this</code>가 자기 자신이 되겠지만 엄연히 객체 리터럴을 가진 key로 어떤 변수에 <code>const getInfoFucn = user.getInfo</code> 이런식으로 담게 되면 <code>getInfo</code>의 값인 함수 자체가 <code>getInfoFunc</code>에 담기게 되어 <code>getInfoFunc</code>는 하나의 함수가 된다.</p>
<p>고로 이때 <code>getInfoFunc</code>를 실행한다면 엉뚱한 <code>this</code>가 나오는 것을 확인할 수 있다(global)</p>
<p><strong>주의해서 작성해주자!</strong></p>
<h2 id="프로퍼티-제어">프로퍼티 제어</h2>
<p>우리는 보통 프로퍼티를 변경하거나 만들 때 처음 객체 리터럴을 만들 때 할당하는 식으로 하거나 </p>
<pre><code class="language-javascript">obj.id = 1
obj[&#39;name&#39;] = &#39;lee&#39;</code></pre>
<p>이런식으로 <code>.</code>과 <code>[]</code>를 이용해서 할당하기도 할것이다.
변경또한 똑같이 재할당하는 식으로도 하는데 몇가지를 진행해보면 <code>[Symbol]</code>타입은 출력이 안되는 것을 확인할 수 있다.
<code>Object.keys(user)</code>와 <code>Object.values(user)</code>를 사용해서 살펴봐도 symbol타입은 출력되지 않는다.</p>
<p>이때 <code>Object.keys</code>와 <code>Object.values</code>는 각각 <code>Object</code>가 가지고 있는 메서드로 인자로 전달된 객체의 모든 <code>key</code>들과 모든 <code>value</code>들을 각각 배열로 출력해준다.
하지만 Symbol 타입과 같은 것들은 출력해주지 않는데 어떻게 해야 출력이 가능할까?
<code>Reflect</code>를 사용하면 된다</p>
<p><code>Reflect</code>엔 많은 기능들이 있는데 그 중에 <code>ownKeys</code>라는 메서드가 있는데 이 경우 <code>Reflect.ownKeys(user)</code>를 찍어볼 경우 Symbol까지 포함한 모든 key들이 출력되는 것을 확인할 수 있다</p>
<p>Reflect를 이용해서 오브젝트를 복사할 때 일반적으로 출력되지 않는 심볼타입들도 전부 가져와 복사 할 수 있고 현재 계속해서 Reflect가 발전중이니 유용할 것 같다.</p>
<p>이외에도 여러가지가 잇는데</p>
<ul>
<li>기본적으로 <code>.</code>과 <code>[]</code>을 이용해 프로퍼티에 접근할 수 있다(이때 <code>[]</code>는 다이렉트로 key값을 적을 경우엔 무조건 string으로 작성해줘야하며 변수를 넣을 수도 있다</li>
<li><code>in</code>을 통해 해당 키값이 존재하는지 확인할 수 있다. 같은걸로 <code>hasOwnProperty(key)</code>가 있다</li>
<li>Reflect를 사용하려면 <code>Reflect.has(obj,key)</code>로 확인할 수 있다</li>
<li>심볼 프로퍼티가 있는지 유무만 체크하고 싶을 때 <code>Reflect</code>를 쓰지않고 <code>Object</code>의 메서드를 사용하려면 <code>Object.getOwnPropertySymbols(user)</code>를 사용하면 된다.</li>
<li>프로퍼티를 삭제하려면 <code>delete</code>를 사용하자</li>
</ul>
<h4 id="오브젝트-클래스의-메서드">오브젝트 클래스의 메서드</h4>
<ul>
<li><p><code>getOwnPropertyDescriptor(obj,key)</code>는 해당 프로퍼티의 value값과 프로퍼티 속성들이 나온다.(작성가능한지? 노출시킬건지? 수정가능한지? 등)</p>
</li>
<li><p>똑같이 작성하되 뒤에<code>getOwnPropertyDescriptors(obj)</code> s를 붙여주면 모든 프로퍼티를 확인할 수 있다</p>
</li>
<li><p><code>Object.defineProperty(obj,key,변경할 내용)</code>을 이용한다면 해당 프로퍼티의 속성들을 변경할 수 있는데</p>
<ul>
<li>value : 해당 프로퍼티의 값</li>
<li>configurable : 속성의 값을 변경할 수 있고 삭제가 가능할 수 있는지?</li>
<li>enumerable : 속성이 밖으로 노출되서 확인이 가능한지?</li>
<li>writable : 할당연산자로 속성의 값을 바꿀수 있는지?</li>
</ul>
</li>
<li><p><code>Object.keys(obj)</code> 와 <code>Object.values(obj)</code>로 각각의 키들과 값들을 배열로 받을 수 있다</p>
</li>
<li><p><code>Object.entries(obj)</code>를 사용하면 키와 값이 2차원 배열 즉, <code>[[key,value],[key,value]]</code>형식으로 나오게 된다</p>
</li>
<li><p>거꾸로 사용하고 싶다면 <code>Object.fromEntries([[key,value],[key,value]])</code>을 통해 2차원배열을 객체로 변경할 수 있다</p>
</li>
<li><p>임의의 객체 <code>obj</code>가 있는데 해당 <code>obj</code>를 다음과 같은 방법으로 새롭게 만들었다<code>Object.assign({},obj)</code>와 <code>{...obj}</code>, <code>new Object(obj)</code> 면 전부 같을까?
직접 입력해본다면 다른 걸 확인할 수 있다. 오로지 같은건 <code>obj === new Object(obj)</code>만 <code>true</code>가 나오게 된다</p>
</li>
<li><p><code>Object.preventExtensions(obj)</code>를 넣게 되면 객체가 확장되는 것을 방지 한다( 추가(X),삭제,읽기,쓰기,재정의) x를 제외한 나머지는 가능하다</p>
</li>
<li><p><code>Object.seal(obj)</code> 추가(X),삭제(X),재정의(X), 읽기,쓰기</p>
</li>
<li><p><code>Object.freeze(obj)</code>는 아예 얼려버리는 것으로 읽기를 제외한 모든것이 안된다(enumerable만 false)</p>
<ul>
<li><strong>주의할 점은 값을할당해도 오류는 없으며 하위 객체까지 freeze가 되진 않는다는 점이 있다</strong></li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - 재귀함수]]></title>
            <link>https://velog.io/@lee_moi/JS-%EC%9E%AC%EA%B7%80%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@lee_moi/JS-%EC%9E%AC%EA%B7%80%ED%95%A8%EC%88%98</guid>
            <pubDate>Thu, 05 Oct 2023 20:07:53 GMT</pubDate>
            <description><![CDATA[<h2 id="재귀-함수">재귀 함수</h2>
<p>다시 나를 호출한다라고 생각하면 편한데 <code>f()</code>라는 함수가 있다면 이 <code>f()</code>함수 내부에서 또 자기 자신을 <code>f()</code>함수로 부르는 것이다.</p>
<p>바로 예시를 살펴보자</p>
<pre><code class="language-javascript">function makeArray(n) {
  if (n === 1) return [1];
  return [...makeArray(n-1), n]
};
makeArray(10);</code></pre>
<p>위 코드는 1~10까지 원소로 이루어진 배열을 만드는 재귀함수이다.</p>
<p>동작흐름을 저번 실행컨텍스트를 따라 순서대로 나열하자면</p>
<ul>
<li>전역 평가 : makeArray라는 함수 &lt;f.o&gt;가 만들어지고 테이블에 등록된다</li>
<li>전역 실행 : makeArray에 arguments로 10을 넘겨주며 실행한다</li>
<li>makeArray 함수 평가 : if의 블럭은 EnvironmentRecord가 만들어는지겠지만 아무것도 가지고 있지는 않다. 파라미터로 쓰일 n을 미리 지정해놓는다(UIY)</li>
<li>makeArray 함수 실행 : n에 arguments로 전달받은 10을 할당하고 진행하는데 return에 makeArray(n-1)이 있으므로 잠시 실행컨텍스트는 멈춘채 makeArray(n-1)을 실행한다. 이는 n이 10이기에 10-1인 makeArray(9)가 실행 컨텍스트에 올라간다.</li>
<li>이후로 makeArray(8),makeArray(7),....,makeArray(1)까지 실행컨텍스트에 올라간다.</li>
<li>이는 return값이 재귀로 인해 물리고 물렸기 때문이며 n에 1이 할당되었을 때 드디어 <code>if</code>문을 만나 <code>return [1]</code>이 실행되어 다시 역순으로 리턴값을 전달하고 마지막에 우리가 원하는 배열을 받을 수 있다</li>
</ul>
<p>이처럼 스스로를 계속 꼬리물듯이 다시 부르는 것을 재귀함수라고 한다</p>
<p>재귀함수를 만들때 가장 중요한 부분으로는 <strong>종료 조건을 만들어놔야 되는 것</strong>이다
결국 재귀함수는 반복문과 비슷하다고 볼 수 있으며 이로인해 종료조건을 만들지 않을 경우 무한 실행되어 stack overflow가 일어나게 된다.</p>
<p><strong>꼭 종료 조건을 가장 먼저 만들 수 있도록 하자</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - 클로저]]></title>
            <link>https://velog.io/@lee_moi/JS-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@lee_moi/JS-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Tue, 03 Oct 2023 18:58:22 GMT</pubDate>
            <description><![CDATA[<h2 id="클로저">클로저</h2>
<p>우리가 실행컨텍스트를 배웠다면 클로저는 이해하기 쉽다.</p>
<pre><code class="language-javascript">function f1() {
  let i = 0;
  return function () {
    i += 1;
    console.log(i);
  }
}

const ff = f1();
ff(); //1
ff(); //2
ff(); //3</code></pre>
<p>이처럼 함수에 리턴되는 함수를 만들고 리턴된 함수를 다른 변수에 담아 사용한다면 참조되는 Environment Record가 삭제되지 않고 남아있다는 건데 이렇게 말하면 이해하기 힘들 것 같긴 하다.</p>
<p>하지만 실행컨텍스트를 생각한다면 금방 이해된는데 차근차근 봐보자</p>
<ul>
<li>전역 코드 평가
가장 먼저 전역 코드가 평가 되는데 이때 전역 코드속에서 호이스팅될 문들을 찾아야하는데 먼저<ul>
<li><code>f1</code> 함수가 전역 함수로 선언되어 있기 때문에 &lt;f.o&gt;에 올라가게 된다.</li>
<li><code>ff</code> 변수가 <code>const</code> 선언문으로인해 Declarative Environment Record에 <code>UIY</code>으로 올라간다</li>
</ul>
</li>
<li>전역 코드 실행
더이상 평가 될 것이 없으니 이제 코드가 실행돼야 하는데 앞에 있는 <code>f1</code>함수는 function 선언문이니 실행할 것은 아니니 넘어간다<ul>
<li><code>const ff = f1()</code>문을 할당해줘야 하기 때문에 <code>f1</code>함수를 <code>()</code>실행한다.</li>
</ul>
</li>
<li><code>f1</code>함수 실행
<code>f1</code>함수가 실행되었으니 이제 ECS에 <code>f1</code>함수 컨텍스트 스택이 올라가게 되고 <code>f1</code>의 레코드 가 만들어진다.</li>
<li><code>f1</code>함수 평가<ul>
<li><code>let i</code>가 있으니 <code>i</code>를 <code>f1</code>함수 실행 환경 레코드에 올리고 return은 실행시 반환될 것이니 아직 대기한다</li>
</ul>
</li>
<li><code>f1</code>함수 실행<ul>
<li><code>let i=0</code>을 통해 <code>i</code>에 0을 할당하고 return문을 실행한다</li>
<li>return문을 실행하며 함수가 반환되기 때문에 return문에 적힌 익명함수를 &lt;f.o&gt;에 올린다</li>
<li>익명함수를 return한다</li>
</ul>
</li>
<li><code>f1</code>함수 종료 -&gt; 전역 실행으로 돌아감
<code>f1</code>함수가 종료되면서 실행 컨텍스트는 pop되고 <code>f1</code>함수가 return한 익명함수는 <code>const ff = f1();</code>를 통해 <code>ff</code>라는 변수에 할당된다<ul>
<li>이때 원래라면 <code>f1</code>의 함수가 실행 컨텍스트 스택에서 pop 될 때 <code>f1</code>함수가 스택에 올라오며 생성된 Record는 더이상 참조되지 않으니 사라져야 하는게 맞다</li>
<li>하지만 이 Record는 살아있는데 이는 아까 <code>f1</code>함수가 실행되며 return하게된 익명함수가 전역에 있는 <code>ff</code>란 변수를 통해 참조되고 있고 그 익명함수의 <code>[[Envrionment]]</code>(함수가 &lt;f.o&gt;를 생성할 당시의 envrecord이기 때문에 <code>f1</code>함수가 실행되며 생성되었다)가 <code>f1</code>의 레코드를 참조하고 있기 때문에 G.C가 돌지 않아 계속해서 살아남아 있는 것이다</li>
</ul>
</li>
<li>그렇기에 그뒤에 실행되는 <code>ff()</code>는 그 &lt;f.o&gt;에 생성된 익명함수를 실행시키는 것이고 그 익명함수는 <code>f1</code>함수의 레코드를 <code>[[Environment]]</code>를 통해 참조하고 있으니 <code>f1</code>함수의 레코드에서 <code>i</code>를 찾아 연산하고 반환하게 되는 것이다</li>
</ul>
<p>이렇게 단순히 함수가 함수를 반환하는 것이 클로저가 아니라 이런 Function Object가 생성될 당시의 참조 Record등을 생각해서 이 Record 참조가 계속 유지 되는 것이 클로저이다.</p>
<ul>
<li>외부 함수를 참조하는 내부 함수(스코프 사슬에 접근) - 안토 아라빈스, 스리칸스 마치리주(Functional Javascript, 2020)</li>
<li>함수가 특정 스코프에 접근할 수 있도록 의도적으로 그 스코프에서 정의하는 것 - 이선 브라운(러닝 자바스크립트, 2017)</li>
<li>로컬 변수를 참조하고 있는 함수 내의 함수 - 야마다 요시히로(자바스크립트 마스터 북, 2017)</li>
<li>자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만을 기억하여 유지시키는 함수 - 유인동(함수형 자바스크립트 프로그래밍, 2017)</li>
<li>함수 선언 시 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수 - 베어 바이볼트,존 레식(자바스크립트 닌자 비급, 2014)</li>
<li>이미 생명 주기상 끝난 외부 함수의 변수를 참조하는 함수 - 송형주,고현준(인사이드 자바스크립트, 2014)</li>
<li>자유변수가 있는 함수와 자유변수를 알 수 있는 환경의 결합 - 에릭 프리먼(헤드퍼스트자바스크립트, 2012)</li>
<li>자신을 내포하는 함수의 컨텍스트에 접근할 수 있는 함수 - 더글라스 크록포드(자바스크립트 핵심 가이드, 2008)</li>
</ul>
<p>각 책들에서 나오는 클로저에 대한 내용인데 한번 읽어보면 좋다</p>
<h2 id="메모이제이션">메모이제이션</h2>
<p>그럼 이 클로저를 이용해 어떤 걸 활용할 수 있냐면... 우리가 React를 배울 때 많이 했던 memoization 기능을 활용할 수 있다.</p>
<p>만약 우리가 클로저를 활용해 어떠한 값을 레코드 참조로 기억해두고 있다면 만약 다시 실행했을 때 그 값이 있거나 이미 실행했었다면 굳이 다시 코드를 전부 실행하는 것이 아닌 클로저 된 환경에서 해당 값을 찾으면 되는 것이다</p>
<p>메모이제이션 즉 메모하기, 기억해두는 것이다</p>
<pre><code class="language-javascript">const forFibonachi = () =&gt; {
  const arr = [0, 1];

  return (k) =&gt; {
    if (arr.length &gt; k) return arr.slice(0, k + 1);
    for (let i = arr.length; i &lt;= k; i++) {
      arr[i] = arr[i - 2] + arr[i - 1];
    }
    return arr;
  };
};
const fibo = forFibonachi();</code></pre>
<p>피보나치 수열을 메모이제이션 하는 것인데
<code>forFibonachi</code>가 return하는 함수가 있고 return되는 익명함수에 <code>arr</code>을 클로저 시켜 저장하도록 했다
return된 익명함수는 피보나치 수열을 동작하지만 만약 내가 이미 한번 실행했던 피보나치 수열의 위치라면 <code>for</code>반복문을 실행하지 않고 <code>slice</code>를 활용해 해당 위치까지의 피보나치 수열을 반환한다.</p>
<p>고로 똑같은 값을 2번 실행하면 1번째는 반복문을 똑같이 실행하겠지만 2번째는 반복문을 실행하지도 않고 바로 답을 반환하여 시간,메모리 모두를 아낄 수 있다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - 실행컨텍스트 그리기]]></title>
            <link>https://velog.io/@lee_moi/JS-%EC%8B%A4%ED%96%89%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EA%B7%B8%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@lee_moi/JS-%EC%8B%A4%ED%96%89%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-%EA%B7%B8%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Tue, 03 Oct 2023 17:55:14 GMT</pubDate>
            <description><![CDATA[<h2 id="실행컨텍스트-그려보기">실행컨텍스트 그려보기</h2>
<pre><code class="language-javascript">const arr = [];
function f2() {
  function Bee() {   // Bee = function() { conso…}
    console.log(&#39;f2.Bee&#39;);   // → return i;
  }
  let z = 0;
  for (let i = 0; i &lt; 2; i += 1) {
    const x = i + 1;
    z = 1;
    function Bee() {   // 실행시 &lt;f.o&gt;로 인정! Bee = function() { return i; }
      return i;
    }
    arr.push(Bee);
    console.log(i, Bee()); // 0, 0
  }

  console.log(arr[0] === arr[1], arr[1] === Bee, z); // false, true, 1
  console.log(&#39;f2&gt;&gt;&#39;, Bee()); // 1
}
f2();</code></pre>
<p>직접 drow.io를 통해 그렸고 전역 평가부터 시작해서 쭉 <code>f2()</code>까지 이어진다
<img src="https://velog.velcdn.com/images/lee_moi/post/ffdebe32-d8e9-4946-a6ec-ff6b132555a1/image.png" alt=""></p>
<ul>
<li>Global Object 가 먼저 생성된다</li>
<li>전역 평가시 ECS에 GEC(글로벌 실행 컨텍스트)가 올라간다</li>
<li>GEC 실행을 통해 GER(글로벌 환경 레코드)가 생성된다</li>
<li>각 내장 필드 등을 통해 DeclarativeRecord와 BindingObject가 만들어지고 참조한다</li>
</ul>
<p><img src="https://velog.velcdn.com/images/lee_moi/post/ba79d8e8-2aea-4f2b-b40d-b755c5b354f8/image.png" alt="">
<img src="https://velog.velcdn.com/images/lee_moi/post/086fa30d-5fc8-415f-968b-5e6474870112/image.png" alt="">
<img src="https://velog.velcdn.com/images/lee_moi/post/b5e5388b-570e-459e-a467-56cfb607439e/image.png" alt="">
<img src="https://velog.velcdn.com/images/lee_moi/post/c5d7c9dc-6fce-4ff6-b2f0-04330a5747ec/image.png" alt="">
<img src="https://velog.velcdn.com/images/lee_moi/post/66f4a7e1-846d-4977-af31-e3354b9f1d74/image.png" alt="">
<img src="https://velog.velcdn.com/images/lee_moi/post/ba07b065-a2f2-47e7-9c37-fdc05f29629e/image.png" alt="">
<img src="https://velog.velcdn.com/images/lee_moi/post/0f6ad453-c557-4516-aa40-144b256348cf/image.png" alt=""></p>
<p>어렵긴 하겠지만 하나씩 살펴보다보면 이해하게 되니 여러번 살펴보자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[실행 컨텍스트]]></title>
            <link>https://velog.io/@lee_moi/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@lee_moi/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Tue, 03 Oct 2023 17:31:35 GMT</pubDate>
            <description><![CDATA[<h1 id="실행컨텍스트">실행컨텍스트</h1>
<p>이전에도 블로그에서 쓴 적 있었지만 그건 면접을 대비 하기 위해 그냥 내용을 외운 것밖에 아니었지만 이번에 확실하게 실행컨텍스트를 이해할 수 있게 되었다</p>
<p>물론 딥다이브 책도 봤지만 성호님 수업을 들으면서 ECMA에 기재된 현 버전의 실행컨텍스트를 새롭게 배울 수 있었고 정말 javascript에 대한 이해가 많이 높아지는 공부였다
먼저 연산자등 js 사용법을 안 상태에서 개념적인 부분이 들어오니 앞서 배운 것들이 확 이해가 올라오면서 수업 자체가 엄청 재밋는 상태다</p>
<p>그럼 내가 이해한 실행 컨텍스트를 하나씩 확인해보자</p>
<h2 id="스코프">스코프</h2>
<p>개념은 각 식별자들의 유효 범위 이다. 범위로는</p>
<ul>
<li>Global</li>
<li>Function</li>
<li>Block Scope</li>
</ul>
<p>이렇게 3가지가 있는데 각 스코프별로 Lexical Enviroment가 생성된다
-&gt; 아마 이 내용이 딥 다이브에 있었는데 최근 문서들을 살펴보면 언급되는 부분이 없다고 한다.
직접 그림을 그려보면 확실히 Lexical Enviroment가 하는일이... 없다
그냥 중개 역할을 하는 것 처럼 보이는데 Enviroment Record 라는 곳에도 참조(OtuerEnviroment)가 생기면서 그냥 동일한 역할을 한다.</p>
<p>이 스코프들에서도 또 2가지 동작 방식이 있는데 정의된 위치를 찾는 것은 <code>Static scope</code>라고 하고 어디서 호출됐는지, 호출된 시간을 기반으로 찾느것이 <code>Dynamic scope</code>이다
이를 통해 변수와 함수가 어떤 스코프로 동작하는지 알 수 있는데</p>
<pre><code class="language-javascript">var x = 3
function foo(){
    console.log(x)
    var x =2
    console.log(&quot;global foo&quot;)
}
function bar(){
    function foo() {
        console.log(&#39;local foo&#39;)
    }
    foo()
}
foo()
bar()</code></pre>
<p>이를 살펴볼 때 <code>foo</code>함수가 실행될 때 안에 있는 <code>console.log(x)</code>는 무엇이 출력될까?
<strong>정답은 <code>undfined</code>가 출력된다</strong>
분명 전역에서 <code>var x = 3;</code> 을 통해서 <code>x</code>라는 변수를 선언 해 뒀는데 왜 <code>x</code>를 출력하면 <code>3</code>이 아니라 <code>undefined</code>가 나오는 걸까??
이유는 <code>foo</code>라는 함수 스코프 안에서 <code>var x =2</code>라는 문 때문에 <code>foo</code>함수 스코프 안에서의 <code>x</code>가 호이스팅 되었기에 <code>undfined</code>가 나온것이다. 그럼 이걸 통해 &#39;아 <strong>변수는 Dynamic scope를가지고 있구나를 알 수 있다.</strong></p>
<p>처음 이 변수가 만들어진 스코프에서 가져오는 것(<code>var x = 3</code>이 전역 스코프에서 만들어졌기에 <code>x</code>는 전역 스코프에 있지만)이 아니라 가장 가까운 스코프부터 찾아나가는 것이다.
<code>console.log(x)</code>가 불린 위치가 <code>foo</code>함수 스코프 안이기 때문에 <code>foo</code>함수 스코프부터 탐색을 시작했고 호이스팅 된 <code>var x = undefined;</code>를 먼저 찾았기 때문에 <code>undefined</code>가 출력된 것이다.</p>
<p>하지만 함수는 다르다.</p>
<p>마지막에 실행된 <code>foo()</code>와 <code>bar</code>함수안의 <code>foo()</code>는 각각 누가 실행될까?</p>
<p>전역에서 실행된 <code>foo()</code>는 전역의 <code>foo</code>함수가 실행되고 <code>bar</code>함수 안의 <code>foo()</code>실행은 <code>bar</code>안의 함수 <code>foo</code>가 실행된다.</p>
<p>&#39;??그럼 함수 스코프 안의 foo가 함수 안을 실행했으니 Dynamic 아닌가요?&#39;
라고 생각할 수 있지만 전혀 아니다.
함수는 Static scope로 자신이 만들어진 즉 정의된 위치를 기반으로 스코프를 검색한다.</p>
<p>전역에서 실행한 <code>foo</code>는 전역에서 <code>function foo</code>를 통해 함수오브젝트에 등록된 순간의 <code>foo</code>이고 <code>bar</code>안의 <code>foo</code>는 <code>bar</code>라는 함수가 실행된 순간에 함수 오브젝트에서 등록된 순간의 <code>foo</code>이다. 
엄연히 둘은 다른 <code>foo</code>라는 함수이며 참조하고 있는 <code>Environment</code>즉 환경이 다르다.
<code>bar</code>안에서 선언된 <code>foo</code>라는 함수는 <code>bar</code>함수가 종료된다면 더이상 참조되고 있는 환경이 없기 때문에 G.C를 통해 소멸된다.</p>
<h2 id="global-object전역-객체">Global Object(전역 객체)</h2>
<p>실행 컨텍스트에서 항상 등장하는 놈으로 많이 나오는 객체로 JS engine process가 생성(시작)할 때 가장 만저 생성된다.
즉 전역객체는 엔진 프로세스가 종료 되기 전까진 끝까지 살아있는 객체인 것을 알 수가 있다.</p>
<p>구성요소들로는</p>
<ul>
<li>BuiltIn(standard) properties &amp; functions <ul>
<li>Built-in properties : Infinity, NaN, undefined 등 </li>
<li>Built-in functions : eval, isInifite, isNaN, parseInt, parseFloat 등</li>
</ul>
</li>
<li>host object(brower, node API) 및 var/fn object도 보유한다</li>
<li><strong>전역 변수는 전역 객체에 영원히 존재</strong> -&gt; 이는 메모리 낭비와 가독성을 떨어뜨리고 모듈의 namespace를 오염시킬 수도 있으니 전역은 항상 자제하자. 매번 클린코드 얘기가 나올 때 전역 변수를 최소하 하자는 얘기가 괜히 있는 것은 아니다.</li>
<li>직접 생성(컨트롤) 하지 못하고, window(globalThis) 키워드 생략 가능</li>
<li>const/let 은 전역 객체가 아닌 Declarative Environment Record라는 곳에 별도로 생성된다</li>
<li>선언이 없는 식별자는 암묵적으로 전역으로 전역객체에 등록된다</li>
</ul>
<pre><code class="language-javascript">ig = 1;
console.log(window.ig) // 1
console.log(global.ig)//1</code></pre>
<p>Declarative Environment Record에 는 하단에서 Environment Record 부분을 다루면서 정리할 예정이니 일단 <code>const</code>/<code>let</code> 은 Declarative Environment Record 라는 곳에 생성되는 구나 하고 이해하고 있자</p>
<h2 id="execution-context-실행-컨텍스트">Execution Context (실행 컨텍스트)</h2>
<p>그럼 앞서 스코프와 전역 객체에 대해 배웟으니 대망의 실행 컨텍스트 부분을 살펴보자</p>
<h3 id="실행-컨텍스트를-생성하는-code들">실행 컨텍스트를 생성하는 code들</h3>
<p>기본적으로 <strong>전역 코드, 함수 코드, eval코드, module 코드는 각각의 실행 컨텍스트를 생성한다</strong>
이들이 CallStack에 생성됨으로 이를 &#39;Execution Context Stack&#39; ECS라 부른다</p>
<blockquote>
<p>LexicalEnvironment(렉시컬 환경)
현재 많은 책에서 렉시컬 환경에 대해 서술하고 있지만 v8엔진 이후로 tc39랑 찾아보면 LexicalEnvironment에 대한 얘기가 더이상 등장하지 않는다.
Environment Record 내장 슬롯으로 <code>[[OutEnv]]</code>라는 이전 렉시컬 환경에서 사용하던 <code>OuterEnvironmentReference</code>가 대체 되는 부분이 나왔기 때문 아닌가 싶다.
실제로 Record에 기록된 변수를 찾아 올라갈 때 각 환경의 Record에서 바로 상위 Record로 갈 수 있는 <code>[[OutEnv]]</code>가 있음으로 굳이 렉시컬 환경을 통해 돌아 돌아 갈 필요가 없어졌기 때문 아닐까 싶다.</p>
</blockquote>
<h2 id="environment-record">Environment Record</h2>
<p>그럼 실행 컨텍스트가 콜스택에 올라가 생성되는 Envrionment Record들에 대해 살펴보자.
Envrionment Record는 실행 컨텍스트 스택 즉 ECS에 실행 컨텍스트가 올라올 경우 생성된다.
이건 Global 또한 마찬가지다</p>
<h3 id="global-environment-recordinner-field">Global Environment Record(inner-field)</h3>
<p>많은 내장 슬롯이 있지만 알아 두어야 할 것만 한번 살펴보려 한다.</p>
<ul>
<li>[[ObjectRecord]] : global built-in bindings, FunctionDeclaration, asyncFunctionDeclaration 등과 같이 전역적으로 필요한 것들이 포함된 전역 객체를 참조한다 </li>
<li>[[DeclarativeRecord]] : const / let 로 선언된 식별자들이 들어간다 생각하면 편하다</li>
<li>[[GlobalThisValue]] : GlobalObject pointer 즉 전역 스코프의 this가 반환하는 값이다(Global Object)</li>
<li>[[VarNames]] : 식별자 List로 함수(Function, Generator, AsyncFunction, AsyncGenerator) 또는 변수 선언에 바인딩된 문자열 리스트이다</li>
<li><strong>[[OuterEnv]]</strong> : null</li>
</ul>
<p>등이 있는데 OuterEnv는 외부 Environment Record에 대한 참조이지만 전역은 자신 위로 아무도 없고 스스로가 최상위이기 때문에 <code>null</code>을 가지고 있다</p>
<h3 id="functionclass-object">Function(class) Object</h3>
<p>함수가 만들어질 때 Function Object라는 곳에 함수의 코드들과 참조하고 있는 위치를 알려주는 트리들이 생성되는데 이 Function Object에서 이 코드들을 보관하게 된다.
물론 실행을 위한 평가 같은 것들이 진행된 상태는 아니다</p>
<p>총 15개의 슬롯을 가지고 있지만 우리가 실행 컨텍스트를 배우는데에 가장 중요한 것은
<strong><code>[[Environment]]</code></strong> 이 하나이다</p>
<p><strong>이 <code>[[Environment]]</code> 는 Function Object를 생성할 당시의 Envrionment Record에 대한 참조를 가지고 있다</strong></p>
<p>가장 위에서 다룬 스코프 부분에서 x의 값이 찍히는 부분이나 함수 실행 부분이 다른 이유가 이 <code>[[Environment]]</code> 때문이니 매우 중요한 부분이다</p>
<h3 id="functionenvironmentrecord-inner-slot">FunctionEnvironmentRecord (inner slot)</h3>
<p>JS는 함수형 프로그래밍 언어로 가장 많이 보게될 실행 컨텍스트라고 생각한다. 가지고 있는 내장 슬롯들이 많은데 하나씩 살펴보자</p>
<ul>
<li><code>[[FunctionObject]]</code> : FunctionObject에 대한 참조를 가진다. 즉, 나의 &lt;f.o&gt;(function object)에 대한 참조이다</li>
<li><code>[[NewTarget]]</code> : 생성자 함수( new Array 같은 것)로 호출한 경우(클래스인 경우 포함) Function Object에 대한 참조이다. 아니라면 <code>undefined</code></li>
<li><code>[[ThisValue]]</code> : 함수 호출할 때 사용되는 this 값으로 this를 바인딩 하지 않으면 Global Ojbect를 참조한다. (ex. <code>fn.call({id:1}) 일 때 fn()함수 내의 this는 {id:1}</code>)</li>
<li><code>[[ThisBindingStatus]]</code> : 값이 lexical이면 이 함수는 arraow function이며, local this를 갖지 않는다. lexical이 아니라면 uninitialized 상태였다가 <code>[[ThisValue]]</code>값이 set되면 initialized 상태로 변경된다</li>
<li><code>[[OuterEnv]]</code> : Function Object의 <code>[[Environment]]</code> 내부 슬롯이 참조하는 값. 즉, &lt;f.o&gt;가 생성될 당시의 Lexical Scope</li>
</ul>
<p>다음 정리때 한번 실행컨텍스트를 직접 그려보자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - 제어문(조건문&반복문)]]></title>
            <link>https://velog.io/@lee_moi/JS-%EC%A0%9C%EC%96%B4%EB%AC%B8%EC%A1%B0%EA%B1%B4%EB%AC%B8%EB%B0%98%EB%B3%B5%EB%AC%B8</link>
            <guid>https://velog.io/@lee_moi/JS-%EC%A0%9C%EC%96%B4%EB%AC%B8%EC%A1%B0%EA%B1%B4%EB%AC%B8%EB%B0%98%EB%B3%B5%EB%AC%B8</guid>
            <pubDate>Mon, 02 Oct 2023 09:04:08 GMT</pubDate>
            <description><![CDATA[<h2 id="제어문">제어문</h2>
<p>제어문은 조건과 반복까지 포함되는 단어로 많은 종류가 있다.</p>
<h3 id="조건문">조건문</h3>
<ul>
<li>if ... else ... : 우리가 흔히 아는 <code>if</code>,<code>else</code>문으로  조건과 실행문으로 되어있다</li>
<li>switch... case... : if처럼 조건을 걸지만 여러 조건들을 switch를 사용하면 if조건 if조건 if 조건 이런식으로 사용하는 것이아닌 하나의 switch문 안에 여러 조건들과 실행문을 한꺼번에 만들 수 있다<h3 id="반복문">반복문</h3>
</li>
<li>while(조건) {}</li>
<li>do {} while(조건)</li>
</ul>
<p>2가지 모두 while의 조건이 true가 된다면 반복되는 형식으로 <code>do</code>가 없다면 while문 안 실행문들이 실행되고 <code>do</code>를 작성한다면 <code>do</code>안의 실행문들이 실행된다.</p>
<ul>
<li>for 반복문 : 우리가 처음 코딩을 배울 때 가장 많이 사용하는 <code>for</code>문이다</li>
</ul>
<p><code>while</code>문을 생각해보면 보통 <code>while</code>문을 사요할 때 <code>()</code>안 조건과 종료조건들 밖에 설정해야하는데</p>
<pre><code class="language-javascript">const i = 0;

while(i&lt;5) {
  console.log(&#39;wihle&gt;&gt;&#39;,i)
  i += 1
}</code></pre>
<p>이처럼 <code>const i = 0</code>으로 종료 조건에 사용할 변수를 따로 선언하고 <code>( )</code>안쪽에 종료 조건을 쓰고 <code>i += 1</code>을 이용해 종료 조건에 올 수 있도록 실행할 문을 작성해야한다.</p>
<p>그럼 만약 <code>while</code>문 안에서 실행될 실행 코드들이 엄청 많아진다면 <code>while</code>문 밖에서 작성한 조건 변수에 조건 종료를 위해 실행될 실행문도 저 아래 있다면 찾기가 많이 힘들어질 것이다.
그럼 어차피 이 3개는 반복문이 실행되기 위한 것들이니 한곳에 모여 작성하는 것이 더 좋을 것 같다 라는 시점으로 
<code>for(조건변수선언 ; 종료 조건문 ; 조건 false시 실행할 실행문){실행코드}</code>로 한꺼번에 묶어놓은 것이다.
예시코드</p>
<pre><code class="language-javascript">const arr = [1,2,3,4,5]
for(let i=0; i&lt;arr.length; i++){
  console.log(arr[i])
}</code></pre>
<p><code>for</code>문을 작성 할 때는 종료조건만도 가능하다 <code>for(;!didEnd;){}</code> 처럼도 가능</p>
<p>반복문의 종류로는 <code>for문</code>, <code>for...in</code>, <code>for...of</code>, <code>forEach</code> 등이 있고 Array 고차함수로 <code>map,filter,reduce...</code>등 많은데 앞서 한번 정리 한 적이 있다</p>
<p><a href="https://velog.io/@lee_moi/TIL-20221105-130%EB%B2%88for%EB%AC%B8%EC%9D%98-%EC%A2%85%EB%A5%98">for문의 종류</a></p>
<p>반복문을 사용할 때 <code>continue</code>와 <code>break</code>,<code>return</code>등을 사용할 수 있는데 </p>
<ul>
<li><code>continue</code> : 현재 반복을 더이상 실행하지 않고 다음 반복문 실행으로 넘어가는 것</li>
<li><code>break</code> : 반복문이나 조건문등을 종료하는것(switch.. case문에서 쓰이기 때문)</li>
<li><code>return</code> : 아예 종료하는 것이다. 함수 안에서 반복문을 사용할 때 <code>return</code>을 조건에 맞춰 추가해 놓으면 반복문만 종료되는 것이 아닌 함수 자체를 종료 시킬 수 있다</li>
</ul>
<h4 id="switch문-예시">switch문 예시</h4>
<pre><code class="language-javascript">let i = 1
switch(i) {
  case 0 : console.log(&#39;i는 0입니다&#39;);
    break;
  case 1 : console.log(&#39;i는 1입니다&#39;);
    break;
  case 2 : console.log(&#39;i는 2입니다&#39;);
    break;
  default : console.log(i)
}</code></pre>
<p><code>i</code>값이 무엇이냐에 따라 i값에 맞는 case문을 실행시키는 것이 switch case문인데 주의할 것으론 <code>break</code>를 꼭 넣어줘야 하는 것이다.
<strong><code>break</code>를 넣지 않을 경우 모든 해당 조건 case문 이후로의 모든 case문을 지나가며 실행시키게 되니 조심하자</strong>
또한 <code>defalut</code>를 집어넣어 모든 case조건에 부합하지 않을 경우 기본 값으로 출력될 부분도 구현해 놓자.</p>
<blockquote>
<p>주의할 점
switch...case문을 사용할 때 변수를 사용해야 한다면 조심해야 하는 부분이 있다
위 코드를 살펴보면 switch...case문의 <code>{}</code> 블럭은 어디서부터 어디까지 일까? 각각의 case까지 일까?
아니다 case문에는 코드에서도 안 써있듯이 <code>{}</code>이 존재 하지 않고 전체를 감싸고 있는 <code>switch(i) {}</code> 이 부분이 하나의 블럭으로 인정되기 때문에 블럭스코프인 <code>let</code>과 <code>const</code>를 사용할 경우 다른 case의 실행문에도 모두 포함 될 수 있다는 부분을 조심해야 한다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - 연산자(2)]]></title>
            <link>https://velog.io/@lee_moi/JS-%EC%97%B0%EC%82%B0%EC%9E%902</link>
            <guid>https://velog.io/@lee_moi/JS-%EC%97%B0%EC%82%B0%EC%9E%902</guid>
            <pubDate>Mon, 02 Oct 2023 07:29:08 GMT</pubDate>
            <description><![CDATA[<h2 id="구조-분해-할당-destructuring확장spread연산자">구조 분해 할당 (destructuring)/확장(spread)연산자</h2>
<p>구조분해 할당과 스프레드 연산자는 프론트 개발자라면 꼭 알고 가야하는 연산자 중 하나로 특히 React에서 많은 활용을 한다.
props를 내려줄때나 state 업데이트를 할 때나 많은 사용을 한다</p>
<p>그 전에 할당 연산자를 알아보면</p>
<h3 id="할당">할당</h3>
<p>우리가 변수를 선언한 후 값을 할당한다. 라는 얘기를 많이 들었을 것이다. 거기서 나온 것이 바로 할당이다</p>
<pre><code class="language-javascript">let x,y;
x = y = 9; // x = 9, y = 9
const z = (y++, x+y); z = 19</code></pre>
<p>이와 같이 <code>=</code>를 활용 해서 할당하는 것이 바로 할당 연산자이다.
<code>const z</code>부분은 <code>( )</code> 안의 <code>y++</code>가 먼저 실행되어 <code>y</code>가 1이 증가해서 <code>10+9</code>가 돼서 <code>19</code>가 된다.</p>
<h3 id="구조-분해-할당">구조 분해 할당</h3>
<p>뭔가 말이 이해된다면 이 연산을 쉽게 볼 수 있을 것이라 생각된다. 
말 그대로 구조 자체를 분해해서 각각 할당하는 것인데</p>
<pre><code class="language-javascript">const u = {id: 1, name: &#39;lee&#39;, age: 29}
let {id, name, addr} = u
console.log(id,name,addr) // 1 , &#39;lee&#39;, 29</code></pre>
<p>이처럼 객체나 배열등의 구조를 분해해서 각각 할당해주는 것인데 하나하나 파보면
선언된 <code>u</code>라는 오브젝트가 있고 아래에 <code>let</code>변수 선언문 뒤에 동일한 오브젝트 <code>{ }</code>를 만들었다.
이후 안에 변수로 선언될 녀석들을 적는데 이 변수 이름들은 <code>u</code>라는 오브젝트의 키값과 동일하게 적는다.
이 변수 이름을 이용해 js가 <code>u</code> 오브젝트에서 해당 변수 이름을 키값으로 밸류 값을 찾아 해당 변수에 할당하게 된다.
이는 배열도 마찬가지인데</p>
<pre><code class="language-javascript">const arr = [1, 2, 3, 4, 5]
let [a,b,,,c] = arr
console.log(a,b,c) // 1,2,5</code></pre>
<p>배열은 객체와 달리 키값이 눈에 보이지가 않는데 실제로는 배열도 객체로 각각 <code>index: value</code>라는 프로퍼티들로 이루어져 있다
<code>Reflect.ownkeys</code>를 사용해서 <code>key</code>값을 뽑아내면 실제로 0부터 시작한 index값들이 붙어있고 끝에 <code>length</code>라는 키값이 있는 걸 확인해 볼 수 있다.</p>
<p>그럼 구조분해 할당을 할 때 왜 <code>[a,b,,,c]</code>와 같은 불필요한 <code>,</code>를 붙이는 것일까?
아까 오브젝트를 구조분해 할당 할 때 각 변수의 이름들을 이용해 키값으로 활용하여 밸류를 찾는다고 했는데 배열의 경우 배열에서 선언된 변수들의 위치 인덱스를 통해 할당할 <code>arr</code>의 배열 인덱스의 위치와 동일하게 변수를 할당하게 된다
따라서 만약 <code>[a,b,c]</code>로 선언하게 되면 <code>1,2,3</code>이라는 값이 되게된다
그러니 항상 주의해서 작성하자</p>
<blockquote>
<p><strong>왜 JS의 배열 끝에는 <code>length</code>라는 프로퍼티가 존재할까?</strong>
배열은 리스트와 같아서 트리 형태로 살펴본다면 각각의 인덱스 값에서 다음 값의 위치를 함께 가지고 있다.
0번째 인덱스를 찾으면 0번째 인덱스에서 1번째 인덱스로 내려오고 1번째 인덱스를 통해 2번째 인덱스로 내려오고 그런 방식이다 보니 중간에 하나가 빠지게 되거나 0번째 인덱스를 빼버리는 경우 배열 전체가 다시 설정되는 그런 불편함이 있다
그러다보니 보통 <code>length</code>라는 배열의 길이를 구하려면 0번째부터 마지막 인덱스까지 쭉 타고 들어가 총 배열 크기를 카운팅 해야 하는데 만약 우리가 자주 사용하는 <code>for(let i=0; i&lt;arr.length;i++)</code>과 같이 반복문안에서 사용해 버리면 반복문이 실행할 때마다 그 긴 배열이 카운팅되어 제곱에 가까운 시간이 생기게된다.
그래서 실제로 <code>arr.length</code>를 다른 변수에 담아놓고 <code>for</code>문에서 사용하는 것이 카운팅 횟수를 줄일 수 있는 방법이다.
그렇기에 JS의 경우 배열이 만들어질 때 해당 배열의 크기를 <code>length</code>프로퍼티로 가지고 있게 하는 방법이 채택되었다고 볼수있다</p>
</blockquote>
<h3 id="복잡한-destructuring">복잡한 destructuring</h3>
<p>좀 더 복잡한 분해 할당으로 들어가기 전 구조분해 할당 시 사용되는 간단한 연산자를 봐보자</p>
<pre><code class="language-javascript">const obj = {id:1,what:&quot;lee&quot;}
const {id,what:name,addr = &#39;seoul&#39;} = obj
console.log(id,name,addr) // 1 , &#39;lee&#39; , &#39;seoul&#39;</code></pre>
<p>이와 같이 <code>:</code>는 <code>what</code>을 통해 찾게된 value값을 내가 원하는 <code>name</code>라는 이름의 변수로 할당할 수 있게 변경해준다
뒤에 선언된 <code>addr</code>의 경우 <code>obj</code>에 해당 프로퍼티가 없는 것을 볼 수 있다. 이렇게 될 경우 <code>addr</code>은 <code>undefined</code>로 값이 할당되게 되는데 이는 매우 좋지 않으므로 <code>=</code>할당 연사자를 붙여 기본값을 설정해주는 것이다.
함수에서 파라미터 값을 <code>=</code>을 통해 기본값을 정해주는 것과 동일한 방법이다.</p>
<p><strong>그럼 좀 더 복잡한 destructuring을 알아보자</strong>
아래 코드들은 연속적으로 작성된 하나의 프로세스 안에서 실행된 코드들이라고 생각하며 살펴보자</p>
<pre><code class="language-javascript">const user = {name:&#39;Lee&#39;, age: 30}
const fn = ({age}) =&gt; age
console.log(fn(user)) // 30</code></pre>
<p>한수안의 arguments로 해당 객체를 보내면서 도착했을 때 파라미터를 destructuring하여 <code>age</code>만 뽑아낸것이다.
이렇게 할 경우 파라미터를 객체로 받아 <code>user.age</code>와 같은 불필요한 코드를 짧게 줄일 수 있다.
이 방법은 React 프레임 워크를 사용할 때 많이 사용되니 꼭 알아두자</p>
<pre><code class="language-javascript">const {age2:age3 = fn(user)} = {age22: 20}; 
console.log(age3)//30
console.log(age2)// not defined</code></pre>
<p>이는 구조분해 할당시 <code>age2</code>의 키값이 없으니 <code>age2</code>를 찾지 못했고 <code>:</code>를 통해 변수 이름은 <code>age3</code>로 변해서 <code>age2</code>는 <code>not defiend</code>라는 에러가 발생한 것
하지만 <code>age3</code>는 <code>=</code>연산자를 이용해 <code>fn</code>함수가 내뱉은 값이 할당되니 <code>30</code>이 출력되는 것이다
혹시 중첩되는 객체를 구조분해할당 하려고 하면</p>
<pre><code class="language-javascript">const u3 = {id:3, name: &#39;kim&#39;, addr: {id: 1, city: &#39;Seoul&#39;}};
let {id:idd, addr:{id:aid}} = u3; // idd =3, aid = 1</code></pre>
<p>똑같이 구조분해 할당을 하는데 <code>addr</code>을 통해 1번째 프로퍼티를 찾고 해당 프로퍼티 안을 <code>{}</code>구조분해 할당을 한번더 해서 그 안의 <code>id</code>를 찾게 되는 것이다.
여기서 사용되는 <code>:</code>는 이름 변경의 연산자가 아님을 꼭 기억하자</p>
<h3 id="객체--배열-연산자">객체 / 배열 연산자</h3>
<p>객체와 배열에서 사용할 수 있는 연산자들이 있다</p>
<ul>
<li>점(.)연산자 : u.name === u[&#39;name&#39;] 키값을 검색해서 밸류값을 반환한다</li>
<li>대괄호([])연산자 : 객체에서 <code>[]</code>를 사용할 경우 키값을 적을 때 변수를 사용할 수 있다.<code>[]</code>를 사용할 때 대괄호 안에 키값을 string으로 주고 싶다면 꼭 <code>&#39;&#39;</code>을 사용하자</li>
<li>in 연산자 : 해당 프로퍼티가 있는지 확인해준다. <code>u.hasOwnProperty(&#39;id&#39;) &lt;==&gt; Reflect.has(u,&#39;id&#39;)</code></li>
<li>new 연산자 : <code>const d = new Dog()</code> class를 사용할 때 많이 봤을 것 같다</li>
<li>instanceof 연산자 : <code>d instanceof Dog</code> d라는 객체가 Dog라는 오브젝트의 프로토타입 체인에 걸리는지 확인한다</li>
<li>rest(...)연산자 : 아까 배열이나 객체 distructuring을 할 때 만약 나머지 값들을 넣고 싶다면 <code>...</code>연산자를 통해 나머지 값들을 다 붙일 수 있다</li>
<li>delete 연산자 : 프로퍼티를 삭제해준다</li>
<li>arr?.length 연산자 : Optional-Chaining으로 에러를 내뱉지 않는다. 이전 연산자 1번부분에서 다룬 부분이니 확인해보자</li>
</ul>
<blockquote>
<p>rest(<code>...</code>)연산자는 spread와 동일하게 사요하지만 동작은 다르다.
spread를 더 쉽게 이해하고 싶다면 <code>arr</code>이란 <code>[1,2,3,4,5]</code>라는 배열이 존재할 경우 <code>...arr</code>을 사용한다면 <code>[]</code>이 벗겨진다고 생각하자.
만약 distructuring과 함께 사용한다면 rest로 동작하게 될텐데 <code>const [a,b,...c] = arr</code>을 사용한다면 <code>a</code>와 <code>b</code>는 각 인덱스 값들이 들어가지만 <code>c</code>는 나머지 <code>[3,4,5]</code>인 나머지 값들이 들어가게 된다.
객체에서 사용할 경우 미리 spread 연산자로 복사시킨후 해당 키값을 다른 값으로 다시 적으면 위에 덮어쓸 수 있게 된다</p>
</blockquote>
<h3 id="연습">연습</h3>
<p>user객체를 id와 name을 출력하는 3개의 함수로 만들어보자</p>
<pre><code class="language-javascript">const hong = {id:1, name:&#39;hong&#39;}

const f1 = (obj) =&gt; {
  console.log(`id:${obj.id},name:${obj.name}`)
}
const f2 = ({id,name}) =&gt; {
  console.log(`id:${id},name:${name}`)
}
const f3 = () =&gt; {
  console.log(`id:${this.id},name:${this.name}`)
}
const f4 = (id,name) =&gt; {
  console.log(`id:${id},name:${name}`)
}
const f5 = (...args) =&gt; {
  if(!args.length)return;
  const {id,name} = args;
  console.log(`id:${id},name:${name}`)
}

f1(hong)
f2(hong)
f3().bind(hong)()
f4(hong.id,hong.name)
f5(hong)</code></pre>
]]></description>
        </item>
    </channel>
</rss>