<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>cielo_hello.log</title>
        <link>https://velog.io/</link>
        <description>아이디어와 구현을 좋아합니다!</description>
        <lastBuildDate>Mon, 24 Mar 2025 11:43:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>cielo_hello.log</title>
            <url>https://velog.velcdn.com/images/cielo_hello/profile/1147fa3f-1ea8-4ee1-9fb0-dd91df6188d0/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. cielo_hello.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cielo_hello" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[도메인, HTTPS, 세션, 그리고 쿠키.. 말 그대로 백엔드 배포 삽질기]]></title>
            <link>https://velog.io/@cielo_hello/%EB%8F%84%EB%A9%94%EC%9D%B8-HTTPS-%EC%84%B8%EC%85%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%BF%A0%ED%82%A4..-%EB%A7%90-%EA%B7%B8%EB%8C%80%EB%A1%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%B0%B0%ED%8F%AC-%EC%82%BD%EC%A7%88%EA%B8%B0</link>
            <guid>https://velog.io/@cielo_hello/%EB%8F%84%EB%A9%94%EC%9D%B8-HTTPS-%EC%84%B8%EC%85%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%BF%A0%ED%82%A4..-%EB%A7%90-%EA%B7%B8%EB%8C%80%EB%A1%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EB%B0%B0%ED%8F%AC-%EC%82%BD%EC%A7%88%EA%B8%B0</guid>
            <pubDate>Mon, 24 Mar 2025 11:43:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>완전히 죽어버린 백엔드 서버를 다시 살리기
NestJS + EC2 + RDS + Nginx + OAuth + Session + HTTPS</p>
</blockquote>
<h1 id="다시-살려보자"><strong>다시 살려보자!</strong></h1>
<p>코끼리 사이트 너무 아깝다.</p>
<p>진짜 이쁘고, 진짜 열심이 만들었는데, 이대로 사라지게 둘 수는 없었다.</p>
<p>다시 살려 보자! 라는 마음에서 시작했다.</p>
<p>근데 백엔드 서버가 완전히 죽었다는 것을 알게 됐다.</p>
<p><strong>완전히 죽었다는 뜻은, 프리티어 기간이 끝나서 EC2 서버가 통째로 사라졌다는 것.</strong></p>
<p>RDS 주소도, 도메인도, 아무것도 남아있지 않았다.</p>
<p><strong>처음부터 다시 시작해야 했다.</strong></p>
<ul>
<li>기존 NestJS 백엔드 코드를 레포로 받아오고,</li>
<li>AWS EC2에서 서버를 다시 세팅하고,</li>
<li>RDS로 DB도 새로 만들고,</li>
<li>HTTPS 적용을 위해 Certbot과 Nginx 설정을 직접 해주고,</li>
<li>OAuth 키도 새로 발급받고,</li>
<li>CORS와 쿠키 관련 문제를 고치면서</li>
</ul>
<p><strong>빌드만 거의 100번</strong>은 했다.</p>
<p>기존에는 프론트 코드만 다뤘고, 배포는 쉽게만 했었는데,</p>
<p>이번엔 <strong>서버 인프라 전체를 직접 구성</strong>했다.</p>
<h3 id="결과-→-httpsco-kkiri-apicom">결과 → <a href="https://co-kkiri-api.com/">https://co-kkiri-api.com/</a></h3>
<h1 id="기술-스택"><strong>기술 스택</strong></h1>
<table>
<thead>
<tr>
<th><strong>항목</strong></th>
<th><strong>내용</strong></th>
</tr>
</thead>
<tbody><tr>
<td>백엔드 프레임워크</td>
<td>NestJS</td>
</tr>
<tr>
<td>배포 대상</td>
<td>AWS EC2 (Ubuntu 22.04)</td>
</tr>
<tr>
<td>DB</td>
<td>MySQL (AWS RDS)</td>
</tr>
<tr>
<td>세션 저장소</td>
<td>Redis</td>
</tr>
<tr>
<td>인증</td>
<td>OAuth (Google, GitHub, Kakao)</td>
</tr>
<tr>
<td>웹서버</td>
<td>Nginx + HTTPS (Certbot)</td>
</tr>
<tr>
<td>배포 자동화</td>
<td>PM2</td>
</tr>
<tr>
<td>프론트 호스팅</td>
<td>Netlify (dev.co-kkiri-api.com)</td>
</tr>
<tr>
<td>도메인</td>
<td>AWS Route 53에서 구매, 서브도메인 사용 (be.co-kkiri-api.com)</td>
</tr>
</tbody></table>
<h1 id="백엔드-배포-과정"><strong>백엔드 배포 과정</strong></h1>
<h2 id="1-서버-환경-구성"><strong>1. 서버 환경 구성</strong></h2>
<p><strong>EC2 인스턴스 생성 (Ubuntu 22.04)</strong></p>
<ul>
<li>퍼블릭 IP 자동 할당</li>
<li>키페어 생성: co-kkiri.pem</li>
</ul>
<p><strong>SSH 접속</strong></p>
<pre><code>chmod 400 co-kkiri.pem
ssh -i &quot;co-kkiri.pem&quot; ubuntu@&lt;EC2 퍼블릭 IP&gt;</code></pre><p><strong>필수 패키지 설치</strong></p>
<pre><code>sudo apt update
sudo apt install -y nodejs npm git nginx
sudo apt install -y mysql-client</code></pre><hr>
<h2 id="2-프로젝트-설정-및-빌드"><strong>2. 프로젝트 설정 및 빌드</strong></h2>
<pre><code>git clone https://github.com/내레포/BE_co-KKIRI.git
cd BE_co-KKIRI
npm install
touch .env
vim .env</code></pre><p>.env 파일에는 아래와 같은 설정을 추가함:</p>
<pre><code>DB_HOST=&lt;rds-endpoint&gt;
DB_PORT=3306
DB_USERNAME=admin
DB_PASSWORD=***
DB_NAME=cokkiri
COOKIE_NAME=***
COOKIE_SECRET=***
SESSION_COOKIE_DOMAIN=co-kkiri-api.com
REDIS_HOST=&lt;redis-host&gt;
REDIS_PORT=6379
REDIS_PASSWORD=***</code></pre><hr>
<h2 id="3-pm2로-백엔드-실행"><strong>3. PM2로 백엔드 실행</strong></h2>
<pre><code class="language-bash">npm run build  # NestJS 프로젝트를 TypeScript → JavaScript로 컴파일하여 dist/ 폴더에 저장

pm2 start dist/main.js --name main  
# PM2로 빌드된 서버(main.js)를 실행함
# --name main: 프로세스 이름을 &#39;main&#39;으로 지정 (관리하기 쉬움)

pm2 save  
# 현재 실행 중인 PM2 프로세스 목록을 저장함 (재부팅 시 복구용)

pm2 startup  
# EC2 재부팅 시 PM2가 자동으로 실행되도록 시스템에 등록함
# 출력되는 명령어 (sudo ... update...)를 복사해서 실행해야 적용됨</code></pre>
<hr>
<h2 id="4-rds-구성"><strong>4. RDS 구성</strong></h2>
<ul>
<li>새로운 MySQL RDS 인스턴스 생성 (프리티어)</li>
<li>보안 그룹 인바운드 설정:</li>
</ul>
<table>
<thead>
<tr>
<th><strong>Type</strong></th>
<th><strong>Port</strong></th>
<th><strong>Source</strong></th>
</tr>
</thead>
<tbody><tr>
<td>MySQL/Aurora</td>
<td>3306</td>
<td>EC2 보안 그룹</td>
</tr>
</tbody></table>
<p><strong>접속 테스트</strong></p>
<pre><code>mysql -h &lt;rds-endpoint&gt; -u admin -p</code></pre><p><strong>TypeORM 설정 예시</strong></p>
<pre><code class="language-tsx">TypeOrmModule.forRootAsync({
  useFactory: () =&gt; ({
    type: &#39;mysql&#39;,
    host: process.env.DB_HOST,
    port: 3306,
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    synchronize: true,
    autoLoadEntities: true,
  }),
});</code></pre>
<p>⚠️ synchronize: true는 테스트 때만!</p>
<p>배포 시에는 반드시 false로 변경할 것.</p>
<hr>
<h2 id="5-redis로-세션-저장"><strong>5. Redis로 세션 저장</strong></h2>
<pre><code>sudo apt install redis-server
redis-cli ping</code></pre><p>.env 설정 추가</p>
<pre><code>REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=</code></pre><p>NestJS main.ts에서 Redis 연결</p>
<pre><code class="language-tsx">const redisClient = createClient({
  socket: {
    host: configService.get(&#39;REDIS_HOST&#39;),
    port: configService.get(&#39;REDIS_PORT&#39;),
  },
});</code></pre>
<hr>
<h2 id="6-cors--쿠키-설정"><strong>6. CORS + 쿠키 설정</strong></h2>
<p>이 부분에서 가장 많은 삽질을 했다.</p>
<p><strong>문제</strong></p>
<ul>
<li>Set-Cookie는 오지만, <strong>브라우저에 저장이 안 됨</strong></li>
<li>프론트와 백엔드의 도메인이 달라서 SameSite 정책 충돌</li>
</ul>
<p><strong>해결</strong></p>
<ul>
<li>프론트: <a href="https://co-kkiri-api.com">https://co-kkiri-api.com</a> (메인 도메인)</li>
<li>백엔드: <a href="https://be.co-kkiri-api.com">https://be.co-kkiri-api.com</a> (서브 도메인)</li>
</ul>
<pre><code class="language-tsx">app.enableCors({
  origin: [&#39;https://co-kkiri-api.com&#39;],
  credentials: true,
});</code></pre>
<pre><code class="language-tsx">cookie: {
    maxAge: 1000 * 60 * 60 * 24,
  domain: &#39;.co-kkiri-api.com&#39;,
  httpOnly: true,
  sameSite: &#39;lax&#39;,
}</code></pre>
<hr>
<h2 id="7-nginx--https-설정"><strong>7. Nginx + HTTPS 설정</strong></h2>
<pre><code>sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d be.co-kkiri-api.com</code></pre><p>/etc/nginx/sites-available/be.co-kkiri-api.com</p>
<pre><code>server {
  listen 80;
  server_name be.co-kkiri-api.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;
  server_name be.co-kkiri-api.com;

  location / {
      proxy_pass http://localhost:8080;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
  }

  ssl_certificate /etc/letsencrypt/live/be.co-kkiri-api.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/be.co-kkiri-api.com/privkey.pem;
}</code></pre><hr>
<h1 id="배운-점"><strong>배운 점</strong></h1>
<ul>
<li>진짜 웹 서비스가 돌아가려면, <strong>코드만으로는 안 된다.</strong></li>
<li>배포 환경, 도메인, 인증서, 세션, CORS, 쿠키, DB, Redis 등 전부 직접 관리해야 한다.</li>
<li>그리고 이건 <strong>혼자 삽질하면서 직접 해봐야</strong> 알 수 있다.</li>
</ul>
<h1 id="마무리-요약"><strong>마무리 요약</strong></h1>
<table>
<thead>
<tr>
<th><strong>항목</strong></th>
<th><strong>했던 일</strong></th>
</tr>
</thead>
<tbody><tr>
<td>EC2</td>
<td>Ubuntu 설치 + 서버 실행</td>
</tr>
<tr>
<td>PM2</td>
<td>백엔드 프로세스 관리</td>
</tr>
<tr>
<td>Nginx</td>
<td>리버스 프록시 + HTTPS</td>
</tr>
<tr>
<td>Route53</td>
<td>A레코드로 도메인 연결</td>
</tr>
<tr>
<td>Certbot</td>
<td>Let’s Encrypt 인증서 발급</td>
</tr>
<tr>
<td>RDS</td>
<td>MySQL 생성 + 보안 그룹 설정</td>
</tr>
<tr>
<td>Redis</td>
<td>세션 저장소</td>
</tr>
<tr>
<td>OAuth</td>
<td>3종 연동 + 세션 기반 로그인</td>
</tr>
<tr>
<td>CORS</td>
<td>쿠키 포함 설정</td>
</tr>
<tr>
<td>Session</td>
<td>redis + cookie 기반 인증</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[회원가입 중 실수 방지: Next.js에서 뒤로가기 UX 보호 설계]]></title>
            <link>https://velog.io/@cielo_hello/%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EC%A4%91-%EC%8B%A4%EC%88%98-%EB%B0%A9%EC%A7%80-Next.js%EC%97%90%EC%84%9C-%EB%92%A4%EB%A1%9C%EA%B0%80%EA%B8%B0-UX-%EB%B3%B4%ED%98%B8-%EC%84%A4%EA%B3%84</link>
            <guid>https://velog.io/@cielo_hello/%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85-%EC%A4%91-%EC%8B%A4%EC%88%98-%EB%B0%A9%EC%A7%80-Next.js%EC%97%90%EC%84%9C-%EB%92%A4%EB%A1%9C%EA%B0%80%EA%B8%B0-UX-%EB%B3%B4%ED%98%B8-%EC%84%A4%EA%B3%84</guid>
            <pubDate>Mon, 24 Feb 2025 13:24:11 GMT</pubDate>
            <description><![CDATA[<h1 id="코드-작성-계기">코드 작성 계기</h1>
<p>로잇의 회원가입 페이지는 총 3단계로 이루어져 있다.</p>
<p>회원가입을 완료하기 전에 사용자가 브라우저의 뒤로 가기 버튼을 누르면 기존에 입력한 데이터가 사라질 위험이 있다.</p>
<p>특히, 이메일 인증 과정이나 개인정보 작성을 진행하던 중 실수로 뒤로 가기를 누르면, 사용자는 다시 처음부터 입력해야 하는 불편함을 겪게 된다.</p>
<p>이러한 불편함이 반복되면 사용자 이탈률이 증가할 가능성이 높다고 판단했다.</p>
<p>따라서 사용자가 실수로 브라우저 뒤로 가기를 눌러도 기존 입력 데이터가 초기화되지 않도록 보호하는 방법이 필요했다.</p>
<p>이를 해결하기 위해, 뒤로 가기 버튼을 눌렀을 때 경고 모달을 띄우고, 사용자가 정말로 뒤로 갈지 선택할 수 있도록 하는 기능을 구현하기로 했다.</p>
<h1 id="해결-방법--routerbeforepopstate-활용"><strong>해결 방법 – <code>router.beforePopState</code> 활용</strong></h1>
<p>Next.js에서는 <code>router.beforePopState</code>를 활용하면 브라우저의 뒤로 가기 이벤트를 감지하여 원하는 동작을 실행할 수 있다.</p>
<p>이를 활용해 다음과 같은 기능을 구현할 수 있다.</p>
<ul>
<li>뒤로 가기 이벤트 감지 – 특정 조건에서만 뒤로 가기 차단</li>
<li>뒤로 가기를 차단하고 모달을 띄우기 – 사용자가 실수로 뒤로 가기를 눌렀을 때 경고 모달 표시</li>
<li>사용자가 확인을 누르면 정상적으로 이전 페이지로 이동하기</li>
</ul>
<p>이 글에서는 이 기능을 구현하는 과정에서의 시행착오와 최종 해결 방법을 기록하고,</p>
<p>Next.js에서 브라우저의 뒤로 가기 동작을 제어하는 방법을 정리해보려고 한다.</p>
<h1 id="nextjs의-공식-문서-분석--routerbeforepopstate"><strong>Next.js의 공식 문서 분석 – <code>router.beforePopState</code></strong></h1>
<p><a href="https://nextjs.org/docs/pages/api-reference/functions/use-router#routerbeforepopstate">Next.js 공식 문서</a>에서는 <code>router.beforePopState</code>를 활용하면 브라우저의 popstate 이벤트가 발생하기 전에 특정 동작을 실행할 수 있다고 설명하고 있다.</p>
<h2 id="routerbeforepopstatecb란"><strong><code>router.beforePopState(cb)</code>란?</strong></h2>
<p><code>router.beforePopState(cb)</code>는 브라우저에서 popstate 이벤트(뒤로 가기, 앞으로 가기 등)가 발생할 때 실행되는 콜백 함수를 등록하는 메서드다.</p>
<ul>
<li>콜백 함수 cb는 다음과 같은 객체를 매개변수로 받는다.</li>
</ul>
<pre><code>| **속성** | **설명** |
| --- | --- |
| url | 뒤로 가기 후 이동할 라우트 (예: /about → /home으로 이동하는 경우 url = &quot;/home&quot;) |
| as | 브라우저에 표시될 실제 URL (예: /home?ref=google 등) |
| options | `router.push()` 또는 `router.replace()`에서 전달된 추가 옵션 |</code></pre><ul>
<li>cb가 false를 반환하면 Next.js의 기본 popstate 동작이 실행되지 않는다.</li>
</ul>
<p>→ 이를 이용하면 사용자가 뒤로 가기를 눌렀을 때 Next.js가 자동으로 이전 페이지로 이동하는 것을 막고, 원하는 동작을 실행할 수 있을 거라고 판단했다.</p>
<h1 id="시행착오---routerbeforepopstate를-활용한-뒤로-가기-차단"><strong>시행착오 - <code>router.beforePopState</code>를 활용한 뒤로 가기 차단</strong></h1>
<p>처음에는 단순하게 “뒤로 가기 버튼을 누르면 <code>popstate</code> 이벤트가 발생하니까, 이를 감지해서 <code>beforePopState</code>에서 모달을 띄우면 되겠지?” 라고 생각했다.</p>
<p>그래서 아래와 같은 코드를 작성했다.</p>
<h3 id="오류가-발생한-코드">오류가 발생한 코드</h3>
<pre><code class="language-jsx">import { useRouter } from &quot;next/router&quot;;
import { useEffect } from &quot;react&quot;;
import { useModalStore } from &quot;@/stores/modalStore&quot;;

export default function useSignupNavigation() {
  const router = useRouter();
  const { openModal } = useModalStore();

  useEffect(() =&gt; {
    router.beforePopState(() =&gt; {
      // 모달을 띄운다
      openModal(&quot;confirmNavigationBack&quot;);
      return false; // 뒤로 가기 동작을 막는다
    });

    return () =&gt; {
      router.beforePopState(() =&gt; true); // cleanup
    };
  }, [router, openModal]);

  return {};
}</code></pre>
<p>실행하기 전까지 이 코드가 다음과 같이 실행할 거라고 생각했다.</p>
<ol>
<li><p>사용자가 브라우저의 뒤로 가기 버튼을 누른다.</p>
</li>
<li><p>router.beforePopState가 이를 감지한다.</p>
</li>
<li><p>return false;를 반환해 Next.js의 기본 뒤로 가기 동작을 막는다.</p>
</li>
<li><p>모달을 띄워 사용자의 확인을 받는다.</p>
</li>
</ol>
<p>하지만 예상과 다르게 동작했는데,</p>
<p>모달이 뜨면서 URL은 뒤로 가기가 동작하는데, 화면은 그대로 모달이 유지되는 이상한 상황이 발생했다.
<img src="https://velog.velcdn.com/images/cielo_hello/post/8ec34b65-a638-42c5-918d-732470ba9c3e/image.gif" alt=""></p>
<h1 id="브라우저의-뒤로-가기-동작을-정확히-이해하기"><strong>브라우저의 뒤로 가기 동작을 정확히 이해하기</strong></h1>
<p>이 문제를 해결하기 위해 뒤로 가기 버튼을 눌렀을 때 발생하는 일의 순서를 차례로 정리해봤다.</p>
<table>
<thead>
<tr>
<th><strong>순서</strong></th>
<th><strong>발생하는 이벤트</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td>1️⃣</td>
<td>사용자가 브라우저의 “뒤로 가기” 버튼을 클릭</td>
<td>뒤로 가기 이벤트 발생</td>
</tr>
<tr>
<td>2️⃣</td>
<td>브라우저가 히스토리 스택을 이동</td>
<td>/pageC → /pageB로 이동 (URL이 변경됨)</td>
</tr>
<tr>
<td>3️⃣</td>
<td><code>popstate</code> 이벤트 발생</td>
<td><code>window.onpopstate</code>가 실행됨</td>
</tr>
<tr>
<td>4️⃣</td>
<td>Next.js가 URL 변경을 감지</td>
<td><code>router.beforePopState()</code> 실행됨</td>
</tr>
<tr>
<td>5️⃣</td>
<td><code>router.beforePopState()</code>의 콜백이 실행됨</td>
<td>개발자가 정의한 로직이 실행됨</td>
</tr>
<tr>
<td>6️⃣</td>
<td><code>return true;</code>일 경우</td>
<td>Next.js가 자동으로 이전 페이지를 렌더링</td>
</tr>
<tr>
<td>7️⃣</td>
<td><code>return false;</code>일 경우</td>
<td>Next.js의 내부 라우팅은 취소되지만, URL 변경은 이미 발생한 상태</td>
</tr>
</tbody></table>
<p>분석결과 문제가 되는 부분은 순서 2번으로 <code>return false;</code>를 사용해도 브라우저 URL은 이미 변경된 상태가 된다는게 원인이었다.</p>
<h1 id="해결-방법--windowhistorypushstate로-url을-되돌리기"><strong>해결 방법 – <code>window.history.pushState()</code>로 URL을 되돌리기</strong></h1>
<p>사용자가 뒤로 가기 버튼을 클릭하면 브라우자가 히스토리 스택을 이동하며 URL이 변경되는데 이는 브라우저의 기본 동작이라서 막을 수 없다. 따라서 브라우저의 URL 변경을 방지하려면 <code>window.history.pushState()</code>를 사용하여 변경된 URL을 원래대로 되돌려야 한다. </p>
<h3 id="수정-코드">수정 코드</h3>
<pre><code class="language-jsx">import { useEffect } from &quot;react&quot;;
import { useRouter } from &quot;next/router&quot;;
import { useModalStore } from &quot;@/stores/modalStore&quot;;

export default function useSignupNavigation() {
  const router = useRouter();
  const { openModal } = useModalStore();

  useEffect(() =&gt; {
    router.beforePopState(() =&gt; {
      window.history.pushState(null, &quot;&quot;, router.asPath); // URL 변경 방지
      openModal(&quot;confirmNavigationBack&quot;); // 모달 띄우기
      return false; // Next.js의 기본 popstate 동작 차단
    });

    return () =&gt; {
      router.beforePopState(() =&gt; true); // cleanup
    };
  }, [router]);

  return {};
}</code></pre>
<p>이 코드는 아래와 같은 순서로 동작하는데, 이제 브라우저의 뒤로 가기를 눌러도 URL이 변경되지 않는다.</p>
<table>
<thead>
<tr>
<th><strong>순서</strong></th>
<th><strong>발생하는 이벤트</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td>1️⃣</td>
<td>사용자가 브라우저의 “뒤로 가기” 버튼을 클릭</td>
<td>뒤로 가기 이벤트 발생</td>
</tr>
<tr>
<td>2️⃣</td>
<td>브라우저가 히스토리 스택을 이동</td>
<td>/pageC → /pageB로 이동 (URL이 변경됨)</td>
</tr>
<tr>
<td>3️⃣</td>
<td>popstate 이벤트 발생</td>
<td><code>window.onpopstate</code>가 실행됨</td>
</tr>
<tr>
<td>4️⃣</td>
<td>Next.js가 URL 변경을 감지</td>
<td><code>router.beforePopState()</code> 실행됨</td>
</tr>
<tr>
<td>5️⃣</td>
<td>개발자가 <code>return false;</code>를 반환</td>
<td>Next.js의 내부 라우팅이 취소됨</td>
</tr>
<tr>
<td>6️⃣</td>
<td><code>window.history.pushState()</code> 실행</td>
<td>URL을 다시 /pageC로 되돌림</td>
</tr>
<tr>
<td>7️⃣</td>
<td>모달을 띄움</td>
<td>사용자에게 이전으로 돌아갈지 여부를 확인함</td>
</tr>
<tr>
<td>8️⃣</td>
<td>사용자가 “확인”을 선택한 경우</td>
<td><code>router.beforePopState(() =&gt; true);</code> 실행 후 <code>router.back();</code> 호출</td>
</tr>
<tr>
<td>9️⃣</td>
<td>뒤로 가기 정상 동작</td>
<td>브라우저가 /pageB로 이동</td>
</tr>
</tbody></table>
<h1 id="결론"><strong>결론</strong></h1>
<ul>
<li><code>router.beforePopState()</code>를 활용하면 브라우저 뒤로 가기 이벤트를 감지할 수 있다.</li>
<li>하지만 <code>return false;</code>를 사용해도 URL은 이미 변경된 상태가 된다.</li>
<li>이를 해결하려면 <code>window.history.pushState()</code>를 사용해 URL 변경을 되돌려야 한다.</li>
<li>이후 모달을 띄워 사용자에게 확인 요청 → 사용자가 확인하면 <code>router.back()</code> 실행할 수 있다.</li>
</ul>
<h2 id="사용자가-모달에서-확인을-누르면-정상적으로-뒤로-가기-실행"><strong>사용자가 모달에서 확인을 누르면 정상적으로 뒤로 가기 실행</strong></h2>
<p>이제, 사용자가 모달에서 “확인”을 눌렀을 때 정상적으로 이전 페이지로 이동하도록 구현하면 된다.</p>
<pre><code class="language-jsx">const handleBrowserBack = () =&gt; {
  router.beforePopState(() =&gt; true); // 뒤로 가기 차단 해제
  router.back(); // 이전 페이지로 이동
};</code></pre>
<ul>
<li><code>router.beforePopState(() =&gt; true);</code> → 기존에 막아놓았던 뒤로 가기 동작을 해제</li>
<li><code>router.back();</code> → 이전 페이지로 이동(이 함수는 사용자가  “이전으로 가기” 모달에서 확인 버튼을 눌렀을 때 실행되도록 함)</li>
</ul>
<h2 id="전체-코드-정리"><strong>전체 코드 정리</strong></h2>
<p>아래는 최종적으로 뒤로 가기를 감지하여 모달을 띄우고, 확인 버튼을 눌렀을 때 정상적으로 뒤로 가기를 수행하는 코드다.</p>
<pre><code class="language-jsx">import { useRouter } from &quot;next/router&quot;;
import { useEffect } from &quot;react&quot;;
import { useModalStore } from &quot;@/stores/modalStore&quot;;

export default function useSignupNavigation() {
  const router = useRouter();
  const { openModal, closeModal } = useModalStore();

  useEffect(() =&gt; {
    const handlePopState = () =&gt; {
      window.history.pushState(null, &quot;&quot;, router.asPath);
      openModal(&quot;confirmNavigationBack&quot;);
      return false;
    };

    router.beforePopState(handlePopState);

    return () =&gt; {
      router.beforePopState(() =&gt; true);
    };
  }, [router, openModal]);

  /**
   * &#39;이전으로 가기&#39; 모달에서 확인 버튼 클릭 시 실행되는 함수
   * - beforePopState의 반환값을 true로 변경해 뒤로 가기 차단 해제
   * - router.back()을 호출해 정상적으로 이전 페이지로 이동
   */
  const handleBrowserBack = () =&gt; {
    router.beforePopState(() =&gt; true);
    router.back();
  };

  return {
    handleBrowserBack,
    closeModal,
  };
}</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/4ea604e1-8be5-4ee0-86cf-2ced4825cb5d/image.gif" alt=""></p>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://nextjs.org/docs/pages/api-reference/functions/use-router#routerbeforepopstate">https://nextjs.org/docs/pages/api-reference/functions/use-router#routerbeforepopstate</a></li>
<li><a href="https://velog.io/@starplanter/Next.js%EC%97%90%EC%84%9C-%EB%92%A4%EB%A1%9C%EA%B0%80%EA%B8%B0-%EB%B0%A9%EC%A7%80-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0">https://velog.io/@starplanter/Next.js에서-뒤로가기-방지-구현하기</a></li>
</ul>
<hr>
<p>글을 다시 읽다가 궁금한 점이 생겨 더 알아본 부분을 추가로 작성한다.</p>
<h3 id="1-popstate-이벤트-발생-시-실제-주소창url은-바뀌는가">1. popstate 이벤트 발생 시, 실제 주소창(URL)은 바뀌는가?</h3>
<p>결론부터 말하면, <strong>실제로 브라우저 주소창은 바뀐다.</strong></p>
<ul>
<li><code>router.back()</code> 혹은 브라우저의 뒤로가기 버튼을 누르면<br>브라우저는 즉시 <code>window.history.back()</code>을 실행하고, 주소창을 업데이함</li>
<li>이 시점에서 주소창에는 이전 페이지 URL이 반영됨</li>
<li>다만, <code>beforePopState()</code> 내부에서 <code>pushState()</code>로 즉시 현재 URL을 다시 덮어쓰기 때문에 사용자 눈에는 바뀐 주소가 보이기 전에 되돌아간 것처럼 느껴짐</li>
</ul>
<p>즉, 주소는 실제로 바뀌지만, <strong>너무 빠르게 다시 복구되기 때문에 변화가 눈에 띄지 않는 것</strong>이다.</p>
<p>setTimeout으로 <code>pushState()</code>를 지연시키면 주소창이 바뀌는 것을 직접 확인할 수 있다.</p>
<pre><code class="language-ts">router.beforePopState(() =&gt; {
  setTimeout(() =&gt; {
    window.history.pushState(null, &quot;&quot;, router.asPath); // 300ms 지연
  }, 300);
  return false;
});</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/d9353e6c-28b0-4626-b82c-77f10d1b1274/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드 없이 시작하는 프론트 개발: MSW로 가입 플로우 설계한 방식]]></title>
            <link>https://velog.io/@cielo_hello/Next.js%EC%97%90%EC%84%9C-MSW%EB%A1%9C-API-%EC%97%86%EC%9D%B4-%ED%94%84%EB%A1%A0%ED%8A%B8-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cielo_hello/Next.js%EC%97%90%EC%84%9C-MSW%EB%A1%9C-API-%EC%97%86%EC%9D%B4-%ED%94%84%EB%A1%A0%ED%8A%B8-%EA%B0%9C%EB%B0%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 18 Feb 2025 09:07:44 GMT</pubDate>
            <description><![CDATA[<h1 id="사용-계기">사용 계기</h1>
<p>로잇 프로젝트를 개발하면서 원래는 API 개발이 늦어져도 UI만 먼저 간단히 작업하면 된다고 생각했지만, 회원가입 페이지가 3단계로 나뉘면서 상태 관리해야 할 요소가 많아져 코드 작성에 시간이 걸릴 것으로 예상됐습니다.</p>
<p>API가 완성될 때까지 기다리면, 프로젝트 막바지에 급하게 코드를 작성하는 일이 발생할 가능성이 높아 보였고, 이를 방지하기 위해 <strong>백엔드를 흉내내 Mocking API를 요청을 하는 방식으로 개발을 진행</strong>하기로 했습니다.</p>
<h1 id="mswmock-service-worker를-선택한-이유">MSW(Mock Service Worker)를 선택한 이유</h1>
<p>처음에는 JSON Server를 사용해 간단한 API 응답을 만들려고 했지만, 실제 서비스처럼 에러 처리(예: 모달 표시, 에러 메시지 출력)까지 구현 하기에는 한계가 있었습니다.</p>
<p>그래서 해결책을 찾던 중, Nock과 MSW(Mock Service Worker)를 발견했습니다.</p>
<p>결과적으로 향후 개발할 WebSocket API 기능까지 지원하고, 브라우저 환경에서도 API 요청을 처리할 수 있는 MSW를 선택했습니다.</p>
<h1 id="msw의-작동-원리">MSW의 작동 원리</h1>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/7934f196-a578-493d-a8d4-bf5578c8a27c/image.png" alt=""></p>
<p>출처: <a href="https://velog.io/@khy226/msw%EB%A1%9C-%EB%AA%A8%EC%9D%98-%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0">https://velog.io/@khy226/msw로-모의-서버-만들기</a></p>
<p>MSW는 API 요청을 가로채서 미리 정의된 응답을 반환하는 도구입니다.</p>
<p>브라우저 환경에서는 Service Worker(SW)를 이용해 네트워크 요청을 중간에서 가로채고, Node.js 환경에서는 <strong>Request Interceptor</strong>를 활용하여 요청을 처리합니다.</p>
<p>위 사진을 통해 설명하면 </p>
<blockquote>
<ol>
<li><strong>(요청 발생)</strong></li>
</ol>
<ul>
<li>사용자가 fetch 또는 axios 같은 HTTP 요청을 보냄</li>
<li>원래는 이 요청이 실제 백엔드 서버로 가야 함</li>
</ul>
<ol start="2">
<li><strong>(MSW의 서비스 워커가 요청을 가로챔)</strong><ul>
<li>브라우저에서 Service Worker로 동작</li>
<li>프론트엔드에서 보내는 API 요청을 <strong>네트워크 단계에서 가로채</strong>서 Mock 응답을 반환함</li>
<li>실제 서버와 통신하지 않고, 미리 정의된 핸들러를 기반으로 응답을 제공</li>
</ul>
</li>
<li><strong>(요청 핸들러에서 요청을 확인)</strong><ul>
<li>http.get(), http.post() 등의 핸들러를 설정하여 요청 URL과 메서드에 따라 특정 응답을 반환</li>
<li>예를 들어, /api/user에 대한 GET 요청이 오면 { name: &quot;Haeun Kim&quot; } 같은 응답을 줄 수 있음</li>
</ul>
</li>
<li><strong>(요청에 맞는 응답을 반환)</strong><ul>
<li>요청이 핸들러 목록과 매칭되면, 사전에 정의된 응답을 찾음</li>
<li>브라우저는 실제 서버에서 응답을 받은 것처럼 처리함</li>
</ul>
</li>
<li><strong>(클라이언트가 응답을 받음)</strong><ul>
<li>최종적으로 브라우저는 MSW가 반환한 Mock 응답을 실제 서버 응답처럼 받게 됨</li>
<li>이 과정에서 HTTP 상태 코드(200, 400, 500 등)와 JSON 데이터를 반환할 수 있음</li>
<li>예를 들어, HttpResponse.json({ userId: 1, name: &quot;Haeun Kim&quot; }, { status: 200 }) 같은 응답이 반환됨</li>
<li>따라서 백엔드 개발 없이도 프론트엔드 개발과 테스트가 가능해짐</li>
</ul>
</li>
</ol>
</blockquote>
<h1 id="msw-사용-방법">MSW 사용 방법</h1>
<p><a href="https://mswjs.io/docs/integrations/browser">공식 문서</a>를 참고해서 Next.js 프로젝트에서 MSW를 설정하고 사용하는 방법을 정리했습니다.</p>
<table>
<thead>
<tr>
<th><strong>단계</strong></th>
<th><strong>브라우저 환경</strong></th>
<th><strong>Node.js 환경 (SSR, Jest)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. 초기화</strong></td>
<td>setupWorker().start()로 서비스 워커 등록</td>
<td>setupServer().listen()으로 요청 인터셉트</td>
</tr>
<tr>
<td><strong>2. 요청 발생</strong></td>
<td>브라우저에서 API 요청 (fetch, axios)</td>
<td>서버 코드에서 API 요청 (getServerSideProps, test 등)</td>
</tr>
<tr>
<td><strong>3. 요청 가로채기</strong></td>
<td>서비스 워커가 요청을 가로채고 핸들러를 확인</td>
<td>HTTP 인터셉터가 요청을 가로채고 핸들러를 확인</td>
</tr>
<tr>
<td><strong>4. Mock 응답 반환</strong></td>
<td>미리 정의된 응답을 반환</td>
<td>테스트 코드에서 미리 정의된 응답을 반환</td>
</tr>
</tbody></table>
<h3 id="1-msw-라이브러리-설치"><strong>1. MSW 라이브러리 설치</strong></h3>
<p><code>npm install msw --save-dev</code></p>
<h3 id="2--서비스-워커-파일-생성-mockserviceworkerjs">2.  <strong>서비스 워커 파일 생성 (mockServiceWorker.js)</strong></h3>
<p>Next.js에서는 public 폴더 내 정적 파일을 브라우저에서 직접 접근할 수 있기 때문에, MSW의 서비스 워커 파일을 public 폴더에 생성해야 합니다.</p>
<pre><code class="language-jsx">npx msw init public --save</code></pre>
<ul>
<li>생성된 파일  </li>
</ul>
<pre><code>📂 public
┗ 📜 mockServiceWorker.js  ✅ (MSW 서비스 워커 파일)</code></pre><ul>
<li><p><strong>서비스 워커 등록 과정</strong></p>
<ol>
<li><p>브라우저가 mockServiceWorker.js를 public 폴더에서 가져옴</p>
</li>
<li><p>setupWorker().start() 실행 시 브라우저가 서비스 워커를 등록</p>
</li>
<li><p>이후 브라우저의 모든 네트워크 요청이 서비스 워커를 거치게 됨</p>
</li>
<li><p>MSW가 요청을 가로채고, 미리 정의된 Mock 응답을 반환</p>
</li>
</ol>
</li>
</ul>
<h3 id="3-api-핸들러-작성"><strong>3. API 핸들러 작성</strong></h3>
<p>API 요청을 가로채고 응답을 반환하는 핸들러를 작성합니다. 이 단계는 일단 틀만 만들어놓고 세부 내용은 가장 나중에 작성했습니다.</p>
<pre><code class="language-jsx">// mocks/handlers.js

import { http, HttpResponse } from &quot;msw&quot;;

async function checkUserExists(email) {
  const dummyUserDatabase = [&quot;test@example.com&quot;, &quot;user@domain.com&quot;];
  return dummyUserDatabase.includes(email);
}

export const handlers = [
  http.post(&quot;/api/send-email-code&quot;, async ({ request }) =&gt; {
    const { email } = await request.json();

    if (!email) {
      return HttpResponse.json(
        { error: &quot;이메일이 누락되었습니다.&quot; },
        { status: 400 },
      );
    }

    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      return HttpResponse.json(
        { error: &quot;잘못된 이메일 형식입니다.&quot; },
        { status: 400 },
      );
    }

    const existingUser = await checkUserExists(email);
    if (existingUser) {
      return HttpResponse.json(
        { error: &quot;이미 가입된 이메일입니다.&quot; },
        { status: 409 },
      );
    }

    return HttpResponse.json(
      { message: &quot;인증 코드가 전송되었습니다.&quot; },
      { status: 200 },
    );
  }),

 // 중략
];
</code></pre>
<h3 id="4-msw-실행-설정">4. MSW 실행 설정</h3>
<p>Next.js에서는 브라우저에서 worker를 실행하고, 서버에서 server를 실행해야 합니다.</p>
<ul>
<li><p>브라우저 실행 (CSR)</p>
<pre><code class="language-jsx">  // mocks/browser.ts

  import { setupWorker } from &#39;msw/browser&#39;
  import { handlers } from &#39;./handlers&#39;

  export const worker = setupWorker(...handlers)</code></pre>
</li>
<li><p>서버 실행(SSR)</p>
<pre><code class="language-jsx">  // mocks/server.ts

  import { setupServer } from &quot;msw/node&quot;;
  import { handlers } from &quot;./handlers&quot;;

  export const server = setupServer(...handlers);</code></pre>
</li>
</ul>
<h3 id="5-msw-실행을-위한-설정-함수-추가"><strong>5. MSW 실행을 위한 설정 함수 추가</strong></h3>
<p>Next.js에서 MSW를 자동으로 실행하기 위한 파일을 생성합니다.</p>
<pre><code class="language-jsx">// mocks/index.ts

export async function initMsw() {
  if (typeof window === &quot;undefined&quot;) {
    // 서버에서 실행
    const { server } = await import(&quot;./server&quot;);
    server.listen();
  } else {
    // 클라이언트에서 실행
    const { worker } = await import(&quot;./browser&quot;);
    await worker.start();
  }
}</code></pre>
<h3 id="6-nextjs에서-msw-활성화"><strong>6. Next.js에서 MSW 활성화</strong></h3>
<p>MSW를 Next.js에서 사용하려면 app.tsx 또는 providers.tsx에서 실행해야 합니다.</p>
<pre><code class="language-jsx">if (process.env.NODE_ENV === &quot;development&quot;) {
  import(&quot;../mocks&quot;).then(({ initMsw }) =&gt; initMsw());
}</code></pre>
<h1 id="결과">결과</h1>
<h3 id="브라우저에서-api-요청-테스트">브라우저에서 API 요청 테스트</h3>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/dd604c1d-b2c6-49d4-98d7-02eb18b2c6ee/image.png" alt=""></p>
<h3 id="정상-응답">정상 응답</h3>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/28d0904b-d7fd-45fc-aa26-fe4a0ffded84/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/fe2d8706-e81b-4636-b555-2a7a9217973d/image.png" alt=""></p>
<h3 id="이미-가입된-이메일-요청인-경우-에러-반환">이미 가입된 이메일 요청인 경우 에러 반환</h3>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/edc89714-0ec3-4dce-86e7-4ca8e532f9f2/image.png" alt=""></p>
<h1 id="회고">회고</h1>
<ul>
<li>API가 준비될 때까지 기다리는 것보다 MSW를 활용하는 것이 훨씬 효율적이었음</li>
<li>특히, 3단계 회원가입 페이지처럼 상태 관리가 중요한 경우 API가 없어도 미리 개발하고 테스트할 수 있어서 유용했음</li>
<li>현재는 SSR을 활용한 페이지가 없지만, 향후 최적화 과정에서 SSR을 적용할 계획이므로, Node.js 환경에서의 MSW 설정도 유용하게 활용될 것으로 예상됨</li>
<li>결론적으로, MSW는 개발 생산성을 높이고, 프론트엔드와 백엔드의 의존성을 줄여줘서 사용할 만한 가치가 있었음</li>
</ul>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://mswjs.io/docs/integrations/browser">https://mswjs.io/docs/integrations/browser</a></li>
<li><a href="https://oliveyoung.tech/2024-01-23/msw-frontend/">https://oliveyoung.tech/2024-01-23/msw-frontend/</a></li>
<li><a href="https://deswaq.tistory.com/55">https://deswaq.tistory.com/55</a></li>
<li><a href="https://velog.io/@khy226/msw%EB%A1%9C-%EB%AA%A8%EC%9D%98-%EC%84%9C%EB%B2%84-%EB%A7%8C%EB%93%A4%EA%B8%B0">https://velog.io/@khy226/msw로-모의-서버-만들기</a></li>
</ul>
<hr>
<h1 id="추가-설명-및-ssr-환경에서의-setupserver-사용에-대한-고찰">[추가 설명 및 SSR 환경에서의 <code>setupServer()</code> 사용에 대한 고찰]</h1>
<p>본 글에서는 <code>setupServer()</code>를 <code>provider.tsx</code>에서 초기화하여 SSR 환경에서도 MSW를 적용하려는 시도를 했습니다. 하지만 이후 공식 문서와 실행 구조를 분석하면서 아래와 같은 문제점이 있다는 것을 확인했습니다:</p>
<ol>
<li><code>setupServer()</code>는 <strong>Node.js 환경에서만 동작</strong>하며, 브라우저 환경에서는 무의미합니다.</li>
<li>SSR에서 <code>getServerSideProps</code>나 API Route가 <strong>요청마다 실행되는 함수</strong>인 반면,
<code>setupServer()</code>는 <strong>전역 싱글톤 객체</strong>이기 때문에, 요청마다 실행하면 핸들러가 중복 등록되어 충돌이 발생할 수 있습니다.</li>
<li>Node.js 실행 컨텍스트를 격리하지 않는 이상, SSR 환경에서 안정적으로 mock 서버를 구성하는 것은 어렵습니다.</li>
</ol>
<p>따라서 현재 기준으로 MSW는 SSR에서 실시간 mock 대응보다는<br><strong>테스트 환경(Jest, Vitest 등) 또는 CSR 개발 환경에서 mock 개발 지원 용도로 사용하는 것이 안정적</strong>이라는 결론을 내렸습니다.</p>
<p><strong>요약하면</strong></p>
<ul>
<li>SSR에서의 <code>setupServer()</code> 사용은 <strong>이론적으로 가능하지만</strong>, 실무에서는 <strong>충돌과 부작용이 많아 비추천</strong>됩니다.</li>
<li>구조적으로 브라우저 환경(CSR)과 Node.js 환경(SSR)의 mock 전략을 <strong>명확히 분리해서 설계하는 것이 필요</strong>합니다.</li>
<li>해당 글 내용은 당시의 학습 과정 중 시도였으며, 현재는 위 내용을 바탕으로 코드를 다시 작성하고 있습니다.</li>
</ul>
<p>참고로 <a href="https://m.blog.naver.com/dlaxodud2388/223433157608">이 글</a>처럼 모킹을 위한 미들웨어 서버를 하나 더 띄우는 것도 하나의 방법이 될 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js에서 네이버 지도 API 사용하기]]></title>
            <link>https://velog.io/@cielo_hello/Next.js%EC%97%90%EC%84%9C-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%A7%80%EB%8F%84-API-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cielo_hello/Next.js%EC%97%90%EC%84%9C-%EB%84%A4%EC%9D%B4%EB%B2%84-%EC%A7%80%EB%8F%84-API-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 27 Jan 2025 11:05:21 GMT</pubDate>
            <description><![CDATA[<p>Next.js에서 <strong>Script 컴포넌트</strong>를 이용해 네이버 지도 API를 동적으로 로드하고, 네이버 지도를 생성하는 방법을 정리해보았습니다.</p>
<h2 id="네이버-지도-구현의-주요-3단계">네이버 지도 구현의 주요 3단계</h2>
<ol>
<li><strong>외부 스크립트 동적으로 로드</strong><ul>
<li>네이버 지도 API 스크립트를 동적으로 로드</li>
</ul>
</li>
<li><strong>네이버 지도를 삽입할 DOM 설정</strong><ul>
<li>지도 렌더링을 위한 DOM 요소를 <code>useRef</code>로 관리</li>
</ul>
</li>
<li><strong>설정한 DOM에 네이버 지도 생성</strong><ul>
<li>네이버 지도 API를 사용해 지도를 초기화 및 렌더링</li>
</ul>
</li>
</ol>
<p>각 단계에서 만난 <strong>의문점(❓)</strong>과 <strong>이슈(🚧)</strong>를 함께 기록하면서 문제를 어떻게 해결했는지도 함께 작성해 보았습니다.</p>
<hr>
<h2 id="1-script-컴포넌트-네이버-지도-api-동적-로드"><strong>1. Script 컴포넌트: 네이버 지도 API 동적 로드</strong></h2>
<p><code>Next.js</code>에서 네이버 지도 API를 동적으로 로드하려면 <strong>Script 컴포넌트</strong>를 사용해야 합니다. 아래는 네이버 지도 API를 <code>strategy=&quot;afterInteractive&quot;</code> 옵션과 함께 로드하는 방법입니다.</p>
<pre><code class="language-jsx">import Script from &quot;next/script&quot;;

export default function Dashboard() {
  return (
    &lt;&gt;
      &lt;Script
        src=&quot;https://example.com/script.js&quot;
        strategy=&quot;afterInteractive&quot;
      /&gt;
    &lt;/&gt;
  );
}
</code></pre>
<ul>
<li><strong><code>strategy</code> 옵션</strong>:<ul>
<li><strong><code>beforeInteractive</code></strong>: 초기 렌더링 전에 스크립트를 로드. 모든 페이지에서 전역적으로 적용할 경우에 사용</li>
<li><strong><code>afterInteractive</code></strong>: 렌더링이 끝난 후 스크립트를 로드. 특정 페이지에서만 필요한 비필수 스크립트에 적합</li>
</ul>
</li>
</ul>
<h3 id="❓-스크립트를-동적-로드해야-하는-이유"><strong>❓ 스크립트를 동적 로드해야 하는 이유</strong></h3>
<ul>
<li>성능 최적화: 네이버 지도 API는 약 88.3KB의 스크립트로, 크기가 크진 않지만, 지도 기능이 필요 없는 페이지에서는 로드하지 않음으로써 네트워크 요청과 초기 로딩 시간을 줄일 수 있음</li>
<li>리소스 관리: 지도 API 스크립트는 모든 페이지에서 필요하지 않기 때문에, 필요한 페이지에서만 동적으로 로드하는 것이 성능 관점에서 더 효율적임</li>
</ul>
<hr>
<h2 id="2-useref로-dom-설정-네이버-지도-렌더링-준비"><strong>2. useRef로 DOM 설정: 네이버 지도 렌더링 준비</strong></h2>
<p>네이버 지도를 렌더링하려면 <strong>지도를 삽입할 DOM 요소</strong>를 지정해야 합니다. 여기서는 React의 <code>useRef</code>를 사용해 DOM 요소를 참조합니다.</p>
<pre><code class="language-jsx">export default function NaverMap({ coords }: { coords: TCoords }) {
  const mapRef = useRef&lt;HTMLDivElement | null&gt;(null);

  return (
    &lt;&gt;
      &lt;Script
        src=&quot;https://example.com/script.js&quot;
        strategy=&quot;afterInteractive&quot;
      /&gt;
      &lt;div className=&quot;naver-map&quot; ref={mapRef} /&gt;
    &lt;/&gt;
  );
}
</code></pre>
<h3 id="❓-왜-useref를-사용하는가"><strong>❓ 왜 <code>useRef</code>를 사용하는가?</strong></h3>
<p><code>id</code>를 사용할 수도 있지만, React에서는 <code>useRef</code>가 더 안전하고 권장됩니다. </p>
<table>
<thead>
<tr>
<th><strong>특성</strong></th>
<th><strong><code>useRef</code></strong></th>
<th><strong><code>id</code></strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>React와 동기화</strong></td>
<td>React 렌더링 모델과 자연스럽게 동기화됨.</td>
<td>DOM 렌더링 여부와 React 상태가 불일치할 가능성.</td>
</tr>
<tr>
<td><strong>조건부 렌더링</strong></td>
<td>렌더링 후 자동으로 <code>ref</code>에 설정.</td>
<td>렌더링 조건에 따라 DOM 요소를 찾지 못할 수 있음.</td>
</tr>
<tr>
<td><strong>안전성</strong></td>
<td>컴포넌트 내부적으로 안전하게 관리.</td>
<td>전역적으로 <code>id</code> 중복 가능성으로 예기치 않은 문제 발생.</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-initializemap-함수-네이버-지도-생성"><strong>3. initializeMap 함수: 네이버 지도 생성</strong></h2>
<p><code>initializeMap</code> 함수는 설정한 DOM(<code>mapRef.current</code>)에 네이버 지도를 생성하고, 마커와 이벤트 리스너를 추가합니다.</p>
<pre><code class="language-tsx">export const initializeMap = (
  mapRef: MutableRefObject&lt;HTMLDivElement | null&gt;,
  coords: TCoords,
) =&gt; {
  const { latitude, longitude } = coords;

  if (mapRef.current &amp;&amp; window.naver) {
    // 지도 옵션 설정
    const mapOptions = {
      center: new window.naver.maps.LatLng(latitude, longitude),
      zoom: 15,
      minZoom: 12,
    };

    // 네이버 지도 생성
    const map = new window.naver.maps.Map(mapRef.current, mapOptions);

    // 마커 설정
    const marker = new window.naver.maps.Marker({
      position: new window.naver.maps.LatLng(latitude, longitude),
      map: map,
      icon: {
        url: &quot;/images/lawyers/mapMarker.svg&quot;,
        size: new naver.maps.Size(24, 30),
      },
    });

    // 이벤트 리스너 설정
    window.naver.maps.Event.addListener(marker, &quot;click&quot;, () =&gt; {
      const redirectUrl = generateNaverMapLink(coords);
      window.open(redirectUrl, &quot;_blank&quot;);
    });
  } else {
    console.error(NAVER_MAP_ERRORS.containerNotFound);
  }
};
</code></pre>
<hr>
<h2 id="4-script의-onload와-useeffect-동작-문제-해결"><strong>4. Script의 onLoad와 useEffect: 동작 문제 해결</strong></h2>
<h3 id="🚧-문제-1-windownaver가-정의되지-않음"><strong>🚧 문제 1: <code>window.naver</code>가 정의되지 않음</strong></h3>
<ul>
<li><code>initializeMap</code>이 실행될 때 <code>window.naver</code>가 아직 로드되지 않아서 에러 발생</li>
</ul>
<h3 id="해결책-script의-onload-사용"><strong>해결책: Script의 <code>onLoad</code> 사용</strong></h3>
<p><code>onLoad</code>를 활용하여 스크립트가 로드된 이후에만 <code>initializeMap</code>을 호출</p>
<pre><code class="language-jsx">&lt;Script
  src=&quot;https://example.com/script.js&quot;
  strategy=&quot;afterInteractive&quot;
  onLoad={() =&gt; {
    if (mapRef.current) {
      initializeMap(mapRef, coords);
    }
  }}
/&gt;
</code></pre>
<hr>
<h3 id="🚧-문제-2-탭-전환-후-지도가-렌더링되지-않음"><strong>🚧 문제 2: 탭 전환 후 지도가 렌더링되지 않음</strong></h3>
<ul>
<li><code>onLoad</code>는 스크립트 로드 시 한 번만 실행되므로, 탭 전환 후 다시 렌더링될 때 <code>initializeMap</code>이 호출되지 않음</li>
</ul>
<h3 id="해결책-useeffect로-상태-변화-처리"><strong>해결책: <code>useEffect</code>로 상태 변화 처리</strong></h3>
<p><code>useEffect</code>를 사용해 컴포넌트가 다시 렌더링될 때도 <code>initializeMap</code>이 호출되도록 수정</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  if (window.naver &amp;&amp; mapRef.current) {
    initializeMap(mapRef, coords);
  }
}, [coords]);
</code></pre>
<hr>
<h2 id="최종-코드"><strong>최종 코드</strong></h2>
<p>아래는 개선된 최종 코드입니다.</p>
<pre><code class="language-tsx">export default function NaverMap({ coords }: { coords: TCoords }) {
  const mapRef = useRef&lt;HTMLDivElement | null&gt;(null);

  useEffect(() =&gt; {
    if (window.naver &amp;&amp; mapRef.current) {
      initializeMap(mapRef, coords);
    }
  }, [coords]);

  return (
    &lt;&gt;
      &lt;Script
        type=&quot;text/javascript&quot;
        src={NAVER_MAP_SCRIPT}
        strategy=&quot;afterInteractive&quot;
        onLoad={() =&gt; {
          if (mapRef.current) {
            initializeMap(mapRef, coords);
          } else {
            console.error(NAVER_MAP_ERRORS.apiNotLoaded);
          }
        }}
        onError={() =&gt; {
          console.error(NAVER_MAP_ERRORS.scriptLoadFailed);
        }}
      /&gt;
      &lt;div className={cx(&quot;naver-map&quot;)} ref={mapRef} /&gt;
    &lt;/&gt;
  );
}</code></pre>
<hr>
<h2 id="결과"><strong>결과</strong></h2>
<ol>
<li><p><strong>스크립트 동적 로드</strong></p>
<p> 성능 최적화 및 필요 페이지에서만 로드
 <img src="https://velog.velcdn.com/images/cielo_hello/post/526e233f-cc2e-4afc-8a7a-47122f6e0b21/image.png" alt=""></p>
</li>
<li><p><strong>DOM 설정 및 지도 렌더링</strong></p>
<p> 스크립트 로드 후 네이버 지도 API 정상 작동
 <img src="https://velog.velcdn.com/images/cielo_hello/post/5540074f-bc0c-4c7f-a23c-f7e28b24279e/image.png" alt=""></p>
</li>
</ol>
<pre><code>탭 전환 후에도 안정적으로 지도 생성

![](https://velog.velcdn.com/images/cielo_hello/post/24072c10-39a0-4bd6-b7de-07fbb0b74955/image.gif)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 12일차(RSC/RCC, 시스템 파일, 메타 데이터, 병렬 패칭, 캐싱)]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-12%EC%9D%BC%EC%B0%A8RSCRCC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%8C%8C%EC%9D%BC-%EB%A9%94%ED%83%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%91%EB%A0%AC-%ED%8C%A8%EC%B9%AD-%EC%BA%90%EC%8B%B1</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-12%EC%9D%BC%EC%B0%A8RSCRCC-%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%8C%8C%EC%9D%BC-%EB%A9%94%ED%83%80-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%91%EB%A0%AC-%ED%8C%A8%EC%B9%AD-%EC%BA%90%EC%8B%B1</guid>
            <pubDate>Sat, 03 Aug 2024 10:35:10 GMT</pubDate>
            <description><![CDATA[<h1 id="🧐-회고">🧐 회고</h1>
<h2 id="1-배운-것-핵심-내용--인사이트">1. 배운 것 (핵심 내용 &amp; 인사이트)</h2>
<ul>
<li><strong>RSC와 RCC의 개념과 차이</strong>: 서버 컴포넌트와 클라이언트 컴포넌트의 역할과 사용법을 명확히 이해하게 됨</li>
<li><strong>Next.js 14의 시스템 파일 종류와 특징</strong>: 다양한 시스템 파일(page, layout, error 등)의 역할을 학습하고, 이를 효율적으로 활용하는 방법을 익힘</li>
<li><strong>병렬 패칭 방법</strong>: Promise.all, 컴포넌트 분리, Suspense를 활용한 병렬 데이터 패칭 기법을 배움</li>
<li><strong>Next.js의 캐싱 시스템</strong>: Next.js의 다양한 캐싱 메커니즘과 작동 방식을 이해하고, 이를 통해 성능을 최적화하는 방법을 알게 됨</li>
</ul>
<h2 id="2-앞으로-더-조사해볼-내용">2. 앞으로 더 조사해볼 내용</h2>
<ul>
<li><strong>캐시와 관련된 직접 코드 실행</strong>: 학습한 내용을 코드로 직접 구현하고 테스트하여 실습 경험을 쌓기</li>
<li><strong>Next.js에서의 코드 분할 필요 여부</strong>: Next.js가 자동으로 코드 분할을 처리하는지, 추가적인 설정이 필요한지 조사하기</li>
<li><strong>리액트 트리 상단에서 RCC 사용 시 대처법</strong>: RCC를 리액트 트리 상단에서 사용할 때 발생할 수 있는 문제와 그 해결 방법을 더 알아보기</li>
</ul>
<h2 id="3-앞으로-적용해야겠다고-느낀-점">3. 앞으로 적용해야겠다고 느낀 점</h2>
<ul>
<li><strong>Next.js 14의 캐싱 시스템 활용</strong>: 데이터가 원하는 대로 업데이트되지 않을 때 캐싱 시스템을 활용하는 방법 익히기</li>
<li><strong>Suspense를 이용한 병렬 데이터 패칭</strong>: 효율적인 데이터 패칭을 위해 Suspense를 적극 활용하기</li>
</ul>
<h2 id="4-학습-평가-및-다짐목표">4. 학습 평가 및 다짐/목표</h2>
<p>Next.js 14와 관련된 새로운 기능과 개념을 잘 이해하고 이를 응용할 수 있는 능력을 키울 수 있었다. 배운 이론을 적절하게 적용할 수 있도록 실제 프로젝트를 해보고, 더 깊이 있는 연구와 실험을 해봐야 할 것 같다.</p>
<img src="https://velog.velcdn.com/images/cielo_hello/post/ad2ccd69-3cbd-4a11-ac00-45439dd4270d/image.png" width=50%/>


<hr>
<h1 id="📒-학습-내용">📒 학습 내용</h1>
<h1 id="nextjs에서-서버-컴포넌트와-클라이언트-컴포넌트">Next.js에서 서버 컴포넌트와 클라이언트 컴포넌트</h1>
<h2 id="서버-컴포넌트-rsc-react-server-component">서버 컴포넌트 (RSC, React Server Component)</h2>
<p>서버에서만 렌더링되고 클라이언트(브라우저)로 전송되지 않는 컴포넌트로, <strong>data fetching</strong>을 주로 담당함</p>
<ul>
<li><strong>사용법</strong>: 모든 컴포넌트는 기본적으로 서버 컴포넌트로 간주됨</li>
<li><strong>data fetching</strong>: <code>axios</code>나 <code>fetch</code> 등을 사용하여 데이터를 서버에서 요청할 수 있음</li>
<li><strong>비동기 처리</strong>: 컴포넌트에서 <code>async</code>와 <code>await</code>를 사용하여 비동기 작업을 처리할 수 있음</li>
<li><strong>훅 사용 불가</strong>: 서버 컴포넌트에서는 React 훅을 사용할 수 없음</li>
</ul>
<p><strong>➡️ RSC로 data fetching을 하는 이유</strong>
서버에서 데이터를 빠르게 가져와 클라이언트 측에서의 데이터 로딩 시간을 줄여 사용자 경험을 개선할 수 있음</p>
<h2 id="클라이언트-컴포넌트-rcc-react-client-component">클라이언트 컴포넌트 (RCC, React Client Component)</h2>
<p>클라이언트 컴포넌트는 브라우저에서 실행되며, <strong>사용자와의 상호작용</strong>을 처리함</p>
<ul>
<li><strong>사용법</strong>: 클라이언트 컴포넌트를 사용하려면 <code>use client</code> 지시어를 추가해야 함</li>
<li><strong>사용자 상호작용</strong>: 버튼 클릭, 폼 제출 등 사용자와의 직접적인 상호작용을 처리함</li>
<li><strong>훅 사용 가능</strong>: <code>useState</code>, <code>useEffect</code>와 같은 React 훅을 사용할 수 있음</li>
<li><strong>이중 렌더링</strong>: 클라이언트 컴포넌트는 서버와 클라이언트에서 각각 한 번씩 렌더링되며 이는 성능에 큰 영향을 주지 않음</li>
</ul>
<h2 id="컴포넌트-트리-구조">컴포넌트 트리 구조</h2>
<p>클라이언트 컴포넌트 밑에 서버 컴포넌트가 있으면 더 이상 서버 컴포넌트로서의 역할을 못 하므로
<strong>루트에 서버 컴포넌트</strong>를 두고 컴포넌트 트리의 <strong>말단으로 클라이언트 컴포넌트</strong>를 밀어 넣는 것이 좋음</p>
<p>정말 클라이언트 컴포넌트는 내부에서 서버 컴포넌트가 제 역할을 못하는지 알아보자.</p>
<h3 id="case-1-rsc---rcc">Case 1. RSC -&gt; RCC</h3>
<ul>
<li><strong>터미널:</strong> <code>console.log</code>로 메세지를 찍었을 때 터미널에도 RCC의 메세지가 나옴 -&gt; RCC는 서버에서도 렌더링 된다는 사실을 알 수 있음</li>
<li><strong>브라우저 콘솔:</strong> 브라우저의 콘솔에는 RSC의 메세지는 나오지 않고 RCC의 메세지만 나옴 -&gt; RSC는 서버에서만 렌더링 됐고, RCC는 클라이언트에서도 렌더링 된다는 사실을 알 수 있음
<img src="https://velog.velcdn.com/images/cielo_hello/post/63720001-5cc4-47d4-bc98-1f3ce5ba3e99/image.png" alt=""> </li>
</ul>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/cielo_hello/post/2791aacc-4392-45c1-81bd-68b4ea013faa/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/cielo_hello/post/33c50e1e-6363-40ed-b519-f8acf020d8d2/image.png" alt=""></th>
</tr>
</thead>
</table>
<h3 id="case-2-rcc---rsc">Case 2. RCC -&gt; RSC</h3>
<ul>
<li><strong>터미널:</strong> Case 1처럼 터미널에 나온 메세지를 미루어 보아 RSC, RCC 모두 서버에서 렌더링 됐음을 알 수 있음</li>
<li><strong>브라우저 콘솔:</strong> Case 1과 달리 RSC의 메세지가 브라우저 콘솔에 찍힘 -&gt; 서버에서만 렌더링 돼야 하는 RSC의 역할을 제대로 하지 못하는 것을 알 수 있음
<img src="https://velog.velcdn.com/images/cielo_hello/post/b7b0325f-6e73-4e1e-b05a-fc9c3ae3b6cc/image.png" alt=""></li>
</ul>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/cielo_hello/post/237b31fa-91f7-441a-b39b-4b340c34500c/image.png" alt=""></th>
<th><img src="https://velog.velcdn.com/images/cielo_hello/post/d82e0689-a353-47b2-ae70-dcede85dcb59/image.png" alt=""></th>
</tr>
</thead>
</table>
<h4 id="➕-참고하면-좋은-글">➕ 참고하면 좋은 글</h4>
<ul>
<li><a href="https://yozm.wishket.com/magazine/detail/2271/">새로 등장한 &#39;리액트 서버 컴포넌트&#39; 이해하기</a></li>
</ul>
<h1 id="section-02---시스템-파일과-메타데이터">Section 02 - 시스템 파일과 메타데이터</h1>
<h2 id="page">page</h2>
<p><code>page.tsx</code>: 라우트의 경로를 지정할 때 사용하는 페이지</p>
<h2 id="layout">layout</h2>
<p><code>layout.tsx</code>: Next.js는 각 라우트 경로마다 layout.tsx 파일을 지정하여 공통된 레이아웃을 처리해줄 수 있음</p>
<h3 id="루트-레이아웃">루트 레이아웃</h3>
<ul>
<li>하나의 Next.js 애플리케이션에서는 반드시 필수로 필요함</li>
<li>가장 최상단의 라우트 경로에 생성됨</li>
<li>Next.js 시스템에 의해 자동으로 생성됨<h3 id="라우트-레이아웃">라우트 레이아웃</h3>
</li>
<li>각 라우트 경로마다 개별적으로 설정 가능한 레이아웃</li>
<li>경로마다 생성하면 레이아웃을 중첩할 수 있음<img src="https://velog.velcdn.com/images/cielo_hello/post/3b23ceff-9d16-4f11-b4a1-661d5cbad2de/image.png" width=50%/>

</li>
</ul>
<h2 id="not-found">not-found</h2>
<p><code>not-found.tsx</code>: 페이지를 찾을 수 없을 때 보이는 페이지</p>
<ul>
<li>가장 최상위 즉, 루트의 not-found를 보여주는 것이 일반적임</li>
<li>경로 마다 다른 not-found를 보여주고 싶다면 <code>notFound</code>함수를 이용해야 함<ul>
<li>이 경우 가장 가까운 경로의 not-found를 보여줌
<code>if (params.id === &quot;2&quot;) notFound();</code></li>
</ul>
</li>
</ul>
<h2 id="error">error</h2>
<p><code>error.tsx</code>: 컴포넌트에서 에러가 발생했을 때 사용자에게 보여주는 커스텀 페이지</p>
<ul>
<li>‘use client’를 사용해야 하는 클라이언트 컴포넌트임</li>
<li>자신의 라우트 경로와 가장 가까이에 있는 <code>error.tsx</code> 컴포넌트를 우선 렌더링함</li>
<li><code>error</code>와 <code>reset</code> 객체를 props로 받을 수 있음<ul>
<li>error message를 이용할 수 있음</li>
<li>reset: 클라이언트 컴포넌트에서 발생한 error를 복구할 수 있음 -&gt; error가 발생한 페이지에 CSR을 다시 해줌</li>
</ul>
</li>
</ul>
<h2 id="loading">loading</h2>
<p><code>loading.tsx</code>: 컴포넌트의 로딩 상태를 관리해 주는 페이지</p>
<ul>
<li>자신의 라우트 경로와 가장 가까이에 있는 <code>loading.tsx</code> 컴포넌트를 우선 렌더링함</li>
<li><code>loading.tsx</code> 파일이 없으면 서버 컴포넌트 로딩 발생시 화면이 표시되지 않음</li>
</ul>
<h2 id="메타-데이터-meta-data">메타 데이터 (Meta Data)</h2>
<h3 id="seo-search-engine-optimization">SEO (Search Engine Optimization)</h3>
<p>검색 엔진 최적화, 검색 엔진에서 사이트의 노출도를 올리게 하는 코드 최적화 기법으로 크게 두가지 방법으로 나눌 수 있음</p>
<ol>
<li>메타 데이터 올바르게 사용하기</li>
<li>HTML 마크업 시맨틱하게 작성하기</li>
</ol>
<h3 id="nextjs-14에서-메타-데이터-정의-방법">Next.js 14에서 메타 데이터 정의 방법</h3>
<h4 id="1-정적-메타-데이터-객체">1. 정적 메타 데이터 객체</h4>
<p>metadata 변수로 정의한 객체를 내보내는 방법</p>
<pre><code class="language-js">export const metadata = {
    title: &quot;Meta Data Welcome!&quot;
}</code></pre>
<h4 id="2-동적-메타-데이터-생성-함수">2. 동적 메타 데이터 생성 함수</h4>
<p>약속된 함수 <code>generateMetadata()</code>를 정의해서 내보내는 방법으로 앞으로 더 공부해 볼 것!</p>
<h3 id="메타-데이터에서-사용할-수-있는-속성">메타 데이터에서 사용할 수 있는 속성</h3>
<p><a href="https://nextjs.org/docs/app/api-reference/functions/generate-metadata">Metadata Object and generateMetadata Options</a></p>
<h3 id="title에-동일한-텍스트-넣는-법">title에 동일한 텍스트 넣는 법</h3>
<ul>
<li>title을 템플릿 처럼 사용할 수 있는 방법</li>
<li>default는 필수 값으로 해당 페이지에 나타낼 텍스트도 됨<pre><code class="language-js">export const metadata: Metadata = {
title: { template: &#39;%s | 수코딩&#39;, default: &#39;Home | 수코딩&#39; },
description: &#39;Generated by create next app&#39;,
};</code></pre>
<h1 id="section-03---데이터-패칭">Section 03 - 데이터 패칭</h1>
<h2 id="병렬-fetching">병렬 fetching</h2>
<h3 id="방법-1-promiseall">방법 1. Promise.all</h3>
</li>
<li>요청을 병렬적으로 처리할 수 있음<pre><code class="language-jsx">async function getData() {
await new Promise((resolve) =&gt; setTimeout(resolve, 2000));
const res = await fetch(&#39;https://jsonplaceholder.typicode.com/posts&#39;);
const data = await res.json();
return data;
}
</code></pre>
</li>
</ul>
<p>async function getData2() {
  await new Promise((resolve) =&gt; setTimeout(resolve, 2000));
  const res = await fetch(&#39;<a href="https://jsonplaceholder.typicode.com/posts&#39;">https://jsonplaceholder.typicode.com/posts&#39;</a>);
  const data = await res.json();
  return data;
}</p>
<p>export default async function Home() {
  const [data, data2] = await Promise.all([getData(), getData2()]);</p>
<p>  return (
    &lt;&gt;
      {JSON.stringify(data, null, 2)}
      <hr />
      {JSON.stringify(data2, null, 2)}
    &lt;/&gt;
  );
}</p>
<pre><code>### 방법 2. 컴포넌트 분리 
- 요청을 병렬적으로 처리할 수 있음
- `loading.tsx` 컴포넌트 상태를 공유함
- 전체 로딩 상태는 가장 오래 걸리는 요청을 기준으로 결정됨

```jsx
// page.tsx
import Fetching1 from &#39;@/components/Fetching1&#39;;
import Fetching2 from &#39;@/components/Fetching2&#39;;

export default function Home() {
  return (
    &lt;&gt;
      &lt;Fetching1 /&gt;
      &lt;Fetching2 /&gt;
    &lt;/&gt;
  );
}</code></pre><pre><code class="language-jsx">// Fetching1.tsx
async function getData() {
  await new Promise((resolve) =&gt; setTimeout(resolve, 5000));
  const res = await fetch(&#39;https://jsonplaceholder.typicode.com/posts&#39;);
  const data = await res.json();
  return data;
}

export default async function Fetching1() {
  const data = await getData();
  return (
    &lt;&gt;
      &lt;h1&gt;Fetching1&lt;/h1&gt;
      {JSON.stringify(data, null, 2)}
    &lt;/&gt;
  );
}</code></pre>
<h3 id="방법-3-suspense">방법 3. Suspense</h3>
<ul>
<li>각각의 요청을 병렬적 + 개별적으로 로딩할 수 있음</li>
<li>데이터가 로드되는 동안 부분적으로 UI를 스트리밍할 수 있게 해줌</li>
</ul>
<pre><code class="language-jsx">import Fetching1 from &#39;@/components/Fetching1&#39;;
import Fetching2 from &#39;@/components/Fetching2&#39;;
import { Suspense } from &#39;react&#39;;

export default function Home() {
  return (
    &lt;&gt;
      &lt;h1&gt;Home&lt;/h1&gt;
      &lt;Suspense fallback={&lt;h1&gt;loading1...&lt;/h1&gt;}&gt;
        &lt;Fetching1 /&gt;
      &lt;/Suspense&gt;
      &lt;Suspense fallback={&lt;h1&gt;loading2...&lt;/h1&gt;}&gt;
        &lt;Fetching2 /&gt;
      &lt;/Suspense&gt;
    &lt;/&gt;
  );
}</code></pre>
<h4 id="➕-참고하면-좋은-글-1">➕ 참고하면 좋은 글</h4>
<p><a href="https://nextjs.org/learn/dashboard-app/streaming">Streaming</a></p>
<h2 id="nextjs의-캐싱-시스템">Next.js의 캐싱 시스템</h2>
<p>자주 사용하는 데이터를 메모리에 저장해놓고 빠르게 가져와서 쓰는 시스템</p>
<h4 id="➕-필독-글">➕ 필독 글</h4>
<p><a href="https://nextjs.org/docs/app/building-your-application/caching">Caching in Next.js</a></p>
<h4 id="💡-실무-팁">💡 실무 팁</h4>
<p>정확한 캐싱 적용을 위해서 개발자 모드는 끄고 코드 실행하기</p>
<h3 id="4가지-캐싱의-종류">4가지 캐싱의 종류</h3>
<ul>
<li>우선순위: Router Cache &gt; Full Route Cache &gt; Request Memoization &gt; Data Cache
<img src="https://velog.velcdn.com/images/cielo_hello/post/f337d81e-fe7a-4431-891a-3e66428bbd0d/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/58d79294-4bcd-41f1-ba5d-7a07941fd4ae/image.png" alt=""></p>
<h3 id="1-router-cache">1. Router Cache</h3>
<p>Next.js 애플리케이션에서 브라우저 측에서 작동하는 임시 데이터 저장 시스템으로 사용자가 페이지를 이동할 때 더 빠른 내비게이션을 제공할 수 있음 </p>
<p>사용자가 웹사이트를 탐색하는 동안 RSC Payload(React Server Component Payload)를 개별 라우트 세그먼트 단위로 나누어 브라우저의 임시 메모리에 저장함</p>
<p>저장된 데이터는 사용자 세션 동안 유지되며, 페이지 이동 시 서버 요청 없이 빠른 로딩을 가능하게 함
다만, 사용자가 웹사이트를 떠나거나 페이지를 새로 고침하면 이 캐시는 삭제됨</p>
<p><strong>📍RSC Payload (React Server Component Payload)</strong></p>
<ul>
<li>렌더링된 React 서버 컴포넌트 트리를 압축된 이진 형태로 표현한 데이터로 클라이언트에서 React가 브라우저의 DOM을 업데이트하는 데 사용됨</li>
<li>RSC Payload는 다음을 포함함<ul>
<li>서버 컴포넌트의 렌더링 결과</li>
<li>클라이언트 컴포넌트가 렌더링될 위치의 자리 표시자와 해당 JavaScript 파일에 대한 참조</li>
<li>서버 컴포넌트에서 클러이언트 컴포넌트로 전달된 모든 props</li>
</ul>
</li>
</ul>
<p>즉, 서버 컴포넌트의 렌더링 결과물을 포함하는 데이터를 의미하며, 주로 서버에서 클라이언트로 전송되는 HTML과 초기화된 상태 데이터를 포함함</p>
<h4 id="작동-방식">작동 방식</h4>
<ul>
<li>사용자가 라우트와 라우트를 이동할 때 즉, 내비게이션을 할 때 Next.js는 방문한 라우트 세그먼트를 캐시함</li>
<li>사용자가 이동할 라우트를 prefetch함(이는 주로 사용자의 화면에 보이는 <code>&lt;Link&gt;</code> 컴포넌트를 기반으로 예측됨)
<img src="https://velog.velcdn.com/images/cielo_hello/post/5c6bdc24-4570-4e87-9e47-36d01c7162e7/image.png" alt="">
(이 때 layout은 <code>link</code>때문이 아니므로 30초가 지나도 변하지 않음)</li>
</ul>
<h4 id="효과">효과</h4>
<p>사용자에게 향상된 내비게이션 경험을 제공할 수 있음</p>
<ul>
<li>방문한 라우트가 캐시되어 즉시 앞뒤로 이동할 수 있음</li>
<li>prefetching과 부분 렌더링 덕분에 새로운 라우트로 빠르게 이동할 수 있음</li>
<li>내비게이션 할 때 전체 페이지가 새로고침되지 않고 React 상태와 브라우저 상태가 유지됨</li>
</ul>
<h4 id="지속-시간">지속 시간</h4>
<p>캐시는 브라우저의 임시 메모리에 저장되는데 지속 시간은 두 가지 요인에 의해 결정됨</p>
<ol>
<li><p><strong>세션:</strong></p>
<ul>
<li>캐시는 내비게이션할 때는 유지되지만 페이지를 새로 고침하면 지워짐</li>
</ul>
</li>
<li><p><strong>자동 무효화 기간:</strong></p>
<ul>
<li>개별 세그먼트의 캐시는 일정 시간이 지나면 자동으로 무효화됩니다. 이 기간은 리소스가 미리 가져온 방식에 따라 달라집니다:<ul>
<li>기본 프리패칭(prefetch={null} 또는 미지정): 30초</li>
<li>전체 프리패칭(prefetch={true} 또는 router.prefetch): 5분</li>
</ul>
</li>
</ul>
</li>
</ol>
<p>페이지 새로 고침은 모든 캐시된 세그먼트를 지우지만, 자동 무효화 기간은 개별 세그먼트가 미리 가져온 시점부터 적용됩니다.</p>
<h4 id="캐시-무효화-방법">캐시 무효화 방법</h4>
<p>라우터 캐시를 무효화 하는 방법은 두 가지가 있음</p>
<ol>
<li><p><strong>Server Action</strong></p>
<ul>
<li><code>revalidatePath</code> 또는 <code>revalidateTag</code> 함수 이용</li>
</ul>
</li>
<li><p><strong><code>router.refresh</code> 호출</strong></p>
<ul>
<li><code>router.refresh</code>를 호출하면 라우터 캐시가 무효화되고, 현재 사용자가 보고 있는 페이지에 대해 서버로부터 최신 데이터를 다시 요청함</li>
<li>서버는 최신 데이터를 응답으로 보내고, 브라우저는 이 데이터를 사용하여 페이지를 업데이트함</li>
</ul>
</li>
</ol>
<h4 id="옵트-아웃">옵트 아웃</h4>
<ul>
<li>라우터 캐시를 완전히 비활성화할 수는 없음</li>
<li>따라서 위에 작성한 캐시 무효화 방법을 사용해야 함</li>
<li><code>&lt;Link&gt;</code> 컴포넌트의 <code>prefetch</code> 속성을 <code>false</code>로 설정하여 프리패칭을 비활성화할 수 있음<ul>
<li>하지만 이 경우에도:<ul>
<li>사용자가 한 번 방문한 경로 세그먼트는 30초 동안 임시로 저장됨</li>
<li>즉각적인 내비게이션이 가능함</li>
<li>방문한 라우트는 여전히 캐시됨</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>장점:</strong></p>
<ul>
<li><strong>빠른 내비게이션:</strong> 페이지 이동 시 캐시된 데이터를 사용하여 빠르게 로드할 수 있음</li>
<li><strong>서버 요청 감소:</strong> 자주 방문하는 페이지에 대해 반복적인 서버 요청을 줄여줌</li>
<li><strong>사용자 경험 향상:</strong> 빠른 페이지 로딩으로 더 나은 사용자 경험을 제공함</li>
</ul>
<h3 id="2-full-route-cache">2. Full Route Cache</h3>
<p>Next.js에서 서버 측에서 작동하는 캐시 시스템으로, HTML과 RSC Payload를 저장함</p>
<p>이는 사용자가 페이지를 요청할 때마다 서버에서 다시 렌더링하지 않고 캐시된 결과를 제공함으로써 빠른 로딩 속도를 제공함</p>
<p>Full Route Cache는 빌드 시 또는 재검증 시 정적으로 렌더링된 라우트를 서버에 캐시하며 이를 통해 서버 요청에 대한 렌더링 비용을 줄이고 성능을 향상시킴</p>
<h4 id="작동-방식-1">작동 방식</h4>
<ol>
<li><p><strong>서버에서의 React 렌더링:</strong></p>
<ul>
<li>Next.js는 React의 API를 사용하여 서버에서 렌더링 작업을 수행함</li>
<li>렌더링 작업은 개별 라우트 세그먼트와 서스펜스를 기준으로 나뉨</li>
<li>React는 서버 컴포넌트를 특수한 데이터 형식으로 렌더링하고 이를 RSC Payload라고 함</li>
<li>RSC Payload와 클라이언트 컴포넌트의 JavaScript를 사용하여 HTML을 서버에서 렌더링함</li>
</ul>
</li>
<li><p><strong>서버에서의 캐싱:</strong></p>
<ul>
<li>렌더링된 결과(RSC Payload와 HTML)를 서버에 캐시함</li>
<li>이는 정적으로 렌더링된 라우트에 적용됨</li>
</ul>
</li>
<li><p><strong>클라이언트에서의 React 하이드레이션 및 조정:</strong>
클라이언트에서 요청시, </p>
<ul>
<li>RSC와 RCC의 유저와 상호작용이 안되는 상태의 HTML을 빠르게 보여줌</li>
<li>RSC Payload를 사용하여 클라이언트와 렌더링된 서버 컴포넌트 트리를 조정하고 DOM을 업데이트함</li>
<li>JavaScript 명령을 사용하여 클라이언트 컴포넌트를 하이드레이트하고 애플리케이션을 상호작용 가능하게 만듦</li>
</ul>
</li>
<li><p><strong>클라이언트에서의 캐싱 (라우터 캐시)</strong>
위에서 언급한 라우터 캐시 과정을 통해 RSC Payload는 클라이언트 측 라우터 캐시에 저장됨</p>
</li>
<li><p><strong>이후 내비게이션 시</strong></p>
<ul>
<li>이후의 내비게이션이나 프리패칭 중 라우터 캐시에 RSC payload가 저장돼 있는지 확인하고 저장돼 있다면 서버에 새로운 요청을 보내지 않음</li>
<li>만약 라우트 세그먼트가 캐시에 없으면 RSC Payload를 서버에서 가져온 후 클라이언트 라우터 캐시에 저장함</li>
</ul>
</li>
</ol>
<p><strong>📍정적 렌더링 vs. 동적 렌더링</strong>
라우트가 빌드 시에 캐시되는지 여부는 해당 라우트가 정적으로 렌더링되는지 동적으로 렌더링되는지에 따라 다름
정적 라우트는 기본적으로 캐시되지만, 동적 라우트는 요청 시에 렌더링되며 캐시되지 않음</p>
<p>아래 그림은 정적으로 렌더링된 라우트와 동적으로 렌더링된 라우트 간의 차이를, 캐시된 데이터와 캐시되지 않은 데이터를 통해 보여줌
<img src="https://velog.velcdn.com/images/cielo_hello/post/86e84e3e-9c4b-47ad-aa74-7a62f2b4cd3a/image.png" width=70%/></p>
<h4 id="지속-시간-1">지속 시간</h4>
<p>기본적으로 Full Route Cache는 계속 유지됨
즉, 한 번 렌더링된 페이지 결과가 여러 사용자가 요청할 때마다 계속 사용된다는 뜻임</p>
<h4 id="캐시-무효화-방법-1">캐시 무효화 방법</h4>
<p>Full Route Cache를 무효화 하는 방법은 2가지가 있음</p>
<ol>
<li><strong>데이터 재검증:</strong><ul>
<li>Data Cache를 재검증(Revalidating)하면 라우터 캐시가 무효화되고, 서버에서 컴포넌트를 리렌더링하여 새로운 렌더링 결과를 캐시함</li>
</ul>
</li>
<li><strong>재배포:</strong><ul>
<li>Data Cache와 달리 새로운 배포 시 Full Route Cache는 삭제됨</li>
</ul>
</li>
</ol>
<h4 id="옵트-아웃-1">옵트 아웃</h4>
<p>Full Route Cache를 사용하지 않도록 설정해서 모든 요청마다 동적으로 컴포넌트를 렌더링 할 수 있음</p>
<ul>
<li>동적 함수 사용: 이 방법을 사용하면 해당 라우트를 풀 라우트 캐시에서 제외하고 요청 시마다 동적으로 렌더링할 수 있으며 Data Cache는 여전히 사용할 수 있음</li>
<li><code>dynamic = &#39;force-dynamic&#39;</code> 또는 <code>revalidate = 0</code> 라우트 세그먼트 설정 옵션을 사용하면 Full Route Cache와 Data Cache를 건너뛸 수 있음 <ul>
<li>즉, 서버에 들어오는 모든 요청마다 컴포넌트가 렌더링되고 데이터를 새로 가져옴</li>
<li>라우터 캐시는 클라이언트 측 캐시이므로 여전히 적용됨</li>
</ul>
</li>
<li>Data Cache 옵트 아웃: 만약 특정 라우트에 캐시되지 않는 fetch 요청이 있다면, 이 라우트는 Full Route Cache에서 제외됨<ul>
<li>해당 fetch 요청의 데이터는 들어오는 모든 요청마다 새로 가져오게 됨</li>
<li>옵트 아웃하지 않은 다른 fetch 요청은 여전히 Data Cache에 저장되며 이를 통해 캐시된 데이터와 캐시되지 않은 데이터를 혼합하여 사용할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="router-cache와-full-route-cache의-차이점">Router Cache와 Full Route Cache의 차이점</h3>
<ul>
<li>라우터 캐시는 사용자 세션 동안 React 서버 컴포넌트 페이로드를 브라우저에 임시로 저장하는 반면, 풀 라우트 캐시는 여러 사용자가 요청할 때마다 서버에 RSC Payload와 HTML을 저장해 두고, 동일한 데이터를 반복해서 사용함</li>
<li>풀 라우트 캐시는 정적으로 렌더링된 라우트만 캐시하는 반면, 라우터 캐시는 정적 및 동적으로 렌더링된 라우트 모두에 적용됩니다.</li>
</ul>
<h3 id="3-request-memoization">3. Request Memoization</h3>
<p>React 컴포넌트 트리 내에서 동일한 URL과 옵션으로 fetch 요청을 할 때 자동으로 메모이제이션하는 기능
이를 통해 동일한 데이터를 여러 곳에서 사용할 때 중복된 네트워크 요청을 피할 수 있음
<img src="https://velog.velcdn.com/images/cielo_hello/post/8df8bcd1-2335-4f79-8978-2235a5cc709b/image.png" width=70%/></p>
<h4 id="작동-방식-2">작동 방식</h4>
<ol>
<li><p><strong>첫 번째 요청:</strong></p>
<ul>
<li>처음 특정 요청이 호출되면 메모리에 저장되지 않아 캐시 미스가 발생함</li>
<li>함수가 실행되고 외부 데이터 소스에서 데이터를 가져와 메모리에 저장함</li>
</ul>
</li>
<li><p><strong>이후 요청:</strong></p>
<ul>
<li>동일한 요청이 동일한 렌더링 패스에서 다시 호출되면 캐시 히트가 발생하고 메모리에서 데이터를 반환함</li>
<li>렌더링 패스가 완료되면 메모리 초기화와 함께 메모이제이션 항목이 지워짐</li>
</ul>
<img src="https://velog.velcdn.com/images/cielo_hello/post/ee1f3ac9-04d2-43a6-9dcb-46bb7dae505d/image.png" width=70%/>

</li>
</ol>
<h4 id="지속-시간-2">지속 시간</h4>
<p>클라이언트가 서버에 요청을 보내고 서버가 그 요청에 대해 응답을 반환할 때까지 지속되며, React 컴포넌트 트리가 렌더링을 완료할 때까지 유지됨</p>
<h4 id="재검증">재검증</h4>
<p>Request Memoization은 서버 요청 간에 공유되지 않으므로 재검증이 필요 없음</p>
<h4 id="옵트-아웃-2">옵트 아웃</h4>
<ul>
<li>메모이제이션은 GET 메서드에만 적용됨</li>
<li>개별 요청을 관리하려면 AbortController의 signal 속성을 사용할 수 있음</li>
</ul>
<h3 id="4-data-cache">4. Data Cache</h3>
<p>Next.js에는 가져온 데이터를 서버 요청과 배포 후에도 계속 유지하는 기능이 있음
이 기능은 Next.js가 fetch 기능을 확장하여, 서버에서 요청할 때마다 데이터를 캐시에 저장할 수 있도록 하기 때문에 가능함</p>
<p>기본적으로, fetch를 사용하여 데이터를 요청하면 그 데이터는 캐시되며 fetch의 cache와 next.revalidate 옵션을 사용하여 캐싱 동작을 설정할 수 있음</p>
<h4 id="작동-방식-3">작동 방식</h4>
<ol>
<li><p><strong>첫 번째 fetch 요청:</strong></p>
<ul>
<li>렌더링 중에 fetch 요청이 처음 호출되면, Next.js는 Data Cache를 확인함</li>
<li>캐시가 있으면 즉시 반환하고 메모이제이션 함</li>
<li>캐시가 없으면 데이터 소스에 요청을 보내고 결과를 Data Cache에 저장하고 메모이제이션 함</li>
</ul>
</li>
<li><p><strong>캐시되지 않은 데이터:</strong></p>
<ul>
<li>캐시되지 않은 데이터(예: <code>{ cache: &#39;no-store&#39; }</code> 설정)는 항상 데이터 소스에서 가져오고 메모이제이션됨</li>
</ul>
</li>
</ol>
<p>데이터가 캐시되었든 캐시되지 않았든, 동일한 데이터를 반복해서 요청하는 것을 피하기 위해 모든 요청은 메모이제이션됨</p>
   <img src="https://velog.velcdn.com/images/cielo_hello/post/1b8b5dbd-cca8-4b6a-b95f-ae7a21f328b0/image.png" width=70%/>

<p><strong>📍 Data Cache와 Request Memoization의 차이점</strong>
Data Cache와 Request Memoization 둘 다 캐시된 데이터를 재사용하여 성능을 향상시키지만, Data Cache는 여러 요청과 배포에 걸쳐 지속되는 반면, 메모이제이션은 요청 과정 동안(클라이언트가 서버에 요청을 보내고 서버가 그 요청에 대해 응답을 반환할 때까지)만 지속됨</p>
<ul>
<li>Request Memoization을 사용하면 한 번의 렌더링 동안, 동일한 데이터를 여러 번 요청하는 경우 네트워크를 통해 데이터 캐시 서버(CDN 또는 엣지 네트워크)나 데이터 소스(데이터가 실제로 저장된 곳, 예: 데이터베이스 또는 CMS)에 중복된 요청을 보내는 것을 줄여줌<ul>
<li>예를 들어, 페이지를 렌더링하는 동안 동일한 데이터를 여러 컴포넌트에서 요청하면, 첫 번째 요청 후 메모이제이션된 데이터를 사용하여 다시 요청하지 않음</li>
</ul>
</li>
<li>Data Cache는 여러 요청과 배포에 걸쳐 여러 사용자 요청이나 애플리케이션 배포 후에도 원본 데이터 소스로 보내는 요청의 수를 줄여줌<ul>
<li>예를 들어, 데이터를 한 번 캐시에 저장하면, 이후의 요청에서는 원본 데이터 소스가 아닌 캐시에서 데이터를 가져옴</li>
</ul>
</li>
</ul>
<p>요약하면, Request Memoization은 한 번의 렌더링 과정에서 동일한 데이터를 여러 번 요청하지 않도록 도와주고,
Data Cache는 여러 사용자 요청이나 애플리케이션 배포 후에도 데이터를 캐시에 저장하여 원본 데이터 소스로의 요청을 줄여줌</p>
<h4 id="지속-시간-3">지속 시간</h4>
<p>Data Cache는 기본적으로 계속 유지되며, 재검증하거나 옵트 아웃하지 않는 한 유지됨</p>
<h4 id="재검증-1">재검증</h4>
<ol>
<li><strong>시간 기반 재검증:</strong><ul>
<li>일정 시간이 지나고 새로운 요청이 있을 때 데이터를 재검증함</li>
<li>데이터가 자주 변경되지 않으면서 꼭 최신일 필요가 없는 경우에 유용함</li>
</ul>
</li>
</ol>
<pre><code class="language-js">// Revalidate at most every hour
fetch(&#39;https://...&#39;, { next: { revalidate: 3600 } })</code></pre>
  <img src="https://velog.velcdn.com/images/cielo_hello/post/f93eddf8-e1ce-4fdd-b182-6fed51729542/image.png" width=70% />

<ol start="2">
<li><strong>온디맨드 재검증:</strong><ul>
<li>이벤트 기반으로 데이터를 재검증함</li>
<li>예를 들어, 폼 제출 시 태그(<code>revalidateTag</code>) 또는 경로(<code>revalidatePath</code>)에 따라 데이터를 재검증할 수 있음</li>
<li>최신 데이터를 가능한 한 빨리 표시해야 할 때 유용함</li>
</ul>
</li>
</ol>
<img src="https://velog.velcdn.com/images/cielo_hello/post/a6d27bd7-a636-4ca9-ad99-f13c16bf19e0/image.png" width=70%/>


<h4 id="옵트-아웃-3">옵트 아웃</h4>
<ul>
<li>fetch 요청에서 <code>cache: &#39;no-store&#39;</code> 설정으로 캐싱을 비활성화할 수 있음<pre><code class="language-js">// 개별 `fetch` 요청에 대해 캐싱을 비활성화
fetch(`https://...`, { cache: &#39;no-store&#39; })</code></pre>
</li>
<li>특정 라우트 세그먼트에 대해 캐싱을 비활성화할 수 있음<ul>
<li>해당 라우트 세그먼트에서 발생하는 모든 데이터 요청이 캐시되지 않으며, 서드 파티 라이브러리의 요청도 포함함<pre><code class="language-js">// 라우트 세그먼트 내의 모든 데이터 요청에 대해 캐싱을 비활성화
export const dynamic = &#39;force-dynamic&#39;</code></pre>
</li>
</ul>
</li>
</ul>
<h4 id="📌-캐시-깔끔하게-지우는-법">📌 캐시 깔끔하게 지우는 법</h4>
<ol>
<li>서버 끄기</li>
<li>about:blank로 이동</li>
<li><code>.next.cache</code> 파일지우기</li>
<li>개발 서버 다시 구동</li>
<li><a href="http://localhost:3000%EC%9C%BC%EB%A1%9C">http://localhost:3000으로</a> 이동</li>
</ol>
<h4 id="💡-react의-fetch-api와-nextjs-14의-fetch-api의-차이">💡 React의 fetch API와 Next.js 14의 fetch API의 차이</h4>
<ul>
<li><strong>웹 API</strong>: 원래는 서버에서 사용할 수 없는 클라이언트 전용 API임</li>
<li><strong>Next.js의 확장</strong>: Next.js 팀이 기존 fetch API를 래핑하여 서버 컴포넌트에서도 동작하도록 만들었음 <ul>
<li><code>next</code> 속성을 추가하여 revalidate 같은 기능을 사용할 수 있게 함</li>
<li>따라서 이름은 같지만 React에서 사용하던 fetch와는 다름</li>
</ul>
</li>
<li><strong>Axios 필요 없음</strong>: Axios도 서버에서 사용할 수 있도록 만들어진 라이브러리 이지만, Next.js의 확장된 fetch는 캐싱 제어(opt-out, revalidate 등) 기능을 제공하므로 굳이 Axios를 사용할 필요가 없음</li>
</ul>
<h4 id="➕-더-알아볼-것">➕ 더 알아볼 것</h4>
<p>Next.js에서는 code splitting를 사용하지 않아도 되나?</p>
<p>——————————————————————————
본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 과정(B-log) 리뷰로 작성 되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 7일차(폰트, 컴포넌트)]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-7%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-7%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Sun, 28 Jul 2024 13:39:51 GMT</pubDate>
            <description><![CDATA[<h1 id="🧐-회고">🧐 회고</h1>
<h2 id="1-배운-것-핵심-내용--인사이트">1. 배운 것 (핵심 내용 &amp; 인사이트)</h2>
<ul>
<li><strong>컴포넌트 구조 및 이벤트 처리 방식:</strong> React의 기본 컴포넌트 구조와 이벤트 핸들링 방법을 배웠다.</li>
<li><strong>props:</strong> 읽기 전용 특성과 단방향 데이터 흐름에 대한 깊이 있게 조사했다.</li>
<li><strong>즉시 실행 함수로 조건부 렌더링:</strong> 즉시 실행 함수를 사용해서 조건부 렌더링을 하는 방법을 새롭게 알게 됐다.<h2 id="2-앞으로-더-조사해볼-내용">2. 앞으로 더 조사해볼 내용</h2>
</li>
<li><strong>Tailwind CSS:</strong> 커스텀 클래스 정의 및 효율적인 사용 방법을 더 고민해 봐야겠다.</li>
<li><strong>공통 컴포넌트 범용성:</strong> type과 스프레드 문법을 이용해서 컴포넌트의 범용성을 높이는 게 인상 깊었다. 이 외에 다른 벙법이 더 있는지 찾아볼 예정이다. (polymorphic components 공부하기)</li>
<li><strong><code>@import</code>와 <code>@font-family</code>의 차이점:</strong> 폰트를 적용할 때 <code>@import</code>와 <code>@font-face</code>를 통한 폰트 적용의 차이점과 각 방법의 장단점이 궁금해졌다.<h2 id="3-앞으로-적용해야겠다고-느낀-점">3. 앞으로 적용해야겠다고 느낀 점</h2>
</li>
<li><strong>twMerge:</strong> Tailwind CSS에서 중복 클래스 문제를 해결하고 코드 가독성을 높이기 위해 twMerge를 적극적으로 활용할 계획이다.</li>
<li><strong>Children:</strong> 재사용 가능한 컴포넌트를 설계할 때 children prop을 더 활용해야 겠다. 현재는 children을 활용할 수 있는 상황에도 일반 prop을 이용하는 경향이 있다.<h2 id="4-학습-평가-및-다짐목표">4. 학습 평가 및 다짐/목표</h2>
이번 학습을 통해 React와 CSS 관리의 기초적인 부분을 더욱 확실히 다질 수 있었다. 앞으로는 더 복잡한 상태 관리와 데이터 흐름을 관리하는 패턴들을 학습하고, 이를 실제 프로젝트에 적용해보고 싶다. 또한, 다양한 조건부 렌더링 기법을 익혀서, 복잡한 UI를 구현하고 코드의 가독성과 유지보수성을 높일 수 있도록 해야겠다.</li>
</ul>
<hr>
<h1 id="📒-학습-내용">📒 학습 내용</h1>
<h1 id="section-02---리액트-시작하기">Section 02 - 리액트 시작하기</h1>
<h4 id="💡-실무-팁">💡 실무 팁</h4>
<ul>
<li>package.json 파일은 아주 중요한 파일이므로 직접 수정하지 않도록 해야함</li>
<li>오류를 해결할 때 node_modules 폴더 내의 코드를 수정하지 않아야 함<ul>
<li>로컬에서는 정상적으로 동작할지라도 실제 환경에서는 문제가 발생할 수 있음</li>
<li>특히, <code>node_modules/</code>는 GitHub에 올리지 않기 때문에 실제 환경에서는 수정이 반영되지 않을 가능성이 높음</li>
</ul>
</li>
</ul>
<h2 id="폰트-적용-방법">폰트 적용 방법</h2>
<h3 id="구글-폰트">구글 폰트</h3>
<p><a href="https://fonts.google.com/">Google Fonts</a>에서 원하는 폰트를 선택하여 <code>@import</code>문을 사용해 CSS 파일에 추가하기</p>
<pre><code class="language-js">@import url(&quot;https://fonts.googleapis.com/css2?family=Inter:wght@100..900&amp;display=swap&quot;);

body {
  font-family: &quot;Inter&quot;, sans-serif;
}</code></pre>
<h3 id="그-외-사이트">그 외 사이트</h3>
<p>대표적으로 <a href="https://noonnu.cc/index">눈누</a> 사이트가 있음</p>
<p>웹 폰트를 <code>font-face</code>로 제공한다면 복사해서 사용하면 되지만 그렇지 않다면 직접 폰트를 로컬에 다운로드 해야함</p>
<ul>
<li>다운 받은 TTF 또는 OTF 파일을 woff로 변환 </li>
<li><code>/src/assets/fonts</code> 등의 경로에 저장</li>
<li><code>font-face</code> 등록<pre><code class="language-css">@font-face {
font-family: &quot;JejuDoldam&quot;; /* 사용할 폰트의 이름을 정의 */
src: url(&quot;./assets/fonts/EF_jejudoldam\(OTF\).woff2&quot;),
  url(&quot;./assets/fonts/EF_jejudoldam_OTF_.woff&quot;); /* 폰트 파일의 위치 */
}</code></pre>
</li>
</ul>
<h4 id="💡-실무-팁-1">💡 실무 팁</h4>
<ul>
<li>모든 다운로드 폰트는 저작권을 위반하지 않도록 항상 조심할 것!</li>
<li><code>.woff</code>는 더 널리 지원되지만, <code>.woff2</code>는 더 작은 파일 크기와 더 나은 성능을 제공함</li>
<li>대부분의 최신 웹 프로젝트에서는 두 형식을 모두 사용하여 다양한 브라우저를 지원함</li>
</ul>
<h4 id="➕-더-알아볼-것">➕ 더 알아볼 것</h4>
<p><code>@import</code>와 <code>@font-family</code>의 차이점</p>
<h2 id="컴포넌트">컴포넌트</h2>
<h3 id="이벤트">이벤트</h3>
<h4 id="이벤트-타입">이벤트 타입</h4>
<ul>
<li><strong>태그에서 사용</strong>: HTML 태그에 이벤트를 지정할 때는 이벤트 타입 앞에 <code>on</code>이 붙음</li>
<li><strong>카멜케이스 사용</strong>: HTML과 달리 React에서는 이벤트 핸들러를 지정할 때 카멜케이스(CamelCase) 표기법을 사용함</li>
<li><strong>인라인 이벤트 처리</strong>: React에서는 DOM 요소에 이벤트 리스너를 직접 추가할 수 없으며, 모든 이벤트를 인라인으로 설정해야 함. 따라서 <code>onClick</code>과 같은 이벤트 속성에 함수를 직접 전달함</li>
</ul>
<pre><code class="language-html">&lt;!--HTML에서 버튼 클릭 이벤트를 처리하는 방식--&gt;
&lt;button onclick=&quot;activateLasers()&quot;&gt;
  Activate Lasers
&lt;/button&gt;</code></pre>
<pre><code class="language-jsx">// React에서 버튼 클릭 이벤트를 처리하는 방식
&lt;button onClick={activateLasers}&gt;
  Activate Lasers
&lt;/button&gt;</code></pre>
<h4 id="기본-동작-방지">기본 동작 방지</h4>
<p>HTML에서는 이벤트 핸들러에서 <code>false</code>를 반환하여 기본 동작을 방지할 수 있음. 
예를 들어, 폼 제출을 막기 위해 <code>return false</code>를 사용함</p>
<pre><code class="language-html">&lt;form onsubmit=&quot;console.log(&#39;You clicked submit.&#39;); return false&quot;&gt;
  &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
&lt;/form&gt;</code></pre>
<p>하지만 React에서는 <code>false</code>를 반환해도 기본 동작이 방지되지 않음.
기본 동작을 방지하려면 <code>preventDefault()</code> 메서드를 명시적으로 호출해야 함</p>
<pre><code class="language-jsx">function Form() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log(&#39;You clicked submit.&#39;);
  }

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
    &lt;/form&gt;
  );
}</code></pre>
<h4 id="이벤트-객체">이벤트 객체</h4>
<p>React 이벤트는 기본 HTML 이벤트와 유사하지만, React 이벤트 시스템으로 감싸져 있음. 
이 시스템은 크로스 브라우저 호환성을 보장하며, 추가적인 기능을 제공함</p>
<h4 id="타입스크립트에서의-이벤트-처리">타입스크립트에서의 이벤트 처리</h4>
<p>타입스크립트를 사용할 때, 이벤트 핸들러의 타입을 명시적으로 지정하지 않아도 자동으로 추론됨. 콜백 함수를 직접 작성하면 타입스크립트가 올바른 타입을 추론하여 적용함.</p>
<pre><code class="language-typescript">const onClickHandler = (event: React.MouseEvent&lt;HTMLButtonElement&gt;) =&gt; {
  alert(&#39;버튼이 클릭되었습니다.&#39;);
};</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/c0cc53cc-4a4e-41af-bde2-166aa360fd91/image.png" alt=""></p>
<h3 id="props">props</h3>
<p>컴포넌트 간에 데이터를 전달하는 방법
부모 컴포넌트가 자식 컴포넌트로 데이터를 전달하는 방법 
UI 구성 요소 간의 독립성과 재사용성을 높이는 핵심 개념</p>
<h4 id="특징">특징</h4>
<ul>
<li><p><strong>읽기 전용(Read-only)</strong>: Props는 자식 컴포넌트 내에서 수정할 수 없는 읽기 전용 데이터로 부모 컴포넌트가 전달한 값이 변경되면 자식 컴포넌트가 다시 렌더링됨
이 원칙은 컴포넌트의 예측 가능성을 유지하는 데 중요한 역할을 함</p>
</li>
<li><p><strong>단방향 데이터 흐름</strong>: Props는 부모에서 자식으로만 전달됨
자식 컴포넌트는 부모 컴포넌트의 상태나 데이터를 직접 변경할 수 없으며, 변경이 필요할 경우 부모에게 알려야 함</p>
</li>
<li><p><strong>다양한 데이터 타입</strong>: Props는 문자열, 숫자, 배열, 객체, 함수 등 다양한 데이터 타입을 전달할 수 있음
이로 인해 컴포넌트 간의 기능과 인터페이스를 유연하게 정의할 수 있음</p>
</li>
<li><p><strong>기본 값 설정</strong>: Props는 기본 값을 설정할 수 있음
이를 통해 부모 컴포넌트에서 특정 값을 전달하지 않을 경우에도 자식 컴포넌트가 예상 가능한 동작을 수행할 수 있음</p>
</li>
</ul>
<h4 id="➕-더-알아볼것">➕ 더 알아볼것</h4>
<p>&#39;읽기 전용&#39;, &#39;단방향 데이터 흐름&#39;이라고 했는데 useState의 setter를 props로 받으면 데이터를 변경할 수 있는 거 아닌가? </p>
<ul>
<li>부모 컴포넌트의 상태를 변경할 수 있는 함수(예: useState의 setter 함수)를 props로 전달하여, 자식 컴포넌트에서 부모 컴포넌트의 상태를 간접적으로 변경할 수 있음</li>
<li>이 경우, 자식 컴포넌트는 상태 자체를 변경하는 것이 아니라, 상태 변경을 요청하는 함수만 호출하는 것임</li>
<li>또한, 자식 컴포넌트가 props로 받은 함수를 수정하거나 교체하는 것이 아니라, 주어진 함수를 사용하는 것으로 자식 컴포넌트는 부모 컴포넌트의 상태나 그 상태를 변경하는 로직을 직접 제어하지 않음</li>
<li>이는 React의 단방향 데이터 흐름을 유지하면서도 상위 컴포넌트의 상태를 자식 컴포넌트가 조작할 수 있게 해줌</li>
</ul>
<h3 id="children">Children</h3>
<p>특정 태그의 컨텐츠 자체를 prop으로 넘겨주는 것
JSX 태그 사이에 있는 내용을 자식 컴포넌트로 전달함</p>
<ul>
<li>children을 사용하면 컴포넌트 내부에 어떤 내용을 표시할지 부모 컴포넌트가 결정할 수 있어, 재사용 가능한 컴포넌트를 만들 때 유용함</li>
<li>children prop은 모든 React 컴포넌트에 기본적으로 존재하며, 부모 컴포넌트는 자식 컴포넌트를 동적으로 렌더링할 수 있음 </li>
</ul>
<img src="https://velog.velcdn.com/images/cielo_hello/post/1b0ae2a7-f9fd-4e86-9946-9651705b238f/image.png" width=70%/>

<pre><code class="language-jsx">const Wrapper = (props: { children: React.ReactNode }) =&gt; {
  return &lt;div className=&quot;box&quot;&gt;{props.children}&lt;/div&gt;;
};

export default Wrapper;</code></pre>
<h3 id="조건부-렌더링">조건부 렌더링</h3>
<p>특정 조건에 따라 컴포넌트나 요소를 렌더링하는 것</p>
<h4 id="방법-1-if">방법 1. if</h4>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;;

const App = () =&gt; {
  const [isLogin, setIsLogin] = useState(true);

  if (isLogin) {
    return (
      &lt;&gt;
        &lt;h1&gt;Hello, Login!&lt;/h1&gt;
      &lt;/&gt;
    );
  }

  return (
    &lt;&gt;
      &lt;h1&gt;Hello, Not Login!&lt;/h1&gt;
    &lt;/&gt;
  );
};
export default App;</code></pre>
<h4 id="방법-2-삼항-연산자">방법 2. 삼항 연산자</h4>
<p>짧은 조건부 렌더링에서 유용</p>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;;

const App = () =&gt; {
  const [isLogin, setIsLogin] = useState(true);
  return &lt;&gt;{isLogin ? &lt;h1&gt;Hello, Login!&lt;/h1&gt; : &lt;h1&gt;Hello, Not Login!&lt;/h1&gt;}&lt;/&gt;;
};
export default App;</code></pre>
<h4 id="방법-3-논리-연산자">방법 3. 논리 연산자 &amp;&amp;</h4>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;;

const App = () =&gt; {
  const [isLogin, setIsLogin] = useState(true);
  return (
    &lt;&gt;
      {isLogin ? &lt;h1&gt;Hello, Login!&lt;/h1&gt; : &lt;h1&gt;Hello, Not Login!&lt;/h1&gt;}
      {!isLogin &amp;&amp; &lt;button onClick={() =&gt; setIsLogin(true)}&gt;Login&lt;/button&gt;}
      {isLogin &amp;&amp; &lt;button onClick={() =&gt; setIsLogin(false)}&gt;Logout&lt;/button&gt;}
    &lt;/&gt;
  );
};
export default App;</code></pre>
<h4 id="방법-4-즉시-실행-함수">방법 4. 즉시 실행 함수</h4>
<p>더 복잡한 로직이 필요하거나, 변수의 범위를 제한하고 싶을 때 유용</p>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;;

const App = () =&gt; {
  const [isLogin, setIsLogin] = useState(true);

  return (
    &lt;&gt;
      {(() =&gt; {
        if (isLogin) {
          return (
            &lt;&gt;
              &lt;h1&gt;Hello, Login!&lt;/h1&gt;
              &lt;button onClick={() =&gt; setIsLogin(false)}&gt;Logout&lt;/button&gt;
            &lt;/&gt;
          );
        } else {
          return (
            &lt;&gt;
              &lt;h1&gt;Hello, Not Login!&lt;/h1&gt;
              &lt;button onClick={() =&gt; setIsLogin(true)}&gt;Login&lt;/button&gt;
            &lt;/&gt;
          );
        }
      })()}
    &lt;/&gt;
  );
};

export default App;</code></pre>
<h3 id="조건부로-tailwind-적용">조건부로 Tailwind 적용</h3>
<h4 id="방법-1-기본">방법 1. 기본</h4>
<ul>
<li>이 방법은 스타일 길이가 길 때 불편해질 수 있음<pre><code class="language-jsx">&lt;h1 className={isLoggedIn ? &#39; text-5xl text-rose-500&#39; : &#39;text-3xl underline&#39;}&gt;App Component&lt;/h1&gt;</code></pre>
<h4 id="방법-2-tailwind-merge-라이브러리">방법 2. tailwind-merge 라이브러리</h4>
<a href="https://www.npmjs.com/package/tailwind-merge">tailwind-merge</a>의 <code>twMerge</code>를 이용하면 중복된 클래스를 자동으로 제거하고, 동일한 속성을 설정하는 클래스 중 마지막으로 적용된 클래스를 우선시함
이는 코드의 중복을 줄이고, CSS 적용 우선순위와 관련된 문제를 방지하는 데 유용함<pre><code class="language-jsx">&lt;h1 className={twMerge(&#39;text-3xl underline&#39;, isLoggedIn ? &#39;text-5xl text-rose-500&#39; : &#39;&#39;)}&gt;App Component&lt;/h1&gt;</code></pre>
</li>
</ul>
<h3 id="공통-컴포넌트-만들기">공통 컴포넌트 만들기</h3>
<h4 id="input">Input</h4>
<pre><code class="language-jsx">import { twMerge } from &#39;tailwind-merge&#39;;

type TInputProps = React.ComponentPropsWithoutRef&lt;&#39;input&#39;&gt;;

const Input = ({ className, ...rest }: TInputProps) =&gt; {
  return (
    &lt;&gt;
      &lt;input
        className={twMerge(
          &#39;inter w-[240px] h-11 text-sm font-medium px-[16px] py-[13.5px] border border-[#4F4F4F] rounded-lg placeholdr:text-[#ACACAC] outline-none&#39;,
          className
        )}
        {...rest}
      /&gt;
    &lt;/&gt;
  );
};
export default Input;</code></pre>
<h4 id="button">Button</h4>
<pre><code class="language-jsx">type TButtonProps = React.ComponentPropsWithoutRef&lt;&#39;button&#39;&gt;;

import { twMerge } from &#39;tailwind-merge&#39;;

const Button = ({ className, children, ...rest }: TButtonProps) =&gt; {
  return (
    &lt;&gt;
      &lt;button
        className={twMerge(&#39;w-[77px] h-[44px] text-[#F5F5F5] rounded-[8px] bg-rose-500 cursor-pointer&#39;, className)}
        {...rest}
      &gt;
        {children}
      &lt;/button&gt;
    &lt;/&gt;
  );
};
export default Button;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 6일차(NPM, 리액트)]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-6%EC%9D%BC%EC%B0%A8NPM-%EB%A6%AC%EC%95%A1%ED%8A%B8</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-6%EC%9D%BC%EC%B0%A8NPM-%EB%A6%AC%EC%95%A1%ED%8A%B8</guid>
            <pubDate>Sun, 28 Jul 2024 02:18:58 GMT</pubDate>
            <description><![CDATA[<h1 id="🧐-회고">🧐 회고</h1>
<h2 id="1-배운-점">1. 배운 점</h2>
<ul>
<li><strong>npm과 npx의 차이:</strong> npm은 패키지 설치 및 관리를 위한 도구이며, npx는 패키지를 실행하는 데 특화된 도구라는 점을 알게 되었다. 특히, npx는 설치 없이도 패키지를 즉시 실행할 수 있어 편리하다.</li>
<li><strong>가상 돔의 원리와 사용 이유:</strong> 리액트에서 가상 돔은 DOM 조작을 효율적으로 하기 위해 사용되며, 이를 통해 성능을 최적화한다는 점을 이해했다.</li>
<li><strong>트랜스파일러의 역할:</strong> 트랜스파일러가 JSX 코드를 React.createElement 호출로 변환하여 브라우저에서 이해할 수 있는 코드로 만드는 역할을 한다는 것을 배웠다.</li>
</ul>
<h2 id="2-앞으로-더-조사해볼-내용">2. 앞으로 더 조사해볼 내용</h2>
<ul>
<li><strong>CRA보다 Vite가 빠른 이유:</strong> Vite의 빠른 빌드 속도의 비결을 알아보고, CRA와 비교했을 때 어떤 장점이 있는지 조사할 필요가 있다.</li>
<li><strong>Compiler 및 Webpack:</strong> 컴파일러와 웹팩의 기능과 역할을 더 깊이 이해하고, 이를 통해 프로젝트 설정 및 최적화 방법을 학습하고자 한다.</li>
<li><strong>SWC(Speedy Web Compiler):</strong> SWC가 기존의 Babel을 대체할 수 있는지, 그리고 어떤 장점이 있는지에 대해 더 알아볼 예정이다.</li>
<li><strong>리액트에서 말하는 서버란?:</strong> 리액트의 서버사이드 렌더링(SSR) 개념과 실제로 &#39;서버&#39;가 어떤 역할을 하는지 조사할 계획이다.</li>
<li><strong>런타임 오버헤드란?:</strong> 런타임 오버헤드의 개념과 이를 줄이는 방법을 학습하고, 성능 최적화 방안을 찾아볼 예정이다.</li>
</ul>
<h2 id="3-앞으로-적용해야겠다고-느낀-점">3. 앞으로 적용해야겠다고 느낀 점</h2>
<ul>
<li><strong>npx와 npm의 상황별 사용:</strong> 상황에 맞게 npx와 npm을 활용하여 효율적으로 패키지를 관리하고 실행할 것이다.</li>
<li><strong>Tailwind 사용:</strong> 스타일링을 더욱 체계적이고 일관되게 하기 위해 Tailwind CSS를 사용해볼 것이다.</li>
</ul>
<h2 id="4-학습-평가-및-다짐목표">4. 학습 평가 및 다짐/목표</h2>
<p>이번 수업을 통해 최신 웹 기술과 도구에 대해 이해를 할 수 있었고, 특히 리액트 관련 성능 최적화 방법에 대해 깊이 있게 배웠다. 내일부터 본격적으로 배우게 될 Tailwind와 리액트가 기대된다.</p>
<img src="https://velog.velcdn.com/images/cielo_hello/post/0bd48f65-3c27-47aa-9771-d1bf128ba49f/image.png" width=50%/>

<hr>
<h1 id="📒-학습-내용-정리">📒 학습 내용 정리</h1>
<h1 id="section-00---리액트에서-자주-쓰는-es6-문법들">Section 00 - 리액트에서 자주 쓰는 ES6 문법들</h1>
<ol>
<li>const, let</li>
<li>템플릿 문자열</li>
<li>객체 리터럴</li>
<li>화살표 함수</li>
<li>비구조화 할당 -&gt; <a href="https://pointy-porkpie-abf.notion.site/6b144c8b34f246058226cd6d2d9e96b3?pvs=4">사용 예시</a></li>
<li>스프레드 연산자 -&gt; <a href="https://pointy-porkpie-abf.notion.site/8261590de9f140dc8dc1fb0911435cd4?pvs=4">사용 예시</a></li>
</ol>
<h1 id="section-01---리액트-준비">Section 01 - 리액트 준비</h1>
<h2 id="npm-npx-yarn-이해하기">npm, npx, yarn 이해하기</h2>
<h4 id="💡-실무-팁">💡 실무 팁</h4>
<ul>
<li>보통 무언가를 설치할 때 지역적으로 설치하는 것이 좋으나 node.js, npm은 전역으로 설치하는 게 좋음</li>
<li>하나의 프로젝트에 하나의 프로젝트 매니저만 사용할 수 있음</li>
</ul>
<h3 id="npm-node-package-manager">npm (Node Package Manager)</h3>
<p>Node.js의 기본 패키지 관리자</p>
<p>📍패키지(package): 소프트웨어 개발에서 재사용할 수 있도록 묶어놓은 코드의 집합, 특정 기능이나 기능 세트를 구현한 라이브러리, 프레임워크, 도구 등을 포함할 수 있음</p>
<ul>
<li>Node.js와 함께 설치됨</li>
<li>Node.js 애플리케이션의 종속성을 관리하고 패키지를 설치, 업데이트, 제거하는 데 사용함</li>
<li><code>.npmrc</code> 파일을 통해 설정 가능</li>
<li><code>package-lock.json</code> 파일을 사용하여 패키지 버전을 고정</li>
</ul>
<h3 id="npxnode-package-execute">npx(Node Package Execute)</h3>
<p>Node 패키지를 실행하는 도구</p>
<ul>
<li>Node.js와 함께 설치됨</li>
<li>npm으로 로컬 또는 글로벌로 다운로드 받지 않은 패키지도 실시간으로 설치하여 실행할 수 있음</li>
<li>개발 의존성을 줄이고 필요한 경우 패키지를 설치하여 실행</li>
<li>임시적으로 패키지를 실행할 때 유용함</li>
</ul>
<p>📍개발 의존성(Development Dependency): 개발 과정에서 필요한 도구와 라이브러리로, 애플리케이션의 실제 배포 및 실행에는 필요하지 않음, 개발 의존성을 줄이는 것은 프로젝트의 복잡성을 줄이고, 빌드 및 배포 과정을 최적화하며, 보안을 강화하고, 유지보수 비용을 절감하는 데 중요한 역할을 함</p>
<h4 id="➕-참고하면-좋은-글">➕ 참고하면 좋은 글</h4>
<p><a href="https://youngmin.hashnode.dev/npm-npx">npm 과 npx 는 뭐가 다를까 ?</a></p>
<h3 id="yarn">Yarn</h3>
<p>페이스북에서 개발한 패키지 매니저</p>
<ul>
<li>패키지 설치, 삭제 등에 있어서 병렬로 처리함 -&gt; 성능 향상</li>
<li>yarn.lock 파일을 사용하여 더 확정적인 의존성 트리 생성</li>
<li>모든 패키지에서 지원하지 않으므로 확장성 면에서 npm보다 떨어짐</li>
<li>오프라인 모드 지원: 이전에 설치된 패키지를 다시 다운로드하지 않고 설치 가능</li>
</ul>
<h4 id="➕-더-알아볼것">➕ 더 알아볼것</h4>
<p>최신 패키지 매니저 중 Bun과 PNPM이라는 것도 있음
<img src="https://velog.velcdn.com/images/cielo_hello/post/c3eca682-85c1-4911-b1cf-f6b0961ca0bb/image.png" width=50%/>
출처: <a href="https://medium.com/@olivier.trinh/npm-vs-yarn-vs-pnpm-which-package-manager-is-right-for-you-d55738a9e1aa">NPM vs Yarn vs PNPM: Which Package Manager is Right for You?</a></p>
<h2 id="패키지-버전-읽는-법">패키지 버전 읽는 법</h2>
<img src="https://velog.velcdn.com/images/cielo_hello/post/1ac1fa47-491d-42b3-b175-7a648c66a424/image.png" width=30%/>

<ul>
<li>Major : 주요 릴리즈<ul>
<li>주로 이전 버전과 호환성을 깨트릴 정도의 중요한 패치의 경우</li>
</ul>
</li>
<li>Minor: 새로운 기능<ul>
<li>이전 버전과 호환성은 유지함</li>
</ul>
</li>
<li>Patch: 버그 수정</li>
<li>옵셔널 <ul>
<li>특정 버전 뒤에 문자열로 된 의미를 부여하고 싶을 때 사용</li>
</ul>
</li>
</ul>
<h2 id="리액트-프로젝트-생성-방법">리액트 프로젝트 생성 방법</h2>
<h4 id="💡-실무-팁-1">💡 실무 팁</h4>
<ul>
<li>프레임 워크를 시작할 때 설치 방법은 공식 사이트의 가이드를 따르는 것이 좋음</li>
<li>VS Code에서 작업할 폴더의 루트에 설치하는 것이 가장 좋음</li>
<li><code>package-lock.json</code>파일은 <code>npm install</code> 시 자동으로 생성 되므로 <code>.gitignore</code>파일에 추가 하자<ul>
<li><code>npm install</code>: <code>package.json</code> 파일을 참고해서 <code>node_modules</code>, <code>package-lock.json</code>을 자동으로 생성함</li>
</ul>
</li>
</ul>
<h3 id="create-react-app">Create React App</h3>
<p>가장 고전적인 방법, 더이상 권장되지 않음</p>
<h4 id="프로젝트-생성-방법">프로젝트 생성 방법</h4>
<h4 id="1-npx">1. npx</h4>
<p><code>npx create-react-app my-app</code> -&gt; (npm 레지스트리의 최신 버전 실행)</p>
<ul>
<li><a href="https://create-react-app.dev/docs/getting-started">공식 사이트</a>에서 권장하는 방법</li>
<li>직접 패키지를 설치하고 업데이트를 하지 않더라도, npm 레지스트리에 올라가 있는 최신 버전을 실행</li>
<li>만약 해당 패키지가 이미 로컬에 설치되어 있다면, 그 버전을 사용하므로 주의 할것</li>
</ul>
<h4 id="2-npm-사용x">2. npm (사용x)</h4>
<p><code>npm install -g creact-react-app</code> -&gt; (npm 레지스트리의 최신 버전 설치)
<code>create-react-app my-app</code> -&gt; (설치 당시의 버전 실행)</p>
<ul>
<li>설치된 <code>create-react-app</code> 버전이 시간이 지남에 따라 최신 상태가 아닐 수 있으므로 수동 업데이트하지 않는 한 새로운 버그 수정이나 기능 업데이트를 놓칠 수 있음</li>
</ul>
<h3 id="vite">Vite</h3>
<p>근래에 가장 주목 받고 있는 프로젝트 생성 방법</p>
<h4 id="프로젝트-생성-방법-1">프로젝트 생성 방법</h4>
<p><code>npm create vite@latest</code></p>
<ul>
<li><a href="https://vitejs.dev/guide/">공식 사이트</a>에 명시 돼 있는 방법</li>
<li><code>npm create</code> 명령어는 내부적으로 npx 패키지를 활용하여 프로젝트를 생성해주기 때문에 항상 최신의 버전의 패키지를 사용함</li>
</ul>
<h4 id="➕-더-알아볼것-1">➕ 더 알아볼것</h4>
<ul>
<li>최근 CRA를 사용하지 않는 이유</li>
<li>CRA는 왜 Vite보다 느릴까
<img src="https://velog.velcdn.com/images/cielo_hello/post/a3a93de3-1bad-4084-8dcd-106362d7ff8e/image.png" alt="">
출처: <a href="https://dev.to/tahmidbintaslim/the-best-way-to-start-your-react-project-in-2024-embracing-vite-over-create-react-app-57l">The Best Way to Start Your React Project in 2024: Embracing Vite over Create React App</a></li>
</ul>
<h1 id="section-02---리액트-시작하기">Section 02 - 리액트 시작하기</h1>
<h2 id="주요-폴더-및-파일">주요 폴더 및 파일</h2>
<p><code>node_modules/</code>: </p>
<ul>
<li>프로젝트의 모든 의존성을 포함함</li>
<li><code>npm install</code> 명령어 통해 <code>package.json</code> 파일에 명시된 모든 의존성을 이 폴더에 설치함</li>
<li>일반적으로 버전 관리 시스템(Git 등)에 포함되지 않음</li>
</ul>
<p><code>public/</code>:</p>
<ul>
<li>정적 파일을 포함하는 폴더</li>
<li>웹팩의 영향을 받지 않음</li>
<li>이 폴더 안의 파일은 빌드 과정에서 그대로 복사되어 최종 빌드에 포함됨</li>
</ul>
<p><code>src/</code>:</p>
<ul>
<li>애플리케이션의 소스 코드가 포함된 폴더</li>
<li><code>assets/</code>: 이미지, 폰트 등의 정적 자산을 포함할 수 있는 폴더, <code>public/</code>과 달리 웹팩의 영향을 받음</li>
<li><code>App.jsx</code>: 메인 애플리케이션 컴포넌트 파일, React 컴포넌트 구조의 시작점</li>
<li><code>main.jsx</code>: 애플리케이션의 진입점 파일, <code>ReactDOM</code>을 사용하여 <code>App</code> 컴포넌트를 HTML에 렌더링함</li>
</ul>
<p><code>index.html</code>: </p>
<ul>
<li><code>public/</code> 또는 최상위 디렉토리에 있는 HTML파일</li>
<li>Vite는 이 파일을 기반으로 빌드하고 애플리케이션을 시작함</li>
<li>개발 서버가 구동되면 가장 먼저 불러와지는 파일(?)</li>
</ul>
<p><code>package.json</code></p>
<ul>
<li>프로젝트의 메타데이터와 의존성을 정의하는 파일</li>
<li>스크립트, 의존성, 개발 의존성, 프로젝트 이름, 버전 등의 정보가 포함됨</li>
</ul>
<p><code>vite.config.ts</code></p>
<ul>
<li>Vite의 다양한 설정을 커스터마이즈할 수 있는 설정 파일</li>
</ul>
<h4 id="➕-더-알아볼것-2">➕ 더 알아볼것</h4>
<ul>
<li>Compiler 및 Webpack은 무엇인가?</li>
<li>SWC(Speedy Web Compiler)는 무엇인가?<ul>
<li>자바스크립트 프로젝트의 컴파일과 번들링에 모두 사용될 수 있는 빌드 툴 </li>
<li>기존에 사용하던 Babel을 대체할 수 있음</li>
<li>최근 Next.js 팀에서 권장하고 있어 주목 받고 있는 빌드 툴</li>
</ul>
</li>
</ul>
<h2 id="리액트를-사용하는-이유">리액트를 사용하는 이유</h2>
<h3 id="1-강력한-커뮤니티와-광범위한-생태계">1. 강력한 커뮤니티와 광범위한 생태계</h3>
<ul>
<li>국내외로 인기가 많아 매우 큰 커뮤니티를 보유하고 있어 관련 자료가 많아 학습 및 문제 해결이 쉬움</li>
<li>다양한 서드파티 라이브러리와 도구를 지원함</li>
</ul>
<h3 id="2-강력한-컴포넌트-기능">2. 강력한 컴포넌트 기능</h3>
<p>리액트를 가장 인기있는 프레임워크로 만들어 준 핵심 기능</p>
<ul>
<li><strong>컴포넌트:</strong> React에서 웹의 구성 요소를 작은 단위로 분할하여 재사용 가능한 독립적인 코드로 작성한 것</li>
<li>컴포넌트를 활용하면 반복적인 웹 구성 요소를 효율적으로 관리할 수 있으며 코드의 가독성과 유지보수성을 높일 수 있음</li>
</ul>
<h3 id="3-virtual-dom-가상-돔">3. Virtual DOM (가상 돔)</h3>
<p>React는 SPA 구조에서 빈번한 DOM 조작으로 인한 성능 문제를 해결하기 위해 가상 돔을 사용함</p>
<h4 id="crpcritical-rendering-path">CRP(Critical Rendering Path)</h4>
<p>웹 페이지가 사용자에게 표시되기까지의 과정. CRP는 페이지 로드 성능에 중요한 영향을 미침
<img src="https://velog.velcdn.com/images/cielo_hello/post/12c2855c-d5ee-42cf-8cf6-27934a50b442/image.png" alt=""></p>
<ul>
<li><p><strong>HTML 파싱 및 DOM 생성:</strong> 브라우저는 HTML 문서를 파싱하여 DOM(Document Object Model)을 생성함</p>
</li>
<li><p><strong>CSS 파싱 및 CSSOM 생성:</strong> CSS 스타일시트를 파싱하여 CSSOM(CSS Object Model)을 생성함</p>
</li>
<li><p><strong>JavaScript 실행:</strong> JavaScript는 DOM이나 CSSOM을 수정할 수 있으며, 이를 통해 렌더링에 영향을 줄 수 있음</p>
</li>
<li><p><strong>렌더 트리 생성:</strong> DOM과 CSSOM을 결합하여 렌더 트리를 생성, 이 트리는 실제로 렌더링할 요소만 포함함</p>
</li>
<li><p><strong>레이아웃:</strong> 렌더 트리를 기반으로 각 요소의 크기와 위치를 계산함</p>
</li>
<li><p><strong>페인팅:</strong> 최종적으로 계산된 정보를 사용하여 화면에 픽셀을 그림</p>
</li>
<li><p><strong>Reflow와 Repaint:</strong> 페이지 이동이나 JavaScript 조작으로 CRP가 다시 발생하며, Reflow와 Repaint는 성능 비용이 큼. 따라서 이를 최적화하는 것이 중요함</p>
</li>
</ul>
<h4 id="최적화-예시">최적화 예시</h4>
<ul>
<li><p>최적화 전
이 코드는 1000개의 <code>&lt;li&gt;</code> 요소를 추가할 때마다 Reflow와 Repaint가 1000번 발생함 -&gt; 성능 저하</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot; /&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
  &lt;title&gt;Document&lt;/title&gt;
  &lt;script&gt;
    function createDOM() {
      console.time();
      const ulEl = document.querySelector(&#39;ul&#39;);
      for (let i = 0; i &lt; 1000; i++) {
        ulEl.innerHTML += `&lt;li&gt;${i}&lt;/li&gt;`;
      }
      console.timeEnd();
    }
  &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;button onClick=&quot;createDOM()&quot;&gt;추가&lt;/button&gt;
  &lt;ul&gt;&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
<li><p>최적화
DOM 조작을 최소화하여 성능을 개선할 수 있음. 여기서는 모든 <code>&lt;li&gt;</code> 요소를 한 번에 추가하여 Reflow와 Repaint를 한 번으로 줄임</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot; /&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
  &lt;title&gt;Document&lt;/title&gt;
  &lt;script&gt;
    function createDOM() {
      console.time();
      const ulEl = document.querySelector(&#39;ul&#39;);
      let liEl = &#39;&#39;;
      for (let i = 0; i &lt; 1000; i++) {
        liEl += `&lt;li&gt;${i}&lt;/li&gt;`;
      }
      ulEl.innerHTML = liEl;
      console.timeEnd();
    }
  &lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;button onClick=&quot;createDOM()&quot;&gt;추가&lt;/button&gt;
  &lt;ul&gt;&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
</li>
</ul>
<h4 id="리액트에서-virtual-dom의-역할">리액트에서 Virtual DOM의 역할</h4>
<p>JavaScript만으로 최적화하는 과정은 복잡하고 어렵기 때문에 React와 같은 프레임워크가 등장함
프레임워크는 이런 최적화를 자동으로 처리할 수 있으며, React는 Virtual DOM을 사용하여 효율적으로 DOM을 조작함
<img src="https://velog.velcdn.com/images/cielo_hello/post/61d9fb2c-9dc6-44b1-8021-aced82f9c3ab/image.png" width=70%/>
출처: <a href="https://www.geeksforgeeks.org/what-is-diffing-algorithm/">What is Diffing Algorithm ? - GeeksforGeeks</a></p>
<ul>
<li><strong>Diffing &amp; Reconciliation:</strong> Virtual DOM은 메모리 내에서 가상의 DOM을 생성하고, 실제 DOM과 비교하여 변경이 필요한 최소한의 부분만 업데이트함
이를 통해 Reflow와 Repaint의 비용을 최소화하며 이 과정을 Diffing &amp; Reconciliation라고 함</li>
</ul>
<h4 id="➕-더-알아볼것-3">➕ 더 알아볼것</h4>
<p>Batch Update는 무엇인가?</p>
<h4 id="한계">한계</h4>
<p>Virtual DOM도 성능 비용이 있으며 React는 내부적으로 이러한 성능을 최적화하지만, 모든 경우에 완벽한 최적화를 보장하지는 않음</p>
<h2 id="react에서-ui-요소-작성-방법">React에서 UI 요소 작성 방법</h2>
<h3 id="1-createelement">1. CreateElement</h3>
<ul>
<li><code>createElement</code> 함수를 사용해서 모든 React 컴포넌트와 엘리먼트 생성 가능</li>
<li>JSX 없이 순수한 JavaScript 코드로 요소를 생성</li>
<li>초기에는 JSX가 없었고, 개발자들은 <code>createElement</code>를 사용해 모든 요소를 수동으로 작성함</li>
</ul>
<pre><code class="language-jsx">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;

// React.createElement를 사용하여 컴포넌트 정의
const element = React.createElement(
  &#39;div&#39;, // 태그 이름
  { className: &#39;greeting&#39; }, // 속성
  &#39;Hello, world!&#39; // 자식 요소(내용)
);

// 해당 컴포넌트를 DOM에 렌더링
ReactDOM.render(
  element,
  document.getElementById(&#39;root&#39;)
);</code></pre>
<h3 id="2-jsx-javascript-xml">2. JSX (Javascript XML)</h3>
<ul>
<li><p><code>createElement</code>를 사용하는 방식은 번거롭고 가독성이 떨어짐</p>
</li>
<li><p>페이스북 팀은 개발자들이 더 익숙한 HTML과 유사한 문법을 사용해 React 컴포넌트를 작성할 수 있는 JSX를 도입 </p>
</li>
<li><p>JSX는 개발 편의성을 크게 높였고, React의 채택을 가속화하는 데 중요한 역할을 함</p>
</li>
<li><p>JSX는 브라우저에서 직접 실행할 수 없는 문법이기 때문에, Babel과 같은 트랜스파일러가 필요함</p>
<ul>
<li>트랜스파일러는 JSX 코드를 <code>React.createElement</code> 호출로 변환하므로 JSX와 <code>React.createElement</code>는 서로 상호 보완적인 관계에 있음</li>
<li>CRA: <code>react-scripts</code> 패키지(스크립트 모음으로, 프로젝트를 설정하고 관리하는 데 필요한 웹팩, 바벨과 같은 스크립트가 포함되어 있음)</li>
<li>Vite: <code>@vitejs/plugin-react(-swc)</code> (웹 팩과 바벨의 역할을 하는 패키지)</li>
</ul>
</li>
<li><p>주요 문법 특징</p>
<ul>
<li><strong>단일 루트 태그:</strong> 모든 JSX 표현식은 하나의 루트 태그로 감싸야 함. 다중 요소를 반환할 때는 <code>&lt;React.Fragment&gt;</code> 또는 <code>&lt;&gt;&lt;/&gt;</code>를 사용할 수 있음</li>
<li><strong>태그 규칙:</strong> 여러 줄의 JSX 코드를 반환할 때는 소괄호로 감싸야 하며, 빈 태그는 반드시 닫아야 함</li>
<li><strong>표현식 사용:</strong> JSX 내부에서는 중괄호 <code>{}</code>를 사용하여 JavaScript 표현식을 포함할 수 있음 예를 들어, <code>&lt;h1&gt;Count: {10 * 2}&lt;/h1&gt;</code>와 같이 작성할 수 있음</li>
<li><strong>컴포넌트 명명 규칙:</strong> 함수형 컴포넌트는 대문자로 시작해야 함. 이는 React가 컴포넌트를 구별하는 방식임</li>
<li><strong>클래스 속성:</strong> HTML에서 class 속성은 JSX에서 className으로 사용됨. 이는 JavaScript의 예약어인 class와의 혼동을 피하기 위함임</li>
</ul>
</li>
</ul>
<h2 id="컴포넌트-css-스타일링">컴포넌트 CSS 스타일링</h2>
<p>리액트에서 컴포넌트에 CSS 스타일을 적용하는 방법에는 여러 가지가 있음</p>
<h3 id="인라인-스타일-inline-style">인라인 스타일 (Inline Style)</h3>
<p>컴포넌트의 JSX 내에서 <code>style</code> 속성을 통해 직접 스타일을 지정하는 방법</p>
<ul>
<li>자바스크립트 객체로 스타일을 정의하므로, CSS 속성명은 카멜 케이스로 표기해야 함<pre><code class="language-jsx">const App = () =&gt; {
return (
  &lt;div&gt;
    &lt;h1 style={{ fontSize: &quot;30px&quot;, color: &quot;#ed4848&quot;, textDecoration: &quot;line-through&quot; }}&gt;
      Hello World!
    &lt;/h1&gt;
  &lt;/div&gt;
);
};
</code></pre>
</li>
</ul>
<p>export default App;</p>
<pre><code>### 외부 스타일 (External Stylesheet)
별도의 CSS 파일에 CSS 코드를 작성하고, 리액트 컴포넌트 파일과 연결해서 사용하는 방법
- 일반적으로 모든 컴포넌트에 영향을 미치는 전역 스타일은 `main.tsx`와 같은 파일에 `import`함

### CSS Modules
각 클래스 이름을 로컬 범위로 한정시켜 스타일 충돌을 방지하는 방법
- 스타일 파일명은 `.module.css` 확장자를 사용하며, 컴포넌트에서 `import`하여 사용함
- 파일은 컴포넌트가 있는 위치에 만들며 파일명은 컴포넌트 이름과 똑같이 짓는 것이 관례임

### Tailwind CSS
대표적인 Atomic CSS 라이브러리, 클래스 이름만으로 스타일을 적용할 수 있음
- [설치 방법](https://tailwindcss.com/docs/guides/vite)
- **장점:** 커스터마이즈 가능, 불필요한 CSS를 줄여 파일 크기를 최소화할 수 있음
- **단점:** 처음에는 클래스 이름이 혼란스러울 수 있으며, HTML이 복잡해질 수 있음

### CSS-in-JS
CSS-in-JS는 자바스크립트 파일 내에서 스타일을 정의하고, 컴포넌트와 밀접하게 결합하여 사용하는 방법
- 주요 라이브러리로는 Styled Components, Emotion, Vanilla Extract, Linaria 등이 있음

#### ➕ 더 알아볼것
리액트에서 말하는 서버는 뭘까?
런타임 오버헤드란?
dependency vs devDependency

---

# 참고 자료
- [수코딩](https://www.sucoding.kr/) 강사님의 노션 페이지

——————————————————————————
본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 과정(B-log) 리뷰로 작성 되었습니다.</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 5일차(DOM, 이벤트, 타입스크립트)]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-5%EC%9D%BC%EC%B0%A8DOM-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-5%EC%9D%BC%EC%B0%A8DOM-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Sat, 20 Jul 2024 08:07:45 GMT</pubDate>
            <description><![CDATA[<p>오늘은 본가에 가야 해서 온라인으로 수업을 들었다. DOM, 이벤트, 타입스크립트에 대해 배웠는데, 프레임워크를 배우고 나서부터 DOM에 대한 내용을 많이 잊어버려 복습할 수 있어 의미 있었다. 하지만 내용이 방대해서 간략하게만 짚고 넘어가서 지난번에 공부하고 기록해 놓은 블로그 글을 다시 읽어보면서 부족한 부분을 채웠다. 또한, 간단한 문제를 풀었는데 생각보다 쉽지 않아서 충격이었다. 바닐라 자바스크립트를 연습해야 할 이유가 생겼다.</p>
<p>타입스크립트는 여러 프로젝트에서 사용했지만, 다시 공부하고 싶은 부분 중 하나였다. 특히 제네릭 사용이 많이 어려워서 그 부분에 집중해서 들었다. 또한, 강사님은 <code>interface</code>대신 <code>type</code>만 사용한다고 하신 부분이 인상깊었는데, 나도 프로젝트 때 마다 관련 주제로 논쟁을 했었는데 강사님의 주장과 탄탄한 근거를 들으니 설득됐다. 배운 내용을 잊어버리지 않도록 정리하려 한다.</p>
<h1 id="18-domdocument-object-model">18. DOM(Document Object Model)</h1>
<ul>
<li>웹 개발에서 자바스크립트는 HTML 요소를 조작하고, 사용자 인터랙션을 처리하는 도구임</li>
<li>DOM은 HTML 요소를 객체로 표현하여 자바스크립트로 접근하고 조작할 수 있게 해줌 </li>
<li>웹 브라우저는 HTML을 파싱하여 DOM을 생성하고, 이를 통해 자바스크립트로 HTML 요소에 접근하고 조작할 수 있음</li>
</ul>
<h2 id="문서-객체-찾는-법-dom-조작의-시작">문서 객체 찾는 법 (DOM 조작의 시작)</h2>
<h3 id="1-queryselector">1. <code>querySelector</code></h3>
<p><code>querySelector</code> 메서드는 CSS 선택자와 동일한 방법으로 HTML 요소를 한 개만 찾을 때 사용</p>
<pre><code class="language-javascript">const h1El = document.querySelector(&#39;h1&#39;);
console.log(h1El); // &lt;h1&gt;가장 먼저 찾아지는 요소를 반환함</code></pre>
<h3 id="2-queryselectorall">2. <code>querySelectorAll</code></h3>
<p><code>querySelectorAll</code> 메서드는 CSS 선택자와 동일한 방법으로 여러 HTML 요소를 찾을 때 사용</p>
<pre><code class="language-javascript">const h1Els = document.querySelectorAll(&#39;body &gt; h1&#39;);
console.log(h1Els);</code></pre>
<img src="https://velog.velcdn.com/images/cielo_hello/post/68e07a14-9ea4-41d4-83aa-db50e934d297/image.png" width="50%">

<h2 id="문서-바꾸기">문서 바꾸기</h2>
<p>HTML 요소의 내용을 바꾸는 방법</p>
<pre><code class="language-javascript">const h1El = document.querySelector(&#39;h1&#39;);
h1El.innerHTML = &#39;&lt;i&gt;sucoding&lt;/i&gt;&#39;;
h1El.innerText = &#39;&lt;i&gt;sucoding&lt;/i&gt;&#39;;
console.log(h1El);</code></pre>
<h2 id="스타일-주는-법">스타일 주는 법</h2>
<p>CSS 스타일을 직접 설정할 수 있음</p>
<pre><code class="language-javascript">const h1El = document.querySelector(&#39;h1&#39;);
h1El.style.color = &#39;red&#39;;
h1El.style.fontSize = &#39;130px&#39;;
console.log(h1El);</code></pre>
<h2 id="클래스-추가하는-법">클래스 추가하는 법</h2>
<p>HTML 요소에 클래스를 추가하는 방법</p>
<pre><code class="language-javascript">const h1El = document.querySelector(&#39;h1&#39;);
h1El.classList.add(&#39;active&#39;); // 기존 클래스 뒤에 추가
console.log(h1El);</code></pre>
<h2 id="클래스-제거하는-법">클래스 제거하는 법</h2>
<p>HTML 요소에서 클래스를 제거하는 방법</p>
<pre><code class="language-javascript">const h1El = document.querySelector(&#39;h1&#39;);
h1El.classList.remove(&#39;active&#39;);
console.log(h1El);</code></pre>
<h2 id="클래스-토글하기">클래스 토글하기</h2>
<p>클래스가 있으면 제거하고, 없으면 추가하는 방법</p>
<pre><code class="language-javascript">const h1El = document.querySelector(&#39;h1&#39;);
h1El.classList.toggle(&#39;done&#39;);
console.log(h1El);</code></pre>
<h2 id="input에-입력된-값-가져오기">input에 입력된 값 가져오기</h2>
<p>input 요소에 입력된 값을 가져오는 방법</p>
<pre><code class="language-javascript">setTimeout(() =&gt; {
  const inputEl = document.querySelector(&#39;input&#39;);
  console.log(inputEl.value);
}, 3000);</code></pre>
<h2 id="➕-더-자세한-내용은-지난번에-작성한-글-참고하기">➕ 더 자세한 내용은 지난번에 작성한 글 참고하기</h2>
<ul>
<li><a href="https://velog.io/@cielo_hello/%EA%B0%95%EC%9D%98-%EC%9D%B8%ED%84%B0%EB%9E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0">[강의] 인터랙티브 자바스크립트 시작하기</a></li>
<li><a href="https://velog.io/@cielo_hello/%EA%B0%95%EC%9D%98-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%99%80-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD">[강의] 브라우저와 자바스크립</a></li>
</ul>
<hr>
<h1 id="20-이벤트">20. 이벤트</h1>
<h2 id="➕-더-자세한-내용은-지난번에-작성한-글-참고하기-1">➕ 더 자세한 내용은 지난번에 작성한 글 참고하기</h2>
<ul>
<li><a href="https://velog.io/@cielo_hello/%EA%B0%95%EC%9D%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0">[강의] 이벤트 살펴보기</a></li>
<li><a href="https://velog.io/@cielo_hello/%EA%B0%95%EC%9D%98-%EB%8B%A4%EC%96%91%ED%95%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0">[강의] 다양한 이벤트 알아보기</a></li>
</ul>
<h2 id="❓아래-코드에서-this가-가리키는-요소는">❓아래 코드에서 <code>this</code>가 가리키는 요소는?</h2>
<pre><code class="language-javascript">const buttonEl = document.querySelector(&#39;button&#39;);
buttonEl.addEventListener(&#39;click&#39;, function (ev) {
  console.log(this); // 클릭된 button 요소
  console.log(ev);   // 이벤트 객체
  console.log(&#39;click&#39;);
});</code></pre>
<h4 id="❗위-코드에서-this는-buttonel을-가리킴">❗위 코드에서 <code>this</code>는 <code>buttonEl</code>을 가리킴</h4>
<ul>
<li>이는 이벤트 핸들러 함수가 일반 함수로 정의되었기 때문임</li>
<li>이때, <code>this</code>는 이벤트가 바인딩된 요소를 참조함</li>
<li>만약 화살표 함수를 사용하면 <code>this</code>는 다르게 동작함 </li>
<li>화살표 함수에서는 <code>this</code>가 상위 스코프의 <code>this</code>를 유지함</li>
</ul>
<pre><code class="language-javascript">const buttonEl = document.querySelector(&#39;button&#39;);
buttonEl.addEventListener(&#39;click&#39;, (ev) =&gt; {
  console.log(this); // 상위 스코프의 this
  console.log(ev);   // 이벤트 객체
  console.log(&#39;click&#39;);
});</code></pre>
<ul>
<li>위 코드에서 <code>this</code>는 상위 스코프의 <code>this</code>를 유지하므로, 전역 객체(<code>window</code>)를 가리킬 수 있음</li>
</ul>
<hr>
<h1 id="타입스크립트">타입스크립트</h1>
<p>타입스크립트는 자바스크립트에 타입 시스템을 추가하여 코드의 안정성과 가독성을 높이는 도구임</p>
<h2 id="타입-추론-type-inference">타입 추론 (Type Inference)</h2>
<ul>
<li>타입스크립트는 변수의 타입을 추론할 수 있음</li>
<li>변수를 선언할 때 타입을 명시하지 않아도, 할당된 값을 통해 타입을 추론함
<img src="https://velog.velcdn.com/images/cielo_hello/post/6d33add1-8ae9-4abc-8a3c-13a11fe24268/image.png" alt="">
<img src="https://velog.velcdn.com/images/cielo_hello/post/ce7c644a-823a-417d-a401-172cbb7164c2/image.png" alt=""></li>
</ul>
<ul>
<li>위 코드에서 타입스크립트는 <code>str</code> 변수가 문자열 타입임을 추론함</li>
<li><code>str2</code>변수는 <code>const</code>로 선언 돼 변수의 타입을 문자열 리터럴 &quot;hello&quot;로 추론함</li>
</ul>
<h2 id="타입-명시-type-annotation">타입 명시 (Type Annotation)</h2>
<ul>
<li>변수를 선언할 때 명확하게 타입을 명시할 수도 있음 </li>
<li>변수명 뒤에 콜론(:)을 붙이고 타입을 명시함</li>
</ul>
<pre><code class="language-typescript">const num: number = 10;
const arr: number[] = [1, 2, 3];
const arr2: [number, string, number] = [1, &quot;A&quot;, 3]; // 튜플(tuple)
const arr3: (string | number)[] = [1, &quot;A&quot;, 3];

const obj: {} = {};
const obj2: { name: string; age: number } = { name: &quot;kim&quot;, age: 20 };</code></pre>
<h2 id="리터럴-타입">리터럴 타입</h2>
<p>특정 값만 가질 수 있는 타입</p>
<pre><code class="language-typescript">let num: 10 | 20 = 10;
let str: &quot;A&quot; | &quot;B&quot; = &quot;A&quot;;

let obj: { name: &quot;kim&quot; } = { name: &quot;kim&quot; };

const printName = (name: &quot;kim&quot;) =&gt; {
  console.log(name);
};

printName(&quot;kim&quot;);
printName(obj.name);</code></pre>
<h2 id="타입-오퍼레이터">타입 오퍼레이터</h2>
<p>타입을 조작하는 연산자</p>
<h3 id="유니온-타입">유니온 타입</h3>
<p>OR 연산자(<code>|</code>)를 사용하여 여러 타입 중 하나를 선택할 수 있음</p>
<pre><code class="language-typescript">const arr: (number | string)[] = [1, &quot;A&quot;, 3];</code></pre>
<h3 id="인터섹션-타입">인터섹션 타입</h3>
<p>AND 연산자(<code>&amp;</code>)를 사용하여 여러 타입을 조합할 수 있음</p>
<pre><code class="language-typescript">const obj: { name: string } &amp; { age: number } = { name: &quot;kim&quot;, age: 20 };</code></pre>
<h2 id="인터페이스-interface">인터페이스 (Interface)</h2>
<p>객체의 구조를 정의하는 방법 </p>
<ul>
<li>객체의 형태를 명확하게 정의하고, 코드의 가독성과 안정성을 높일 수 있음</li>
</ul>
<h3 id="기본-사용법">기본 사용법</h3>
<ul>
<li>인터페이스를 정의하고 이를 사용하여 객체의 타입을 명시할 수 있음</li>
<li>암묵적으로 대문자 I를 붙여주는 관행이 있음</li>
</ul>
<pre><code class="language-typescript">interface IUser {
  name: string;
  age: number;
}

const user: IUser = {
  name: &quot;John&quot;,
  age: 30,
};

console.log(user.name); // &quot;John&quot;
console.log(user.age); // 30</code></pre>
<h3 id="옵셔널-프로퍼티optional-property">옵셔널 프로퍼티(Optional Property)</h3>
<ul>
<li>인터페이스에서 옵셔널 프로퍼티를 정의할 수 있음 </li>
<li>객체가 해당 속성을 가질 수도 있고, 가지지 않을 수도 있음을 나타냄</li>
</ul>
<pre><code class="language-typescript">interface IUser {
  name: string;
  age: number;
  height?: number; // 옵셔널 프로퍼티
}

const user1: IUser = {
  name: &quot;John&quot;,
  age: 30,
};

const user2: IUser = {
  name: &quot;Jane&quot;,
  age: 25,
  height: 170,
};

console.log(user1.height); // undefined
console.log(user2.height); // 170</code></pre>
<h3 id="읽기-전용-속성-readonly-properties">읽기 전용 속성 (Readonly Properties)</h3>
<ul>
<li>읽기 전용 속성은 객체가 생성된 후에 값을 변경할 수 없도록 함 </li>
<li><code>readonly</code> 키워드를 사용하여 읽기 전용 속성을 정의함</li>
</ul>
<pre><code class="language-typescript">interface IUser {
  name: string;
  readonly age: number; // 읽기 전용 속성
}

const user: IUser = {
  name: &quot;John&quot;,
  age: 30,
};

user.name = &quot;Jane&quot;; // 가능
user.age = 25; // 오류 발생: age는 읽기 전용 속성입니다.</code></pre>
<h3 id="인터페이스-상속-interface-inheritance">인터페이스 상속 (Interface Inheritance)</h3>
<ul>
<li>인터페이스는 다른 인터페이스를 상속받을 수 있음</li>
<li>이를 통해 인터페이스 간의 재사용성을 높일 수 있음</li>
</ul>
<pre><code class="language-typescript">interface IPerson {
  name: string;
  age: number;
}

interface IEmployee extends IPerson {
  employeeId: number;
}

const employee: IEmployee = {
  name: &quot;John&quot;,
  age: 30,
  employeeId: 12345,
};

console.log(employee.name); // &quot;John&quot;
console.log(employee.employeeId); // 12345</code></pre>
<h3 id="인터페이스-병합-interface-merging">인터페이스 병합 (Interface Merging)</h3>
<ul>
<li>타입스크립트에서는 동일한 이름의 인터페이스가 여러 번 정의되면, 자동으로 병합됨</li>
<li>이를 통해 모듈 간에 인터페이스를 확장할 수 있음</li>
</ul>
<pre><code class="language-typescript">interface ICar {
  brand: string;
}

interface ICar {
  model: string;
}

const car: ICar = {
  brand: &quot;Toyota&quot;,
  model: &quot;Corolla&quot;,
};

console.log(car.brand); // &quot;Toyota&quot;
console.log(car.model); // &quot;Corolla&quot;</code></pre>
<h3 id="인덱스-시그니처-index-signatures">인덱스 시그니처 (Index Signatures)</h3>
<ul>
<li>인덱스 시그니처를 사용하면 동적으로 속성 이름을 정의할 수 있음 </li>
<li>객체가 특정 패턴을 따르는 속성 이름과 타입을 가질 수 있도록 함</li>
<li>하지만 인덱스 시그니처의 무분별한 사용은 지양해야함<ul>
<li><strong>타입 안전성 감소:</strong> 객체의 키와 값에 대해 매우 유연한 타입을 허용하므로, 예상치 못한 타입 오류가 발생할 가능성이 있음</li>
<li><strong>자동 완성 및 코드 가독성 저하:</strong> 인덱스 시그니처를 사용하면 IDE가 제공하는 자동 완성 기능이 제대로 동작하지 않을 수 있음</li>
<li><strong>유지보수성 문제:</strong> 인덱스 시그니처를 사용하면 객체의 구조가 명확하지 않아서, 다른 개발자가 코드를 이해하고 유지보수하는 데 어려움을 겪을 수 있습니다. 명확한 객체 구조를 정의하는 것이 유지보수성을 높이는 데 중요함<pre><code class="language-typescript">interface IStringArray {</code></pre>
</li>
</ul>
</li>
</ul>
<p>}</p>
<p>const myArray: IStringArray = [&quot;Alice&quot;, &quot;Bob&quot;, &quot;Charlie&quot;];</p>
<p>console.log(myArray[0]); // &quot;Alice&quot;
console.log(myArray[1]); // &quot;Bob&quot;</p>
<pre><code>
- 인덱스 시그니처는 객체에서도 사용할 수 있음

```typescript
interface IDictionary {
  [key: string]: string | number;
}

const dict: IDictionary = {
  name: &quot;John&quot;,
  age: 30,
  country: &quot;USA&quot;,
};

console.log(dict.name); // &quot;John&quot;
console.log(dict.age); // 30</code></pre><h3 id="함수-타입-function-types">함수 타입 (Function Types)</h3>
<ul>
<li>인터페이스는 함수의 타입을 정의할 수도 있음</li>
</ul>
<pre><code class="language-typescript">interface ISearchFunc {
  (source: string, subString: string): boolean;
}

const mySearch: ISearchFunc = (src, sub) =&gt; {
  return src.includes(sub);
};

console.log(mySearch(&quot;Hello, world!&quot;, &quot;world&quot;)); // true
console.log(mySearch(&quot;Hello, world!&quot;, &quot;typescript&quot;)); // false</code></pre>
<h2 id="타입-별칭-type-alias">타입 별칭 (Type Alias)</h2>
<p><code>type</code> 키워드를 사용하여 새로운 타입을 정의하는 방법</p>
<ul>
<li>복잡한 타입을 간결하게 정의하고, 코드의 가독성을 높일 수 있음</li>
<li>여러 타입을 하나의 이름으로 묶어서 사용할 수 있음</li>
<li>암묵적으로 대문자 T를 붙여주는 관행이 있음</li>
</ul>
<h3 id="예시-1-문자열-리터럴-타입">예시 1: 문자열 리터럴 타입</h3>
<ul>
<li>문자열 리터럴 타입을 사용하면 특정 문자열 값만을 허용하는 타입을 정의할 수 있음</li>
</ul>
<pre><code class="language-typescript">type TRainbowColor = &quot;red&quot; | &quot;orange&quot; | &quot;yellow&quot; | &quot;green&quot; | &quot;blue&quot; | &quot;indigo&quot; | &quot;violet&quot;;

const phoneColor: TRainbowColor = &quot;indigo&quot;;
const wrongColor: TRainbowColor = &quot;pink&quot;; // 오류 발생</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/6d25ec3d-d1be-47f5-8c0e-da29f340206f/image.png" alt=""></p>
<ul>
<li>위 예시에서 <code>TRainbowColor</code> 타입은 무지개의 색상 중 하나의 값만을 가질 수 있는 타입을 정의함</li>
<li><code>phoneColor</code> 변수는 이 타입을 사용하여 정의되었으며, 올바른 색상 값만을 가질 수 있음</li>
</ul>
<h3 id="예시-2-객체-타입">예시 2: 객체 타입</h3>
<ul>
<li>객체 타입을 정의할 때도 타입 별칭을 사용할 수 있음</li>
</ul>
<pre><code class="language-typescript">type TUser = {
  name: string;
};

type TJob = {
  readonly title?: string;
};

type TUserAndJob = TUser &amp; TJob;

const user1: TUserAndJob = {
  name: &quot;kim&quot;,
  title: &quot;developer&quot;,
};

const user2: TUser = {
  name: &quot;kim&quot;,
};</code></pre>
<ul>
<li><code>TUserAndJob</code> 타입에서 처럼 <code>TUser</code>와 <code>TJob</code> 타입을 결합(<code>&amp;</code>)하여 두 타입의 속성을 모두 가지는 객체를 정의할 수 있음</li>
</ul>
<h3 id="예시-3-함수와-객체-타입">예시 3: 함수와 객체 타입</h3>
<ul>
<li>타입 별칭을 사용하여 함수의 매개변수 타입을 정의할 수 있음</li>
</ul>
<pre><code class="language-typescript">type TUser = {
  name: string;
  age: number;
};

const user1: TUser = {
  name: &quot;kim&quot;,
  age: 20,
};

function printUserName({ name }: TUser) {
  console.log(name);
}

printUserName(user1); // &quot;kim&quot;</code></pre>
<h2 id="인터페이스와-타입-별칭의-차이점">인터페이스와 타입 별칭의 차이점</h2>
<p>타입 별칭과 인터페이스는 유사하지만 몇 가지 중요한 차이점이 있음</p>
<h3 id="병합">병합</h3>
<ul>
<li><p><strong>인터페이스:</strong> 동일한 이름으로 여러 번 정의되면 자동으로 병합됨</p>
<pre><code class="language-typescript">interface Car {
  brand: string;
}

interface Car {
  model: string;
}

const myCar: Car = {
  brand: &quot;Toyota&quot;,
  model: &quot;Corolla&quot;,
};

console.log(myCar.brand); // &quot;Toyota&quot;
console.log(myCar.model); // &quot;Corolla&quot;</code></pre>
</li>
<li><p><strong>타입 별칭:</strong> 동일한 이름으로 여러 번 정의할 수 없음</p>
<pre><code class="language-typescript">type Car = {
  brand: string;
};

// 다음 줄은 오류 발생
// type Car = {
//   model: string;
// };

const myCar: Car = {
  brand: &quot;Toyota&quot;,
  // model: &quot;Corolla&quot;, // 오류 발생
};</code></pre>
<h3 id="상속">상속</h3>
</li>
<li><p><strong>인터페이스:</strong> 상속을 지원함, 한 인터페이스가 다른 인터페이스를 확장할 수 있음</p>
<pre><code class="language-typescript">interface IPerson {
name: string;
}
</code></pre>
</li>
</ul>
<p>interface IEmployee extends IPerson {
  readonly title?: string;
}</p>
<p>const employee: IEmployee = {
  name: &quot;John&quot;,
  title: &quot;developer&quot;,
};</p>
<pre><code>- **타입 별칭:** 상속을 지원하지 않음, 타입 별칭을 사용할 때는 `&amp;` 연산자를 사용하여 여러 타입을 결합할 수 있음
```typescript
type TUser = {
  name: string;
};

type TJob = {
  readonly title?: string;
};

type TUserAndJob = TUser &amp; TJob;

const user1: TUserAndJob = {
  name: &quot;kim&quot;,
  title: &quot;developer&quot;,
};</code></pre><h3 id="툴팁-지원">툴팁 지원</h3>
<p>VS Code에서는 <code>type</code>으로 정의한 타입은 툴팁으로 확인할 수 있지만, 인터페이스로 정의한 타입은 툴팁에서 바로 확인할 수 없는 경우가 있음</p>
<ul>
<li><p><strong>인터페이스:</strong>
<img src="https://velog.velcdn.com/images/cielo_hello/post/ba121550-5d59-4c26-9343-87ba41a86b68/image.png" alt=""></p>
</li>
<li><p><strong>타입 별칭:</strong>
<img src="https://velog.velcdn.com/images/cielo_hello/post/38dd2038-1871-418c-954d-28ae5d229f5c/image.png" alt=""></p>
</li>
</ul>
<h2 id="제네릭">제네릭</h2>
<p>제네릭은 타입 매개변수를 사용하여 여러 타입을 유연하게 처리할 수 있음</p>
<ul>
<li>즉, 타입을 미리 지정하지 않고 사용하는 시점에 타입을 정의해서 쓸 수 있는 문법</li>
<li>타입 매개변수는 주로 <code>T</code>를 사용하지만, 다른 이름을 사용할 수도 있음</li>
<li>제네릭을 사용하면 함수, 클래스, 인터페이스, 타입 별칭 등을 작성할 때 구체적인 타입을 나중에 지정할 수 있음</li>
</ul>
<h3 id="기본-예시">기본 예시</h3>
<pre><code class="language-typescript">const firstElements = &lt;T&gt;(elements: T[]): T =&gt; {
  return elements[0];
};

console.log(firstElements&lt;number&gt;([1, 2, 3])); // 1
console.log(firstElements&lt;string&gt;([&quot;a&quot;, &quot;b&quot;, &quot;c&quot;])); // &quot;a&quot;
console.log(firstElements&lt;boolean&gt;([true, false])); // true</code></pre>
<ul>
<li>위 코드에서 <code>firstElements</code> 함수는 배열의 첫 번째 요소를 반환함 </li>
<li>함수 정의에서 <code>&lt;T&gt;</code>는 타입 매개변수를 선언한 것임</li>
<li>호출할 때 <code>firstElements&lt;number&gt;([1, 2, 3])</code>와 같이 구체적인 타입을 전달하여 사용할 수 있음</li>
</ul>
<h3 id="제네릭을-사용한-인터페이스와-클래스">제네릭을 사용한 인터페이스와 클래스</h3>
<p>제네릭은 인터페이스와 클래스에서도 사용할 수 있음</p>
<h4 id="인터페이스에서-제네릭-사용">인터페이스에서 제네릭 사용</h4>
<pre><code class="language-typescript">// 예시 1
interface Container&lt;T&gt; {
  value: T;
}

const stringContainer: Container&lt;string&gt; = { value: &quot;Hello&quot; };
const numberContainer: Container&lt;number&gt; = { value: 123 };

console.log(stringContainer.value); // &quot;Hello&quot;
console.log(numberContainer.value); // 123</code></pre>
<ul>
<li><code>Container</code> 인터페이스는 제네릭 타입 <code>T</code>를 가지며, <code>value</code> 속성은 <code>T</code> 타입임</li>
<li>이를 통해 다양한 타입의 값을 가지는 컨테이너를 정의할 수 있음</li>
</ul>
<pre><code class="language-typescript">// 예시 2
type TCar&lt;T&gt; = {
    name: string;
    options: T;
  };

  const car1: TCar&lt;string&gt; = {
    name: &quot;sonata&quot;,
    options: &quot;auto&quot;,
  };

  const car2: TCar&lt;string[]&gt; = {
    name: &quot;sonata&quot;,
    options: [&quot;auto&quot;, &quot;sunroof&quot;],
  };</code></pre>
<h4 id="클래스에서-제네릭-사용">클래스에서 제네릭 사용</h4>
<pre><code class="language-typescript">class Box&lt;T&gt; {
  contents: T;

  constructor(contents: T) {
    this.contents = contents;
  }

  getContents(): T {
    return this.contents;
  }
}

const stringBox = new Box&lt;string&gt;(&quot;Hello&quot;);
console.log(stringBox.getContents()); // &quot;Hello&quot;

const numberBox = new Box&lt;number&gt;(123);
console.log(numberBox.getContents()); // 123</code></pre>
<ul>
<li><code>Box</code> 클래스는 제네릭 타입 <code>T</code>를 가지며, <code>contents</code> 속성과 <code>getContents</code> 메서드는 <code>T</code> 타입임</li>
<li>이를 통해 다양한 타입의 값을 가지는 박스를 정의할 수 있음</li>
</ul>
<h3 id="제네릭-타입-제약-type-constraints">제네릭 타입 제약 (Type Constraints)</h3>
<ul>
<li>제네릭 타입 매개변수에 제약을 추가하여 특정 조건을 만족하는 타입만 허용할 수 있음</li>
<li>이는 <code>extends</code> 키워드를 사용하여 제네릭 타입 매개변수에 제약을 두는 방식으로 구현됨</li>
</ul>
<pre><code class="language-typescript">const getLength = &lt;T extends { length: number }&gt;(item: T): number =&gt; {
  return item.length;
};

console.log(getLength([1, 2, 3])); // 3
console.log(getLength(&quot;Hello&quot;)); // 5
console.log(getLength({ length: 10 })); // 10
// console.log(getLength(10)); // 오류 발생: &#39;number&#39; 형식에는 &#39;length&#39; 속성이 없음</code></pre>
<ul>
<li>위 코드에서 <code>getLength</code> 함수는 <code>length</code> 속성을 가진 객체만 받을 수 있도록 제약을 두었음</li>
<li><code>T extends { length: number }</code>는 <code>T</code> 타입이 <code>length</code> 속성을 가져야 함을 명시함</li>
<li>따라서, 배열, 문자열, <code>length</code> 속성을 가진 객체는 사용할 수 있지만, 숫자와 같은 타입은 사용할 수 없음</li>
</ul>
<h3 id="제네릭을-활용한-고급-예제">제네릭을 활용한 고급 예제</h3>
<h4 id="제네릭을-사용한-다중-타입-매개변수">제네릭을 사용한 다중 타입 매개변수</h4>
<p>제네릭을 사용할 때, 다중 타입 매개변수를 정의할 수도 있음</p>
<pre><code class="language-typescript">function mapPair&lt;K, V&gt;(key: K, value: V): [K, V] {
  return [key, value];
}

const pair1 = mapPair&lt;string, number&gt;(&quot;age&quot;, 30);
const pair2 = mapPair&lt;number, boolean&gt;(1, true);

console.log(pair1); // [&quot;age&quot;, 30]
console.log(pair2); // [1, true]</code></pre>
<ul>
<li>위 코드에서 <code>mapPair</code> 함수는 두 개의 타입 매개변수 <code>K</code>와 <code>V</code>를 가지며, <code>key</code>와 <code>value</code>의 타입을 각각 <code>K</code>와 <code>V</code>로 지정함</li>
<li>반환 값은 <code>[K, V]</code> 타입의 튜플임</li>
</ul>
<h4 id="제네릭을-사용한-인터페이스-확장">제네릭을 사용한 인터페이스 확장</h4>
<p>제네릭 인터페이스를 다른 인터페이스가 확장할 수 있음</p>
<pre><code class="language-typescript">interface Response&lt;T&gt; {
  data: T;
  status: number;
  error?: string;
}

interface User {
  name: string;
  age: number;
}

const userResponse: Response&lt;User&gt; = {
  data: { name: &quot;John&quot;, age: 30 },
  status: 200,
};

console.log(userResponse.data.name); // &quot;John&quot;
console.log(userResponse.status); // 200</code></pre>
<ul>
<li><code>Response</code> 인터페이스는 제네릭 타입 <code>T</code>를 가지며, <code>data</code> 속성의 타입을 <code>T</code>로 지정함</li>
<li>이를 통해 다양한 타입의 응답 데이터를 처리할 수 있음</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 4일차(Class, 표준 내장 객체, 비동기)]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-4%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-4%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 19 Jul 2024 11:13:29 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트를 처음 배울 때, 표준 내장 객체를 이용해 코드를 간단히 작성할 수 있다는 점이 매우 신기했다. 반복문이나 조건문을 통해 힘들게 로직을 구성하지 않아도 데이터를 원하는 대로 가공할 수 있었기 때문이다. 마치 마법 같았다. 하지만 콜백 함수를 넘겨주는 방식에 익숙하지 않고, 메소드마다 반환값이 달라서 생소하고 어려운 것도 사실이었다. 그래도 오늘 수업을 통해 조금 더 익숙해 졌다. 특히 메소드 체이닝을 많이 연습할 수 있어서 유익한 시간이었다. 또, <code>reduce</code>를 이용한 연습 문제 풀이가 마음에 든다.</p>
<pre><code class="language-js">//6. 남학생들의 평균 연령 구하기
  const maleTotalInfo = students.reduce(
    (accumulator, currentValue) =&gt; {
      if (currentValue.gender === &#39;male&#39;) {
        accumulator.maleTotalAge += currentValue.age;
        accumulator.maleTotalNum += 1;
      }
      return accumulator;
    },
    { maleTotalAge: 0, maleTotalNum: 0 }
  );

  const maleAverageAge = maleTotalInfo.maleTotalAge / maleTotalInfo.maleTotalNum;
  console.log(maleAverageAge);</code></pre>
<p>비동기를 처리하는 방법으로는 콜백 함수, Promise, async/await가 있다는 사실을 알고 있었지만, 자세히 설명할 자신은 없었다. 특히 Promise는 다시 공부해야겠다는 생각이 들 정도로 어려웠다. 사실 오늘도 완벽히 이해했다고 말하기는 어려울 것 같다. 집에 돌아오는 지하철에서 관련 동영상을 여러 개 보았지만, 여전히 부족한 느낌이었다. 대신, 조금 더 익숙해진 것은 사실이다. 이제 콜백 지옥에 대해서는 코드 예시를 통해 설명할 수 있을 것 같다. 오늘 특히 좋았던 부분은 콜백 지옥 코드를 Promise와 async/await를 이용해서 개선해보는 시간이었다.</p>
<h1 id="12class">12.Class</h1>
<ul>
<li>자바스크립트는 프로토타입 기반의 언어로 원래 클래스가 없었음</li>
<li>기존의 문법을 활용해 편의성이나 기능을 더한 문법을 &quot;syntactic sugar&quot;라고 하는데, ES6에서 추가된 클래스가 바로 이러한 문법적 설탕 중 하나<ul>
<li>생성자 함수와 클래스의 작동 원리는 같으나, 보다 유용하게 사용하기 위한 문법적 기능들이 추가된 것</li>
</ul>
</li>
</ul>
<h2 id="생성자-함수-vs-클래스">생성자 함수 vs 클래스</h2>
<h3 id="생성자-함수로-작성한-코드를-클래스로-바꿔-보기">생성자 함수로 작성한 코드를 클래스로 바꿔 보기</h3>
<h4 id="생성자-함수">생성자 함수</h4>
<pre><code class="language-jsx">function Shape(color) {
  this.color = color;
  this.getColor = function () {
    return `이 도형의 색상은 ${this.color}입니다.`;
  };
}
const shape1 = new Shape(&#39;red&#39;);

function Rectangle(color, width, height) {
  Shape.call(this, color); // Shape를 상속받게 하기
  this.color = color;
  this.width = width;
  this.height = height;
  this.getArea = function () {
    return this.width * this.height;
  };
}

const rect1 = new Rectangle(&#39;blue&#39;, 20, 20);

console.log(shape1);</code></pre>
<h3 id="클래스">클래스</h3>
<pre><code class="language-jsx">class Shape {
  constructor(color) {
    this.color = color;
  }

  getColor() {
    return `이 도형의 색상은 ${this.color}입니다.`;
  }
}

const shape1 = new Shape(&#39;red&#39;);

class Rectangle extends Shape {  // Shape 상속
  constructor(color, width, height) {
    super(color);
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

const rect1 = new Rectangle(&#39;blue&#39;, 20, 20);

console.log(shape1);
console.dir(rect1);</code></pre>
<h3 id="비교">비교</h3>
<ol>
<li>클래스는 프로토타입을 명시하지 않아도 자체적으로 프로토타입에 포함되므로 인스턴스 메모리 최적화가 자동으로 이루어짐<ul>
<li>클래스로 만든 <code>shap1</code> 객체의 메소드 <code>getColor</code>가 프로토타입 내부에 존재함을 알 수 있음</li>
<li>클래스로 만든 <code>rect1</code> 객체는 <code>Shape</code>를 상속 받아서 프로토타입의 프로토타입의 <code>constructor</code>가 <code>class Shape</code>이며 <code>getColor</code>도 여기 존재함을 알 수 있음</li>
</ul>
</li>
</ol>
<p>&lt;콘솔에 출력된 결과 (생상자 함수<code>shape1</code> / 클래스 <code>shape1</code> / 클래스 <code>rect1</code>)&gt;</p>
<table>
<thead>
<tr>
<th><img src="https://velog.velcdn.com/images/cielo_hello/post/cfa852f9-367e-4568-b06f-211b13ec195b/image.png" alt="생성자 함수"></th>
<th><img src="https://velog.velcdn.com/images/cielo_hello/post/40f3494e-5512-4250-891e-a28824778bf5/image.png" alt="클래스"></th>
<th><img src="https://velog.velcdn.com/images/cielo_hello/post/63a442bc-6bff-4f8a-8e4e-95d321c62112/image.png" alt=""></th>
</tr>
</thead>
</table>
<ol start="2">
<li>클래스가 보다 직관적이어서 코드 가독성이 좋고 유지보수가 용이함</li>
</ol>
<h4 id="setter">setter</h4>
<p>Setter 메서드는 클래스의 특정 속성에 값을 설정할 때 사용자 정의 검사를 수행할 수 있게 함</p>
<ul>
<li>아래 예제에서는 <code>Car</code> 클래스에서 <code>speed</code> 속성을 설정할 때 음수 값을 허용하지 않도록 검사하는 <code>setter</code>를 정의함</li>
</ul>
<pre><code class="language-jsx">class Car {
  constructor(speed) {
    this.speed = speed;
  }

  set speed(speed) {
    if (speed &lt; 0) {
      throw new Error(&#39;속도는 음수가 될 수 없습니다.&#39;);
    }
    this._speed = speed; // 내부 속성에 저장
  }

  getSpeed() {
    return `현재 속도는 ${this._speed}입니다.`;
  }
}

const car1 = new Car(100);
console.log(car1.getSpeed()); // 현재 속도는 100입니다.</code></pre>
<ul>
<li>Setter 메서드를 올바르게 사용하지 않으면 무한 호출이 발생할 수 있으므로<code>this.speed = speed</code>를 <code>this._speed = speed</code>로 변경하여 이를 해결함</li>
</ul>
<img src="https://velog.velcdn.com/images/cielo_hello/post/f77d62c5-e7f2-48ca-9328-da6ecf6907cf/image.png" width="50%">


<h4 id="getter">Getter</h4>
<p>Getter 메서드는 속성 값을 참조할 때 사용됨</p>
<ul>
<li><code>Car</code> 클래스의 예제에서는 <code>speed</code> 속성을 안전하게 참조할 수 있도록 getter를 추가함</li>
</ul>
<pre><code class="language-jsx">class Car {
  constructor(speed) {
    this.speed = speed;
  }

  set speed(speed) {
    if (speed &lt; 0) {
      throw new Error(&#39;속도는 음수가 될 수 없습니다.&#39;);
    }
    this._speed = speed;
  }

  get speed() {
    return this._speed;
  }

  getSpeed() {
    return `현재 속도는 ${this.speed}입니다.`;
  }
}

const car1 = new Car(100);
console.log(car1.getSpeed()); // 현재 속도는 100입니다.</code></pre>
<h4 id="private-속성">Private 속성</h4>
<p>클래스 내에서만 접근 가능한 속성을 정의할 때는 <code>#</code> 기호를 사용하여 private 속성을 만듦, 따라서 <code>setter</code>와 양립할 수 없음</p>
<pre><code class="language-jsx">class Car {
  #name; // private 속성

  constructor(name, speed) {
    this.#name = name;
    this.speed = speed;
  }

  set speed(speed) {
    if (speed &lt; 0) {
      throw new Error(&#39;속도는 음수가 될 수 없습니다.&#39;);
    }
    this._speed = speed;
  }

  get speed() {
    return this._speed;
  }

  get name() {
    return this.#name; // private 속성 접근에 이런 식으로 접근 해도 값을 바꿀 수 없음
  }

  getCarName() {
    return `차 이름은 ${this.#name}입니다.`;
  }

  get getSpeed() {
    return `현재 속도는 ${this.speed}입니다.`;
  }
}

const car1 = new Car(&#39;벤츠&#39;, 100);
car1.name = &#39;아우디&#39;; // private 속성은 외부에서 변경되지 않음

console.log(car1.getCarName()); // 차 이름은 벤츠입니다.</code></pre>
<h4 id="static-메서드와-속성">Static 메서드와 속성</h4>
<p>정적 메서드와 속성은 클래스 자체에 속하며 멤버 속성이나 프로토타입 속성에 포함되지 않으므로 인스턴스가 아닌 클래스 이름으로 호출해야 함</p>
<pre><code class="language-jsx">class Car {
  #name; // private 속성

  static CREATED = &#39;2022&#39;;

  constructor(name, speed) {
    this.#name = name;
    this.speed = speed;
  }

  set speed(speed) {
    if (speed &lt; 0) {
      throw new Error(&#39;속도는 음수가 될 수 없습니다.&#39;);
    }
    this._speed = speed;
  }

  get speed() {
    return this._speed;
  }

  get name() {
    return this.#name;
  }

  getCarName() {
    return `차 이름은 ${this.#name}입니다.`;
  }

  get getSpeed() {
    return `현재 속도는 ${this.speed}입니다.`;
  }

  // 정적 메서드
  static getSpec() {
    return `차는 타이어 4개와 문 4개가 있습니다.`;
  }
}

const car1 = new Car(&#39;벤츠&#39;, 100);

console.log(Car.getSpec()); // 차는 타이어 4개와 문 4개가 있습니다.
console.log(Car.CREATED); // 2022</code></pre>
<img src="https://velog.velcdn.com/images/cielo_hello/post/0cc574da-e59e-423f-9525-d3d68288a411/image.png" width="50%">

<ul>
<li><p>인스턴스로 호출하면 다음과 같은 에러가 발생함</p>
<pre><code class="language-js">console.log(car1.getSpec());</code></pre>
<img src="https://velog.velcdn.com/images/cielo_hello/post/c420bd5b-c2a8-4ee5-8142-00e487f20c27/image.png" width="50%">
</li>
<li><p>표준 내장 객체 중 <code>Math</code> 객체에는 정적 메서드와 속성만이 포함되어 있음</p>
<ul>
<li>Math 객체는 인스턴스를 만들 수 없으며, Math의 메서드와 속성은 항상 Math 이름을 통해서만 접근할 수 있음<pre><code class="language-js">console.log(Math.PI); // Math 객체의 정적 속성
console.log(Math.max(1, 2, 3)); // Math 객체의 정적 메서드</code></pre>
</li>
</ul>
</li>
</ul>
<hr>
<h1 id="13-표준-내장-객체-standard-built-in-objects">13. 표준 내장 객체 (Standard Built-in Objects)</h1>
<p>자바스크립트 엔진에 기본으로 내장되어 있는 객체들은 자바스크립트 엔진이 상시적으로 제공하는 기능으로, 어디서든 활용할 수 있음</p>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects">표준 내장 객체 종류</a></li>
</ul>
<h2 id="리터럴-표기법과-생성자-함수">리터럴 표기법과 생성자 함수</h2>
<p>자바스크립트에서 데이터를 생성하는 두 가지 주요 방법: 리터럴 표기법과 생성자 함수</p>
<h3 id="생성자-함수-1">생성자 함수</h3>
<ul>
<li>생성자 함수를 사용하여 데이터를 생성할 수 있음<pre><code class="language-javascript">  const str = new String(&#39;Hello&#39;);</code></pre>
<ul>
<li>이 방식은 문자열을 객체로 감싸서 다양한 메서드와 프로퍼티를 사용할 수 있게 함</li>
<li>그러나 생성자 함수를 사용하는 방식은 코드가 복잡해지고, 필요 이상으로 메모리를 사용할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="리터럴-표기법">리터럴 표기법</h3>
<ul>
<li>보다 편리하게 데이터를 생성하는 방법으로 리터럴 표기법이 있음<pre><code class="language-javascript">  const str = &quot;Hello&quot;;</code></pre>
<ul>
<li>리터럴 표기법은 간결하며 메모리를 효율적으로 사용함</li>
<li>문자열 리터럴은 기본 데이터 타입으로 저장되며, 자바스크립트 엔진이 필요할 때 자동으로 객체처럼 취급하여 메서드와 프로퍼티를 사용할 수 있음</li>
</ul>
</li>
</ul>
<h3 id="리터럴-표기법과-생성자-함수-비교">리터럴 표기법과 생성자 함수 비교</h3>
<ul>
<li><p>리터럴 표기법으로 작성된 데이터가 프로토타입 객체를 사용할 수 있는 이유는 자바스크립트 엔진이 일시적으로 인스턴스 객체처럼 래핑(wrapping)하기 때문임</p>
<pre><code class="language-javascript">  const str = new String(&#39;Hello&#39;);
  console.dir(str);

  const str2 = &#39;Hello&#39;;
  console.dir(str2);</code></pre>
  <img src="https://velog.velcdn.com/images/cielo_hello/post/d33579db-8087-494e-b608-a5c67c489059/image.png" width="50%">

<ul>
<li>문자열 리터럴 <code>str2</code>는 기본 타입으로 저장되지만, 메서드를 호출할 때 임시로 객체로 변환됨</li>
<li>이 과정에서 문자열 리터럴도 생성자 함수로 생성된 객체와 동일한 메서드와 프로퍼티를 사용할 수 있음</li>
</ul>
</li>
</ul>
<ol>
<li><p><strong>리터럴 표기법</strong></p>
<pre><code class="language-javascript"> const str = &quot;Hello&quot;;</code></pre>
<ul>
<li>문자열을 기본 타입으로 생성</li>
<li>메모리를 효율적으로 사용</li>
<li>자주 사용되는 방식으로, 코드가 간결함</li>
<li>객체의 메서드와 프로퍼티를 사용할 수 있음 (자바스크립트 엔진이 자동으로 객체처럼 취급)</li>
</ul>
</li>
<li><p><strong>생성자 함수</strong></p>
<pre><code class="language-javascript"> const str = new String(&#39;Hello&#39;);</code></pre>
<ul>
<li>문자열을 객체 타입으로 생성</li>
<li>메모리를 더 많이 사용</li>
<li>객체의 메서드와 프로퍼티를 사용할 수 있음</li>
<li>그러나 대부분의 경우, 문자열을 다루기 위해 굳이 객체로 만들 필요가 없음</li>
</ul>
</li>
</ol>
<h3 id="예시">예시</h3>
<pre><code class="language-javascript">// 리터럴 표기법
const str1 = &quot;Hello&quot;;
console.log(str1.length);  // 5
console.log(str1.toUpperCase());  // &quot;HELLO&quot;

// 생성자 함수
const str2 = new String(&#39;Hello&#39;);
console.log(str2.length);  // 5
console.log(str2.toUpperCase());  // &quot;HELLO&quot;</code></pre>
<ul>
<li>두 방식 모두 문자열의 메서드와 프로퍼티를 사용할 수 있지만, 리터럴 표기법이 더 간결하고 효율적임</li>
<li>따라서 자바스크립트에서는 일반적으로 리터럴 표기법을 사용함</li>
</ul>
<hr>
<h3 id="❓numobject는-객체인데-consolelognumliteral--numobject가-왜-20일까">❓<code>numObject</code>는 객체인데 <code>console.log(numLiteral + numObject);</code>가 왜 20일까?</h3>
<pre><code class="language-javascript">let numLiteral = 10;
let numObject = new Number(10);

console.log(numLiteral + numObject); // 20
console.log(numLiteral == numObject); // true</code></pre>
<h4 id="❗-자바스크립트의-타입-변환-규칙-때문임">❗ 자바스크립트의 타입 변환 규칙 때문임</h4>
<ul>
<li><p>자바스크립트의 타입 변환 (Type Conversion)</p>
<ul>
<li><p>자바스크립트는 피연산자가 서로 다른 타입일 때 자동으로 타입을 변환하는 능력이 있음</p>
</li>
<li><p>이 과정은 &quot;암묵적 타입 변환&quot; 또는 &quot;타입 강제 변환&quot;이라고 부름</p>
<ol>
<li><p><strong>객체에서 기본 값으로의 변환</strong>:</p>
<ul>
<li>자바스크립트는 객체를 기본 타입 값으로 변환할 때, 객체의 <code>valueOf</code> 메서드나 <code>toString</code> 메서드를 호출함</li>
<li><code>Number</code> 객체의 경우, <code>valueOf</code> 메서드가 기본 숫자 값을 반환함</li>
</ul>
</li>
<li><p><strong>산술 연산</strong>:</p>
<ul>
<li>산술 연산 (<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>)을 수행할 때, 자바스크립트는 피연산자를 숫자로 변환하려고 시도함</li>
</ul>
</li>
<li><p><strong>비교 연산</strong>:</p>
<ul>
<li><code>==</code> 연산자는 두 값을 비교할 때 타입을 강제로 변환함. <code>numObject</code>는 객체이지만, 비교를 위해 <code>valueOf</code> 메서드를 호출하여 기본 값인 10을 얻음</li>
<li>따라서 <code>10 == 10</code>이 되어 <code>true</code>를 반환함</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<h4 id="참고-자료">참고 자료</h4>
<p><a href="https://ko.javascript.info/object-toprimitive">객체를 원시형으로 변환하기</a></p>
<blockquote>
<p>참고: <code>===</code> 연산자는 타입을 변환하지 않고 비교하기 때문에, <code>numLiteral === numObject</code>는 <code>false</code>가 됨</p>
</blockquote>
<pre><code class="language-javascript">console.log(numLiteral === numObject); // false</code></pre>
<hr>
<h3 id="❓생성자-함수로-만든-문자열은-메모리에-어떻게-저장되는가">❓생성자 함수로 만든 문자열은 메모리에 어떻게 저장되는가?</h3>
<h4 id="❗다음-그림과-같이-리터럴-표기법으로-만든-문자열은-스택에-저장되고-생성자-함수로-만든-문자열은-힙에-저장됨">❗다음 그림과 같이 리터럴 표기법으로 만든 문자열은 스택에 저장되고, 생성자 함수로 만든 문자열은 힙에 저장됨</h4>
<img src="https://velog.velcdn.com/images/cielo_hello/post/b28eb05e-7a71-4613-bf91-223d6085a47f/image.png" width="50%">

<ol>
<li><p><strong>기본 타입(Primitive Types)</strong>:</p>
<ul>
<li>숫자, 문자열, 불리언 등 기본 타입은 일반적으로 스택(Stack)에 저장됨. 이 값들은 불변(immutable)하며, 실제 값이 스택에 저장됨</li>
</ul>
</li>
<li><p><strong>객체 타입(Object Types)</strong>:</p>
<ul>
<li>객체, 배열, 함수 등 복합 타입은 힙(Heap)에 저장됨. 힙은 동적으로 할당된 메모리 블록들을 저장하는 데 사용됨</li>
</ul>
</li>
<li><p><strong>문자열의 경우</strong>:</p>
<ul>
<li>리터럴 표기법으로 생성된 문자열은 기본 타입으로 저장됨. 기본 타입은 스택에 저장됨</li>
<li>생성자 함수를 사용하여 생성된 문자열은 <code>String</code> 객체로, 힙에 저장됨. <code>String</code> 객체는 래퍼 객체로, 문자열 값을 래핑하고 있으며, 추가 메서드와 프로퍼티를 제공함</li>
</ul>
</li>
</ol>
<h4 id="예시-코드">예시 코드</h4>
<pre><code class="language-javascript">let strLiteral = &quot;Hello&quot;;            // 기본 타입 문자열, 스택에 저장
let strObject = new String(&quot;Hello&quot;); // String 객체, 힙에 저장</code></pre>
<ul>
<li><code>strLiteral</code>은 기본 타입 문자열로, 스택에 값이 저장됨</li>
<li><code>strObject</code>는 <code>String</code> 객체로, 힙에 객체가 저장되며, 스택에는 힙에 있는 객체를 가리키는 참조(reference)가 저장됨</li>
</ul>
<h4 id="참고-자료-1">참고 자료</h4>
<p><a href="https://medium.com/@selmankoral/memory-management-in-javascript-781e1098e5b3">Memory Management in JavaScript</a>
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_management">Memory management</a></p>
<hr>
<h3 id="❓자바스크립트에서-실행컨텍스트가-생성되는-call-stack과-원시값이-저장되는-메모리-상의-stack은-다른-것인가">❓자바스크립트에서 실행컨텍스트가 생성되는 call stack과 원시값이 저장되는 메모리 상의 stack은 다른 것인가?</h3>
<h4 id="❗자바스크립트-엔진은-원시-타입-값을-콜-스택에-참조-타입-값을-힙에-저장하며-콜-스택은-원시-타입-값과-함수-호출의-실행-컨텍스트를-저장하는-곳임">❗자바스크립트 엔진은 원시 타입 값을 콜 스택에, 참조 타입 값을 힙에 저장하며, 콜 스택은 원시 타입 값과 함수 호출의 실행 컨텍스트를 저장하는 곳임</h4>
<img src="https://velog.velcdn.com/images/cielo_hello/post/f0df3fa6-b4f8-4e54-90b1-568edbbcaee4/image.png" width="70%">

<ul>
<li>스택과 힙 메모리 사용 시각화
<a href="https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap?slide=10">메모리 사용 과정 보기</a>
<img src="https://velog.velcdn.com/images/cielo_hello/post/e8d6d52d-588e-482d-bac3-dce616cd2ec3/image.png" alt=""></li>
</ul>
<ol>
<li>전역 스코프는 스택에서 &quot;전역 프레임&quot;에 보관됨</li>
<li>모든 함수 호출은 프레임 블록으로 스택 메모리에 추가됨</li>
<li>반환 값과 인자를 포함한 <strong>모든 지역 변수들은 스택에서 함수 프레임 블록 안에 저장</strong>됨</li>
<li>int와 string과 같은 모든 원시 타입 값은 스택에 바로 저장됨. 이는 전역 스코프에서도 적용됨</li>
<li>Employee와 Function과 같은 객체 타입의 값은 힙에서 생성되고 스택 포인터를 사용해 힙에서 스택을 참조함. 전역 스코프에도 적용됨</li>
<li>현재 함수에서 호출된 함수들은 스택의 최상단에 추가됨(실행 컨텍스트 추가)</li>
<li>함수 프레임이 반환, 즉 함수가 종료될 때 스택에서 제거됨</li>
</ol>
<ul>
<li>결론
자바스크립트 엔진은 원시 타입 값을 콜 스택에, 참조 타입 값을 힙에 저장하며, 콜 스택은 원시 타입 값과 함수 호출의 실행 컨텍스트를 저장하는 곳임. 이 두 스택 메모리 영역은 같은 개념이며, 힙은 동적 데이터가 저장되는 별도의 메모리 영역임</li>
</ul>
<h4 id="참고-자료-2">참고 자료</h4>
<ul>
<li>ChatGPT</li>
<li><a href="https://www.linkedin.com/pulse/understanding-javascript-execution-part-2-exploring-call-kadour/">Understanding JavaScript Engine (Part 2): Exploring the Components of the Call Stack and Memory Heap</a></li>
<li><a href="https://github.com/baeharam/Must-Know-About-Frontend/blob/main/Notes/javascript/stack-heap.md">콜 스택(Call stack)과 힙(Heap)</a></li>
<li><a href="https://speakerdeck.com/deepu105/v8-memory-usage-stack-and-heap?slide=10">V8 Memory usage(Stack &amp; Heap)</a></li>
<li><a href="https://ui.toast.com/weekly-pick/ko_20200228">V8 엔진(자바스크립트, NodeJS, Deno, WebAssembly) 내부의 메모리 관리 시각화하기</a></li>
<li><a href="https://charming-kyu.tistory.com/19">[javascript] 콜스택/메모리힙 구조, 데이터 저장/참조 원리
</a></li>
</ul>
<hr>
<h2 id="14-배열-내장-객체-array-built-in-objects">14. 배열 내장 객체 (Array Built-in Objects)</h2>
<h3 id="파괴적-메서드-mutative-methods">파괴적 메서드 (Mutative Methods)</h3>
<ul>
<li><p>인스턴스 메서드를 호출했을 때, 원본 데이터가 변경되는 메서드</p>
<pre><code class="language-javascript">  const arr = [1, 2, 3];
  arr.push(4);  // [1, 2, 3, 4]
  arr.unshift(0);  // [0, 1, 2, 3, 4]
  arr.pop();  // [0, 1, 2, 3]
  arr.shift();  // [1, 2, 3]
  arr.splice(1, 1);  // [1, 3] (index 1부터 1개 요소 제거)
  arr.reverse();  // [3, 1]
  arr.sort();  // [1, 3]</code></pre>
<ul>
<li><code>push</code>: 배열의 끝에 요소를 추가</li>
<li><code>unshift</code>: 배열의 시작에 요소를 추가</li>
<li><code>pop</code>: 배열의 끝 요소를 제거하고 반환</li>
<li><code>shift</code>: 배열의 첫 요소를 제거하고 반환</li>
<li><code>splice</code>: 배열의 특정 위치에 요소를 추가하거나 제거</li>
<li><code>reverse</code>: 배열의 요소 순서를 반대로 변경</li>
<li><code>sort</code>: 배열의 요소를 정렬</li>
</ul>
</li>
</ul>
<h3 id="비파괴적-메서드-non-mutative-methods">비파괴적 메서드 (Non-mutative Methods)</h3>
<ul>
<li><p>이 메서드를 호출했을 때, 원본 데이터가 변경되지 않는 메서드</p>
<pre><code class="language-javascript">  const arr = [1, 2, 3, 4];
  const filteredArr = arr.filter(num =&gt; num &gt; 2);  // [3, 4]
  const mappedArr = arr.map(num =&gt; num * 2);  // [2, 4, 6, 8]
  const sum = arr.reduce((acc, num) =&gt; acc + num, 0);  // 10
  const concatenatedArr = arr.concat([5, 6]);  // [1, 2, 3, 4, 5, 6]
  const slicedArr = arr.slice(1, 3);  // [2, 3]
  const hasSome = arr.some(num =&gt; num &gt; 2);  // true
  const allAboveZero = arr.every(num =&gt; num &gt; 0);  // true
  const found = arr.find(num =&gt; num === 3);  // 3
  const foundIndex = arr.findIndex(num =&gt; num === 3);  // 2</code></pre>
<ul>
<li><code>filter</code>: 조건에 맞는 요소들로 새로운 배열을 만듦</li>
<li><code>map</code>: 모든 요소에 대해 주어진 함수를 호출한 결과로 새로운 배열을 만듦</li>
<li><code>reduce</code>: 배열을 순회하며 누산기(accumulator)를 사용해 값을 하나로 줄임</li>
<li><code>concat</code>: 두 배열을 합쳐서 새로운 배열을 만듦</li>
<li><code>slice</code>: 배열의 일부분을 잘라내어 새로운 배열을 만듦</li>
<li><code>some</code>: 조건에 맞는 요소가 하나라도 있는지 여부를 확인하여 true 또는 false를 반환</li>
<li><code>every</code>: 모든 요소가 조건에 맞는지 여부를 확인하여 true 또는 false를 반환</li>
<li><code>find</code>: 조건에 맞는 첫 번째 요소를 반환</li>
<li><code>findIndex</code>: 조건에 맞는 첫 번째 요소의 인덱스를 반환</li>
</ul>
</li>
</ul>
<hr>
<h2 id="16-math-객체">16. Math 객체</h2>
<ul>
<li>Math 객체는 다양한 수학적 계산을 쉽게 수행할 수 있게 해주는 메서드들을 포함하고 있으며, 인스턴스를 생성할 필요 없이 직접 호출하여 사용할 수 있음<pre><code class="language-javascript">  console.log(Math.random());  // 0과 1 사이의 난수 생성
  console.log(Math.max(1, 2, 3));  // 3
  console.log(Math.min(1, 2, 3));  // 1
  console.log(Math.pow(2, 3));  // 8</code></pre>
<ul>
<li><code>Math.random()</code>: 0과 1 사이의 난수를 반환</li>
<li><code>Math.max()</code>: 전달된 인수 중 최대값을 반환</li>
<li><code>Math.min()</code>: 전달된 인수 중 최소값을 반환</li>
<li><code>Math.pow()</code>: 제곱 값을 반환</li>
</ul>
</li>
</ul>
<hr>
<h1 id="자바스크립트의-동기와-비동기">자바스크립트의 동기와 비동기</h1>
<p><strong>자바스크립트는 싱글 스레드 언어</strong>로 한 번에 하나의 작업만 처리할 수 있음을 의미하며 동기적으로 실행됨</p>
<ul>
<li><strong>동기 (Synchronous)</strong>: 코드가 순차적으로 실행되며, 코드의 순서가 보장됨</li>
<li><strong>비동기 (Asynchronous)</strong>: 코드가 순차적으로 실행되지 않으며, 코드의 순서가 보장되지 않음</li>
</ul>
<h2 id="동기-실행의-문제점">동기 실행의 문제점</h2>
<ul>
<li><strong>동기 실행</strong>에서는 모든 코드가 순서대로 실행되기 때문에, 실행 시간이 오래 걸리는 작업이 있을 경우 해당 작업이 완료될 때까지 자바스크립트 엔진은 다른 작업을 수행할 수 없음</li>
</ul>
<h2 id="비동기-실행의-필요성">비동기 실행의 필요성</h2>
<ul>
<li><strong>비동기 실행</strong>을 통해 자바스크립트는 실행 시간이 오래 걸리는 작업을 백그라운드에서 처리할 수 있음</li>
<li>이는 자바스크립트 엔진이 긴 작업을 처리하는 동안에도 다른 작업을 계속 수행할 수 있게 함 </li>
<li>예를 들어, 네트워크 요청을 보내고 응답을 기다리는 동안 UI가 멈추지 않고 계속 반응할 수 있음</li>
</ul>
<h2 id="결론">결론</h2>
<ul>
<li>비동기 실행은 자바스크립트의 싱글 스레드 특성으로 인해 발생할 수 있는 블로킹 문제를 해결함</li>
<li>실행 시간이 오래 걸리는 작업을 비동기로 처리함으로써, 사용자 경험을 향상시키고 애플리케이션의 성능을 최적화할 수 있음</li>
<li>비동기 실행을 통해 자바스크립트는 동시에 여러 작업을 효율적으로 처리할 수 있음</li>
</ul>
<h4 id="❓싱글스레드와-병렬-실행비동기의-차이">❓싱글스레드와 병렬 실행(비동기)의 차이</h4>
<h4 id="❓자바스크립트-엔진은-비동기로-처리할지-말지를-어떻게-결정하지">❓자바스크립트 엔진은 비동기로 처리할지 말지를 어떻게 결정하지?</h4>
<hr>
<h1 id="17-콜백-함수">17. 콜백 함수</h1>
<p>다른 함수의 매개변수로 전달되어 그 함수가 실행되는 동안 특정 시점에 호출되는 함수</p>
<ul>
<li>콜백 함수는 동기적일 수도 있고 비동기적일 수도 있음</li>
</ul>
<h2 id="동기-콜백-함수">동기 콜백 함수</h2>
<p>동기 콜백 함수는 즉시 실행되는 콜백 함수를 의미함</p>
<pre><code class="language-javascript">function greeting(callbackFn) {
  console.log(&#39;Hello&#39;);
  callbackFn();
}

function goodbye() {
  console.log(&#39;goodbye&#39;);
}

greeting(goodbye);</code></pre>
<ul>
<li><code>greeting</code> 함수는 <code>goodbye</code> 콜백을 즉시 실행함</li>
</ul>
<h2 id="비동기-콜백-함수">비동기 콜백 함수</h2>
<p>비동기 콜백 함수는 비동기 작업이 끝난 후 호출되는 콜백 함수를 의미함</p>
<pre><code class="language-javascript">function task1(callback) {
  setTimeout(() =&gt; {
    console.log(&#39;task1 시작&#39;);
    callback();
  }, 1000);
}

function task2() {
  console.log(&#39;task2 시작&#39;);
}

task1(task2);</code></pre>
<ul>
<li><code>task1</code>함수는 <code>task2</code>콜백을 비동기 작업이 끝난 후 실행함</li>
</ul>
<h2 id="콜백-지옥">콜백 지옥</h2>
<p>비동기 콜백을 연속으로 사용하면 코드가 복잡해지고 가독성이 떨어지는 &quot;콜백 지옥&quot;에 빠질 수 있음</p>
<pre><code class="language-js">function task1(callback) {
  setTimeout(() =&gt; {
    console.log(&#39;task1 시작&#39;);
    callback();
  }, 1000);
}

function task2(callback) {
  setTimeout(() =&gt; {
    console.log(&#39;task2 시작&#39;);
    callback();
  }, 1000);
}

function task3(callback) {
  setTimeout(() =&gt; {
    console.log(&#39;task3 시작&#39;);
    callback();
  }, 1000);
}

function task4(callback) {
  setTimeout(() =&gt; {
    console.log(&#39;task4 시작&#39;);
    callback();
  }, 1000);
}

task1(() =&gt; {
  task2(() =&gt; {
    task3(() =&gt; {
      task4(() =&gt; {
        console.log(&#39;모든 작업 끝&#39;);
      });
    });
  });
});</code></pre>
<ul>
<li>이를 해결하기 위해 자바스크립트에서는 Promise 객체를 제공함</li>
</ul>
<h1 id="18-promise-then">18. Promise Then</h1>
<p>Promise는 비동기 작업을 처리할 수 있도록 도와주는 자바스크립트 내장 객체이며 다음과 같은 세 가지 상태를 가짐:</p>
<ul>
<li><strong>pending</strong>: 비동기 처리가 아직 수행되지 않은 상태</li>
<li><strong>fulfilled</strong>: 비동기 처리가 성공적으로 완료된 상태</li>
<li><strong>rejected</strong>: 비동기 처리가 실패한 상태</li>
</ul>
<h2 id="promise-사용-예시">Promise 사용 예시</h2>
<h3 id="전체-코드-흐름">전체 코드 흐름</h3>
<ul>
<li><code>Promise</code> 객체를 사용하면 비동기 작업을 쉽게 관리할 수 있음 </li>
<li><code>Promise</code> 객체를 생성할 때 전달되는 콜백 함수는 즉시 실행됨</li>
<li>이 콜백 함수 내부에서 비동기 작업을 시작할 수 있으며, 작업이 완료되면 <code>resolve</code> 또는 <code>reject</code> 함수를 호출하여 <code>Promise</code> 객체의 상태를 업데이트할 수 있음</li>
</ul>
<pre><code class="language-javascript">const promise = new Promise((resolve, reject) =&gt; {
  console.log(&#39;doing something...&#39;); // 콜백 함수에 있는 코드를 즉시 실행함
  setTimeout(() =&gt; {
    resolve(&#39;success&#39;);
  }, 1000);
});

promise
  .then((value) =&gt; console.log(value))  // &#39;success&#39; 출력
  .catch((error) =&gt; console.error(error))
  .finally(() =&gt; console.log(&#39;finally&#39;));</code></pre>
<ol>
<li><code>Promise</code> 객체가 생성됨</li>
<li>콜백 함수가 즉시 실행되어 <code>console.log(&#39;doing something...&#39;)</code>이 출력됨</li>
<li>1초 후 <code>setTimeout</code>의 콜백이 실행되어 <code>resolve(&#39;success&#39;)</code>가 호출됨</li>
<li><code>resolve</code>가 호출되면 <code>Promise</code> 객체의 상태가 <code>pending</code>에서 <code>fulfilled</code>로 변경됨</li>
<li><code>then</code> 메서드 내부의 콜백 함수가 실행되어 <code>console.log(value)</code>에서 <code>&#39;success&#39;</code>가 출력됨</li>
<li><code>catch</code> 메서드는 호출되지 않으며, <code>finally</code> 메서드가 실행되어 <code>console.log(&#39;finally&#39;)</code>가 출력됨</li>
</ol>
<h2 id="결과-받는-방법">결과 받는 방법</h2>
<p>Promise 객체는 비동기 작업의 결과를 처리하기 위해 <code>then</code>, <code>catch</code>, <code>finally</code> 메서드를 제공함</p>
<h3 id="then">then</h3>
<ul>
<li><code>then</code> 메서드는 <code>Promise</code>가 <code>fulfilled</code> 상태일 때 호출됨</li>
<li>첫 번째 매개변수로 <code>resolve</code> 함수의 결과 값을 받는 콜백 함수를 지정함</li>
<li>두 번째 매개변수로 <code>reject</code> 함수의 결과 값을 받는 콜백 함수를 지정할 수도 있음</li>
<li>그러나 일반적으로 에러 처리는 <code>catch</code> 메서드를 사용하는 것이 가독성 측면에서 더 좋음<pre><code class="language-js">const promise = new Promise((resolve, reject) =&gt; {
const isSuccess = true;
setTimeout(() =&gt; {
  isSuccess ? resolve(&#39;success&#39;) : reject(new Error(&#39;fail&#39;));
});
});
</code></pre>
</li>
</ul>
<p>promise
  .then(
    (value) =&gt; console.log(value),
    (error) =&gt; console.error(error)
  )
  .catch((error) =&gt; console.error(error)) // 만약 에러가 발생해도 catch에서는 에러가 안 걸림
  .finally(() =&gt; console.log(&#39;finally&#39;));</p>
<p>console.log(&#39;hello&#39;);</p>
<pre><code>![](https://velog.velcdn.com/images/cielo_hello/post/690b885b-d5cc-4abf-a9d8-c512a9936700/image.png)


### catch

- `catch` 메서드는 `Promise`가 `rejected` 상태일 때 호출됨
- 콜백 함수는 `reject` 함수의 결과 값을 받음
```js
const promise = new Promise((resolve, reject) =&gt; {
  const isSuccess = false;
  setTimeout(() =&gt; {
    isSuccess ? resolve(&#39;success&#39;) : reject(new Error(&#39;fail&#39;));
  }, 1000);
});

promise
  .then((value) =&gt; console.log(value))
  .catch((error) =&gt; console.error(error))
  .finally(() =&gt; console.log(&#39;finally&#39;));

console.log(&#39;hello&#39;);
</code></pre><p><img src="https://velog.velcdn.com/images/cielo_hello/post/ea683251-7921-42c8-8c5a-4b3c5518bd53/image.png" alt=""></p>
<h3 id="finally">finally</h3>
<ul>
<li><code>finally</code> 메서드는 <code>Promise</code>가 완료되면 무조건 호출됨</li>
<li>성공 또는 실패 여부와 상관없이 항상 실행됨</li>
</ul>
<h3 id="promise의-연속-처리">Promise의 연속 처리</h3>
<ul>
<li><code>then</code>에서 어떤 값을 반환하면 자동으로 <code>resolve</code>가 처리되므로 연속해서 <code>resolve</code>를 처리할 수 있음</li>
<li>그러나 여러 비동기 작업을 처리할 때 <code>then()</code>에서 에러가 발생하면 이후의 <code>then</code>은 실행되지 않는 문제가 있음</li>
</ul>
<pre><code class="language-javascript">const fetchNumber = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; {
    resolve(1);
  }, 1000);
});

fetchNumber
  .then((num) =&gt; new Promise((resolve, reject) =&gt; resolve(num * 2))) // 2 promise resolve(2)
  .then((num) =&gt; num * 3) // 6
  .then((num) =&gt; num * 2) // 12
  .then((num) =&gt; console.log(num))
  .catch((error) =&gt; console.error(error));</code></pre>
<ul>
<li><code>catch</code>를 사용하여 에러 이후의 <code>then</code>을 계속 실행할 수 있지만 일반적인 방법은 아님 </li>
</ul>
<pre><code class="language-javascript">const fetchNumber = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; {
    resolve(1);
  }, 1000);
});

fetchNumber
  .then((num) =&gt; new Promise((resolve, reject) =&gt; reject(num))) // 에러 발생
  .catch((num) =&gt; num) // 에러 처리
  .then((num) =&gt; num * 3) // 3
  .then((num) =&gt; num * 2) // 6
  .then((num) =&gt; console.log(num));</code></pre>
<h2 id="콜백-지옥-해결">콜백 지옥 해결</h2>
<pre><code class="language-javascript">function task1() {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; {
      console.log(&#39;task1 시작&#39;);
      resolve();
    }, 1000);
  });
}

function task2() {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; {
      console.log(&#39;task2 시작&#39;);
      resolve();
    }, 1000);
  });
}

function task3() {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; {
      console.log(&#39;task3 시작&#39;);
      resolve();
    }, 1000);
  });
}

function task4() {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; {
      console.log(&#39;task4 시작&#39;);
      resolve();
    }, 1000);
  });
}

task1()
  .then(() =&gt; task2())
  .then(() =&gt; task3())
  .then(() =&gt; task4())
  .then(() =&gt; {
    console.log(&#39;모든 작업 끝&#39;);
  })
  .catch((error) =&gt; {
    console.error(&#39;에러 발생:&#39;, error);
  });</code></pre>
<hr>
<h1 id="19-async-await">19. async await</h1>
<h2 id="async">async</h2>
<ul>
<li><code>async</code> 키워드를 사용하면 함수가 항상 <code>Promise</code>를 반환함<ul>
<li>즉, <code>async</code> 함수 내에서 반환된 값은 자동으로 <code>Promise.resolve</code>로 감싸짐</li>
<li>또, <code>async</code> 함수 내에서 발생한 에러는 <code>Promise.reject</code>로 처리됨</li>
</ul>
</li>
</ul>
<h3 id="예시-1">예시</h3>
<h4 id="promise를-사용하는-코드"><code>Promise</code>를 사용하는 코드:</h4>
<pre><code class="language-javascript">const getStarIcon = () =&gt;
  new Promise((resolve) =&gt; {
    resolve(&#39;⭐&#39;);
  });

getStarIcon().then((star) =&gt; console.log(star));</code></pre>
<ul>
<li>위 코드에서 <code>getStarIcon</code> 함수는 <code>Promise</code> 객체를 반환하며, <code>then</code> 메서드를 사용하여 <code>resolve</code>된 값을 출력함</li>
</ul>
<pre><code class="language-js">async function example() {
  throw new Error(&#39;Something went wrong&#39;);
}

example().catch((error) =&gt; console.error(error)); // Error: Something went wrong</code></pre>
<ul>
<li>위 코드에서 <code>example</code> 함수는 에러를 던짐</li>
<li>async 함수 내에서 발생한 예외는 자동으로 Promise.reject로 처리되므로, catch 메서드를 사용하여 예외를 처리할 수 있음</li>
</ul>
<h4 id="async-키워드를-사용하여-동일한-기능을-구현한-코드"><code>async</code> 키워드를 사용하여 동일한 기능을 구현한 코드:</h4>
<pre><code class="language-javascript">const getStarIcon = async () =&gt; &#39;⭐&#39;; // 무조건 resolve()
getStarIcon().then((star) =&gt; console.log(star));</code></pre>
<p>여기서 <code>getStarIcon</code> 함수는 <code>async</code> 키워드를 사용하여 자동으로 <code>Promise</code> 객체를 반환하며, 반환된 값은 <code>resolve</code>됨. 따라서 동일하게 <code>then</code> 메서드를 사용하여 값을 출력할 수 있음</p>
<h2 id="await">await</h2>
<ul>
<li><code>await</code> 키워드는 <code>async</code> 함수 내부에서만 사용할 수 있음</li>
<li><code>await</code>는 <code>Promise</code>가 처리될 때까지 기다린 다음, <code>Promise</code>가 처리되면 <code>resolve</code>된 값을 반환함</li>
</ul>
<h3 id="잘못된-예시">잘못된 예시</h3>
<pre><code class="language-javascript">const getStarIcon = async () =&gt; setTimeout(() =&gt; &#39;⭐&#39;, 1000); // 값이 즉시 반환되지 않음</code></pre>
<ul>
<li><code>aysyc</code>는 <code>Promise</code>를 반환할 뿐 비동기 함수를 기다려 주지는 않음</li>
<li>따라서 <code>setTimeout</code>함수를 기다려 주지 않음</li>
<li><code>setTimeout</code>함수는 1초 후에 &#39;⭐&#39;을 반환하지만 <code>async</code>가 기다려 주지 않아서 <code>undefined</code>를 즉시 반환함</li>
<li>따라서 우리가 원하는 &#39;⭐&#39;을 반환 받으려면 <code>Promise</code>가 처리될 때까지 기다려 주는 <code>await</code>가 필요함</li>
<li>또, <code>setTimeout</code>을 <code>Promise</code>로 감싸야 함</li>
</ul>
<h3 id="올바른-예시">올바른 예시</h3>
<p>비동기 처리를 위해 <code>await</code>를 사용하는 예시:</p>
<pre><code class="language-javascript">const delay = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

const getStarIcon = async () =&gt; {
  await delay(1000);
  return &#39;⭐&#39;; // resolve(&quot;⭐&quot;)
};

getStarIcon().then((star) =&gt; console.log(star));</code></pre>
<ul>
<li><code>delay</code> 함수는 <code>Promise</code>를 반환하며 이 Promise는 setTimeout을 사용하여 지정된 시간(ms) 후에 resolve됨</li>
<li><code>getStarIcon</code> 함수 내부에서 <code>await delay(1000)</code>는 <code>delay</code> 함수가 반환한 <code>Promise</code>가 <code>resolve</code>될 때까지 1초 동안 기다림</li>
<li>1초 후, <code>delay</code> 함수의 <code>Promise</code>가 resolve되면 <code>getStarIcon</code> 함수는 <code>⭐</code>을 반환함</li>
<li><code>getStarIcon().then((star) =&gt; console.log(star));</code>는 <code>getStarIcon</code> 함수가 반환한 <code>Promise</code>가 <code>resolve</code>될 때, 즉 <code>⭐</code>이 반환될 때 <code>then</code> 메서드가 실행되어 <code>star</code>를 출력함</li>
</ul>
<h2 id="또-다른-콜백-지옥-예시">또 다른 콜백 지옥 예시</h2>
<h4 id="async와-await를-사용하지-않은-경우의-콜백-지옥"><code>async</code>와 <code>await</code>를 사용하지 않은 경우의 콜백 지옥:</h4>
<pre><code class="language-javascript">const delay = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

const getStarIcon = () =&gt; delay(1000).then(() =&gt; &#39;⭐&#39;);
const getWaveIcon = () =&gt; delay(1000).then(() =&gt; &#39;🌊&#39;);
const getFaceIcon = () =&gt; delay(1000).then(() =&gt; &#39;🥰&#39;);

const getAllIcon = () =&gt; {
  getStarIcon().then((star) =&gt; {
    getWaveIcon().then((wave) =&gt; {
      getFaceIcon().then((face) =&gt; {
        console.log(`${star} ${wave} ${face}`);
      });
    });
  });
};

getAllIcon();</code></pre>
<ul>
<li>위 코드는 여러 비동기 작업이 중첩되어 가독성이 떨어짐</li>
</ul>
<h4 id="async와-await를-사용하여-콜백-지옥을-해결한-예시"><code>async</code>와 <code>await</code>를 사용하여 콜백 지옥을 해결한 예시:</h4>
<pre><code class="language-javascript">const delay = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

const getStarIcon = async () =&gt; {
  await delay(1000);
  return &#39;⭐&#39;;
};

const getWaveIcon = async () =&gt; {
  await delay(1000);
  return &#39;🌊&#39;;
};

const getFaceIcon = async () =&gt; {
  await delay(1000);
  return &#39;🥰&#39;;
};

const getAllIcon = async () =&gt; {
  const star = await getStarIcon();
  const wave = await getWaveIcon();
  const face = await getFaceIcon();

  console.log(`${star} ${wave} ${face}`);
};

getAllIcon();</code></pre>
<ul>
<li>위 코드는 <code>async</code>와 <code>await</code>를 사용하여 비동기 작업을 순차적으로 실행하면서도 코드의 가독성을 높임</li>
</ul>
<h2 id="비동기-작업-최적화">비동기 작업 최적화</h2>
<h3 id="순차-실행">순차 실행</h3>
<ul>
<li>순차 실행은 작업을 하나씩 순서대로 처리함</li>
<li>아래 코드는 각 작업이 완료된 후 다음 작업을 실행함</li>
</ul>
<pre><code class="language-javascript">const delay = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

const task1 = async () =&gt; {
  await delay(1000);
  return &#39;task1 시작&#39;;
};

const task2 = async () =&gt; {
  await delay(2000);
  return &#39;task2 시작&#39;;
};

const task3 = async () =&gt; {
  await delay(1000);
  return &#39;task3 시작&#39;;
};

const task4 = async () =&gt; {
  await delay(1000);
  return &#39;task4 시작&#39;;
};

const startTasks = async () =&gt; {
  console.time();
  const msg1 = await task1();
  const msg2 = await task2();
  const msg3 = await task3();
  const msg4 = await task4();

  console.log(msg1, msg2, msg3, msg4);
  console.timeEnd();
};
startTasks();</code></pre>
<ul>
<li>위 코드에서는 각 작업이 완료된 후 다음 작업이 시작됨</li>
<li>따라서 모든 작업이 완료되려면 총 4초가 소요됨
<img src="https://velog.velcdn.com/images/cielo_hello/post/24a08cd9-2db2-4ed4-98b3-da3cf2e7e928/image.png" alt=""></li>
</ul>
<h3 id="병렬-실행">병렬 실행</h3>
<ul>
<li>병렬 실행은 여러 작업을 동시에 처리하여 실행 시간을 단축할 수 있음</li>
<li>다음은 3가지 병렬 실행 방법임</li>
</ul>
<h4 id="1-코드-수정하기">1. 코드 수정하기</h4>
<pre><code class="language-javascript">const delay = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

async function task1() {
  await delay(1000);
  return &#39;task1 시작&#39;;
}

async function task2() {
  await delay(2000);
  return &#39;task2 시작&#39;;
}

async function task3() {
  await delay(1000);
  return &#39;task3 시작&#39;;
}

async function task4() {
  await delay(1000);
  return &#39;task4 시작&#39;;
}

async function startTasks() {
  console.time(&#39;병렬 실행&#39;);

  const task1Promise = task1();
  const task2Promise = task2();
  const task3Promise = task3();
  const task4Promise = task4();

  const msg1 = await task1Promise;
  const msg2 = await task2Promise;
  const msg3 = await task3Promise;
  const msg4 = await task4Promise;

  console.log(msg1, msg2, msg3, msg4);
  console.timeEnd(&#39;병렬 실행&#39;);
}

startTasks();</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/0a84048e-24cd-47a5-82fc-74b19e644bc8/image.png" alt=""></p>
<ul>
<li>위 코드에서는 모든 작업이 동시에 시작되므로, 가장 오래 걸리는 작업인 <code>task2</code>가 완료되는 2초 후에 모든 작업이 완료됨</li>
<li>총 실행 시간이 2초로 단축됨</li>
</ul>
<h4 id="2-promiseall">2. Promise.all</h4>
<ul>
<li><code>Promise.all</code>을 사용하여 병렬로 비동기 작업을 처리할 수 있음</li>
</ul>
<pre><code class="language-javascript">const delay = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

async function task1() {
  await delay(1000);
  return &#39;task1 시작&#39;;
}

async function task2() {
  await delay(2000);
  return &#39;task2 시작&#39;;
}

async function task3() {
  await delay(1000);
  return &#39;task3 시작&#39;;
}

async function task4() {
  await delay(1000);
  return &#39;task4 시작&#39;;
}

async function startTasks() {
  console.time(&#39;병렬 실행&#39;);

  const tasks = await Promise.all([task1(), task2(), task3(), task4()]);

  console.log(tasks.join(&#39;, &#39;));
  console.timeEnd(&#39;병렬 실행&#39;);
}

startTasks();</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/0f9b5695-156f-4ec8-a721-644926060830/image.png" alt=""></p>
<ul>
<li>위 코드는 <code>Promise.all</code>을 사용하여 모든 작업을 병렬로 실행하고, 모든 작업이 완료되면 결과를 출력함</li>
<li>만약 전달된 <code>Promise</code> 중 하나라도 <code>rejected</code> 상태가 되면, 나머지 <code>Promise</code>가 완료되지 않았더라도 <code>Promise.all</code>은 즉시 <code>rejected</code> 상태로 변함</li>
<li>즉, 하나의 Promise가 실패하면 모든 Promise가 실패한 것으로 간주되어 전체 작업이 중단됨</li>
</ul>
<h4 id="3-promiseallsettled">3. Promise.allSettled</h4>
<ul>
<li><code>Promise.allSettled</code>는 모든 Promise의 완료 여부와 상관없이 결과를 반환함</li>
<li>이는 개별 <code>Promise</code>의 성공과 실패를 구분하고, 각각의 결과를 처리할 수 있게 함</li>
</ul>
<pre><code class="language-javascript">const delay = (ms) =&gt; new Promise((resolve) =&gt; setTimeout(resolve, ms));

async function task1() {
  await delay(1000);
  return &#39;task1 시작&#39;;
}

async function task2() {
  await delay(2000);
  throw new Error(&#39;에러&#39;);
}

async function task3() {
  await delay(1000);
  return &#39;task3 시작&#39;;
}

async function task4() {
  await delay(1000);
  return &#39;task4 시작&#39;;
}

async function startTasks() {
  console.time(&#39;병렬 실행&#39;);

  const tasks = await Promise.allSettled([task1(), task2(), task3(), task4()]);

  console.log(tasks);
  console.timeEnd(&#39;병렬 실행&#39;);
}

startTasks();
</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/1c9899cd-46b0-4150-a303-3430e8b8d375/image.png" alt=""></p>
<ul>
<li>위 코드는 <code>Promise.allSettled</code>를 사용하여 모든 <code>Promise</code>가 완료될 때까지 기다리며, 각 <code>Promise</code>의 결과를 배열로 반환함</li>
<li>실패한 <code>Promise</code>도 포함되므로, 모든 결과를 확인할 수 있음</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 3일차(컨텍스트, 생성자 함수, 프로토타입)]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-%EC%82%AC%EC%A0%84%EC%A7%81%EB%AC%B4%EA%B5%90%EC%9C%A1-3%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-%EC%82%AC%EC%A0%84%EC%A7%81%EB%AC%B4%EA%B5%90%EC%9C%A1-3%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Wed, 17 Jul 2024 14:45:13 GMT</pubDate>
            <description><![CDATA[<p>오늘은 그동안 관심 있었던 주제들이 많아 수업이 더 흥미로웠다. 특히 기억하고 싶은 내용들을 정리하고자 한다. 수업을 들으며 급하게 필기했기 때문에 아래 내용에 틀린 부분이 있을 수도 있다. 그래도 오늘 배운 내용을 잊지 않기 위해 일단 기록한다. 나중에 모던 자바스크립트 딥다이브 책과 유튜브 영상을 통해 틀린 내용을 수정하고, 내용을 더 확고히 할 것이다.</p>
<h1 id="08-컨텍스트">08. 컨텍스트</h1>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/d4032841-76bc-4a93-9f8a-bd998428b9fb/image.png" alt="출처: [https://haileychoi15.medium.com/실행-컨텍스트-execution-context-와-호이스팅-hoisting-3f407ad4820e](https://haileychoi15.medium.com/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-execution-context-%EC%99%80-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-hoisting-3f407ad4820e)"></p>
<p>출처: <a href="https://haileychoi15.medium.com/%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-execution-context-%EC%99%80-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85-hoisting-3f407ad4820e">https://haileychoi15.medium.com/실행-컨텍스트-execution-context-와-호이스팅-hoisting-3f407ad4820e</a></p>
<h2 id="실행-컨텍스트">실행 컨텍스트</h2>
<ul>
<li>자바스크립트 코드가 실행될 때 필요한 환경을 제공해주는 객체</li>
<li>전역 실행 컨텍스트는 무조건 만들어짐</li>
<li>함수가 호출될 때마다 새로운 실행 컨텍스트가 만들어짐</li>
<li>내부적으로 레코드(Record)와 아우터(Outer)로 구성되어 있음<ul>
<li><strong>Record:</strong> 두 개의 단계를 거쳐 코드가 실행됨<ol>
<li>생성 단계: 코드 실행에 영향을 줌<ul>
<li>코드 전체를 훑으며 변수 및 선언문을 기록함</li>
<li>이 때, <code>var</code>와 <code>let</code>, <code>const</code>의 경우 다름<ul>
<li><code>var</code>: 변수는 호이스팅되며 <code>undefined</code>로 초기화됨</li>
<li><code>let</code>과 <code>const</code>: 변수는 호이스팅되지만 초기화되지 않으며, 실제 선언 위치까지 &quot;일시적 사각지대&quot;(TDZ)에 놓임</li>
</ul>
</li>
<li>이 때, 함수 선언식으로 만든 함수와 함수 표현식, 화살표 함수의 경우 다름<ul>
<li>함수 선언식: 함수 선언 전체가 호이스팅됨</li>
<li>함수 표현식과 화살표 함수: 변수는 호이스팅되지만 초기화되지 않음 (변수 호이스팅 규칙을 따름)</li>
</ul>
</li>
</ul>
</li>
<li>실행 단계: 코드가 실행되며 변수에 값이 할당되고 함수가 실행됨</li>
</ol>
</li>
<li><strong>Outer</strong>:<ul>
<li>스코프 체인을 통해 상위 스코프를 참조함</li>
<li>전역 실행 컨텍스트는 상위 스코프가 없기 때문에 전역 객체(window)를 참조함</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="코드-예시로-흐름-따라가기">코드 예시로 흐름 따라가기</h3>
<pre><code class="language-jsx">const num = 10;
function printNum() {
  const num = 30;
  console.log(num);
}
printNum()</code></pre>
<ul>
<li><p>전역 실행컨텍스트 - Record 객체</p>
<ol>
<li>생성 단계<ol>
<li><code>num</code> 변수 선언 (TDZ)</li>
<li><code>printNum</code> 함수 선언 (호이스팅)</li>
<li>실행 단계로 넘어감</li>
</ol>
</li>
<li>실행 단계<ol>
<li><code>num</code> 변수 초기화 (<code>10</code>)</li>
<li><code>printNum</code> 함수는 선언만 되어있어 별다른 액션이 없음</li>
<li><code>printNum()</code>을 실행하면 새로운 실행 컨텍스트가 생성됨</li>
</ol>
</li>
</ol>
</li>
<li><p><code>printNum</code> 실행컨텍스트 - Record 객체</p>
<ol>
<li><p>생성 단계</p>
<ol>
<li><p><code>num</code> 변수 선언 (TDZ)</p>
<p> → 이때 전역 실행컨텍스트에서 <code>const</code>로 선언한 <code>num</code>을 여기서 또 선언할 수 있는 이유는 두 <code>num</code> 이 서로 다른 컨텍스트에 있기 때문임. 즉, 각 실행 컨텍스트는 독립적인 스코프를 가지며, 다른 컨텍스트에서 선언된 변수와 충돌하지 않음</p>
</li>
</ol>
</li>
<li><p>실행 단계</p>
<ol>
<li><code>num</code> 변수 초기화 (<code>30</code>)</li>
<li><code>console.log(num)</code> 실행: <code>30</code> 출력</li>
</ol>
</li>
<li><p>실행 완료 후 실행 컨텍스트에서 제거됨</p>
</li>
<li><p>전역 실행 컨텍스트로 돌아감</p>
</li>
</ol>
</li>
</ul>
<pre><code class="language-jsx">const num = 10;
function printNum() {
  console.log(num);
}
printNum();</code></pre>
<ul>
<li>전역 실행컨텍스트 - Record 객체<ol>
<li>생성 단계<ol>
<li><code>num</code> 변수 선언 (TDZ)</li>
<li><code>printNum</code> 함수 선언 (호이스팅)</li>
</ol>
</li>
<li>실행 단계<ol>
<li><code>num</code> 변수 초기화 (<code>10</code>)</li>
<li><code>printNum</code> 함수는 선언된 상태로 존재</li>
<li><code>printNum()</code> 호출 시 새로운 실행 컨텍스트가 생성됨</li>
</ol>
</li>
</ol>
</li>
<li><code>printNum</code> 실행컨텍스트 - Record 객체<ol>
<li>생성 단계<ol>
<li>Outer 객체 설정: <code>printNum</code>의 Outer 객체는 전역 실행 컨텍스트를 참조함</li>
</ol>
</li>
<li>실행 단계<ol>
<li><code>console.log(num)</code> 실행: 전역 스코프에서 <code>num</code>을 찾아 <code>10</code> 출력</li>
</ol>
</li>
<li>실행 완료 후 실행 컨텍스트에서 제거됨</li>
<li>전역 실행 컨텍스트로 돌아감</li>
</ol>
</li>
</ul>
<pre><code class="language-jsx">const num = 10;
function floor2() {
  const num = 20;
  function floor3() {
    const num = 30;
    console.log(num);
  }
  floor3();
}
floor2();
console.log(num);
</code></pre>
<ol>
<li><code>num</code> 기록 초기화는 안함, <code>floor2</code> 함수 온전히 기록해 놔야지</li>
<li>실행 단계: <code>num</code>은 알고 보니 <code>10</code>이었고 <code>floor2</code>는 함수인 거 알았으니 업데이트 안 함</li>
<li><code>floor2</code> 함수 호출하고 실행하기 위한 실행 컨텍스트 만듦</li>
<li><code>floor2</code> 실행 컨텍스트 - Record 객체<ol>
<li>생성 단계<ol>
<li><code>num</code> 변수 선언 (TDZ)</li>
<li><code>floor3</code> 함수 선언 (호이스팅)</li>
</ol>
</li>
<li>실행 단계<ol>
<li><code>num</code> 변수 초기화 (<code>20</code>)</li>
<li><code>floor3</code> 함수는 선언만 되어있어 별다른 액션이 없음</li>
<li><code>floor3()</code>을 실행하면 새로운 실행 컨텍스트가 생성됨</li>
</ol>
</li>
</ol>
</li>
<li><code>floor3</code> 실행 컨텍스트 - Record 객체<ol>
<li>생성 단계<ol>
<li><code>num</code> 변수 선언 (TDZ)</li>
</ol>
</li>
<li>실행 단계<ol>
<li><code>num</code> 변수 초기화 (<code>30</code>)</li>
<li><code>console.log(num)</code> 실행: <code>30</code> 출력</li>
</ol>
</li>
<li>실행 완료 후 실행 컨텍스트에서 제거됨</li>
<li><code>floor2</code> 실행 컨텍스트로 돌아감</li>
</ol>
</li>
<li><code>floor2</code> 실행 컨텍스트 완료 후 제거됨</li>
<li>전역 실행 컨텍스트에서 <code>console.log(num)</code> 실행: <code>10</code> 출력</li>
</ol>
<hr>
<h2 id="클로저">클로저</h2>
<p>실행 컨텍스트가 정상적으로 제거(종료)되지 못하는 현상</p>
<ul>
<li><p>실행컨텍스트: 자기가 가지고 있는 코드의 실행이 끝나면 제거 돼야함(메모리에서 삭제 됨)</p>
</li>
<li><p>실행 컨텍스트가 제대로 삭제되지 못하는 경우</p>
<pre><code class="language-jsx">  function outerFunc() {
    let count = 0;
    return function innerFunc() {
      count++;
      console.log(count);
    };
  }

  let counter = outerFunc();
  counter(); // 1
  counter(); // 2
  counter(); // 3

  counter = null;</code></pre>
<ol>
<li>전역 실행 컨텍스트가 만들어짐</li>
<li>outerFunc 호출하는 순간 실행컨텍스트 만들어짐</li>
<li>생성단계에 count, innerFunc 기록</li>
<li>할당 및 함수 실행 → return;</li>
<li>실행컨텍스트 제거하려고 하지만 전역 실행컨텍스트에 넘겨준 함수가 count를 참조하고 있음<ol>
<li>이를 처리하기 위해 실행컨텍스트를 콜스택에서 제거를 하긴하되 기록을 메모리상 어딘가에 보관함. 나를 참조하고 있는 애가 없어질 때까지⇒ 이런 상황을 클로져라고 함. 메모리 누수가 발생함</li>
<li>보관의 용도이므로 새롭게 호출해서 refresh 되지 않음</li>
<li>따라서 카운터를 여러번 호출했을때 숫자가 무한히 증가함</li>
</ol>
</li>
</ol>
</li>
<li><p>메모리 누수를 방지하기 위해 클로져를 의도적으로 만들어 사용했다면 <code>null</code> 값으로 초기화 해줘야함</p>
</li>
</ul>
<hr>
<h1 id="10-생성자-함수">10. 생성자 함수</h1>
<p>객체를 생성하는 함수</p>
<ul>
<li>객체의 속성이 같고, 값이 다른 경우에 객체를 생성할 수 있는 생성자 함수라는 문법을 제공함</li>
<li>같은 속성을 가지고 있는 객체를 여러개 만들어야 할때 사용함</li>
<li>생성자 함수로 만든 객체들의 속성을 한번에 변경할 수 있어서 유지 보수가 쉬움</li>
<li>매개변수를 가질 수 있음</li>
</ul>
<h2 id="만드는-법-및-규칙">만드는 법 및 규칙</h2>
<ul>
<li>함수 선언식으로 선언함 - 관례</li>
<li>생성자 함수는 대문자로 시작해야함 - 관례</li>
<li>생성자 함수에서 변수는 this 키워드로 생성함</li>
<li>생성자 함수를 호출할 때는 <code>new</code> 키워드를 붙임<ul>
<li><code>new</code> 키워드를 붙이면 암묵적으로 <code>this = {}</code> 와 <code>return this</code> 가 생략돼 있다고 생각해도 됨</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">
function User(name, age, gender) {
    // this = {}
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.introduce = function () {
    console.log(&#39;이름: &#39; + this.introducename + &#39;, 나이: &#39; + this.age + &#39;, 성별: &#39; + this.gender);
  };
  // return this;
}

const user1 = new User(&#39;John&#39;, 30, &#39;male&#39;);
user1.introduce();

const user2 = new User(&#39;Ann&#39;, 12, &#39;female&#39;);
user2.introduce();</code></pre>
<hr>
<h1 id="11-프로토타입">11. 프로토타입</h1>
<p>객체의 상속과 재사용을 가능하게 하는 원리</p>
<h2 id="무엇을-위한-속성-인가">무엇을 위한 속성 인가?</h2>
<p>생성자 함수의 prototype 객체를 가리키기(참조) 위한 속성</p>
<h2 id="프로토타입과-함수">프로토타입과 함수</h2>
<ul>
<li><p>생성자 함수의 프로토타입 객체: 모든 함수는 그 함수와 연결돼 있는 프로토타입 속성(프로토타입 객체를 참조할 수 있음)이 있음</p>
<ul>
<li><p>모든 함수는 자신과 1:1로 매칭되는 프로토타입이라는 공간을 가짐</p>
</li>
<li><p>서로 참조할 수 있는 속성이 존재함 → <code>prototype</code>, <code>constructor</code></p>
<p>  <img src="https://velog.velcdn.com/images/cielo_hello/post/472fc0d1-af95-4454-851d-ced73c3dae7d/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<h2 id="효율적으로-인스턴스-만들기">효율적으로 인스턴스 만들기</h2>
<ul>
<li><p>공통적으로 사용할 수 있는 메서드나 속성값 같은 경우에는 prototype이라는 객체에다 넣어주자</p>
<ul>
<li><p>그래서 생성자 함수로 만들어지는 모든 객체들이 참조할 수 있게 하자</p>
</li>
<li><p>그러면 만개의 객체를 찍어내더라도 불필요한 메모리 낭비를 줄일 수 있지 않을까?</p>
<pre><code class="language-jsx">  function User(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }

  User.prototype.introduce = function () {
    console.log(&#39;이름: &#39; + this.introducename + &#39;, 나이: &#39; + this.age + &#39;, 성별: &#39; + this.gender);
  };

  const user1 = new User(&#39;John&#39;, 30, &#39;male&#39;);
  user1.introduce();

  const user2 = new User(&#39;Ann&#39;, 12, &#39;female&#39;);
  user2.introduce();</code></pre>
<p>  <img src="https://velog.velcdn.com/images/cielo_hello/post/19cd5793-2131-4816-a0cc-cbe95e7e014c/image.png" alt=""></p>
</li>
</ul>
</li>
</ul>
<pre><code>- User의 prototype과 user1이 참조하고 있는 prototype은 같은 것인가?

    ```jsx
    console.dir(user1.__proto__ === User.prototype); // true
    ```</code></pre><h2 id="프로토타입-체인">프로토타입 체인</h2>
<blockquote>
<p>위 예시에서 프로토타입 객체 내부에 있는 <code>introduce</code>를<code>user2.introduce();</code> 로 호출할 수 있는 이유는 뭘까?</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/6dbd0504-2177-49dc-be0e-d20bb06bfb03/image.png" alt=""></p>
<ul>
<li><p>프로토타입 체인: 인스턴스 내부의 <code>__proto__</code> 속성으로 자신을 생성한 생성자 함수의 프로토타입 객체를 참조하는 현상</p>
</li>
<li><p>프로토타입 체이닝: 프로토타입 체인을 따라서 상위 프로토타입을 타고타고 올라가는 현상</p>
</li>
<li><p>자바스크립트에서 최상위에 <code>Object</code>라는 생성자 함수가 있음</p>
<p>  <img src="https://velog.velcdn.com/images/cielo_hello/post/a8250933-f2d1-499e-976a-af79828caf1f/image.png" alt=""></p>
</li>
</ul>
<pre><code>- 자바스크립트의 모든 요소는 이 `Object` 로부터 파생됨</code></pre><p><img src="https://velog.velcdn.com/images/cielo_hello/post/073153c7-9224-494f-aca4-ee2cf2a914a0/image.png" alt=""></p>
<h2 id="직접-비교해보기">직접 비교해보기</h2>
<h3 id="프로토타입에-공통-메소드-입력">프로토타입에 공통 메소드 입력</h3>
<pre><code class="language-jsx">function User(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
}

User.prototype.introduce = function () {
  console.log(&#39;이름: &#39; + this.introducename + &#39;, 나이: &#39; + this.age + &#39;, 성별: &#39; + this.gender);
};

console.time();
const userList = [];
for (let i = 0; i &lt; 9000000; i++) {
  userList.push(new User(&#39;John&#39;, 30, &#39;male&#39;));
}

console.timeEnd();
</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/3f551d86-4ed0-4091-a287-425c0733f595/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/ac6a4f58-3716-4008-8a78-a6ae93d672b3/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/db941c72-2056-4295-aff0-54b091a6a30f/image.png" alt=""></p>
<h3 id="객체-메소드로-공통-메소드-입력">객체 메소드로 공통 메소드 입력</h3>
<pre><code class="language-jsx">function User(name, age, gender) {
  this.name = name;
  this.age = age;
  this.gender = gender;
  this.introduce = function () {
    console.log(&#39;이름: &#39; + this.introducename + &#39;, 나이: &#39; + this.age + &#39;, 성별: &#39; + this.gender);
  };
}

console.time();
const userList = [];
for (let i = 0; i &lt; 9000000; i++) {
  userList.push(new User(&#39;John&#39;, 30, &#39;male&#39;));
}

console.timeEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/c3c09cc5-9917-4299-9d54-5255bd4c1a44/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/e25b5ba6-0060-412a-96a9-a605c37266c9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/6bf4a782-97c7-422d-93ee-e9fae47b4f70/image.png" alt=""></p>
<p>따라서 성능 유지 차원에서 공통 메서드 및 속성들은 프로토 타입에 만들어 놓는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 2일차 후기]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-%EC%82%AC%EC%A0%84%EC%A7%81%EB%AC%B4%EA%B5%90%EC%9C%A1-2%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-%EC%82%AC%EC%A0%84%EC%A7%81%EB%AC%B4%EA%B5%90%EC%9C%A1-2%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Tue, 16 Jul 2024 08:49:49 GMT</pubDate>
            <description><![CDATA[<p>어제에 이어 자바스크립트 기초 내용부터 배우기 시작했다. 
복습을 하며 부족한 부분을 중점적으로 필기했다.</p>
<h1 id="01-변수">01. 변수</h1>
<p>변수 선언 방법 3가지 및 차이점, 자바스크립트 프로그래밍 용어, 연습 문제 풀기를 했다. 이미 아는 내용이었지만 이론을 깔끔하게 정리할 수 있어서 좋았다. 특히 <code>var</code>, <code>let</code>, <code>const</code>의 차이점을 명확히 하고 호이스팅 및 TDZ에 대해 복습하고 정리했다. 연습 문제는 수월하게 풀었으나 <code>let</code>과 <code>const</code>를 사용할 때 생각없이 사용한다는 사실을 알게 됐다. 변할 일이 없을 것 같은 변수는 꼭 const로 선언하자! </p>
<h2 id="var를-사용하지-않는-이유">var를 사용하지 않는 이유</h2>
<p>내가 선언한 변수가 다른 스크립트 파일의 동명의 변수에 의해 덮어씌워져서 모른다면(인지할 수 없음) 큰 오류를 발생할 수 있음</p>
<h2 id="변수-선언별-차이">변수 선언별 차이</h2>
<ul>
<li>var - 변수명 중복, 재할당 가능</li>
<li>let - 변수명 중복 불가, 재할당 가능</li>
<li>const - 변수명 중복 불가, 재할당 불가능</li>
</ul>
<h2 id="자바스크립트에서-사용되는-용어">자바스크립트에서 사용되는 용어</h2>
<pre><code class="language-js">const num = 10;</code></pre>
<ol>
<li>키워드: <code>const</code></li>
<li>식별자(키워드의 역할에 맞춰서 변할 수 있음): <code>num</code></li>
<li>연산자: <code>=</code>, <code>+</code></li>
<li>값: <code>10</code></li>
<li>표현식: <code>10+20</code></li>
<li>선언</li>
<li>할당</li>
</ol>
<h2 id="호이스팅">호이스팅</h2>
<ul>
<li><a href="https://www.notion.so/JavaScript-c0c81509d1ef4b6f948d64c7c88342c3?pvs=4">호이스팅에 대해 배우고 공부한 내용을 정리한 글</a></li>
</ul>
<h2 id="세미콜론">세미콜론</h2>
<ul>
<li>세미콜론을 안 써도 자바스크립트 엔진이 자동으로 세미콜론을 붙여줌</li>
<li>하지만 의도하지 않은 일이 일어날 수 있으므로 세미콜론을 반드시 써서 하나의 문장이 끝났음을 알려주자(Prettier 활용)</li>
</ul>
<hr>
<h1 id="02-자료형data-type">02. 자료형(Data Type)</h1>
<ul>
<li>자료형: 자바스크립트에서 다룰 수 있는 값의 종류</li>
<li>크게 기본자료형(primitive datatype)와 참조 자료형(reference datatype)으로 구분<ul>
<li>기본자료형: 숫자형, 문자열형, 논리형, 특수자료형(undefined/null), 심볼(sybol)의 5가지 자료형으로 구분</li>
<li>참조 자료형: 객체, 배열, 함수의 3가지 자료형으로 구분하여 총 6가지의 자료형으로 구분</li>
</ul>
</li>
</ul>
<h2 id="기본-자료형원시-타입">기본 자료형(원시 타입)</h2>
<h3 id="숫자형">숫자형</h3>
<ul>
<li>우리가 생각하는 수의 값(양수, 음수, 정수, 소수, 지수)</li>
<li>지수를 입력해도 십진수로 반환한다.</li>
</ul>
<h3 id="특수-자료형">특수 자료형</h3>
<ul>
<li><code>undefined</code>: 자바스크립트 엔진이 다루는 값<ul>
<li>개발자가 다루는 값이 아님</li>
<li>ex) 변수를 선언하고 값을 할당하지 않았을 때 <code>undefined</code>를 넣어줌</li>
</ul>
</li>
<li><code>null</code>: 의도가 있는 값<ul>
<li>개발자가 변수 공간을 만든 것을 인지하고 있음</li>
</ul>
</li>
<li><code>undefined</code>와 <code>null</code> 둘 다 데이터가 없는 것</li>
</ul>
<h4 id="강사님께-질문-한-것">강사님께 질문 한 것</h4>
<p>❓<code>var</code>로 변수를 선언했을 때 호이스팅에 의해 변수가 <code>undefined</code>가 되는 상황에서 <code>undefined</code>가 &quot;할당&quot;된다고 표현해도 되나? 
❗실행 컨텍스트와 관련이 있는데, 지금 단계에서는 할당한다고 표현해도 됨!</p>
<h3 id="심볼">심볼</h3>
<p>절대로 중복되지 않는 유니크한 값을 생성함</p>
<pre><code class="language-jsx">const t = Symbol(&quot;회원 테이블의 고유한 id 값으로 사용하기 위해&quot;)
const a = Symbol();</code></pre>
<ul>
<li>절대로 중복되지 않는 값이 필요할 때 사용함</li>
</ul>
<h2 id="참조-자료형">참조 자료형</h2>
<h3 id="배열">배열</h3>
<p>여러 개의 값을 묶어둔 값</p>
<ul>
<li>자바스크립트에서 다룰 수 있는 모든 자료형의 값을 넣을 수 있음</li>
<li>순수하게 값을 묶어둠(값이 직관적이지 않음)</li>
</ul>
<h3 id="객체">객체</h3>
<p>여러 개의 값을 묶어둔 값</p>
<ul>
<li>값을 (키, 값)으로 묶어둠</li>
<li>값을 꺼내 쓰는 법<ol>
<li>마침표 연산자: <code>scoreObj.englishScore</code></li>
<li>대괄호 연산자: <code>scoreObj[”koreanScore”]</code></li>
</ol>
</li>
</ul>
<h4 id="강사님께-질문-한-것-1">강사님께 질문 한 것</h4>
<p>❓기본 자료형과 참조 자료형을 나누는 기준</p>
<p>❗값을 할당하는 방식이 다르다</p>
<ul>
<li>기본 자료형은 값을 다이렉트로 변수 공간에 넣음</li>
<li>참조자료형의 값들은 변수 공간에 값을 다이렉트로 집어넣지 않고 값이 저장된 메모리의 주소를 집어넣음</li>
<li>즉, 참조자료형을 선언한 변수 안에는 값이 아닌 주소값이 들어가 있음</li>
</ul>
<pre><code class="language-jsx">const arr1 = [10, 20];
const arr2 = [10, 20];

console.log(arr1 === arr2) // false</code></pre>
<h2 id="typeof">typeof</h2>
<ul>
<li>해당 변수에 들어있는 값의 자료형을 문자열로 반환해줌</li>
</ul>
<pre><code class="language-jsx">let num = [];
console.log(typeof num); // object 자바스크립트의 버그</code></pre>
<hr>
<h1 id="03-연산자">03. 연산자</h1>
<ul>
<li>자바스크립트에서는 연산자를 크게 산술, 증감, 대입, 비교, 논리, 삼항, 비트로 구분하고 있음</li>
</ul>
<h2 id="증감-연산자">증감 연산자</h2>
<ul>
<li><p>전치 연산 또는 전위 연산자</p>
<ul>
<li><p><code>++num</code></p>
</li>
<li><p>~~하기 전에</p>
</li>
<li><p>할당하기 전에 증가시키고 할당해라</p>
<pre><code class="language-jsx">let num = 10;
const incrementNum = ++num;
console.log(incrementNum) // 11</code></pre>
</li>
</ul>
</li>
<li><p>후치 연산 또는 후치 연산잔</p>
</li>
<li><p><code>num++</code></p>
</li>
<li><p>~~한 후에</p>
<pre><code class="language-jsx">  let num = 10;
  const incrementNum = num++;
  console.log(incrementNum) // 10</code></pre>
</li>
</ul>
<h3 id="예제">예제</h3>
<ol>
<li>변수 <code>y</code>에 숫자 7을 할당하세요.<ol start="2">
<li>후위 증가 연산자를 사용하여 <code>y</code>를 1 증가시키고, 증가하기 전과 후의 값을 각각 출력하세요.</li>
<li>후위 감소 연산자를 사용하여 <code>y</code>를 1 감소시키고, 감소하기 전과 후의 값을 각각 출력하세요.</li>
<li>전위 증가 연산자를 사용하여 <code>y</code>를 1 증가시키고, 증가한 값을 출력하세요.</li>
<li>전위 감소 연산자를 사용하여 <code>y</code>를 1 감소시키고, 감소한 값을 출력하세요.<pre><code class="language-js">let y = 7; 
</code></pre>
</li>
</ol>
</li>
</ol>
<p>console.log(y); // 증가하기 전 : 7
console.log(y++); //증가한 후 : 7</p>
<p>console.log(y); // 감소하기전 : 8
console.log(y--); // 감소한후 : 8</p>
<p>// y = 7
console.log(++y); // 증가한값 8</p>
<p>// y = 8
console.log(--y); // 감소한값: 7</p>
<pre><code>

## 비교 연산자

- 암묵적 형변환: 자바스크립트 엔진이 코드 실행 중 자동으로 데이터 타입을 변환하는 과정을 말함
- 동등 연산자 `==`  및 부등 연산자 `!=`는 암묵적 형변환을 하기 때문에 실무에서는 사용을 피해야함 (일치 연산자 사용하기)
---
# 04. 조건문

### if

- 삼항연산자로 표현하는 것이 더 났다.
- `if` 문 내부 코드가 한줄이면 생략가능하다

```jsx
if(isLoggedIn) message= &quot;로그인 되었습니다&quot;;
else message=&quot;로그인이 필요합니다.&quot;;</code></pre><ul>
<li><code>if</code> vs. <code>else if</code> : 상황에 따라 다르지만 퍼포먼스적으로 <code>else if</code> 가 더 좋을 때가 있다. 이런 것들을 생각하면서 코드를 짜자.</li>
</ul>
<h3 id="switch">switch</h3>
<ul>
<li><code>break</code>의 사용위치가 중요함</li>
<li>다음과 같이 쓸 수도 있음</li>
</ul>
<pre><code class="language-jsx">const area = &#39;제주도&#39;;
let baesongbi = 0;

switch (area) {
  case &#39;서울&#39;:
  case &#39;인천&#39;:
  case &#39;남양주&#39;:
    baesongbi = 3000;
    break;
  case &#39;강원도&#39;:
    baesongbi = 3500;
    break;
  default:
    baesongbi = 10000;
}

console.log(baesongbi);</code></pre>
<hr>
<h1 id="05-반복문">05. 반복문</h1>
<ul>
<li>무한 루프 조심하기<ul>
<li>코드 실행 블록 안에서 반복문을 종료시키는 조건을 충족시켜 줄것</li>
</ul>
</li>
</ul>
<h3 id="dowhile">do…while</h3>
<p>어떤 내용을 한 번은 실행시키고 반복하려고 할 때 유용하게 사용할 수 있는 문법</p>
<pre><code class="language-jsx">do{
  //무조건 한 번은 실행
  //한 번 이후에는 expr이 참이면 실행
}while(expr);</code></pre>
<h3 id="forin">for..in</h3>
<p>배열이나 객체를 반복할 때 사용</p>
<pre><code class="language-jsx">let arr = [&quot;banana&quot;, &quot;apple&quot;, &quot;orange&quot;];
let obj = {name:&quot;철수&quot;, age:20};

for(let index in arr){
  console.log(arr[index]);
}

for(let key in obj){
  console.log(obj[key]);
}</code></pre>
<h3 id="forof">for..of</h3>
<p>배열에만 쓸 수 있음. 대신 배열의 인덱스가 아닌 값에 직접적으로 접근함</p>
<pre><code class="language-jsx">for (let value of arr) {
    console.log(value)
}</code></pre>
<hr>
<h1 id="06-함수">06. 함수</h1>
<p>함수는 하나의 특별한 목적을 가지고 코드를 실행하도록 만드는 문법</p>
<h2 id="함수-선언식">함수 선언식</h2>
<pre><code class="language-jsx">function gugudan () {}
gugudan(); // 호출</code></pre>
<h2 id="함수-표현식">함수 표현식</h2>
<h3 id="익명-함수">익명 함수</h3>
<pre><code class="language-jsx">const gugudan = function () {}
gugudan(); // 호출</code></pre>
<h3 id="기명-함수">기명 함수</h3>
<ul>
<li>함수 자체에 이름이 있기 때문에 디버깅이 쉬운 장점이 있음</li>
</ul>
<pre><code class="language-jsx">const gugudan = function name() {}
gugudan(); // 호출</code></pre>
<p>❓함수 선언식, 익명 함수, 기명 함수 중 어떤걸 사용해야 하나요?</p>
<p>❗프레임워크에서 권장하는 방법을 상황에 맞춰서 사용하기. 단, 기명 함수를 사용하는 게 정석이긴 하지만 실무에서는 익명 함수를 더 많이 사용하기는 한다.</p>
<h2 id="매개변수">매개변수</h2>
<ul>
<li><p>함수의 재사용성을 높여줌</p>
</li>
<li><p>매개변수를 받지 않는다고 해서 에러로 인식하지 않는다.</p>
</li>
<li><p>매개변수를 가변으로 받기</p>
<ul>
<li><p>문제 상황</p>
<pre><code class="language-jsx">  function add(a, b, c) {
  console.log(a+b+c)
  }

  add(10, 20); // 두 수만 더하고 싶을 때는 NaN이 나옴
  add(10, 20, 30);</code></pre>
</li>
<li><p>해결책(가변 인자를 처리하는 기법)</p>
<ol>
<li><p>나머지 매개변수 이용</p>
<pre><code class="language-jsx"> function add(...args) {
     let sum = 0;
     for (let i = 0; i &lt; args.length; i++) {
     sum += args[i];
     console.log(sum);
 }
 add(10, 20);
 add(10, 20, 30);</code></pre>
<ul>
<li><p>나머지 매개변수와 일반 매개변수를 혼용해서 사용할때는 나머지 매개변수가 항상 마지막에 와야함</p>
<pre><code class="language-jsx">  function add(a, b, ...args) {
      console.log(a, b, args);
  }
  add(10, 20, 30, 40, 50);</code></pre>
</li>
</ul>
</li>
<li><p><code>arguments</code> </p>
<ul>
<li><p>함수도 객체다</p>
</li>
<li><p><code>arguments</code> : 유사배열 객체</p>
</li>
<li><p>Es6에서 나머지 매개변수가 나오고 난 이후에는 잘 사용되지 않는 방법</p>
<pre><code class="language-jsx">  function add() {

  }
  console.dir(add)</code></pre>
<p>  <img src="https://velog.velcdn.com/images/cielo_hello/post/c73a160e-9f93-4513-a3ae-1e0d51772ebd/image.png" alt=""></p>
</li>
</ul>
</li>
</ol>
</li>
</ul>
</li>
</ul>
<h2 id="return">return</h2>
<ul>
<li>함수 내부에서만 사용할 수 있음</li>
<li>리턴 뒤의 코드는 작동하지 않음</li>
<li>리턴을 이용해서 함수의 흐름을 끊기도 함</li>
<li>하지만 궁극적인 본질은 값을 넘겨주는 것임<ul>
<li><code>return;</code> 의 적확한 의미는 함수 종료가 아닌 <code>undefined</code> 반환임</li>
</ul>
</li>
<li>반환할 수 있는 데이터의 종류는 한개임</li>
</ul>
<pre><code class="language-jsx">//외부 함수
function membershipCard() {
    //내부 함수
    return function () {
        return {
            name: &quot;수코딩 온라인 사이트&quot;,
            grade: &quot;basic&quot;
        };
    };
};

console.log(membershipCard()());</code></pre>
<h2 id="화살표-함수">화살표 함수</h2>
<ul>
<li><p>객체를 즉시 <code>return</code> 하는 법: 소괄호로 묶어주기</p>
<pre><code class="language-jsx">  const membershipCard = () =&gt; ({
      name: &quot;수코딩 온라인 사이트&quot;,
      grade: &quot;basic&quot;
  })</code></pre>
</li>
<li><p>매개 변수가 한개면 매개 변수의 소괄호를 생략할 수 있음</p>
<pre><code class="language-jsx">  const membershipCard = sitename =&gt; ({
      name: `${sitename} 온라인 사이트`,
      grade: &quot;basic&quot;
  })

  const membership = membershipCarp(&quot;스나이퍼 팩토리&quot;);</code></pre>
</li>
<li><p>예제: 아래 함수를 <code>return</code> 키워드를 사용하지 않고 화살표 함수로 바꾸기</p>
<pre><code class="language-jsx">  function membershipCard() {
    return function () {
      return function () {
        return {
          name: &quot;수코딩 온라인 사이트&quot;,
          grade: &quot;basic&quot;,
        };
      };
    };
  }</code></pre>
<ul>
<li><p>답</p>
<pre><code class="language-jsx">const membershipCard = () =&gt; () =&gt; () =&gt; ({
name: &#39;수코딩 온라인 사이트&#39;,
grade: &#39;basic&#39;,
});

console.log(membershipCard()()());</code></pre>
</li>
</ul>
</li>
</ul>
<hr>
<h1 id="회고">회고</h1>
<ul>
<li>강사님께 자바스크립트에서 자료형을 기본 자료형과 참조 자료형으로 나누는 기준을 질문 했는데 두루뭉술하게 알고 있던 내용을 확실하게 알 수 있었다.</li>
<li>증감 연산자에서 전치 연산과 후치 연산이 정확히 어떤 방식으로 작동하는지 모르고 있었는데 이제는 코드에서 어떻게 작동하는지 해석할 수 있게 됐다.</li>
<li><code>else if</code> 대신 <code>if</code>를 사용할 수 있다는 사실은 알고 있었지만 퍼포먼스가 차이가 있을 거라는 생각은 하지 못했는데 이번 기회를 통해 코드를 짤 때 성능에 대해서도 생각해보자고 다짐했다.</li>
<li><code>for..in</code>문과 <code>for..of</code>문의 차이를 명확히 몰랐는데 이번에 명확히 함</li>
<li>함수 표현식을 쓸 때 익명 함수를 더 많이 사용했는데 기명 함수를 사용하는 게 정석이라는 사실을 알게됨. 근데 기명 함수가 디버깅이 더 쉽다는데 어떤식으로 더 쉽다는 지는 잘 모르겠다.</li>
<li>매개 변수가 존재하는 이유가 함수의 재사용성을 높이기 위해서라는 사실을 알게됨</li>
<li>많은 사람들 앞에서 손들고 질문을 해서 뿌듯했다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 - 사전직무교육 1일차 후기]]></title>
            <link>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-%EC%82%AC%EC%A0%84%EC%A7%81%EB%AC%B4%EA%B5%90%EC%9C%A1-1%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@cielo_hello/%EC%9C%A0%EB%8D%B0%EB%AF%B8x%EC%8A%A4%EB%82%98%EC%9D%B4%ED%8D%BC%ED%8C%A9%ED%86%A0%EB%A6%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%BA%A0%ED%94%84-Next.js-2%EA%B8%B0-%EC%82%AC%EC%A0%84%EC%A7%81%EB%AC%B4%EA%B5%90%EC%9C%A1-1%EC%9D%BC%EC%B0%A8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Mon, 15 Jul 2024 14:15:13 GMT</pubDate>
            <description><![CDATA[<h1 id="참여-계기">참여 계기</h1>
<p>원래 &quot;프론트엔드 프로젝트 캠프 : Next.js 1기&quot;에 참여하려 했지만 2024 관광데이터 활용 공모전 일정과 겹쳐서 신청하지 못했다. 1기에 참여한 분들의 후기도 좋아서 아쉬웠던 차에 2기도 모집한다는 사실을 알게됐다. 최종적으로 다음 3가지 내용을 배우고 싶어서 2기에 지원했다.</p>
<ol>
<li><p>App Router: 이전부터 서버 컴포넌트와 클라이언트 컴포넌트의 구분이 가능하고, 레이아웃 관리가 쉬운 App Router를 사용해 보고 싶었음</p>
</li>
<li><p>자바스크립트 기초: 클로저, Promise 객체 등 어려운 개념 다시 공부하기 / 자바스크립트의 전반적인 내용을 복습하며 기초 실력을 더욱 공고히 하기</p>
</li>
<li><p>어플리케이션 최적화: React, Next.js에서의 렌더링부터 최적화까지 원리 이해하면서 공부하기</p>
</li>
</ol>
<hr>
<h1 id="사전-직무-교육-일정">사전 직무 교육 일정</h1>
<p>커리큘럼이 아주 마음에 든다!
자바스크립트, 타입스크립트, 리액트, Next.js를 다시 한번 쭉 훑으면서 개념을 확실하게 정리하고 기록해야 겠다. 이번에는 좀더 깊이있게 원리를 파보고 싶다.
<img src="https://velog.velcdn.com/images/cielo_hello/post/c308ef32-333f-47b0-bfe8-2ad299d7101a/image.png" alt=""></p>
<hr>
<h1 id="day-1-수업-내용">Day 1 수업 내용</h1>
<h2 id="오리엔테이션">오리엔테이션</h2>
<p>오리엔테이션에서는 필수 제출 서류 및 과제 등 간단한 안내 및 프로젝트 별 기업소개를 들었다. 이미 공모전을 하고 있어서 프로젝트에 참여는 못 할거라고 생각했는데 막상 소개를 들으니 프로젝트에 대한 욕심이 났다. 특히 스팩스페이스에서 진행하는 &quot;제공된 코드가 침해사고를 유발하지 않는지 검사할 수 있는 웹 기반 플랫폼&quot;이 하고 싶었다. 그리고 유데미에서 진행하는 프로젝트에 대해 설명할 때 프로젝트의 선택기준과 경험 요소에 대한 이야기가 좋았다. 앞으로 프로젝트를 할 때 이런 요소들을 고려해서 선택하면 좋을 것 같다.</p>
<ul>
<li>프로젝트 선택 기준<ul>
<li>프로젝트의 비즈니스 가치 및 영향</li>
<li>프로젝트 실현 가능성 및 지원 요구 사항</li>
<li>학습 및 성장 기회</li>
</ul>
</li>
</ul>
<ul>
<li>경험 요소<ul>
<li>효과적인 프로젝트 관리 및 팀워크</li>
<li>코딩 실력 및 문제해결 능력 향상</li>
<li>문서화 및 기술 문서 작성 능력</li>
</ul>
</li>
</ul>
<h2 id="웹-개발-트렌드">웹 개발 트렌드</h2>
<h3 id="nodejs-vs-code-설치">Node.js VS Code 설치</h3>
<p>본격적으로 수코딩님의 강의가 시작 됐다. Node.js, VS Code를 설치했다. Node.js 버전이 중요한 이유에 대해 알려주셨다.</p>
<ul>
<li>Node.js 버전이 중요한 이유<ul>
<li>프로젝트 진행 시 사용하는 라이브러리들이 Node.js 버전에 종속 돼 있기 때문</li>
<li>새로운 프로젝트를 시작할 때 최신 버전을 사용해야함<ul>
<li>Node.js가 업데이트 될 때 마다 라이브러리도 업데이트 됨</li>
<li>최신 Node.js 버전을 사용하지 않는 것은 새로운 프로젝트의 수명을 줄이는 것</li>
</ul>
</li>
<li>Node.js 버전 체크를 잘 하자!</li>
</ul>
</li>
</ul>
<p>VS Code를 설치하고 extension을 install 하는 건 어렵지 않았는데 내 컴퓨터에서만 저장할때 Prettier가 자동으로 적용되지 않았다. </p>
<pre><code>[&quot;INFO&quot; - 3:53:39 PM] Require config set to true and no config present. Skipping file.</code></pre><p>위와 같은 메세지가 떠서 조금 더 알아 보니 <code>settings.json</code>파일의 <code>&quot;prettier.requireConfig&quot;: true</code>가 문제 였다.  <code>prettier.requireConfig</code> 설정이 <code>true</code>로 되어 있어  <code>.prettierrc</code>와 같은 Prettier 설정 파일이 없으면 포맷팅이 작동하지 않아서 발생한 문제였다. <code>false</code>로 바꾸니 저장할 때마다 정상적으로 포맷팅이 됐다.</p>
<pre><code class="language-json">&quot;prettier.requireConfig&quot;: false</code></pre>
<h2 id="javascript의-역사">JavaScript의 역사</h2>
<p>JavaScript의 역사에 대해 설명해 주셨는데, 흥미로웠다. 들으면서 키워드만 간략하게 정리해봤는데 팀 버너스리, 모자이크 브라우저, 넷스케이프 내비게이터, 마이크로소프트, EcmaScript, 파이어폭스, 익스플로러, jQuery, 트랜스컴파일러(babel)등에 대해 &quot;자바스크립트 딥 다이브&quot; 책을 통해 더 자세히 알아봐야겠다.</p>
<h2 id="javascript-파일-실행-방법">JavaScript 파일 실행 방법</h2>
<h3 id="1-nodejs-명령어-사용">1. Node.js 명령어 사용</h3>
<p>Node.js를 이용하여 JavaScript 파일을 실행할 수 있음</p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/b5a779cd-27c2-48fb-9064-e5a53b908022/image.png" alt=""></p>
<h3 id="2-코드-러너code-runner-extension-사용">2. 코드 러너(Code Runner) Extension 사용</h3>
<p>VS Code의 Code Runner 확장 프로그램을 이용하여 JavaScript 파일을 실행할 수 있음</p>
<h3 id="3-html-파일에서-script-태그를-사용하는-방법">3. HTML 파일에서 script 태그를 사용하는 방법</h3>
<h4 id="1-내부-스크립트">(1) 내부 스크립트</h4>
<p>HTML 파일에 직접 JavaScript 코드를 작성하여 실행함</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;script&gt;
      var str = &#39;Hello World!! HTML&#39;;
      console.log(str);
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h4 id="2-외부-스크립트">(2) 외부 스크립트</h4>
<p>JavaScript 코드를 별도의 파일로 분리하여 HTML 파일에서 호출함</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;script src=&quot;main.js&quot;&gt;&lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="4-웹-브라우저-개발자-도구의-콘솔-창-사용">4. 웹 브라우저 개발자 도구의 콘솔 창 사용</h3>
<p>브라우저의 개발자 도구에서 콘솔 창을 이용하여 JavaScript 코드를 실행할 수 있음
<img src="https://velog.velcdn.com/images/cielo_hello/post/60b8b271-8983-451e-83f3-6e5bd33a6385/image.png" alt=""></p>
<h2 id="외부-스크립트를-효율적으로-사용하는-방법">외부 스크립트를 효율적으로 사용하는 방법</h2>
<h3 id="script-태그의-src-속성-사용-시-단점"><code>script</code> 태그의 src 속성 사용 시 단점</h3>
<ul>
<li><strong>HTML 파싱 중단:</strong> 웹브라우저는 HTML 파일의 첫 번째 줄부터 순차적으로 파싱함. <code>script</code> 태그를 만나면 HTML 파싱을 중단하고 JavaScript를 파싱하게 됨. 이 과정에서 화면에 표시되는 내용에 딜레이가 발생할 수 있음</li>
</ul>
<h3 id="해결-방법">해결 방법</h3>
<ol>
<li><p><strong><code>script</code> 태그를 <code>body</code> 태그 끝에 위치시키기</strong></p>
<ul>
<li>화면의 주요 내용이 먼저 로드되고, 그 후에 JavaScript 파일이 로드됨</li>
</ul>
</li>
<li><p><strong><code>async</code> 속성 사용하기</strong></p>
<ul>
<li><code>async</code> 속성을 사용하면 <code>script</code> 태그를 만나도 HTML 파싱을 멈추지 않고 계속 진행함. 단, JavaScript 파일의 로드(fetching)는 병렬로 진행하고, 실행(executing) 시 HTML 파싱을 중단함</li>
<li><strong>주의:</strong> 파일 로드 순서가 보장되지 않으며, 먼저 로드된 파일이 먼저 실행됨</li>
</ul>
</li>
<li><p><strong><code>defer</code> 속성 사용하기 (보다 세련된 방법)</strong></p>
<ul>
<li><code>defer</code> 속성을 사용하면 JavaScript 파일을 병렬로 로드하되, HTML 파싱이 모두 끝난 후에 실행함</li>
<li><strong>순서 보장:</strong> 파일 로드 순서가 보장됨</li>
</ul>
</li>
</ol>
<blockquote>
<p>Tip: async와 defer 속성을 사용할 경우 script 태그를 head 태그 내에 위치시키는 것이 좋음. body 태그 끝에 작성하는 것보다 효율적임.</p>
</blockquote>
<h3 id="정리">정리</h3>
<blockquote>
<h3 id="async-속성"><code>async</code> 속성</h3>
</blockquote>
<ul>
<li><strong>병렬 로드</strong>: <code>async</code> 속성을 사용하면 브라우저는 HTML 파싱과 동시에 스크립트를 병렬로 로드합니다.</li>
<li><strong>즉시 실행</strong>: 스크립트 로드가 완료되면 HTML 파싱을 중단하고 즉시 스크립트를 실행합니다. 즉, HTML 파싱과 스크립트 실행은 동시에 이루어지지 않습니다.</li>
<li><strong>로드 순서 비보장</strong>: 여러 스크립트가 <code>async</code> 속성을 사용하면 로드된 순서대로 실행되므로, 스크립트 간의 의존성이 있을 경우 문제가 발생할 수 있습니다.<h3 id="defer-속성"><code>defer</code> 속성</h3>
</li>
<li><strong>병렬 로드</strong>: <code>defer</code> 속성을 사용하면 브라우저는 HTML 파싱과 동시에 스크립트를 병렬로 로드합니다.</li>
<li><strong>파싱 완료 후 실행</strong>: 스크립트 로드는 완료되지만, HTML 파싱이 끝난 후에 스크립트를 실행합니다.</li>
<li><strong>순서 보장</strong>: 여러 <code>defer</code> 스크립트는 HTML 파싱이 완료된 후 작성된 순서대로 실행됩니다.<h3 id="head-태그-내에-위치시키는-이유"><code>head</code> 태그 내에 위치시키는 이유</h3>
</li>
</ul>
<ol>
<li><strong>페이지 로드 속도 향상</strong>:<ul>
<li><code>script</code> 태그를 <code>head</code>에 위치시키면, 브라우저가 초기 HTML 문서를 파싱하는 동안 스크립트를 병렬로 로드할 수 있습니다. 이는 페이지 로드 시간을 단축시킵니다.</li>
<li><code>async</code>나 <code>defer</code>를 사용하지 않고 <code>body</code> 끝에 스크립트를 배치하면, HTML 파싱이 완료된 후 스크립트 로드를 시작하므로 페이지 로드 시간이 길어질 수 있습니다.</li>
</ul>
</li>
<li><strong>의존성 관리</strong>:<ul>
<li><code>head</code> 태그에 있는 스크립트는 <code>async</code>나 <code>defer</code> 속성으로 인해 HTML 파싱과 병렬로 로드되기 때문에, 페이지의 초기 로드 과정에 불필요한 지연을 주지 않습니다.</li>
<li>스크립트 간의 의존성이 있다면, <code>defer</code> 속성을 사용하여 스크립트가 HTML 파싱이 끝난 후 순서대로 실행되도록 할 수 있습니다.</li>
</ul>
</li>
<li><strong>페이지 렌더링 최적화</strong>:<ul>
<li><code>head</code> 태그 내의 <code>async</code>나 <code>defer</code> 스크립트는 HTML 파싱과 병렬로 로드되므로, 페이지의 렌더링이 최적화됩니다.</li>
<li>이는 특히 외부 라이브러리나 프레임워크를 로드하는 경우에 유용합니다. 예를 들어, 페이지 초기화 시점에 필요한 스크립트를 빠르게 로드할 수 있습니다.<h3 id="요약">요약</h3>
</li>
</ul>
</li>
</ol>
<ul>
<li><strong><code>async</code></strong>: 스크립트 로드와 동시에 HTML 파싱이 계속되지만, 로드가 완료되면 즉시 실행되며, 실행 시 HTML 파싱이 중단됩니다. 실행 순서는 보장되지 않습니다.</li>
<li><strong><code>defer</code></strong>: 스크립트 로드가 병렬로 이루어지고, HTML 파싱 완료 후 순서대로 실행됩니다.</li>
<li><strong>효율성</strong>: <code>script</code> 태그를 <code>head</code> 태그 내에 위치시켜 HTML 파싱과 병렬로 스크립트를 로드함으로써 페이지 로드 시간을 단축하고, 초기 렌더링을 최적화할 수 있습니다.</li>
<li>따라서 <code>async</code>와 <code>defer</code> 속성을 사용할 경우 <code>script</code> 태그를 <code>head</code> 태그 내에 위치시키는 것이 <code>body</code> 끝에 위치시키는 것보다 더 효율적입니다.</li>
</ul>
<hr>
<h1 id="마무리-회고">마무리 회고</h1>
<p>오랜만에 공부를 해서 행복했다. 특히 이전에는 급하게 배우느라 놓쳤던 부분들을 챙길수 있어서 유익했다. defer, async 속성도 코드를 짜면서 두루뭉술하게만 알고있었지 이렇게 체계적으로 알지는 못했다. 매일 블로그에 깨닫게 되는 지식을 기록하고 기억해야겠다.
<img src="https://velog.velcdn.com/images/cielo_hello/post/40c68a90-eff5-460d-aedb-8ee69035897e/image.png" width="50%"></p>
<hr>
<p>본 후기는 본 후기는 [유데미x스나이퍼팩토리] 프로젝트 캠프 : Next.js 2기 과정(B-log) 리뷰로 작성 되었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스터디] 리팩터링_chapter 9_데이터 조직화]]></title>
            <link>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-9%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A1%B0%EC%A7%81%ED%99%94</link>
            <guid>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-9%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A1%B0%EC%A7%81%ED%99%94</guid>
            <pubDate>Thu, 04 Jul 2024 01:58:03 GMT</pubDate>
            <description><![CDATA[<p>지난 주에 이어서 &quot;코스 메이커&quot; 프로젝트의 코드를 리팩토링 할 때 9장의 기술을 적용해 보기로 했다.</p>
<h1 id="91-변수-쪼개기">9.1 변수 쪼개기</h1>
<h2 id="변수의-용도">변수의 용도</h2>
<ol>
<li>변수에 값을 여러 번 대입할 수밖에 없는 경우<ul>
<li>루프 변수</li>
<li>수집 변수</li>
</ul>
</li>
<li>긴 코드의 결과를 저장했다가 나중에 쉽게 참조하려는 목적으로 쓰이는 경우<ul>
<li>이런 변수에는 값을 단 한 번만 대입해야 함</li>
<li>대입이 두 번 이상 이뤄진다면 여러 가지 역할을 수행한다는 신호임</li>
<li>역할이 둘 이상인 변수가 있다면 쪼개야함</li>
</ul>
</li>
</ol>
<h2 id="코드에-적용하기입력-매개변수의-값을-수정할-때">코드에 적용하기(입력 매개변수의 값을 수정할 때)</h2>
<ul>
<li><p>회원가입 페이지에서 입력된 숫자를 전화번호 형식(000-0000-0000)으로 변환하는 함수인 <code>formatPhoneNumber</code>함수에서 매개변수 <code>value</code>가 두가지 용도로 사용된다는 사실을 알게 됨</p>
</li>
<li><p>수정 전 함수</p>
<pre><code class="language-js">export const formatPhoneNumber = (value: string) =&gt; {
value = value.replace(/\D/g, &quot;&quot;);
if (value.length &gt; 11) {
  value = value.slice(0, 11);
}

const parts = [];
if (value.length &gt; 3) {
  parts.push(value.substring(0, 3));
  if (value.length &gt; 7) {
    parts.push(value.substring(3, 7));
    parts.push(value.substring(7));
  } else {
    parts.push(value.substring(3));
  }
} else {
  parts.push(value);
}

return parts.join(&quot;-&quot;);
};
</code></pre>
</li>
</ul>
<pre><code>- 위 함수에서 매개변수 `value`는 다음 2가지 역할을 함
  1. 함수에 데이터를 전달
  2. 입력된 전화번호 문자열을 저장하고 가공
- 수정 후 함수
```js
export const formatPhoneNumber = (phoneNumber: string) =&gt; {
  let formattedNumber = phoneNumber.replace(/\D/g, &quot;&quot;);
  if (formattedNumber.length &gt; 11) {
    formattedNumber = formattedNumber.slice(0, 11);
  }

  const parts = [];
  if (formattedNumber.length &gt; 3) {
    parts.push(formattedNumber.substring(0, 3));
    if (formattedNumber.length &gt; 7) {
      parts.push(formattedNumber.substring(3, 7));
      parts.push(formattedNumber.substring(7));
    } else {
      parts.push(formattedNumber.substring(3));
    }
  } else {
    parts.push(formattedNumber);
  }

  return parts.join(&quot;-&quot;);
};</code></pre><ul>
<li><code>value</code> 매개변수를 <code>phoneNumber</code>와 <code>formattedNumber</code>로 분리해서 각 변수가 하나의 역할만 수행하도록 함<ol>
<li>함수에 데이터를 전달 -&gt; <code>phoneNumber</code></li>
<li>입력된 전화번호 문자열을 저장하고 가공 -&gt; <code>formattedNumber</code></li>
</ol>
</li>
<li>그 결과 변수들의 역할을 더 분명해졌고, 코드를 유지 보수하기에도 용이해짐</li>
</ul>
<hr>
<h1 id="92-필드-이름-바꾸기">9.2 필드 이름 바꾸기</h1>
<ul>
<li>레코드의 유효 범위가 제한적이라면 필드에 적근하는 모든 코드를 수정한 후 테스트 하면 됨</li>
<li>레코드의 유효 범위가 넓다면 캡슐화를 진행하고 게터와 세터 이름을 바꾸면 됨<ul>
<li>바꾸는 과정에서 <code>this_title = (data.title !== undefined) ? data.title : data.name</code>과 같이 코드를 구성하여 바꾸기 전<code>name</code>과 바꾼 후<code>title</code>가 모두 적용되도록 조치하는 것을 잊지 말자</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스터디] 리팩터링_chapter 8_기능 이동]]></title>
            <link>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-8%EA%B8%B0%EB%8A%A5-%EC%9D%B4%EB%8F%99</link>
            <guid>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-8%EA%B8%B0%EB%8A%A5-%EC%9D%B4%EB%8F%99</guid>
            <pubDate>Mon, 01 Jul 2024 08:36:21 GMT</pubDate>
            <description><![CDATA[<p>요즘 &quot;코스메이커&quot; 프로젝트에서 리팩터링을 진행하고 있는데, 이번 챕터에서 나온 기술들을 직접 적용해 보기로 했다.</p>
<h1 id="81-함수-옮기기">8.1 함수 옮기기</h1>
<blockquote>
<p>함수를 옮길지 말지를 정하기란 쉽지 않다. 그럴 땐 대상 함수의 현재 컨텍스트와 후보 컨텍스르틑 둘러보면 도움이 된다.</p>
</blockquote>
<ul>
<li>좋은 소프트웨어 설계의 핵심은 모듈화가 얼마나 잘 되어 있느냐를 뜻하는 모듈성이다. 모듈성이란 프로그램의 어딘가를 수정하려 할 때 해당 기능과 깊이 관련된 작은 일부만 이해해도 가능하게 해주는 능력이다.</li>
</ul>
<h2 id="실제-코드에-적용하기">실제 코드에 적용하기</h2>
<h3 id="1-선택한-함수가-현재-컨텍스트에서-사용-중인-모든-프로그램-요소를-살펴본다-이-요소들-중에도-함께-옮겨야-할-게-있는지-고민해-본다">1. 선택한 함수가 현재 컨텍스트에서 사용 중인 모든 프로그램 요소를 살펴본다. 이 요소들 중에도 함께 옮겨야 할 게 있는지 고민해 본다.</h3>
<ul>
<li>선택한 함수: <code>groupTagsByDescription</code>함수로 <code>src\components\domains\spotRegister\BadgeLists.tsx</code>와 <code>src\pages\SearchPage\index.tsx</code>에서 사용되고 있는 함수이다.<pre><code class="language-jsx">const groupTagsByDescription = (tags: tagResponseDto[]) =&gt; {
  return tags.reduce(
    (acc, tag) =&gt; {
      if (!acc[tag.description]) {
        acc[tag.description] = [];
      }
      acc[tag.description].push(tag);
      return acc;
    },
    {} as Record&lt;string, tagResponseDto[]&gt;,
  );
};</code></pre>
</li>
<li>함께 옮겨야 할 요소는 따로 없다.<h3 id="2-선택한-함수가-다형-메서드인지-확인한다">2. 선택한 함수가 다형 메서드인지 확인한다.</h3>
</li>
<li>다형 메서드(Polymorphic Function): <ul>
<li>다형 메서드는 함수의 인수 타입에 따라 다르게 동작하는 함수를 의미함</li>
<li>즉, 같은 이름의 함수라도 인수 타입이 다르면 다른 동작을 할 수 있음</li>
<li>이를 통해 코드의 유연성과 재사용성을 높일 수 있음</li>
</ul>
</li>
<li><code>groupTagsByDescription</code> 함수는 <code>tagResponseDto[]</code> 타입의 배열을 받아 <code>description</code> 속성을 기준으로 그룹화하여 반환하는 함수임<ul>
<li>이 함수는 인수 타입이 고정되어 있으므로 다형 메서드라고 볼 수 없음</li>
</ul>
</li>
</ul>
<h3 id="3-선택한-함수를-타깃-컨텍스트로-복사한다-타깃-함수가-새로운-터전에-잘-자리-잡도록-다듬는다">3. 선택한 함수를 타깃 컨텍스트로 복사한다. 타깃 함수가 새로운 터전에 잘 자리 잡도록 다듬는다.</h3>
<ul>
<li><code>src\utils\groupTags.ts</code>파일을 만들고 함수를 소스 함수를 복사함<pre><code class="language-js">import { tagResponseDto } from &quot;@/api/tag/type&quot;;
</code></pre>
</li>
</ul>
<p>const groupTags = (tags: tagResponseDto[]): Record&lt;string, tagResponseDto[]&gt; =&gt; {
  return tags.reduce(
    (acc, tag) =&gt; {
      if (!acc[tag.description]) {
        acc[tag.description] = [];
      }
      acc[tag.description].push(tag);
      return acc;
    },
    {} as Record&lt;string, tagResponseDto[]&gt;,
  );
};</p>
<p>export default groupTags;</p>
<pre><code>
### 4. 정적 분석을 수행한다.
- 정적 분석
  - 코드를 실행하지 않고 소스 코드를 분석하여 잠재적인 문제점을 찾는 방법
  - 이를 통해 보안 취약점, 성능 문제 등을 사전에 발견할 수 있음

### 5. 소스 컨텍스트에서 타깃 함수를 참조할 방법을 찾아 반영한다.
- 모듈 문법을 사용하여 타깃 함수를 `import`해옴
```js
import groupTags from &quot;@/utils/groupTags&quot;;
//중략

const BadgeLists = ({ selectedBadges, onChange }: BadgeListProps) =&gt; {
 //중략
  useEffect(() =&gt; {
    const fetchLists = async () =&gt; {
      try {
        const response = await getTag();
        setTagsData(response);
        // console.log(response.data);
      } catch (error) {
        console.error(&quot;Error fetching data:&quot;, error);
      }
    };

    fetchLists();
  }, []);

  const groupedTags = groupTags(tagsData);

  return (
    &lt;&gt;
        //중략
    &lt;/&gt;
  );
};

export default BadgeLists;
</code></pre><h3 id="6-소스-함수를-타깃-함수의-위임-함수가-되도록-수정한다">6. 소스 함수를 타깃 함수의 위임 함수가 되도록 수정한다.</h3>
<ul>
<li>간단한 경우여서 위임 함수를 만들지 않고 바로 사용함</li>
</ul>
<h3 id="7-테스트-한다">7. 테스트 한다.</h3>
<ul>
<li>tag 목록이 화면에 잘 렌더링 됐고, submit 시에도 tag 데이터가 잘 출력되는 것을 확인함
<img src="https://velog.velcdn.com/images/cielo_hello/post/8d0e4eb2-47c3-4f61-8bf5-cb7da83fac0d/image.png" alt=""></li>
</ul>
<h3 id="8-소스-함수를-인라인할지-고민해본다">8. 소스 함수를 인라인할지 고민해본다.</h3>
<ul>
<li>함수를 인라인한 후 마무리 했다.</li>
<li>최종 코드</li>
</ul>
<pre><code class="language-jsx">import { getTag } from &quot;@/api/tag&quot;;
import { tagResponseDto } from &quot;@/api/tag/type&quot;;
import BadgeList from &quot;@/components/commons/BadgeList/BadgeList&quot;;
import groupTags from &quot;@/utils/groupTags&quot;;
import { useEffect, useState } from &quot;react&quot;;

interface BadgeListProps {
  selectedBadges: tagResponseDto[];
  onChange: (updatedDestinationBadges: tagResponseDto[]) =&gt; void;
}

const BadgeLists = ({ selectedBadges, onChange }: BadgeListProps) =&gt; {
  const [tagsData, setTagsData] = useState&lt;tagResponseDto[]&gt;([]);
  const [selectedDestinationBadges, setSelectedDestinationBadges] = useState&lt;tagResponseDto[]&gt;(selectedBadges);

  useEffect(() =&gt; {
    onChange(selectedDestinationBadges);
  }, [selectedDestinationBadges, onChange]);

  useEffect(() =&gt; {
    const fetchLists = async () =&gt; {
      try {
        const response = await getTag();
        setTagsData(response);
      } catch (error) {
        console.error(&quot;Error fetching data:&quot;, error);
      }
    };

    fetchLists();
  }, []);

  const groupedTags = groupTags(tagsData);
  return (
    &lt;&gt;
      {Object.entries(groupedTags).map(([description, tags]) =&gt; (
        &lt;BadgeList
          key={description}
          title={description}
          tags={tags}
          selectedBadges={selectedDestinationBadges}
          setSelectedBadges={setSelectedDestinationBadges}
        /&gt;
      ))}
    &lt;/&gt;
  );
};

export default BadgeLists;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[라이브러리] 상태 관리 라이브러리]]></title>
            <link>https://velog.io/@cielo_hello/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@cielo_hello/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Fri, 24 May 2024 11:52:19 GMT</pubDate>
            <description><![CDATA[<h1 id="프론트엔드에서-상태-관리란">프론트엔드에서 상태 관리란?</h1>
<h2 id="상태state란">상태(State)란?</h2>
<blockquote>
<p>State: A Component&#39;s Memory</p>
</blockquote>
<p>사용자가 어플리케이션의 인풋창에 무언가를 입력하면 입력 필드가 업데이트 되고, 캐러셀에서 다음을 클릭하면 이미지가 변경되며, &#39;구매&#39;를 클릭하면 제품이 장바구니에 담겨야 합니다. 이처럼 컴포넌트는 사용자와 어플리케이션 간의 상호작용의 결과에 따라 화면을 변경해야 합니다. 따라서 컴포넌트는 현재 입력값, 현재 이미지, 장바구니 항목 같은 것들을 &#39;기억&#39;해야하는데 <strong>React에서는 이런 종류의 컴포넌트별 메모리를 state, 즉 상태라고 합니다</strong>.
<a href="https://react.dev/learn/state-a-components-memory">리액트 공식 문서_State</a></p>
<h2 id="상태-관리state-management란">상태 관리(State Management)란?</h2>
<blockquote>
<p>FE에서의 상태관리란 데이터를 설계된 UI, UX에 맞게 설계하고 구현하는 일입니다. 또한 네트워크를 통해 서버로 전달되는 클라이언트의 요청에 따라 변화하는 상태를 관리하는 일입니다.
<a href="https://joonfluence.tistory.com/473">블로그_[FE] 프론트엔드에서의 상태관리란 무엇인가? (1) 등장배경과 정의
</a></p>
</blockquote>
<h3 id="상태-관리가-필요한-이유">상태 관리가 필요한 이유</h3>
<ol>
<li>상태(데이터)가 바뀌었을 때 페이지 전체가 아닌 해당 부분만 렌더링 시킬 수 있다.</li>
<li>의도하지 않은 UI/UX를 보여주지 않을 수 있다.</li>
<li>네트워크 통신 횟수를 획기적으로 줄일 수 있다.
<a href="https://www.youtube.com/watch?v=S5BouBRoO9E">유튜브_[10분 테코톡] 패트릭의 상태관리</a></li>
</ol>
<hr>
<h1 id="상태-관리를-하는-다양한-방법들">상태 관리를 하는 다양한 방법들</h1>
<h2 id="react-내장-상태-관리-도구">React 내장 상태 관리 도구</h2>
<p>React 내장 상태 관리 도구 에는 useState, useReducer, Context가 있는데, useState와 Context의 특징과 한계점에 대해 알아보았다.</p>
<h3 id="usestate">useState</h3>
<blockquote>
<p>useState는 가장 기본적인 상태 관리 훅으로 간단한 상태 변화를 다룰 때 사용됩니다. 컴포넌트 내부에서 사용자의 상호작용이나 데이터 변화에 따른 상태의 업데이트가 필요할 때 useState를 통해 이를 간편하게 처리할 수 있습니다.</p>
</blockquote>
<pre><code class="language-js">import React, { useState } from &#39;react&#39;;

function Counter() {
  const [count, setCount] = useState(0);

  return (
    &lt;div&gt;
      &lt;p&gt;현재 카운트: {count}&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;증가&lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;감소&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p><a href="https://reactnext-central.xyz/blog/react/state-context-reducer">리액트 상태 관리: useState, useContext, useReducer 활용하기</a></p>
<h4 id="장점">장점</h4>
<ul>
<li>DOM에 접근 없이도 데이터가 변경되면 값을 변경할 수 있음</li>
<li>DOM 중심의 상태관리 로직에서 데이터 중심의 상태관리 로직으로 변화함
<del>- 어디서 상태가 변경되었는지 추적에 유리함</del>
<a href="https://www.youtube.com/watch?v=jqir73Lourk">유튜브_[10분 테코톡] 온스타의 상태관리</a></li>
</ul>
<h4 id="한계점">한계점</h4>
<ul>
<li>state는 오직 자식 컴포넌트에게만 전달되기 때문에, 상태를 사용하는 컴포넌트 간 거리가 멀 경우 Props Drilling 현상이 발생할 수 있다.<ul>
<li>어디서 상태가 변경되었는지 추적이 어려워짐</li>
<li>불필요한 렌더링이 발생할 수 있음</li>
<li>이외에도 유지보수의 어려움, 컴포넌트 재사용성 저하, 리팩토링의 어려움 등의 문제가 있음
<img src="https://velog.velcdn.com/images/cielo_hello/post/861a2681-3b9e-4c84-ba88-c8d03bf86efa/image.png" alt="">
<a href="https://react.dev/learn/passing-data-deeply-with-context">리액트 공식 문서_Prop drilling</a></li>
</ul>
</li>
</ul>
<h3 id="context">Context</h3>
<p>컨텍스트를 활용하면, 부모 컴포넌트가 props를 통해 상태를 전달하지 않아도, 자식 컴포넌트의 깊이와 상관없이 특정 정보를 &#39;전달&#39;할 수 있다. 따라서 Props Drilling 문제를 해결하고, 전역 상태를 관리하는 데 유용하다.
<a href="https://react.dev/learn/passing-data-deeply-with-context">Passing Data Deeply with Context</a></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/31e037f2-92ba-4b85-89b1-19b8f121c756/image.png" alt="">
<a href="https://react.dev/learn/passing-data-deeply-with-context">리액트 공식 문서_Using context in distant children</a></p>
<p>사실 Context는 상태 관리 도구는 아니며 그저 전역적으로 state를 공유하는 기능만 수행한다. 상태 관리는 사실상 useState, useReducer가 해준다. 
<a href="https://kang-ju.tistory.com/entry/React-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%9D%98-%EC%9D%B4%ED%95%B4-Redux-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC">[React] 상태 관리 라이브러리의 이해 - Redux 동작 원리</a></p>
<h4 id="장점-1">장점</h4>
<ul>
<li>Props Drilling 문제를 해결해 전역 상태 관리에 유용함</li>
<li>추가적인 라이브러리 없이 리액트 자체에서 제공되는 기능임<ul>
<li>비교적 간편하게 사용할 수 있음</li>
<li>라이브러리 다운로드로 인한 앱 용량 증가를 막을 수 있음</li>
</ul>
</li>
</ul>
<h4 id="한계점-1">한계점</h4>
<ul>
<li>불필요한 렌더링이 많음<ul>
<li>context의 상태값이 달라지면 구독하고 있는 하위 컴포넌트가 모두 다시 렌더링 됨</li>
<li>많은 하위 컴포넌트를 가지고 있거나, 렌더링 비용이 높은 컴포넌트들에게 문제가 될 수 있음</li>
</ul>
</li>
<li>복잡한 상태 로직이나 미들웨어를 필요로 하는 경우에는 한계가 있음</li>
</ul>
<h4 id="총평">총평</h4>
<ul>
<li>단순히 Props Drilling 해결이 목적이고 렌더링 최적화가 필요없는 경우에 적합함</li>
<li>상태(state) 변경이 거의 일어나지 않으면서 전역 상태로 관리해야 하는 locale이나 theme정보 등을 관리할 때 적합함</li>
<li>간단한 상태 관리가 필요한 소규모 프로젝트의 경우 적합함</li>
</ul>
<h2 id="전역-상태-관리-라이브러리">전역 상태 관리 라이브러리</h2>
<p>전역 상태 관리 라이브러리는 애플리케이션의 여러 상태를 효율적으로 관리하기 위한 도구이다. 애플리케이션의 상태를 전역적으로 관리할 수있는 것이 가장 큰 장점이며 라이브러리 마다 세부적인 특징은 다르다. 전역 상태 관리 라이브러리에는 Redux, MobX, Recoil, Zustand, Jotai, Valtio 등이 있다.
<img src="https://velog.velcdn.com/images/cielo_hello/post/79ff9e47-a204-44cb-bd9d-16a97193ccba/image.png" alt="">
<a href="https://www.youtube.com/watch?v=nkXIpGjVxWU&amp;t=931s">유튜브_프론트엔드 상태관리 실전 편 with React Query &amp; Zustand</a></p>
<h3 id="🧐전역-상태-관리-라이브러리가-해결하고자-하는-문제">🧐전역 상태 관리 라이브러리가 해결하고자 하는 문제</h3>
<ol>
<li>저장된 상태를 컴포넌트 트리 어디에서든지 읽어 올 수 있는 기능 제공</li>
<li>저장된 상태를 수정하는 기능 제공</li>
<li>렌더링을 최적화하는 메커니즘 제공</li>
<li>메모리 사용을 최적화하는 메커니즘 제공
<a href="https://medium.com/@yujso66/%EB%B2%88%EC%97%AD-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%9D%90%EB%A6%84-6e5ed0022e39">Medium_[번역] 리액트 상태 관리의 새로운 흐름</a></li>
</ol>
<h3 id="redux">Redux</h3>
<blockquote>
<p>A JS library for predictable and maintainable global state management
<a href="https://redux.js.org/">Redux 공식 문서</a></p>
</blockquote>
<p>Redux는 리액트 애플리케이션에서 가장 널리 사용되는 상태 관리 라이브러리 중 하나다. Flux 아키텍쳐와 reducer의 개념을 합쳐서 만든 전역 상태 관리 라이브러리이다. 공식 문서에서는 애플리케이션의 복잡성을 낮춰서 작성한 코드가 어떤 결과를 가져올지 예측 가능하게 만들어주는 도구라고 소개하고 있다.
<a href="https://www.youtube.com/watch?v=Jr9i3Lgb5Qc&amp;list=PLuHgQVnccGMB-iGMgONoRPArZfjRuRNVc">유튜브_Redux - 1. 수업소개</a></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/4684acab-c37c-4176-a7d8-4bcbc563c062/image.png" alt="">
대표적인 특징은 다음과 같다.</p>
<ul>
<li>중앙 집중식 상태 관리: 애플리케이션의 모든 상태를 하나의 스토어에서 관리하여 일관성과 예측 가능성을 높인다.</li>
<li>불변성: 상태는 불변 객체로 관리되며, 상태 변경은 순수 함수인 리듀서를 통해 이루어진다.</li>
<li>액션과 디스패치: 상태 변경은 액션 객체를 디스패치하여 이루어지며, 액션은 상태 변경의 의도를 명확히 나타낸다.</li>
<li>미들웨어: Redux Thunk나 Redux Saga와 같은 미들웨어를 사용하여 비동기 로직이나 부수 효과를 관리할 수 있다.</li>
<li>디버깅 도구: Redux DevTools와 같은 강력한 디버깅 도구를 제공하여 상태 변화와 액션을 추적하고 디버깅을 용이하게 한다.</li>
<li>렌더링 최적화: connect 함수와 useSelector 훅을 사용하여 필요한 상태가 변경될 때만 컴포넌트를 다시 렌더링하도록 최적화할 수 있어서, 불필요한 업데이트를 방지하여 성능을 향상시킨다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/ec413b29-3301-4825-ba2f-3475c0da1555/image.png" alt="">
직접 설치해본 결과 Redux 5.0.1 버전의 용량은 약 289.8 kB 였는데 실제로 애플리케이션에서 사용하는 경우, 필요한 미들웨어와 DevTools 등을 포함하면 이보다 약간 더 커질 수 있을 것 같다.</p>
<h4 id="장점-2">장점</h4>
<ul>
<li>크고 복잡한 앱에서 확장성이 높음</li>
<li>액션에 따른 모든 변경을 추적 가능</li>
<li>&quot;특정 상태 조각이 언제 변경되었으며 데이터는 어디에서 왔는지&quot; 동작을 예측 가능</li>
<li>Redux DevTools로 디버깅과 상태 변화의 추적을 용이하게 할 수 있음</li>
<li>풍부한 Redux 관련 오픈소스 생태계
<a href="https://velog.io/@yena1025/%EB%A6%AC%EB%8D%95%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%ED%95%AD%EC%83%81-%EC%A2%8B%EC%9D%80%EA%B0%80%EC%9A%94-feat.-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C">velog_리덕스를 써야 할 때 &amp; 리덕스의 장단점 (feat. 공식문서)</a></li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>사용법이 상대적으로 복잡하며, 보일러플레이트 코드가 많음</li>
<li>Redux 자체뿐만 아니라 미들웨어, 바인딩 라이브러리 등 학습해야할 것이 많음</li>
</ul>
<h4 id="총평-1">총평</h4>
<p>Redux 공식 문서에 따르면 다음과 같은 경우에 사용하길 권장한다.</p>
<ul>
<li>앱의 여러 위치에서 필요한 상태의 양이 많을 때</li>
<li>앱 상태가 자주 업데이트되는 경우</li>
<li>상태를 업데이트 하는 로직이 복잡한 경우</li>
<li>중간 또는 큰 규모의 코드베이스를 가지고 많은 사람들이 작업할 때</li>
<li>시간이 지남에 따라 상태가 어떻게 업데이트되는지 확인해야 할 때
<a href="https://redux.js.org/faq/general">Redux 공식 문서</a></li>
</ul>
<h3 id="recoil">Recoil</h3>
<p>Redux와 같은 기존의 전역 상태 관리 도구는 리액트 라이브러리가 아니어서 리액트 내부 스케줄러에 접근할 수 없다는 단점이 있었다. 이런 단점은 리액트에 동시성 모드(Concurrent mode)와 같은 새로운 기능이 도입되었을 때 이 기능들이 최적으로 작동하지 못하는 문제를 야기했다. Recoil은 이런 문제를 해결하기 위해 만들어 졌고, 2020년 페이스북 팀의 한 엔지니어가 실험 단계로 컨퍼런스에서 공개하면서 세상에 알려지게 되었다. </p>
<p>간단히 말해, Recoil은 기존 상태 관리 도구들이 가지고 있던 한계를 극복하고, 리액트 애플리케이션의 성능과 개발 효율성을 높이기 위한 목적으로 개발되었다. 즉, 리액트의 최신 기능들, 특히 동시성 모드와 같은 고급 기능을 지원하기 위해 만들어진 상태 관리 라이브러리이다.</p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/b163fcdf-5c5a-4e88-a41c-5f3d12a1ca4a/image.png" alt="">
<a href="https://dev.to/coleredfearn/atomos-a-new-recoil-visualization-tool-powered-by-react-flow-4b6l">Atomos — A New Recoil Visualization Tool Powered by React Flow</a></p>
<p>리코일의 주요 특징은 다음과 같다.</p>
<ul>
<li>리액트 문법 친화적이다. 리액트의 상태처럼 간단한 get/set 인터페이스로 사용할 수 있는 보일러 플레이트가 없는 API를 제공한다.</li>
<li>비동기 처리를 추가적인 라이브러리 없이(e.g. redux-thunk, redux-saga) 리코일 안에서 가능하다.</li>
<li>내부적으로 캐싱을 지원한다. 동일한 atom 값에 대한 내부적으로 메모이제이션된 값을 반환하여 속도가 빠르다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/cb0e065c-bd4c-4e04-bf53-1b8fa3fb158c/image.png" alt=""></p>
<p>직접 설치해본 결과 Recoil7.7 버전의 용량은 약 2.2 MB였다.</p>
<h4 id="장점-3">장점</h4>
<ul>
<li>낮은 러닝 커브와 적은 코드량</li>
<li>간편한 비동기 처리</li>
<li>랜더링 최적화</li>
</ul>
<h4 id="한계점-2">한계점</h4>
<ul>
<li>안정성<ul>
<li>레포지토리가 facebook/Recoil이 아닌 facebookexperimental/Recoil임</li>
</ul>
</li>
<li>관련 오픈소스 생태계가 redux에 비해서 부족함</li>
<li>아톰이 여러 군데에서 사용되면 사이드 이펙트가 발생할 수 있음<ul>
<li>전역 상태를 아톰으로 관리하고 어느 컴포넌트에서도 바로 아톰을 구독해서 업데이트를 받을 수 있기 때문</li>
</ul>
</li>
<li>아톰과 셀렉터가 많아지면 의존성이 여러 방향으로 엮이면서 점점 어떤 의존이 이루어지는지 파악하기 어려워짐<ul>
<li>컴포넌트를 컨테이너/프레젠테이션 방식으로 나누어 상태를 관리하는 부분/보여주기만 별도로 가져가는 방법을 이용하면 이런 이슈를 컨트롤할 수 있음</li>
</ul>
</li>
</ul>
<p><a href="https://www.youtube.com/watch?v=S5BouBRoO9E">유튜브_[10분 테코톡] 패트릭의 상태관리</a>
<a href="https://yozm.wishket.com/magazine/detail/2233/">리액트 상태 관리 라이브러리, 어떤 것을 써야 할까?</a></p>
<h3 id="zustand">zustand</h3>
<p>주스탠드(zustand)는 독일어로 ‘상태’라는 뜻을 가졌고, 조타이(Jotai)를 만든 카토 다이시가 주스탠드도 만들어 관리하고 있다. 데이터를 중앙 집중형 스토어에 저장하고 Action을 통해 데이터를 조작하는 패턴인 Flux 패턴을 따르는데, 이런 점은 Redux와 비슷한다. 주스탠드는 특정 라이브러리에 종속되어 만들어진 도구는 아니므로 바닐라 자바스크립트에서도 사용이 가능하다.</p>
<p>zustand의 주요 특징은 다음과 같다.</p>
<ul>
<li>주스탠드는 발행/구독 모델(pub/sub)을 기반으로 이루어져 있다. <ul>
<li>스토어의 상태 변경이 일어날 때 실행할 리스너 함수를 모아 두었다가(sub), 상태가 변경되었을 때 등록된 리스너에게 상태가 변경되었다고 알려준다(pub).</li>
</ul>
</li>
<li>스토어를 생성하는 함수 호출 시 클로저를 사용한다. <ul>
<li>이로 인한 특징으로 상태를 변경, 조회, 구독하는 인터페이스를 통해서만 상태를 다루고, 실제 상태는 생명 주기에 따라 처음부터 끝까지 의도하지 않는 변경에 대해 막을 수 있다는 점이 있다.</li>
</ul>
</li>
<li>상태를 위에서 내리는 방식은 top-down 방식으로 상태를 관리한다.<ul>
<li>전체적인 오버뷰에서 디테일 세부사항으로 스토어 모델링을 하는 것이 좋다. </li>
<li>예를 들어, 블로그를 위한 스토어를 만든다고 하면 블로그 &gt; 포스트 &gt; 작가, 제목, 내용 이런 식으로 말이다.</li>
</ul>
</li>
</ul>
<p><a href="https://yozm.wishket.com/magazine/detail/2233/">리액트 상태 관리 라이브러리, 어떤 것을 써야 할까?</a></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/ab05a61c-d48d-4dc5-9798-a449cec5769e/image.png" alt=""></p>
<p>직접 설치해본 결과 zustand4.5.2 버전의 용량은 326.9kB 였다.</p>
<h4 id="장점-4">장점</h4>
<ul>
<li>낮은 러닝 커브와 적은 코드량</li>
<li>랜더링 최적화</li>
<li>Redux DevTools로 디버깅과 상태 변화의 추적을 용이하게 할 수 있음</li>
<li>아톰을 바꿔주는 로직을 컴포넌트나 커스텀 훅이 아닌 스토어에서 바로 할 수 있음</li>
<li>다양한 미들웨어 지원</li>
<li>일시적 업데이트(Transient Update)를 이용해서 상태가 자주 바뀌더라도 매번 업데이트가 일어나지 않고 리렌더링을 제어할 수 있음</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>성능이 중요한 앱에서 탑다운 방식은 적합하지 않음</li>
<li>비교적 생긴지 얼마 되지 않은 도구이기 때문에, IDE 등에서 쓸 수 있는 익스텐션, 플러그인, 스니펫 등이 많이 없음</li>
</ul>
<h3 id="jotai">Jotai</h3>
<p>조타이(Jotai)는 아토믹 접근(Atomic Approach)을 가지고 만든 리액트 상태 관리 도구다. 이런 점은 Recoil과 비슷하다. 조타이의 가장 기본적인 단위는 아톰(Atom)이다. 원시 타입과 객체 타입을 담을 수 있고, 다른 아톰에서 값을 가져와서 만드는 것도 가능하다.</p>
<p>상태를 밑에서 부터 조립해서 위로 올리는 방식인 bottion-up 방식을 사용한다. 처음에 아톰을 정의하고, 그것을 차곡차곡 큰 조각의 상태들로 만들어 나간다. 이러한 바텀업 방식은 성능이 중요한 앱에서 많이 사용된다.</p>
<p><a href="https://yozm.wishket.com/magazine/detail/2233/">리액트 상태 관리 라이브러리, 어떤 것을 써야 할까?</a></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/843ed295-b6b6-419f-ab84-cdedc66a29f1/image.png" alt="">
직접 설치해본 결과 Jotai2.8.1 버전의 용량은 528.4kB 였다.</p>
<h4 id="장점-5">장점</h4>
<ul>
<li>낮은 러닝 커브</li>
<li>리렌더링을 줄여주는 selectAtom이나 splitAtom과 같은 유틸에 대한 지원이 많음</li>
<li>바텀업 방식은 성능이 중요한 앱에서 많이 사용됨</li>
</ul>
<h4 id="단점-2">단점</h4>
<ul>
<li>아직 다른 경쟁 도구들에 비해 사용자 수가 많지 않고, 레퍼런스가 부족함</li>
</ul>
<h3 id="표로-비교해-보기">표로 비교해 보기</h3>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/37108357-cf95-4b7b-9d35-dd12e4dc78e2/image.png" alt=""></p>
<p><a href="https://reliasoftware.com/blog/react-state-management-libraries">7 Best React State Management Libraries for Any Project Size
</a></p>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/085f5691-9f16-44f2-94ab-fc1db6909f98/image.png" alt=""></p>
<p><a href="https://medium.com/@yujso66/%EB%B2%88%EC%97%AD-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC%EC%9D%98-%EC%83%88%EB%A1%9C%EC%9A%B4-%ED%9D%90%EB%A6%84-6e5ed0022e39">[번역] 리액트 상태 관리의 새로운 흐름</a></p>
<h2 id="서버-상태-관리-라이브러리">서버 상태 관리 라이브러리</h2>
<p>클라이언트 애플리케이션에서 서버에서 가져온 테이터를 효율적으로 관리하고 동기화하는 데 중점을 둔다. TanStack Query(구 React Query), Apollo Client, SWR 등이 있다.</p>
<hr>
<h1 id="결론">결론</h1>
<h2 id="다른-사람들은-어떤-라이브러리를-사용할까">다른 사람들은 어떤 라이브러리를 사용할까?</h2>
<h3 id="npm-trends">npm trends</h3>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/e6797d23-cacc-407a-9ee9-b7eb26cefb58/image.png" alt="">
<img src="https://velog.velcdn.com/images/cielo_hello/post/6f7485ce-c6db-46db-8944-be85ea0fad3e/image.png" alt=""></p>
<p>현업에서 Redux가 4년 정도 자리를 잡고 있었고, 기존에 있는 것들을 함부로 바꾸기는 어렵기 때문에 현재도 압도적인 1위를 차지하고 있는 것으로 보인다. 하지만 러닝 커브가 높고 많은 세팅을 필요로하기 때문에 신사업이나 새로 하는 프로젝트에서는 많이 쓰고 있지 않다고 한다.
<a href="https://www.youtube.com/watch?v=AT1b0jxmiRY">유튜브_프론트엔드 개발자가 React 상태관리 선택이 중요한 이유ㅣ유니콘 기업 현직자</a></p>
<h2 id="우리-프로젝트에는-어떤-방법을-써야할까">우리 프로젝트에는 어떤 방법을 써야할까?</h2>
<ol>
<li>지금 내가 해결하려는 문제를 구체적으로 먼저 정의 (무엇을 해결하려고 하는가?)</li>
<li>이 문제를 해결하기 위해 더 잘 해결할 수 있는 도구가 있는지 생각하기</li>
</ol>
<hr>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://www.youtube.com/watch?v=S5BouBRoO9E">유튜브_[10분 테코톡] 패트릭의 상태관리</a></li>
<li><a href="https://kang-ju.tistory.com/entry/React-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%9D%98-%EC%9D%B4%ED%95%B4-Redux-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC">블로그_[React] 상태 관리 라이브러리의 이해 - Redux 동작 원리</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스터디] 리팩터링_chapter 6_기본적인 리팩터링]]></title>
            <link>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-6%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-6%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81</guid>
            <pubDate>Thu, 23 May 2024 03:11:17 GMT</pubDate>
            <description><![CDATA[<h1 id="함수-추출하기">함수 추출하기</h1>
<h2 id="예시유효범위를-벗어나는-변수가-없을-때">예시:유효범위를 벗어나는 변수가 없을 때</h2>
<ul>
<li>이 경우는 아주 간단한 경우로 아래 순서대로 하면 된다.<ol>
<li>추출할 코드를 잘라내서 새 함수에 붙인다.</li>
<li>원래 자리에 새 함수 호출문을 넣는다.<pre><code class="language-js">//before
</code></pre>
</li>
</ol>
</li>
</ul>
<p>function printOwing(invoice) {
  let outstanding = 0;</p>
<p>  // 배너 출력
  console.log(&#39;<strong><strong>***</strong></strong>&#39;);
  console.log(&#39;<strong>고객 채무</strong>&#39;);
  console.log(&#39;<strong><strong>***</strong></strong>&#39;);</p>
<p>  // ...생략
}</p>
<pre><code>
```js
//after

function printOwing(invoice) {
  let outstanding = 0;

  // 배너 출력
  printBanner();

  // ...생략
}

function printBanner() {
  console.log(&#39;***********&#39;);
  console.log(&#39;**고객 채무**&#39;);
  console.log(&#39;***********&#39;);
}</code></pre><h2 id="예시-지역-변수를-사용할-때">예시: 지역 변수를 사용할 때</h2>
<ul>
<li>지역 변수를 사용하지만 다른 값을 다시 대입하지는 않는 경우다.</li>
<li>지역 변수를 매개변수로 받는 함수로 추출하면 된다.</li>
</ul>
<pre><code class="language-js">// before

function printOwing(invoice) {

  //..생략

  // 세부사항 출력
  console.log(`고객명: ${invoice.customer}`);
  console.log(`채무액: ${outstanding}`);
  console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
}</code></pre>
<pre><code class="language-js">// after

function printOwing(invoice) {
  let outstanding = 0;

  //..생략

  // 세부사항 출력
  printDetails(invoice, outstanding);
}

function printDetails(invoice, outstanding) {
  console.log(`고객명: ${invoice.customer}`);
  console.log(`채무액: ${outstanding}`);
  console.log(`마감일: ${invoice.dueDate.toLocaleDateString()}`);
}</code></pre>
<ul>
<li>만약 지역변수가 배열, 객체와 같은 데이터 구조라면 매개변수로 넘긴 후 필드 값을 수정할 수 있다.<pre><code class="language-js">// before
</code></pre>
</li>
</ul>
<p>function printOwing(invoice) {
  //...생략</p>
<p>  // 마감일 기록
  const today = Clock.today; // <a href="https://martinfowler.com/bliki/ClockWrapper.html">https://martinfowler.com/bliki/ClockWrapper.html</a>
  invoice.dueDate = new Date(
    today.getFullYear(),
    today.getMonth(),
    today.getDate() + 30
  );</p>
<p>  //...생략
}</p>
<pre><code>
```js
// after

function printOwing(invoice) {
  //...생략

  // 마감일 기록
  recordDueDate(invoice);

  //...생략
}

function recordDueDate(invoice) {
  const today = Clock.today;
  invoice.dueDate = new Date(
    today.getFullYear(),
    today.getMonth(),
    today.getDate() + 30
  );
}</code></pre><h2 id="예시-지역-변수의-값을-변경할-때">예시: 지역 변수의 값을 변경할 때</h2>
<ul>
<li>만약 매개변수에 값을 대입하는 코드를 발견하면 그 변수를 쪼개서 임시 변수를 새로 하나 만들어 그 변수에 대입하게 한다.<em><del>(왜 이렇게 해야 하지?)</del></em></li>
<li>대입 대상이 되는 임시 변수는 크게 두 가지로 나눌 수 있다.<ol>
<li>변수가 추출된 코드 안에서만 사용될 때<ul>
<li>문장 슬라이드를 통해 변수 조작을 모두 한 곳에 처리하도록 모은다.</li>
</ul>
</li>
<li>변수가 추출한 함수 밖에서 사용될 때
1) 변수 선언문을 변수가 사용되는 코드 근처로 슬라이드한다.
2) 추출할 부분을 새로운 함수로 복사한다.
3) 이 때 선언문을 추출할 코드 앞으로 옮겼기 때문에 매개변수로 전달하지 않아도 된다.
4) 추출한 코드에서 변경된 변수의 값을 반환한다.
5) 추출한 코드의 원래 자리에 새로 만든 함수를 호출하는 문장으로 교체한다. 이때 추출한 함수에서 새 값을 반환하므로 이 값을 원래 변수에 저장한다.
6) 추출한 코드 내부의 반환 값의 이름을 내 코딩 스타일에 맞게 바꾼다.</li>
</ol>
</li>
</ul>
<pre><code class="language-js">// before

function printOwing(invoice) {
  let outstanding = 0;

  //...생략

  // 미해결 채무(outstanding) 계산
  for (const o of invoice.orders) {
    outstanding += o.amount;
  }

  //...생략
}
</code></pre>
<pre><code class="language-js">// after

function printOwing(invoice) {
  //...생략

  // 미해결 채무(outstanding) 계산
  const outstanding = calculateOutstanding(invoice);

  //...생략
}

function calculateOutstanding(invoice) {
  let result = 0;

  for (const o of invoice.orders) {
    outstanding += o.amount;
  }
  return result;
}</code></pre>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스크랩] Korean FE Article_리액트 19 베타]]></title>
            <link>https://velog.io/@cielo_hello/%EC%8A%A4%ED%81%AC%EB%9E%A9-Korean-FE-Article%EB%A6%AC%EC%95%A1%ED%8A%B8-19-%EB%B2%A0%ED%83%80</link>
            <guid>https://velog.io/@cielo_hello/%EC%8A%A4%ED%81%AC%EB%9E%A9-Korean-FE-Article%EB%A6%AC%EC%95%A1%ED%8A%B8-19-%EB%B2%A0%ED%83%80</guid>
            <pubDate>Wed, 22 May 2024 09:00:35 GMT</pubDate>
            <description><![CDATA[<h1 id="뉴스레터-요약">뉴스레터 요약</h1>
<p>리액트 공식 사이트에 리액트 19 베타 버전에 대한 설명이 올라왔다.
이 글에는 리액트 19에서 새롭게 추가된 기능, 개선된 사항 등을 소개하고 있다.
리액트 19에서 새롭게 추가된 기능과 훅, API 로는</p>
<ul>
<li>기능: Actions, <code>&lt;form&gt;</code>Actions</li>
<li>훅: useTransition, useActionState, useFormStatus, useOptimistic</li>
<li>API: use</li>
</ul>
<p>가 있고, 개선된 사항으로는 </p>
<ul>
<li>함수 컴포넌트에서 <code>ref</code>를 프로퍼티로 접근 가능하게 함</li>
<li>하이드레이션 에러 발생 시 구체적인 정보를 제공함</li>
<li><code>Context.provider</code> 대신 <code>Context</code>를 프로바이더로 이용함</li>
<li><code>ref</code> 콜백에서 클린업 함수를 반환하는 것을 지원함</li>
<li><code>useDeferredValue</code>에 <code>initialValue</code> 옵션을 추가함</li>
<li>컴포넌트 자체에 문서 메타데이터 태그를 렌더링하는 기능이 추가됨</li>
<li>스타일시트의 precedence를 지정하면 DOM에서 스타일시트의 삽입 순서를 관리하고, 해당 스타일 규칙에 의존하는 콘텐츠를 보여주기 전에 스타일시트가 로드되도록(외부 스타일시트인 경우) 보장함</li>
<li>비동기 스크립트 지원 -&gt; 제대로 이해하지 못함</li>
<li>리소스 프리로딩 지원</li>
<li>서드파티 스크립트와 확장 프로그램 호환성 -&gt; 제대로 이해하지 못함</li>
<li>중복 에러 제거 등 에러 보고의 개선</li>
<li>사용자 정의 요소(Custom Elements) 지원 -&gt; 제대로 이해하지 못함</li>
</ul>
<h1 id="액션actions">액션(Actions)</h1>
<ul>
<li>액션 부분을 읽던 도중 아래 문장이 이해가 가지 않아서 직접 코드를 써서 테스트 해 보기로 했다.<blockquote>
<p>비동기 트랜지션은 isPending 값을 즉시 true로 설정하며 비동기 요청을 보내고, 트랜지션이 수행되면 isPending을 false로 전환합니다. 이러한 방식은 데이터가 변경되는 동안에도 현재 UI의 반응성 및 상호작용을 유지할 수 있습니다.</p>
</blockquote>
</li>
</ul>
<h2 id="리액트-19-베타-설치하기">리액트 19 베타 설치하기</h2>
<ol>
<li>vite 설치
<code>npm create vite@latest</code></li>
<li>프로젝트 폴더로 이동 후 의존성 설치
<code>cd vite-project</code>
<code>npm install</code></li>
<li>리액트 19로 업데이트
<a href="https://react.dev/blog/2024/04/25/react-19-upgrade-guide#installing">React 19 Beta Upgrade Guide</a>에 나와있는 대로 아래 명령어 입력
<code>npm install react@beta react-dom@beta</code>
<img src="https://velog.velcdn.com/images/cielo_hello/post/a6076415-627c-4b6a-ab11-8c7069076467/image.png" alt=""><center><em>pakage.json</em></center></li>
</ol>
<h2 id="테스트-코드-작성하기">테스트 코드 작성하기</h2>
<pre><code class="language-jsx">import { useState, useTransition } from &#39;react&#39;;

async function updateNumber(count, setCount) {
  await new Promise((resolve) =&gt;
    setTimeout(() =&gt; {
      setCount(count + 1);
      resolve();
    }, 3000)
  );
  return null;
}

function App() {
  // React 18 버전
  const [count, setCount] = useState(0);
  const [isPending, setIsPending] = useState(false);

  const handleClickReact18 = async () =&gt; {
    setIsPending(true);
    await updateNumber(count, setCount);
    setIsPending(false);
  };

  // React 19 버전
  const [count19, setCount19] = useState(0);
  const [isPending19, startTransition] = useTransition();

  const handleClickReact19 = async () =&gt; {
    startTransition(async () =&gt; {
      await updateNumber(count19, setCount19);
    });
  };

  return (
    &lt;div&gt;
      {/* React 18 버전 */}
      &lt;h1&gt;리액트 18 예시&lt;/h1&gt;
      &lt;p&gt;Count: {isPending ? &#39;Loading...&#39; : count}&lt;/p&gt;
      &lt;button onClick={handleClickReact18}&gt;Increment&lt;/button&gt;

      {/* React 19 버전 */}
      &lt;h1&gt;리액트 19 예시&lt;/h1&gt;
      &lt;p&gt;Count: {isPending19 ? &#39;Loading...&#39; : count19}&lt;/p&gt;
      &lt;button onClick={handleClickReact19}&gt;Increment&lt;/button&gt;
    &lt;/div&gt;
  );
}  </code></pre>
<ul>
<li>위 테스트 코드를 통해 &quot;이러한 방식은 데이터가 변경되는 동안에도 현재 UI의 반응성 및 상호작용을 유지할 수 있습니다.&quot; 부분을 이해 할 수 있었다.</li>
<li>18버전에서는 useState를 이용해서 isPending 상태일 때 &quot;Loading...&quot;을 나타낼 수 있었지만 19버전에서는 useTransition을 통해 보다 간편하게 isPending 상태를 관리할 수 있다는 의미의 문장이었다.</li>
</ul>
<h2 id="액션이란">액션이란</h2>
<p>액션은 비동기 트랜지션(async transitions)을 사용하는 함수를 말한다.</p>
<ul>
<li>사실 비동기 트랜지션이 뭔지 잘 모르겠다.</li>
<li>어쨌든 이전 버전에서는 useState 등을 이용해서 수동으로 관리하던 데이터들을 자동으로 관리할 수 있게 해준다.</li>
<li>위 예시에서 나왔던 Pending state외에도 Optimistic updates, Error, Form과 관련된 데이터들을 자동으로 관리할 수 있다.</li>
</ul>
<h1 id="회고">회고</h1>
<ul>
<li>액션이 등장함으로써 비동기 함수와 관련된 상태관리가 쉬워진것 같다. 나는 Tanstack Query를 이용해서 로딩 및 에러 상태를 처리하고, optimistic update를 구현했는데 리액트 19가 상용화 되면 어떤 것을 쓰게 될지 궁금하다. 또, form을 관리할 때는 React Hook Form을 사용했는데 <code>&lt;form&gt;</code>Actions로 이런 라이브러리를 대체할 수 있을 지 궁금하다.</li>
<li>메타데이터 태그를 렌더링 하는 기능이 기대된다. react-helmet을 사용하면 콘솔에 오류가 떠서 불편했는데 이 라이브러리를 대체할 수 있을 것 같다.</li>
<li>액션을 이해하기 위해서 코드를 짜던 도중 useState와 비동기에 대한 이해가 부족하다고 느꼈다. 이 부분을 보충해야 겠다.</li>
<li>리액트를 사용할 때 마다 중복 에러가 떠서 불편하다고 생각했는데 이런 점이 개선되니 신기했다.</li>
<li>사실 액션의 핵심 부분만 이해하고 나머지 부분은 시간 관계상 꼼꼼하게 읽기 어려웠다. 흥미로운 지식을 얻기 위해서 더 많은 공부가 필요함을 느꼈다.</li>
</ul>
<h1 id="참고-자료">참고 자료</h1>
<ul>
<li><a href="https://velog.io/@typo/react-19-beta?utm_source=substack&amp;utm_medium=email">React 19 Beta 번역</a></li>
<li><a href="https://react.dev/blog/2024/04/25/react-19#how-to-upgrade">React 19 Beta 원문</a></li>
<li><a href="https://youtu.be/_Ikl0qhI65Y?si=4CxhYKgiXRNpkojv">React 19 Beta 유튜브 자료</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스터디] 리팩터링_chapter 4_테스트 구축하기]]></title>
            <link>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-4%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-4%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 16 May 2024 04:04:19 GMT</pubDate>
            <description><![CDATA[<p>4장에서는 테스트를 작성하는 방법 보다는 테스트를 작성했을 때 효율이 좋아지는 이유에 대해 이야기 하고 있다.</p>
<h1 id="자가-테스트-코드의-가치">자가 테스트 코드의 가치</h1>
<blockquote>
<p>모든 테스트를 완전히 자동화하고 그 결과까지 스스로 검사하게 만들자.</p>
</blockquote>
<ul>
<li>자가 테스트: 프로그램이 제대로 된 값을 출력했는지 사람이 직접 확인하는 것이 아닌 컴퓨터가 확인하는 것</li>
<li>버그를 찾는 강력한 도구<ol>
<li>자가 테스트 코드</li>
<li>테스트를 자주 수행하는 습관(예를 들면 컴파일할 때 테스트도 함께하기)</li>
</ol>
</li>
<li>테스트 주도 개발(Test-Driven Development, TDD)<ul>
<li>테스트-코딩-리팩터링 과정으로</li>
<li>테스트 작성, 테스트를 통과하게끔 코드를 작성, 결과 코드를 최대한 깔끔하게 리팩터링하는 과정을 짧은 주기로 반복함</li>
</ul>
</li>
</ul>
<h1 id="테스트할-샘플-코드">테스트할 샘플 코드</h1>
<ul>
<li><p>책에서 제시된 코드 중 저자는 set production()이 계산 결과를 지역 데이터(_province)에 갱신하는 코드가 지저분 해서 리팩터링을 하려고 한다.</p>
</li>
<li><p>이때 리팩터링하기 전에 먼저 테스트를 작성해야 한다.</p>
<pre><code class="language-js">class Producer {
constructor(aProvince, data) {
  this._province = aProvince;
  this._name = data.name;
  this._cost = data.cost;
  this._production = data.production || 0;
}
get name() {
  return this._name;
}
get cost() {
  return this._cost;
}
set cost(arg) {
  this._cost = parseInt(arg);
}
get production() {
  return this._production;
}
set production(amountStr) { // 이부분 이다.
  const amount = parseInt(amountStr);
  const newProduction = Number.isNaN(amount) ? 0 : amount;

  this._province.totalProduction += newProduction - this.production;
  this._production = newProduction;
}
}</code></pre>
</li>
</ul>
<h1 id="첫-번째-테스트">첫 번째 테스트</h1>
<ul>
<li>모카(Mocha): Node.js에서 사용하는 대표적인 테스트 프레임워크</li>
<li>모카를 이용해서 테스트 예시를 보여주고 있다.</li>
<li>픽스처(fixture)<ul>
<li>픽스처는 테스트를 실행하기 위해 필요한 데이터와 객체의 고정된 상태를 의미함</li>
<li>테스트를 수행하기 위해서는 테스트 대상 코드에 대한 입력 데이터와 초기 상태가 필요한데 이러한 입력 데이터와 초기 상태를 픽스처라고 함</li>
<li>픽스처는 테스트 코드가 실행될 때마다 동일한 초기 상태에서 시작할 수 있도록 함</li>
<li>이를 통해 테스트 결과의 일관성과 재현성을 보장할 수 있음</li>
</ul>
</li>
<li>먼저, 생산 부족분을 제대로 계산하는지 확인 하는 테스트는 다음과 같다.<pre><code class="language-js">import { Province, sampleProvinceData } from &#39;./index.js&#39;;
import assert from &#39;assert&#39;;
</code></pre>
</li>
</ul>
<p>// Mocha에서는 &#39;describe&#39; 함수를 사용하여 테스트 그룹을 정의함
// 첫 번째 인자: 테스트 그룹의 이름, 두 번째 인자: 테스트 그룹을 정의하는 콜백 함수
describe(&#39;province&#39;, function () {
  // Mocha에서는 &#39;it&#39; 함수를 사용하여 개별 테스트 케이스를 정의함
  // 첫 번째 인자는: 테스트 케이스의 이름, 두 번째 인자: 테스트 케이스를 정의하는 콜백 함수
  it(&#39;shortfall&#39;, function () {
    // 1. 픽스처 설정
    // 여기서는 &#39;Province&#39; 클래스의 인스턴스 &#39;asia&#39;를 생성함
    const asia = new Province(sampleProvinceData());</p>
<pre><code>// 2. 검증
// Mocha에서는 &#39;assert&#39; 모듈을 사용하여 검증 로직을 작성함
// 여기서는 &#39;assert.equal&#39; 함수를 사용하여 &#39;asia.shortfall&#39; 값이 5인지 검증함
assert.equal(asia.shortfall, 5);</code></pre><p>  });
});</p>
<pre><code>- 실행하면 다음과 같이 아주 간결한 피드백을 보여준다.
![](https://velog.velcdn.com/images/cielo_hello/post/f6c55014-e75d-4fc5-8425-99a6b304be90/image.png)
- 일부러 코드에 오류를 주입해서 테스트가 실패하는 모습도 직접 확인해 보아야 한다.
```js
  get shortfall() {
    return this.demand - this.totalProduction * 2; // 오류 주입
  }</code></pre><p><img src="https://velog.velcdn.com/images/cielo_hello/post/e3dd26c0-65b8-4146-8f7a-c42b37f0a34a/image.png" alt=""></p>
<blockquote>
<p>자주 테스트하라. 작성 중인 코드는 최소한 몇 분 간격으로 테스트하고, 적어도 하루에 한 번은 전체 테스트를 돌려보자.</p>
</blockquote>
<h1 id="테스트-추가하기">테스트 추가하기</h1>
<blockquote>
<p>테스트는 위험 요인을 중심으로 작성해야 한다!
완벽하게 만드느라 테스트를 수행하지 못하느니, 불완전한 테스트라도 작성해 실행하는 게 낫다.</p>
</blockquote>
<h2 id="기대값을-구하는-방법">기대값을 구하는 방법</h2>
<ul>
<li>초기 픽스터로부터 총수익이 제대로 계산되는지 검사하는 코드를 작성하면 다음과 같다.<pre><code class="language-js">import { Province, sampleProvinceData } from &#39;./index.js&#39;;
import assert from &#39;assert&#39;;
</code></pre>
</li>
</ul>
<p>describe(&#39;province&#39;, function () {
  it(&#39;shortfall&#39;, function () {
    const asia = new Province(sampleProvinceData());
    assert.equal(asia.shortfall, 5);
  });
  it(&#39;profit&#39;, function() {
    const asia = new Province(sampleProvinceData());
    expect(asia.profit).equal(230)
});</p>
<p>```</p>
<ul>
<li>이 때 기대값 230을 구하는 방식은 다음과 같다.<ul>
<li>임의의 값인 임시 값을 설정 -&gt; 코드를 실행시켜 나온 실제 값(230)으로 대체 -&gt; 오류를 심기 -&gt; 실제 값으로 되돌리기</li>
</ul>
</li>
</ul>
<h2 id="beforeeach-구문을-이용해서-픽스처-만들기">beforeEach 구문을 이용해서 픽스처 만들기</h2>
<ul>
<li>똑같은 픽스처를 설정하면 중복 </li>
<li>beforeEach구문을 이용하면 &#39;테스트끼리 상호작용하게 하는 공유 픽스처&#39;를 생성하지 않을 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스터디] 리팩터링_chapter 2_리팩터링 원칙]]></title>
            <link>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-2%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-2%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Thu, 09 May 2024 03:06:37 GMT</pubDate>
            <description><![CDATA[<p>2장에서는 리팩터링 전반에 적용된는 원칙을 이야기 하고 있다. 리팩터링의 정의부터 리팩터링을 어떤 자세로 해야하는지, 리팩터링을 하는 이유는 무엇인지, 리팩터링 시 고려해야 할 문제는 무엇인지 등을 이야기 하고 있다.</p>
<h1 id="리팩터링의-정의">리팩터링의 정의</h1>
<blockquote>
<p>리팩터링: 소프트웨어의 겉보기 동작은 그대로 유지한 채, 코드를 이해하고 수정하기 쉽도록 내부 구조를 변경하는 기법</p>
</blockquote>
<ul>
<li>이 때 겉보기 동작을 그대로 유지한다는 말의 뜻은 사용자 관전에서 달라지는 점이 없어야 한다는 것이다.</li>
<li>리팩터링의 목적은 코드를 이해하고 수정하기 쉽게 만드는 것이라서 프로그램 성능은 좋아질 수도, 나빠질 수도 있다. 즉, 성능 최적화와는 다른 것이다.</li>
</ul>
<h1 id="두-개의-모자">두 개의 모자</h1>
<blockquote>
<p>기능을 추가할 때는 &#39;기능 추가&#39;모자를 쓴 다음 기존 코드는 절대 건드리지 않고 새 기능을 추가하기만 한다.</p>
</blockquote>
<blockquote>
<p>리팩터링할 떄는 &#39;리팩터링&#39; 모자를 쓴 다음 기능 추가는 절대 하지 않기로 다짐한 뒤 오로지 코드 재구성에만 전념한다. 테스트도 새로 만들지 않는다.</p>
</blockquote>
<blockquote>
<p>내가 쓰고 있는 모자가 무엇인지와 그에 따른 미묘한 작업 방식의 차이를 분명하게 인식해야 한다.</p>
</blockquote>
<ul>
<li>나는 CO-KKIRI 프로젝트를 할 때 &#39;기능 추가&#39;와 &#39;리팩터링&#39;을 구분하지 않고, 내가 어떤 모자를 썼는지도 인식하지 않았는데 그 점을 반성하게 됐다. </li>
</ul>
<h1 id="리팩터링하는-이유">리팩터링하는 이유</h1>
<h3 id="소프트웨어-설계가-좋아진다">소프트웨어 설계가 좋아진다.</h3>
<ul>
<li>규칙적인 리팩터링은 코드의 구조를 지탱해줄 것이다.<h3 id="소프트웨어를-이해하기-쉬워진다">소프트웨어를 이해하기 쉬워진다.</h3>
</li>
<li>코드의 목적이 더 잘 드러나게 개선할 수 있다.<h3 id="버그를-쉽게-찾을-수-있다">버그를 쉽게 찾을 수 있다.</h3>
</li>
<li>프로그램의 구조를 명확하게 다듬으면 두루뭉술 했던 가정들이 분명히 드러난다.<h3 id="프로그래밍-속도를-높일-수-있다">프로그래밍 속도를 높일 수 있다.</h3>
</li>
<li>모듈화가 잘 되어 있으면 전체 코드베이스 중 작은 일부만 이해하면 된다.</li>
</ul>
<blockquote>
<p>처음부터 좋은 설계를 마련하기란 매우 어렵다. 그래서 빠른 개발이라는 숭고한 목표를 달성하려면 리팩터링이 반드시 필요하다.</p>
</blockquote>
<h1 id="언제-리팩터링해야-할까">언제 리팩터링해야 할까?</h1>
<h3 id="준비를-위한-리팩터링-기능을-쉽게-추가하게-만들기">준비를 위한 리팩터링: 기능을 쉽게 추가하게 만들기</h3>
<ul>
<li>리팩터링하기 가장 좋은 시점은 코드베이스에 기능을 새로 추가하기 직전이다.<h3 id="이해를-위한-리팩터링-코드를-이해하기-쉽게-만들기">이해를 위한 리팩터링: 코드를 이해하기 쉽게 만들기</h3>
</li>
<li>내가 이해한 것을 코드에 반영해두면 더 오래 보존할 수 있을 뿐만 아니라 동료들도 알 수 있다.</li>
<li>즉, 코드를 분석할 때 리팩터링을 해보라는 뜻이다.<h3 id="쓰레기-줍기-리팩터링">쓰레기 줍기 리팩터링</h3>
</li>
<li>간단히 수정할 수 있는 것은 즉시 고치고, 시간이 좀 걸리는 일은 짧은 메모만 남긴 다음, 하던 일을 끝내고 나서 처리한다.</li>
<li>이해를 위한 리팩터링의 변형이다.</li>
<li>코드를 고치고 싶은데 일이 너무 커질까봐 손대지 못한 경험이 있다. 그래서 이 방법을 적극 이용하려고 한다.<h3 id="계획된-리팩터링과-수시로-하는-리팩터링">계획된 리팩터링과 수시로 하는 리팩터링</h3>
</li>
<li>리팩터링 작업 대부분은 드러나지 않게, 기회가 될 때마다 해야 한다.</li>
<li>리팩터링 커밋을 분리한다고 해서 무조건 좋은 것은 아님을 명심하고, 팀에 적합한 방식을 실험을 통해 찾아내자<h3 id="오래-걸리는-리팩터링">오래 걸리는 리팩터링</h3>
</li>
<li>라이브러리를 새것으로 교체하는 등의 대규모 리팩터링의 경우 팀 전체가 리팩터링에 매달리기 보다는 누구든지 리팩털이해야할 코드와 관련한 작업을 할 때마다 조금씩 개선하는 것을 추천한다.</li>
<li>라이브러리를 교체할 때는 기존 것과 새 것 모두를 포용하는 추상 인터페이스부터 마련해야 한다.<h3 id="코드-리뷰에-리팩털이-활용하기">코드 리뷰에 리팩털이 활용하기</h3>
</li>
<li>리팩터링은 코드 리뷰의 결과를 더 구체적으로 도출하는 데에 도움이 된다.<h3 id="리팩터링하지-말아야-할-때">리팩터링하지 말아야 할 때</h3>
</li>
<li>외부 API 다루듯 호출해서 쓰는 코드 처럼 굳이 수정할 필요가 없다면 리팩터링 하지 않고 내부 동작을 이해해야 할 시적에 리팩터링을 한다. 그래야 효과를 제대로 볼 수 있다.</li>
<li>리팩터링하는 것보다 처음부터 새로 작성한는 게 쉬울 때도 리팩터링하지 않는다.</li>
</ul>
<h1 id="리팩터링-시-고려할-문제">리팩터링 시 고려할 문제</h1>
<h3 id="브랜치">브랜치</h3>
<ul>
<li>CI(Continuous Integration): 지속적 통합. CI에 따르면 모든 팀원이 하루에 최소 한 번은 마스터와 통합한다.</li>
<li>익스트림 프로그래밍(XP):  익스트림 프로그래밍은 애자일 방법론 중 하나로, 빠르게 변화하는 비즈니스 요구사항에 적합한 소프트웨어 개발 방법론으로 책에서는 CI와 리팩터링을 합쳐서 만든 것이라 한다.</li>
<li>책에서는 기능 브랜치 방식의 단점을 말하고 있고 기능브랜치 방식을 사용하더라도 브랜치 통합 주기는 짧아야 하며 CI를 적용하는 걸 추천하고 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[스터디] 리팩터링_chapter 3_언제 리팩터링을 해야 하는가]]></title>
            <link>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-3%EC%96%B8%EC%A0%9C-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81%EC%9D%84-%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80</link>
            <guid>https://velog.io/@cielo_hello/%EC%8A%A4%ED%84%B0%EB%94%94-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81chapter-3%EC%96%B8%EC%A0%9C-%EB%A6%AC%ED%8C%A9%ED%84%B0%EB%A7%81%EC%9D%84-%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94%EA%B0%80</guid>
            <pubDate>Thu, 09 May 2024 02:12:21 GMT</pubDate>
            <description><![CDATA[<p>3장에서는 리팩터링을 언제 적용해야 하는지를 판단하는 기준을 제시한다. 책에서는 리팩터링할 &#39;시점&#39;을 설명할 때 &#39;냄새&#39;라는 표현을 사용했다. 쓰레기를 제때 치우지 않으면 악취가 나고 문제를 일으키기 마련인데 정리되지 않은 코드도 문제를 일으킬 수 있고 찜찜함을 남긴다는 점에서 매우 재밌고 정확한 표현이라고 생각했다.</p>
<p>3장에서는 코드가 풍기는 냄새의 종류를 소개하고 있는데, 이 장에서 배운 내용을 기반으로 내 코드에서 나타난 문제점에도 이름을 붙일 수 있을 것 같다. 문제에 이름을 붙이면 해결이 더 쉬워지므로 꼼꼼히 이해하고 내가 그동안 짠 코드들에 대입해보려고 노력했다. 물론 이해가 안되는 부분도 있었는데, 앞으로 책을 더 읽으면서 이해되리라고 생각하고 일단은 넘어갔다. 이해한 개념들을 중심으로 책 내용을 정리해 보았다.</p>
<h1 id="기이한-이름">기이한 이름</h1>
<ul>
<li>아래 인용 부분이 특히 와닿았다. 코드를 짜다가 함수나 변수명이 바로 떠오르지 않는 경우는 내가 그 코드 구조를 모호하게 파악하고 있거나 코드 구조가 잘못된 경우가 많았다.<blockquote>
<p>이름 바꾸기는 단순히 이름을 다르게 표현하는 연습이 아니다. 마땅한 이름이 떠오르지 않는다면 설계에 더 근본적인 문제가 숨어 있을 가능성이 높다.</p>
</blockquote>
</li>
</ul>
<h1 id="중복-코드">중복 코드</h1>
<ul>
<li><p>아래 인용 문장을 이해하기 어려워서 코드로 나타내 보았다.</p>
<blockquote>
<p>한 클래스에 딸린 두 메서드가 똑같은 표현식을 사용하는 경우가 있다.</p>
</blockquote>
<ul>
<li>calculateArea() 메서드와 calculateVolume() 메서드 모두 length * width 라는 동일한 표현식을 사용하고 있는데. 이것이 중복 코드의 가장 기본적인 형태이다.<pre><code class="language-js">const myObject = {
calculateArea(length, width) {
return length * width;
},
calculateVolume(length, width, height) {
return length * width * height;
}
};</code></pre>
</li>
<li>이런 경우 함수 추출 기법을 사용하여 중복 코드를 제거할 수 있다.<pre><code class="language-js">const myObject = {
calculateRectangleArea(length, width) {
return length * width;
},
calculateArea(length, width) {
return this.calculateRectangleArea(length, width);
},
calculateVolume(length, width, height) {
return this.calculateRectangleArea(length, width) * height;
}
};</code></pre>
</li>
</ul>
</li>
</ul>
<h1 id="긴-함수">긴 함수</h1>
<ul>
<li><p>긴 코드를 짧게 구성할 때 코드를 읽는 사람 입장에서 함수가 하는 일을 파악하기 위해 왔다 갔다 해야 하므로 여전히 부담이 되지 않을까 하는 우려를 한적이 있다. 책에서는 이에 대해 이렇게 이야기 한다.</p>
<blockquote>
<p>짧은 함수로 구성된 코드를 이해하기 쉽게 만드는 가장 확실한 방법은 좋은 이름이다. 함수 이름을 잘 지어두면 본문 코드를 볼 이유가 사라진다.</p>
</blockquote>
</li>
<li><p>긴 함수를 쪼갤 때 기준을 어떻게 삼아야 할지 고민한 적이 있다. 책에서는 이렇게 정의 해 준다.</p>
<blockquote>
<ul>
<li>여기서 핵심은 함수의 길이가 아닌, 함수의 목적 (의도)과 구현 코드의 괴리가 얼마나 큰가다. 즉, &#39;무엇을 하는지&#39;를 코드가 잘 설명해주지 못할수록 함수로 만드는 게 유리하다.</li>
<li>코드가 단 한 줄이어도 따로 설명할 필요가 있다면 함수로 추출하는 게 좋다.</li>
</ul>
</blockquote>
</li>
</ul>
<h1 id="긴-매개변수-목록">긴 매개변수 목록</h1>
<ul>
<li>코드를 짜다보면 (특히 api 관련 함수에서) 매개변수 목록이 길어질 때가 있었고, 이럴때 마다 난감했다. 책에서는 다음과 같은 방법을 추천했다.<ul>
<li>매개변수를 질의 함수로 바꾸기, 객체 통째로 넘기기, 매개변수 객체 만들기, 플래그 인수 제거하기, 여러 함수를 클래스로 묶기</li>
<li>이 기법들에 대해서는 6장과 11장에서 더 자세히 배울 것이다. 기대된다.</li>
</ul>
</li>
</ul>
<h1 id="전역-데이터">전역 데이터</h1>
<ul>
<li>전역 데이터는 어디서든 변경할 수 있지만 그 원인이 되는 코드를 찾아내기 어렵게 하기 때문에 문제가 될 수 있는데 변수 캡슐화하기를 통해 리팩터링 할 수 있다.</li>
<li>사실 캡슐화에 대한 정확한 개념을 잘 모른다. 그래서 이 강의를 들어보려 한다.
<a href="https://www.codeit.kr/topics/object-oriented-javascript/lessons/4461">코드잇 캡슐화 강의</a></li>
</ul>
<h1 id="뒤엉킨-변경--산탄총-수술">뒤엉킨 변경 &amp; 산탄총 수술</h1>
<p><img src="https://velog.velcdn.com/images/cielo_hello/post/50725116-b1ab-4393-abe4-1a5d262a740c/image.png" alt=""></p>
<blockquote>
<p>코드를 수정할 때는 시스템에서 고쳐야 할 딱 한 군데를 찾아서 그부분만 수정할 수 있기를 바란다. 이렇게 할 수 없다면 뒤엉킨 변경과 산탄총 수술 중 하나가 풍긴다.</p>
</blockquote>
<blockquote>
<p>뒤엉킨 변경은 단일 책임 원칙(Single Responsibility Principle)이 제대로 지켜지지 않을 떄 나타난다.</p>
</blockquote>
<ul>
<li>SPR: 위키피디아에 따르면 단일 책임 원칙이란 &quot;A module should be responsible to one, and only one, actor.&quot;라고 설명 돼 있는데 &quot;하나의 모듈은 반드시 하나의 동작만의 책임을 갖는다&quot;는 원칙이다.</li>
</ul>
<blockquote>
<p>산탄총 수술은 뒤엉킨 변경과 비슷하면서도 정반대다. 이 냄새는 코드를 변경할 때마다 자잘하게 수정해야 하는 클래스가 많을 때 풍긴다.</p>
</blockquote>
<ul>
<li>즉, 뒤엉킨 변경은 하나의 클래스나 모듈이 너무 많은 변화를 겪는 상황이며, 산탄총 수술은 하나의 변경이 여러 클래스를 동시에 수정해야 하는 상황을 이야기 한다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>