<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>aug8_.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 28 Apr 2025 06:20:39 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>aug8_.log</title>
            <url>https://velog.velcdn.com/images/aug8_/profile/d2d3a860-d6c9-4ca0-9d98-e6e791f9b2e6/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. aug8_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/aug8_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[React Lifecycle]]></title>
            <link>https://velog.io/@aug8_/React-Lifecycle</link>
            <guid>https://velog.io/@aug8_/React-Lifecycle</guid>
            <pubDate>Mon, 28 Apr 2025 06:20:39 GMT</pubDate>
            <description><![CDATA[<p>리액트는 UI를 컴포넌트라는 독립적이고 재사용 가능한 블록으로 구성한다. 각각의 컴포넌트는 사용자 인터페이스의 특정 부분을 정의하며, 자체적인 상태(state)와 속성(props)을 기반으로 렌더링한다.</p>
<p>컴포넌트는 &quot;처음 화면에 나타날 때&quot;, &quot;업데이트할 때&quot;, &quot;화면에서 사라질 때&quot; 다양한 동작이 필요하다. 이런 흐름을 리액트는 <code>라이프사이클(Lifecycle)</code>이라고 부른다.</p>
<p>라이프사이클을 이해하면 불필요한 렌더링을 막고 최적화된 사용자 경험을 제공할 수 있다.</p>
<h2 id="함수형-컴포넌트">함수형 컴포넌트</h2>
<p>과거 리액트는 클래스형 컴포넌트에서 라이프사이클 메서드를 사용했지만, 현재는 함수형 컴포넌트 + 훅(Hooks)를 사용해 라이프사이클을 관리한다.</p>
<h3 id="핵심-hooks">핵심 Hooks</h3>
<ul>
<li><code>useEffect</code></li>
<li><code>useLayoutEffect</code></li>
<li><code>useRef</code></li>
</ul>
<h2 id="주요-라이프사이클-단계">주요 라이프사이클 단계</h2>
<h3 id="1-mount">1. Mount</h3>
<p>컴포넌트가 처음 화면에 나타나는 시점이다. <code>useEffect</code>를 사용해 컴포넌트가 마운트될 때 실행할 코드를 작성할 수 있다. 의존성 배열을 빈 배열 <code>[]</code>로 설정하면 처음 한 번만 실행된다.</p>
<pre><code class="language-jsx">import { useEffect } from &quot;react&quot;;

function Example() {

  useEffect(() =&gt; {
    console.log(&quot;컴포넌트가 화면에 나타났습니다!&quot;);  
    // 빈 배열을 넣으면 Mount 시에만 실행
  }, []); 

}</code></pre>
<h3 id="2-update">2. Update</h3>
<p>컴포넌트가 이미 마운트된 후, state나 props 변경되어 리렌더링이 일어날 때 발생한다. <code>useEffect</code>에 특정 값을 의존성 배열에 넣으면, 그 값이 바뀔 때마다 해당 코드가 다시 실행된다.</p>
<pre><code class="language-jsx">import { useEffect, useState } from &quot;react&quot;;

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

  useEffect(() =&gt; {
    console.log(`count가 ${count}로 업데이트되었습니다.`);
    // count가 변할 때마다 실행
  }, [count]);
}</code></pre>
<h3 id="3-unmount">3. Unmount</h3>
<p>컴포넌트가 더 이상 필요없어 화면에서 사라질 때 발생한다. <code>useEffect</code> 안에서 <code>return</code> 함수를 작성하면, 언마운트 시 자동으로 호출된다. 이때 필요한 정리 작업을 해주면 되는데, 정리를 하지 않으면 메모리 누수가 발생하거나 사라진 컴포넌트가 계속 작업을 시도하는 문제가 발생할 수 있다.</p>
<pre><code class="language-jsx">import { useEffect } from &quot;react&quot;;

function Example() {
  useEffect(() =&gt; {
    const timer = setInterval(() =&gt; {
      console.log(&quot;타이머 작동 중...&quot;);
    }, 1000);

    return () =&gt; {
      clearInterval(timer);
      console.log(&quot;컴포넌트가 사라졌습니다. 타이머 정리 완료!&quot;);
    };
  }, []);
}</code></pre>
<h2 id="핵심-hooks-상세">핵심 Hooks 상세</h2>
<h3 id="useeffect">useEffect</h3>
<p>함수형 컴포넌트의 표준 라이프사이클 훅으로, 비동기 로직을 처리할 때 자주 사용한다.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  // 실행할 코드

  return () =&gt; {
    // 정리(cleanup)할 코드
  };
}, [의존성 배열]);</code></pre>
<h4 id="의존성-배열">의존성 배열</h4>
<ul>
<li><code>[]</code>: 마운트될 때만 실행</li>
<li><code>[value]</code>: value가 변할 때마다 실행</li>
<li><code>배열 없음</code>: 매 렌더링 때마다 실행 (비권장)</li>
</ul>
<h3 id="useref">useRef</h3>
<p>렌더링과 무관하게 값을 저장할 수 있는 훅이다. 상태(state)처럼 값을 유지하지만, 값이 변해도 리렌더링을 발생시키지 않는다. 이전 값을 기억할 때 사용한다.</p>
<pre><code class="language-jsx">import { useEffect, useRef, useState } from &quot;react&quot;;

