<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hope_k.log</title>
        <link>https://velog.io/</link>
        <description>🙋🏻‍♀️</description>
        <lastBuildDate>Tue, 18 Mar 2025 04:25:27 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>hope_k.log</title>
            <url>https://velog.velcdn.com/images/hope_k/profile/45995032-2356-497a-b681-2fefadfe0e63/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. hope_k.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hope_k" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 14장 비동기와 실습]]></title>
            <link>https://velog.io/@hope_k/14-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9E%91%EC%97%85%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@hope_k/14-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9E%91%EC%97%85%EC%9D%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Tue, 18 Mar 2025 04:25:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>브라우저 API 통신방법중 자주 사용하는 비동기 처리 방식에 대해서 알아보자!</p>
</blockquote>
<h2 id="동기와-비동기">동기와 비동기</h2>
<p>1) 동기(Synchronous)란? </p>
<ul>
<li>작업이 순차적으로 실행되는 방식이다. (순서 보장)</li>
<li>현재 작업이 끝나야 다음 작업을 실행할 수 있다. </li>
<li>결과를 즉시 확인할 수 있다. </li>
</ul>
<p>2) 비동기(Asynchronous)란?</p>
<ul>
<li>작업이 독립적으로 실행되는 방식이다. (순서 보장X)</li>
<li>작업 요청 후 결과를 기다리지 않고 다른 작업을 동시에 처리한다. </li>
<li><code>비동기 처리란?</code> 비동기로 작업을 요청하고, 작업이 완료되면 이후에 값을 처리하는 방식을 말한다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/hope_k/post/2397e48f-e6c3-4992-bbfe-2a716ba61c9d/image.png" alt=""></p>
<p>** 정리 ** </p>
<table>
<thead>
<tr>
<th>특징</th>
<th>동기</th>
<th>비동기</th>
</tr>
</thead>
<tbody><tr>
<td>작업 처리 방식</td>
<td>순차적 실행</td>
<td>독립적(병렬적)실행</td>
</tr>
<tr>
<td>작업 대기 여부</td>
<td>작업완료까지 대기(blocking)</td>
<td>대기하지 않고, 다른작업 실행(non-blocking)</td>
</tr>
<tr>
<td>사용 사례</td>
<td>트랜잭션 처리, 데이터베이스 작업, 순서가 중요한 통신</td>
<td>네트워크 요청, 파일I/O처리</td>
</tr>
<tr>
<td>장점</td>
<td>간단하고 구현이 쉬움</td>
<td>작업 병렬 처리로 성능 향상</td>
</tr>
<tr>
<td>단점</td>
<td>작업 대기 시간이 길어질 수 있음</td>
<td>구현이 복잡하고 디버깅이 어려움</td>
</tr>
</tbody></table>
<ul>
<li><p>기본적으로 모든 언어는 실행 흐름에 따라 동기 실행이 원칙</p>
<pre><code class="language-java">// 실행 순서: 1 → 2 → 3 (순차 실행, 동기)
public class SyncExample {
  public static void main(String[] args) {
      System.out.println(&quot;1. 시작&quot;);
      System.out.println(&quot;2. 실행&quot;);
      System.out.println(&quot;3. 종료&quot;);
  }
}</code></pre>
</li>
<li><p>JavaScript는 기본적으로 동기 실행이지만, 비동기 처리가 기본 내장됨</p>
</li>
<li><p>그 외 언어에서 비동기 실행은 명시적으로 작성해야 동작함 (예: Java에서 Thread, CompletableFuture 사용)</p>
</li>
</ul>
<blockquote>
<p>Q. Javscript 만 비동기 기능을 포함하는 이유? 
원래 웹 브라우저에서 실행되는 언어로 개발된 JS는 싱글스레드 언어로 한번에 하나의 작업만 처리할 수 있다. 브라우저에서 UI 조작, 네트워크 요청, 사용자 입력 등을 모두 처리해야하는데 동기로 처리하기에 작업을 기다리며 다른 작업이 Blocking 처리되어 브라우저가 멈추게됨. 따라서 비동기 기능을 기본으로 제공하여 원활한 사용자 경험이 가능함</p>
</blockquote>
<h3 id="콜백함수">콜백함수</h3>
<p>콜백함수란, <strong>다른 함수에 인자로 전달</strong>되어 특정 이벤트가 발생하거나 작업이 완료된 후 호출되는 함수를 의미한다. 
주로 비동기 프로그래밍에서 많이 사용되며, 함수의 실행 흐름을 제어하는 데 활용한다. </p>
<p>1) <code>일반 콜백함수</code> 일반 콜백함수는 함수의 재사용이 가능하다. </p>
<pre><code class="language-javascript">function greet(name, callback) {
     console.log(`${name}님 안녕하세요`)
      callback();
}

greet(&#39;희망&#39;, () =&gt; {
    console.log(&quot;사전적 영어 이름은 Hope 입니다.&quot;)
})

greet(&#39;희망&#39;, () =&gt; {
    console.log(&quot;회사 이름은 Gabi 입니다.&quot;)
})
</code></pre>
<p>2) <code>Array의 고차 함수 콜백</code> </p>
<pre><code class="language-javascript">const numbers = [1,2,3,4,5];

const squaredNumbers = numbers.map(num =&gt; num * num);</code></pre>
<p>3) <code>비동기 콜백</code> 비동기 함수 실행 후, 후처리 함수로 주로 사용한다. </p>
<pre><code class="language-javascript">function printMe() {
 console.log(&quot;Hello World!&quot;) 
}

setTimeout(printMe, 3000) // 비동기함수 호출
console.log(&quot;대기중...&quot;)

// console 결과
대기중...
Hello World!</code></pre>
<p>그러나 콜백함수로 비동기를 처리할때 단점이 있다. 바로 콜백지옥에 빠질수 있다는 점이다. 😈
콜백 지옥은 비동기 함수를 사용하여 순차로 처리해야할 여러 로직이 중첩으로 호출되면서 로직이 깊어지는 현상을 이야기한다. </p>
<pre><code class="language-javascript">function getUser(userId, callback) {
    setTimeout(() =&gt; {
        console.log(&quot;사용자 정보 가져오기&quot;);
        callback({ id: userId, name: &quot;홍길동&quot; });
    }, 1000);
}

function checkPermission(user, callback) {
    setTimeout(() =&gt; {
        console.log(&quot;권한 확인&quot;);
        callback({ ...user, permission: &quot;admin&quot; });
    }, 1000);
}

function saveData(user, callback) {
    setTimeout(() =&gt; {
        console.log(&quot;데이터 저장 완료&quot;);
        callback(&quot;저장 성공!&quot;);
    }, 1000);
}

// 콜백 중첩 (콜백 지옥 발생)
getUser(1, (user) =&gt; {
    checkPermission(user, (userWithPermission) =&gt; {
        saveData(userWithPermission, (result) =&gt; {
            console.log(result);
        });
    });
});</code></pre>
<p>콜백함수는 유지보수가 어렵고, 가독성이 떨어져 현업에서 사용하길 지양한다. </p>
<h3 id="promise">Promise</h3>
<p>콜백함수의 단점으로 ES6에서 탄생한 비동기 처리 함수이다. 
순차처리할 로직을 중첩으로 호출하는 것이 아니라,  Promise 체이닝인 then을 연속적으로 호출하며 가독성이 좋아졌다. </p>
<pre><code class="language-javascript">function getUser(userId) {
    return new Promise((resolve) =&gt; {
        setTimeout(() =&gt; {
            console.log(&quot;사용자 정보 가져오기&quot;);
            resolve({ id: userId, name: &quot;홍길동&quot; });
        }, 1000);
    });
}

function checkPermission(user) {
    return new Promise((resolve) =&gt; {
        setTimeout(() =&gt; {
            console.log(&quot;권한 확인&quot;);
            resolve({ ...user, permission: &quot;admin&quot; });
        }, 1000);
    });
}

function saveData(user) {
    return new Promise((resolve) =&gt; {
        setTimeout(() =&gt; {
            console.log(&quot;데이터 저장 완료&quot;);
            resolve(&quot;저장 성공!&quot;);
        }, 1000);
    });
}

// Promise 체이닝으로 해결
getUser(1)
    .then(checkPermission) // (data) =&gt; checkPermission(data) 로 같은 로직 
    .then(saveData)
    .then(console.log)
    .catch(console.error);</code></pre>
<h3 id="asyncawait">Async/await</h3>
<p>Promise 를 좀 더 진화한 ES8 문법으로 async 함수를 선언 한 후 Promise를 리턴하는 함수를 await 처리하면 된다. </p>
<p><code>async/await</code> 함수는 비동기 코드를 동기 코드처럼 읽을 수 있다. 
<code>try-catch</code>를 사용하여 에러 핸들링을 할 수 있다. </p>
<pre><code class="language-javascript">async function process() {
    try {
        const user = await getUser(1);
        const userWithPermission = await checkPermission(user);
        const result = await saveData(userWithPermission);
        console.log(result);
    } catch (error) {
        console.error(error);
    }
}

process();</code></pre>
<h3 id="promise-vs-asyncawait">Promise vs Async/await</h3>
<p>그럼 무조건 최신버전인 async/await를 쓰면 될까?? 
실제 현업에서 보면 Promise then 체이닝을 종종 쓰는걸 접할 수 있다. 
그럼 두개의 차이와 언제 쓰면 좋을까?</p>
<p>결론은 두개의 비동기 처리 함수는 <strong>실행 흐름에서 차이</strong>가 있다.</p>
<h4 id="promise-1">Promise</h4>
<ul>
<li>then() 체이닝을 통해 비동기 결과를 처리</li>
<li>비동기 작업이 끝날 때까지 기다리지 않고 바로 다음 코드가 실행됨</li>
<li>비동기 작업이 완료된 후 then() 내부 코드 실행됨</li>
</ul>
<p>언제쓸까?</p>
<ul>
<li>비동기 작업을 여러 개 병렬로 실행해야할 때 (Promise.All)</li>
<li>Promise 기반 API 를 그대로 활용해야할 때 </li>
<li>간단한 비동기 로직 처리 (체이닝이 많지 않을때) </li>
</ul>
<h4 id="asyncawait-1">Async/await</h4>
<ul>
<li>await을 만나면 해당 함수 내부에서 일시 정지 후 기다림 (동기적으로 실행)</li>
<li>하지만, 함수 바깥의 코드(다음 코드)는 먼저 실행됨</li>
<li>비동기 작업이 완료되면, await이 있던 자리에서 다시 동기적으로 실행됨</li>
</ul>
<p>언제쓸까?</p>
<ul>
<li>가독성이 중요한 코드, 즉 복잡한 비즈니스 로직일 경우 (동기 코드처럼 작성 가능)</li>
<li>함수가 길어질때</li>
<li>예외처리를 try-catch로 쉽게 하고싶을때</li>
<li>비동기 함수 내부에서 여러 개의 await를 순차적으로 처리하고싶을때 </li>
</ul>
<h2 id="axios">Axios</h2>
<p>axios는 현재 가장 많이 사용되고있는 HTTP 클라이언트이다. 
라이브러리의 특징은 HTTP 요청을 Promise 기반으로 처리한다는 점이다. (비동기 요청)</p>
<h2 id="custom-hook">Custom Hook</h2>
<p>커스텀훅은 React에서 중복되는 로직을 재사용하기 위해 사용하는 함수이다.</p>
<h3 id="커스텀훅은-언제-사용해야할까">커스텀훅은 언제 사용해야할까?</h3>
<ul>
<li>동일한 로직이 여러 컴포넌트에서 반복될때</li>
<li>비즈니스 로직을 컴포넌트에서 분리하고 싶을때</li>
<li>컴포넌트 내부의 코드가 너무 복잡할 때</li>
<li>API 요청, 상태관리, 이벤트 리스너, 타이머등 부가 로직을 분리하고 싶을때</li>
<li>렌더링 로직과 UI로직을 분리하여 유지보수를 쉽게 하고 싶을때 </li>
</ul>
<p><strong>사용예제 1. 중복된 API 요청 코드</strong></p>
<pre><code class="language-javascript">import { useEffect, useState } from &quot;react&quot;;

function Users() {
    const [users, setUsers] = useState([]);

    useEffect(() =&gt; {
        fetch(&quot;https://jsonplaceholder.typicode.com/users&quot;)
            .then((res) =&gt; res.json())
            .then((data) =&gt; setUsers(data));
    }, []);

    return &lt;div&gt;{users.length}명의 사용자&lt;/div&gt;;
}

function Posts() {
    const [posts, setPosts] = useState([]);

    useEffect(() =&gt; {
        fetch(&quot;https://jsonplaceholder.typicode.com/posts&quot;)
            .then((res) =&gt; res.json())
            .then((data) =&gt; setPosts(data));
    }, []);

    return &lt;div&gt;{posts.length}개의 게시글&lt;/div&gt;;
}</code></pre>
<pre><code class="language-javascript">import { useEffect, useState } from &quot;react&quot;;

function useFetch(url) {
    const [data, setData] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() =&gt; {
        fetch(url)
            .then((res) =&gt; res.json())
            .then((data) =&gt; {
                setData(data);
                setLoading(false);
            });
    }, [url]);

    return { data, loading };
}

function Users() {
    const { data: users, loading } = useFetch(&quot;https://jsonplaceholder.typicode.com/users&quot;);
    if (loading) return &lt;div&gt;Loading...&lt;/div&gt;;
    return &lt;div&gt;{users.length}명의 사용자&lt;/div&gt;;
}

function Posts() {
    const { data: posts, loading } = useFetch(&quot;https://jsonplaceholder.typicode.com/posts&quot;);
    if (loading) return &lt;div&gt;Loading...&lt;/div&gt;;
    return &lt;div&gt;{posts.length}개의 게시글&lt;/div&gt;;
}</code></pre>
<p>사용예제 2. 중복된 이벤트 리스너 코드</p>
<pre><code class="language-javascript">import { useEffect, useState } from &quot;react&quot;;

function ComponentA() {
    const [width, setWidth] = useState(window.innerWidth);

    useEffect(() =&gt; {
        const handleResize = () =&gt; setWidth(window.innerWidth);
        window.addEventListener(&quot;resize&quot;, handleResize);
        return () =&gt; window.removeEventListener(&quot;resize&quot;, handleResize);
    }, []);

    return &lt;div&gt;창 너비: {width}px&lt;/div&gt;;
}

function ComponentB() {
    const [width, setWidth] = useState(window.innerWidth);

    useEffect(() =&gt; {
        const handleResize = () =&gt; setWidth(window.innerWidth);
        window.addEventListener(&quot;resize&quot;, handleResize);
        return () =&gt; window.removeEventListener(&quot;resize&quot;, handleResize);
    }, []);

    return &lt;div&gt;컴포넌트 B 창 너비: {width}px&lt;/div&gt;;
}</code></pre>
<pre><code class="language-javascript">import { useEffect, useState } from &quot;react&quot;;

function useWindowSize() {
    const [width, setWidth] = useState(window.innerWidth);

    useEffect(() =&gt; {
        const handleResize = () =&gt; setWidth(window.innerWidth);
        window.addEventListener(&quot;resize&quot;, handleResize);
        return () =&gt; window.removeEventListener(&quot;resize&quot;, handleResize);
    }, []);

    return width;
}

function ComponentA() {
    const width = useWindowSize();
    return &lt;div&gt;창 너비: {width}px&lt;/div&gt;;
}

function ComponentB() {
    const width = useWindowSize();
    return &lt;div&gt;컴포넌트 B 창 너비: {width}px&lt;/div&gt;;
}</code></pre>
<blockquote>
<p>util과 다른점? 
유틸함수는 리액트와 관계없이 순수 함수 형태이다. 일반적으로 데이터 가공, 변환, 계산 등을 수행할 때 사용한다. 
따라서 그냥 데이터를 변환/처리 하는 함수라면 유틸함수를 만들어서 사용한다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 12장 immer]]></title>
            <link>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-12%EC%9E%A5-immer</link>
            <guid>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-12%EC%9E%A5-immer</guid>
            <pubDate>Tue, 04 Mar 2025 02:40:51 GMT</pubDate>
            <description><![CDATA[<h2 id="immer란">immer란?</h2>
<p>immer란 리액트 상태관리 중 불변성을 유지하도록 하기위한 라이브러리이다. </p>
<h3 id="리액트의-불변성이란">리액트의 불변성이란?</h3>
<p>리액트의 불변성이란 리액트가 컴포넌트를 정상적으로 렌더링시키기 위해 기존 상태 값을 유지시키면서, 새로운 상태 값을 추가하는 것을 의미한다. </p>
<p>아래와 같은 예제를 보자.
어떤 상품을 찜하기 버튼을 눌러 patch API를 호출하여 DB를 업데이트가 필요하다. </p>
<ul>
<li>찜하기 Button을 통해 상품의 위시값 DB 변경을 위한 patch API를 호출</li>
<li>patch API가 성공할때 이전에 요청했던 해당 상품의 상세정보 응답 캐시중 wish 값만 수정하여 캐시에 저장함 (조회 API 재요청X)</li>
</ul>
<pre><code class="language-javascript">const ProductDetails = ({ product }) =&gt; {
  const queryClient = useQueryClient()

  const toggleWishHandler = async () =&gt; {
    try {
      await api.patchProductWish({ ...product, wish: !product.wish })

     // query cache 갱신
       queryClient.setQueryData(
         queryKey,
         (data: ProductDetailResponse) =&gt; {
          return {
            ...data,
            productInfo: {
              ...data.productInfo,
              wish: !data.productInfo.wish
            }

         }
         )

    } catch (e) {
     ....
    }
  }

  return (
    &lt;div&gt;
      &lt;p&gt;{product.name}&lt;/p&gt;
      &lt;button onClick={toggleWishHandler}&gt;찜하기&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<p>위에 경우처럼 불변성을 유지하기 위해 중첩된 데이터 모두 새로 갱신해줘야 하기 때문에 번거로움이 있다. </p>
<p>중첩함수 깊이가 깊어지면 유지보수도 어려울 뿐더러 코드 가독성이 굉장히 떨어지게 된다. </p>
<p>이때 사용하는 것이 immer 이다!</p>
<p>immer를 사용하면 아래와같이 변경가능하다.</p>
<pre><code class="language-javascript">import { produce } from &quot;immer&quot;;

const ProductDetails = ({ product }) =&gt; {
  const queryClient = useQueryClient();

  const toggleWishHandler = async () =&gt; {
    try {
      await api.patchProductWish({ ...product, wish: !product.wish });

      // query cache 갱신 (immer 적용)
      queryClient.setQueryData(queryKey, (data) =&gt;
        produce(data, (draft) =&gt; {
          draft.productInfo.wish = !draft.productInfo.wish;
        })
      );
    } catch (e) {
      // 에러 처리
      console.error(e);
    }
  };

  return (
    &lt;div&gt;
      &lt;p&gt;{product.name}&lt;/p&gt;
      &lt;button onClick={toggleWishHandler}&gt;찜하기&lt;/button&gt;
    &lt;/div&gt;
  );
};</code></pre>
<p>복잡한 스프레드 문법이 produce 함수를 통해 간단히 처리되었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 11장 컴포넌트 렌더링 최적화]]></title>
            <link>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-11%EC%9E%A5-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-11%EC%9E%A5-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Tue, 18 Feb 2025 01:57:00 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프론트엔드 개발자라면 많은 양의 데이터를 렌더링할때 브라우저가 멈춰버리는 현상을 종종 경험할 수 있다. 개인적으로 페이지네이션없이 많은 양의 데이터를 렌더링해서 편집하는 페이지는 좋지 않은 UI/UX 라고 생각하지만, 그래도 종종 다뤄야할 때가 있다. 이때 최적화를 어떻게 하는지 공부해보았다. </p>
</blockquote>
<p>우선 본격적으로 들어가기 앞서 중요한 점은 <strong>모든 컴포넌트를 최적화 할 필요는 없다</strong>. </p>
<p>주로 현업에서는 많은 양의 데이터를 한번에 렌더링하는 경우는 거의 없으며 
<strong>API를 통한 페이지네이션</strong> 처리 또는 <strong>Lazy 렌더링</strong> 등 최소한의 렌더링을 하는 기술을 사용하기 때문이다. </p>
<p>그래도  최적화가 필요한 경우는 반복되는 List, ListItem, Table, Tr 등에 주로 사용되며 화면상 렌더링되는 건수가 2000건 이상일때 고려해보는 것이 좋다. </p>
<p>그렇지 않을경우, 오히려 메모이제이션은 독이 될 수 있으니 유의하자!</p>
<hr>
<h2 id="1-최적화-방법">1. 최적화 방법</h2>
<p>많은 양의 컴포넌트가 리렌더링되는 영역에서 최적화가 필요하다.</p>
<blockquote>
<p>리액트 리렌더링 조건</p>
</blockquote>
<ul>
<li>props 가 바뀔때 </li>
<li>state 가 바뀔때 </li>
<li>부모 컴포넌트가 리렌더링 될때 </li>
<li>forceUpdate() 함수가 실행 될때</li>
</ul>
<p>주소 정보를 변경할 수 있는 UserList 예제를 보자.</p>
<pre><code class="language-javascript">
// 3000건의 user dummy 데이터 생성 
const getUsers = () =&gt; {
    return Array.from({ length: 3000 }, (_, i) =&gt; ({
        number: i + 1,
        name: `홍길동${i + 1}`,
        address: &#39;&#39;,
    }));
};

const List = () =&gt; {
    const [users, setUsers] = useState(getUsers());

    const addressHandler = (input: string, index: number) =&gt; {
        const newUsers = users.map((user, i) =&gt; i === index ? {...user, address: input} : user)
        setUsers(newUsers);
    };

    return (
        &lt;div&gt;
            {users.map((user, index) =&gt; (
                &lt;User
                    key={user.number}
                    {...user}
                    addressHandler={(value) =&gt; addressHandler(value, index)}
                /&gt;
            ))}
        &lt;/div&gt;
    );
};

const User = ({
                  name,
                  address,
                  addressHandler,
              }: {
    name: string;
    address: string;
    addressHandler: (value: string) =&gt; void;
}) =&gt; {
    return (
        &lt;div style={{
            border: &#39;1px solid grey&#39;,
            padding: &#39;4px&#39;,
            display: &#39;flex&#39;,
            flexDirection: &#39;column&#39;,
        }}&gt;
            &lt;p&gt;{name}&lt;/p&gt;
            &lt;input
                type=&quot;text&quot;
                value={address}
                onChange={(e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt;
                    addressHandler(e.target.value)
                }
            /&gt;
        &lt;/div&gt;
    );
};



export default List;
</code></pre>
<p>해당 코드를 실행해보면 3000건의 User 리스트가 렌더링되고 각각 address를 입력받는 input을 가지고 있다. 이때 아무 인덱스의 Address 정보를 변경하면 아래와 같은 결과가 나타난다. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/341d5860-7055-43ae-81ae-68b5c26fed0c/image.gif" alt=""></p>
<p>하나의 데이터만 변경했는데 3000건의 아이템들이 전부다 리렌더링 되고있다. 
불필요한 시스템 자원이 낭비되고 있으며, 사양에 따라 다르지만 사양이 낮은 컴퓨터에서는 굉장히 버벅거리고 사용자경험이 떨어질 수 있다. </p>
<p>또한 개발된 설계에 따라 다른 컴포넌트에서 변경한 데이터로 인해 List 또한 함께 리렌더링이 발생하며 브라우저가 뻗어버리는 경우가 발생할 수도 있다. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/3e1bde55-1774-4eb2-a7f2-d399bafa53ec/image.gif" alt="">
List 컴포넌트에 Job state 값을 추가했더니 job을 바꿀때마다 UserList 가 리렌더링 되는것을 볼 수 있다. </p>
<h3 id="reactmemo">React.memo</h3>
<p><code>memo</code> 함수는 컴포넌트를 메모이제이션 한다. props 값이 변하지 않으면 이전에 메모이제이션 한 컴포넌트를 재사용하기 때문에 리렌더링 하지 않는다. </p>
<pre><code class="language-javascript">
const List = React.memo(() =&gt; {
    const [job, setJob] = useState(&#39;developer&#39;)
    const [users, setUsers] = useState(getUsers());

    const addressHandler = (input: string, index: number) =&gt; {
        const newUsers = users.map((user, i) =&gt; i === index ? {...user, address: input} : user)
        setUsers(newUsers);
    };

    return (
        &lt;div&gt;
            &lt;label&gt;
                &lt;input name=&quot;job&quot; type=&quot;radio&quot; value=&quot;developer&quot; checked={job === &#39;developer&#39;} onChange={() =&gt; setJob(&#39;developer&#39;)} /&gt;
                개발자
            &lt;/label&gt;
            &lt;label&gt;
                &lt;input name=&quot;job&quot; type=&quot;radio&quot; value=&quot;doctor&quot; checked={job === &#39;doctor&#39;} onChange={() =&gt; setJob(&#39;doctor&#39;)}/&gt;
                의사
            &lt;/label&gt;
            &lt;label&gt;
                &lt;input name=&quot;job&quot; type=&quot;radio&quot; value=&quot;lawyer&quot; checked={job === &#39;lawyer&#39;} onChange={() =&gt; setJob(&#39;lawyer&#39;)}/&gt;
                변호사
            &lt;/label&gt;
            &lt;br/&gt;
            &lt;br/&gt;
            &lt;br/&gt;
            &lt;div&gt;
                {users.map((user, index) =&gt; (
                    &lt;User
                        key={user.number}
                        {...user}
                        addressHandler={(value) =&gt; addressHandler(value, index)}
                    /&gt;
                ))}
            &lt;/div&gt;
        &lt;/div&gt;
    );
});



const User = React.memo(({
                  name,
                  address,
                  addressHandler,
              }: {
    name: string;
    address: string;
    addressHandler: (value: string) =&gt; void;
}) =&gt; {
    return (
        &lt;div style={{
            border: &#39;1px solid grey&#39;,
            padding: &#39;4px&#39;,
            display: &#39;flex&#39;,
            flexDirection: &#39;column&#39;,
        }}&gt;
            &lt;p&gt;{name}&lt;/p&gt;
            &lt;input
                type=&quot;text&quot;
                value={address}
                onChange={(e: ChangeEvent&lt;HTMLInputElement&gt;) =&gt;
                    addressHandler(e.target.value)
                }
            /&gt;
        &lt;/div&gt;
    );
});


User.displayName = &#39;User&#39;;
List.displayName = &#39;List&#39;;
</code></pre>
<h3 id="props에-전달하는-함수는-인자값에-참조값-대신-함수형-넣기">Props에 전달하는 함수는 인자값에 참조값 대신 함수형 넣기</h3>
<p>위에 예제에서 User 컴포넌트에서 <code>addressHandler</code> 함수는 users state 값을 참조한다. 따라서 users 가 바뀌게 되면 해당 메서드 또한 새로 생성되어 props 에 전달되고 자식 컴포넌트를 리렌더링 시킨다. </p>
<p>이때 해결할 수 있는 방법은 참조값 대신 콜백 함수를 넣어주면 된다. </p>
<p><code>AS-IS</code></p>
<pre><code class="language-javascript">// users를 참조함
    const addressHandler = (input: string, index: number) =&gt; {
        const newUsers = users.map((user, i) =&gt; i === index ? {...user, address: input} : user)
        setUsers(newUsers);
    };
</code></pre>
<p><code>TO-BE</code></p>
<pre><code class="language-javascript">// users를 참조하지 않고 콜백함수를 통해 처리함
    const addressHandler = useCallback((input: string, index: number) =&gt; {
        setUsers((users) =&gt; users.map((user, i) =&gt; i === index ? {...user, address: input} : user));
    }, []);
</code></pre>
<h3 id="react-virtualized-라이브러리-사용하기">react-virtualized 라이브러리 사용하기</h3>
<p>위에 방법 이외에 외부 라이브러리를 사용하여 lazy rendering 하는 기법도 있다. 
해당 라이브러리는 화면상 보이는 부분만 초기 렌더링하고, 스크롤시 추가로 렌더링하는 기법이다. </p>
<h3 id="주의할점">주의할점</h3>
<p>리액트는 얕은비교를 통해 props의 변경을 감지하기 때문에 데이터 타입이 Array, Object일때 중첩된 Array, Object 또한 모두 새로운 참조값을 생성해야한다. </p>
<pre><code class="language-javascript">const [response, setResponse] = useState({
    data: {
        data: {
          contents: [...]
        },
          pageSize: 10,
          page: 0
    },
      httpStatus: 200,
      error: null
})

// 불변성을 유지하는 법
const newState = {
  ...response,
  data: {
    ...response.data,
      contents: [...response.data.contents]
  }
}

</code></pre>
<p>중첩된 데이터가 참조형일때 새로운 객체를 복사하기에 까다로움이 있어 추후 immer 외부 라이브러리를 사용한다. </p>
<h2 id="2-성능-확인법">2. 성능 확인법</h2>
<p>개발자 도구 &gt; Perfomence 탭에서 확인
<img src="https://velog.velcdn.com/images/hope_k/post/3763480b-27e7-49d0-8f44-e1aa59be0dc2/image.png" alt="">
<img src="https://velog.velcdn.com/images/hope_k/post/06a6349f-06d6-44b2-9ea3-aabcbf5e0ec1/image.png" alt=""></p>
<h2 id="3-그-밖에-usetransition-을-사용하는-방법이-있다">3. 그 밖에 useTransition 을 사용하는 방법이 있다.</h2>
<p>최적화는 아니지만, 그 밖에 UI를 차단시키지 않고 
사용자 경험을 개선시키는 방법중 하나는 <code>useTransition</code> 을 사용하는 방법이 있다. 
JS는 싱글스레드 언어이기 때문에 동시에 작업을 처리하지 못한다.
이 방법은 처리 흐름중에 발생하는 렌더링의 우선순위를 뒤로 미뤄 화면이 멈추는 현상을 막는 방법이다. </p>
<p>사용법은 우선순위를 미룰 로직에 <code>startTransition</code> 함수를 사용하여 콜백처리하면 된다. </p>
<p>List 컴포넌트에 address 값을 일괄로 처리하는 input 을 넣어보겠다.</p>
<pre><code class="language-javascript">const List = React.memo(() =&gt; {
    const [bulk, setBulk] = useState(&#39;&#39;)
    const [resultMessage, setResultMessage] = useState(&#39;&#39;);
    const [users, setUsers] = useState(getUsers());
    const [isPending, startTransition] = useTransition();

    const addressHandler = useCallback((input: string, index: number) =&gt; {
        setUsers((prevState) =&gt; prevState.map((user, i) =&gt; i === index ? {...user, address: input} : user));
    }, []);

    const bulkUpdateHandler = () =&gt; {
        try {
            startTransition(() =&gt; {
                setUsers(prevState =&gt; prevState.map(user =&gt; ({...user, address: bulk})))
            })
            setResultMessage(&quot;업데이트 성공&quot;)
        } catch (e) {
            setResultMessage(&quot;업데이트 실패&quot;)
        }
    }

    return (
        &lt;div&gt;
            &lt;span&gt;일괄 변경&lt;/span&gt;
            &lt;input type=&quot;text&quot; placeholder=&quot;주소를 입력하세요&quot; value={bulk} onChange={(e) =&gt; setBulk(e.target.value)}/&gt;
            &lt;button type=&quot;button&quot; onClick={bulkUpdateHandler}&gt;적용&lt;/button&gt;
            &lt;p style={{color: &#39;red&#39;, marginBottom: &#39;20px&#39;}}&gt;{resultMessage}&lt;/p&gt;
            &lt;div&gt;
                {users.map((user, index) =&gt; (
                    &lt;User
                        key={user.number}
                        {...user}
                        addressHandler={(value) =&gt; addressHandler(value, index)}
                    /&gt;
                ))}
            &lt;/div&gt;
        &lt;/div&gt;
    );
});
</code></pre>
<p><code>useTransition 미적용시</code>
미적용시 resultMessage가 List 리렌더링 이후에 나타난다.
<img src="https://velog.velcdn.com/images/hope_k/post/33d6ed4f-ba5b-4db3-9183-41165a904055/image.gif" alt=""></p>
<p><code>useTransition 적용시</code>
미적용시 resultMessage가 나타나고 이후에 List가 리렌더링된다. (UI 차단 막을 수 있음)
<img src="https://velog.velcdn.com/images/hope_k/post/c2b40174-c023-4a6f-bb81-851ff2b27ceb/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hydration과 렌더링 주체]]></title>
            <link>https://velog.io/@hope_k/Hydration</link>
            <guid>https://velog.io/@hope_k/Hydration</guid>
            <pubDate>Thu, 23 Jan 2025 01:31:41 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트, Next를 처음 시작하면 알아야 할 중요한 개념이다.
개발을 하면서도 하이드레이션이 뭔가 느낌이 와닿지 않았다. 그래서 공부해보려고 한다!</p>
</blockquote>
<p>Hydration이란 사전적 의미로 수분공급이라고 한다.
처음에 수분공급이라는 말이 너무 문학적인 표현의 용어 같아서 와닿지 않았다ㅋㅋ </p>
<p>또한 하이드레이션은 CSR 서비스에서는 접하기 어렵다. 
그래서 신규 프로젝트가 SSR로 작업하게 되면서 하이드레이션 에러도 만나고... 차츰 접하게 되었다. </p>
<h1 id="hydration">Hydration</h1>
<h2 id="1-hydration이란">1. Hydration이란?</h2>
<h3 id="정의">정의</h3>
<p>서버에서 HTML을 생성하여 클라이언트에게 전달하면 이 HTML을 클라이언트가 가지고 있는 HTML(Dom Tree 객체)과의 비교작업 후, 완전 일치할때 다운로드 받은 JS Chunk 파일을 Dom Tree에 연결시켜 인터렉티브 가능한 상태로 만드는 <code>연결 작업</code>이다.</p>
<p>하이드레이션이 완료 된 후, 리액트 클라이언트 측 기능 (클릭, 상태변화 등) 이 동작하게 된다. </p>
<h4 id="hydration-발생-조건">Hydration 발생 조건</h4>
<p>✅ 조건1 : 서버에서 생성된 HTML이 있고
✅ 조건2 : React가 클라이언트에서 해당 HTML을 다시 <strong>연결</strong>해야 하는 경우</p>
<p>즉, React가 클라이언트에서 &quot;이 HTML을 다시 조작할 가능성이 있을 때&quot; Hydration이 발생한다.</p>
<h4 id="hydration이-발생하지-않는-케이스">Hydration이 발생하지 않는 케이스</h4>
<p><code>정적 HTML</code></p>
<pre><code class="language-javascript">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;정적 페이지&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;이 페이지는 Hydration이 필요 없음&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><code>CSR 으로만 이뤄진 리액트(Next) 애플리케이션</code> </p>
<pre><code class="language-javascript">&#39;use client&#39;
const React from &#39;react&#39;

const CSRApplication = () =&gt; {
    return &lt;div&gt;안녕하세요&lt;/div&gt;
}
</code></pre>
<h4 id="정적-페이지에서-hydration이-발생하지-않게-하려면">정적 페이지에서 Hydration이 발생하지 않게 하려면?</h4>
<p><code>output: &quot;export&quot; 사용 (Next.js 13+) 또는 export const runtime = &#39;edge&#39;; 사용</code>
이 설정을 하면 Next.js는 정적 HTML만 생성하고, React는 실행되지 않으므로 Hydration이 발생하지 않음</p>
<pre><code class="language-javascript">{
  &quot;output&quot;: &quot;export&quot;
}

// 컴포넌트 내에서
export const runtime = &#39;edge&#39;;

export default function Page() {
  return &lt;h1&gt;Next.js 정적 페이지&lt;/h1&gt;;
}</code></pre>
<p><code>특정 컴포넌트에서 Hydration 방지 (useEffect)</code>
이렇게 하면 초기 정적 HTML은 렌더링되지만, Hydration이 일어나지 않고 클라이언트에서 다시 렌더링됨</p>
<pre><code class="language-javascript">import { useEffect, useState } from &quot;react&quot;;

export default function Page() {
  const [mounted, setMounted] = useState(false);

  useEffect(() =&gt; {
    setMounted(true);
  }, []);

  return mounted ? &lt;h1&gt;클라이언트에서만 렌더링&lt;/h1&gt; : &lt;h1&gt;정적 HTML&lt;/h1&gt;;
}</code></pre>
<h2 id="2-hydration-탄생이유">2. Hydration 탄생이유?</h2>
<p>웹애플리케이션이 빠르게 성장하면서 SPA 웹의 사용이 증가하였고, SPA 와 궁합이 잘 맞는 CSR 렌더링 기법이 주로 사용되었다. </p>
<p>그러나 CSR의 한계로 이를 극복하기 위해 SSR 도입이 필요하였고, 이 과정에서 리액트는 하이드레이션이라는 기술을 탄생시켰다.</p>
<blockquote>
<p>CSR(Client-Side Rendering) 방식이란?</p>
</blockquote>
<ul>
<li>사용자가 브라우저에서 페이지를 요청하면, 서버는 빈 HTML과 JavaScript 번들 파일을 보냄.</li>
<li>브라우저가 JavaScript를 다운로드하고 실행해야 화면을 렌더링할 수 있음.</li>
<li>모든 DOM 요소와 이벤트 핸들러가 클라이언트에서 생성됨.</li>
</ul>
<h3 id="csr-문제점">CSR 문제점</h3>
<h4 id="초기-로딩속도가-느림">초기 로딩속도가 느림</h4>
<p>사용자에게 빈 HTML 을 먼저 보내고 이후 JS 가 실행되고 렌더링 되므로, 첫화면이 보이기까지 오래 걸리는 문제점이 있다.</p>
<h4 id="seo검색-엔진-최적화에-불리함">SEO(검색 엔진 최적화)에 불리함</h4>
<p>검색엔진은 페이지를 크롤링할 때 HTML 을 분석하는데, CSR 방식에서는 HTML이 빈 상태이기 때문에 SEO에 불리하다. </p>
<h4 id="백엔드-서버의-역할-부족">백엔드 서버의 역할 부족</h4>
<p>모든 렌더링이 클라이언트에서만 이루어지므로, 서버는 단순한 정적 파일 제공자 역할만 하게 된다. </p>
<h3 id="csr-문제를-극복하기-위해-ssr-srg-등장">CSR 문제를 극복하기 위해 SSR, SRG 등장</h3>
<blockquote>
<p>SSR(Server-Side Rendering) 방식이란? </p>
</blockquote>
<ul>
<li>사용자가 브라우저에서 페이지를 요청하면 서버가 실제 HTML을 만들어서 반환</li>
<li>브라우저는 HTML 을 즉시 렌더링</li>
</ul>
<blockquote>
<p>SSG(Static-Site-Generation) 이란? </p>
</blockquote>
<ul>
<li>미리 빌드 시점에 HTML을 생성하여 정적 파일로 배포</li>
<li>사용자가 요청 즉시 HTML을 제공하므로 가장 빠르다. </li>
</ul>
<h3 id="ssr-ssg에서-인터랙티브한-기능이-필요해">SSR, SSG에서 인터랙티브한 기능이 필요해!</h3>
<p>리액트는 이벤트핸들러, 상태 등 인터렉티브 기능들이 모두 Client-Side에서 동작해야 한다. 
따라서 CSR 한계를 극복하기 위해 SSR, SSG가 나오게 되었고 인터렉티브 기능까지 동작하게 하기위해 Hydration 과정을 도입하게 되었다. </p>
<h2 id="정리와-의문점">정리와 의문점</h2>
<p>Q1. 인터렉티브 코드가 없으면 Hydration은 일어나지 않는가?</p>
<pre><code class="language-javascript">export default function Page() {
  return &lt;h1&gt;Next.js 정적 페이지&lt;/h1&gt;;
}</code></pre>
<p><code>Answer</code>
Next.js 같은 경우 기본적으로 모든 페이지를 React 애플리케이션으로 간주한다. 
위에 코드도 React 컴포넌트이며, 서버에서 HTML을 받아 전달 되었다면 인터렉티브가 없어도 리액트는 이를 클라이언트에서 다시 연결해야 된다고 판단한다. </p>
<p>Q2. 그럼 리액트 관리 코드는 모두 Hydration이 일어나는가?
<code>Answer</code>
React가 모든 페이지를 관리한다고 해서 Hydration이 항상 일어나는 것은 아니다. 
React가 관리하는 방식에 따라 3가지 경우로 나눌 수 있다.
<img src="https://velog.velcdn.com/images/hope_k/post/3264fda9-1566-4809-a1d3-194748488528/image.png" alt=""></p>
<p>서버에서 HTML을 미리 만들지 않고 클라이언트에서 React가 처음부터 렌더링한다면 하이드레이션이 발생하지 않는다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 7장 라이프사이클]]></title>
            <link>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-7%EC%9E%A5-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4</link>
            <guid>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-7%EC%9E%A5-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4</guid>
            <pubDate>Tue, 21 Jan 2025 04:16:01 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>데이터의 라이프사이클은 메모리 관리 및 성능에 영향을 끼치기 때문에 모든 언어에서 중요한 지식이다.
개발자가 데이터의 라이프사이클 이해를 바탕으로 설계를 진행하고 개발해야 좋은 어플리케이션이 탄생한다. </p>
</blockquote>
<h2 id="라이프사이클이란">라이프사이클이란?</h2>
<p>컴포넌트가 생성되고 초기화되며 사용된 뒤 소멸되는 과정이다. </p>
<h3 id="중요한-이유">중요한 이유</h3>
<ul>
<li>리소스 관리</li>
<li>성능 최적화</li>
<li>애플리케이션 안정성</li>
</ul>
<h2 id="리액트의-라이프사이클-메서드">리액트의 라이프사이클 메서드</h2>
<p>리액트는 클래스형 컴포넌트에서만 라이프사이클 메서드를 지원한다. 대신 함수형 컴포넌트에서도 대체할 만한 Hooks 들이 있으니 걱정하지 말자!</p>
<p>리액트의 라이프사이클 메서드는 크게 3종류로 나뉜다.</p>
<ul>
<li>Mount : 컴포넌트가 생성, 초기화되고 첫 렌더링될때</li>
<li>Update : 생성된 컴포넌트에서 데이터가 변경됨에 따라 리렌더링 할때</li>
<li>UnMount : 컴포넌트가 소멸될때 발생</li>
</ul>
<h3 id="1-mount-시-실행되는-함수">1. Mount 시 실행되는 함수</h3>
<h4 id="constructor">constructor</h4>
<ul>
<li>컴포넌트 생성자 메서드로 클래스형 컴포넌트를 처음 생성할때 실행됨</li>
<li>초기 State 값 설정에도 사용<h4 id="getderivedstatefromprops">getDerivedStateFromProps</h4>
</li>
<li>props에서 받아온 값으로 state 값을 동기화시킬때 사용함</li>
<li>컴포넌트가 마운트, 업데이트 될때 호출됨</li>
</ul>
<blockquote>
<p>getDerivedStateFromProps 요소만 static 인 이유? 
React 컴포넌트에서 해당 메서드가 컴포넌트의 인스턴스와는 독립적으로 동작해야 하기 때문이다. 
getDerivedStateFromProps는 <strong>컴포넌트가 렌더링되기 전에 호출되어 새로운 props를 기반으로 state를 업데이트하는 역할</strong>을 한다.
이 메서드는 인스턴스와 관련된 속성(예: this)에 접근할 필요가 없다. 단순히 props와 state만 받아서 새로운 state 객체를 반환하면 된다. </p>
</blockquote>
<ul>
<li><code>일반 메서드</code>: this를 통해 인스턴스의 상태나 메서드에 접근.</li>
<li><code>정적 메서드(static)</code>: 인스턴스 없이 클래스 차원에서 호출.</li>
</ul>
<h4 id="render">render</h4>
<ul>
<li>JSX, 커스텀 컴포넌트, null 등을 반환하는 컴포넌트 모양새를 정의하는 함수</li>
<li>유일하게 함수중 필수값</li>
</ul>
<h4 id="componentdidmount">componentDidMount</h4>
<ul>
<li>마운트시 제일 마지막 단계에서 실행됨</li>
<li>컴포넌트가 렌더링이 끝난 후 실행됨 </li>
<li>주로 자바스크립트 라이브러리 호출, setTimeout, setInterval, 네트워크 요청등 <strong>비동기 작업을 처리</strong>함 </li>
</ul>
<h3 id="2-update-시-실행되는-함수">2. Update 시 실행되는 함수</h3>
<blockquote>
<p>컴포넌트가 리렌더링(Update)가 발생하는 케이스</p>
</blockquote>
<ol>
<li>props가 변경될때</li>
<li>state가 변경될때</li>
<li>부모 컴포넌트가 리렌더링될때</li>
<li>this.forceUpdate()가 실행될때 </li>
</ol>
<h4 id="getderivedstatefromprops-1">getDerivedStateFromProps</h4>
<ul>
<li>props에서 받아온 값으로 state 값을 동기화시킬때 사용함</li>
</ul>
<h4 id="shouldcomponentupdate">shouldComponentUpdate</h4>
<ul>
<li>props 또는 state 값이 업데이트 될때 렌더링 할지 말지 여부를 결정함</li>
<li>해당 메서드는 boolean 타입을 리턴함 </li>
<li>false를 리턴하면 이후 작업은 중지됨 </li>
</ul>
<h4 id="render-1">render</h4>
<ul>
<li>데이터가 변경된 내용을 반영하여 JSX를 다시 생성</li>
</ul>
<h4 id="getsnapshotbeforeupdate">getSnapshotBeforeUpdate</h4>
<ul>
<li>render에서 만들어진 결과물이 브라우저에 실제로 반영되기 전에 실행됨</li>
<li>이 메서드에서 반환한 값은 componentDidUpdate에서 사용가능함</li>
<li>주로 DOM이 렌더링 되기전에 스크롤 위치 등을 조정하기 위해 사용함<h4 id="componentdidupdate">componentDidUpdate</h4>
</li>
<li>리렌더링 완료한 후 실행됨</li>
<li>렌더링이 모두 끝났기 때문에 DOM 관련 처리를 해도됨</li>
<li>prevProps, prevState 를 사용하여 컴포넌트가 이전에 가졌던 데이터에 접근할 수 있음</li>
<li>getSnapshotBeforeUpdate 단계에서 반환된 값을 전달받아 사용할 수 있음</li>
</ul>
<h3 id="3-unmount-시-실행되는-함수">3. Unmount 시 실행되는 함수</h3>
<h4 id="componentwillunmount">componentWillUnmount</h4>
<ul>
<li>DOM이 제거되기전 실행됨</li>
<li>주로 componentDidMount 에서 등록한 이벤트, 타이머 등을 제거하는 작업을 수행함</li>
</ul>
<h3 id="4-에러시-실행되는-함수">4. 에러시 실행되는 함수</h3>
<h4 id="componentdidcatch">componentDidCatch</h4>
<ul>
<li>컴포넌트가 렌더링중 예기치못한 에러가 발생했을때 애플리케이션이 먹통이 되는것 이 아니라, 에러 메세지 노출, 에러페이지 리다이랙트 등 예외처리 할 수 있음</li>
<li>에러로그 수집 등 서비스 운영에 필요한 작업을 할 수 있음</li>
<li>단, 해당 컴포넌트가 선언된 에러는 감지를 못하고, 컴포넌트 내에 하위 children 컴포넌트에서 발생한 에러만 감지할 수 있음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hope_k/post/0c6b10f0-8a65-4dbc-89f0-367197e9b696/image.png" alt=""></p>
<h2 id="생성하기-재정의">생성하기 (재정의)</h2>
<ol>
<li><p>코드에서 우클릭 &gt; 생성자 또는 메서드 재정의 
<img src="https://velog.velcdn.com/images/hope_k/post/7c569ab4-3b19-457c-be66-2a5b131fba4f/image.png" alt=""></p>
</li>
<li><p>정의할 멤버 선택
<img src="https://velog.velcdn.com/images/hope_k/post/be508d75-8e34-4536-841e-b083649c5c9f/image.png" alt=""></p>
</li>
</ol>
<h2 id="예제-코드">예제 코드</h2>
<pre><code class="language-javascript">&#39;use client&#39;
import React, { Component } from &quot;react&quot;;

interface LifecycleExampleProps {
    initialValue?: number;
}

interface LifecycleExampleState {
    count: number;
    syncedProp: number;
}

class LifecycleExample extends Component&lt;LifecycleExampleProps, LifecycleExampleState&gt; {
    private interval?: NodeJS.Timeout; // 타이머를 저장할 변수

    constructor(props: LifecycleExampleProps) {
        super(props);
        this.state = {
            count: 0,
            syncedProp: props.initialValue || 0, // 초기 상태 정의
        };
        console.log(&quot;Constructor: 컴포넌트가 생성되었습니다.&quot;);
    }

    static getDerivedStateFromProps(nextProps: LifecycleExampleProps, prevState: LifecycleExampleState) {
        console.log(&quot;getDerivedStateFromProps: props로부터 state를 동기화합니다.&quot;);
        if (nextProps.initialValue !== undefined &amp;&amp; nextProps.initialValue !== prevState.syncedProp) {
            return {
                syncedProp: nextProps.initialValue,
            };
        }
        return null; // 변경 사항이 없을 때
    }

    componentDidMount() {
        console.log(&quot;componentDidMount: 컴포넌트가 마운트되었습니다.&quot;);
        this.interval = setInterval(() =&gt; {
            console.log(&quot;타이머 실행 중...&quot;);
            this.setState((prevState) =&gt; ({ count: prevState.count + 1 }));
        }, 10000);
    }

    shouldComponentUpdate(nextProps: Readonly&lt;LifecycleExampleProps&gt;, nextState: Readonly&lt;LifecycleExampleState&gt;): boolean {
        console.log(&quot;shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.&quot;);
        if (nextState.count &gt;= 10) {
            console.log(&quot;업데이트 중단: count가 10 이상입니다.&quot;);
            return false;
        }
        return true;
    }

    getSnapshotBeforeUpdate(prevProps: Readonly&lt;LifecycleExampleProps&gt;, prevState: Readonly&lt;LifecycleExampleState&gt;) {
        console.log(&quot;getSnapshotBeforeUpdate: 업데이트 전 DOM 상태를 캡처합니다.&quot;);
        return prevState.count; // 이전 count 값을 반환
    }

    componentDidUpdate(prevProps: Readonly&lt;LifecycleExampleProps&gt;, prevState: Readonly&lt;LifecycleExampleState&gt;, snapshot?: number) {
        console.log(&quot;componentDidUpdate: 컴포넌트가 업데이트되었습니다.&quot;);
        console.log(&quot;이전 count:&quot;, snapshot);
    }

    componentWillUnmount() {
        console.log(&quot;componentWillUnmount: 컴포넌트가 언마운트됩니다.&quot;);
        if (this.interval) {
            clearInterval(this.interval); // 타이머 정리
        }
    }

    render() {
        console.log(&quot;render: 컴포넌트를 렌더링합니다.&quot;);
        return (
            &lt;div&gt;
                &lt;h1&gt;React Class Component Lifecycle&lt;/h1&gt;
                &lt;p&gt;Count: {this.state.count}&lt;/p&gt;
                &lt;p&gt;Synced Prop: {this.state.syncedProp}&lt;/p&gt;
                &lt;button onClick={() =&gt; this.setState({ count: 0 })}&gt;
                    Reset Count
                &lt;/button&gt;
            &lt;/div&gt;
        );
    }
}

export default LifecycleExample;
</code></pre>
<p><code>결과 콘솔</code>
Constructor: 컴포넌트가 생성되었습니다.
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:70 render: 컴포넌트를 렌더링합니다.
CustomClassComponent.tsx:36 componentDidMount: 컴포넌트가 마운트되었습니다.
CustomClassComponent.tsx:38 타이머 실행 중...
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:44 shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.
CustomClassComponent.tsx:70 render: 컴포넌트를 렌더링합니다.
CustomClassComponent.tsx:53 getSnapshotBeforeUpdate: 업데이트 전 DOM 상태를 캡처합니다.
CustomClassComponent.tsx:58 componentDidUpdate: 컴포넌트가 업데이트되었습니다.
CustomClassComponent.tsx:59 이전 count: 0
CustomClassComponent.tsx:38 타이머 실행 중...
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:44 shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.
CustomClassComponent.tsx:70 render: 컴포넌트를 렌더링합니다.
CustomClassComponent.tsx:53 getSnapshotBeforeUpdate: 업데이트 전 DOM 상태를 캡처합니다.
CustomClassComponent.tsx:58 componentDidUpdate: 컴포넌트가 업데이트되었습니다.
CustomClassComponent.tsx:59 이전 count: 1
CustomClassComponent.tsx:38 타이머 실행 중...
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:44 shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.
CustomClassComponent.tsx:70 render: 컴포넌트를 렌더링합니다.
CustomClassComponent.tsx:53 getSnapshotBeforeUpdate: 업데이트 전 DOM 상태를 캡처합니다.
CustomClassComponent.tsx:58 componentDidUpdate: 컴포넌트가 업데이트되었습니다.
CustomClassComponent.tsx:59 이전 count: 2
CustomClassComponent.tsx:38 타이머 실행 중...
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:44 shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.
CustomClassComponent.tsx:70 render: 컴포넌트를 렌더링합니다.
CustomClassComponent.tsx:53 getSnapshotBeforeUpdate: 업데이트 전 DOM 상태를 캡처합니다.
CustomClassComponent.tsx:58 componentDidUpdate: 컴포넌트가 업데이트되었습니다.
CustomClassComponent.tsx:59 이전 count: 3
CustomClassComponent.tsx:38 타이머 실행 중...
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:44 shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.
CustomClassComponent.tsx:46 업데이트 중단: count가 5 이상입니다.
CustomClassComponent.tsx:38 타이머 실행 중...
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:44 shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.
CustomClassComponent.tsx:46 업데이트 중단: count가 5 이상입니다.
CustomClassComponent.tsx:38 타이머 실행 중...
CustomClassComponent.tsx:26 getDerivedStateFromProps: props로부터 state를 동기화합니다.
CustomClassComponent.tsx:44 shouldComponentUpdate: 컴포넌트 업데이트 여부를 결정합니다.
CustomClassComponent.tsx:46 업데이트 중단: count가 5 이상입니다.
... 반복하다가 
페이지 이탈시 componentWillUnmount에 의해 setInterval 중지됨</p>
<h2 id="주의할-점-안티패턴">주의할 점 (안티패턴)</h2>
<p>종종 브라우저 렌더링과 리액트의 리렌더링의 내용을 정확히 이해하지 못하고 사용하다보면 불필요한 리렌더링 작업이 발생할 수 있고, 서비스가 커지면 큰 성능이슈가 발생할 수 있으니 아래 내용은 주의해야한다. </p>
<h4 id="1-componentdidupdate에서-dom을-강제로-조작">1. componentDidUpdate에서 DOM을 강제로 조작</h4>
<ul>
<li>Virtual DOM을 통해 UI를 관리한다. 직접 DOM을 조작하면 React의 상태와 실제 DOM이 불일치하게 될 수 있어 예기치 않은 동작을 초래한다.<pre><code class="language-javascript">componentDidUpdate() {
 // React의 상태 관리와 무관하게 DOM을 강제로 조작
 const element = document.getElementById(&#39;custom-text&#39;);
 if (element) {
   element.textContent = &#39;This is directly manipulated!&#39;;
 }
}</code></pre>
</li>
</ul>
<h4 id="2-componentdidupdate에서-상태를-무조건-업데이트">2. componentDidUpdate에서 상태를 무조건 업데이트</h4>
<ul>
<li>상태를 무조건 업데이트하면 무한 루프가 발생할 수 있다.<pre><code class="language-javascript">// 잘못된 예
componentDidUpdate() {
this.setState({ count: this.state.count + 1 });
}
</code></pre>
</li>
</ul>
<p>// 옳은 예
componentDidUpdate(prevProps, prevState) {
  if (prevProps.someValue !== this.props.someValue) {
    this.setState({ updated: true });
  }
}</p>
<pre><code>
#### 3. componentWillUnmount에서 비동기 작업의 상태 업데이트
- 컴포넌트가 언마운트된 후 상태를 업데이트하려 하면 메모리 누수와 경고 메시지가 발생
```javascript
componentWillUnmount() {
  fetch(&#39;/api/data&#39;).then(() =&gt; {
    this.setState({ data: &#39;new data&#39; });
  });
}</code></pre><h4 id="4-render-메서드에서-부작용-실행">4. render 메서드에서 부작용 실행</h4>
<ul>
<li>render는 순수 함수처럼 동작해야 하며, 부작용(예: 네트워크 요청, 이벤트 등록 등)을 실행하면 React의 동작이 예측 불가능해질 수 있음</li>
<li>componentDidMount 에서 대신 처리해야함 </li>
</ul>
<h4 id="5-shouldcomponentupdate-에서-무거운-연산-수행">5. shouldComponentUpdate 에서 무거운 연산 수행</h4>
<ul>
<li>이 메서드는 렌더링 여부를 빠르게 판단하기 위한 용도로 사용됩니다. 여기에 시간이 많이 소요되는 작업을 넣으면 성능이 저하된다.</li>
</ul>
<h4 id="6-라이프사이클-메서드에서-이벤트-리스너를-등록하고-해제하지-않음">6. 라이프사이클 메서드에서 이벤트 리스너를 등록하고 해제하지 않음</h4>
<ul>
<li>리스너를 등록만 하고 해제하지 않으면 메모리 누수와 중복 등록 문제가 발생함</li>
<li>쌍으로 관리해야함</li>
</ul>
<h4 id="7-react의-상태-관리-메커니즘을-무시하고-외부-상태를-직접-변경">7. React의 상태 관리 메커니즘을 무시하고 외부 상태를 직접 변경</h4>
<ul>
<li>상태 관리는 항상 React의 setState 또는 useState를 사용하여 수행해야함</li>
</ul>
<p>자세한 설명은 아래 링크를 참조하자
<a href="https://ko.legacy.reactjs.org/docs/react-component.html">https://ko.legacy.reactjs.org/docs/react-component.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 6장 컴포넌트 반복]]></title>
            <link>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-6%EC%9E%A5-%EB%B0%98%EB%B3%B5%EB%AC%B8</link>
            <guid>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-6%EC%9E%A5-%EB%B0%98%EB%B3%B5%EB%AC%B8</guid>
            <pubDate>Tue, 21 Jan 2025 04:10:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>개발자로서 Map을 활용한 렌더링 기법은 정말 자주 사용하는 기술이다. 
사용법은 그리 어렵지 않으나, 주의해야할 점이 있고 이것에 대해서 자세히 알아보자!</p>
</blockquote>
<h2 id="javascript-map">Javascript Map</h2>
<p>배열의 요소를 순차적으로 돌며 콜백함수를 실행후 새로운 배열을 생성하는 자바스크립트 내장 함수이다. 함수 인자로는 콜백함수를 받는다. </p>
<h3 id="사용예시">사용예시</h3>
<pre><code class="language-javascript">// html에서
 &lt;ul&gt;
    list.map((element, index) =&gt; {
        return &lt;li&gt;{element}&lt;/li&gt;
    })
  &lt;/ul&gt;

// script에서
const newArray =&gt; list.map((item) =&gt; item + 1); 
</code></pre>
<h2 id="주의사항">주의사항</h2>
<h3 id="참조형-타입-불변성-유지">참조형 타입 불변성 유지</h3>
<p>리액트에서 JSX 를 다룰때를 제외하고도 
스크립트 내에서 배열 데이터를 변경할 때도 Map, forEach 와 같은 내장함수를 많이 사용한다. </p>
<p>그 이유는 리액트에서는 state의 불변성을 유지하여 데이터 변화를 감지하기 때문이다. </p>
<p>자바스크립트에서 배열이나, 객체 타입은 모두 <code>object</code> 타입이다. 
object 타입은 모두 참조형 데이터 타입이기 때문에 메모리 상 주소값을 참조하고 있다. </p>
<p>따라서 원본 배열이나 원본 객체를 바꾸는 로직들은 리액트의 감지 대상이 되지 못한다. 
(리액트를 처음 접한 사람은 왜 push 를 쓰면 안되는지 이해를 못하는 이유 ㅠㅠ)</p>
<p>리액트의 감지 대상이 되려면, 
<code>원시형 데이터</code>의 경우는 값을 그대로 바꾸면됨 
<code>참조형 데이터</code>의 경우는 새로운 메모리주소를 할당해주는게 포인트다. </p>
<p>새로운 메모리주소를 할당하는, 즉 데이터를 복사하는 방법에는 아래 방법들이 있다.</p>
<pre><code class="language-javascript">// spread
const copyList = [...originList]
const copyObject = {...originObject}

// javascript 함수 사용 1. 합치기
const copyList = [].concat(originList);
const copyObject = Object.assign({}, originObject)

// javascript 함수 사용 2. map
const copyList = originList.map(item =&gt; item);
</code></pre>
<h3 id="key-의-사용법">Key 의 사용법</h3>
<p>리액트에서 Map을 사용하여 JSX를 그릴때 단순히 렌더링만 한다면 콘솔창에 이러한 경고가 나타날 것이다. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/f2518709-c930-4d4b-92b0-93f341feee74/image.png" alt=""></p>
<p>반복문 또는 이터레이터 안에 각 자식요소들은 유니크한 키를 가져야한다라고 설명하고 있다. </p>
<p>사실 Warning 이기 때문에 애플리케이션이 먹통이 되거나 멈추는 일은 없다.</p>
<p>하지만 리액트를 쓰는 개발자라면 꼭! key를 추가해야한다. </p>
<h4 id="key의-역할">Key의 역할</h4>
<p>리액트에서는 가상돔을 활용하여 이전 상태와 새로운 상태를 비교하여 필요한 부분의 변화만 리렌더링을 실행하고, 나머지 요소들은 이전값을 사용한다. </p>
<p>배열이 추가되거나, 삭제되거나, 순서 변경하거나, 값이 변화할때 key는 React가 각 요소를 고유하게 식별하도록 식별자 역할을 한다. 
만약 key가 없다면 리액트는 리스트의 모든 요소를 다시 리렌더링 시킨다. (= 불필요한 리렌더링 방지)</p>
<h4 id="key가-되는-값들">key가 되는 값들</h4>
<ul>
<li><p>1순위: unique Id 값</p>
<pre><code class="language-javascript">const users = [
{id: 1, name: &#39;gabi&#39;},
{id: 2, name: &#39;kate&#39;},
{id: 3, name: &#39;sandy&#39;}
]

&lt;ul&gt;
  users.map((user) =&gt; {
      return &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
  })
&lt;/ul&gt;</code></pre>
</li>
<li><p>2순위: index 값</p>
</li>
</ul>
<p>만약 ID값이 없다면 map내의 index 값을 활용해도 된다.
다만 주의할 점이 있다!</p>
<p>만약 순서가 변경될 수 있는 여지가 있는 배열의 값이라면 index로 key값을 부여했을때 컴포넌트의 상태가 예상치 못하게 초기화되거나, 의도하지 않은 결과가 나올 수 있다. </p>
<p>index로 key를 생성하는 일은 되도록 지양해야하지만
변화하지 않은 단순 readonly성 리스트에서는 사용해도 괜찮다.</p>
<pre><code class="language-javascript">const items = [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;];

items.map((item, index) =&gt; &lt;input key={index} defaultValue={item} /&gt;);</code></pre>
<p>만약 배열이 [C, A, B]로 변경되면, C에 입력된 값이 유지되지 않을 수 있다.</p>
<ul>
<li>key로 불가능한 값: 변화하는 값
<img src="https://velog.velcdn.com/images/hope_k/post/98175576-a439-4e7d-94b0-e792013ffb68/image.png" alt=""></li>
</ul>
<p>좋아하는 UI형태는 아닌데
현업에서 이런식으로 테이블안에 input 형태가 들어가는 경우가 꽤 많다... 
이럴때 키 값을 변화할 수 있는 Name 값으로 주게된다면 입력할때마다 UI 가 뚝뚝 끊기는 것처럼 보이는 이상한 화면이 만들어지게 된다.</p>
<p>왜그럴까? 
리액트는 key를 식별자로 리렌더링을 위한 상태 비교가 이루어진다고 했다. 
근데 사용자가 Name을 입력할때마다 바뀐 값으로 key값이 새롭게 업데이트가되고 그와 동시에 리액트에서는 기존에 없는 key이기 때문에 해당 새로운 배열 요소라고 인지하여 새롭게 생성하고 렌더링 시키는 것이다. </p>
<p>따라서 변화하는 값으로 key를 부여하면 절대로 안된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 5장 
 Ref]]></title>
            <link>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-5%EC%9E%A5Ref</link>
            <guid>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-5%EC%9E%A5Ref</guid>
            <pubDate>Fri, 03 Jan 2025 08:38:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트를 개발하다 보면 종종 DOM에 직접 접근해야하는 경우가 발생한다. 이런 경우에 사용하는 Ref에 대해서 알아보자!</p>
</blockquote>
<h3 id="1-ref-란">1. Ref 란?</h3>
<p>리액트에서 ref는 DOM요소나 리액트 컴포넌트를 직접적으로 참조하기 위해 사용하는 속성이다. 
DOM의 속성과 내장 함수를 직접 사용하기위해 사용된다. </p>
<h3 id="2-ref-특징">2. Ref 특징</h3>
<h4 id="1-current-속성">(1) current 속성</h4>
<ul>
<li>리액트 내 Ref를 사용하여 DOM 에 접근하기 위해 <code>current</code> 속성을 사용한다. </li>
<li>초기값은 null이며, 컴포넌트가 마운트 된 후에 값이 설정된다. </li>
</ul>
<h4 id="2-렌더링-간-유지">(2) 렌더링 간 유지</h4>
<ul>
<li>상태값, Props 와 달리 렌더링 간 값이 유지되며, ref 값이 변경되어도 리렌더링 되지 않는다. </li>
</ul>
<h3 id="3-ref-생성하기">3. Ref 생성하기</h3>
<h4 id="클래스-컴포넌트">클래스 컴포넌트</h4>
<pre><code class="language-javascript">import React, { Component } from &#39;react&#39;;

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  focusInput = () =&gt; {
    this.inputRef.current.focus(); 
  };

  render() {
    return (
      &lt;div&gt;
        &lt;input ref={this.inputRef} type=&quot;text&quot; /&gt;
        &lt;button onClick={this.focusInput}&gt;Focus Input&lt;/button&gt;
      &lt;/div&gt;
    );
  }
}

export default MyComponent;
</code></pre>
<h4 id="함수형-컴포넌트">함수형 컴포넌트</h4>
<pre><code class="language-javascript">import React, { useRef } from &#39;react&#39;;

const MyComponent = () =&gt; {
  const inputRef = useRef(null);

  const focusInput = () =&gt; {
    inputRef.current.focus();
  };

  return (
    &lt;div&gt;
      &lt;input ref={inputRef} type=&quot;text&quot; /&gt;
      &lt;button onClick={focusInput}&gt;Focus Input&lt;/button&gt;
    &lt;/div&gt;
  );
};

export default MyComponent;</code></pre>
<h3 id="4-사용-시-주의사항">4. 사용 시 주의사항</h3>
<h4 id="1-최소한으로-사용">(1) 최소한으로 사용</h4>
<ul>
<li>리액트의 선언적 프로그래밍 원칙에 어긋날 수 있으므로 꼭 필요한 경우에만 사용한다. <h4 id="2-읽기-전용으로-사용">(2) 읽기 전용으로 사용</h4>
</li>
<li>Ref를 직접 수정하는 방식은 데이터흐름 파악이 어려울 수있다. </li>
<li>데이터는 State, 또는 Props를 사용할 것을 권장</li>
</ul>
<h3 id="5-ref-적절한-사용-사례">5. Ref 적절한 사용 사례</h3>
<h4 id="1-포커스-관리">(1) 포커스 관리</h4>
<p>사용자 경험 개선을 위해 포커스를 설정하거나 초기화</p>
<pre><code class="language-javascript">const AutoFocusInput = () =&gt; {
  const inputRef = useRef(null);

// 컴포넌트 마운트 시 자동 포커스
  useEffect(() =&gt; {
    inputRef.current.focus(); 
  }, []);

  return &lt;input ref={inputRef} type=&quot;text&quot; placeholder=&quot;Auto-focused Input&quot; /&gt;;
};</code></pre>
<h4 id="2-스크롤-제어">(2) 스크롤 제어</h4>
<p>특정 요소로 스크롤을 이동하거나, 스크롤 위치를 측정 및 조작할 때</p>
<pre><code class="language-javascript">const ScrollExample = () =&gt; {
  const scrollRef = useRef(null);

  const scrollToBottom = () =&gt; {
    scrollRef.current.scrollIntoView({ behavior: &#39;smooth&#39; });
  };

  return (
    &lt;div&gt;
      &lt;button onClick={scrollToBottom}&gt;Scroll to Bottom&lt;/button&gt;
      &lt;div ref={scrollRef} style={{ height: &#39;100px&#39;, background: &#39;blue&#39; }}&gt;&lt;/div&gt;
    &lt;/div&gt;
  );
};</code></pre>
<h4 id="3-파일-입력-요소-접근">(3) 파일 입력 요소 접근</h4>
<p>파일 업로드 기능 구현 시 ref로 파일 선택창을 트리거</p>
<pre><code class="language-javascript">const FileInput = () =&gt; {
  const fileInputRef = useRef(null);

  const handleClick = () =&gt; {
    fileInputRef.current.click(); // 파일 선택창 열기
  };

  return (
    &lt;div&gt;
      &lt;input ref={fileInputRef} type=&quot;file&quot; style={{ display: &#39;none&#39; }} /&gt;
      &lt;button onClick={handleClick}&gt;Upload File&lt;/button&gt;
    &lt;/div&gt;
  );
};</code></pre>
<h4 id="4-저장된-값-유지-mutable-object">(4) 저장된 값 유지 (Mutable Object)</h4>
<p>렌더링 간 값을 저장하고 유지해야 하지만, 상태를 변경하여 리렌더링할 필요가 없을 때</p>
<pre><code class="language-javascript">const Timer = () =&gt; {
  const countRef = useRef(0);

  useEffect(() =&gt; {
    const interval = setInterval(() =&gt; {
      countRef.current += 1;
      console.log(countRef.current); // 렌더링 없이 값 증가
    }, 1000);

    return () =&gt; clearInterval(interval);
  }, []);

  return &lt;div&gt;Check Console for Timer&lt;/div&gt;;
};</code></pre>
<h4 id="5-애니메이션-또는-외부-라이브러리-사용할때">(5) 애니메이션 또는 외부 라이브러리 사용할때</h4>
<p>외부 라이브러리(e.g., D3.js, Chart.js, Leaflet.js 등)와 React를 통합할 때 ref를 사용해 DOM 요소를 조작</p>
<pre><code class="language-javascript">import { useEffect, useRef } from &#39;react&#39;;
import Chart from &#39;chart.js/auto&#39;;

const ChartExample = () =&gt; {
  const canvasRef = useRef(null);

  useEffect(() =&gt; {
    new Chart(canvasRef.current, {
      type: &#39;bar&#39;,
      data: {
        labels: [&#39;Red&#39;, &#39;Blue&#39;, &#39;Yellow&#39;],
        datasets: [{
          label: &#39;Colors&#39;,
          data: [12, 19, 3],
        }],
      },
    });
  }, []);

  return &lt;canvas ref={canvasRef}&gt;&lt;/canvas&gt;;
};</code></pre>
<h4 id="6-react-컴포넌트-참조">(6) React 컴포넌트 참조</h4>
<p>추후 hooks에서 자세히...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 4장 이벤트 핸들링]]></title>
            <link>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-4%EC%9E%A5-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%A7%81</link>
            <guid>https://velog.io/@hope_k/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A5%BC-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EA%B8%B0%EC%88%A0-4%EC%9E%A5-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%ED%95%B8%EB%93%A4%EB%A7%81</guid>
            <pubDate>Thu, 26 Dec 2024 12:38:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>웹브라우저에서 이벤트 요소는 유저와의 상호작용에서 뺄래야 뺼 수 없는 중요한 요소이다. 정말 정말 자주 사용하는 마우스, 키보드 이벤트 외에도 다양한 이벤트가 있고 적재 적소에 활용한다면 좋은 UI/UX 를 만들 수 있을것이다. 😎</p>
</blockquote>
<p><span style="color: blue">이벤트란? 유저가 웹 브라우저에서 DOM 요소와 상호작용 하는 모든것을 말한다.</span></p>
<h2 id="리액트의-이벤트">리액트의 이벤트</h2>
<h3 id="1-reactsyntheticevent">1. React.SyntheticEvent</h3>
<p>자바스크립트 네이티브 이벤트(Event) 객체를 감싸는 리액트 객체이다. 네이티브 이벤트 인터페이스가 같으므로 순수 자바스크립트에서 HTML 이벤트를 다룰 때와 똑같이 사용한다. </p>
<h3 id="2-사용-시-주의사항">2. 사용 시 주의사항</h3>
<ul>
<li>HTML 이벤트 속성명과 달리 카멜케이스를 사용한다 (onclick -&gt; onClick)</li>
<li>이벤트 속성 내 값은 함수 형태의 값을 넣는다. </li>
<li>DOM 요소에만 이벤트를 설정 할 수 있다. </li>
</ul>
<h3 id="3-예제-코드">3. 예제 코드</h3>
<p><code>클래스형 컴포넌트</code></p>
<pre><code class="language-javascript">// 1번 생성자에 의해
class ClickButton extends Component {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
  }

  function handleClick()  {
    console.log(&#39;Button clicked!&#39;);
    alert(&#39;Button clicked!&#39;);
  };

  render() {
    return (
      &lt;div&gt;
        &lt;button onClick={this.handleClick}&gt;Click Me!&lt;/button&gt;
      &lt;/div&gt;
    );
  }
}

export default ClickButton;</code></pre>
<pre><code class="language-javascript">// 화살표 함수를 이용
class ClickButton extends Component {
   handleClick = () =&gt; {
    console.log(&#39;Button clicked!&#39;);
    alert(&#39;Button clicked!&#39;);
  };

  render() {
    return (
      &lt;div&gt;
        &lt;button onClick={this.handleClick}&gt;Click Me!&lt;/button&gt;
      &lt;/div&gt;
    );
  }
}

export default ClickButton;</code></pre>
<ul>
<li>1번 생성자를 이용한 함수 생성에 bind 해줘야 하는 이유
일반 함수(function() {...})에서 <code>this</code>는 함수를 호출하는 방법에 따라 결정되는 객체를 참조한다. 즉, <code>this</code>는 현재 실행 중(런타임)인 코드의 실행 컨텍스트에 따라 값이 결정된다.</li>
<li>2번 화살표 함수(() =&gt; {...})는 <strong>작성된 위치의 스코프(렉시컬 스코프)</strong>에서 <code>this</code>를 상속받아 사용한다. </li>
</ul>
<blockquote>
<ul>
<li>렉시컬 스코프(Lexical Scope)란? <strong>코드를 작성한 위치(문법적 구조)</strong>에 따라 변수의 유효 범위를 결정하는 방식이다. 
대부분의 현대 프로그래밍 언어는 렉시컬 스코핑을 사용한다.
즉, 어디서 호출되었는지가 아니라, 어디서 정의되었는지에 따라 스코프가 결정된다.</li>
</ul>
</blockquote>
<ul>
<li>다이나믹 스코프(Dynamic Scope)란? 다이나믹 스코프는 함수가 호출된 시점의 실행 컨텍스트를 기반으로 스코프를 결정하는 방식이다. 즉 런타임에 따라 변수 참조가 달라질 수 있다.</li>
</ul>
<p><code>함수형 컴포넌트</code></p>
<pre><code class="language-javascript">const ClickButton = () =&gt; {
  // 함수형 형태
  const handleClick = () =&gt; {
    console.log(&#39;Button clicked!&#39;);
    alert(&#39;Button clicked!&#39;);
  };

  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;Click Me!&lt;/button&gt;
    &lt;/div&gt;
  );
};</code></pre>
<h3 id="3-종류">3. 종류</h3>
<h4 id="clipboard-events">Clipboard Events</h4>
<ol>
<li>기본적으로 텍스트나 콘텐츠를 복사, 잘라내기, 붙여넣기 작업과 관련된 이벤트</li>
<li>주로 사용되는 요소는 <code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code></li>
<li>이벤트 종류</li>
</ol>
<ul>
<li><code>onCopy</code> 사용자가 복사(Copy) 동작을 수행할 때 발생하는 이벤트</li>
<li><code>onCut</code> 사용자가 잘라내기(Cut) 동작을 수행할 때 발생하는 이벤트</li>
<li><code>onPaste</code> 사용자가 붙여넣기(Paste) 동작을 수행할 때 발생하는 이벤트<h4 id="composition-events">Composition Events</h4>
</li>
</ul>
<ol>
<li><strong>IME(입력기)</strong>를 사용하는 언어(예: 한글, 일본어, 중국어 등)에서 입력 조합 상태를 추적할 수 있도록 설계된 이벤트</li>
<li>주로 사용되는 요소는 <code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code></li>
<li>이벤트 종류</li>
</ol>
<ul>
<li><code>onCompositionStart</code> 입력 조합이 시작될 때 발생 (예: ㅎ)</li>
<li><code>onCompositionUpdate</code> 입력 조합이 진행 중일 때 발생 (예: 하)</li>
<li><code>onCompositionEnd</code> 입력 조합이 완료될 때 발생 (예: 한)<h4 id="keyboard-events">Keyboard Events</h4>
</li>
</ul>
<ol>
<li>사용자가 키보드로 입력을 수행할 때 발생하는 이벤트</li>
<li>주로 사용되는 요소는 <code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code></li>
<li>이벤트 종류</li>
</ol>
<ul>
<li><code>onKeyDown</code> 키가 눌릴 때 발생하는 이벤트</li>
<li><code>onKeyPress</code> 키 입력 시 발생하는 이벤트 (<strong>Deprecated</strong>, <code>onKeyDown</code> 사용 권장)</li>
<li><code>onKeyUp</code> 키를 뗄 때 발생하는 이벤트</li>
</ul>
<ol start="4">
<li><p>예제</p>
<pre><code class="language-javascript">//  키 조합 동작 구현 (Ctrl + S)
function App() {
const handleKeyDown = (event: React.KeyboardEvent&lt;HTMLDivElement&gt;) =&gt; {
 if (event.ctrlKey &amp;&amp; event.key === &#39;s&#39;) {
   event.preventDefault(); // 기본 동작 방지
   alert(&#39;Ctrl + S 키가 눌렸습니다!&#39;);
 }
};

return (
 &lt;div
   tabIndex={0}
   onKeyDown={handleKeyDown}&gt;
   Ctrl+S를 눌러보세요
 &lt;/div&gt;
);
}
</code></pre>
</li>
</ol>
<pre><code>
#### Focus Events
1. 사용자가 요소에 포커스를 맞추거나 벗어날 때 발생하는 이벤트 
2. 주로 사용되는 요소는 `&lt;input&gt;`, `&lt;textarea&gt;`, `&lt;select&gt;`, `&lt;button&gt;` 
3. 이벤트 종류
- `onFocus` 요소에서 포커스가 맞춰질 때 발생하는 이벤트
- `onBlur` 요소에서 포커스가 벗어날 때 발생하는 이벤트
#### Form Events
1. 폼 요소에서 발생하는 다양한 상호작용 이벤트, 주로 입력 처리, 폼 제출, 유효성 검사 등의 작업
2. 주로 사용되는 요소는 `&lt;form&gt;`, `&lt;input&gt;`, `&lt;textarea&gt;`, `&lt;select&gt;` 
3. 이벤트 종류
- `onChange` 폼 요소가 입력 또는 선택에 의해 값이 변경될때 발생하는 이벤트
- `onInput` 사용자가 입력하는 즉시 발생
- `onInvalid` HTML5 폼 검증에서 유효성 검사 실패 시 발생
- `onReset` 폼이 리셋될 때 발생
- `onSubmit` 폼이 제출될 때 발생
&gt; `onInvalid`는 HTML5 유효성 검사 속성과 함께 작동한다. 
required: 필수 입력
min, max: 숫자 범위 제한.
minLength, maxLength: 문자열 길이 제한.
pattern: 정규식을 사용한 입력 형식 제한.
#### Image Events
1. 이미지를 포함한 리소스가 로드되거나 로드 중 에러가 발생했을 때 트리거되는 이벤트
2. 주로 사용되는 요소는 `&lt;img&gt;`, `&lt;script&gt;`, `&lt;link&gt;`
3. 이벤트 종류
- `onError` 이미지나 스크립트, 비디오 등에서 리소스를 찾지 못하거나 로드하지 못할 경우 발생하는 이벤트
- `onLoad` 이미지, 스크립트, 스타일 등에서 리소스가 완전히 로드된 경우 발생하는 이벤트
#### Mouse Events
1. 사용자가 마우스와 상호작용할 때 발생하는 이벤트이다. 클릭, 드래그, 이동 등 다양한 동작을 처리
2. 주로 사용되는 요소는 `&lt;button&gt;`, `&lt;div&gt;`, `&lt;span&gt;`, `&lt;a&gt;`
3. 이벤트 종류
- `onClick` 마우스 버튼을 클릭할 때 발생
- `onContextMenu` 마우스 우클릭(컨텍스트 메뉴 열기) 시 발생
- `onDoubleClick` 마우스를 두 번 클릭할 때 발생
- `onDrag` 드래그가 시작될 때부터 진행 중일 때까지 발생
- `onDragStart` 드래그가 시작될 때 발생
- `onDragEnd` 드래그가 끝났을 때 발생
- `onDragEnter` 드래그 중 요소에 들어왔을 때 발생
- `onDragLeave` 드래그 중 요소에서 벗어났을 때 발생
- `onDragOver`  드래그 중인 요소가 다른 요소 위에 있을 때 발생
- `onDrop` 드래그 중인 요소가 다른 요소 위에 드롭될 때 발생
- `onMouseDown` 마우스 버튼을 누를 때 발생
- `onMouseEnter` 마우스가 요소에 들어올 때 발생
- `onMouseLeave` 마우스가 요소에 벗어날 때 발생
- `onMouseMove` 마우스가 요소 안에서 이동할 때마다 발생
- `onMouseOut` 마우스가 요소 바깥으로 이동할 때 발생
- `onMouseOver` 마우스가 요소 위로 올라갈 때 발생. (하위 요소 포함, onMouseEnter와 다르게, 자식 요소에 진입해도 이벤트 발생)
- `onMouseUp` 마우스 버튼을 눌렀다가 뗄 때 발생
#### Touch Events
- `onTouchCancel` 터치 이벤트가 시스템에 의해 취소될 때 발생 (예, 전화수신, 다른 앱 활성화)
- `onTouchEnd` 사용자가 화면에서 손가락을 뗄 때 발생
- `onTouchMove` 사용자가 터치한 손가락을 움직일 때 발생
-`onTouchStart` 사용자가 화면을 터치하기 시작할 때 발생
#### Pointer Events
1. 마우스, 터치 스크린, 스타일러스 펜 등 다양한 입력 장치를 일관되게 처리할 수 있도록 설계된 이벤트, 기존 `Mouse Events`와 `Touch Events`를 통합한 이벤트 시스템으로, 모든 입력 장치를 포괄적으로 다룰 수 있음
2. 모든 DOM 요소에서 사용 가능
3. 이벤트 종류
- `onPointerDown` 포인터(마우스, 터치 등)가 요소를 누를 때 발생
- `onPointerMove` 포인터가 요소 위에서 이동할 때 발생
- `onPointerUp` 포인터가 눌린 상태에서 뗄 때 발생
- `onPointerCancel` 포인터 동작이 시스템에 의해 취소될 때 발생 (예: 터치 이벤트 중 다른 앱 실행)
- `onGotPointerCapture` 요소가 포인터 캡처(즉, 모든 포인터 이벤트를 해당 요소로 전달)를 획득할 때 발생
- `onLostPointerCapture` 요소가 포인터 캡처를 잃을 때 발생
- `onPointerEnter` 포인터가 요소에 들어올 때 발생 (버블링되지 않음
- `onPointerLeave` 포인터가 요소를 벗어날 때 발생 (버블링되지 않음)
- `onPointerOver` 포인터가 요소 위로 올라갈 때 발생 (하위 요소 포함)
- `onPointerOut` 포인터가 요소 바깥으로 이동할 때 발생 (하위 요소 포함)
4. 터치 장치 또는 스타일러스 펜을 다루는 UI에 유용, 다양한 입력 장치를 지원하는 애플리케이션에 유용
#### Selection Events
1. 사용자가 텍스트를 드래그를 통해, 또는 더블클릭으로 선택했을 때 발생하는 이벤트
2. 주로 사용되는 요소는 `&lt;input&gt;`, `&lt;textarea&gt;`
3. 이벤트 종류
- `onSelect`  사용자가 텍스트를 선택했을 때 발생하는 이벤트 (마우스로 드래그하거나 키보드로 텍스트를 선택할 때 발생함)
#### UI Events
- `onScroll` 사용자가 스크롤 동작을 수행할 때 발생하는 이벤트
#### Wheel Events
- `onWheel` 이벤트는 사용자가 마우스 휠을 스크롤하거나 터치패드로 스크롤 동작을 수행할 때 발생.
#### Animation Events
- `onAnimationStart` CSS 애니메이션이 시작될 때 발생
- `onAnimationEnd` CSS 애니메이션이 종료될 때 발생
- `onAnimationIteration` CSS 애니메이션이 한 번 반복될 때마다 발생
#### Transition Events
- `onTransitionEnd` CSS 트랜지션이 끝났을 때 발생
#### Other Events
- `onToggle` onToggle 이벤트는 HTML5 `details` 요소가 열리거나 닫힐 때 발생


&gt; _자세한 사항은 아래 링크를 통해 확인가능하다. _
[SyntheticEvent 공식문서 링크](https://legacy.reactjs.org/docs/events.html)

---
## 이벤트 흐름
### 1. 이벤트 버블링(Event Bubbling)
- 개념: 이벤트가 발생한 요소에서 **부모 요소로 전파되는 과정**이다.
- 특징: 기본동작이며, 대부분 이벤트 `onClick`, `onChange` 등에서 적용된다.
```javascript
function App() {
  const handleDivClick = () =&gt; {
    console.log(&#39;부모 &lt;div&gt; 클릭&#39;);
  };

  const handleButtonClick = () =&gt; {
    console.log(&#39;자식 &lt;button&gt; 클릭&#39;);
  };

  return (
    &lt;div onClick={handleDivClick}&gt;
      &lt;button onClick={handleButtonClick}&gt;클릭&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre><p><strong>Console 결과</strong></p>
<pre><code>자식 &lt;button&gt; 클릭
부모 &lt;div&gt; 클릭</code></pre><h3 id="2-이벤트-캡쳐링event-capturing">2. 이벤트 캡쳐링(Event Capturing)</h3>
<ul>
<li><p>개념: 이벤트가 발생한 요소까지 부모 요소에서 <strong>자식 요소로 내려오며 전파</strong>되는 과정이다.</p>
</li>
<li><p>특징: 기본적으로 React의 SyntheticEvent는 <strong>버블링 이벤트</strong>를 처리한다. 캡처 단계에서 처리하려면 <code>onClickCapture</code>와 같은 Capture 이벤트를 사용해야 함</p>
<pre><code class="language-javascript">function App() {
const handleDivClick = () =&gt; {
  console.log(&#39;부모 &lt;div&gt; 클릭 (캡처)&#39;);
};

const handleButtonClick = () =&gt; {
  console.log(&#39;자식 &lt;button&gt; 클릭&#39;);
};

return (
  &lt;div onClickCapture={handleDivClick}&gt;
    &lt;button onClick={handleButtonClick}&gt;클릭&lt;/button&gt;
  &lt;/div&gt;
);
}</code></pre>
</li>
<li><p><em>Console 결과*</em></p>
<pre><code>부모 &lt;div&gt; 클릭 (캡처)
자식 &lt;button&gt; 클릭</code></pre></li>
</ul>
<h3 id="3-이벤트-중단">3. 이벤트 중단</h3>
<ol>
<li><p><code>stopPropagation</code> 이벤트가 부모로 전파되지 않도록 중단</p>
<pre><code class="language-javascript">function App() {
const handleDivClick = () =&gt; {
 console.log(&#39;부모 &lt;div&gt; 클릭&#39;);
};

const handleButtonClick = (event: React.MouseEvent) =&gt; {
 event.stopPropagation(); // 부모로 전파 중단
 console.log(&#39;자식 &lt;button&gt; 클릭&#39;);
};

return (
 &lt;div onClick={handleDivClick}&gt;
   &lt;button onClick={handleButtonClick}&gt;클릭&lt;/button&gt;
 &lt;/div&gt;
);
}</code></pre>
</li>
</ol>
<p><strong>Console 결과</strong></p>
<pre><code>자식 &lt;button&gt; 클릭</code></pre><ol start="2">
<li><p><code>preventDefault</code> 커스텀 동작을 정의하거나 기본 동작을 비활성화 하기위한 기본 동작(예: 링크 이동, 폼 제출)을 막기 위해 사용</p>
<pre><code class="language-javascript">function App() {
const handleLinkClick = (event: React.MouseEvent) =&gt; {
 event.preventDefault(); // 링크 이동 막기
 console.log(&#39;커스텀 동작 실행&#39;);
};

return &lt;a href=&quot;https://example.com&quot; onClick={handleLinkClick}&gt;링크&lt;/a&gt;;
}</code></pre>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSR, SSR]]></title>
            <link>https://velog.io/@hope_k/%EB%A0%8C%EB%8D%94%EB%A7%81</link>
            <guid>https://velog.io/@hope_k/%EB%A0%8C%EB%8D%94%EB%A7%81</guid>
            <pubDate>Tue, 24 Dec 2024 05:09:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>프론트엔드 개발자는 서비스의 특성에 따라 요구되는 렌더링을 잘 이해하고 구현할 수 있어야한다.</p>
</blockquote>
<h2 id="렌더링">렌더링</h2>
<h3 id="1-개념">1. 개념</h3>
<p>화면에 표시할 웹 페이지를 만드는 과정을 의미</p>
<h3 id="2-렌더링-과정">2. 렌더링 과정</h3>
<p>1) 로더가 서버로 부터 HTML을 불러옴
2) HTML을 파싱하여 DOM Tree 생성
3) CSS 파일과, 스타일 요소를 분류하여 CSSOM 트리를 생성
4) DOM 트리와 CSSOM 트리를 결합하여 렌더링 트리를 생성
5) 렌더링 트리의 요소들의 크기와 위치를 계산
6) 계산된 크기와 위치에 맞게 화면에 출력</p>
<p><span style="color: red">이러한 렌더링 과정중 Client와 Server 중 어느쪽에서 렌더링이 준비되느냐에 따라 CSR과 SSR로 나뉘게 된다. </span></p>
<h2 id="client-side-rendering-csr">Client Side Rendering (CSR)</h2>
<h3 id="1-csr이란">1. CSR이란?</h3>
<p>말 그대로 클라이언트 측에서 렌더링을 담당하는 기법이다.</p>
<h3 id="2-동작과정">2. 동작과정</h3>
<p><img src="https://velog.velcdn.com/images/hope_k/post/d9a22cea-8194-45c9-99c3-d9c3d13242e3/image.png" alt=""></p>
<p>(1) 사용자가 웹사이트에 방문하면 브라우저는 서버에 콘텐츠 (정적파일)을 요청한다. 
(2) 서버는 script, meta, link 등 태그가 포함된 최소한의 HTML과 연결된 JS 번들을 반환한다.
(3) 브라우저는 반환받은 JS 파일을 다운로드하고 실행하면서 필요한 데이터를 API 서버에 요청한다. 
(4) API에서 데이터를 받아와 자바스크립트를 활용하여 동적인 페이지(DOM)을 생성한다. 
(5) 사용자가 다른 페이지를 방문하면, 서버에 HTML을 요청하지 않고, 이미 받은 자바스크립트를 이용해 렌더링 한다. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/152553ae-be90-41e0-94a9-269a6bf69148/image.png" alt=""></p>
<p>(1) localhost 호출 후 최소한의 HTML 반환
(2) 서버에서 받은 font, CSS파일과 JS파일, 이미지를 다운로드
(3) API를 통해 데이터를 받아와 동적으로 렌더링</p>
<h3 id="3-장단점">3. 장단점</h3>
<h4 id="장점">장점</h4>
<ul>
<li><code>빠른 사용자 경험</code> 한번 로드되면 페이지 간 이동이 빠름, 새로고침 없이 SPA 처럼 동작</li>
<li><code>유연한 UI에 최적화</code> 자바스크립트를 사용하여 복잡한 인터랙션과 UI를 구현하기 적합하다. </li>
<li><code>서버 부하 감소</code> 초기 요청에 HTML만 전달하고 이후부터는 클라이언트에서 요청하여 렌더링하기때문에 서버 부담이 적다. </li>
<li><code>SPA에 적합</code> CSR은 SPA 구조에 잘 맞다. 필수는 아님(React, Vue, Angular 등)<h4 id="단점">단점</h4>
</li>
<li><code>SEO 문제</code> 콘텐츠가 브라우저에서 생성되므로, 검색 엔진이 콘텐츠를 크롤링하기 어렵다. 이를 해결하기 위해 SSR 이나 Hydration 같은 기술을 추가로 사용한다. </li>
<li><code>초기 로딩 시간</code> 초기 렌저링 시 자바스크립트 번들을 다운로드 하고 실행해야 하므로, 첫 화면 표시까지 시간이 다소 걸린다. </li>
<li><code>JS 의존성</code> 브라우저가 자바스크립트를 비활성화하면 페이지 동작이 제한됨</li>
</ul>
<h4 id="관련된-주요-기술">관련된 주요 기술</h4>
<ul>
<li><code>React, Vue, Angular</code> CSR을 주로 사용하는 프레임워크들, DOM을 동적으로 관리함</li>
<li><code>Next.js, Nuxt.js</code> React와 Vue 기반의 프레임워크로 근래에는 CSR, SSR 모두 혼합하여 사용가능하다</li>
<li><code>Webpack, Vite</code> 자바스크립트 번들링과 빌드 툴로 CSR 환경에서 필수적임</li>
</ul>
<h4 id="csr-최적화-전략">CSR 최적화 전략</h4>
<ul>
<li><code>코드 스플리팅(Code Splitting)</code> 페이지별로 자바스크립트 번들을 나눠서 필요한 부분만 로드 </li>
<li><code>Lazy Loading</code> 필요할 때만 컴포넌트를 로드하여 초기 로딩 시간을 단축</li>
<li><code>Hydration</code> 서버에서 렌더링된 HTML을 브라우저에서 자바스크립트로 보강하여 동적인 상태로 전환</li>
<li><code>캐싱</code> 자바스크립트와 데이터를 브라우저 캐시에 저장하여 재방문시 빠르게 로딩</li>
<li><code>Preloading</code> 사용자가 필요로 할 데이터를 미리 가져와 로딩 시간을 줄임</li>
</ul>
<h2 id="server-side-rendering-ssr">Server Side Rendering (SSR)</h2>
<h3 id="1-ssr이란">1. SSR이란?</h3>
<p>말 그대로 서버 측에서 렌더링을 담당하는 기법이다.</p>
<h3 id="2-동작과정-1">2. 동작과정</h3>
<p><img src="https://velog.velcdn.com/images/hope_k/post/3bc7cfc8-1db1-4237-8783-27ac6e004332/image.png" alt=""></p>
<ol>
<li>사용자가 웹사이트에 방문하면 브라우저는 서버에 콘텐츠를 요청한다. </li>
<li>서버는 페이지에 필요한 모든 데이터 및 css까지 적용해서 렌더링 준비를 마친 HTML과 자바스크립트 코드를 브라우저에 반환한다.</li>
<li>완전한 HTML을 클라이언트로 전달한다.</li>
<li>브라우저는 HTML을 즉시 렌더링하여 사용자에게 보여준다. </li>
<li>브라우저는 HTML을 렌더링한 후 자바스크립트를 다운로드하고 실행한다. 실행된 자바스크립트는 HTML과 연결되어 페이지를 동적인 SPA로 전환한다. (Hydration)</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hope_k/post/c7058a7e-bb22-4e43-b50e-32e722e2b679/image.png" alt=""></p>
<p>(1) localhost를 호출하여 데이터를 서버에서 페치하여 렌더링 가능한 상태로 HTML을 반환함
(2) 브라우저는 즉시 HTML을 그림
(3) 이후에 JS 파일을 다운로드 받아 동적인 상태로 바꿈 (하이드레이션)</p>
<h3 id="3-장단점-1">3. 장단점</h3>
<h4 id="장점-1">장점</h4>
<ul>
<li><code>SEO 친화적</code> 서버에서 완전한 HTML을 제공하므로 검색 엔진이 콘텐츠를 쉽게 크롤링할 수 있음 (SEO에 필수적인 페이지는 SSR로 개발해야함!!)</li>
<li><code>빠른 초기 렌더링</code> 브라우저가 자바스크립트를 실행하기 전에 HTML을 렌더링하여 사용자에게 빠른  첫 화면을 제공함 </li>
<li><code>동적 데이터 포함</code> 서버 내부에서 데이터를 fetch하여 반영하기 때문에 최신 데이터를 반영한 HTML 생성가능 <h4 id="단점-1">단점</h4>
</li>
<li><code>서버 부하 증가</code> 서버가 매 요청마다 HTML을 생성해야 하므로 부하가 증가.</li>
<li><code>느린 응답 시간</code> 데이터 처리 및 HTML 생성으로 초기 응답 시간이 길어질 수 있음.</li>
<li><code>복잡성 증가</code> 클라이언트와 서버에서 모두 렌더링 로직을 관리해야 함.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSE 이용하여 브라우저 실시간 알림 서비스 구현하기]]></title>
            <link>https://velog.io/@hope_k/SSE-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%95%8C%EB%A6%BC-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hope_k/SSE-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%95%8C%EB%A6%BC-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 23 Dec 2024 06:44:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트 엔진의 대한 이해로 SSE를 활용하여 브라우저 알림 서비스를 개발한 내용을 작성해보겠다!</p>
</blockquote>
<p>우선 SSE란 뭘까?
쉽게 말하면 TCP 기반의 HTTP 프로토콜을 사용하여 Streaming API 통신을 통해 일정 시간동안 서버와의 연결을 유지하여 실시간으로 서버에서 응답을 받을 수 있는 방법이다. 
자주 사용하는 Rest API 통신과는 다른 점이 있다. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/c5b8eb02-bcdf-44b0-9eb6-7ed172aea9d5/image.png" alt=""></p>
<h4 id="rest-api-통신-방법">Rest API 통신 방법</h4>
<ul>
<li>사용자 이벤트에 의해 클라이언트는 데이터를 비동기 요청으로 호출함 (Request)</li>
<li>요청을 받은 서버는 그에 대한 대답을 브라우저로 응답함 (Response)</li>
<li>요청과 응답은 서버에서 설정한 시간내에 이루어져야하며 그 이상의 응답을 받지 못하면 Timeout 처리로 통신 실패 처리함 </li>
<li>클라이언트(브라우저)는 항상 API를 통해 데이터를 요청하는 입장, 서버는 항상 브라우저의 요청을 수신받아 응답하는 입장이다. (역으로 불가능)</li>
</ul>
<h4 id="sreaming-api-통신-방법">Sreaming API 통신 방법</h4>
<ul>
<li>클라이언트는 서버에 연결을 요청함 (TCP handshake)</li>
<li>연결에 성공하면 서버와 지속적인 연결을 유지시킴 </li>
<li>서버는 이벤트를 발생시켜 텍스트 형식의 데이터 조각(chunk)를 클라이언트로 푸시함 </li>
<li>클라이언트는 Web API의 내장 window의 addEventListener 를 사용하여 서버에서 오는 데이터를 실시간으로 처리함. (비동기처리)</li>
<li>서비스 규칙에 따라 설정된 시간내에 이벤트를 받지 못하거나, 클라이언트 또는 서버에서 연결을 종료시킬 수 있음</li>
</ul>
<blockquote>
<p>클라이언트가 먼저 종료할 경우? </p>
</blockquote>
<ul>
<li>클라이언트는 SSE 연결을 종료하기 위해 EventSource.close() 메서드를 호출한다.</li>
<li>이로 인해 클라이언트는 현재 TCP 연결을 종료하며, 서버는 연결 종료를 감지한다.</li>
<li>서버는 연결 종료를 감지하고 연결을 정리한다. </li>
</ul>
<blockquote>
<p>서버에서 먼저 종료 할 경우? </p>
</blockquote>
<ul>
<li>서버가 응답을 종료하거나 close 메세지를 전달함 </li>
<li>서버가 필요한 로직이 수행되여 더이상 연결이 필요하지 않을때 마지막 메세지 전달 여부를 확인(Ack 응답) 하고 종료시킨다.</li>
<li>대부분의 SSE 구현에서는 TCP의 신뢰성에 의존해 메시지를 보낸 뒤 바로 연결을 종료한다.</li>
</ul>
<p>그럼 본격적으로 리액트, Next에서 SSE 알림서비스를 어떻게 개발했는지 작성해보도록 하겠다. </p>
<p><strong>서비스 요구사항</strong></p>
<ol>
<li>대용량 파일을 다운로드 요청함</li>
<li>파일 다운로드가 완료되면 서버에서 완료 처리 메세지를 받아 다운로드 링크 url을 유저에게 제공함</li>
<li>다운로드 중에 유저는 프로즌 브라우저가 아닌 웹서비스 내에서 어떤 동작도 가능하도록 해야함 (비동기처리)</li>
</ol>
<p><strong>개발 시 고려해야 할 사항</strong></p>
<ol>
<li>여러 페이지 내 공통 서비스 로직으로 공통 모듈로 개발해야함</li>
<li>웹 페이지 내 페이지 이동이 있어도 알림을 정상적으로 수신할 수 있어야함 (=리액트 컴포넌트 라이프사이클과는 별개로 핸들링할 수 있는 객체와 로직이 필요하다)</li>
</ol>
<hr>
<p>아래부터는 시도해본 개발 내용들이다... </p>
<p>우선 회사 서비스는 아래와 같은 컴포넌트 구조로 되어있었다. 
별로 체크한 곳에서 파일 다운로드 요청이 이루어지고 어디에선가 공통으로 이벤트를 처리할 곳이 필요했다. 
<img src="https://velog.velcdn.com/images/hope_k/post/ab0d9f3b-6586-42b0-9111-15b43a4c9e18/image.png" alt=""></p>
<h4 id="방법-1-top-header에서-이벤트-핸들러-수신하기">방법 1. Top Header에서 이벤트 핸들러 수신하기</h4>
<p>공통 모듈로 개발하려다보니 여러가지 방법중 제일 보편적으로 생각했던 방법이다. </p>
<p>브라우저에서 SSE는 EventSource 객체를 생성하여 구현하는데, 해당 객체에 처리가 필요한 리스너를 함께 등록해야한다. </p>
<p>Rest API를 통해 다운로드를 요청하면 알림서버에서 각각 고유한 키로 사용될 <code>uuid</code>가 떨어졌다. </p>
<p>해당 uuid를 통해 알림서버에 연결요청을 한다. 
그런데 다운로드 요청은 children 컴포넌트에서 이루어지고 EventSource 처리는 Top Header에서 처리하려고 하다보니 uuid를 Top Header에 전달해줄 매개체가 필요했다.</p>
<p>Vue 개발할때는 이벤트버스 라는게 있었는데, (동료분께서) 리액트에서는 잘 사용하지 않는다고 하시길래
다른방법을 찾아보았으나, Store로 전달하는 방법밖에 떠오르지 않았다. </p>
<p>그런데 한가지 마음에 걸리는게 있었다 ㅠㅠ
공용 Store로 데이터를 전달하고 핸들링하다보면 예기치못한 렌더링이 일어날 수도 있고, 데이터를 직접 할당, 삭제 등등 핸들링이 필요하기때문에 과연 이 방법이 적당한가 고민이 되었다...</p>
<h4 id="방법-2-eventsource-class-객체-생성하기">방법 2. EventSource Class 객체 생성하기</h4>
<p>개발하다 보니, 꼭 Top Header에서만 이벤트를 처리하지 않아도 브라우내 내장 객체인 WebAPI, Task Queue, GC를 잘 활용하면 될것 같다는 생각이 들었다.</p>
<pre><code class="language-javascript">// 다운로드 호출부
const fileDownlaodHandler = (uuid: string) =&gt; {
    try {
      const eventSource = new NotificationEventSource(&#39;API URL&#39;);
      eventSource.doProcess(uuid)
    } catch(e) {
      // error 핸들링
    }
}</code></pre>
<pre><code class="language-javascript">import { type Event, EventSourcePolyfill } from &#39;event-source-polyfill&#39;
import { type EventSourcePolyfillInit } from &#39;event-source-polyfill&#39;
import { toast } from &#39;react-hot-toast&#39;

export default class NotificationEventSource {
    private _eventSource: EventSourcePolyfill | null = null 
    private url = &#39;서버 요청 url&#39;


      constructor(url: string) {
      this.url = url;
    }

    public doProcess(uuid: string) {
      if(!uuid) {
           throw {message: &#39;잘못된 요청입니다.&#39;}; 
      }

      const fetchUrl = `${this.url}/${uuid}`
      this._eventSource = new EventSourcePolyfill(fetchUrl) 

      this._eventSource.addEventListener(&#39;connect&#39;, () =&gt; {
          // 연결 처리 callback 함수
      }) 

      this._eventSource.addEventListener(&#39;error&#39;, () =&gt; {
          // 에러 처리, Closed 처리 callback 함수
        // ...
        this.close() // 메모리 해제
      }) 

      this._eventSource.addEventListener(&#39;completed&#39;, () =&gt; {
          // 완료 처리 callback 함수
        // .... 

        toast.custom((t) =&gt; {
         return &lt;div&gt;알림 완료 토스트입니다!&lt;/div&gt;
        }, {
            position: &#39;bottom-center&#39;,
            icon: null,
            duration: 10000,
          })

        this.close() // 메모리 해제
      }) 

    }

  private close() {
    this._eventSource?.close()
    this._eventSource = null
  }
}
</code></pre>
<p><strong>처리과정은 아래와 같다.</strong></p>
<p>1) <code>fileDownlaodHandler</code> 호출
2) <code>NotificationEventSource</code> 객체가 메모리힙에 생성
3) 해당 인스턴스의 <code>doProcess</code> 실행 
4) connect, error, completed 의 대한 콜백 함수가 메모리 힙에 등록
5) 서버에서 메세지 수신되면 핸들러에 의해 비동기 이벤트 처리
(이벤트 루프에 의해 핸들러 Callback 함수가 콜스택으로 이동하는 과정) 
6) 완료처리 혹은 서버 에러 발생시 <code>close</code> 함수를 통해 메모리 해제 </p>
<p>6번의 메모리 해제가 명시적으로 있을때까지 EventSource 인스턴스 및 참조객체들은 메모리힙에 계속 살아있는 상태가되기때문에 GC의 대상이 아니게 된다. </p>
<p>그래서 페이지를 이동해도 메모리힙의 객체는 살아있고
이벤트 루프에 의해 WebAPI 에서 Callback 함수를 큐에 대기시켜 토스트를 띄울 수 있는것이다. </p>
<p>추가로 새로고침시 메모리 및 데이터가 전부 초기화 되기때문에
이럴 경우 어떻게 방어할지는 고민해봐야할것 같다... </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 엔진 구조 (2) 메모리힙]]></title>
            <link>https://velog.io/@hope_k/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D-%EB%A9%94%EB%AA%A8%EB%A6%AC%ED%9E%99</link>
            <guid>https://velog.io/@hope_k/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D-%EB%A9%94%EB%AA%A8%EB%A6%AC%ED%9E%99</guid>
            <pubDate>Mon, 23 Dec 2024 03:24:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트 엔진에 두번째 파트인 메모리힙에 대해서 알아보자!</p>
</blockquote>
<h2 id="구성요소">구성요소</h2>
<p>자바스크립트 엔진에는 크게 두가지의 구성요소를 가지고있다.
<img src="https://velog.velcdn.com/images/hope_k/post/94923262-9685-458d-b4f3-e2cc9ab39d90/image.png" alt=""></p>
<p>먼저 들어가기 앞서 자료구조 Heap에 대해서 간단하게 알아보겠다.</p>
<h3 id="자료구조-힙heap이란">자료구조 힙(heap)이란?</h3>
<ul>
<li>완전 이진 트리 기반의 자료구조로, 최대 힙과 최소 힙 중 하나를 만족하는 특성을 갖는다. </li>
<li>여러 개의 값들 중에서 최대값이나 최솟값을 빠르게 찾도록 만들어진 자료구조이다. </li>
</ul>
<blockquote>
<ul>
<li>최대 힙 (Max-Heap): 부모 노드의 값이 자식 노드의 값보다 항상 크거나 같음</li>
</ul>
</blockquote>
<ul>
<li>최소 힙 (Min-Heap): 부모 노드의 값이 자식 노드의 값보다 항상 작거나 같음</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hope_k/post/42968d46-4ee6-4e4e-90f1-c0b3238a47e9/image.png" alt=""></p>
<h4 id="사용예시">사용예시</h4>
<ul>
<li>우선순위 큐: 작업 스케쥴링</li>
<li>힙 정렬: 효율적인 정렬 알고리즘 구현</li>
<li>동적 데이터 처리: 최대값이나 최소값 유지에 유리한 데이터 처리 방식</li>
</ul>
<h3 id="메모리힙">메모리힙</h3>
<p>자바스크립트가 실행될때 콜스택이 원시형 데이터를 저장한다면, 
메모리 힙은 동적인 데이터인 <strong>참조타입의 데이터 (객체, 배열, 함수...) 등이 저장되는 저장소</strong>이다. </p>
<h4 id="왜-자료구조인-힙heap을-사용하는가">왜 자료구조인 힙(heap)을 사용하는가?</h4>
<ol>
<li>동적 메모리 할당</li>
</ol>
<ul>
<li>객체나 배열 등 참조 타입의 크기는 런타임에 결정되므로, 정적인 스택에 올리기 어렵다. </li>
<li>힙은 동적으로 크기가 변할 수 있는 데이터를 저장하기 적합하다. </li>
</ul>
<ol start="2">
<li>유연한 메모리 관리</li>
</ol>
<ul>
<li>프로그램 실행 중 필요한 데이터를 동적으로 할당하고 해제할 수 있다.</li>
<li>자바스크립트의 <code>가비지 컬렉션(GC)</code>은 힙 영역의 사용되지 않는 메모리를 자동으로 해제하여 메모리 누수를 방지한다. </li>
</ul>
<ol start="3">
<li>객체와 참조의 관리</li>
</ol>
<ul>
<li>객체는 종종 다른 객체를 참조하거나 큰 데이터를 포함한다. 힙은 이러한 구조를 효율적으로 관리할 수 있는 공간을 제공한다. </li>
</ul>
<h3 id="가비지-컬렉터-garbage-collection">가비지 컬렉터 (Garbage Collection)</h3>
<p>자바스크립트에서 변수나 함수 등 모든것을 만들때 자바스크립트 엔진은 메모리를 할당하고 더 이상 필요하지 않으면 해제한다. 
메모리 할당은 메모리 공간을 예약하는 과정이며, 메모리를 해제하면 공간이 확보되어 다른 용도로 사용 할 수 있게 된다. </p>
<p>아래와 같은 순서로 메모리 라이프 사이클은 동작한다. </p>
<ol>
<li><code>메모리 할당</code> 자바스크립트는 생성한 객체에 대해 필요한 메모리를 할당한다. </li>
<li><code>메모리 사용</code> 코드에서 명시적으로 수행되는 작업으로, 메모리를 읽고 쓰는 작업은 곧 변수에서 읽거나 변수에 쓰는 작업을 말한다.</li>
<li><code>메모리 해제</code> 엔진은 사용한 메모리를 해제하고 새로운 용도로 쓰도록 메모리를 해제한다. </li>
</ol>
<p>그런데 우리는 메모리를 해제하는 작업을 예외상황이 아니면 별도로 하지 않는다. </p>
<blockquote>
<p>예외상황: 클로져 함수를 사용하는 경우에는 명시적으로 null 처리를 함</p>
</blockquote>
<p>메모리 해제는 자바스크립트 엔진 내 <strong>가비지 컬렉터(이하 GC)가 알아서 해주고 있기 때문에 개발자는 이를 신경쓰지 않아도 됐던 것</strong>이다. </p>
<p>GC는 어떤 변수나 함수가 더 이상 필요하지 않다는 것을 파악하면 이것을 차지하고 있던 메모리를 해제한다. </p>
<p>그런데 GC는 어떻게 더이상 쓰지 않은 데이터를 어떻게 수집하여 해제하는 것일까?</p>
<h4 id="1-기본원칙-reachablilty-도달-가능성">1. 기본원칙: Reachablilty (도달 가능성)</h4>
<ul>
<li>객체가 메모리에 계속 남아있을 필요가 있는지 판단하는 기준이다. </li>
<li>루트에서부터 참조(Chain)에 의해 접근가능한 모든 변수, 호출 스택등을 파악하여 도달 가능성이 절대 없는 객체는 GC의 수집대상이 된다. </li>
</ul>
<h4 id="2-gc-과정">2. GC 과정</h4>
<ul>
<li>Mark (마킹단계): GC는 루트객체부터 시작하여, 모든 도달가능한 객체를 탐색하고 마킹한다. </li>
<li>Sweep (정리단계): 마킹되지 않은 객체는 도달 불가능한 것으로 판단하고 메모리에서 해제한다. 
<img src="https://velog.velcdn.com/images/hope_k/post/ac510029-5995-43e5-8e7d-5929a2690ea4/image.png" alt=""></li>
</ul>
<h4 id="3-세대별-가비지-컬렉션">3. 세대별 가비지 컬렉션</h4>
<p>자바스크립트 엔진은 세대별 가비지 컬렉션 전략을 활용하여 성능을 최적화한다. 메모리를 영 객체와 장기 객체로 나누어 관리한다. </p>
<ul>
<li>영 객체: 새롭게 생성된 객체는 영세대로 간주한다. 대부분 객체는 수명이 짧으므로 이 영역에 수집된다. 이 과정을 Minor GC 라고한다. </li>
<li>장기 객체: 영세대에서 살아남은 객체는 장기 객체로 취급한다. 장기 객체는 상대적으로 크고 수집 빈도가 낮다. 이 과정을 Major GC 또는 Full GC 라고 하며, 큰 리소스를 소요한다. </li>
</ul>
<h4 id="메모리-누수와-gc의-한계">메모리 누수와 GC의 한계</h4>
<p>메모리 누수란? 쉽게 말해 GC가 정리하지 못한 불필요한 데이터가 힙 영역에서 존재하는것을 말한다. </p>
<p>우리는 메모리 누수가 발생하지 않도록 개발시 주의를 해야한다. 
아래와 같은 개발 방식은 로직이 끝난 후 메모리 해제 로직도 함께 꼭 추가되어야 한다. </p>
<p>메모리 해제는 변수에 <code>null</code>을 할당하면 됨</p>
<p><strong>[메모리 누수가 발생하는 대표 상황]</strong></p>
<p><strong>1. 순환참조</strong></p>
<pre><code class="language-javascript">function createCircularReference() {
    let objA = {};
    let objB = {};

      objA.ref = objB; // 서로 참조하는 상황
      objB.ref = objA;
    return objA;
}
// 순환참조로 인해 GC가 메모리 해제를 하지않음
let circular = createCircularReference();

// 메모리 해제
circular = null
</code></pre>
<p>*<em>2. 전역 객체 참조 *</em></p>
<pre><code class="language-javascript">// 전역 객체를 초기화하지 않으면 메모리가 해제되지 않음
window.leakedObject = {data: &quot;This will not be collected&quot;}

// 메모리 해제
window.leakedObject = null
</code></pre>
<p><strong>3. 클로저</strong></p>
<pre><code class="language-javascript">function createClosure() {
 let largeArray = new Array(100000).fill(&quot;leak&quot;);

 return function() {
        console.log(largeArray[0]);
        // 명시적 메모리 해제
            largeArray = null;
    };
}

// 실행 후 largeArray는 더 이상 참조되지 않음
let closure = createClosure();
closure(); </code></pre>
<h4 id="현업에서-자바스크립트-엔진의-이해를-통해-개발한-경험은-해당-포스팅에서">현업에서 자바스크립트 엔진의 이해를 통해 개발한 경험은 해당 포스팅에서!</h4>
<p><a href="https://velog.io/@hope_k/SSE-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%95%8C%EB%A6%BC-%EC%84%9C%EB%B9%84%EC%8A%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0">SSE 이용하여 브라우저 실시간 알림 서비스 구현하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 엔진 구조 (1) 콜스택]]></title>
            <link>https://velog.io/@hope_k/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@hope_k/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Fri, 20 Dec 2024 08:45:25 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트 엔진를 내장한 브라우저는 개발자가 작성한 코드를 읽어 컴퓨터 언어로 해석하여 실행할 수 있는 환경을 가지고 있다. 대표적으로는 구글 크롬에 V8이 있다.</p>