function Example() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef(0);

  useEffect(() =&gt; {
    prevCountRef.current = count;
  }, [count]);

  return (
    &lt;div&gt;
      &lt;p&gt;현재 값: {count}&lt;/p&gt;
      &lt;p&gt;이전 값: {prevCountRef.current}&lt;/p&gt;
      &lt;button onClick={() =&gt; setCount((c) =&gt; c + 1)}&gt;증가&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="주의할-점">주의할 점</h2>
<h3 id="무한-렌더링-방지">무한 렌더링 방지</h3>
<p><code>useEffect</code> 내부에서 상태를 변경하면 컴포넌트가 다시 렌더링된다. 만약 적절한 조건 없이 상태를 수정하면 <strong>무한 루프</strong>가 발생할 수 있으니 주의해야 한다.</p>
<h4 id="잘못된-예시">잘못된 예시</h4>
<pre><code class="language-jsx">useEffect(() =&gt; {
  setCount(count + 1); 
  // 매 렌더링마다 count 증가 → 무한 렌더링 발생
});</code></pre>
<h4 id="수정한-예시">수정한 예시</h4>
<pre><code class="language-jsx">useEffect(() =&gt; {
  if (count === 0) {
    setCount(1);
  }
}, [count]);</code></pre>
<h3 id="의존성-배열-관리">의존성 배열 관리</h3>
<p><code>useEffect</code>의 의존성 배열을 올바르게 관리해야 원하는 타이밍에 실행된다. 필요한 변수를 빠뜨리면 오래된 데이터를 사용할 위험이 있고, 불필요한 변수를 넣으면 불필요한 렌더링이 반복될 수 있다.</p>
<h3 id="메모리-누수-방지">메모리 누수 방지</h3>
<p>컴포넌트가 언마운트될 때 정리 작업을 하지 않으면 타이머, 이벤트 리스너, 네트워크 요청 등이 계속 남아 메모리 누수를 유발할 수 있다.</p>
<h4 id="정리-예시">정리 예시</h4>
<pre><code class="language-jsx">useEffect(() =&gt; {
  const timer = setInterval(() =&gt; {
    console.log(&quot;running...&quot;);
  }, 1000);

  return () =&gt; {
    clearInterval(timer); // 언마운트 시 타이머 해제
  };
}, []);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[렌더링 아키텍처]]></title>
            <link>https://velog.io/@aug8_/%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</link>
            <guid>https://velog.io/@aug8_/%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98</guid>
            <pubDate>Fri, 04 Apr 2025 08:22:49 GMT</pubDate>
            <description><![CDATA[<h2 id="mpa--spa">MPA &amp; SPA</h2>
<blockquote>
<p><em>페이지 구조는 어떠한가?</em></p>
</blockquote>
<p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4Wzci%2FbtsKITB7BZr%2F9TTMl1K38e556HxQvUtXYK%2Fimg.png" alt=""></p>
<h3 id="mpa-multi-page-application">MPA (Multi Page Application)</h3>
<p>전통적인 웹 사이트 구조로, 사용자가 새로운 페이지로 이동할 때마다 서버에 요청을 보내 새로운 HTML 문서를 받아오는 방식이다.</p>
<ul>
<li>각 페이지가 독립된 HTML 파일로 존재</li>
<li>페이지 이동 시 전체 새로고침(Full Page Reload) 발생 → 페이지 전환 속도 느림</li>
<li>서버 중심 렌더링 (SSR) → 서버 부하 증가</li>
<li>SEO 친화적</li>
</ul>
<h3 id="spa-single-page-application">SPA (Single Page Application)</h3>
<p>웹 애플리케이션 전체가 하나의 HTML에서 작동하며, 페이지 이동은 클라이언트에서 처리된다. 실제로 페이지를 이동하는 것이 아니라, 콘텐츠만 동적으로 교체되는 방식이다.</p>
<ul>
<li>초기에는 하나의 HTML만 로드</li>
<li>이후 필요한 데이터만 API를 받아서 렌더링 → 빠른 페이지 전환, 부드러운 사용자 경험</li>
<li>React, Vue, Angular 등 프론트엔드 프레임워크 기반</li>
<li>클라이언트 중심 렌더링 (CSR)</li>
<li>SEO에 불리</li>
</ul>
<br>

<hr>
<h2 id="csr--ssr">CSR &amp; SSR</h2>
<blockquote>
<p><em>페이지를 어떻게 렌더링할 것인가?</em></p>
</blockquote>
<p><img src="https://blog.kakaocdn.net/dn/du5x5u/btsKJkTE6By/erdqlOPEvXS2ZrLGBk6Zl0/img.png" alt=""></p>
<h3 id="csr-client-side-rendering">CSR (Client Side Rendering)</h3>
<p>최소한의 HTML과 JS 번들만 전달받은 후, 브라우저가 JS를 실행해 화면을 렌더링하는 방식이다. 클라이언트 측에서 동적으로 콘텐츠를 렌더링한다.</p>
<ul>
<li>서버는 HTML을 렌더링하지 않고 정적 파일(JS, CSS)만 제공</li>
<li>JS가 완전히 로드되어야 콘텐츠 표시 가능 → 초기 로딩 느림</li>
<li>이후 페이지 전환은 빠르고 부드러움 (SPA) → 동적 기능 구현에 유리</li>
<li>SEO에 불리 (콘텐츠 인식 어려움)</li>
</ul>
<h3 id="ssr-server-side-rendering">SSR (Server Side Rendering)</h3>
<p>사용자의 요청이 있을 때마다 서버에서 HTML을 완전히 렌더링해 브라우저에 전달하는 방식이다.</p>
<ul>
<li>콘텐츠가 포함된 HTML이 즉시 전달 → 초기 렌더링 속도 빠름</li>
<li>이후 JS가 로드되며 사용자 인터랙션 활성화 (Hydration)</li>
<li>페이지 전환 시 서버 재요청 필요</li>
<li>복잡한 페이지일수록 서버 부하 증가</li>
<li>SEO 유리 (콘텐츠 인식 가능)</li>
</ul>
<br>

<hr>
<h2 id="ssg--isr--hydration">SSG &amp; ISR &amp; Hydration</h2>
<blockquote>
<p><em>정적 페이지를 어떻게 갱신할 것인가?</em></p>
</blockquote>
<p><img src="https://enjoydev.life/posts/1-ssr-ssg-isr/230821-132345.png" alt=""></p>
<h3 id="ssg-static-site-generation">SSG (Static Site Generation)</h3>
<p>배포 시점에 HTML 파일을 미리 생성해두고, 요청 시 서버 계산 없이 정적 HTML 파일을 그대로 전달하는 방식이다.</p>
<ul>
<li>빌드할 때 서버가 API로부터 데이터를 받아 HTML 생성</li>
<li>매우 빠른 응답 속도, 서버 부하 없음</li>
<li>콘텐츠 변경 시 매번 전체 빌드 필요 → 자주 바뀌는 데이터에는 부적합</li>
<li>SEO 유리</li>
</ul>
<h3 id="isr-incremental-static-regeneration">ISR (Incremental Static Regeneration)</h3>
<p>SSG의 단점을 보안하기 위해 고안된 방식으로, 일정 주기마다 백그라운드에서 정적 페이지를 재생성하는 방식이다.</p>
<ul>
<li>최초 요청에 정적 HTML를 제공한 이후, 지정된 시간이 지나면 백그라운드로 새 HTML을 생성 → 다음 요청에서는 새 HTML 제공</li>
<li>빌드 없이 콘텐츠 최신 상태 유지</li>
<li>서버 부하 최소화, 동적 콘텐츠 대응 가능</li>
</ul>
<h3 id="hydration">Hydration</h3>
<p>SSG와 ISR로 생성된 정적 HTML 문서를 SPA처럼 동작하게 만드는 과정이다. 초기 렌더링 속도가 빠르고, 이후에 JS로 동작을 완성한다.</p>
<ol>
<li>브라우저는 SSG/ISR로 생성된 HTML을 먼저 렌더링</li>
<li>JS 번들이 로드되면서 Hydration 수행</li>
<li>사용자 인터렉션(클릭, 입력 등)이 가능해짐</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[GIT 명령어]]></title>
            <link>https://velog.io/@aug8_/GIT-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@aug8_/GIT-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Sun, 17 Nov 2024 04:16:19 GMT</pubDate>
            <description><![CDATA[<h2 id="설정">설정</h2>
<h3 id="git-config">git config</h3>
<p>Git의 설정을 관리한다. 해당 저장소에만 적용되는 <code>local</code>과 해당 사용자에게 적용되는<code>global</code>이 있다.</p>
<pre><code class="language-bash">git config --list
git config --l

user.name=이름
user.email=이메일@주소
...</code></pre>
<p><code>git config --list</code>를 사용하여 현재 설정된 모든 정보를 확인할 수 있다.</p>
<pre><code class="language-bash">git config user.name

이름</code></pre>
<p><code>git config &lt;key&gt;</code>로 특정 key의 값을 확인할 수 있다.</p>
<pre><code class="language-bash">git config --global user.name &quot;이름&quot;
git config --global user.email &quot;이메일@주소&quot;</code></pre>
<p>만약 프로젝트마다 다른 이름과 이메일 주소를 사용하고 싶다면, <code>--local</code>을 사용하거나 <code>--global</code>을 제외하면 된다.</p>
<h3 id="git-init">git init</h3>
<pre><code class="language-bash">git init</code></pre>
<p>현재 디렉토리를 기준으로 새로운 Git 저장소 <code>.git</code> 디렉토리를 생성한다. 새로운 프로젝트를 시작할 때 사용한다.</p>
<hr>
<h2 id="커밋-및-저장소">커밋 및 저장소</h2>
<h3 id="git-status">git status</h3>
<p>현재 작업 중인 저장소의 상태를 보여준다.</p>
<pre><code class="language-bash">git status 

On branch main

# 스테이징 영역에 추가된 파일
Changes to be committed:
  (use &quot;git restore --staged &lt;file&gt;...&quot; to unstage)
    modified:   example.txt
    new file:   newfile.txt

# 수정되었으나 스테이징 영역에 추가되지 않은 파일
Changes not staged for commit:
  (use &quot;git add &lt;file&gt;...&quot; to update what will be committed)
    modified:   anotherfile.txt

# 추적되지 않는 새로운 파일
Untracked files:
  (use &quot;git add &lt;file&gt;...&quot; to include in what will be committed)
    untrackedfile.txt</code></pre>
<p>현재 체크아웃되어 있는 브랜치의 이름, 작업 디렉토리 상태, 변경 사항 요약이 표시된다.</p>
<blockquote>
<h4 id="스테이징-영역staging-area">스테이징 영역(Staging Area)</h4>
<p>커밋할 파일을 임시로 모아두는 공간이다. 작업 디렉토리에서 변경한 파일은 곧바로 커밋되지 않고, 먼저 스테이징 영역에 추가되어 <code>git add</code> 어떤 파일을 커밋할지 <code>git commit</code> 관리할 수 있다.</p>
</blockquote>
<h3 id="git-add">git add</h3>
<p>작업 디렉토리에서 수정된 파일을 스테이징 영역에 추가한다.</p>
<ul>
<li><code>git add &lt;파일 또는 폴더명&gt;</code> : 특정 파일을 스테이징 영역에 추가한다.</li>
<li><code>git add -A</code>, <code>git add -u</code>, <code>git add .</code>은 모두 파일을 스테이징하는 방법이지만, 다음과 같은 차이가 있다.</li>
</ul>
<table>
<thead>
<tr>
<th>명령어</th>
<th>수정된 파일</th>
<th>삭제된 파일</th>
<th>새로 생성된 파일</th>
<th>적용 범위</th>
</tr>
</thead>
<tbody><tr>
<td><code>git add -A</code></td>
<td>O</td>
<td>O</td>
<td>O</td>
<td>프로젝트 전체</td>
</tr>
<tr>
<td><code>git add -u</code></td>
<td>O</td>
<td>O</td>
<td>X</td>
<td>현재 + 하위 디렉토리</td>
</tr>
<tr>
<td><code>git add .</code></td>
<td>O</td>
<td>X</td>
<td>O</td>
<td>현재 + 하위 디렉토리</td>
</tr>
</tbody></table>
<h3 id="git-commit">git commit</h3>
<p>스테이징 영역에 있는 파일을 커밋하여 저장소에 기록한다. 각 커밋은 고유한 ID(SHA 해시)로 식별된다.</p>
<ul>
<li><code>git commit -m &quot;메시지&quot;</code> : 커밋 메시지와 함께 스테이징 영역의 파일을 커밋한다.</li>
<li><code>git commit -a -m &quot;메시지&quot;</code> : add와 commit을 한번에 한다.</li>
<li><code>git amend</code> : 직전의 커밋을 편집할 수 있다. 메시지를 변경하거나 파일 추가가 가능하다. 단, push하기 전에만 가능하다.</li>
</ul>
<h3 id="git-push">git push</h3>
<p>커밋한 변경 사항을 저장소에 업로드한다.</p>
<pre><code class="language-bash">git push origin main</code></pre>
<p>현재 로컬 브랜치의 변경 사항을 <code>origin</code> 저장소의 <code>main</code> 브랜치에 푸시한다는 뜻이다.</p>
<ul>
<li>다른 사람이 먼저 push했을 경우, <code>git pull</code>로 상태를 반영한 후에 push할 수 있다.</li>
<li>push한 커밋은 되돌리기 어려우므로 신중하게 사용해야 한다.</li>
</ul>
<h3 id="git-pull">git pull</h3>
<p><code>git fetch</code>와 <code>git merge</code>를 함께 수행한다. 원격 저장소의 변경 사항을 가져와 로컬 작업 디렉토리에 병합한다.</p>
<pre><code class="language-bash">git pull origin main</code></pre>
<p>모든 변경 사항이 자동으로 병합되기 때문에, 큰 변경 사항이 있을 때는 <code>git fetch</code>로 미리 확인하는 게 좋다.</p>
<h3 id="git-fetch">git fetch</h3>
<p>원격 저장소의 변경 사항을 로컬 저장소에 업데이트하지만, 브랜치에 직접적으로 반영하지는 않는다. </p>
<p>다른 개발자들이 업데이트한 변경 사항을 확인하고 싶을 때 사용할 수 있다. 협업 시 충돌을 줄이거나 미리 충돌을 확인하는데 유용하다.</p>
<h3 id="git-merge">git merge</h3>
<p>두 브랜치의 변경 사항을 하나의 브랜치에 통합한다. 현재 체크아웃된 브랜치와 다른 브랜치를 병합하는데 사용한다. 변경 사항에 따라 자동 병합이 이루어질 수도 있고, 충돌이 발생하면 수동으로 해결해야 한다.</p>
<p><code>git fetch</code>와 함께 사용하여, 원격 브랜치에서 최신 변경 사항을 가져온 후, 로컬 브랜치와 병합한다.</p>
<h3 id="git-log">git log</h3>
<p>저장소의 커밋 히스토리를 시간순으로 보여준다. 가장 최근 커밋이 위에 나온다. 각 커밋은 커밋 해시, 작성자, 날짜, 커밋 메시지로 표시된다.</p>
<ul>
<li><code>git log</code> : 기본 형태</li>
<li><code>git log -n &lt;숫자&gt;</code> : 가장 최근 &lt;숫자&gt;개의 커밋만 표시한다.</li>
<li><code>git log --after=&quot;2024-01-01&quot; --before=&quot;2024-12-31&quot;</code> : 특정 기간의 커밋만 표시한다.</li>
<li><code>git log --oneline</code> : 각 커밋을 한 줄로 요약해서 표시한다.</li>
<li><code>git log --graph</code> : 브랜치와 병합 상황을 시각적인 그래프로 표시한다.</li>
<li><code>git log &lt;파일명&gt;</code> : 특정 파일의 히스토리만 확인한다.</li>
</ul>
<hr>
<h2 id="브랜치-관리">브랜치 관리</h2>
<h3 id="git-branch">git branch</h3>
<p>브랜치를 생성, 삭제, 목록을 표시하는데 사용한다.</p>
<ul>
<li><p><code>git branch</code> : 모든 브랜치 목록을 출력한다. 체크아웃된 브랜치는 <code>*</code>로 표시된다.</p>
</li>
<li><p><code>git branch &lt;이름&gt;</code> : 새 브랜치를 생성한다. 체크아웃하지는 않는다.</p>
</li>
<li><p><code>git branch -d &lt;이름&gt;</code> : 브랜치를 삭제한다. 체크아웃된 상태가 아니어야 한다. 변경 사항 때문에 삭제가 거부될 경우, <code>-D</code>를 사용하면 강제로 삭제할 수 있다.</p>
</li>
</ul>
<h3 id="git-checkout">git checkout</h3>
<p>브랜치를 전환하거나 특정 커밋이나 파일을 체크아웃하는데 사용한다.</p>
<ul>
<li><code>git checkout &lt;이름&gt;</code> : 해당 브랜치로 전환한다.</li>
<li><code>git checkout &lt;commit-hash&gt;</code> : HEAD 포인터가 해당 커밋으로 이동하게 되며, <code>detached HEAD</code> 상태에 들어가게 된다.</li>
</ul>
<blockquote>
<h4 id="detached-head">detached HEAD</h4>
<p>기본적으로 HEAD 포인터는 현재 작업 중인 브랜치를 가리키지만, <code>HEAD -&gt; branch -&gt; commit</code> detached HEAD 상태에서는 브랜치가 아닌 특정 커밋을 가리킨다. <code>HEAD -&gt; commit</code> 
현재 커밋이 어떤 브랜치에도 속하지 않아 커밋이 사라질 가능성이 있다. detached 상태에서 커밋을 만든 후, 새로운 브랜치를 생성하면 해당 커밋이 그 브랜치에 포함된다.</p>
</blockquote>
<hr>
<h2 id="취소-및-복구">취소 및 복구</h2>
<h3 id="git-reset">git reset</h3>
<p>특정 변경 사항을 취소하거나 HEAD와 브랜치 포인터를 이동시킨다. 로컬에서 작업 중인 변경 사항을 롤백하거나 커밋을 취소할 때 사용된다. 기본 값은 <code>--mixed</code>이다.</p>
<ul>
<li><p><code>git reset --soft &lt;commit-hash&gt;</code> : HEAD를 이동하고, 변경 사항은 스테이징 영역과 워킹 디렉토리에 남긴다. 이전 커밋으로 돌아가지만 변경 사항은 유지하고 싶을 때 사용한다.</p>
</li>
<li><p><code>git reset --mixed &lt;commit-hash&gt;</code> : HEAD를 이동하고, 스테이징 영역은 비우지만 워킹 디렉토리의 변경 사항은 남아있다. 이전 커밋으로 돌아가고 스테이징을 해제하지만 워킹 디렉토리에서 수정 가능하게 남기고 싶을 때 사용한다.</p>
</li>
<li><p><code>git reset --hard &lt;commit-hash&gt;</code> : HEAD를 이동하고, 스테이징 영역과 워킹 디렉토리의 변경 사항을 모두 삭제한다.</p>
</li>
</ul>
<h3 id="git-revert">git revert</h3>
<p>커밋을 되돌리기 위해 새로운 커밋을 생성하는 방식이다. </p>
<pre><code class="language-bash">git revert &lt;commit-hash&gt;</code></pre>
<p>GIT의 히스토리를 유지하기 때문에 안전하게 사용할 수 있다. 원격 저장소에 푸시된 커밋을 되돌리고 싶을 때 사용한다.</p>
<h3 id="git-stash">git stash</h3>
<p>스테이징된 변경 사항과 워킹 디렉토리의 변경 사항을 임시 저장하고, 작업 디렉토리를 가장 최근 커밋 상태로 복구한다. 다른 브랜치로 전환해야 할 때 코드를 저장하기 위해 사용할 수 있다.</p>
<pre><code class="language-bash">git stash
# 변경 사항을 임시저장한 후 워킹 디렉토리를 초기화한다.

git stash -u
# 추적되지 않은 파일도 임시저장한다.

git stash list
stash@{0}: WIP on main: 123abc Update feature
stash@{1}: WIP on main: 456def Fix bug
...
# 저장된 모든 스태시 항목을 나열한다.</code></pre>
<pre><code class="language-bash">git stash pop
# 가장 최근에 저장된 스태시를 복원하고, 해당 스태시를 삭제한다.

git stash apply
# 최근 스태시를 적용하지만, 삭제하지는 않는다.

git stash apply stash@{1}
# 특정 스태시를 지정하여 복원한다.

git stash drop stash@{0}
# 특정 스태시를 삭제한다.

git stash clear
# 모든 스태시를 삭제한다.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[DevOps]]></title>
            <link>https://velog.io/@aug8_/DevOps</link>
            <guid>https://velog.io/@aug8_/DevOps</guid>
            <pubDate>Sat, 05 Oct 2024 11:41:48 GMT</pubDate>
            <description><![CDATA[<h2 id="devops">DevOps</h2>
<p>DevOps는 소프트웨어 개발(Dev)와 운영(Ops)을 결합한 개념이다. Dev와 Ops는 초기 소프트웨어 계획에서 개발, 빌드, 테스트, 릴리즈, 운영에 이르기까지 모든 라이프사이클에 긴밀한 관계를 맺고 있다. DevOps는 두 팀 간의 협업과 소통을 강화해 소프트웨어 개발과 개발 과정을 자동화하고 최적화하는 방법론이다.</p>
<h3 id="주요-개념">주요 개념</h3>
<ul>
<li><code>CI/CD</code> : 코드의 변경 사항을 빠르고 자동으로 배포할 수 있는 환경을 구축한다.</li>
<li><code>Infrastructure as Code</code> : 인프라를 코드로 정의하고 자동화하여 일관적으로 관리한다. </li>
<li><code>Monitoring &amp; Logging</code> : 애플리케이션의 성능을 모니터링하고, 로그를 분석하여 문제를 찾아내고 대응한다.</li>
</ul>
<h3 id="장점">장점</h3>
<ul>
<li>자동화된 프로세스를 통해 변경 사항을 더 자주, 더 빠르게 릴리스할 수 있다.</li>
<li>통합된 시스템을 통해 소프트웨어의 품질을 유지하면서 빠르게 개선할 수 있다.</li>
<li>개발과 운영이 협력하여 시스템 관리와 배포가 더 효율적이다.</li>
</ul>
<hr>
<h2 id="cicd">CI/CD</h2>
<p>CI/CD는 Continuous Integration(지속적 통합)과 Continuous Deployment(지속적 배포)를 의미한다. 소프트웨어 개발에서 자동화와 효율성을 높이기 위한 방법론이다. 소프트웨어 배포에 일관된 프로세스를 유지해 오류를 최소화한다. </p>
<h3 id="ci">CI</h3>
<p>CI는 Continuous Integration, 지속적인 통합을 의미한다. 개발자가 변경한 코드를 자주 통합하는 과정이다. 개발자가 코드를 레포지토리에 push하면 CI 시스템이 자동으로 빌드하고 테스트하여 통합 과정에서 발생할 수 있는 문제를 빠르게 확인하고 해결할 수 있도록 한다.</p>
<h3 id="cd">CD</h3>
<p>CD는 Continuous Deployment, 지속적인 배포를 의미한다. 배포 프로세스가 자동화되어 즉시 프로덕션 환경에 배포된다.</p>
<hr>
<h2 id="infrastructure-as-code">Infrastructure as Code</h2>
<p>Infrastructure as Code(IaC)는 코드로 인프라를 관리하는 개념이다. </p>
<blockquote>
<p>▶ <strong><em>인프라?</em></strong>
애플리케이션이 실행되는 기반 시스템과 환경을 의미한다. 물리적이거나 가상화된 하드웨어로 구성될 수 있다. 서버, 네트워크 장비, 데이터베이스, 스토리지, 보안 설정 등이 해당된다. </p>
</blockquote>
<p>IaC는 코드로 구성되어 수작업을 줄일 수 있다. 텍스트 파일로 저장되어 버전을 관리할 수 있고, 코드를 통해 일관성 있는 인프라를 구현할 수 있다.</p>
<p>어떤 환경에서도 동일한 인프라 구성을 복사해서 사용할 수 있기 때문에, 개발, 테스트, 운영 환경에서 동일한 설정을 유지할 수 있다.</p>
<h4 id="주요-도구">주요 도구</h4>
<ul>
<li>Terraform</li>
<li>AWS CloudFormation</li>
<li>Ansible</li>
</ul>
<hr>
<h2 id="monitoring--logging">Monitoring &amp; Logging</h2>
<h3 id="monitoring">Monitoring</h3>
<p>모니터링은 성능과 가용성을 실시간으로 추적한다. 시스템의 상태를 시각화하고, 성능 저하나 오류가 발생했을 때 신속하게 대응할 수 있다.</p>
<ul>
<li><code>리소스 사용</code> : CPU, 메모리, 대역폭, 디스크 사용량 등 리소스 과부화를 감지한다.</li>
<li><code>성능</code> : 응답 시간, 쿼리 속도 등 성능을 추적하여 병목 현상을 파악한다.</li>
<li><code>장애 감지</code> : 서버가 다운되거나 성능에 문제가 발생했을 때 알림을 보내 문제를 즉시 파악하게 한다.</li>
<li><code>추세 분석</code> : 장기간의 모니터링 데이터를 바탕으로 성능을 최적화하고 향후 계획에 도움을 준다.</li>
</ul>
<h3 id="logging">Logging</h3>
<p>로깅은 애플리케이션이 실행되는 동안 발생한 이벤트나 트랜잭션 기록을 남기는 것이다. 로그 파일에는 동작 기록, 에러 메시지, 사용자 활동 내역 등이 포함된다. 문제 해결 및 분석에 데이터를 제공하여 시스템을 파악하는데 도움을 준다.</p>
<ul>
<li><code>오류 기록</code> : 오류 내용을 기록해 원인을 파악한다.</li>
<li><code>활동 추적</code> : 사용자가 어떤 요청을 보냈는지 기록해 문제를 찾아낸다.</li>
<li><code>성능 탐지</code> : 응답 시간과 오류를 분석해 성능 저하 원인을 찾는다.</li>
<li><code>보안 감사</code> : 시스템 접근 기록과 작업을 기록해 보안 사고에 대응한다.</li>
</ul>
<hr>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://aws.amazon.com/ko/devops/what-is-devops/">DevOps란 무엇입니까?</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[BEM 방법론]]></title>
            <link>https://velog.io/@aug8_/BEM-%EB%B0%A9%EB%B2%95%EB%A1%A0</link>
            <guid>https://velog.io/@aug8_/BEM-%EB%B0%A9%EB%B2%95%EB%A1%A0</guid>
            <pubDate>Sat, 28 Sep 2024 07:43:03 GMT</pubDate>
            <description><![CDATA[<h2 id="bem">BEM</h2>
<p>BEM(Block, Element, Modifier)은 CSS 클래스의 이름을 체계적으로 관리하기 위한 방법론이다. 웹 프로젝트에서 CSS의 재사용성과 유지보수성을 높이는데 도움이 된다.</p>
<p>BEM에서는 세 가지 주요 개념으로 구성된다.
<code>block__element--modifier</code></p>
<p>아래는 BEM 구조 예시이다.</p>
<pre><code class="language-html">&lt;div class=&quot;header&quot;&gt;

  &lt;div class=&quot;header--logo&quot;&gt;
    &lt;div class=&quot;logo&quot;&gt;
        &lt;a href=&quot;#&quot; class=&quot;logo__link&quot;&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class=&quot;header--navbar&quot;&gt;
    &lt;ul class=&quot;navbar&quot;&gt;
      &lt;li class=&quot;navbar__item navbar__item--active&quot;&gt;Home&lt;/li&gt;
      &lt;li class=&quot;navbar__item&quot;&gt;About&lt;/li&gt;
      &lt;li class=&quot;navbar__item&quot;&gt;Contact&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;

&lt;/div&gt;
</code></pre>
<p><code>소문자</code>와 <code>숫자</code>만 사용하고, 두 단어를 조합할 때는 <code>-</code>으로 연결해야 한다는 공통 규칙이 있다.</p>
<p>ex) <code>blackBox</code>(x) <code>black-box</code>(o)</p>
<h3 id="block">Block</h3>
<ul>
<li><p>독립적이고 재사용 가능한 단위를 의미한다. </p>
</li>
<li><p>블록은 보이는 것이 아니라, 목적을 설명한다.</p>
</li>
<li><p>블록은 중첩될 수 있다.</p>
</li>
<li><p>ex) <code>navbar</code>, <code>button</code>, <code>card</code>, <code>nav-menu</code></p>
</li>
</ul>
<pre><code class="language-html">&lt;!-- O : 목적--&gt;
&lt;div class=&quot;error&quot;&gt;&lt;/div&gt;

&lt;!-- X : 보이는것 --&gt;
&lt;div class=&quot;red-text&quot;&gt;&lt;/div&gt;

&lt;!-- 중첩 예시 --&gt;
&lt;header class=&quot;header&quot;&gt; 
    &lt;div class=&quot;logo&quot;&gt;&lt;/div&gt; 
    &lt;form class=&quot;search-form&quot;&gt;&lt;/form &gt; 
&lt;/header&gt;</code></pre>
<h3 id="element">Element</h3>
<ul>
<li><p>Block의 일부로, 좀 더 개별적인 부분을 의미한다. 블록에 종속되기 때문에 블록 없이 존재할 수 없으며, 블록 외부에서 사용할 수도 없다.</p>
</li>
<li><p>엘리먼트는 중첩될 수 있지만, 계층을 정의할 수는 없다. 요소의 요소를 만들 수 없음에 주의하자.</p>
</li>
<li><p>블록과 엘리먼트는 <code>__</code>로 연결한다.</p>
</li>
<li><p>ex) <code>navbar__item</code>, <code>button__icon</code>, <code>card__title</code></p>
</li>
</ul>
<pre><code class="language-html">
&lt;!-- O : 중첩 --&gt;
&lt;form class=&quot;search-form&quot;&gt;
    &lt;div class=&quot;search-form__content&quot;&gt;
        &lt;input class=&quot;search-form__input&quot;&gt;
        &lt;button class=&quot;search-form__button&quot;&gt;Search&lt;/button&gt;
    &lt;/div&gt;
&lt;/form&gt;

&lt;!-- X : 계층 --&gt;
&lt;form class=&quot;search-form&quot;&gt;
    &lt;div class=&quot;search-form__content&quot;&gt;
        &lt;input class=&quot;search-form__content__input&quot;&gt;
        &lt;button class=&quot;search-form__content__button&quot;&gt;Search&lt;/button&gt;
    &lt;/div&gt;
&lt;/form&gt;

&lt;!-- 중첩 예시 --&gt;
&lt;div class=&quot;block&quot;&gt;
    &lt;div class=&quot;block__elem1&quot;&gt;
        &lt;div class=&quot;block__elem2&quot;&gt;&lt;/div&gt;
    &lt;/div&gt;
    &lt;div class=&quot;block__elem3&quot;&gt;&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<h3 id="modifier">Modifier</h3>
<ul>
<li>Block이나 Element의 모양, 상태, 동작을 나타낸다. 기본 기능을 변경하지 않으며 크기, 색상, 활성화 상태, 동작 등을 변경할 때 사용한다.</li>
<li>수정자는 <code>--</code>로 연결한다.</li>
<li>ex) <code>navbar__item--active</code>, <code>button--disabled</code>, <code>card__title--large</code></li>
</ul>
<pre><code class="language-HTML">&lt;!-- O --&gt;
&lt;div class=&quot;block block--mod&quot;&gt;&lt;/div&gt;
&lt;div class=&quot;block block--size-big block--shadow-yes&quot;&gt;&lt;/div&gt;

&lt;!-- X --&gt;
&lt;div class=&quot;block--mod&quot;&gt;&lt;/div&gt;
</code></pre>
<br>

<h2 id="주요-장점">주요 장점</h2>
<ul>
<li><p><strong>유지보수</strong> </p>
<ul>
<li><p>BEM은 네이밍 규칙이 명확하여 시간이 지나거나 다른 개발자가 프로젝트에 합류해도 쉽게 코드를 이해할 수 있다.</p>
</li>
<li><p>블록, 엘리먼트, 수정자가 각각 분리되어 있기 때문에 각 클래스가 어떤 역할을 하는지 직관적으로 알 수 있다.</p>
</li>
<li><p>체계적인 네이밍 규칙으로 새로운 요소나 상태를 추가할 때 일관되게 적용할 수 있다.</p>
</li>
</ul>
</li>
<li><p><strong>재사용성</strong></p>
<ul>
<li><p>각 블록이 독립적으로 동작하기 때문에 여러 페이지나 컴포넌트에서 재사용하기 쉽다. </p>
</li>
<li><p>컴포넌트를 작은 단위로 모듈화해서 사용하므로 스타일을 체계적으로 관리할 수 있다.</p>
</li>
</ul>
</li>
<li><p><strong>CSS 충돌 방지</strong></p>
<ul>
<li><p>각 블록과 엘리먼트가 고유한 이름을 갖기 때문에 CSS 클래스 간에 충돌할 가능성이 낮아진다.</p>
</li>
<li><p>특정 블록에 엘리먼트나 수정자를 추가해도 다른 블록과 상호작용하지 않으므로 CSS 충돌을 피할 수 있다.</p>
</li>
</ul>
</li>
<li><p><strong>가독성</strong></p>
<ul>
<li>클래스 이름은 길어지지만, 네이밍 규칙이 규칙적이기 때문에 HTML의 역할이 명확하게 드러난다. 코드를 직관적으로 읽을 수 있다.</li>
</ul>
</li>
</ul>
<br>

<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://getbem.com/introduction/">BEM — Block Element Modifier</a></li>
<li><a href="https://en.bem.info/">BEM</a></li>
<li><a href="https://kyledev.tistory.com/225">[CSS] BEM 방법론</a></li>
<li><a href="https://nykim.work/15">[CSS 방법론] BEM 방식</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[적절한 알고리즘 찾기]]></title>
            <link>https://velog.io/@aug8_/%EC%A0%81%EC%A0%88%ED%95%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%B0%BE%EA%B8%B0</link>
            <guid>https://velog.io/@aug8_/%EC%A0%81%EC%A0%88%ED%95%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%B0%BE%EA%B8%B0</guid>
            <pubDate>Sat, 31 Aug 2024 08:44:46 GMT</pubDate>
            <description><![CDATA[<h2 id="정렬-알고리즘">정렬 알고리즘</h2>
<p>정렬 알고리즘은 데이터를 특정 기준에 따라 나열하는 알고리즘이다.</p>
<h3 id="사용-사례">사용 사례</h3>
<p>데이터의 정렬이 필요할 때 사용한다.</p>
<p>일반적으로 빠른 성능이 필요하다면 <strong>퀵 정렬</strong>을, 데이터가 크고 안정적인 정렬이 필요하다면 <strong>병합 정렬</strong>을, 데이터가 거의 정렬되어 있다면 <strong>삽입 정렬</strong>을 선택하는 게 좋다.</p>
<h3 id="종류">종류</h3>
<ul>
<li><p><strong>버블 정렬 (Bubble Sort)</strong></p>
<ul>
<li>인접한 두 값을 비교해 순서를 바꾼다.</li>
<li>시간복잡도는 O(n²)이다.</li>
<li><strong>사용 사례</strong> : 데이터 양이 적고, 코드 구현이 간단해야 하는 경우</li>
</ul>
</li>
<li><p><strong>선택 정렬 (Selection Sort)</strong></p>
<ul>
<li>맨 앞에서부터 위치에 적절한 값을 선택해 교환한다.</li>
<li>시간복잡도는 O(n²)이다.</li>
<li><strong>사용 사례</strong> : 데이터의 크기가 작고, 메모리 공간이 부족한 경우. 추가적인 메모리 사용이 거의 없다.</li>
</ul>
</li>
<li><p><strong>삽입 정렬 (Insertion Sort)</strong></p>
<ul>
<li>새로운 값을 이미 정렬된 부분에 삽입한다.</li>
<li>시간복잡도는 O(n²)이다.</li>
<li><strong>사용 사례</strong> : 데이터가 거의 정렬된 경우, 또는 데이터 양이 적은 경우. 실시간으로 입력되는 데이터를 정렬해야 할 때.</li>
</ul>
</li>
<li><p><strong>병합 정렬 (Merge Sort)</strong></p>
<ul>
<li>데이터를 반으로 나눈 후 정렬하여 다시 병합한다.</li>
<li>시간복잡도는 O(n log n)이다.</li>
<li><strong>사용 사례</strong> : 데이터 양이 많을 때. 데이터의 크기가 커도 효율적으로 동작하지만 추가 메모리가 필요하다.</li>
</ul>
</li>
<li><p><strong>퀵 정렬 (Quick Sort)</strong></p>
<ul>
<li>피벗을 선택해 그보다 작은 값과 큰 값을 분할한 후, 각 부분을 재귀적으로 정렬한다.</li>
<li>시간복잡도는 O(n log n)이다.</li>
<li><strong>사용 사례</strong> : 일반적으로 널리 사용된다. 피벗 선택에 따라 성능이 크게 달라질 수 있으며, 최악의 경우 O(n²)이 될 수 있다.</li>
</ul>
</li>
<li><p><strong>힙 정렬 (Heap Sort)</strong></p>
<ul>
<li>힙 자료구조를 사용해 값을 정렬한다.</li>
<li>시간복잡도는 O(n log n)이다.</li>
<li><strong>사용 사례</strong> : 안정성이 필요하지 않은 경우. 데이터 양이 많을 때도 효율적으로 동작하며, 추가 메모리가 필요하지 않다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="탐색-알고리즘">탐색 알고리즘</h2>
<p>데이터에서 원하는 값을 찾을 때 사용하는 알고리즘이다.</p>
<h3 id="사용-사례-1">사용 사례</h3>
<p>데이터 구조와 탐색 목표에 따라 적절한 알고리즘을 선택해야 한다. </p>
<p>정렬되지 않은 배열에서 탐색할 때는 <strong>선형 탐색</strong>을, 정렬된 배열에서 효율적으로 탐색할 때는 <strong>이진 탐색</strong>을, 그래프를 탐색할 때는 <strong>DFS</strong>나 <strong>BFS</strong>를 사용한다.</p>
<h3 id="종류-1">종류</h3>
<ul>
<li><p><strong>선형 탐색 (Linear Search)</strong></p>
<ul>
<li>데이터를 처음부터 끝까지 순차적으로 확인하며 원하는 값을 찾는다. 정렬되지 않은 데이터에서도 사용할 수 있다.</li>
<li>시간복잡도는 O(n)이다.</li>
<li><strong>사용 사례</strong> : 정렬되지 않았거나 정렬 여부를 알 수 없는 경우, 데이터 크기가 작아서 시간복잡도가 중요하지 않은 경우.</li>
</ul>
</li>
<li><p><strong>이진 탐색 (Linear Search)</strong></p>
<ul>
<li>정렬된 배열에서 중앙 값을 기준으로 탐색 범위를 반으로 줄여가며 값을 찾는다.</li>
<li>시간복잡도는 O(log n)이다.</li>
<li><strong>사용 사례</strong> : 배열이 이미 정렬된 상태일 때. 데이터의 크기가 크고 빠른 탐색이 요구될 때.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="완전탐색-알고리즘">완전탐색 알고리즘</h2>
<p>가능한 모든 경우를 전부 탐색하여 답을 찾는 알고리즘이다.</p>
<h3 id="종류-2">종류</h3>
<ul>
<li><p><strong>브루트 포스 (Brute Force)</strong></p>
<ul>
<li>가능한 모든 방법을 찾아 검사하여 해답을 찾는다.</li>
</ul>
</li>
<li><p><strong>순열 (Permutation)</strong></p>
<ul>
<li>주어진 원소를 나열하는 모든 가능한 순서를 생성해 가능한 모든 경우의 수를 확인한다.</li>
<li><strong>사용 사례</strong> : 주어진 숫자들로 만들 수 있는 가장 큰 수, 특정 조건을 만족하는 순서.</li>
</ul>
</li>
<li><p><strong>백트래킹 (Backtracking)</strong></p>
<ul>
<li>가능한 모든 방법을 탐색하다가, 조건이 맞지 않는 경우 되돌아간다.</li>
<li><strong>사용 사례</strong> : 스도쿠, N-Queens 문제, 조건을 만족하는 경로 찾기.</li>
</ul>
</li>
<li><p><strong>비트마스크 (Bitmask)</strong></p>
<ul>
<li>이진수를 사용해 부분집합을 표현하고 처리한다. 각 비트는 원소의 포함 여부를 나타낸다.</li>
<li><strong>사용 사례</strong> : 부분집합을 빠르게 생성해야 할 때. 상태를 비트로 표현할 수 있을 때.</li>
</ul>
</li>
<li><p><strong>재귀 (Recursion)</strong></p>
<ul>
<li>복잡한 문제를 간단한 문제로 나누어 해결하는 방법이다.</li>
<li><strong>사용 사례</strong> : 하노이의 탑</li>
</ul>
</li>
</ul>
<hr>
<h2 id="그래프-알고리즘">그래프 알고리즘</h2>
<p>노드와 간선으로 표현되는 관계를 탐색할 때 사용하는 알고리즘이다.</p>
<h3 id="종류-3">종류</h3>
<ul>
<li><p><strong>깊이 우선 탐색 (DFS)</strong></p>
<ul>
<li>시작 정점에서 가능한 깊게 들어가며 탐색한 후, 더 이상 탐색할 수 없으면 되돌아가는 방식이다. 스택을 사용하거나 재귀적으로 구현한다.</li>
<li><strong>사용 사례</strong> : 경로 탐색, 연결 요소 찾기, 미로 찾기</li>
</ul>
</li>
<li><p><strong>너비 우선 탐색 (BFS)</strong></p>
<ul>
<li>시작 정점에서 인접한 정점을 먼저 탐색하고, 그 다음 인접한 정점을 탐색하는 방식이다. 큐를 사용해 구현한다.</li>
<li><strong>사용 사례</strong> : 최단 경로 문제, 레별 별 탐색</li>
</ul>
</li>
<li><p><strong>다익스트라 알고리즘 (Dijkstra&#39;s Algorithm)</strong></p>
<ul>
<li>가중치가 있는 그래프에서 최단 경로를 찾는다. 음의 가중치가 있는 간선은 처리할 수 없다.</li>
<li><strong>사용 사례</strong> : 최단 거리 경로 찾기</li>
</ul>
</li>
<li><p><strong>크루스칼 알고리즘 (Kruskal&#39;s Algorithm)</strong></p>
<ul>
<li>그래프의 모든 간선을 가중치 순으로 정렬하고, 가장 작은 가중치의 간선을 선택해 사이클이 생기지 않도록 트리를 확장한다.</li>
<li><strong>사용 사례</strong> : 최소 비용으로 모든 정점 연결</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 메타데이터]]></title>
            <link>https://velog.io/@aug8_/Next.js-%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@aug8_/Next.js-%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Sat, 24 Aug 2024 17:23:22 GMT</pubDate>
            <description><![CDATA[<h1 id="metadata">Metadata</h1>
<p>메타데이터는 HTML 문서의 <code>&lt;head&gt;</code>에 포함되어, 해당 웹 페이지에 대한 정보를 제공한다. 검색 엔진 최적화(SEO)에 영향을 주기 때문에 중요하다.</p>
<p>아래는 메타데이터의 예시이다.</p>
<pre><code class="language-HTML">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;description&quot; content=&quot;웹사이트 설명&quot;&gt;
    &lt;meta name=&quot;keywords&quot; content=&quot;키워드1, 키워드2&quot;&gt;
    &lt;meta name=&quot;author&quot; content=&quot;저자 이름&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;웹사이트 제목&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;h1&gt;환영합니다!&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<h1 id="메타데이터-설정">메타데이터 설정</h1>
<p>Next.js 14에서 메타데이터를 추가하는 방법은 두 가지가 있다. 아래 옵션을 사용하면 Next.js가 자동으로 페이지에 관련 <code>&lt;head&gt;</code>를 생성한다.</p>
<ul>
<li>구성 기반 메타데이터 : 정적 <code>metadata</code> 객체나 동적 <code>generateMetadata</code> 함수를 사용한다.</li>
<li>파일 기반 메타데이터 : 특정 라우트 세그먼트에 정적 또는 동적으로 생성된 특수 파일을 추가한다.</li>
</ul>
<h2 id="정적-메타데이터">정적 메타데이터</h2>
<p>정적 메타데이터를 정의하려면, <code>app</code> 디렉토리의 <code>layout.tsx</code> 또는 <code>page.tsx</code>에 다음과 같이 설정하면 된다.</p>
<pre><code class="language-tsx">import type { Metadata } from &#39;next&#39;

export const metadata: Metadata = {
  title: &#39;...&#39;,
  description: &#39;...&#39;,
}</code></pre>
<h2 id="동적-메타데이터">동적 메타데이터</h2>
<p><code>generateMetadata</code> 함수를 사용해 동적으로 설정할 수도 있다.</p>
<pre><code class="language-tsx">import type { Metadata, ResolvingMetadata } from &#39;next&#39;

type Props = {
  params: { id: string }
  searchParams: { [key: string]: string | string[] | undefined }
}

export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise&lt;Metadata&gt; {

  const product = await fetch(`https://.../${params.id}`).then((res) =&gt; res.json())
  const previousImages = (await parent).openGraph?.images || []

  return {
    title: product.title,
    openGraph: {
      images: [&#39;/some-specific-page-image.jpg&#39;, ...previousImages],
    },
  }
}</code></pre>
<h2 id="파일-기반-메타데이터">파일 기반 메타데이터</h2>
<p><code>public</code> 디렉토리에 다음과 같은 특수 파일을 사용할 수 있다.</p>
<ul>
<li><code>favicon.ico</code>, <code>apple-icon.jpg</code>, <code>icon.jpg</code></li>
<li><code>opengraph-image.jpg</code> ,<code>twitter-image.jpg</code></li>
<li><code>robots.txt</code></li>
<li><code>sitemap.xml</code></li>
</ul>
<h2 id="메타데이터-필드">메타데이터 필드</h2>
<ul>
<li><code>title</code></li>
<li><code>description</code></li>
<li><code>metadataBase</code></li>
<li><code>openGraph</code></li>
<li><code>robots</code></li>
<li><code>icons</code></li>
<li><code>manifest</code></li>
<li><code>viewport</code></li>
<li>그외에도 <a href="https://nextjs.org/docs/app/api-reference/functions/generate-metadata#metadata-fields">다양한 필드</a>를 지원한다.</li>
</ul>
<h3 id="title">title</h3>
<p><code>title</code>은 제목을 설정한다.</p>
<pre><code class="language-js">export const metadata = {
  title: &#39;Next.js&#39;,
}</code></pre>
<pre><code class="language-html">&lt;title&gt;Next.js&lt;/title&gt;</code></pre>
<p><code>title</code>에는 <code>default</code>, <code>template</code>, <code>absolute</code> 세 가지 객체가 있다. </p>
<ul>
<li><p><code>title.default</code>는 title을 정의하지 않는 자식 세그먼트에 대체 제목을 설정한다.</p>
<pre><code class="language-tsx">export const metadata: Metadata = {
title: {
  default: &#39;Acme&#39;,
},
}</code></pre>
</li>
<li><p><code>title.template</code>는 자식 세그먼트에 접두사나 접미사를 추가하는데 사용한다.</p>
<pre><code class="language-tsx">// app/layout.tsx
export const metadata: Metadata = {
title: {
  template: &#39;%s | Acme&#39;,
  default: &#39;Acme&#39;, // 템플릿을 만들 땐 deault를 설정해야 한다.
},
}</code></pre>
<pre><code class="language-tsx">// app/about/page.tsx
export const metadata: Metadata = {
title: &#39;About&#39;,
}
</code></pre>
</li>
</ul>
<p>// Output: <title>About | Acme</title></p>
<pre><code>

- `title.absolute`는 부모 세그먼트의 `template`를 무시한다.
```tsx
export const metadata: Metadata = {
  title: {
    absolute: &#39;About&#39;,
  },
}

// Output: &lt;title&gt;About&lt;/title&gt;</code></pre><h2 id="행동-규칙">행동 규칙</h2>
<p>메타데이터를 정의하지 않더라도 다음 두 태그는 기본으로 설정된다. 덮어쓰는 것도 가능하다.</p>
<pre><code class="language-html">&lt;meta charset=&quot;utf-8&quot; /&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&gt;</code></pre>
<p>메타데이터는 루트 세그먼트부터 시작해 최종 <code>page</code> 세그먼트까지 순서대로 평가된다. 순서에 따라, 동일한 경로의 여러 세그먼트에서 보내진 메타데이터 객체는 얕게 병합되어 최종 메타데이터를 출력한다. 중복된 내용은 마지막 세그먼트에서 정의된 메타데이터로 덮어쓰여진다.</p>
<pre><code class="language-js">&lt;!-- app/layout.js --&gt;
export const metadata = {
  title: &#39;Acme&#39;,
  openGraph: {
    title: &#39;Acme&#39;,
    description: &#39;Acme is a...&#39;,
  },
}</code></pre>
<pre><code class="language-js">&lt;!-- app/blog/page.js --&gt;
export const metadata = {
  title: &#39;Blog&#39;,
  openGraph: {
    title: &#39;Blog&#39;,
  },
}</code></pre>
<pre><code class="language-html">&lt;!-- Output --&gt;
&lt;title&gt;Blog&lt;/title&gt;
&lt;meta property=&quot;og:title&quot; content=&quot;Blog&quot; /&gt;</code></pre>
<ul>
<li><code>app/layout.js</code>의 <code>title</code>은 <code>app/blog/page.js</code>의 <code>title</code>로 대체된다.</li>
<li><code>app/layout.js</code>의 모든 <code>openGraph</code> 필드는 <code>app/blog/page.js</code>에서 openGraph 메타데이터를 설정하므로 대체된다. <code>openGraph.description</code>이 없는 점에 유의하자.</li>
</ul>
<p>일부 중첩 필드를 세그먼트 간에 공유하려면 변수를 사용하면 된다.</p>
<pre><code class="language-js">&lt;!-- app/shared-metadata.js --&gt;
export const openGraphImage = { images: [&#39;http://...&#39;] }</code></pre>
<pre><code class="language-js">&lt;!-- app/about/page.js --&gt;
import { openGraphImage } from &#39;./shared-metadata&#39;

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: &#39;About&#39;,
  },
}</code></pre>
<p>중첩되지 않은 필드는 상속된다.</p>
<pre><code class="language-js">&lt;!-- app/layout.js --&gt;
export const metadata = {
  title: &#39;Acme&#39;,
  openGraph: {
    title: &#39;Acme&#39;,
    description: &#39;Acme is a...&#39;,
  },
}</code></pre>
<pre><code class="language-js">&lt;!-- app/about/page.js --&gt;
export const metadata = {
  title: &#39;About&#39;,
}</code></pre>
<pre><code class="language-html">&lt;!-- Output --&gt;
&lt;title&gt;About&lt;/title&gt;
&lt;meta property=&quot;og:title&quot; content=&quot;Acme&quot; /&gt;
&lt;meta property=&quot;og:description&quot; content=&quot;Acme is a...&quot; /&gt;</code></pre>
<h1 id="참고">참고</h1>
<ul>
<li><a href="https://nextjs.org/docs/app/building-your-application/optimizing/metadata">Metadata</a></li>
<li><a href="https://nextjs.org/docs/app/api-reference/functions/generate-metadata">generateMetadata</a></li>
<li><a href="https://reactnext-central.xyz/blog/nextjs/metadata">Next.js에서 메타데이터 설정과 최적화 기법</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Storage]]></title>
            <link>https://velog.io/@aug8_/Storage</link>
            <guid>https://velog.io/@aug8_/Storage</guid>
            <pubDate>Sun, 18 Aug 2024 11:11:42 GMT</pubDate>
            <description><![CDATA[<h1 id="web-storage">Web Storage</h1>
<p>웹 스토리지는 클라이언트에 데이터를 저장하는 API이다. HTML5에서 도입되었으며 세션 스토리지와 로컬 스토리지를 제공한다. 동기식으로 작동한다. 클라이언트에 데이터를 저장하므로 보안 이슈가 있을 수 있다. 중요한 데이터나 유실되어선 안 되는 데이터는 서버 DB에 저장해야 한다.</p>
<p>key-value 형태로 데이터를 저장한다. key는 고유 식별자 역할을 하고, value에는 문자열 형태로 데이터를 저장할 수 있다. 이외의 타입은 변환하여 사용하여야 한다. 대체로 5MB의 저장 용량이 제공된다.</p>
<h2 id="session-storage">Session Storage</h2>
<p>세션 스토리지에 저장된 데이터는 브라우저 탭을 닫으면 삭제된다. 같은 탭에서만 데이터를 공유할 수 있으며, 새로운 탭을 열면 새로운 세션으로 간주된다. </p>
<p>폼 입력 상태 저장, 페이지간 이동 시 일시적인 데이터 자장 등 한 번의 방문 동안 유지할 일시적인 상태 정보를 저장하는데 적합하다.</p>
<pre><code class="language-js">// 데이터 저장
sessionStorage.setItem(&#39;key&#39;, &#39;value&#39;);

// 데이터 읽기
const value = sessionStorage.getItem(&#39;key&#39;);

// 데이터 삭제
sessionStorage.removeItem(&#39;key&#39;);

// 모든 데이터 삭제
sessionStorage.clear();</code></pre>
<h2 id="local-storage">Local Storage</h2>
<p>로컬 스토리지에 저장된 데이터는 브라우저를 종료하더라도 삭제되지 않는다. 사용자가 명시적으로 데이터를 삭제하지 않는 한 계속 저장된다. 저장된 데이터는 동일 도메인 내에서만 접근할 수 있다.</p>
<p>사용자 설정, 로그인 상태 유지 등 장기적인 데이터를 저장하는데 적합하다.</p>
<pre><code class="language-js">// 데이터 저장
localStorage.setItem(&#39;key&#39;, &#39;value&#39;);

// 데이터 읽기
const value = localStorage.getItem(&#39;key&#39;);

// 데이터 삭제
localStorage.removeItem(&#39;key&#39;);

// 모든 데이터 삭제
localStorage.clear();</code></pre>
<p>웹 스토리지 데이터는 문자열 형태로만 저장된다. 복잡한 형태의 데이터를 제정하기 위해서는, 객체를 JSON 문자열로 변환한 다음에 저장해야 한다.</p>
<pre><code class="language-js">const data = {a: 1, b: 2};

// JSON 문자열로 변환하여 저장
localStorage.setItem(&#39;key&#39;, JSON.stringify(data));

// 데이터 가져오기
const storedData = localStorage.getItem(&#39;key&#39;);

// JSON 문자열을 JavaScript 객체로 변환
const parsedData = JSON.parse(storedData);</code></pre>
<h1 id="indexeddb">IndexedDB</h1>
<p>IndexedDB는 웹 브라우저에서 대량의 구조화된 데이터를 클라이언트에 저장할 수 있는 비관계형 데이터베이스이다. HTML5에서 도입되었으며, 세션 스토리지와 로컬 스토리지보다 더 복잡하고 많은 양의 데이터를 관리할 수 있다.</p>
<p>IndexedDB는 비동기적으로 작동하여 작업을 수행하는 동안 웹 페이지의 성능을 저하시키지 않는다. 브라우저마다 차이가 있으나, 수백MB의 용량을 제공한다. 더 복잡하지만 더 많은 기능과 유연성을 갖추고 있다.</p>
<h3 id="데이터베이스-생성">데이터베이스 생성</h3>
<pre><code class="language-js">const name = &#39;myDatabase&#39;;
const version = 1
const request = indexedDB.open(name, version);

// 저장소 생성
request.onupgradeneeded = (event) =&gt; {
  const db = event.target.result;
  const objectStore = db.createObjectStore(&#39;myStore&#39;, { keyPath: &#39;id&#39; });


// 성공 확인
request.onsuccess = (event) =&gt; {
  const db = event.target.result;
  console.log(&#39;Database opened successfully&#39;);
};

// 에러 발생
request.onerror = (event) =&gt; {
  console.error(&#39;Database error:&#39;, event.target.errorCode);
};</code></pre>
<h3 id="데이터-다루기">데이터 다루기</h3>
<pre><code class="language-js">const request = indexedDB.open(&#39;myDatabase&#39;, 1);

request.onsuccess = (event) =&gt; {
  const db = event.target.result;
  const transaction = db.transaction([&#39;myStore&#39;], &#39;readwrite&#39;);
  // type: readonly / readwrite / versionchange
  const objectStore = transaction.objectStore(&#39;myStore&#39;);

  // 데이터 추가
  const data = { id: 1, name: &#39;kim&#39;, age: 30 };
  const request = objectStore.add(data);

  request.onsuccess = () =&gt; {
    console.log(&#39;Data added successfully&#39;);
  };

  request.onerror = (event) =&gt; {
    console.error(&#39;Add data error:&#39;, event.target.errorCode);
  };
};</code></pre>
<pre><code class="language-js">  // 데이터 조회
  const request = objectStore.get(1);</code></pre>
<pre><code class="language-js">  // 데이터 삭제
  const request = objectStore.delete(1);</code></pre>
<h1 id="참고">참고</h1>
<ul>
<li><a href="https://adjh54.tistory.com/56">[JS] 웹 스토리지(로컬 스토리지 / 세션 스토리지) 이해하기</a></li>
<li><a href="https://velog.io/@miho2582/indexed-DB">indexed DB 사용해보기</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[babel, webpack]]></title>
            <link>https://velog.io/@aug8_/babel-webpack</link>
            <guid>https://velog.io/@aug8_/babel-webpack</guid>
            <pubDate>Mon, 05 Aug 2024 11:19:52 GMT</pubDate>
            <description><![CDATA[<h1 id="babel">Babel</h1>
<img src="https://babeljs.io/img/babel.svg" width="500px;">

<p>Babel은 주로 ECMAScript 2015+ (ES6+) 코드를 이전 버전의 브라우저나 환경에서 호환되는 JavaScript 버전으로 변환하는데 사용하는 도구이다. 개발자는 Babel를 사용해 소스코드를 웹 브라우저가 처리할 수 있는 JavaScript 버전으로 변환하여 JavaScript 언어의 최신 기능을 활용할 수 있게 된다. TypeScript를 JavaScript로 컴파일하는데에도 사용할 수 있다.</p>
<h2 id="필요한-이유">필요한 이유</h2>
<h3 id="cross-browsing">Cross Browsing</h3>
<p>JavaScript는 지속적으로 새로운 기능이 추가된다. 최신 기능은 개발자의 생산성을 늪이고 코드를 더 효율적으로 작성하는데 도움을 준다. 
문제는 모든 브라우저가 최신 JavaScript 문법을 지원하지 않는다는 점이다. Babel을 사용하면 다양한 브라우저와 환경에서 코드를 일관되게 실행할 수 있다. 다양한 브라우저를 지원해야 하는 웹 사이트에서 특히 중요할 수밖에 없다.</p>
<h3 id="babel-polyfill">babel-polyfill</h3>
<p>Babel을 사용한다고 해서 최신 함수를 사용할 수 있는 것은 아니다. 브라우저 엔진이 구현하지 않은 새로운 함수를 사용하기 위해서, <code>polyfill</code> 을 사용한다. polyfill은 스크립트에 사용자가 원하는 최신 함수를 추가한다. 자바스크립트는 동적인 언어로 어떤 함수라도 스크립트에 추가할 수 있기 때문에, polyfill이 이를 대신한다. Babel은 컴파일시에, polyfill은 런타임에 실행된다는 차이점이 있다.</p>
<h2 id="변환">변환</h2>
<p>아래는 Babel의 변환 예시이다. 이외에도 다양한 변환이 있다.</p>
<h3 id="arrow-function">Arrow Function</h3>
<p>화살표 함수는 ES6에서 도입된 기능으로, 함수 표현식을 간결하게 작성할 수 있게 해준다. Babel은 화살표 함수를 일반 함수 선언으로 변환한다.</p>
<pre><code class="language-js">// 입력 코드
const add = (a, b) =&gt; a + b;

// 변환된 코드
var add = function(a, b) {
  return a + b;
};</code></pre>
<h3 id="class">Class</h3>
<p>ES6에서 도입된 클래스 문법을 변환할 수 있다.</p>
<pre><code class="language-js">// 입력 코드
class Person {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return `Hello, my name is ${this.name}`;
  }
}

// 변환된 코드
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return &#39;Hello, my name is &#39; + this.name;
};</code></pre>
<h3 id="비동기-함수">비동기 함수</h3>
<p>ES8에서 도입된 비동기 함수를 변환할 수 있다.</p>
<pre><code class="language-js">// 입력 코드
async function fetchData() {
  const response = await fetch(&#39;https://api.example.com/data&#39;);
  const data = await response.json();
  return data;
}

// 변환된 코드
function fetchData() {
  return regeneratorRuntime.async(function fetchData$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return regeneratorRuntime.awrap(fetch(&#39;https://api.example.com/data&#39;));

        case 2:
          response = _context.sent;
          _context.next = 5;
          return regeneratorRuntime.awrap(response.json());

        case 5:
          data = _context.sent;
          return _context.abrupt(&quot;return&quot;, data);

        case 7:
        case &quot;end&quot;:
          return _context.stop();
      }
    }
  });
}</code></pre>
<h3 id="destructuring">Destructuring</h3>
<p>ES6에서 도입된 디스트럭처링 할당 문법을 변환할 수 있다.</p>
<pre><code class="language-js">// 입력 코드
const [a, b] = [1, 2];
const { name, age } = { name: &#39;Alice&#39;, age: 25 };

// 변환된 코드
var _ref = [1, 2],
    a = _ref[0],
    b = _ref[1];

var _ref2 = { name: &#39;Alice&#39;, age: 25 },
    name = _ref2.name,
    age = _ref2.age;</code></pre>
<h3 id="template-literals">Template Literals</h3>
<p>ES6에서 도입된 템플릿 리터럴을 변환할 수 있다.</p>
<pre><code class="language-js">// 입력 코드
const greeting = `Hello, ${name}!`;

// 변환된 코드
var greeting = &#39;Hello, &#39; + name + &#39;!&#39;;</code></pre>
<h3 id="jsx">JSX</h3>
<p>JSX는 React에서 사용되는 문법으로, HTML과 유사한 구조를 JavaScript 코드에서 사용할 수 있게 한다. Babel은 JSX 코드를 일반 JavaScript로 변환한다.</p>
<pre><code class="language-jsx">// 입력 코드
const element = &lt;h1&gt;Hello, world!&lt;/h1&gt;;

// 변환된 코드
const element = React.createElement(&#39;h1&#39;, null, &#39;Hello, world!&#39;);</code></pre>
<h3 id="typescript">TypeScript</h3>
<p>TypeScript는 정적 타입을 명시할 수 있는 JavaScript의 상위집합이다. Babel은 TypeScript 코드를 일반 JavaScript로 변환한다.</p>
<pre><code class="language-js">// 입력 코드
const greeting: string = &quot;Hello, world!&quot;;

// 변환된 코드
const greeting = &quot;Hello, world!&quot;;</code></pre>
<h2 id="동작-3단계">동작 3단계</h2>
<h3 id="1-파싱parsing">1. 파싱(parsing)</h3>
<p>JavaScript 코드를 분석해 추상 구문 트리(Abstract Syntax Tree, AST)로 변환한다. 이 과정에서 코드를 구조화된 데이터로 표현하게 된다.</p>
<h3 id="2-변환transsforming">2. 변환(Transsforming)</h3>
<p>생성한 AST를 변환한다. 각종 플러그인과 프리셋을 사용해 브라우저가 지원하는 문법의 AST로 수정한다.</p>
<h3 id="3-출력printing">3. 출력(Printing)</h3>
<p>변환된 AST를 다시 JavaScript 코드로 변환한다. </p>
<hr>
<h1 id="webpack">Webpack</h1>
<img src="https://webpack.kr/site-logo.c0e60df418e04f58.svg" width="500px;">

<p>Webpack은 JavaScript 정적 모듈 번들러(module bundler)이다. 모듈 번들러란 웹 애플리케이션을 구성하는 HTML, CSS, JS, 이미지 등의 리소스를 모두 각각의 모듈로 보고 이를 조합해서 하나의 결과물을 만드는 도구를 의미한다. 웹 개발의 복잡성이 증가하며 여러개의 파일과 모듈을 효율적으로 관리하고 번들링하는 도구의 필요성이 생겼다. Webpack은 이러한 필요성을 위해 다양한 파일 형식을 하나의 번들로 결합하고 최적화한다.</p>
<h2 id="필요한-이유-1">필요한 이유</h2>
<p>Webpack이 해결해주는 문제는 다음과 같다.</p>
<h3 id="자바스크립트-변수-유효-범위">자바스크립트 변수 유효 범위</h3>
<p>아래 코드처럼 스크립트를 추가하는 경우, 복잡한 애플리케이션을 개발할 때 변수의 이름이 중복될 문제가 있다. 반대로 하나의 거대한 JavaScript 파일을 만들어서 사용한다면 유효범위와 크기, 가독성, 유지보수의 문제가 생긴다. 따라서 파일 단위로 변수를 관리해야만 했다.</p>
<pre><code class="language-html">  &lt;body&gt;
    &lt;script&gt;
          var num = 10;
      function getNum() =&gt; {console.log(num);};
    &lt;/script&gt;
          var num = 20;
      function getNum() =&gt; {console.log(num);};
    &lt;script&gt;
    &lt;/script&gt;
    &lt;script&gt;
      getNum(); // 20
    &lt;/script&gt;
  &lt;/body&gt;</code></pre>
<h3 id="http-요청-제한">HTTP 요청 제한</h3>
<p>TCP에 따라 브라우저에서 한번에 서버로 보낼 수 있는 HTTP 요청 숫자는 제한되어 있다. 스크립트가 많을수록 웹페이지를 로드하는 시간이 늘어나기 때문에 HTTP 요청 숫자를 줄이는 것이 웹 애플리케이션의 성능을 높이는 방법이다.</p>
<h3 id="사용하지-않는-코드-관리">사용하지 않는 코드 관리</h3>
<p>사용하지 않는 라이브러리나 코드가 생기거나, 라이브러리를 사용하더라도 일부 기능만 사용하는 경우가 있다. 모든 코드를 번들 파일에 포함시킨다면 불필요한 용량이 크게 증가할 것이다.</p>
<h3 id="dynamic-loading--lazy-loading">Dynamic Loading &amp; Lazy Loading</h3>
<p>사용자는 웹페이지의 일부만 본다. 성능을 향상하기 위해선 모든 코드를 로드하는 것이 아닌, 필요한 모듈만 동적으로 로딩해야 한다. </p>
<h2 id="주요-기능">주요 기능</h2>
<p><img src="https://miro.medium.com/v2/resize:fit:638/1*XKDiOZ6M3u1PxNz1eTDbGg.jpeg" alt=""></p>
<h3 id="module-bundling">Module Bundling</h3>
<p>Webpack의 가장 기본적인 기능은 여러 개의 JavaScript 파일을 하나의 번들 파일로 합치는 것이다. 이 과정에서 Webpack은 모듈 간의 종속성을 분석하고, 최적의 방식으로 결합한다.</p>
<p>모듈 번들러는 두 가지 단계를 거쳐 작동하게 된다.</p>
<h4 id="1-의존성-해결">1. 의존성 해결</h4>
<p>의존성 그래프를 매핑하는 단계이다. 모든 파일 간 의존성을 파악하여 관계 맵을 생성한다. 브라우저가 함수를 요청할 때, 함수를 검색하기 위해 의존성 순서가 필수적이다.</p>
<h4 id="2-packing">2. packing</h4>
<p>여러 개의 코드 파일과 의존성을 브라우저가 처리할 수 있는 단일 파일로 제공한다. 의존성 그래프를 기반으로 코드 파일을 하나로 통합하고, 각 모듈이 독립적으로 작동하고 다른 모듈에서 사용할 수 있도록 하고, 모든 코드 파일과 의존성을 하나의 파일로 만들어낸다.</p>
<h3 id="code-splitting">Code Splitting</h3>
<p>코드 분할은 애플리케이션에서 필요한 부분만 로드하도록 해 초기 로드 시간을 줄이는 방법이다. Webpack은 코드 분할을 통해 대규모 애플리케이션의 성능을 최적화할 수 있다.</p>
<h3 id="loaders">Loaders</h3>
<p>로더는 Webpack이 JavaScript 외에 다른 파일(CSS, Image, Font 등)을 처리할 수 있게 해준다.</p>
<hr>
<h1 id="정리">정리</h1>
<ul>
<li><p><code>Babel</code>은 자바스크립트 코드를 변환하는데 사용하는 도구이다. 최신 JavaScript 문법을 구형 브라우저에서도 호환될 수 있도록 변환하는 역할을 한다.</p>
</li>
<li><p><code>Webpack</code>은 여러 개의 파일과 리소스를 하나의 번들로 묶어주는 모듈 번들러(module bundler)이다. JavaScript, CSS, 이미지 등 다양한 리소스를 효율적으로 관리하고 빌드할 수 있도록 도와준다.</p>
</li>
</ul>
<p><code>Babel</code>과 <code>Webpack</code>은 현대 웹 개발에서 필수적으로 사용된다. 서로 보완적인 역할을 하며, Babel로 최신 문법을 변환한 뒤 Webpack으로 번들링하여 배포하는 방식이 일반적이다.</p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Babel_(transcompiler)">Babel (transcompiler)</a></li>
<li><a href="https://velog.io/@guddls64/Babel-%EB%B0%94%EB%B2%A8-%EB%AD%90%ED%95%98%EB%8A%94-%EC%B9%9C%EA%B5%AC-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%9C%EA%B1%B0">Babel - 바벨, 뭐하는 친구? 왜 필요한거?</a></li>
<li><a href="https://velog.io/@suyeon9456/Babel">Babel 직접 적용하며 이해하기</a></li>
<li><a href="https://medium.com/atant/what-is-webpack-252513b3c1ad">What is Webpack?</a></li>
<li><a href="https://velog.io/@greencloud/%EA%B7%B8%EB%9E%98%EC%84%9C-webpack%EC%9D%B4-%EB%AD%94%EB%8D%B0">그래서 webpack이 뭔데?</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[playwright]]></title>
            <link>https://velog.io/@aug8_/playwright</link>
            <guid>https://velog.io/@aug8_/playwright</guid>
            <pubDate>Wed, 24 Jul 2024 11:27:58 GMT</pubDate>
            <description><![CDATA[<h1 id="frontend-test">FrontEnd Test</h1>
<ul>
<li><p><code>Unit Test</code> : 단위 테스트는 함수나 모듈 등 가장 작은 단위를 테스트한다. 특정 기능이 예상대로 작동하는지 확인하는데 중점을 둔다. 빠르고 간단하며, 의존성이 적어 독립적으로 실행할 수 있다.</p>
</li>
<li><p><code>Integration Test</code> : 통합 테스트는 여러 모듈이 함께 작동하는 방식을 테스트한다. 모듈 간 의존성과 상호작용을 검증할 수 있다.</p>
</li>
<li><p><code>End-To-End Test</code> : E2E 테스트는 전체 흐름을 테스트한다. 브저우저 환경에서 실제 사용자가 경험하는 것과 동일한 방식으로 테스트를 수행한다. UI 요소와 사용자 상호작용을 포함한 전체 시스템을 테스트할 수 있지만, 가장 느리고 복잡하다.</p>
</li>
</ul>
<h2 id="e2e">E2E</h2>
<blockquote>
<p><em>End-To-End Testing은 실제 상황에서 애플리케이션의 기능을 처음부터 끝까지 테스트하는 방법이다.</em></p>
</blockquote>
<ul>
<li><p>E2E는 사용자 관점에서 상호작용한다. 버튼 클릭, 폼 제출 등을 자동화된 스크립트를 통해 수행하게 된다. 사용자 로그인, 제품 검색, 구매 등의 실제 사용 과정을 시뮬레이션할 수 있다.</p>
</li>
<li><p>프론트엔드, 백엔드, 데이터베이스를 포함한 모든 애클리케이션의 구성 요소가 올바르게 동작하는지 확인할 수 있다.</p>
</li>
<li><p>사용자의 실제 경험을 기반으로 하여 신뢰성이 높다. 시스템 간의 상호작용에서 발생하는 문제를 찾아낼 수 있다.</p>
</li>
<li><p>테스트 스크립트로 반복 실행할 수 있어 테스트 과정을 자동화할 수 있다.</p>
</li>
<li><p><code>Cypress</code>, <code>Selenium</code>, <code>Puppeteer</code>, <code>Playwright</code> 등이 있다.</p>
</li>
</ul>
<h1 id="playwright">Playwright</h1>
<p><code>Playwright</code>는 Microsoft에서 개발한 오픈 소스 자동화 테스트 도구이다. 브라우저를 자동으로 제어하여 사용자가 수행할 수 있는 모든 동작을 스크립트로 작성할 수 있다.</p>
<p>이미 광범위하게 사용되고 있는 E2E 도구 <code>Cypress</code>가 있지만, 그럼에도 <code>Playwright</code>에 주목하는 이유는 다음과 같다.</p>
<ul>
<li>여러 프로그래밍 언어로 테스트를 작성할 수 있다. TypeScript, JavaScript, Python, Java 등을 지원하고 있다.</li>
<li>크로스 브라우저를 지원하기 때문에, 애플리케이션이 다양한 브라우저에서 원활하게 작동하는지 확인할 수 있다.</li>
<li>Microsoft에서 제공하기 때문에 <code>Playwright Test for VSCode</code>을 지원한다. 훨씬 편리하게 테스트할 수 있다.</li>
<li>Playwright는 병렬로 테스트를 수행한다. (Cypress은 유료 옵션) E2E는 느린 테스트이다. 테스트 코드가 늘어날 수록 속도 면에서 체감할 수 있을 정도로 확연한 차이를 보인다.</li>
<li>모든 작업은 요소가 보이고 실행 가능해질 때까지 자동으로 기다린 후에 수행된다.</li>
</ul>
<hr>
<blockquote>
<p><strong>Playwright API reference</strong>
<a href="https://playwright.dev/">공식문서</a>를 참고해 기본적인 내용을 정리했다.</p>
</blockquote>
<h1 id="test">test</h1>
<p>사용 방법은 다음과 같다.</p>
<ul>
<li><code>test(title, body)</code></li>
<li><code>test(title, details, body)</code></li>
</ul>
<pre><code class="language-jsx">import { test, expect } from &#39;@playwright/test&#39;;

test(&#39;basic test&#39;, async ({ page }) =&gt; {
  await page.goto(&#39;https://playwright.dev/&#39;);
  // ...
});</code></pre>
<p>테스트 진행 후엔 <code>/playwright-report/index.html</code>에서 테스트 보고서를 확인할 수 있다.
아래는 <code>details</code>에 넣을 수 있는 두 가지 옵션이다. 테스트 보고서에 반영된다.</p>
<ul>
<li><code>@symbol</code>을 활용한 태그 지정<pre><code class="language-jsx">test(&#39;basic test&#39;, {
tag: &#39;@smoke&#39;,
}, async ({ page }) =&gt; {
await page.goto(&#39;https://playwright.dev/&#39;);
// ...
});
</code></pre>
</li>
</ul>
<p>test(&#39;another test @smoke&#39;, async ({ page }) =&gt; {
  await page.goto(&#39;<a href="https://playwright.dev/&#39;">https://playwright.dev/&#39;</a>);
  // ...
});</p>
<pre><code>![](https://velog.velcdn.com/images/aug8_/post/b32316dc-1b19-485b-a5df-89e806699827/image.PNG)

- `annotation`를 활용한 주석 달기

```jsx
test(&#39;basic test&#39;, {
  annotation: {
    type: &#39;issue&#39;,
    description: &#39;https://github.com/microsoft/playwright/issues/23180&#39;,
  },
}, async ({ page }) =&gt; {
  await page.goto(&#39;https://playwright.dev/&#39;);
  // ...
});</code></pre><p><img src="https://velog.velcdn.com/images/aug8_/post/6263af1d-fa0b-41b2-9bf9-fcc90ae1bd2f/image.PNG" alt=""></p>
<h2 id="afterall-aftereach">afterAll, afterEach</h2>
<p><code>afterAll</code> 훅은 파일의 모든 테스트가 끝난 후에 실행되고, <code>afterEach</code> 훅은 각 테스트가 끝날 때마다 실행된다. <code>title</code>로 제목을 지정해줄 수도 있다.</p>
<pre><code class="language-jsx">test.afterAll(async () =&gt; {
  console.log(&#39;Done with tests&#39;);
  // ...
});

test.afterEach(&#39;Status check&#39;, async ({ page }) =&gt; {
  if (test.info().status !== test.info().expectedStatus)
    console.log(`Did not run as expected, ended up at ${page.url()}`);
});</code></pre>
<h2 id="beforeall-beforeeach">beforeAll, beforeEach</h2>
<p><code>beforeAll</code> 훅은 모든 테스트 전에 한 번 실행되고, <code>beforeEach</code> 훅은 각 테스트가 시작하기 전에 실행된다. <code>title</code>로 제목을 지정해줄 수도 있다.</p>
<pre><code class="language-jsx">test.beforeAll(&#39;Setup&#39;, async () =&gt; {
  console.log(&#39;Before tests&#39;);
});

test.beforeEach(&#39;Open start URL&#39;, async ({ page }) =&gt; {
  console.log(`Running ${test.info().title}`);
  await page.goto(&#39;https://my.start.url/&#39;);
});</code></pre>
<h2 id="describe">describe</h2>
<p><code>describe</code>는 테스트의 그룹을 선언한다. <code>title</code> 없이 선언하는 것도 가능하며, <code>details</code>로 태그나 주석을 달 수도 있다.</p>
<pre><code class="language-jsx">test.describe(&#39;two tagged tests&#39;, {
  tag: &#39;@smoke&#39;,
}, () =&gt; {
  test(&#39;one&#39;, async ({ page }) =&gt; {
    // ...
  });

  test(&#39;two&#39;, async ({ page }) =&gt; {
    // ...
  });
});</code></pre>
<h3 id="describeconfigure">describe.configure</h3>
<p><code>describe.configure</code>를 통해 테스트 실행 방식을 지정할 수 있다.
<code>mode</code>엔 <code>default</code>, <code>parallel</code>, <code>serial</code>가 있다.</p>
<pre><code class="language-jsx">test.describe.configure({ mode: &#39;parallel&#39; });

test.describe(&#39;A, runs in parallel with B&#39;, () =&gt; {
  test.describe.configure({ mode: &#39;default&#39; });
  // ...
});

test.describe(&#39;B, runs in parallel with A&#39;, () =&gt; {
  test.describe.configure({ mode: &#39;default&#39; });
  // ...
});</code></pre>
<p><code>mode</code>에 따라, 위 코드에서 A와 B는 병렬로 실행되지만, A와 B 내부의 테스트는 순서대로 실행될 것이다.</p>
<h2 id="settimeout">setTimeout</h2>
<p><code>setTimeout</code>을 통해 테스트의 최대 실행시간을 설정할 수 있다. <code>beforeAll</code>이나 <code>afterAll</code>과 함께 사용하여 테스트가 아닌 훅의 타임아웃에 영향을 미칠 수도 있다.</p>
<pre><code class="language-jsx">test(&#39;very slow test&#39;, async ({ page }) =&gt; {
  test.setTimeout(120000);
  // ...
});

test.beforeAll(async () =&gt; {
  // Set timeout for this hook.
  test.setTimeout(60000);
});</code></pre>
<h2 id="skip-fixme">skip, fixme</h2>
<p><code>skip</code>과 <code>fixme</code>는 해당 테스트를 실행하지 않고 건너뛰게 한다.</p>
<pre><code class="language-jsx">test.skip(&#39;never run&#39;, async ({ page }) =&gt; {
  // ...
});

test.fixme(&#39;to be fixed&#39;, async ({ page }) =&gt; {
  // ...
});

test.describe.fixme(() =&gt; {
  // ...
});</code></pre>
<h1 id="page">Page</h1>
<p>페이지와 상호작용하기 위한 메서드를 제공한다.</p>
<h2 id="goto-goback-goforward">goto, goBack, goForward</h2>
<p>해당 페이지로 이동한다.</p>
<pre><code class="language-jsx">await page.goto(&quot;https://playwright.dev/&quot;);
await page.goBack();
await page.goForward();</code></pre>
<h2 id="getbytext-getbyalttext-getbyplaceholder">getByText, getByAltText, getByPlaceholder</h2>
<p>해당 텍스트가 포함된 요소를 찾는다. 이외에도 여러 메서드가 있다.</p>
<pre><code class="language-js">&lt;div&gt;Hello world&lt;/div&gt;
&lt;img alt=&#39;Playwright logo&#39;&gt;
&lt;input type=&quot;email&quot; placeholder=&quot;name@example.com&quot; /&gt;

await page.getByText(&#39;Hello world&#39;);
await page.getByAltText(&#39;Playwright logo&#39;).click();
await page
    .getByPlaceholder(&#39;name@example.com&#39;)
    .fill(&#39;playwright@microsoft.com&#39;);</code></pre>
<p>더 다양한 방법은 <a href="https://playwright.dev/docs/locators">Locators</a>에서 확인하자.</p>
<h2 id="keyboard">Keyboard</h2>
<pre><code class="language-js">await page.keyboard.type(&#39;Hello World!&#39;);
await page.keyboard.press(&#39;ArrowLeft&#39;);</code></pre>
<h1 id="expect">expect</h1>
<p>테스트가 성공했는지 검증을 위해 사용한다.</p>
<pre><code class="language-jsx">test(&quot;has title&quot;, async ({ page }) =&gt; {
  await page.goto(&quot;https://playwright.dev/&quot;);
  await expect(page).toHaveTitle(/Playwright/);
});</code></pre>
<br>
<br>

<h1 id="참고">참고</h1>
<ul>
<li><a href="https://soojae.tistory.com/74">프론트엔드 테스트 - TDD와 종류(Unit, Integration, E2E)</a></li>
<li><a href="https://emewjin.github.io/playwright-vs-cypress/">playwright는 진짜 cypress보다 빠를까?</a></li>
<li><a href="https://velog.io/@heelieben/E2E-%ED%85%8C%EC%8A%A4%ED%8A%B8-PlayWright-2eub88ik">E2E 테스트 : Playwright</a></li>
<li><a href="https://playwright.dev/">Playwright</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Hooks]]></title>
            <link>https://velog.io/@aug8_/React-Hooks</link>
            <guid>https://velog.io/@aug8_/React-Hooks</guid>
            <pubDate>Sun, 21 Jul 2024 00:18:53 GMT</pubDate>
            <description><![CDATA[<h2 id="hook">Hook</h2>
<p>React Hooks은 <code>use</code>로 시작되는 함수이다. React 16.8에서 추가되었으며, 클래스 컴포넌트에서 사용하던 여러 기능을 함수형 컴포넌트에서도 사용할 수 있게 만들어 준다. </p>
<h3 id="hook의-규칙">Hook의 규칙</h3>
<p>Hook은 호출 위치에 제약이 있다.</p>
<h4 id="최상위-레벨에서만-호출한다">최상위 레벨에서만 호출한다</h4>
<ul>
<li>조건문이나 반복문 내부에선 호출하지 않는다.</li>
<li><code>return</code>문 이후에 호출하지 않는다.</li>
<li>이벤트 핸들러에서 호출하지 않는다.</li>
<li>클래스 컴포넌트에서 호출하지 않는다.</li>
<li><code>useMemo</code>, <code>useReducer</code>, <code>useEffect</code>에 전달된 함수 내부에서 호출하지 않는다.</li>
<li><code>try</code>/<code>catch</code>/<code>finally</code> 블록 내부에서 호출하지 않는다.</li>
</ul>
<h4 id="react-함수-내에서만-호출한다">React 함수 내에서만 호출한다</h4>
<ul>
<li>일반적인 JavaScript 함수에서는 호출하지 않는다.</li>
<li>함수형 컴포넌트나 커스텀 Hook에서는 가능하다.<pre><code class="language-jsx">// 아래는 불가능하다.
function setOnlineStatus() {
const [onlineStatus, setOnlineStatus] = useOnlineStatus();
}</code></pre>
</li>
</ul>
<h2 id="usestate">useState</h2>
<p><code>useState</code>는 상태를 관리하는 훅이다. 초기 상태 값과 상태를 업데이트하는 함수를 반환한다. 업데이트를 통해 상태를 변경할 수 있으며, 이 변경은 컴포넌트를 재렌더링한다.</p>
<pre><code class="language-jsx">function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }
  // ...</code></pre>
<h2 id="setreducer">setReducer</h2>
<p><code>setReducer</code>는 더 복잡한 상태 관리를 위해 사용되는 훅이다. <code>useState</code>와 유사하게 상태와 상태 업데이트 함수를 반환하지만, 상태 업데이트 로직이 별도의 <code>reducer</code> 함수에서 처리되기 때문에 업데이트 로직이 복잡할 수록 유용하다.</p>
<pre><code class="language-jsx">import { useReducer } from &#39;react&#39;;

function reducer(state, action) {
  switch (action.type) {
    case &#39;incremented_age&#39;: {
      return {
        name: state.name,
        age: state.age + 1
      };
    }
    case &#39;changed_name&#39;: {
      return {
        name: action.nextName,
        age: state.age
      };
    }
  }
  throw Error(&#39;Unknown action: &#39; + action.type);
}

export default function Counter() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });

  return (
    &lt;&gt;
      &lt;button onClick={() =&gt; {
        dispatch({ type: &#39;incremented_age&#39; })
      }}&gt;
        Increment age
      &lt;/button&gt;
      &lt;p&gt;Hello! You are {state.age}.&lt;/p&gt;
    &lt;/&gt;
  );
}</code></pre>
<h2 id="usecontext">useContext</h2>
<p><code>useContext</code>는 컴포넌트 트리 전체에 데이터를 제공하는 context를 사용하게 해 주는 훅이다. 전역적인 데이터를 전달하기 좋지만, 컴포넌트를 재사용하기 어려워지기 때문에 꼭 필요할 때만 사용하자.</p>
<pre><code class="language-jsx">const ThemeContext = createContext(&#39;light&#39;);

export default function MyApp() {
  const [theme, setTheme] = useState(&#39;light&#39;);
  return (
    &lt;&gt;
      &lt;ThemeContext.Provider value={theme}&gt;
        &lt;Form /&gt;
      &lt;/ThemeContext.Provider&gt;
      &lt;Button onClick={() =&gt; {
        setTheme(theme === &#39;dark&#39; ? &#39;light&#39; : &#39;dark&#39;);}} /&gt;
    &lt;/&gt;
  )
}

function Form({ children }) {
  const theme = useContext(ThemeContext);
  return (
    //...</code></pre>
<h2 id="useref">useRef</h2>
<p><code>useRef</code>는 렌더링에 필요하지 않은 값을 참조할 수 있는 훅이다. ref 변경은 재렌더링을 유발하지 않기 때문에, 시각적 출력에 영향을 미치지 않는 정보를 저장하는데 적합하다.</p>
<pre><code class="language-jsx">  let ref = useRef(0);

  function handleClick() {
    ref.current = ref.current + 1;
  }</code></pre>
<h2 id="useid">useId</h2>
<p><code>useId</code>는 고유한 ID를 생성하는 훅이다. React에서 ID를 직접 코드에 입력하는 건 좋은 사례가 아니다. 몇 번이고 렌더링되는 동안 ID는 고유해야 한다.</p>
<pre><code class="language-jsx">function PasswordField() {
  const passwordHintId = useId();
  return (
    &lt;&gt;
      &lt;label&gt;
        Password:
        &lt;input
          type=&quot;password&quot;
          aria-describedby={passwordHintId}
        /&gt;
      &lt;/label&gt;
      &lt;p id={passwordHintId}&gt;
        The password should contain at least 18 characters
      &lt;/p&gt;
    &lt;/&gt;
  );
}</code></pre>
<h2 id="useeffect">useEffect</h2>
<p><code>useEffect</code>는 컴포넌트가 리렌더링 될 때마다 특정 작업을 수행할 수 있게 하는 훅이다. 의존성 배열을 사용해 특정 객체의 상태 값이 변할 때마다 리렌더링 시킬 수 있다.</p>
<pre><code class="language-jsx">import { useEffect } from &#39;react&#39;;
import { createConnection } from &#39;./chat.js&#39;;

function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState(&#39;https://localhost:1234&#39;);

  useEffect(() =&gt; {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () =&gt; {
      connection.disconnect();
    };
  }, [serverUrl, roomId]);
  // ...
}</code></pre>
<h2 id="usememo">useMemo</h2>
<p><code>useMemo</code>는 재렌더링 사이에 계산 결과를 캐싱하는 훅이다. 성능 개선을 위해 사용한다. 비용이 많이 드는 계산을 수행하는 경우, 데이터가 변경되지 않았다면 계산을 생략하는 게 더 효율적이기 때문이다. 이것을 메모이제이션이라고 부른다.</p>
<pre><code class="language-jsx">  const visibleTodos = useMemo(() =&gt; filterTodos(todos, tab), [todos, tab]);
  // ...
}</code></pre>
<ul>
<li><code>() =&gt; filterTodos(todos, tab)</code>는 계산 함수이다.</li>
<li><code>[todos, tab]</code>는 종속성 목록이다. 종속성이 변경되지 않았다면 이전에 이미 계산해둔 값을 반환하고, 변경되었다면 다시 연산한다.</li>
</ul>
<h2 id="usecallback">useCallback</h2>
<p><code>useCallback</code>은 재렌더링 사이에 함수를 캐싱하는 훅이다. 성능 개선을 위해 사용한다.</p>
<pre><code class="language-jsx">function ProductPage({ productId, referrer, theme }) {
  function handleSubmit(orderDetails) {
    post(&#39;/product/&#39; + productId + &#39;/buy&#39;, {
      referrer,
      orderDetails,
    });
  }

  return (
    &lt;div className={theme}&gt;
      &lt;ShippingForm onSubmit={handleSubmit} /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>재렌더링 된 함수는 동일하지 않다. 같은 내용이라도 메모리 주소가 다르면 다른 함수로 간주하기 때문이다.</p>
<p>위의 코드의 경우, <code>theme</code>가 변경되면 재렌더링 되어 <code>handleSubmit</code>이 다른 함수가 된다. 때문에 <code>&lt;ShippingForm&gt;</code>도 불필요한 재렌더링을 피할 수 없게 된다.</p>
<pre><code class="language-jsx">function ProductPage({ productId, referrer, theme }) {
  const handleSubmit = useCallback((orderDetails) =&gt; {
    post(&#39;/product/&#39; + productId + &#39;/buy&#39;, {
      referrer,
      orderDetails,
    });
  }, [productId, referrer]);

  return (
    &lt;div className={theme}&gt;
      &lt;ShippingForm onSubmit={handleSubmit} /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p><code>useCallback</code>을 이용해 함수를 캐싱하면 불필요한 재렌더링을 피할 수 있다. <code>handleSubmit</code>이 변경되지 않기 때문에 <code>&lt;ShippingForm&gt;</code>이 재랜더링되지 않는다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ko.react.dev/reference/react/hooks">Hooks</a></li>
<li><a href="https://ko.react.dev/reference/rules/rules-of-hooks">Hook의 규칙</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Suspense]]></title>
            <link>https://velog.io/@aug8_/Suspense</link>
            <guid>https://velog.io/@aug8_/Suspense</guid>
            <pubDate>Fri, 12 Jul 2024 11:38:18 GMT</pubDate>
            <description><![CDATA[<h2 id="suspense">&lt; Suspense &gt;</h2>
<p><code>&lt;Suspense&gt;</code>는 컴포넌트 렌더링을 지연시키고 대체 UI를 보여주는 기능이다. 비동기 작업을 처리할 때 유용하게 사용할 수 있으며, 주로 데이터 로딩이나 코드 스플리팅 같은 작업에 사용된다.</p>
<ul>
<li><p>로딩으로 인한 딜레이 중 사용자 경험을 개선할 수 있다. 빈 화면 대신 로딩 화면을 보여주며 사용자가 멈춤 현상을 경험하지 않게 한다.</p>
</li>
<li><p>지연을 이용해 초기에 필요한 컴포넌트만 로드하여 초기 로딩 속도를 개선할 수 있다.</p>
</li>
</ul>
<pre><code class="language-jsx">&lt;Suspense fallback={&lt;Loading /&gt;}&gt;
  &lt;SomeComponent /&gt;
&lt;/Suspense&gt;</code></pre>
<ul>
<li><p><code>children</code> : 렌더링하려는 실제 UI이다. <code>children</code>의 렌더링이 지연될 때, Suspense는 <code>fallback</code>을 대신 렌더링한다.</p>
</li>
<li><p><code>fallback</code> : 대체 UI이다. <code>children</code>의 렌더링이 지연될 때 대신 렌더링되다가, 데이터가 준비됐을 때 <code>children</code>으로 다시 전환된다. 만약 <code>fallback</code>의 렌더링이 지연된다면 가장 가까운 부모 Suspense가 렌더링된다.</p>
</li>
</ul>
<h2 id="사용법">사용법</h2>
<pre><code class="language-jsx">import { Suspense } from &#39;react&#39;;
import Albums from &#39;./Albums.js&#39;;

export default function ArtistPage({ artist }) {
  return (
    &lt;&gt;
      &lt;h1&gt;{artist.name}&lt;/h1&gt;
      &lt;Suspense fallback={&lt;Loading /&gt;}&gt;
        &lt;Albums artistId={artist.id} /&gt;
      &lt;/Suspense&gt;
    &lt;/&gt;
  );
}

function Loading() {
  return &lt;h2&gt;Loading...&lt;/h2&gt;;
}</code></pre>
<p>위 코드는 앨범 데이터가 로딩되는 동안 <code>Loading</code> 컴포넌트를 표시한다. 로드가 끝났을 땐 <code>Loading</code>을 숨기고 다시 <code>Albums</code> 컴포넌트를 렌더링한다.</p>
<pre><code class="language-jsx">&lt;Suspense fallback={&lt;BigSpinner /&gt;}&gt;
  &lt;Biography /&gt;
  &lt;Suspense fallback={&lt;AlbumsGlimmer /&gt;}&gt;
    &lt;Panel&gt;
      &lt;Albums /&gt;
    &lt;/Panel&gt;
  &lt;/Suspense&gt;
&lt;/Suspense&gt;</code></pre>
<p>여러 Suspense를 중첩하여 로딩 순서를 만들 수도 있다. </p>
<ul>
<li><code>Biography</code>가 로드되지 않은 경우, <code>BigSpinner</code>가 표시된다.</li>
<li><code>Albums</code>가 로드되지 않은 경우, <code>AlbumsGlimmer</code>가 표시된다.</li>
</ul>
<h3 id="usedeferredvalue">useDeferredValue</h3>
<ul>
<li><p><code>useDefferredValue</code>는 사용자가 빠르게 입력을 변경할 때, 결과가 즉시 업데이트되지 않고 약간 지연되도록 도와준다. 사용자 경험을 개선하고 불필요한 렌더링을 줄이는데 도움이 된다.</p>
</li>
<li><p>지연 시간은 고정되어 있지 않으며, React에 여유가 있을 때 업데이트된다. 브라우저의 현재 상태나 작업 부하에 따라 달라질 수 있다.</p>
</li>
</ul>
<p><code>useDeferredValue</code>를 사용하면 쿼리의 지연된 버전을 아래로 전달할 수 있다.</p>
<pre><code class="language-jsx">import { Suspense, useState, useDeferredValue } from &#39;react&#39;;
import SearchResults from &#39;./SearchResults.js&#39;;

export default function App() {
  const [query, setQuery] = useState(&#39;&#39;);
  const deferredQuery = useDeferredValue(query);
  const isStale = query !== deferredQuery;
  return (
    &lt;&gt;
      &lt;label&gt;
        Search albums:
        &lt;input value={query} onChange={e =&gt; setQuery(e.target.value)} /&gt;
      &lt;/label&gt;
      &lt;Suspense fallback={&lt;h2&gt;Loading...&lt;/h2&gt;}&gt;
        &lt;div style={{ opacity: isStale ? 0.5 : 1 }}&gt;
          &lt;SearchResults query={deferredQuery} /&gt;
        &lt;/div&gt;
      &lt;/Suspense&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>위 예제는 검색 결과를 가져오는 동안 <code>SearchResult</code>가 지연되며, 지연되는 동안 이전 검색 결과를 계속해서 보여준다.</p>
<p>예를 들어 <code>a</code>를 검색하고 <code>ab</code>를 검색하면, <code>ab</code>의 결과를 가져오는 동안 <code>a</code>의 검색 결과를 투명도 50%로 보여준다.</p>
<h3 id="lazy">lazy</h3>
<p><code>lazy</code>는 컴포넌트가 렌더링 될 때까지 로딩을 연기할 수 있다.</p>
<pre><code class="language-jsx">import { useState, Suspense, lazy } from &#39;react&#39;;
import Loading from &#39;./Loading.js&#39;;

const MarkdownPreview = lazy(() =&gt; delayForDemo(import(&#39;./MarkdownPreview.js&#39;)));

export default function MarkdownEditor() {
  const [showPreview, setShowPreview] = useState(false);
  const [markdown, setMarkdown] = useState(&#39;Hello, **world**!&#39;);
  return (
    &lt;&gt;
      &lt;textarea value={markdown} onChange={e =&gt; setMarkdown(e.target.value)} /&gt;
      &lt;label&gt;
        &lt;input type=&quot;checkbox&quot; checked={showPreview} onChange={e =&gt; setShowPreview(e.target.checked)} /&gt;
        Show preview
      &lt;/label&gt;

      {showPreview &amp;&amp; (
        &lt;Suspense fallback={&lt;Loading /&gt;}&gt;
          &lt;h2&gt;Preview&lt;/h2&gt;
          &lt;MarkdownPreview markdown={markdown} /&gt;
        &lt;/Suspense&gt;
      )}
    &lt;/&gt;
  );
}</code></pre>
<p>위 예제는 <code>checkbox</code>가 체크되기 전까지 <code>MarkdownPreview</code>가 나타나지 않기 때문에, <code>lazy</code>를 이용해 지연시켰다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ko.react.dev/reference/react/Suspense">Suspense</a></li>
<li><a href="https://ko.react.dev/reference/react/lazy">lazy</a></li>
<li><a href="https://www.elancer.co.kr/blog/view?seq=267">React Suspense: 리액트 서스펜스를 사용하는 이유와 사용법 총정리</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[string과 String]]></title>
            <link>https://velog.io/@aug8_/string%EA%B3%BC-String</link>
            <guid>https://velog.io/@aug8_/string%EA%B3%BC-String</guid>
            <pubDate>Sun, 07 Jul 2024 00:22:27 GMT</pubDate>
            <description><![CDATA[<pre><code>&#39;String&#39; 형식은 &#39;string&#39; 형식에 할당할 수 없습니다.
 string&#39;은(는) 기본 개체이지만 &#39;String&#39;은(는) 래퍼 개체입니다. 가능한 경우 &#39;string&#39;을(를) 사용하세요.</code></pre><h2 id="string과-string은-다른가">String과 string은 다른가?</h2>
<p>결론은: 다르다!
타입스크립트는 <code>string</code>과 <code>String</code> 타입을 구분한다.</p>
<h3 id="primitive-type">Primitive Type</h3>
<p>원시 타입(Primitive Type)은 가장 기본적인 데이터 타입이다. 자바스크립트에는 <code>number</code>, <code>bigint</code>, <code>boolean</code>, <code>null</code>, <code>undefined</code>, <code>symbol</code>이 있다. 메서드를 가지지 않는다는 특징이 있다.</p>
<pre><code>let str: string = &quot;Hello&quot;;
console.log(str.toUpperCase());
// 자바스크립트가 임시로 String 객체를 생성하여 메서드 호출한다.</code></pre><h3 id="reference-type">Reference Type</h3>
<p>참조 타입(Reference Type)은 복합적인 데이터 구조를 가진다. 실제 값을 저장하지 않고, 값이 저장된 메모리 주소를 저장한다. <code>array</code>, <code>function</code>, <code>object</code> 등이 있으며, 동일한 객체를 여러 변수에서 참조할 수 있다. 원시 타입과 달리 메서드를 가질 수 있다.</p>
<p>래퍼 객체(Wrapper Object)는 원시 값을 객체처럼 다룰 수 있게 해주는 객체 타입이다. 자바스크립트에서는 각 원시 타입에 대응하는 래퍼 객체가 있다.</p>
<ul>
<li><code>string</code> -&gt; <code>String</code></li>
<li><code>number</code> -&gt; <code>Number</code></li>
<li><code>boolean</code> -&gt; <code>Boolean</code></li>
</ul>
<pre><code>let primitiveString: string = &quot;Hello&quot;; // 원시 타입
let wrappedString: String = new String(&quot;Hello&quot;); // 래퍼 객체

console.log(typeof primitiveString); // &quot;string&quot;
console.log(typeof wrappedString); // &quot;object&quot;

console.log(primitiveString === &quot;Hello&quot;); // true
console.log(wrappedString === &quot;Hello&quot;); // false
console.log(wrappedString == &quot;Hello&quot;); // true (타입 강제 변환)

console.log(primitiveString.length); // 5
console.log(wrappedString.length); // 5</code></pre><h3 id="string">string</h3>
<p><code>string</code>은 기본 문자열 타입이다. 원시 타입(primitive type)으로, 문자열 값을 나타낸다.</p>
<pre><code>let myString: string = &quot;Hello&quot;;</code></pre><h3 id="string-1">String</h3>
<p><code>String</code>은 객체 타입이다. 문자열 값을 감싸는 래퍼 객체이기도 하다.</p>
<pre><code>let myString: String = new String(&quot;Hello&quot;);</code></pre><h2 id="결론">결론</h2>
<p><code>string</code>이 더 가볍고 성능 면에서 유리하다. <code>String</code> 객체는 불필요한 오버헤드를 추가할 수 있기 때문에 단순히 값을 저장할 때는 적합하지 않다. 특별한 이유가 없다면 <code>string</code>을 사용하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Lazy Loading]]></title>
            <link>https://velog.io/@aug8_/Lazy-Loading</link>
            <guid>https://velog.io/@aug8_/Lazy-Loading</guid>
            <pubDate>Sun, 07 Jul 2024 00:12:56 GMT</pubDate>
            <description><![CDATA[<h2 id="lazy-loading">Lazy Loading</h2>
<p>사용자가 실제로 필요한 콘텐츠만 요청하고 로드하는 방식이다. 페이지가 처음 로드될 때는 필수적인 콘텐츠만 로드하고, 추가적인 콘텐츠를 요청할 때 해당하는 콘텐츠를 로드한다.</p>
<p>예를 들어, 웹 페이지에 많은 이미지가 포함되어 있다면 처음에는 화면에 보이는 이미지만 로드하고, 사용자가 스크롤을 내렸을 때 나머지 이미지를 로드하는 방식이다.</p>
<h3 id="장점">장점</h3>
<ul>
<li>초기 페이지 로드 속도 향상</li>
<li>전체 데이터 전송량 감소</li>
<li>메모리 사용량 최적화</li>
</ul>
<p>Lazy Loading을 이용하면 필요한 콘텐츠 수를 줄일 수 있다. 다운로드 bytes를 줄이기 때문에 유저가 더 빠르게 페이지를 확인할 수 있게 한다.</p>
<p>콘텐츠가 필요하지 않을 땐 로딩하지 않으므로, 페이지를 모두 확인하지 않거나 상단 서비스만 이용하는 유저에게 특히 효과적이다. 불필요한 로딩이 없어 더 부드러운 전환이 가능하다.</p>
<p>전체 데이터 전송량이 줄어들어 사용자의 대역폭 활용도가 향상되며, 특히 모바일 환경에서 데이터 사용량이 절감되기 때문에 사용자 경험을 개선할 수 있다.</p>
<h3 id="활용-방법">활용 방법</h3>
<ul>
<li><code>스크롤 이벤트 기반</code> : 사용자가 페이지를 스크롤할 때 화면에 보이는 영역의 콘텐츠만 로드하는 방식</li>
<li><code>IntersectionObserver API</code> : 화면에 보이는 영역의 콘텐츠를 API를 활용해 감지하고 로드하는 방식</li>
<li><code>사용자 상호작용 기반</code> : 사용자의 클릭, 마우스 오버 등의 상호작용을 감지하여 해당 영역의 콘텐츠를 로드하는 방식</li>
<li><code>데이터 속성 활용</code> : HTML 요소에 data-* 속성을 활용하여 lazy loading 대상 리소스 정보를 저장하는 방식</li>
<li><code>라이브러리 활용</code> : React의 Suspense, Vue의 Lazy Loading 등 프레임워크에서 제공하는 기능을 활용하는 방식</li>
<li><code>Thumbnail</code> : 이미지의 크기와 품질을 낮춘 Thumbnail 이미지를 먼저 로드하는 방식</li>
<li><code>캐싱 활용</code> : 자주 변경되지 않는 리소스들을 브라우저 캐시에 저장하여 재사용하는 방식</li>
<li><code>포맷 최적화</code> : WebP, AVIF 등의 최신 이미지 포맷을 사용하는 방식</li>
</ul>
<h2 id="실전-기술">실전 기술</h2>
<h3 id="img-태그-사용하기"><img> 태그 사용하기</h3>
<p><code>&lt;img&gt;</code> 태그에서 이미지를 로드하기 위해, 브라우저는 <code>src</code> 속성을 사용하게 된다. </p>
<pre><code class="language-html">&lt;img data-src=&quot;https://test.com/main.jpg&quot; /&gt;</code></pre>
<p>예제는 이미지의 실제 URL을 <code>data-src</code>에 넣어두고, <code>src</code>는 비워두었다. 이렇게 하면 브라우저가 페이지를 처음 로드할 때 이미지를 로드하지 않는다. 이후 필요할 때 JavaScript를 사용해 <code>data-src</code> 속성의 값을 <code>src</code> 속성으로 복사하여 이미지를 로드할 수 있다.</p>
<h3 id="intersection-observer-api">Intersection Observer API</h3>
<p>우선 이미지 로드를 지연시키기 위해 모든 이미지에 옵저버를 부착시킨다. 엘리먼트가 뷰포트에 들어간 걸 API가 감지했을 때, URL을 <code>data-src</code> 속성에서 <code>src</code> 속성으로 이동시켜서 브라우저가 이미지를 로드하도록 한다. 전부 로드됐을 땐 부착했던 옵저버를 제거한다.</p>
<pre><code class="language-js">import React, { useEffect, useRef } from &#39;react&#39;;

function Example() {
  const targetRef = useRef(null);

  const handleIntersection = (entries) =&gt; {
    entries.forEach((entry) =&gt; {
      // 타겟 요소가 화면에 보이면 콘솔에 메시지 출력
      if (entry.isIntersecting) {
        console.log(&#39;Target element is visible!&#39;);
      } else {
        console.log(&#39;Target element is not visible.&#39;);
      }
    });
  };

  useEffect(() =&gt; {
    // 옵저버 생성
    const observer = new IntersectionObserver(handleIntersection, {
      // 뷰포트 기준 100% 이상 보이면 감지
      threshold: 1.0,
    });

    // 타겟 요소에 옵저버 연결
    if (targetRef.current) {
      observer.observe(targetRef.current);
    }

    // 컴포넌트 언마운트 시 옵저버 연결 해제
    return () =&gt; {
      if (targetRef.current) {
        observer.unobserve(targetRef.current);
      }
    };
  }, []);

  return (
    &lt;div&gt;
      &lt;div ref={targetRef}&gt;
        This is the target element that will be observed.
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Example;</code></pre>
<h3 id="chrome-loading">Chrome loading</h3>
<p>크롬 브라우저는 lazy loading 기능을 지원한다. <code>&lt;img&gt;</code>, <code>&lt;iframe&gt;</code>, <code>&lt;video&gt;</code> 등의 태그에 loading=&quot;lazy&quot; 속성을 추가하면 해당 리소스가 필요할 때까지 로드되지 않는다.</p>
<pre><code class="language-html">&lt;img src=&quot;example.jpg&quot; loading=&quot;lazy&quot; /&gt;</code></pre>
<ul>
<li><code>lazy</code> : 뷰포트에서 일정한 거리에 닿을 때까지 로딩을 지연한다.</li>
<li><code>eager</code> : 페이지 위치와 상관없이 페이지가 로딩되자마자 로드한다.</li>
</ul>
<p><img src="https://lh6.googleusercontent.com/i2UfbjUx9E4OFGc6lqmH_qF3RwKw5yKu3VecpcdDM0znXUGFCSmHAx8IHcGi_NXOfCPVny7wmAmgXl1k5i3Nacm7gg4hbe22KFog8f4Yf-g1uJLb_pq6hSC0c_3qL_1EvWwf7ETQ" alt=""></p>
<h2 id="placeholder">placeholder</h2>
<p>lazy loading을 사용하면 모든 콘텐츠가 로드되지 않아 보기 좋지 않을 수 있다. 여기 사용자 경험을 개선하기 위한 다양한 방법이 있다.
아래 두 가지 방법은 모두 <code>ImageKit</code>을 사용해 간단하게 구현할 수 있다.</p>
<h3 id="이미지와-비슷한-색상">이미지와 비슷한 색상</h3>
<p>이미지를 1x1 픽셀로 감소시키고 해당 픽셀의 색으로 placeholder를 채우는 방식이다.</p>
<p><img src="https://ik.imagekit.io/demo/img/pinterest-placeholders.gif" alt=""></p>
<h3 id="저화질-이미지">저화질 이미지</h3>
<p>저화질의 흐린 이미지를 placeholder로 사용하여 사용자가 이미지를 추측할 수 있게 한다.
<img src="https://i0.wp.com/css-tricks.com/wp-content/uploads/2018/09/lazy-featured-1.jpg?fit=1200%2C600&ssl=1" alt=""></p>
<h2 id="주의할-점">주의할 점</h2>
<p>모든 콘텐츠를 lazyloading하지 않도록 주의하자. 로딩은 줄어들 수 있지만, 사용자 경험을 떨어뜨릴 위험이 있다. 웹 페이지가 시작되자마자 보이는 상단(뷰포트) 내의 콘텐츠들은 페이지가 로딩되자마자 보여야 하므로 lazy loading에 적합하지 않다. 디바이스마다 화면 크기가 다르기 때문에, 초기 화면에 보이는 콘텐츠 수가 다르다는 것도 고려해야 한다. 뷰포트에서 조금 떨어진 콘텐츠들도 lazy loading의 대상이 아니다. 한두 번의 스크롤로 확인할 수 있는 내용은 처음부터 로드하는 것이 좋다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://helloinyong.tistory.com/297">웹 성능 최적화를 위한 Image Lazy Loading 기법</a></li>
<li><a href="https://velog.io/@js4072751/Lazy-Loading-%EA%B8%B0%EB%B2%95%EC%9D%98-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%ED%99%9C%EC%9A%A9-%EB%B0%A9%EB%B2%95">Lazy Loading 기법의 효과적인 활용 방법</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React State 정리]]></title>
            <link>https://velog.io/@aug8_/React-State-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@aug8_/React-State-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Fri, 28 Jun 2024 12:01:48 GMT</pubDate>
            <description><![CDATA[<p><a href="https://react.dev/learn/state-a-components-memory">React 공식문서</a>를 번역 정리한 내용입니다.</p>
<h1 id="state-a-components-memory">State: A Component&#39;s Memory</h1>
<p>컴포넌트는 상호작용의 결과로 화면에 표시되는 내용을 변경해야 한다. 폼에 입력하면 입력 필드가 업데이트되어야 하고, 다음 이미지로 넘어가는 버튼을 클릭하면 표시되는 이미지가 변경되어야 하며, 구매 버튼을 클릭하면 장바구니에 제품이 담겨야 한다. 컴포넌트는 현재 입력값, 현재 이미지, 장바구니 같은 것들을 &quot;기억&quot;해야 한다. React는 이와 같은 컴포넌트 특유의 메모리를 state라고 부른다.</p>
<h2 id="지역-변수의-한계">지역 변수의 한계</h2>
<p>아래 코드는 Next를 클릭하면 index가 변경되도록 설계되었지만, <strong>작동하지 않는다.</strong></p>
<pre><code class="language-js">export default function Gallery() {
  let index = 0;

  function handleClick() {
    index = index + 1;
  }

  let sculpture = sculptureList[index];
  return (
    &lt;&gt;
      &lt;button onClick={handleClick}&gt;
        Next
      &lt;/button&gt;
      ({index + 1} of {sculptureList.length})
      // 예시: (1 of 12)
    &lt;/&gt;
  );
}</code></pre>
<p><code>handleClick</code> 이벤트 핸들러가 지역 변수 <code>index</code>를 업데이트한다. 하지만 다음과 같은 이유 때문에 변경 사항이 화면에 반영되지 않는다.</p>
<ul>
<li><p><strong>지역 변수는 렌더링 사이에 유지되지 않는다.</strong> React가 이 컴포넌트를 두 번째로 렌더링할 때, 지역 변수의 변경 사항을 기억하지 않고 초기화한다.</p>
</li>
<li><p><strong>지역 변수의 변경은 렌더링을 발생시키지 않는다.</strong> React는 새로운 데이터 때문에 컴포넌트를 다시 렌더링해야 할 필요가 있다는 것을 인식하지 못한다.</p>
</li>
</ul>
<h2 id="usestate-분석">useState 분석</h2>
<pre><code class="language-js">const [index, setIndex] = useState(0);</code></pre>
<p><code>index</code>는 state 변수이고, <code>setIndex</code>는 설정자 함수이다.</p>
<p><code>useState</code>의 유일한 인수는 state 변수의 <strong>초기값</strong>으로, 예제에서는 <code>0</code>으로 설정되어 있다.</p>
<p>컴포넌트가 렌더링될 때마다 <code>useState</code>는 두 개의 값을 포함하는 배열을 반환한다.</p>
<ul>
<li>저장된 값을 가진 상태 변수 <code>index</code></li>
<li>상태 변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 트리거할 수 있는 상태 변경 함수 <code>setIndex</code></li>
</ul>
<p>아래는 변수를 업데이트하는 예제이다.</p>
<pre><code class="language-js">function handleClick() {
  setIndex(index + 1);
}</code></pre>
<h3 id="실제-동작-과정">실제 동작 과정</h3>
<ol>
<li><p>컴포넌트가 처음 렌더링된다. <code>0</code>을 초기값으로 설정했기 때문에, <code>[0, setIndex]</code>가 반환된다. React는 <code>index</code>의 최신 상태 값이 0이라는 것을 기억한다.</p>
</li>
<li><p>사용자가 버튼을 클릭하면 <code>setIndex(index + 1)</code>가 호출된다. 이 경우엔 <code>Index(1)</code>이 호출될 것이다. 이는 React에게 <code>index</code>가 이제 <code>1</code>이라는 것을 알려주고, 렌더링을 트리거한다.</p>
</li>
<li><p>React는 여전히 <code>useState(0)</code>을 보지만, <code>index</code>가 <code>1</code>로 설정되었다는 것을 기억하므로 <code>[1, setIndex]</code>를 반환한다.</p>
</li>
<li><p>위 과정이 반복된다.</p>
</li>
</ol>
<h2 id="격리된-state">격리된 State</h2>
<p>State는 화면에 있는 컴포넌트 인스턴트에 국한된다. 다시 말해, <strong>동일한 컴포넌트를 두 번 렌더링하면 각 사본은 완전히 독립된 상태를 가진다.</strong> 그 중 하나를 변경해도 다른 것에는 영향을 미치지 않는다.</p>
<pre><code class="language-js">import Gallery from &#39;./Gallery.js&#39;;

export default function Page() {
  return (
    &lt;div className=&quot;Page&quot;&gt;
      &lt;Gallery /&gt;
      &lt;Gallery /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>위 예시는 동일한 <code>&lt;Gallery /&gt;</code> 컴포넌트를 두 번 렌더링하지만, 각각 독립적인 상태를 가진다.</p>
<p>State는 특정 함수 호출에 묶이지 않고, 코드의 특정 위치에 묶이지도 않지만, 화면의 특정 위치에 <strong>지역적(local)</strong>이다. 두 개의 컴포넌트를 렌더링했으므로 state는 별도로 저장된다.</p>
<p><code>Page</code> 컴포넌트는 <code>Gallery</code>의 상태나 상태의 존재 여부도 알지 못한다. props와 달리 state는 선언된 컴포넌트 외엔 완전한 비공개이며, 부모 컴포넌트는 이를 변경할 수 없다. 따라서 다른 컴포넌트에 영향 없이 state를 마음껏 추가하고 제거할 수 있다.</p>
<p>두 <code>Gallery</code>의 state를 동기화하는 방법은 자식 컴포넌트에서 state를 제거하고 가장 가까운 공유 부모 컴포넌트에 추가하는 것이다.</p>
<hr>
<h1 id="choosing-the-state-structure">Choosing the State Structure</h1>
<p>State를 잘 구조화하면 수정과 디버깅이 편한 컴포넌트를 만들 수 있다.</p>
<h2 id="구조화-원칙">구조화 원칙</h2>
<p>어떤 state를 보유하는 컴포넌트를 작성할 때, 얼마나 많은 상태 변수를 사용할지, 그리고 그 데이터를 어떤 형태로 할지에 대해 선택해야 한다. 아래는 더 나은 선택을 위한 몇 가지 원칙이다.</p>
<ul>
<li><p><strong>관련 state를 그룹화한다.</strong> 두 개 이상의 상태 변수를 항상 동시에 업데이트한다면, 그것들을 하나로 합치는 것을 고려하자.</p>
</li>
<li><p><strong>state 모순을 피한다.</strong> 여러 state 조각이 서로 모순되고 동의하지 않는 방식으로 구조화된다면 실수할 여지가 있으니 피하자.</p>
</li>
<li><p><strong>불필요한 state을 피한다.</strong> 렌더링 중에 컴포넌트의 props나 기존 state 변수에서 정보를 계산할 수 있다면 해당 정보를 컴포넌트의 state에 넣지 말자.</p>
</li>
<li><p><strong>중복된 state를 피한다.</strong> 여러 state 변수 간에, 혹은 중첩된 객체 내에 동일한 데이터가 중복되면 동기화하기 어렵다.</p>
</li>
<li><p><strong>깊이 중첩된 state를 피한다.</strong> 깊게 계층화된 state는 업데이트하기 힘들다. 가능하다면 평평하게 구조화하자.</p>
</li>
</ul>
<p>원칙의 목표는 실수를 유발하지 않고 state를 쉽게 업데이트할 수 있도록 하는 것이다. 중복되거나 반복되는 데이터를 제거하면 모든 조각이 동기화된 state를 유지하는데 도움이 된다.</p>
<h2 id="state-그룹화">state 그룹화</h2>
<p>단일 state 변수와 다중 state 변수가 있다.</p>
<pre><code class="language-js">const [x, setX] = useState(0);
const [y, setY] = useState(0);</code></pre>
<pre><code class="language-js">const [position, setPosition] = useState({ x: 0, y: 0 });</code></pre>
<p>두 가지 접근 방식 중 하나를 사용할 수 있지만, <strong>두 개 이상의 state 변수가 항상 함께 변경되는 경우</strong>엔 하나의 state 변수로 통합하는 것이 좋다.</p>
<p>데이터를 그룹화하는 또 다른 경우는 <strong>필요한 state 조각 수를 모르는 경우</strong>이다. 예를 들어, 사용자가 사용자 정의 필드를 추가할 수 있는 양식이 있는 경우 유용하다.</p>
<blockquote>
<p>⚠️ State 변수가 객체인 경우 하나의 필드만 업데이트할 수 없다는 점을 기억하자!
예를 들어 x와 y 좌표를 함께 다루는 경우, <code>setPosition({ x: 100 }</code>는 불가능하다. 하나만 설정하고 싶은 경우엔 <code>setPosition({ ...position, x: 100 })</code>처럼 하거나 두 가지 state 변수로 분할해야 한다.</p>
</blockquote>
<h2 id="state-모순-피하기">state 모순 피하기</h2>
<pre><code class="language-js">  const [text, setText] = useState(&#39;&#39;);
  const [isSending, setIsSending] = useState(false);
  const [isSent, setIsSent] = useState(false);

  async function handleSubmit(e) {
    e.preventDefault();
    setIsSending(true);
    await sendMessage(text);
    setIsSending(false);
    setIsSent(true);
  }</code></pre>
<p>위 코드는 작동하지만, 불가능한 state를 허용한다. <code>isSending</code>과 <code>isSent</code>가 동시에 <code>true</code>인 상황이 발생할 위험이 있다. 컴포넌트가 복잡할수록 무슨 일이 일어났는지 이해하기 어려울 것이다. </p>
<pre><code class="language-js">  const [text, setText] = useState(&#39;&#39;);
  const [status, setStatus] = useState(&#39;typing&#39;);

  async function handleSubmit(e) {
    e.preventDefault();
    setStatus(&#39;sending&#39;);
    await sendMessage(text);
    setStatus(&#39;sent&#39;);
  }</code></pre>
<p>실수가 발생하지 않도록 이처럼 하나의 state 변수로 바꾸어 관리하는 것이 좋다.</p>
<h2 id="불필요한-state-피하기">불필요한 state 피하기</h2>
<p>렌더링하는 동안 컴포넌트의 props나 기존 state 변수에서 일부 정보를 계산할 수 있다면, 해당 정보를 컴포넌트 state에 넣지 않아야 한다.</p>
<pre><code class="language-js">  const [firstName, setFirstName] = useState(&#39;&#39;);
  const [lastName, setLastName] = useState(&#39;&#39;);
  const [fullName, setFullName] = useState(&#39;&#39;);

  function handleFirstNameChange(e) {
    setFirstName(e.target.value);
    setFullName(e.target.value + &#39; &#39; + lastName);
  }

  function handleLastNameChange(e) {
    setLastName(e.target.value);
    setFullName(firstName + &#39; &#39; + e.target.value);
  }</code></pre>
<p><code>firstName</code>과 <code>lastName</code> 만으로 <code>fullName</code>을 충분히 계산할 수 있으므로, 아래처럼 바꾸면 좋다.</p>
<pre><code class="language-js">  const [firstName, setFirstName] = useState(&#39;&#39;);
  const [lastName, setLastName] = useState(&#39;&#39;);

  const fullName = firstName + &#39; &#39; + lastName;

  function handleFirstNameChange(e) {
    setFirstName(e.target.value);
  }

  function handleLastNameChange(e) {
    setLastName(e.target.value);
  }</code></pre>
<p>업데이트를 위한 별도의 작업은 필요하지 않다. <code>setFirstName</code>나 <code>setLastName</code>가 호출되면 렌더링이 일어나 <code>fullName</code>이 새롭게 계산된다.</p>
<h3 id="props를-state에-그대로-미러링하면-안-된다">props를 state에 그대로 미러링하면 안 된다</h3>
<pre><code class="language-js">function Message({ messageColor }) {
  const [color, setColor] = useState(messageColor);</code></pre>
<p>부모 컴포넌트가 나중에 다른 <code>messageColor</code>를 전달하더라도 <code>color</code>는 업데이트되지 않는다. state 는 첫 렌더링에서만 초기화되기 때문이다.</p>
<h2 id="중복된-state-피하기">중복된 state 피하기</h2>
<p>아래 코드에서 <code>selectedItem</code>는 <code>items</code>의 첫 번째 항목과 동일하다.</p>
<pre><code class="language-js">  const [items, setItems] = useState([&#39;A&#39;, &#39;B&#39;, ..., &#39;Z&#39;]);
  const [selectedItem, setSelectedItem] = useState(
    items[0]
  );</code></pre>
<p> 하지만 <code>items</code>의 의 첫 번째 항목을 <code>AA</code>로 업데이트 하더라도 <code>selectedItem</code>은 여전히 <code>A</code>를 저장한다. 각 상태는 독립적으로 관리되기 때문에, <code>selectedItem</code>를 업데이트하지 않으면 변경사항이 반영되지 않는다.</p>
<pre><code class="language-js">  const [items, setItems] = useState(initialItems);
  const [selectedId, setSelectedId] = useState(0);</code></pre>
<p>중복된 내용을 제거하고 다른 방식으로 접근하는 것이 더 좋다.</p>
<h2 id="깊이-중첩된-state-피하기">깊이 중첩된 state 피하기</h2>
<pre><code class="language-js">const [plan, setPlan] = useState(initialTravelPlan);

// initialTravelPlan
export const initialTravelPlan = {
  id: 0,
  title: &#39;(Root)&#39;,
  childPlaces: [{
    id: 1,
    title: &#39;Earth&#39;,
    childPlaces: [{
      id: 2,
      title: &#39;Africa&#39;,
      childPlaces: [{
        id: 3,
        title: &#39;Botswana&#39;,
        childPlaces: []
      }, {
        id: 4,
        title: &#39;Egypt&#39;,
        childPlaces: []
      }, { 
        ...</code></pre>
<p>장소를 삭제하는 버튼을 추가한다고 가정해 보자. 중첩된 state를 업데이트할 땐 변경된 부분부터 객체의 복사본을 만들게 된다. 깊게 중첩된 장소를 삭제하려면 상위 장소 체인 전체를 복사해야만 한다.</p>
<p>state가 너무 중첩되면 업데이트가 어려워진다. 따라서 구조를 평평하게 만드는 것이 좋다. 이는 flat 또는 normalized라고 한다. </p>
<pre><code class="language-js">export const initialTravelPlan = {
  0: {
    id: 0,
    title: &#39;(Root)&#39;,
    childIds: [1, 42, 46],
  },
  1: {
    id: 1,
    title: &#39;Earth&#39;,
    childIds: [2, 10, 19, 26, 34]
  },
  2: {
    id: 2,
    title: &#39;Africa&#39;,
    childIds: [3, 4, 5, 6 , 7, 8, 9]
  }, 
  ...</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[children 속성]]></title>
            <link>https://velog.io/@aug8_/children-%EC%86%8D%EC%84%B1</link>
            <guid>https://velog.io/@aug8_/children-%EC%86%8D%EC%84%B1</guid>
            <pubDate>Fri, 28 Jun 2024 06:27:51 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<p>카테고리에 상관 없이 모두 <code>/</code>로 처리되는 걸 카테고리 id별로 나누어 주기 위한 작업 중 문제가 발생하였다.</p>
<pre><code class="language-js">//App.tsx
...
&lt;Route path=&#39;/:cateId&#39; element={&lt;MainBox children={ProductListBox()} /&gt;}/&gt;
...

//CategoryList.tsx
...
navigate(`/${cateId}`);
...

//ProductListBox.tsx
...
const cateId = useParams();
...</code></pre>
<p>라우팅은 문제없이 작동했으나, <code>cateId</code>가 <code>{}</code> 빈 객체로 나오던 상황</p>
<hr>
<h2 id="문제-해결">문제 해결</h2>
<p><strong>기존에 사용하던 방식</strong></p>
<pre><code class="language-js">&lt;Route path=&#39;/:cateId&#39; element={&lt;MainBox children={ProductListBox()} /&gt;}/&gt;</code></pre>
<p><strong>수정한 방식</strong></p>
<pre><code class="language-js">&lt;Route path=&#39;/:cateId&#39; element={&lt;MainBox&gt;&lt;ProductListBox /&gt;&lt;/MainBox&gt;} /&gt;</code></pre>
<p>이후부턴 정상적으로 <code>cateId</code>를 인식할 수 있었다.</p>
<hr>
<h2 id="분석">분석</h2>
<p>두 가지 방법은 비슷해 보이지만, 구조적인 차이를 갖고 있다.</p>
<ul>
<li>children 속성 사용</li>
<li>JSX에서 직접 선언</li>
</ul>
<h3 id="children-속성-사용">children 속성 사용</h3>
<p><code>ProductListBox()</code>을 사용하면 함수 호출로 간주되어 컴포넌트가 즉시 실행된다. 이는 React의 컴포넌트 트리에서 벗어나게 되어 예상치 못한 동작을 초래할 수 있다.</p>
<p>children 속성으로 전달되면 JSX 요소가 아닌 다른 결과값이 될 수 있어, React가 올바르게 렌더링하지 못할 수 있다.</p>
<h3 id="jsx에서-직접-선언">JSX에서 직접 선언</h3>
<p><code>ProductListBox</code>는 <code>Route</code>가 렌더링될 때 호출된다. 이는 React가 컴포넌트를 올바르게 인스턴스화하고, 라이프사이클 메서드를 실행할 수 있게 한다.</p>
<p><code>MainBox</code>의 자식이 <code>ProductListBox</code>라는 것을 명확히 하여 React 컴포넌트 트리가 올바르게 형성된다.</p>
<p>컴포넌트 트리 구조를 명확히 보여주어 코드가 더 직관적이고 이해하기 쉬우며, React 표준 패턴을 준수하여 상태와 훅이 올바르게 작동하고 예기치 않은 동작을 피할 수 있다.</p>
<h2 id="정리">정리</h2>
<p>children props를 직접 설정하는 건 일반적으로 비추천되는 패턴이라고 한다. JSX를 사용해 자식 컴포넌트를 명시적으로 포함시키는 방식이 React의 설계 패턴에 더 적합하기 때문이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[next.js]]></title>
            <link>https://velog.io/@aug8_/next.js</link>
            <guid>https://velog.io/@aug8_/next.js</guid>
            <pubDate>Wed, 19 Jun 2024 12:00:39 GMT</pubDate>
            <description><![CDATA[<h2 id="spa">SPA</h2>
<p>SPA(Single Page Application)는 단일 HTML 페이지로 구성된다. </p>
<p>페이지 전환 시 전체 페이지를 새로 로드하지 않으며, 클라이언트 측(CSR)에서 JavaScript를 통해 콘텐츠를 동적으로 렌더링한다.</p>
<ul>
<li><p><code>장점</code> : 빠른 페이지 전환 속도, 부드러운 사용자 경험, 서버 부하 감소, 개발 생산성 향상</p>
</li>
<li><p><code>단점</code> : 초기 로딩 지연, SEO 문제, 접근성 이슈, 초기 화면 깜박임</p>
</li>
</ul>
<p>SPA는 동적으로 렌더링된다. 전체 페이지를 새로 로드하지 않고 필요한 부분만 업데이트할 수 있기 때문에 사용자에게 부드러운 화면 전환을 제공할 수 있다.</p>
<p>초기 로딩 시 필요한 모든 리소스(JavaScript, CSS 등)을 한 번에 가져오기 때문에 이후 페이지 전환 시 새로 로드할 필요가 없어 빠른 응답 속도를 보장할 수 있다. 단, 초기 로딩이 길어질 수 있으며 사용자가 페이지를 처음 방문할 때 빈 화면이나 깜박임이 발생할 수 있다.</p>
<p>SEO 문제는 주로 JavaScript로 동작하기 때문에 발생한다. 검색 엔진 봇은 JavaScript를 실행하기 전까진 SPA의 초기 콘텐츠를 제대로 인식하지 못한다. 동적으로 콘텐츠를 렌더링하기 때문에 이런 컨텐츠를 제대로 크롤링하고 인덱싱하기도 어렵다. 단일 URL에서 동작하기 때문에 고유한 URL을 가지기 힘들고, 메타데이터를 적절히 설정하기도 어렵다.</p>
<h2 id="mpa">MPA</h2>
<p>MPA(Muiti Page Appliction)는 여러 개의 HTML 페이지로 구성된다.</p>
<p>페이지 전환 시 전체 페이지를 새로 로드하며,서버 측(SSR)에서 HTML을 생성하여 클라이언트에 전송한다.</p>
<ul>
<li><code>장점</code> : SEO 용이, 초기 로딩 속도 빠름, 접근성 높음</li>
<li><code>단점</code> : 페이지 전환 시 지연 발생, 서버 부하 증가, 개발 복잡도 높음</li>
</ul>
<p>MPA는 각 페이지마다 고유한 URL을 가지므로, 검색 엔진 봇의 크롤링과 콘텐츠 인덱싱이 용이하다. 웹 표준을 준수하기 때문에 장애인, 노령자 등의 다양한 사용자의 접근성을 높일 수도 있다.</p>
<p>복잡한 프레임워크와 라이브러리를 사용하는 SPA와 다르게 MPA는 웹 개발 표준 기술을 사용하고, 전통적인 클라이언트 - 서버 아키텍처를 따르기 때문에 다른 복잡한 프레임워크나 라이브러리 없이도 개발이 가능하다.</p>
<p>페이지 단위로 리소스를 로딩하기 때문에 초기 로딩 속도가 상대적으로 빠르다. 다만 페이지 간 이동 시 지연 시간이 발생할 수 있고, 각 페이지에 대한 요청이 서버로 전송되므로 트래픽이 증가할수록 서버 부하가 커질 수 있다.</p>
<h2 id="csr">CSR</h2>
<p>CSR(Client-Side Rendering)은 클라이언트 측에서 렌더링하는 방식이다. </p>
<p>SPA 아키텍처에서 주로 사용되는 방식으로, 초기 페이지 로드 시 전체 애플리케이션의 코드가 전송되고, 이후 클라이언트에서 동적으로 페이지를 렌더링한다.</p>
<p><code>React</code>, <code>Angular</code>, <code>Vue.js</code> 등으로 구현할 수 있다.</p>
<h2 id="ssr">SSR</h2>
<p>SSR(Server-Side Rendering)은 서버 측에서 렌더링하는 방식이다.</p>
<p>MPA 아키텍처에서 주로 사용되는 방식으로, 각 페이지가 서버에서 생성되어 클라이언트로 전송된다.</p>
<p><code>Next.js</code>, <code>Nuxt.js</code>, <code>Express.js</code>, <code>Django</code> 등으로 구현할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images%2Flongroadhome%2Fpost%2F2b082fb2-ca4f-487d-a3d7-d6211e2c14f3%2Ffsfsdbsdfse.png" alt=""></p>
<h2 id="ssg">SSG</h2>
<p>SSG(Static Site Generation)은 웹사이트의 콘텐츠를 빌드 시점에 미리 렌더링하여 정적인 HTML 파일로 생성하는 기술이다.</p>
<p>주로 SSR과 혼용하여 사용한다. 정적 콘텐츠 중심의 웹 사이트나 블로그에서는 SSG를 사용해 빠른 로드 속도와 높은 가용성을 확보할 수 있다. 동적 데이터 처리가 필요한 곳에선 SSR을 사용해 실시간 데이터 반영이 가능하다. 정적 콘텐츠와 동적 콘텐츠가 혼재된 곳에선 두 기술을 적절히 혼용하여 사용할 수도 있다.</p>
<p><img src="https://velog.velcdn.com/images%2Flongroadhome%2Fpost%2F9137471c-0491-41c2-bd59-f9d1cd0a2811%2Fadfasdf.png" alt=""></p>
<h2 id="nextjs">Next.js</h2>
<p><code>Next.js</code>는 React 기반의 프레임워크로, SSR을 구현하는 대표적인 기술 중 하나이다.</p>
<ul>
<li><p><code>Server-Side Rendering</code> : React 애플리케이션을 서버 측에서 렌더링해 초기 로드 속도가 빠르다. </p>
</li>
<li><p><code>Static Site Generation</code> : 빌드 시점에 페이지를 미리 렌더링해 HTML 파일을 생성한다. 동적 콘텐츠를 가진 웹사이트에도 적용할 수 있어 성능 향상에 도움이 된다.</p>
</li>
<li><p><code>Incremental Static Regeneration</code> : 기존에 생성된 정적 페이지를 특정 간격으로 자동 갱신할 수 있다.</p>
</li>
<li><p><code>File-based Routing</code> : 파일 기반의 라우팅 시스템으로, 개발자가 파일 구조 만으로도 라우팅을 설정할 수 있다.</p>
</li>
<li><p><code>API Routes</code> : 서버 사이드 API 엔드포인트를 쉽게 생성할 수 있어, 클라이언트와 서버 간의 데이터를 효율적으로 교환할 수 있다.</p>
</li>
<li><p><code>Code Splitting</code> : 자동으로 코드 분할을 수행하여 초기 로드 시간을 최소화한다.</p>
</li>
</ul>
<br>

<p><code>Next.js</code>는 렌더링을 할 때 <code>pre-rendering</code>을 수행한다. 또한, <code>Hydration</code>을 통해 미리 렌더링된 HTML에 JavaScript를 결합하여 이벤트가 동작할 수 있도록 만든다.</p>
<p><img src="https://enjoydev.life/posts/1-ssr-ssg-isr/230821-131324.png" alt=""></p>
<p><code>Next.js</code>는 기본적으로 서버 측에서 초기 HTML 페이지를 생성한다. 이 HTML 페이지에는 클라이언트 측 JavaScript 코드가 포함된다.</p>
<p>클라이언트가 HTML 페이지를 받으면, 포함된 JavaScript 코드가 실행되어 페이지를 활성화한다. 이 과정에서 클라이언트 측 JavaScript가 서버 측에서 생성된 HTML 요소와 상호작용할 수 있게 된다. &gt; <code>Hydration</code></p>
<p><code>Hydration</code> 이후에는 클라이언트 측 JavaScript가 페이지의 상호작용과 동적 업데이트를 처리할 수 있다. 사용자 이벤트, 데이터 로드, 상태 변경 등의 처리가 이루어진다.</p>
<h3 id="설치">설치</h3>
<pre><code>npx create-next-app@latest</code></pre><pre><code>What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias (@/*)? No / Yes
What import alias would you like configured? @/*</code></pre><h3 id="프로젝트-구조">프로젝트 구조</h3>
<p><code>Next.js</code>의 기본적인 프로젝트 구조는 다음과 같다. React와 유사한 구조를 갖고 있으나, 일부 차이점이 존재한다. 이외에도 프로젝트 요구사항에 따라 추가적인 폴더와 파일을 생성할 수 있다.</p>
<ul>
<li><p><code>.next/</code> : Next.js가 빌드한 결과물이 저장된다.</p>
</li>
<li><p><code>pages/</code> : 이 폴더 안에 있는 파일들이 자동으로 라우팅 시스템에 매핑된다. 각 파일은 하나의 페이지를 나타내며, 이름이 URL 경로가 된다.</p>
</li>
<li><p><code>components/</code> : 재사용 가능한 일반적인 React 컴포넌트를 저장한다.</p>
</li>
<li><p><code>styles/</code> : CSS 스타일 파일을 저장한다.</p>
</li>
<li><p><code>public/</code> : 정적 리소스를 저장한다. 빌드 시 이 폴더의 내용은 루트 디렉토리로 복사된다.</p>
</li>
<li><p><code>next.config.js</code> : Next.js의 설정 파일이다. 웹팩 구성, 환경 변수, 리다이렉션 등을 설정할 수 있다.</p>
</li>
</ul>
<h3 id="라우팅">라우팅</h3>
<p>Next.js에서 라우팅은 파일 기반이다. <code>pages/</code> 폴더 내에 있는 파일명이 URL 경로가 된다.</p>
<pre><code>pages/
  index.js
  about.js
  products/
    index.js
    [id].js
</code></pre><p>예를 들어 위와 같은 파일 구조가 있다고 가정했을 땐, 다음과 같은 라우팅이 자동으로 설정된다.</p>
<ul>
<li><code>/</code> : pages/index.js</li>
<li><code>/about</code> : pages/about.js</li>
<li><code>/products</code> : pages/products/index.js</li>
<li><code>/products/[id]</code> : pages/products/[id].js</li>
</ul>
<pre><code class="language-js">import { useRouter } from &#39;next/router&#39;

export default function ProductPage() {
  const router = useRouter()
  const { id } = router.query

  return (
    &lt;div&gt;
      &lt;h1&gt;{id}&lt;/h1&gt;
      {/* 제품 상세 정보*/}
    &lt;/div&gt;
  )
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 배열 메서드]]></title>
            <link>https://velog.io/@aug8_/Javascript-Array</link>
            <guid>https://velog.io/@aug8_/Javascript-Array</guid>
            <pubDate>Fri, 14 Jun 2024 11:59:50 GMT</pubDate>
            <description><![CDATA[<h2 id="array">Array</h2>
<p>자바스크립트 배열은 다양한 데이터 타입의 값을 포함할 수 있는 동적 배열이다.</p>
<pre><code class="language-js">// 각 요소는 서로 다른 데이터 타입을 가질 수 있다.
const array = [123, &#39;name&#39;, true, function () { }, {}, [&#39;a&#39;, &#39;b&#39;]];

// 배열의 이름은 복수형으로 짓는 게 일반적이다.
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.length); // 5
console.log(numbers[0]); // 1</code></pre>
<h2 id="추가삭제">추가/삭제</h2>
<h3 id="index">index</h3>
<p>인덱스를 사용해 새로운 요소를 추가한다. 빈 자리는 <code>empty items</code>로 채워진다.</p>
<pre><code class="language-js">const numbers = [0, 1, 2];

numbers[numbers.length] = 3; // [0, 1, 2, 3]
numbers[10] = 10; // [0, 1, 2, 3, &lt;6 empty items&gt;, 10]</code></pre>
<h3 id="push-unshift">push, unshift</h3>
<p><code>push()</code>는 배열의 끝에 새로운 요소를 추가하고, <code>unshift()</code>는 배열의 앞에 새로운 요소를 추가한다. 두 개 이상의 값을 한 번에 넣을 수도 있다.</p>
<pre><code class="language-js">const numbers = [0, 1, 2];

numbers.push(3); // [0, 1, 2, 3]
numbers.push(4, 5); // [0, 1, 2, 3, 4, 5]

numbers.unshift(-1); // [-1, 0, 1, 2]
numbers.unshift(-3, -2); // [-3, -2, -1, 0, 1, 2]</code></pre>
<h3 id="concat">concat</h3>
<p><code>concat()</code>은 두 개 이상의 배열을 결합하거나 배열에 값을 추가할 수 있다.</p>
<p><code>push()</code>, <code>unshift()</code>와 다르게 완전히 새로운 배열을 반환하며, 기존 배열은 변경되지 않는다.</p>
<pre><code class="language-js">const nums1 = [0, 1];
const nums2 = [2, 3];

const nums3 = nums1.concat(nums2);
// [0, 1, 2, 3]
const nums4 = nums3.concat(nums1, nums2);
// [0, 1, 2, 3, 0, 1, 2, 3]
const nums5 = nums1.concat(10);
// [0, 1, 10]</code></pre>
<h3 id="splice">splice</h3>
<p><code>splice()</code>는 배열의 내용을 변경하고, 제거된 요소가 담긴 별도의 배열을 새로 반환한다.</p>
<pre><code class="language-js">Array.splice(start, deleteCount, newItem, newItem, ...)</code></pre>
<p><code>start</code> : 변경을 시작할 인덱스
<code>deleteCount</code> : 제거할 요소의 수
<code>newItem</code> : 배열에 추가할 요소 (지정하지 않으면 제거만 한다.)</p>
<pre><code class="language-js">const original = [0, 1, 2];

const spliced = original.splice(0, 2);
console.log(original); // [2]
console.log(spliced); // [0, 1]

const spliced = original.splice(1, 0, &#39;new&#39;);
console.log(original); // [0, &#39;new&#39;, 1, 2]
console.log(spliced); // []</code></pre>
<h3 id="pop">pop</h3>
<p><code>pop()</code>은 배열의 마지막 요소를 제거하고 반환한다.</p>
<pre><code class="language-js">const numbers = [0, 1, 2];
const popped = numbers.pop();

console.log(numbers); // [0, 1]
console.log(popped); // 2</code></pre>
<h3 id="slice">slice</h3>
<p><code>slice()</code>는 배열의 일부를 추출하여 새로운 배열로 반환한다. </p>
<pre><code class="language-js">const numbers = [0, 1, 2];

const sliced = numbers.slice(1);
console.log(sliced); // [1, 2]

const sliced = numbers.slice(0, 1);
console.log(sliced); // [0]</code></pre>
<h2 id="탐색확인">탐색/확인</h2>
<h3 id="indexof-lastindexof">indexOf, lastIndexOf</h3>
<p><code>indexOf()</code>는 특정 요소를 찾을 수 있는 첫 번째 인덱스를 반환하고,<code>lastIndexOf()</code>는 마지막 인덱스를 반환한다. 찾을 수 없는 경우엔 -1를 반환한다.</p>
<pre><code class="language-js">Array.indexOf(searchElement, fromIndex = 0);
Array.lastIndexOf(searchElement, fromIndex = this.length - 1);</code></pre>
<p><code>searchElement</code> : 찾는 요소
<code>fromIndex</code> : 검색을 시작할 인덱스</p>
<pre><code class="language-js">const numbers = [0, 1, 2, 0, 1, 2];

console.log(numbers.indexOf(1)); // 1
console.log(numbers.lastIndexOf(1)); // 4

console.log(numbers.indexOf(2, 3)); // 5
console.log(numbers.lastIndexOf(2, 3)); // 2

console.log(numbers.indexOf(5)); // -1
console.log(numbers.lastIndexOf(5)); // -1</code></pre>
<h3 id="find-findindex">find, findIndex</h3>
<p><code>find()</code>는 각 요소에 콜백 함수를 실행하여 첫 번째로 true를 반환한 요소를 반환한다. <code>findIndex()</code>는 요소의 인덱스를 반환한다. 찾을 수 없는 경우엔 -1를 반환한다.</p>
<p><code>indexOf</code> 보다 복잡한 조건을 사용하여 요소를 찾아야 할 때 사용한다. 객체 배열에서 특정 속성을 가진 요소를 찾는 데 유용하게 쓸 수 있다.</p>
<pre><code class="language-js">const users = [
  { id: 1, name: &#39;kim&#39; },
  { id: 2, name: &#39;lee&#39; },
  { id: 3, name: &#39;park&#39; }
];

const user = users.find(user =&gt; user.id === 2);
console.log(user); // { id: 2, name: &#39;lee&#39; }


function isPark(element) {
  if (element.name === &#39;park&#39;) {
    return true;
  }
}
const park = users.find(isPark);
console.log(park); // { id: 3, name: &#39;park&#39; }


const userIndex = users.findIndex(user =&gt; user.id === 2);
console.log(userIndex); // 1</code></pre>
<h3 id="includes">includes</h3>
<p><code>includes()</code>는 배열에 특정 값이 포함되어 있는지 확인한다. 포함되어 있으면 <code>true</code>를, 포함되어 있지 않으면 <code>false</code>를 반환한다.</p>
<pre><code class="language-js">Array.includes(searchElement, fromIndex);</code></pre>
<p><code>searchElement</code> : 찾는 요소
<code>fromIndex</code> : 검색을 시작할 인덱스</p>
<pre><code class="language-js">const numbers = [0, 1, 2];

console.log(numbers.includes(1)); // true
console.log(numbers.includes(5)); // false

const numbers = [0, NaN, 2];
console.log(numbers.includes(NaN)); // true</code></pre>
<h2 id="순환">순환</h2>
<h3 id="foreach">forEach</h3>
<p><code>forEach()</code>는 배열의 요소를 순회하며 제공된 함수를 실행한다. 별도의 반환값은 없다.</p>
<pre><code class="language-js">const numbers = [1, 2, 3];

numbers.forEach(num =&gt; console.log(num)); // 1, 2, 3

const double = [];
numbers.forEach(num =&gt; double.push(num * 2));
console.log(double); // [2, 4, 6]</code></pre>
<h3 id="map">map</h3>
<p><code>map()</code>은 배열의 각 요소를 순회하며 제공된 함수를 실행하고, 그 결과로 새로운 배열을 생성한다.</p>
<pre><code class="language-js">const numbers = [1, 2, 3];

const doubled = numbers.map(num =&gt; num * 2);
console.log(doubled); // [2, 4, 6]

const string = numbers.map(num =&gt; num.toString());
console.log(string); // [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;]

const info = numbers.map((num, index) =&gt; ({
  value: num,
  index: index
}));
console.log(info);
// [
//   { value: 1, index: 0 },
//   { value: 2, index: 1 },
//   { value: 3, index: 2 },
//   { value: 4, index: 3 },
//   { value: 5, index: 4 }
// ]
</code></pre>
<h3 id="reduce">reduce</h3>
<p><code>reduce()</code>는 배열의 각 요소를 순회하면서 누적 값을 계산한다. 새로운 배열을 생성하는 <code>map()</code>과 다르게 단일 값을 생성한다.</p>
<pre><code class="language-js">Arr.reduce(callback, initialValue);</code></pre>
<p><code>callback</code>: 각 요소에 처리할 함수이다. 네 가지 인수를 받을 수 있다.</p>
<ul>
<li><code>accumulator</code> : 이전 호출의 반환값</li>
<li><code>currentValue</code> : 현재 처리 중인 요소</li>
<li>(선택) <code>currentIndex</code> : 현재 처리 중인 요소의 인덱스</li>
<li>(선택) <code>array</code> : 원본 배열</li>
</ul>
<p><code>initialValue</code> : 초기 acc값 (생략하면 첫 번째 요소가 초기값이 된다.)</p>
<pre><code class="language-js">// 합계 구하기
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, cur) =&gt; acc + cur, 0);
console.log(sum); // 15


// 요소 개수 세기
const fruits = [&#39;apple&#39;, &#39;banana&#39;, &#39;cherry&#39;, &#39;banana&#39;];
const fruitCount = fruits.reduce((count, fruit) =&gt; {
  count[fruit] = (count[fruit] || 0) + 1;
  return count;
}, {});
console.log(fruitCount); // { apple: 1, banana: 2, cherry: 1 }


// 중첩 해제하기
const nestedArray = [[1, 2], [3, 4], [5, 6]];
const flatArray = nestedArray.reduce((acc, cur) =&gt; acc.concat(cur), []);
console.log(flatArray); // [1, 2, 3, 4, 5, 6]</code></pre>
<h3 id="some-every">some, every</h3>
<p><code>some()</code>은 배열의 요소 중 하나라도 테스트 함수를 통과하면 true를 반환한다. <code>every()</code>는 배열의 모든 요소가 테스트 함수를 통과했을 때 true를 반환한다.</p>
<pre><code class="language-js">const numbers = [1, 2, 3];

const negativeNum = numbers.some(num =&gt; num &lt; 0); // false
const evenNum = numbers.some(num =&gt; num % 2 === 0); // true

const positiveAll = numbers.every(num =&gt; num &gt; 0); // true
const evenAll = numbers.every(num =&gt; num % 2 === 0); // false</code></pre>
<p>배열이 비어있을 때, <code>some</code>은 false를 반환하고 <code>every</code>는 true를 반환한다.</p>
<h3 id="filter">filter</h3>
<p><code>filter()</code>은 배열의 각 요소를 순회하며 제공된 함수를 실행하고, 그 결과가 true인 요소만 모은 새로운 배열을 반환한다.</p>
<pre><code class="language-js">const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const even = numbers.filter(num =&gt; num % 2 === 0);
console.log(even); // [2, 4, 6, 8, 10]

const largerThanFive = numbers.filter(num =&gt; num &gt; 5);
console.log(largerThanFive); // [6, 7, 8, 9, 10]</code></pre>
<h2 id="변형">변형</h2>
<h3 id="reverse">reverse</h3>
<p><code>reverse()</code>는 배열의 순서를 반대로 변경한다.</p>
<pre><code class="language-js">const numbers = [0, 1, 2];
const reversed = numbers.reverse();

console.log(numbers); // [2, 1, 0]
console.log(reversed); // [2, 1, 0]</code></pre>
<h3 id="sort">sort</h3>
<p><code>sort()</code>는 배열의 요소를 정렬한다. 기준을 사용자가 정의할 수도 있다.</p>
<pre><code class="language-js">const numbers = [5, 2, 9, 1, 7];
numbers.sort(); // [1, 2, 5, 7, 9]

// 오름차순, 내림차순
numbers.sort((a, b) =&gt; a - b); // [1, 2, 5, 7, 9]
numbers.sort((a, b) =&gt; b - a); // [9, 7, 5, 2, 1]

// 문자열 배열 정렬
const strings = [&#39;banana&#39;, &#39;apple&#39;, &#39;cherry&#39;, &#39;date&#39;];
strings.sort(); // [&#39;apple&#39;, &#39;banana&#39;, &#39;cherry&#39;, &#39;date&#39;]

// 객체 배열 정렬
const people = [
  { name: &#39;Kim&#39;, age: 21 },
  { name: &#39;Lee&#39;, age: 54 },
  { name: &#39;Park&#39;, age: 36 }
];

people.sort((a, b) =&gt; a.age - b.age);
// [
//   { name: &#39;Charlie&#39;, age: 20 },
//   { name: &#39;Alice&#39;, age: 25 },
//   { name: &#39;Bob&#39;, age: 30 }
// ]</code></pre>
<h2 id="문자열">문자열</h2>
<h3 id="tostring">toString</h3>
<p><code>toString()</code>은 배열의 모든 요소를 하나의 문자열로 변환하여 반환한다. 각 요소는 쉼표로 구분되며, 원본 배열은 변경되지 않는다.</p>
<pre><code class="language-js">const numbers = [0, 1, 2];
const string = numbers.toString(); // &quot;0,1,2&quot;</code></pre>
<h3 id="join">join</h3>
<p><code>join()</code>은 각 요소 사이에 지정된 구분자를 삽입하여 문자열로 변환할 수 있다. 지정하지 않으면 기본적으로 쉼표로 구분된다.</p>
<pre><code class="language-js">const numbers = [0, 1, 2];
const result1 = numbers.join(); // &quot;0,1,2&quot;
const result2 = numbers.join(&#39;-&#39;); // &quot;0-1-2&quot;</code></pre>
<h2 id="얕은-복사">얕은 복사</h2>
<p>새로 생성된 배열은 얕은 복사라는 점을 주의하자. </p>
<pre><code class="language-js">const original = [{ name: &#39;kim&#39; }, [1, 2]];
const copied = original.concat();

copied.push(3);
copied[0].name = &#39;lee&#39;;
original[1].push(3);

console.log(original); // [{ name: &#39;lee&#39; }, [1, 2, 3]]
console.log(copied);  // [{ name: &#39;lee&#39; }, [1, 2, 3], 3]</code></pre>
<p>얕은 복사가 드러나는 대표적인 데이터 타입은 다음과 같다.</p>
<ul>
<li>객체 리터럴(<code>{}</code>)</li>
<li>배열 리터럴(<code>[]</code>)</li>
<li>함수 리터럴(<code>Function() {...}</code>)</li>
<li><code>Date</code>, <code>RegExp</code>, <code>Error</code> 등의 내장 객체</li>
</ul>
<p>반면에 다음과 같은 타입은 참조 타입이 아닌 값 타입이기 때문에 깊은 복사가 된다.</p>
<ul>
<li><code>number</code>, <code>string</code>, <code>boolean</code>, <code>undefined</code>, <code>null</code></li>
</ul>
<p>따라서 배열이나 객체에 값 타입 데이터만 포함되어 있다면 복사 시 얕은 복사가 발생하지 않는다. 하지만 참조 타입이 포함되어 있다면 반드시 얕은 복사가 일어난다.</p>
<p>배열을 <strong>깊은 복사</strong> 하기 위해선 JSON 변환을 이용하면 된다. <code>JSON.stringify()</code>를 사용해 배열을 문자열로 변환한 후, <code>JSON.parse()</code>로 다시 배열로 변환하는 방식이다.</p>
<pre><code class="language-js">let original = [1, { a: 2 }, [3, 4]];
let copied = JSON.parse(JSON.stringify(original));</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[반응형 웹 디자인]]></title>
            <link>https://velog.io/@aug8_/%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EB%94%94%EC%9E%90%EC%9D%B8</link>
            <guid>https://velog.io/@aug8_/%EB%B0%98%EC%9D%91%ED%98%95-%EC%9B%B9-%EB%94%94%EC%9E%90%EC%9D%B8</guid>
            <pubDate>Mon, 10 Jun 2024 12:13:13 GMT</pubDate>
            <description><![CDATA[<h2 id="responsive-web-design">Responsive Web Design</h2>
<p><img src="https://www.hostpapa.com/blog/app/uploads/2015/09/ResponsiveWebDesign21.jpg" alt=""></p>
<p><strong>반응형 웹 디자인(RWD)</strong>은 웹사이트가 화면 크기에 따라 레이아웃과 콘텐츠를 조정하는 방식이다. 데스크탑, 태블릿, 스마트폰 등 사용자가 어떤 디바이스를 사용하든 자동으로 조정된다. </p>
<p>별도의 웹사이트를 만들 필요 없이 하나의 웹사이트로 모든 디바이스에서 일관된 사용자 경험을 제공할 수 있다.</p>
<blockquote>
<p><em>HTML은 기본적으로 반응형으로 작동하며, 창 크기를 조정하면 뷰포트에 맞게 자동으로 리플로우한다.</em></p>
</blockquote>
<p>반응형 웹 디자인은 기술이 아닌 접근 방식이다. 모든 기기에 대응할 수 있는 레이아웃을 설명하는 데 사용되는 용어로, 2010년에 만들어졌다.</p>
<hr>
<h2 id="fluid-grid">Fluid Grid</h2>
<p>가변 그리드는 레이아웃을 유연하게 만들어 다양한 화면 크기에 대응할 수 있도록 한다. 고정된 픽셀 값 대신 상대적인 크기(%)를 사용함으로써 구현할 수 있으며, 기본적인 비율로 그리드를 적용한다.</p>
<p><img src="https://i0.wp.com/www.hellodigital.kr/wp-content/uploads/2020/10/01.png?w=1000&ssl=1" alt="이미지"> <img src="https://i0.wp.com/www.hellodigital.kr/wp-content/uploads/2020/10/02.png?w=379&ssl=1" alt=""></p>
<p>가변 그리드 만으로는 반응형 웹을 만족할 수 없다. 특정 화면 크기에서 레이아웃이 크게 변경되어야 할 경우에 추가적인 조정이 필요하다.</p>
<hr>
<h2 id="media-query">Media Query</h2>
<p>미디어 쿼리는 CSS3에서 도입된 기능으로, 특정 조건에 따라 다른 스타일을 적용할 수 있다.</p>
<p>기본 구조는 다음과 같다.</p>
<pre><code class="language-css">@media &lt;미디어 타입&gt; and &lt;조건&gt; {
    /* 스타일 규칙 */
}</code></pre>
<p>아래는 너비가 600px 이하일 때 폰트 크기를 조정하는 예시이다.</p>
<pre><code class="language-css">body {
    font-size: 16px;
}

@media screen and (max-width: 600px) {
    body {
        font-size: 14px;
    }
}</code></pre>
<h3 id="미디어-타입">미디어 타입</h3>
<ul>
<li><p><code>all</code> : 모든 디바이스 유형에 적용된다. 기본 타입으로, 다른 타입을 지정하지 않으면 all이 적용된다.</p>
</li>
<li><p><code>print</code> : 인쇄 출력용으로 스타일을 적용할 때 사용한다. (ex. 문서를 프린터로 출력할 때, PDF로 저장할 때 등.)</p>
</li>
<li><p><code>screen</code> : 화면 디바이스(모니터, 태블릿, 스마트폰 등)에 적용된다. 대부분의 웹사이트에서 사용된다.</p>
</li>
<li><p><code>speech</code> : 스크린 리더 같은 음성 출력 디바이스에 적용된다. 시각 장애인을 위한 웹 접근성 향상에 도움된다.</p>
</li>
<li><p>그 밖에도 <code>projection</code>, <code>tv</code>, <code>braille</code>, <code>embossed</code> 등 다양한 타입이 있다.</p>
</li>
</ul>
<h3 id="조건">조건</h3>
<p>주로 사용되는 조건은 다음과 같으며, 여러 조건을 복합적으로 사용할 수도 있다.</p>
<ul>
<li><code>width</code>, <code>height</code> : 뷰포트 또는 요소의 너비와 높이</li>
</ul>
<pre><code class="language-css">@media (max-width: 600px) {
    body {
        background-color: black;
    }
}

@media (min-width: 600px) and (max-width: 1200px) {
    body {
        background-color: black;
    }
}</code></pre>
<ul>
<li><code>orientation</code> : 디바이스의 방향<ul>
<li>가로 모드는 <code>landscape</code>, 세로 모드는 <code>portrait</code>이다.<pre><code class="language-css">@media (orientation: landscape) {
body {
    background-color: black;
}
}</code></pre>
</li>
</ul>
</li>
</ul>
<ul>
<li><code>resolution</code> : 디바이스의 해상도<pre><code class="language-css">@media (min-resolution: 2dppx) {
  body {
      background-color: black;
  }
}</code></pre>
</li>
</ul>
<h3 id="복합-조건">복합 조건</h3>
<ul>
<li><p><code>and</code> : 조건이 모두 해당되었을 때 적용된다. 아래 예시는 너비가 600 이상이고, 가로 모드인 경우에 해당한다.</p>
<pre><code class="language-css">@media screen and (min-width: 600px) and (orientation: landscape) {
body {
  color: blue;
}
}</code></pre>
</li>
<li><p><code>or</code> : 조건 중 하나라도 해당되면 적용된다. 아래 예시는 너비가 600 이상이거나, 가로 모드인 경우에 해당한다.</p>
<pre><code class="language-css">@media screen and (min-width: 600px), screen and (orientation: landscape) {
body {
  color: blue;
}
}</code></pre>
</li>
<li><p><code>not</code> : 조건의 의미를 반대로 한다. 따라서 아래 예시는 세로 모드인 경우에 해당한다.</p>
<pre><code class="language-css">@media not all and (orientation: landscape) {
body {
  color: blue;
}
}</code></pre>
</li>
</ul>
<hr>
<h2 id="react-responsive">React-Responsive</h2>
<p>react-responsive는 React에서 반응형 디자인을 구현하기 위한 라이브러리이다.</p>
<ul>
<li><p>CSS 미디어 쿼리와 유사하게 조건부로 컴포넌트를 렌더링할 수 있다.</p>
</li>
<li><p>React 컴포넌트로 미디어 쿼리를 정의하여 보다 직관적이고 효율적으로 구현할 수 있다.</p>
</li>
<li><p>서버 사이드 렌더링(SSR)을 지원하여 초기 페이지 로드 시에도 반응형 디자인을 적용할 수 있다.</p>
</li>
</ul>
<h3 id="usemediaquery">useMediaQuery</h3>
<pre><code class="language-jsx">import React from &#39;react&#39;;
import { useMediaQuery } from &#39;react-responsive&#39;;

const Example = () =&gt; {
  const isDesktopOrLaptop = useMediaQuery({ minWidth: 1224 });
  const isTabletOrMobile = useMediaQuery({ maxWidth: 1224 });
  const isPortrait = useMediaQuery({ query: &#39;(orientation: portrait)&#39; });

  return (
    &lt;div&gt;
      {isDesktopOrLaptop &amp;&amp; &lt;p&gt;데스크탑 또는 랩탑&lt;/p&gt;}
      {isTabletOrMobile &amp;&amp; &lt;p&gt;태블릿 또는 스마트폰&lt;/p&gt;}
      {isPortrait &amp;&amp; &lt;p&gt;세로 모드&lt;/p&gt;}

    &lt;/div&gt;
  );
};

export default Example;
</code></pre>
<p>전달하는 인자의 형식에는 두 가지 방식이 있다.</p>
<ul>
<li><p><code>{minWidth: 1224}</code>처럼 간단한 객체를 사용하여 라이브러리가 내부적으로 미디어 쿼리 문자열을 생성할 수 있다. 사용하기 간편하지만, 라이브러리에 미리 정의된 것만 사용할 수 있어 제한적이다.</p>
</li>
<li><p><code>{ query: &#39;(min-width: 1224px)&#39; }</code>처럼 미디어 쿼리 문자열을 직접 정의할 수도 있다. CSS 미디어 쿼리 구문을 그대로 사용하여 더 복잡한 미디어 쿼리를 작성할 수 있다.</p>
</li>
</ul>
<h3 id="mediaquery">&lt; MediaQuery &gt;</h3>
<pre><code class="language-jsx">import React from &#39;react&#39;;
import MediaQuery from &#39;react-responsive&#39;;

const MyComponent = () =&gt; {
  return (
    &lt;div&gt;
      &lt;MediaQuery minWidth={1224}&gt;
        &lt;div&gt;데스크탑 또는 랩탑&lt;/div&gt;
      &lt;/MediaQuery&gt;
      &lt;MediaQuery maxWidth={1224}&gt;
        &lt;div&gt;모바일 또는 태블릿&lt;/div&gt;
      &lt;/MediaQuery&gt;
    &lt;/div&gt;
  );
};

export default MyComponent;</code></pre>
<p><code>&lt;Media&gt;</code>와 유사하지만, 라이브러리에 정의된 객체를 사용하여 보다 직관적으로 작성할 수 있다.</p>
<h3 id="media">&lt; Media &gt;</h3>
<pre><code class="language-jsx">import React from &#39;react&#39;;
import { Media } from &#39;react-responsive&#39;;

const MyComponent = () =&gt; {
  return (
    &lt;div&gt;
      &lt;Media query=&quot;(min-width: 1224px)&quot;&gt;
        {matches =&gt; matches &amp;&amp; &lt;div&gt;데스크탑 또는 랩탑&lt;/div&gt;}
      &lt;/Media&gt;
      &lt;Media query=&quot;(max-width: 1224px)&quot;&gt;
        {matches =&gt; matches &amp;&amp; &lt;div&gt;모바일 또는 태블릿&lt;/div&gt;}
      &lt;/Media&gt;
    &lt;/div&gt;
  );
};

export default MyComponent;</code></pre>
<p>미디어 쿼리 조건을 만족하면 <code>matches</code>에 <code>true</code>가, 그렇지 않으면 <code>false</code>가 온다.</p>
<p>CSS 미디어 쿼리 구문을 그대로 사용하여 보다 복잡한 미디어 쿼리를 작성할 수 있다.</p>
<hr>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Learn/CSS/CSS_layout/Responsive_Design">반응형 디자인</a></li>
<li><a href="https://www.hellodigital.kr/blog/dmkt-general-fluid-design/">Fluid 웹 디자인, 반응형 웹 디자인과 무엇이 다를까?</a></li>
<li><a href="https://developer.mozilla.org/ko/docs/Learn/CSS/CSS_layout/Media_queries">미디어 쿼리 초보자 안내서</a></li>
<li><a href="https://whales.tistory.com/93">[React] React-Responsive / 반응형 웹 만들기</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 용어 정리]]></title>
            <link>https://velog.io/@aug8_/React-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@aug8_/React-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 21 May 2024 10:01:57 GMT</pubDate>
            <description><![CDATA[<h2 id="element">Element</h2>
<p><strong>엘리먼트(Element)</strong>는 React 애플리케이션을 구성하는 블록이다. React 앱의 가장 작은 단위로, 화면에 표시할 내용(UI)을 기술한다.</p>
<p>엘리먼트는 불변 객체로, 생성된 후에는 자식이나 속성을 변경할 수 없다. 업데이트가 필요할 땐 새로운 엘리먼트를 생성하고 ReactDOM에 전달한다.</p>
<p>아래 코드는 JavaScript로 작성되었으며, <code>createElement</code>를 사용해 <code>&lt;h1 class=&quot;greeting&quot;&gt;안녕하세요!&lt;/h1&gt;</code>라는 엘리먼트를 생성한다.</p>
<pre><code class="language-javascript">// javascript
const element = React.createElement(
  &#39;h1&#39;,
  {className: &#39;greeting&#39;},
  &#39;안녕하세요!&#39;
);</code></pre>
<p>아래 코드도 위와 동일한 작업을 수행한다. JSX는 JavaScript를 확장한 문법으로, JSX를 사용하면 HTML과 유사한 문법으로 UI를 표현할 수 있다.</p>
<pre><code class="language-jsx">//jsx
const element = &lt;h1 className=&quot;greeting&quot;&gt;안녕하세요!&lt;/h1&gt;;</code></pre>
<h2 id="component">Component</h2>
<p><strong>컴포넌트(Component)</strong>는 페이지에 렌더링할 엘리먼트를 반환하는 독립적이고 재사용 가능한 코드 조각이다.</p>
<p>JavaScript로 정의되며, 각 컴포넌트는 자신의 상태(state)와 속성(props)을 관리하고 이를 바탕으로 UI를 렌더링한다. 컴포넌트는 다른 컴포넌트를 포함할 수 있어 복잡한 UI를 계층 구조로 구성할 수 있다.</p>
<p>다음은 함수형 컴포넌트의 예시이다.</p>
<pre><code class="language-jsx">function Greeting(props) {
  return &lt;h1&gt;안녕하세요, {props.name}!&lt;/h1&gt;;
}</code></pre>
<p>엘리먼트와 컴포넌트를 혼동하기 쉽다. 엘리먼트가 UI의 <strong>무엇을</strong> 렌더링할지를 정의한다면, 컴포넌트는 UI를 <strong>어떻게</strong> 렌더링할지를 정의한다.</p>
<p>엘리먼트는 UI의 구조를 설명하고, 컴포넌트는 이러한 엘리먼트를 생성하고 조합하는 단위이다. 컴포넌트를 이용하면 더 복잡하고 상호작용적인 UI를 구축할 수 있게 된다.</p>
<h2 id="props">props</h2>
<p><strong>속성(property, props)</strong>은 컴포넌트로 전달되는 데이터를 의미한다. 부모 컴포넌트로부터 자식 컴포넌트로 데이터를 전달할 때 사용한다.</p>
<p>읽기 전용으로, 컴포넌트 내에서 직접 변경할 수 없다. </p>
<pre><code class="language-jsx">function Welcome(props) {
  return &lt;h1&gt;안녕하세요, {props.name}님!&lt;/h1&gt;;
}

const element = &lt;Welcome name=&quot;사용자&quot; /&gt;;
</code></pre>
<h2 id="state">state</h2>
<p><strong>상태(state)</strong>는 컴포넌트가 관리하는 데이터를 의미한다. 동적인 데이터를 관리하기 위해 사용한다.</p>
<p>state는 컴포넌트 내부에서 선언되며, 컴포넌트의 상태(ex. 사용자 입력, 서버 응답 등)에 따라 번경될 수 있다. 외부에서 직접 접근하거나 변경할 수는 없다. state가 변경되면 컴포넌트는 자동으로 다시 리렌더링된다.</p>
<pre><code class="language-jsx">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;/div&gt;
  );
}
</code></pre>
<h2 id="event">event</h2>
<p><strong>이벤트(event)</strong>는 사용자와의 상호작용을 관리한다. React의 이벤트 시스템은 기본적인 웹 브라우저의 이벤트와 비슷하지만, React 자체 처리 시스템을 통해 브라우저 간의 일관된 이벤트 처리를 보장하고 성능을 최적화하고 있다.</p>
<p>React는 이벤트 핸들러 이름으로 캐멀 케이스를 사용하며(ex. onClick), 문자열이 아닌 함수 형태로 전달된다. </p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ko.legacy.reactjs.org/docs/glossary.html">React 기술 용어 모음</a></li>
<li><a href="https://velog.io/@mikio/React-%EA%B8%B0%EB%B3%B8-%EC%9A%A9%EC%96%B4-%EC%A0%95%EB%A6%AC">React 기본 용어 정리</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>