<p>본격적으로 시작하기 앞서,
하나의 스레드 당 하나의 스택 메모리를 사용하는데 <span style="color: red">자바스크립트는 싱글 스레드 언어이기 때문에 콜 스택이라는 스택메모리 하나만 사용</span>한다. </p>
<h2 id="구성요소">구성요소</h2>
<p>자바스크립트 엔진에는 크게 두가지의 구성요소를 가지고있다.
<img src="https://velog.velcdn.com/images/hope_k/post/f62f9e1e-4f46-4288-9f88-db3467f0f087/image.png" alt=""></p>
<h3 id="콜-스택">콜 스택</h3>
<p><strong>코드가 위에서 아래로 실행될때 호출 스택을 저장하는 곳</strong>이다. 
런타임 중 함수가 호출되면 콜 스택 제일 상단에 위치하고 함수 실행이 종료되면 스택에서 제거된다.
실행될 함수 단위를 <code>실행 컨텍스트</code>라고 부른다.
또한 <strong>콜스택에서는 자바스크립트의 원시 타입값이 저장</strong>된다. </p>
<p>그런데 싱글 스레드 언어인 자바스크립트가 어떻게 비동기를 처리할 수 있을까? </p>
<p>그 이유는 브라우저가 제공하는 기능을 활용하는 것이다. 
<img src="https://velog.velcdn.com/images/hope_k/post/17994b94-b104-449c-ac80-4a85447a57bd/image.png" alt=""></p>
<h4 id="web-apis">Web APIs</h4>
<p>비동기 작업을 처리하기 위해서 브라우저에서 제공하는 Web API를 활용한다. 
비동기를 처리할 수 있는 브라우저 내장API의 집합이라고 보면 된다. Web API는 메인 스레드 (콜 스택)에서 비동기 작업을 받아 처리한다. 
대표적인 DOM 조작, 네트워크 요청, 타이머 설정 등이 있다. </p>
<ol>
<li>DOM API
HTML 문서를 자바스크립트로 조작할 수 있게 해주는 API입니다. HTML 요소를 추가, 삭제, 변경하거나 스타일을 조작하는 등의 작업을 할 수 있도록 기능을 제공한다.<pre><code class="language-javascript">let button = document.getElementById(&#39;myButton&#39;);
button.addEventListener(&#39;click&#39;, function() {
alert(&#39;Button clicked!&#39;);
});</code></pre>
</li>
<li>Fetch API
웹 서버와 비동기적으로 데이터를 주고받을 수 있게 해주는 API입니다. 주로 HTTP 요청을 보내고 응답을 받는 데 사용된다.<pre><code class="language-javascript">fetch(&#39;https://api.example.com/data&#39;)
.then(response =&gt; response.json())
.then(data =&gt; console.log(data))
.catch(error =&gt; console.log(&#39;Error:&#39;, error));</code></pre>
</li>
<li>Storage API
클라이언트의 브라우저에 데이터를 저장한다. 로컬 스토리지와 세션 스토리지를 이용하여 데이터를 브라우저에 영구적 또는 세션 기반으로 저장할 수 있다.<pre><code class="language-javascript">localStorage.setItem(&#39;username&#39;, &#39;John&#39;);
let username = localStorage.getItem(&#39;username&#39;);
console.log(username);</code></pre>
</li>
<li>Event API
사용자 인터렉션(클릭, 키 입력 등)과 같은 이벤트를 처리하는 API이다. addEventListener()를 사용하여 이벤트를 리스닝하고, 이벤트 핸들러를 실행할 수 있다. <pre><code class="language-javascript">document.addEventListener(&#39;click&#39;, function(event) {
console.log(&#39;Clicked on:&#39;, event.target);
});</code></pre>
</li>
<li>타이머 API
일정 시간 후에 특정 코드를 실행하거나 반복적으로 코드를 실행할 수 있도록 하는 타이머 API이다. 비동기 작업의 일정 시간 지연 처리를 할 수 있다.</li>
</ol>
<ul>
<li><code>setTimeout()</code>: 일정 시간 후에 코드를 실행.</li>
<li><code>setInterval()</code>: 일정 간격으로 코드를 반복 실행</li>
</ul>
<ol start="6">
<li><p>Notification API
웹 페이지가 사용자에게 시스템 알림을 보낼 수 있게 해주는 API입니다. 주로 푸시 알림이나 알림을 표시하는 데 사용된다. </p>
</li>
<li><p>WebSockets API
클라이언트와 서버 간에 지속적인 양방향 연결을 제공하여 실시간 데이터 전송을 가능하게 하는 API이다. 주로 채팅, 실시간 게임 등에 사용된다. </p>
<pre><code class="language-javascript">let socket = new WebSocket(&#39;ws://example.com/socket&#39;);
socket.onopen = function() {
socket.send(&#39;Hello, server!&#39;);
};
socket.onmessage = function(event) {
console.log(&#39;Received:&#39;, event.data);
};</code></pre>
</li>
</ol>
<p><strong>그 종류는 <a href="https://developer.mozilla.org/en-US/docs/Web/API">공식문서</a>에서 찾을 수 있다</strong></p>
<h4 id="callstack-queue">Callstack Queue</h4>
<p>Web API가 받은 비동기 작업이 완료되면 이후에 실행할 콜백함수 등 후처리의 함수들이 대기하는 공간이라고 보면된다. 큐의 특성상 FIFO 구조로 콜스택에 실행 할 함수가 없으면 순차적으로 하나씩 함수를 콜스택으로 보내어 실행된다. </p>
<p>콜스택 큐에도 두가지의 종류가 있다.</p>
<ul>
<li>Task Queue : setTimeout, setInterval, fetch 등 비동기로 처리되는 함수들의 콜백이 저장됨</li>
<li>Microtask Queue : promise, mutationObserver 등</li>
</ul>
<p>*<em>우선순위는 Task Queue &lt; Microstask Queue *</em></p>
<h4 id="event-loop">Event Loop</h4>
<p>싱글 스레드인 자바스크립트는 여러일을 처리하기 위해 지휘하는 지휘관이 필요한데, 이 역할을 이벤트 루프가 담당한다. 
여러개에 작업중 우선순위를 결정하는 일 또한 이벤트 루프의 컨트롤에 의해 실행된다. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/862ffcab-efa9-4c35-8726-a3c9c7a9ba62/image.gif" alt=""></p>
<blockquote>
</blockquote>
<p>정리</p>
<ol>
<li>코드가 Call Stack에 쌓인 후, 비동기 함수는 Web API에게 위임합니다. </li>
<li>Web API는 비동기 작업을 수행하고, 콜백 함수를 Callback Queue에 push 합니다. </li>
<li>이벤트 루프는 Call Stack에 비어있을 때, Callback Queue에 대기하고 있던 콜백 함수를 콜스택으로 push 합니다.</li>
<li>콜스택에 쌓인 콜백 함수가 실행되고, 콜스택에서 pop 됩니다.</li>
</ol>
<h3 id="메모리-힙은-2탄에서">메모리 힙은 2탄에서...</h3>
<p><a href="https://velog.io/@hope_k/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D-%EB%A9%94%EB%AA%A8%EB%A6%AC%ED%9E%99">https://velog.io/@hope_k/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84-%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D-%EB%A9%94%EB%AA%A8%EB%A6%AC%ED%9E%99</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저 구성요소]]></title>
            <link>https://velog.io/@hope_k/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80</link>
            <guid>https://velog.io/@hope_k/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80</guid>
            <pubDate>Fri, 20 Dec 2024 03:38:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>사용자가 브라우저를 통해 특정 사이트를 방문했을때 브라우저는 서버에서 필요한 데이터를 (정적파일 등) 서버에서 받아 화면에 보여준다. 이러한 정보의 통로인 브라우저의 구성요소와 구조에 대해서 공부해보자!</p>
</blockquote>
<h2 id="브라우저-구조">브라우저 구조</h2>
<p><img src="https://velog.velcdn.com/images/hope_k/post/ee956ea6-e851-4f02-92fa-96ecab4fbe35/image.png" alt=""></p>
<h4 id="1-사용자-인터페이스">1) 사용자 인터페이스</h4>
<p>주소 표시줄, 이전/다음 버튼, 북마크 메뉴 등 유저가 사용하는 브라우저, 유저와 가장 밀접하게 맞닿아 있음 </p>
<h4 id="2-브라우저-엔진">2) 브라우저 엔진</h4>
<p>사용자 인터페이스와 렌더링 엔진 사이의 동작을 제어하는 역할을 한다. <code>레이아웃 엔진</code>이라고도 불리며 렌더링 엔진과 밀접하게 연관되어 있다. </p>
<p>웹 브라우저마다 전용 브라우저 엔진을 사용한다. </p>
<ul>
<li>Blink Engine (Chrome, Opera, Samsung Internet, MS Edge, Whale, Brave)</li>
<li>WebKit Engine (Safari)</li>
<li>Servo Engine (Firefox), Gecko Engine (Firefox)<h4 id="3-렌더링-엔진">3) 렌더링 엔진</h4>
요청한 콘텐츠를 화면에 출력하는 역할을 한다. HTML과 CSS를 파싱해 최종적으로 화면에 그려주며, HTML 및 XML 문서와 이미지를 표시할 수 있다. </li>
</ul>
<p>렌더링 엔진은 아래의 순서로 컨텐츠를 화면에 표시한다.</p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/cb328731-76de-4f6d-aa96-98781a2e132f/image.png" alt=""></p>
<h4 id="4-통신">4) 통신</h4>
<p>HTTP 요청과 같은 네트워크 호출을 담당한다. 서버에서 받은 URI에 해당하는 응답 데이터를 렌더링 엔진에게 돌려주는 역할을 한다. </p>
<h4 id="5-자바스크립트-엔진-자바스크립트-해석기">5) 자바스크립트 엔진 (=자바스크립트 해석기)</h4>
<p>자바스크립트 코드를 해석하여 컴퓨터가 읽을 수 있는 기계어로 변환해주는 인터프리터이다. </p>
<blockquote>
<p>인터프리터란? 프로그래밍 언어의 소스코드를 바로 실행하는 프로그램 또는 환경을 말함</p>
</blockquote>
<p>웹 브라우저마다 전용 자바스크립트 엔진이 탑재되어있다. </p>
<ul>
<li>V8: Google, Chrome, Node JS </li>
<li>Rhino Engine: Mozilla, Firefox </li>
<li>Webkit Engine: Apple, Safari </li>
</ul>
<p>근래 제일 많이 쓰고있는 크롬 브라우저의 V8 엔진의 작동 원리는 아래와 같다. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/4d192160-0430-4817-838d-da7ac5c0b775/image.png" alt=""></p>
<h4 id="6-ui-백엔드">6) UI 백엔드</h4>
<p>렌더링 엔진이 분석한 <strong>렌더트리</strong>를 브라우저에 그리는 역할을 수행한다. Input, Select, Button.. 기본위젯 등</p>
<h4 id="7-자료-저장소-웹-스토리지">7) 자료 저장소 (=웹 스토리지)</h4>
<p>영구적인 저장소인 <code>로컬 스토리지</code>와 임시 저장소인 <code>세션 스토리지</code>를 두어 응용 환경에 맞는 선택이 가능하다. </p>
<ul>
<li>로컬 스토리지 : 보관 기한이 없는 데이터, 브라우저를 닫거나 컴퓨터를 재부팅해도 사라지지 않는다. 도메인 마다 별도의 로컬 스토리지가 생성되며, 도메인만 같으면 전역으로 데이터 공유가 가능하다.</li>
<li>세션 스토리지 : 하나의 세션만을 위한 데이터를 저장한다. 브라우징 되고 있는 브라우저 컨텍스트 내에서만 유지되기 때문에 브라우저 탭을 닫으면 데이터가 사라진다.</li>
</ul>
<p>웹 브라우저에서 직접 데이터를 저장할 수 있고 서버에 전송되지 않는 데이터이다. 
서버 네트워크 트래픽 비용을 절감 시킬 수 있다. 웹 스토리지는 출처(Origin)마다 단 하나씩 존재하고, 하나의 출처에 속하는 모든 웹 페이지는 같은 데이터를 저장하기 때문에 같은 데이터에 접근 할 수있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 3장 컴포넌트]]></title>
            <link>https://velog.io/@hope_k/3.-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@hope_k/3.-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Wed, 18 Dec 2024 06:03:42 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트는 컴포넌트들의 집합이다. 
개인적으로 컴포넌트를 잘 설계하여 랜더링을 최소화 시키고 재사용 하는것이 🌸리액트 기술의 꽃🌸이라고 생각한다! </p>
</blockquote>
<h2 id="컴포넌트를-만드는-두-가지-방법">컴포넌트를 만드는 두 가지 방법</h2>
<p>리액트에서 컴포넌트를 생성하는 방법에는 두가지가 있다. 
하나는 클래스를 통해, 하나는 함수를 통해 생성된다. 두 가지의 기능적 차이는 없지만 리액트에서는 함수형 컴포넌트를 권고한다. (힙 메모리 사용도 미세하지만 함수형이 더 적게 씀)</p>
<h3 id="1-클래스형">1. 클래스형</h3>
<p>자바스크립트 ES6에서 클래스가 정식 도입되면서 리액트에서 클래스를 통해 컴포넌트를 생성할 수 있게 되었다. </p>
<h4 id="기본-형태">기본 형태</h4>
<pre><code class="language-javascript">import { Component } from &#39;react&#39;;

class MyComponent extends Component {
   constructor(props: any) {
    super(props);
    this.state = {
           count: 1 
    }
  }

   render() {
        const state = this.state
     const props = this.props
        return &lt;div&gt;클래스 컴포넌트 형태&lt;/div&gt;
   }
}

export default MyCompoent</code></pre>
<ul>
<li>constructor: 컴포넌트 생성자 <code>super(props)</code> 필수</li>
<li>render: JSX를 반환하여 화면을 그린다. </li>
<li>this: React Component 내 속성값들<pre><code class="language-javascript">{    
  props:{},
  context:{},
  refs:{},
  updater:{
      enqueueForceUpdate: f,
      enqueueReplaceState: f,
      enqueueSetState: f,
      isMounted: f
  },
  state: {
      count: 1
  },
  _reactInternals:{
      queue:[],
      replace:false
  }
}
</code></pre>
</li>
</ul>
<pre><code>- props: 컴포넌트가 부모로부터 받은 속성값, 변경될때 리렌더링 된다.
- context: React Context API를 사용할때 접근할 수 있는 contect
- refs: 컴포넌트에서 선언한 refs, DOM 요소나 다른 컴포넌트의 인스턴스를 참조하는 객체
- updater: 컴포넌트 상태를 업데이트하는데 사용되는 내부적인 객체, 상태 변경을 위한 비동기 큐 및 업데이트 메커니즘을 담당 
- state: 컴포넌트 내 관리하는 데이터들, 변경될때 리렌더링된다. 
- _reactInternals: 리액트 내부 구현에 관련된 정보, 직접 다룰 필요 없음. 
(예시에서 queue는 상태 업데이트 큐이며, replace는 상태가 교체되지 않고 업데이트 될 것임을 나타냅니다. 
이 객체는 React의 내부 최적화 및 리렌더링 관리에 사용됩니다.)


### 2. 함수형 (권고)
리액트 16.8 이상에서 Hooks가 나오면서 클래스형 컴포넌트의 모든 기능을 대체할 수 있게 되었다. 

#### 기본 형태
```javascript
import React, { useState } from &#39;react&#39;;

const MyComponent = (props) =&gt; {
  const [count, setCount] = useState(0);
  return &lt;div&gt;함수형 컴포넌트 형태&lt;/div&gt;;
};

export default MyComponent;
</code></pre><ul>
<li>함수형에서 this는 <code>undefind</code></li>
<li>this.state 👉 useState()</li>
<li>this.props 👉 함수 컴포넌트의 인자</li>
<li>this.context 👉 useContext()</li>
<li>this.refs 👉 useRef()</li>
</ul>
<h2 id="props--state">Props &amp; State</h2>
<h3 id="props">props</h3>
<ul>
<li>컴포넌트 생성할때 참조되는 값으로 읽기 전용이다. </li>
<li>부모에서 자식으로 전달하는 값이다. </li>
<li>prop 값을 변경하고 싶을땐 선언한 부모컴포넌트에서 변경해야한다. </li>
</ul>
<h3 id="state">state</h3>
<ul>
<li>컴포넌트 내에서 스스로가 관리하는 값이다. </li>
<li>마음대로 변경해도 됨, 단 React Hook의 setter 를 통해야한다. </li>
<li>값은 원시형, 참조형, null 등 데이터 타입은 상관없이 선언 할 수 있으나, 참조형 데이터를 변경시 사본을 생성 기존 데이터를 엎어쳐 갈아끼운다.</li>
</ul>
<h4 id="object-spread-연산자-사용">Object: spread 연산자 사용</h4>
<pre><code class="language-javascript">const [profile, setProfile] = useState({
    name: &#39;hope&#39;,
    age: 10,
       address: &#39;경기도&#39;
})

// 주소만 변경하고 싶을때
const newProfile = {...profile, address: &#39;경기도 수원시&#39;}
setProfile(newProfile)</code></pre>
<h4 id="array-spread-혹은-배열-함수-사용">Array: spread 혹은 배열 함수 사용</h4>
<pre><code class="language-javascript">const [productIds, setProductIds] = useState([1, 2, 3, 4, 5])

const addProductIds = productIds.concat(6)
const removeProductIds =productIds.filter((id) =&gt; id &gt; 3)
const newProductIds = [...productIds]</code></pre>
<p>참조형 데이터 타입의 state 변경 방법에는 한가지 공통점이 있다. 
바로 새로운 주소값을 할당하는 것이다. 
<strong>즉, 데이터 사본을 만든다 = 새로운 주소값을 할당</strong></p>
<p>그럼 왜 기존의 할당된 데이터 자체를 수정하지 않고 새로 주소값을 할당하는 것일까? </p>
<p>1장에서도 다루었고 추후에 더 깊이 다루겠지만 
리액트는 렌더링 전 가상돔과 값의 비교를 통해 변화를 감지한다고 했다. 
만약 참조타입의 주소값이 아닌 데이터 값을 일일히 깊은 비교를 하게 된다면 연산의 비용이 크게 들것이고, 지금의 리액트와 같이 빠른 퍼포먼스는 기대하기 힘들것이다.

리액트는 얕은 비교를 통해 빠르게 데이터 변화를 감지하고 화면을 전환한다.
그렇기때문에, 참조형 데이터는 데이터의 사본을 통해 새로운 메모리 주소를 할당받아 변화를 감지하는 작업으로 동작된다. </p>
<p><span style="color:blue">이것이 바로 리액트에서 꼭 지켜야할 데이터의 <strong>불변성 특징(Immer)</strong>이다.</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 2장 JSX]]></title>
            <link>https://velog.io/@hope_k/2.-JSX</link>
            <guid>https://velog.io/@hope_k/2.-JSX</guid>
            <pubDate>Sun, 08 Dec 2024 08:13:58 GMT</pubDate>
            <description><![CDATA[<p>JSX는 리액트를 사용할때 사용자 UI를 쉽고 편리하게 개발할 수 있도록 제공한 리액트의 문법이다. 코드를 보면 HTML 형태처럼 보이나, 실제로는 바벨에 의해 JS 문법으로 변환되어 렌더링에 사용된다. </p>
<p>그럼 2장에서 다룬 내용들을 좀 더 깊게 파보도록 하겠다! </p>
<h2 id="create-react-app">create-react-app</h2>
<p>리액트 프로젝트를 처음 시작할때 콘솔창에 입력하는 명령어가 있다.
(npx는 npm v5 이상부터 쓸 수 있는 노드 실행 도구이다.)</p>
<pre><code>$npx create-react-app my-project
</code></pre><p>위의 명령어를 실행하면 아래와 같은 로그가 찍힌다.
<img src="https://velog.velcdn.com/images/hope_k/post/8c43fe1c-4938-4ecc-95c3-b9ea68d73c1c/image.png" alt=""></p>
<p>react, react-dom, react-scripts, cra-template을 설치하고 1316개의 패키지가 자동으로 같이 추가되었다. </p>
<pre><code class="language-json">// 자동으로 package.json에 추가된 설정들
{
  &quot;name&quot;: &quot;my-product&quot;,
  &quot;version&quot;: &quot;0.1.0&quot;,
  &quot;private&quot;: true,
  &quot;dependencies&quot;: {
    &quot;cra-template&quot;: &quot;1.2.0&quot;,
    &quot;react&quot;: &quot;^19.0.0&quot;,
    &quot;react-dom&quot;: &quot;^19.0.0&quot;,
    &quot;react-scripts&quot;: &quot;5.0.1&quot;
  },
 ...
}</code></pre>
<ul>
<li><p>react: React 라이브러리로, 애플리케이션의 사용자 UI 를 구성하는 데 사용한다. 상태, 속성을 사용하여 동적인 웹 애플리케이션을 만든다. 또한 가상돔을 사용하여 UI 변경 사항을 효율적으로 처리한다. </p>
</li>
<li><p>react-dom: React 애플리케이션을 DOM에 렌더링하는데 사용되는 라이브러리이다. 리액트는 가상돔을 사용하여 UI를 관리하지만, 실제 DOM 변경사항에 반영하고 연결하는 일은 react-dom의 역할이다.</p>
</li>
<li><p>react-scripts: create-react-app에서 제공하는 개발용 도구 모음이다. 개발할때 필요한 도구들을 자동으로 설정하여 바로 개발할 수 있도록 한다.</p>
<ul>
<li>Webpack: 코드 번들링을 담당하는 도구로, 애플리케이션의 자바스크립트, CSS, 이미지 파일 등을 번들링하여 최적화한다.</li>
<li>Babel: 최신 JavaScript(ES6 이상) 문법을 구형 브라우저에서도 동작할 수 있도록 변환해주는 트랜스파일러</li>
<li>EsLint: 코드 품질을 유지하기 위해 정적 코드 분석을 수행하는 도구</li>
<li>Jest: React 애플리케이션의 테스트를 위한 프레임워크이다. </li>
<li>CSS 모듈: CSS 파일을 모듈화하여 각 컴포넌트에게만 적용되는 스타일을 작성할 수 있다.</li>
</ul>
</li>
<li><p>cra-template: Create React App 프로젝트의 기본 템플릿을 제공한다. 이 템플릿은 프로젝트의 초기 구조와 설정을 정의한다. 사용자가 설정을 수정할 필요 없이 바로 개발을 시작하게 한다. </p>
<ul>
<li>기본적인 파일 구조 (예: src, public 폴더, index.html, index.js 등)</li>
<li>기본 React 설정 (예: React.StrictMode, App 컴포넌트 등)</li>
<li>개발 환경을 위한 설정 (예: Webpack, Babel, ESLint 등)</li>
</ul>
</li>
</ul>
<p>.
.
.
.
.
[2025-11-12 업데이트]</p>
<h3 id="cra-deprecated-중단-이유">CRA Deprecated 중단 이유</h3>
<h4 id="활발한-유지보수-부족">활발한 유지보수 부족</h4>
<ul>
<li>CRA는 현재 “새로운 기능” 추가보다는 유지보수 모드로 들어갔습니다. 공식 블로그에서 “active maintainers(활성 유지보수자)가 없다”는 언급이 있습니다. </li>
</ul>
<h4 id="현대적인-요구사항을-제대로-내장하지-않음">현대적인 요구사항을 제대로 내장하지 않음</h4>
<p>CRA 초기에는 좋은 출발점이었지만, 지금 React 앱을 실제로 운영하고 확장할 때 필요한 여러 기능들이 기본에 빠져 있습니다:</p>
<p>라우팅(routes)이나 데이터 패칭(data fetching) 같은 기능이 기본 세팅에 포함되어 있지 않음. </p>
<p>코드 스플리팅(code splitting), 서버사이드 렌더링(SSR), 정적 사이트 생성(SSG) 같은 성능/최적화 관련 기능이 내장되어 있지 않음. </p>
<p>번들러로 사용하는 Webpack 기반 설정이 최근의 빌드툴들(예: Vite 등) 대비 느리고 유연성이 떨어짐. </p>
<h4 id="더-나은-대안의-등장">더 나은 대안의 등장</h4>
<p>React 생태계 외에도 빠르게 발전한 여러 툴/프레임워크들이 등장했고, 이들이 CRA의 약점을 상당부분 보완했습니다. 예컨대 Vite, Next.js 등이 그런 대안입니다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 1장 작업 환경 설정]]></title>
            <link>https://velog.io/@hope_k/1-2.-%EC%9E%91%EC%97%85-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@hope_k/1-2.-%EC%9E%91%EC%97%85-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Sat, 30 Nov 2024 13:36:08 GMT</pubDate>
            <description><![CDATA[<h2 id="nodejs">Node.js</h2>
<p>리액트와 노드는 직접적인 연관은 없지만, 프로젝트를 개발하는 데 필요한 주요 도구들이 Node.js를 사용하기에 개발에 있어 꼭 필수적인 요소가 되었다.</p>
<p>주로 사용하는 개발 도구에는 </p>
<p>1) 구 브라우저에서 지원하지 않는 자바스크립트 문법을 호환시켜주는 바벨
2) 모듈화된 코드를 한 파일로 합치고(Bundler)하고 코드를 수정할때마다 웹 브라우저에서 리로딩(Hot-reload) 시켜주는 기능을 가진 웹팩 등이 있다. </p>
<p>노드를 설치하면 노드의 패키지 매니저인 npm이 같이 설치된다. npm은 똑똑한 개발자들이 미리 만들어놓은 라이브러리를 설치하고 버전을 관리하여 프로젝트의 질을 높여준다.</p>
<p>(npm 저장소에 등록된 패키지는 약 82만개로 엄청난 선물같은 패키지들이 있다...)</p>
<blockquote>
<p>너무 많은 패키지들 중 뭘 써야하지? 고민된다면 NPM Trends를 참고하자 ㅋㅋ 
<a href="https://npmtrends.com/">https://npmtrends.com/</a> </p>
</blockquote>
<h3 id="nvm">NVM</h3>
<p>macOS에서는 Node.js를 여러 버전으로 설치하여 관리해주는 nvm 도구를 주로 사용한다. 추후에 노드 버전을 업데이트 하거나, 프로젝트 별로 버전이 다른 노드를 사용해야 할 경우 유용하기 때문이다. </p>
<h3 id="여러가지-노드-패키지-매니저들">여러가지 노드 패키지 매니저들</h3>
<p>여러가지 개발 환경에서 적절한 패키지 매니저를 골라 사용하면 된다. </p>
<h4 id="npm--가장-보편적이고-작은-프로젝트에서-적합">npm : 가장 보편적이고 작은 프로젝트에서 적합</h4>
<ul>
<li>Node.js의 공식 패키지 매니저로, node 설치시 함께 설치된다.</li>
<li>package.json 파일을 사용하여 프로젝트 의존성을 관리한다.</li>
<li>package-lock.json 을 도입하여 의존성 버전을 고정하고, 설치된 패키지의 정확한 버전을 추적한다. </li>
<li>단일 레지스트리에서 패키지를 다운로드하고 관리한다. <h4 id="yarn--속도와-안정성-면에서-우수하고-큰-프로젝트에서-모노레포-환경에-유리함">yarn : 속도와 안정성 면에서 우수하고 큰 프로젝트에서 모노레포 환경에 유리함</h4>
</li>
<li>npm의 속도를 개선하고, 더 나은 의존성 관리를 제공한다</li>
<li>npm와 호환되어 package.json을 그대로 사용한다. </li>
<li>yarn.lock 파일을 사용하여 의존성의 버전을 고정하고, 팀 간의 일관된 환경을 보장한다. </li>
<li>npm 은 패키지를 병렬로 설치하여 npm보다 더 빠른 설치 속도를 제공한다. 특히 캐싱을 사용하여 이미 설치된 패키지를 재사용하여 속도 향상을 이끈다. </li>
<li>yarn은 여러 패키지를 하나의 레포에서 관리할 수 있는 워크스페이스 기능을 제공한다. 이를 통해 모노레포(Monorepo)에서 여러 패키지를 쉽게 관리할 수 있다. </li>
</ul>
<h4 id="pnpm--디스크-공간과-성능에-효율적이며-많은-프로젝트에서-의존성을-공유하고-빠른설치를-원한다면">pnpm : 디스크 공간과 성능에 효율적이며, 많은 프로젝트에서 의존성을 공유하고 빠른설치를 원한다면</h4>
<ul>
<li>pnpm는 패키지 설치의 성능을 극대화 시키기 위해 설계된 패키지 매니저이다.</li>
<li>심볼릭 링크를 사용하여 여러 프로젝트에서 공통된 의존성을 공유한다. 즉 하나의 의존성 패키지가 여러 프로젝트에서 재사용될 수있다. </li>
<li>pnpm는 병렬설치와 패키지를 중복없이 설치하므로 패키지 설치 속도가 매우 빠르다.</li>
<li>엄격한 의존성 관리로 패키지간의 충돌을 줄이고 패키지가 정확히 선언된 의존성만을 사용하도록 한다. </li>
<li>npm와 완벽하게 호환되어, package.json, package-lock.json 파일을 그대로 사용한다. </li>
</ul>
<h3 id="packagejson-구성요소">package.json 구성요소</h3>
<h4 id="1-기본-구성-요소">1. 기본 구성 요소</h4>
<pre><code>npm init -y</code></pre><p>위의 명령어를 실행하면 기본 설정값의 package.json을 생성할 수 있다.</p>
<pre><code>{
  &quot;name&quot;: &quot;PROJECT_DIRECTORY&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;
}</code></pre><p>각각 기본 구성요소들을 알아보자</p>
<ul>
<li>name:  패키지의 이름을 나타내는 것으로 필수항목</li>
<li>version: 패키지의 버전을 나타내는 것으로 필수항목</li>
<li>descrition: 패키지에 대한 설명으로 npm에 검색되었을때 리스트에 표시되어 사람들이 쉽게찾는데 도움을 준다. </li>
<li>main: 패키지의 진입점(entry point)가 되는 모듈 ID이다. </li>
<li>scripts: 패키지의 생명주기에서 자주 사용할 command를 alias를 통해 지정한 명령어들</li>
<li>keywords: descrition와 마찬가지로 npm에서 검색되기 위한 키워드들 </li>
<li>author: 배포자 이름</li>
<li>license: 배포한 패키지에 대한 권한과 제한사항이 있는지 알기위한 용도</li>
</ul>
<h4 id="2-의존성-관련-요소들">2. 의존성 관련 요소들</h4>
<pre><code>&quot;dependencies&quot; : {
   &quot;react&quot;: &quot;^18.2.0&quot;,
   &quot;react-dom&quot;: &quot;^18.2.0&quot;,
},
&quot;devDependencies&quot; : {
  &quot;eslint&quot;: &quot;^8.31.0&quot;,
  &quot;prettier&quot;: &quot;^2.8.1&quot;,
  &quot;typescript&quot;: &quot;^4.9.4&quot;
},
&quot;files&quot;: [
    &quot;dist/&quot;,
    &quot;js/{src,dist}/&quot;,
    &quot;scss&quot;
]</code></pre><ul>
<li>dependencies: 프로덕션 환경 및 런타임에 사용되는 일반적인 종속성을 가진 라이브러리가 위치한다. 서비스 로직과 관련된 라이브러리, React, 상태관리 라이브러리 등이 있다.</li>
<li>devDependencies: 로컬 개발 환경 또는 프로젝트 빌드에만 필요하거나 서비스 로직과 관련없이 개발시에만 필요한 라이브러리를 설치한다. 예시로는 webpack, testing lib, typescript 관련 (@types/*), eslint, prettier, storybook 등이 있다. </li>
<li>files: 패키지가 의존성으로 설치될 때 같이 포함될 파일들의 배열이다. 생략하면 자동 제외로 설정된 파일을 제외한 모든 파일이 포함된다. <blockquote>
<ul>
<li>설정과 관계없이 무조건 포함되는 파일들: package.json, README, CHANGELOG, LICENSE 또는 LICENCE </li>
<li>항상 무시되는 파일들: .git, CVS, .sv 등</li>
</ul>
</blockquote>
</li>
</ul>
<h3 id="그-밖에-협업에서-작업-환경-설정시-필요한-플러그인">그 밖에 협업에서 작업 환경 설정시 필요한 플러그인</h3>
<h4 id="eslint--코드-정적-분석기">ESlint : 코드 정적 분석기</h4>
<ul>
<li>ESLint는 코드의 스타일뿐만 아니라 코드의 구조, 문법, 베스트 프랙티스 등을 관리하는 규칙을 제공한다. </li>
<li>코드의 잠재적 오류, 구문 오류, 실수, 안티패턴 등을 찾아내고 경고 또는 오류를 발생시킨다.</li>
<li>사용자가 설정한 규칙에 맞는지 검사하고, 위반된 부분에 대해 경고 또는 오류를 반환한다.</li>
<li>자동수정 기능을 제공하여 코드의 오류를 검사하여 자동수정한다. </li>
<li>.eslintrc.json 에 사용자 규칙을 설정한다. <pre><code>{
&quot;extends&quot;: [
  &quot;eslint:recommended&quot;,
  &quot;plugin:prettier/recommended&quot;
],
&quot;plugins&quot;: [&quot;prettier&quot;],
&quot;rules&quot;: {
  &quot;prettier/prettier&quot;: &quot;error&quot;
}
}</code></pre></li>
</ul>
<h4 id="prettier--코드-포멧터">Prettier : 코드 포멧터</h4>
<ul>
<li>코드 형식을 일관되게 수정하여 협업시 일관된 코드를 보장하여 관리가 쉽다. </li>
<li>.prettierrc 에 규칙을 설정한다.<pre><code>{
&quot;singleQuote&quot;: true,
&quot;trailingComma&quot;: &quot;all&quot;
}</code></pre></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트를 다루는 기술] 1장 리액트 특징]]></title>
            <link>https://velog.io/@hope_k/1%EC%9E%A5.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8A%B9%EC%A7%95</link>
            <guid>https://velog.io/@hope_k/1%EC%9E%A5.-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8A%B9%EC%A7%95</guid>
            <pubDate>Sat, 30 Nov 2024 09:24:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트를 다루는 기술의 책 1장에서는 리액트의 대표 컨셉과 중요한 특징을 다루고 있다. 여기서 제일 중요한 가상돔을 사용한 화면(Interface) 표현에 대해서 집중적으로 공부해보도록 하겠다! 🤗</p>
</blockquote>
<h2 id="리액트-컨셉">리액트 컨셉</h2>
<p>리액트 컨셉은 굉장히 단순하면서 터프하다
기존 자바스크립트의 라이브러리인 jQuery 와 비교해보면 왜 단순하고 터프하다고 하는지 알 수 있다. </p>
<p>만약, 사용자가 버튼을 눌러 Count 값을 1 증가한다고 생각해보자. 
먼저, 제이쿼리에서 </p>
<pre><code class="language-html">&lt;html&gt;
  &lt;body&gt;
    &lt;p id=&quot;result&quot;&gt;0&lt;/p&gt;
    &lt;button id=&quot;button&quot;&gt;Click!&lt;/button&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-javascript">var count =$(&quot;#result&quot;).text();

$(&quot;#button&quot;).click(function(){
    count++;
    $(&#39;#result&#39;).text(count);
    $(&#39;#result&#39;).html($(&#39;&lt;b&gt;새로운 HTML&lt;/b&gt; 콘텐츠&#39;);
})</code></pre>
<p>제이쿼리에서는 자바스크립트를 통해 DOM 내부 요소에 직접 접근하여, 내부 데이터를 <strong>직접</strong> 조작하여 업데이트 하는 직관적인 방식을 사용한다. </p>
<p>제이쿼리는 직관적이라는 장점이 있는 반면, 치명적인 단점이 있다. </p>
<ul>
<li>셀렉터로 직접 조작하기 때문에 DOM 탐색이 빈번하게 일어난다.  </li>
<li>DOM 반복적인 업데이트시 비효율적인 반복작업이 일어난다.
아래의 상황에서 #list에 li 요소를 반복적으로 추가하고 있습니다. 이때 1000번의 DOM 업데이트가 발생한다. 🤮</li>
</ul>
<pre><code class="language-javascript">for (var i = 0; i &lt; 1000; i++) {
  $(&#39;#list&#39;).append(&#39;&lt;li&gt;&#39; + i + &#39;&lt;/li&gt;&#39;);
}</code></pre>
<p>각 업데이트마다 화면이 새로 렌더링되며 따라서 대규모 DOM 에서는 성능저하가 빈번하게 일어난다.</p>
<hr>
<p>그럼 똑같은 상황에서 리액트는 어떨까? 
결론적으로 말하면, 리액트는 데이터가 바뀌면 그 데이터가 포함된 컴포넌트 자체를 싹 교체해버린다. (Reconciliation) </p>
<p>리액트에서 중요한 개념인 컴포넌트가 등장하는데 
개발자는 화면상에 데이터를 어떤 방식으로 노출하고, 어떻게 처리할지 등의 정보를 포함한 컴포넌트 단위를 개발한다.</p>
<p>이후에 개발자가 작성한 코드를 읽어 리액트는 최초 렌더링과, 리렌더링을 통해 데이터 변화에 대응한다. </p>
<h3 id="최초-렌더링">최초 렌더링</h3>
<p>리액트 앱를 최초로 실행하면, 리액트의 <code>ReactDOM.render()</code> 또는 <code>createRoot().render()</code>가 호출된다. </p>
<pre><code class="language-javascript">// index.js 또는 main.js
import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom/client&#39;; // React 18 이상에서는 createRoot 사용

import App from &#39;./App&#39;; // 메인 App 컴포넌트

// React 18 이상에서는 createRoot 사용
const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));
root.render(&lt;App /&gt;);</code></pre>
<p>이때 리액트가 가진 엄청난 강점이 발휘된다. 
리액트는 <code>render()</code>함수를 통해 Root부터 최하위 노드의 Child Component 까지 재귀함수를 통해 순회하며 각각 컴포넌트의 정보를 읽어 객체로 생성한다.</p>
<p>위에서와 같은 Count를 증가시키는 리액트 함수형 컴포넌트의 예시이다.</p>
<pre><code class="language-javascript">const CountComponent = () =&gt; {
  const [result, setResult] = useState&lt;number&gt;(0)

  return (
  &lt;div&gt;
       &lt;p&gt;{result}&lt;/p&gt;
       &lt;button onClick={() =&gt; setResult(result++)}&gt;Click!&lt;/button&gt;
  &lt;/div&gt;
)}</code></pre>
<blockquote>
<p>리액트는 render() 함수를 통해 DOM 내부에 선언된 컴포넌트를 순회하며 자바스크립트 객체를 생성한다. </p>
</blockquote>
<p>중요해서 다시한번 말해보았다. ㅋㅋ</p>
<p>위와 같은 컴포넌트를 읽을때 리액트는 다음과 같이 자바스크립트 객체를 생성한다.
리액트 가상돔 객체 안에는 객체 뷰가 어떻게 생겼고, 어떻게 동작하는지에 대한 정보를 담고 있다. 또한 가상돔임을 증명하는 Symbol 값과, 각각 Element들을 고유하게 구분하는 Key 값을 가지고 있다. </p>
<pre><code class="language-javascript">const virtualDom = {
  type: &#39;div&#39;,
  props: {
    children: [
      {
        type: &#39;p&#39;,
        props: {
          children: [&#39;0&#39;] // result의 초기값인 0
        }
      },
      {
        type: &#39;button&#39;,
        props: {
          children: [&#39;Click!&#39;],
          onClick: expectFunction // onClick 이벤트 핸들러 (setResult 호출)
        }
      }
    ]
  }
};</code></pre>
<p>그리고 최상단부터 최하위까지의 객체 생성과정이 끝나면 HTML 마크업을 만들고, 실제 DOM 안에 주입하여 사용자가 볼 수 있는 화면이 렌더링된다. </p>
<h3 id="reconciliation-리렌더링">Reconciliation, 리렌더링</h3>
<p>화면에 CountComponent가 그려지고 사용자는 button을 클릭하여 result 데이터를 1증가 시키려고한다. </p>
<p>리액트는 해당 컴포넌트의 데이터를 직접 바꾸지 않는다.
컴포넌트 내에 데이터 값이 변경되면, 해당 데이터를 기반으로 render() 함수를 다시 실행시켜 새로운 virtualDom 객체를 생성한다. 
그리고 리액트는 새로 생성된 가상 DOM 객체는 <strong>Diff 알고리즘</strong>을 통해 이전 DOM과 비교하는 작업을 거친다. 
이 과정에서 데이터가 바뀐 부분을 <strong>dirty 체크를 하고 batch에 추가한다.</strong> 
(속성 값이 변한 경우에는 속성 값만 업데이트하고, 해당 엘리먼트의 태그 혹은 컴포넌트가 변경되면 해당 노드를 포함한 하위 모든 노드를 제거(언마운트)한 후 새로운 엘리먼트를 생성한다)</p>
<p><strong>이런식으로 batch에 쌓인 모든 변경사항을 처리 한 후, 딱 한번 DOM을 업데이트 한다</strong>. </p>
<p><img src="https://velog.velcdn.com/images/hope_k/post/6e8215b7-8d2f-4220-b4cb-02cbad550c51/image.png" alt=""></p>
<p>우리가 알고있는 가상돔, Virtual DOM은 위의 과정을 말한다. 
리액트는 가상돔의 객체를 생성하여 실제 돔의 반영까지 비교연산을 수행하여 필요한 부분만 최적화하여 렌더링 하는 작업을 관리해준다. </p>
<h3 id="리액트의-비교-알고리즘">리액트의 비교 알고리즘</h3>
<p>위에서 언급한 바와 같이 리액트는 어떤 비교 알고리즘에 의해 이전 돔과, 바뀐 돔을 비교하여 재조정(Reconciliation)을 통해 화면을 다시 생성하는 작업을 반복한다. 
.
.
.
여기서 두 가지 의문점이 생길 수 있다. 🤔</p>
<p><em>Question1. 데이터 하나가 바뀔때, 그 많은 요소들의 객체를 처음부터 끝까지 탐색한다고?</em>
_Question2. 자바스크립트 언어인데 어떤 기준으로 속성값의 데이터를 비교하여 변경이 감지되는거지? _</p>
<h4 id="answer1-diffing-휴리스틱-알고리즘">Answer1. Diffing, 휴리스틱 알고리즘</h4>
<p>리액트는 각 노드 컴포넌트들의 객체 정보를 가지고있다. 그리고 이를 비교하는데 모두 다 비교하기에는 너무 큰 비용이 발생한다.</p>
<blockquote>
<p>휴리스틱 알고리즘이란? 기본적으로 모두 최적해가 될 가능성이 없는 답들을 탐색하는 것을 방지하여 만들어봐야 할 답의 수를 줄이는 것을 목표로 한다. </p>
</blockquote>
<p>리액트는 휴리스틱 알고리즘 방식을 채택했다.
간단히 이야기하면 전부 비교하지 않고, 상식적으로 바뀔만한 곳만 비교하여 비교에 발생하는 비용을 절감하겠다는 기조이다. </p>
<p>리액트는 아래와 같은 방식으로 변화를 감지한다.</p>
<ol>
<li>총 노드의 개수가 같을때 노드의 레벨별로 비교한다. </li>
</ol>
<p><img src="https://velog.velcdn.com/images/hope_k/post/2454cd5f-d529-4f50-a4b9-abc96bdf960e/image.png" alt=""></p>
<ol start="2">
<li><p>레벨별로 비교를 하다가 요소의 타입, 예를들어 p -&gt; div 바뀌거나, 다른 컴포넌트가 들어가는 등의 변화를 감지하면 해당 노드를 포함한 하위 노드를 제거(언마운트)한 후 새로 생성한다. </p>
</li>
<li><p>요소의 타입이 같고 속성만 같은경우는 내부 가상돔 객체의 프로퍼티 값만 수정한다. </p>
</li>
<li><p>같은 레벨간의 형제 요소들간의 key를 부여하면 기존 트리의 자식과 새로운 트리의 자식이 일치하는지 확인하여 불필요한 비교를 줄일 수 있다. </p>
<blockquote>
<p>유니크한 키는 데이터상 절대 변하지 않은 key 값으로 사용하자, index를 사용하는건 최후의 방법!</p>
</blockquote>
</li>
</ol>
<h4 id="answer2-데이터-타입의-따른-속성-비교-objectis">Answer2. 데이터 타입의 따른 속성 비교, Object.is()</h4>
<p>근데 리액트는 자바스크립트의 언어이다. 자바스크립트의 데이터는 원시형과 참조형 데이터로 나뉘고 각각의 Equal 연산은 다르게 동작하고 있다.</p>
<ul>
<li>원시형 (문자 타입, 숫자 타입, Boolean 타입 등): 값 자체를 비교함</li>
<li>참조형 (배열 타입, 객체 타입, 함수형 타입 등): 주소 값을 비교함</li>
</ul>
<p>따라서 리액트의 비교 연산 알고리즘을 모르고 사용한다면, 데이터가 변화하지 않는 등의 개발자의 의도와 다르게 실행될 수 있다. (실제로 현업에서 많은 버그를 발생시킴) </p>
<p>리액트는 <code>Object.is</code>로 먼저 비교를 수행한 다음에 <code>Object.is</code>에서 수행하지 못하는 비교, 즉 객체 간 얕은 비교(객체의 첫 번째 깊이에 존재하는 값만 비교)를 한 번 더 수행한다.</p>
<pre><code>- 얕은 비교(Shallow Compare)란? 원시타입은 값을 비교한다. 객체 등 참조타입의 경우 참조값만 비교한다. 
- 깊은 비교(Deep Compare)란? 객체 내 중첩된 객체까지 값을 비교한다. </code></pre><p>리액트는 state, props의 데이터의 값이 변경되면 렌더링이 일어난다. 
props는 객체이고, 기본적으로 리액트는 props에서 꺼내온 값을 기준으로 렌더링을 수행하기 때문에 일반적인 케이스에서는 얕은 비교로 충분하여 얕은 비교를 사용한다. </p>
<p>만약 props가 깊은 객체를 가지고 있다면 어떻게 될까?
일반적인 컴포넌트 같은 경우에는 대부분 렌더링될것이다. 
그러나 <code>React.memo()</code>를 사용하여 컴포넌트를 최적화 하고 싶을때는 예상하지 못한 케이스가 발생한다.</p>
<p>깊은 객체의 경우, 실제로 데이터가 변경된 값이 없음에도 불구하고, 데이터가 변경되었다고 감지하여 (값은 똑같으나 주소값이 달라지는 경우), 메모이제이션 된 컴포넌트를 반환하지 못한다. </p>
<p>만약 이럴 경우는 Deep Compare 방식을 채택하여 메모이제이션을 할 수 있다. </p>
<pre><code class="language-javascript">import { isEqual } from &#39;lodash&#39;;

const DeepCompareComponent = React.memo(MyComponent, (prevProps, nextProps) =&gt; isEqual(prevProps, nextProps));</code></pre>
<p>.
.
.
.
.</p>
<p>[11/12 업데이트]</p>
<h3 id="리액트-내부-객체--fiber">리액트 내부 객체 , Fiber</h3>
<p>리액트 버전 16부터 등장한 재조정 엔진이다. 핵심 기능은 <strong>증분 렌더링</strong>으로, 렌더링 작업을 여러 개의 덩어리로 나누어 처리하는 것이다. 
이로써 새로운 업데이트가 들어올 때 작업을 일시 중지, 중단, 재사용 등을 통해 <strong>업데이트의 우선순위를 지정</strong>하는 기능, <strong>새로운 동시성 기본 기능</strong>을 사용할 수 있게 되었다.</p>
<ul>
<li><code>useTransition</code> UI 업데이트 중 긴 연산(데이터 패칭 등)과 즉시 반영해야하는 입력을 분리한다. <pre><code class="language-javascript">import { useTransition } from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>function Example() {
  const [isPending, startTransition] = useTransition();
  const [data, setData] = useState([]);</p>
<p>  const handleChange = (e) =&gt; {
    startTransition(() =&gt; {
      // 우선순위 낮은 작업 (렌더링 지연 가능)
      setData(fetchFilteredData(e.target.value));
    });
  };</p>
<p>  return (
    &lt;&gt;
      <input onChange={handleChange} />
      {isPending ? <p>Loading...</p> : <List data={data} />}
    &lt;/&gt;
  );
}</p>
<pre><code>- `useDeferredValue` 값 자체를 지연(defer) 시켜서 렌더링을 느리게 처리할 수 있다.
```javascript
import { useDeferredValue } from &quot;react&quot;;

function Search({ query }) {
  const deferredQuery = useDeferredValue(query);
  const results = useMemo(() =&gt; searchData(deferredQuery), [deferredQuery]);

  return &lt;ResultsList results={results} /&gt;;
}</code></pre><ul>
<li><code>Suspense + Streaming SSR</code> &quot;페이지 껍데기(shell)를 먼저 보내고, 준비되는 조각부터 순차적으로(증분) 흘려보내며, 클라이언트에서 순서대로 하이드레이션&quot; 하는 기술입니다. (느린 데이터/큰 컴포넌트가 있어도 초기 TTFB를 낮추고 체감 성능을 끌어올립니다.)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩테스트 전략]]></title>
            <link>https://velog.io/@hope_k/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A0%84%EB%9E%B5</link>
            <guid>https://velog.io/@hope_k/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A0%84%EB%9E%B5</guid>
            <pubDate>Fri, 04 Oct 2024 03:50:10 GMT</pubDate>
            <description><![CDATA[<h3 id="그래프탐색">그래프탐색</h3>
<h3 id="bfs-기준점의-인접한-노드들-우선-탐색하는-방법">BFS (기준점의 인접한 노드들 우선 탐색하는 방법)</h3>
<ul>
<li>넓게 탐색</li>
<li>최단거리/ 땅따먹기/ 경로 탐색 등</li>
<li>방문여부 필수</li>
<li>재귀 X</li>
<li>선입선출 큐 사용</li>
<li>큐를 이용한 반복적인 형태로 구현</li>
</ul>
<h3 id="dfs-다음분기를-넘어가기전-해당-분기를-완벽하게-탐색하는-방법">DFS (다음분기를 넘어가기전 해당 분기를 완벽하게 탐색하는 방법)</h3>
<ul>
<li>깊게 탐색</li>
<li>특정 조건을 만족하는 경로 탐색/ 백트래킹</li>
<li>자기 자신을 호출하는 재귀 사용</li>
<li>방문여부 필수</li>
<li>후입선출 스택 사용</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[제네릭 <T>]]></title>
            <link>https://velog.io/@hope_k/%EC%A0%9C%EB%84%A4%EB%A6%AD-T</link>
            <guid>https://velog.io/@hope_k/%EC%A0%9C%EB%84%A4%EB%A6%AD-T</guid>
            <pubDate>Sun, 15 Sep 2024 04:17:55 GMT</pubDate>
            <description><![CDATA[<p>언젠가 자바 개발자로 이직 면접 중에 &quot;제네릭 개념에 대해 설명해보세요.&quot; 라는 질문을 받았었다. 그때 당시 면접기출 문제를 달달 외워갔던터라, 개념도 모르고 대충 얼버무리며 대답했던 경험이 생각이 난다.
현재 타입스크립트를 작성하는 개발자로 또 다시 마주한 T.... 이제야 비로소 제네릭이란 도대체 무엇이고, 언제 유용하게 쓰는지 공부해보기로 했다! </p>
<h1 id="제네릭generic이란">제네릭(Generic)이란?</h1>
<h2 id="개념">개념</h2>
<p>제네릭, 영어 사전적 의미로 일반적인 이라는 뜻을 가진다. 
객체지향의 개념을 가진 언어에서 클래스 또는 메서드 내부의 데이터 타입을 일반화하는 것을 말한다. 
그리고 실제 로직 내부에서 인스턴스를 생성할때 타입을 지정한다! </p>
<h2 id="문법">문법</h2>
<h3 id="타입-매개변수">타입 매개변수</h3>
<p>제네릭은 <code>&lt;&gt;</code> 꺽쇠 괄호 키워드를 사용하고 꺽쇠 괄호 안에 식별자 기호(보통 <code>T</code>)를 지정함으로써 파라미터화 할 수 있다. 이것은 마치 메소드가 매개변수를 받아 사용하는 것과 비슷하여 <strong>타입 매개변수</strong> 라고 부른다.</p>
<h3 id="코드">코드</h3>
<p>클래스명 옆에 <code>&lt;T&gt;</code> 라는 기호로 제네릭을 붙여줬다. 그리고 클래스 내부에서 식별자 기호 T를 클래스 필드와 메소드의 매개변수의 타입으로 지정했다.</p>
<h4 id="제네릭-타입의-클래스-생성">제네릭 타입의 클래스 생성</h4>
<pre><code>class Member&lt;T&gt; {
    List&lt;T&gt; users = new ArrayList&lt;&gt;();

    public void signUp(T user) {
        users.add(user);
    }
}</code></pre><h4 id="하나의-클래스의-다른-타입의-인스턴스-생성">하나의 클래스의 다른 타입의 인스턴스 생성</h4>
<pre><code>Member&lt;String&gt; strMemebers = new Member&lt;&gt;();
Member&lt;Integer&gt; intMembers = new Member&lt;&gt;():
</code></pre><h4 id="복수-타입의-제네릭-매개변수">복수 타입의 제네릭 매개변수</h4>
<pre><code>class Cart&lt;T, U&gt; {
    List &lt;T&gt; carts = new ArrayList&lt;&gt;();
    List &lt;U&gt; baskets = new ArrayList&lt;&gt; ();

    public void add(T cart, U basket) {
        carts.add(cart);
        baskets.add(basket);
    }
}

Cart&lt;String, String&gt; myCart = new Cart&lt;&gt;();</code></pre><h2 id="유의할-점">유의할 점</h2>
<h3 id="제네릭-제약의-필요성">제네릭 제약의 필요성</h3>
<p>제네릭은 참조형 타입을 모두 받을 수 있기때문에 발생하는 사이드이펙트가 있다.
만약 Integer 타입 전용으로 만든 클래스에 문자열 파라미터가 들어가면, 컴파일 과정에서는 에러가 발생하지 않으나, 런타임 오류가 발생할 수 있다. 
이를 위해 제네릭은 제한을 걸어 둘 수 있다. </p>
<h4 id="extends-super-와일드카드">extends, super, ?(와일드카드)</h4>
<ul>
<li>extends T 
상한 경계로 T와 T의 사존 타입에 대해서만 지정 가능하다.
T를 Food Class로 지정시, Food 클래스를 상속한 Water, Pizza, HawaiiPizza, CombiPizza 모두 가능</li>
<li>super T
하한 경계로 T와 T 부모 타입에 대해서만 지정가능하다.
T를 Pizza로 지정시, Pizza, Food만 사용가능
<img src="https://velog.velcdn.com/images/hope_k/post/27e53277-4198-4b31-bb2f-cc0ce70e730a/image.png" alt=""></li>
</ul>
<h2 id="장점">장점</h2>
<ul>
<li>코드의 재사용성</li>
<li>코드의 확장성</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[신규 프로젝트 스펙 관련하여]]></title>
            <link>https://velog.io/@hope_k/%EC%8B%A0%EA%B7%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8A%A4%ED%8E%99-%EA%B4%80%EB%A0%A8%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@hope_k/%EC%8B%A0%EA%B7%9C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%8A%A4%ED%8E%99-%EA%B4%80%EB%A0%A8%ED%95%98%EC%97%AC</guid>
            <pubDate>Sun, 15 Sep 2024 04:17:00 GMT</pubDate>
            <description><![CDATA[<p>신규 리액트 프로젝트를 시작하며 zero base에서부터 개발, 패키지매니저, 빌드, 배포라인까지 수많은 라이브러리중에 어떤 것을 적재적소로 사용할지 고민하며 팀원끼리 논의하는 시간을 갖기로 했다. </p>
<p>프로젝트의 특성마다 필요로하는 언어, 라이브러리의 스펙이 달라지며 이를 잘 선택하는 안목 또한 개발자가 갖추어야할 소양이라고 생각한다. </p>
<ol>
<li><p>신규 프로젝트 간략 소개
일부 클라이언트를 위한 상품목록, 주문, 결제등을 갖춘 폐쇄몰 쇼핑몰의 느낌이지만, 추후 B2C 공개몰로 전환할 가능성이 있음
공개몰로 전환될 가능성이 있는 폐쇄 쇼핑몰의 특성을 가진 프로젝트를 개발할 예정이다.
또한 다국어를 지원하는 글로벌한 반응형 쇼핑몰을 개발하는 것이 이 프로젝트의 목적이다. </p>
</li>
<li><p>리액트 v18 선택</p>
</li>
<li><p>App Router vs Pages Router
리액트 공식문서에서는 이 둘을 이렇게 이야기 하고있다. 앱 라우터는 페이지 라우터를 개선하여 만들었고 권장하는 라우팅 방식이다. </p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/hope_k/post/2b4d6e22-2ddc-449f-9087-039035f45786/image.png" alt=""></p>
<p>1) App router 장점</p>
<ul>
<li>layout.tsx 를 통해 하위 Segment들과 UI, 및 State 자원등을 공유하고 상태를 유지하여 리랜더링을 방지한다. </li>
<li>다양하고 최적화된 리액트 기능을 사용할 수 있다. ex) Error Boundery, Suspence</li>
<li>SSR, CSR 등 필요한 렌더링을 쉽게 사용할 수 있다. </li>
</ul>
<ol start="4">
<li><p>상태관리 Zustand </p>
</li>
<li><p>Fetching 도구 SWR vs react-query vs next fetch </p>
</li>
</ol>
<ul>
<li>서버 컴포넌트에서는 Next 에서 제공하는 fetch 기능을, 클라이언트 컴포넌트에서는 SWR을 사용하는 것은 어떨까? </li>
</ul>
<ol start="6">
<li>스타일 라이브러리 tailwind.css vs matierial css vs styled-components vs Radix</li>
</ol>
<ul>
<li>styled-components 는 스타일을 런타임에 생성하기 때문에 캐싱이 어렵고 javascript 해석 시간이 소요된다.</li>
<li>SSR 스타일을 적용하기도 tailwind.css 가 유리하다.</li>
<li>tailwind.css 는 제한된 스타일링만 사용가능하다는 단점이 있다. </li>
<li>Material UI는 낮은 러닝커브와 빠른 개발속도의 장점이 있고, 테마 지정 및 스타일 재정의를 통해 높은 수준의 유연성을 제공하지만 Tailwind CSS에 비해 디자인 자유도가 높지 않을 수 있다.</li>
</ul>
<ol start="6">
<li><p>패키징 매니저 yarn vs yarn berry vs pnpm</p>
</li>
<li><p>모듈 번들러 도구 webpack vs vite </p>
</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>