<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>giant_toothpick.log</title>
        <link>https://velog.io/</link>
        <description>frontend developer</description>
        <lastBuildDate>Wed, 15 May 2024 15:59:20 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>giant_toothpick.log</title>
            <url>https://velog.velcdn.com/images/giant_toothpick/profile/db868698-c648-435d-acbb-5588dc65497e/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. giant_toothpick.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/giant_toothpick" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[아직도 console.log만 씀?]]></title>
            <link>https://velog.io/@giant_toothpick/%EC%95%84%EC%A7%81%EB%8F%84-console.log%EB%A7%8C-%EC%94%80</link>
            <guid>https://velog.io/@giant_toothpick/%EC%95%84%EC%A7%81%EB%8F%84-console.log%EB%A7%8C-%EC%94%80</guid>
            <pubDate>Wed, 15 May 2024 15:59:20 GMT</pubDate>
            <description><![CDATA[<p><code>javascript</code>를 사용해 개발을 할 때 <code>console.log</code>를 항상 달고 살았습니다. console은 javascript에서 제공하는 내장 객체 중 하나인데 주로 디버깅과 로깅에 사용됩니다. 저는 <code>console.log</code>를 사용해 변수를 출력해보거나 if문이 잘 실행되었는지 메시지를 출력해보는 과정에서 특히 더 많이 사용했습니다.</p>
<pre><code class="language-jsx">// 변수명 출력 

let name =&quot;jhon&quot;
console.log(name)</code></pre>
<pre><code class="language-jsx">// if문 실행되는지 확인

if(flag){
    console.log(&quot;if문으로 들어옴!!&quot;)
    // 나머지 코드.. 
}</code></pre>
<p>물론 console 안에는 log보다 훨씬 더 많은 기능이 들어있습니다. 한번쯤 들어봤을 법한 warn, info, error등을 통해 console에 출력되는 메시지의 형태를 변경할 수도 있지만 전혀 사용하지 않았습니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/27f1b382-db1e-4fdd-915a-23a140b109bf/image.png" alt=""></p>
<p><br/><br/><br/></p>
<p>javascript를 사용하면서 어떻게 보면 가장 많이 사용한 문법중 하나가 <code>console.log</code>인데 그동안 전혀 활용하지 못하고 있었다는 생각이 들었습니다. 그래서 이번 기회에 console 함수들을 어떻게 사용하는지, 어떤식으로 활용하면 좋을지 알아보고자 합니다. 활용법은 공식문서를 참고했습니다.</p>
<h1 id="📌group">📌group()</h1>
<p>콘솔에 출력되는 로그를 그룹화 하여 보기 쉽게 만들어 줍니다. <code>group</code>으로 그룹의 시작 지점을 정하고 <code>groupEnd</code>로 로그의 끝을 나타냅니다.</p>
<pre><code class="language-jsx">console.group(&quot;사용자 정보&quot;);

console.log(&quot;이름: John&quot;);
console.log(&quot;나이: 30&quot;);
console.log(&quot;직업: Developer&quot;);

console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/4ac12c9e-cabf-489b-9442-aba83f851b1f/image.png" alt=""></p>
<p>필요한 정보가 그룹화 되어 표시되는 것을 볼 수 있습니다. </p>
<p>물론 겹겹이 사용할 수도 있습니다.</p>
<pre><code class="language-jsx">    //바깥쪽
    console.group(&quot;outer&quot;);
    console.log(&quot;outer&quot;)

    //안쪽
    console.group(&quot;inner&quot;);
    console.log(&quot;inner&quot;)
    console.groupEnd();

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/6c95cfce-e9df-48fb-b2db-6d0b529be976/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h1 id="📌groupcollapsed">📌groupCollapsed()</h1>
<p><code>group</code>과 동일합니다. 다만 차이점은 <code>console</code>에 축소된 상태로 표시됩니다. </p>
<pre><code class="language-jsx">    //groupCollapsed 사용
         console.groupCollapsed(&quot;groupCollapsed : 사용자 정보&quot;);

    console.log(&quot;이름: John&quot;);
    console.log(&quot;나이: 30&quot;);
    console.log(&quot;직업: Developer&quot;);

    console.groupEnd();

    //group 사용
    console.group(&quot;group : 사용자 정보2&quot;);

    console.log(&quot;이름: John&quot;);
    console.log(&quot;나이: 30&quot;);
    console.log(&quot;직업: Developer&quot;);

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/7f95bb05-bed2-4b60-8a65-98f32156a79c/image.png" alt="">
<code>console</code>로 출력할 일이 많을 때 <code>groupCollapsed</code>가 더 가시성이 더 좋을것 같습니다.</p>
<p><br/><br/><br/></p>
<h1 id="📌assert">📌assert()</h1>
<p>주어진 조건이 false인 경우에만 메시지를 출력합니다. 이를  사용하여 간단한 조건 확인을 통해 디버깅을 수행할 수 있습니다. </p>
<pre><code class="language-jsx">     //assert 사용
    console.group(&quot;assert&quot;);

    console.assert(false, &quot;false일 때 출력 됩니다.&quot;);
    console.assert(true, &quot;true일 때는 출력되지 않습니다.&quot;);

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/e593d18b-fcc5-4ece-950f-ab3c6361117b/image.png" alt=""></p>
<p>공식문서에 보면 다음과 같이 표시되어있는데 메시지 혹은 객체를 연속으로 넣을 수 있습니다. </p>
<pre><code class="language-jsx">assert(assertion)

assert(assertion, obj1)
assert(assertion, obj1, obj2)
assert(assertion, obj1, obj2, /* …, */ objN)

assert(assertion, msg)
assert(assertion, msg, subst1)
assert(assertion, msg, subst1, /* …, */ substN)</code></pre>
<p>이를 활용해서 메시지 말고도 다음과 같이 객체를 출력할 수도 있습니다. </p>
<pre><code class="language-jsx">    console.group(&quot;assert&quot;);
    const userInfo = { name: &quot;홍길동&quot;, age: 30 };
    console.assert(userInfo.age &gt; 40, &quot;나이가 40대 이상이 아닙니다.&quot;, userInfo);
    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/79733ffd-b5c3-495c-8f50-bd9cf66a415b/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h1 id="📌count">📌count()</h1>
<p>주로 메서드가 호출된 횟수를 콘솔에 출력할 때 사용됩니다. 주어진 라벨과 함께 호출될 때마다 호출 횟수가 증가합니다. 이벤트나 함수 호출을 추적하고 디버깅할 때 유용합니다. </p>
<pre><code class="language-jsx">    console.group(&quot;count&quot;);

    function func() {
      console.count(&quot;functions count&quot;); //함수를 실행할 때마다 카운트증가
    }
    func();
    func();
    func();

        //라벨을 지정해 다른 카운트 실행
    console.count(&quot;other count&quot;);
    console.count(&quot;other count&quot;);

        //기본 카운트
    console.count()
    console.count()
    console.count()

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/5f4fdf6d-ef78-468b-8c90-53b407467618/image.png" alt="">
라벨을 통해 호출 카운트를 특정지어 사용할 수 있다는 게 특히 유용할 것 같습니다. </p>
<p><br/><br/><br/></p>
<h1 id="📌coutreset">📌coutReset()</h1>
<p>카운터의 개수를 0으로 초기화 시켜주는 역할을 합니다. </p>
<pre><code class="language-jsx">    console.group(&quot;countReset&quot;);

    function func() {
      console.count(&quot;functions count&quot;);
    }
    func();
    func();
    func();
    console.countReset(&quot;functions count&quot;) //카운트 리셋
    func();
    func();

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/a6049712-8227-4e8c-94d6-259dc51d424f/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h1 id="📌log-info-error-warn">📌log(), info(), error(), warn()</h1>
<p>모두 콘솔에 메시지를 출력하는 데 사용된다는 점에서 log와 크게 다르지 않지만 시각적으로 명확하게 어떤 유형의 메시지 인지를 나타내기 위해 사용됩니다. </p>
<pre><code class="language-jsx">    console.group(&quot;log, error, info, warn&quot;);

    // console.info()를 사용하여 정보성 메시지 출력
    console.info(&quot;사용자가 로그인하였습니다.&quot;);

    // console.error()를 사용하여 에러 메시지 출력
    const divisor = 0;
    if (divisor === 0) {
      console.error(&quot;0으로 나눌 수 없습니다.&quot;);
    } else {
      const result = 10 / divisor;
      console.log(&quot;결과:&quot;, result);
    }

    // console.warn()을 사용하여 경고 메시지 출력
    const age = 15;
    if (age &lt; 18) {
      console.warn(&quot;미성년자입니다. 본 사이트 이용이 제한될 수 있습니다.&quot;);
    }

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/eb34150a-6747-4c92-89ae-48bddf37f632/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h1 id="📌table">📌table()</h1>
<p>배열이나 객체를 테이블 형태로 콘솔에 출력하는데 사용됩니다. 이를 통해 데이터를 시각적으로 표시할 수 있습니다. 저는 복잡한 형태의 object가 아니라면 table로 출력하는게 가시성이 훨씬 좋았습니다. </p>
<pre><code class="language-jsx">// 배열사용
   console.group(&quot;table&quot;);

    const fruits = [
      { name: &quot;Apple&quot;, color: &quot;Red&quot;, price: 1 },
      { name: &quot;Banana&quot;, color: &quot;Yellow&quot;, price: 0.5 },
      { name: &quot;Grapes&quot;, color: &quot;Purple&quot;, price: 2 },
    ];

    console.table(fruits);

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/387880d3-3718-4de2-8be4-5e8dcedc71b3/image.png" alt=""></p>
<pre><code class="language-jsx">
//객체 사용
    console.group(&quot;table&quot;)

    const fruits = { name: &quot;Apple&quot;, color: &quot;Red&quot;, price: 1 };
    console.table(fruits);

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/4d7ca6ce-129e-4680-9bd0-b1356c880b15/image.png" alt=""></p>
<p>객체의 경우 객체 안의 객체 형태로 깊이가 깊어질 경우 약식으로 표시될 수도 있어 복잡한 객체에 사용하는건 별로 좋지 않아보입니다. </p>
<pre><code class="language-jsx">  console.group(&quot;table&quot;);

    const someObbject = {
        data1:{
            data2:{
                data3:&quot;data3&quot;
            }
        }
    }
    console.table(someObbject);</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/3f8b3e0f-e524-47cd-9dec-a782c3f29026/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h1 id="📌time-timelog-timeend">📌time() timeLog() timeEnd()</h1>
<p>코드의 시간을 측정하는데 사용됩니다. 일부 코드 블록을 실행되는 데 걸리는 시간을 측정하고 성능 분석 및 최적화에 도움이 됩니다. </p>
<pre><code class="language-jsx">    console.group(&quot;time&quot;);

    // 시간 측정 시작
    console.time(&quot;myTimer&quot;);

    // 시간이 오래 걸리는 작업 수행
    for (let i = 0; i &lt; 1000000; i++) {
      // 작업
    }

    // 시간 측정 종료 및 결과 출력
    console.timeEnd(&quot;myTimer&quot;);

    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/424182c2-fe59-465c-a2cb-606207e3be49/image.png" alt=""></p>
<pre><code class="language-jsx">  // 중간에 경과 시간 로깅
    console.time(&quot;anotherTimer&quot;);
    // 시간이 오래 걸리는 다른 작업 수행
    for (let i = 0; i &lt; 1000000; i++) {
      // 작업
    }
    console.timeLog(&quot;anotherTimer&quot;, &quot;50만 번째 반복 완료&quot;); // 중간에 경과 시간 로깅
    console.timeEnd(&quot;anotherTimer&quot;);

    console.groupEnd();
</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/cbbf86bf-e9b6-489b-a11f-2326b673363f/image.png" alt=""></p>
<p>fetch를 사용해 api통신을 할 때도 얼마나 걸리는지 측정할 수 있습니다. </p>
<pre><code class="language-jsx">    console.time(&quot;fetching&quot;);
    fetch(&quot;https://jsonplaceholder.typicode.com/todos&quot;)
      .then((data) =&gt; console.timeLog(&quot;fetching&quot;, &quot;성공!&quot;, data))
      .catch((error) =&gt; console.timeLog(&quot;fetching&quot;, &quot;실패!&quot;, error))
      .finally(() =&gt; timeEnd(&quot;fetching&quot;));
    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/3f35a491-8882-4f53-aee1-756a31a067d8/image.png" alt=""></p>
<p><br/><br/><br/></p>
<h1 id="📌dir">📌dir</h1>
<p>자바스크립트에서 객체를 출력할 때 사용하는 콘솔 메서드 중 하나입니다. 이 메서드는 객체의 속성과 속성 값을 나열하여 출력합니다. 주로 객체의 구조를 파악할 때 사용합니다. </p>
<p>예시로 console.log와 console.dir의 차이점을 살펴봅시다.</p>
<pre><code class="language-jsx">// html 태그 
&lt;div&gt;박스&lt;/div&gt;

// dir과 log 사용
console.group(&quot;dir&quot;);
const div = document.querySelector(&quot;div&quot;)
console.log(div)
console.dir(div)
console.groupEnd();
</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/ea04e210-a8cd-4b32-91e3-43ca226e2de5/image.png" alt=""></p>
<p>log는 단순히 태그만 출력되는 반면, dir은 div안의 속상까지 출력해 주고 있습니다. 이현상은 객체를 출력할 때도 똑같이 발생합니다. </p>
<pre><code class="language-jsx">// 객체 출력
    console.group(&quot;dir&quot;);
    const person = {
      name: &quot;John&quot;,
      age: 30,
      address: {
        city: &quot;New York&quot;,
        zip: &quot;10001&quot;,
      },
    };
    console.log(person);
    console.dir(person);
    console.groupEnd();</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/c3ae202c-a2e5-4b43-a72f-87d35b00cd96/image.png" alt="">
객체를 속성들을 트리구조로 표시하다보니 log보다 객체를 명확하게 살펴볼 수 있다는 장점이 있습니다. 또한 객체가 복잡한 경우에도 객체의 중첩된 속성을 쉽게 확인할 수 있습니다. </p>
<p><br/><br/><br/></p>
<h1 id="📌trace">📌trace()</h1>
<p>trace를 호출하면 호출 스택의 각 단계에서 함수의 이름과 위치가 표시됩니다. 이를 통해 코의 흐름을 추적하고, 함수가 어디서 호출되었는지를 파악할 수 있습니다. </p>
<pre><code class="language-jsx">console.group(&quot;trace&quot;)
    function innerFunction() {
  console.trace(&quot;Trace from innerFunction:&quot;);
}

function middleFunction() {
  innerFunction();
}

function outerFunction() {
  middleFunction();
}

outerFunction();
console.groupEnd()</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/41af6c91-d0f5-4ff1-812c-853db1c4ee52/image.png" alt="">
함수의 실행순서를 스택 순서로 보여주는것을 알 수 있습니다.</p>
<p><br/><br/><br/></p>
<h1 id="느낀점">느낀점</h1>
<p>그동안 console.log를 사용하면서 알게 모르게 느꼈던 불편점들이 있었는데 이부분을 시원하게 긁어주는 기능이 진작에 구현되어 있었다는걸 깨달았습니다. 특히 성능적인 면을 측정할 때 time을 유용하게 사용할 수 있을거 같아서 벌써부터 기대되네요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[camelCase, snake_case 변환하기 ]]></title>
            <link>https://velog.io/@giant_toothpick/camelCase-snakecase-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@giant_toothpick/camelCase-snakecase-%EB%B3%80%ED%99%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 06 May 2024 17:03:12 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>DB에서는 주로 데이터를 <code>snakeCase</code>로 다루는 경우가 많습니다. 보통 DB의 컬럼 이름을 지을 때 <code>snakeCase</code>를 사용하는 것이 일반적이기 때문입니다. 그에 반해 React에서는 변수 명을 지을 때 <code>camelCase</code>를 많이 사용합니다. 당장 <code>useState</code>를 사용하는 것만 봐도 <code>camelCase</code>를 사용하는 것을 볼 수 있습니다.</p>
<pre><code class="language-jsx">const [userInfo, setUserInfo] = useState({ userName:&quot;이름&quot;, userEmail:&quot;abc@email.com&quot;})</code></pre>
<blockquote>
<p>카멜 케이스 (Camel Case) : <code>userInfo</code>, <code>userEmail</code>, <code>isActive</code></p>
</blockquote>
<blockquote>
<p>스네이크 케이스 (Snake Case) : <code>user_info</code>, <code>user_email</code>, <code>is_active</code></p>
</blockquote>
<br/>
<br/><br/>


<p>이렇게 네이밍 컨벤션이 서로 다르기 때문에 프로젝트를 진행하다 보면 서버에 데이터를 요청할 때 <code>snakeCase</code>로 오는 경우가 많았습니다.  DB에서 꺼낸 그대로 넘겨주거나 혹은 변수 명을 변경해서 보내주더라도 <code>snakeCase</code>보내줄 때가 많았습니다.</p>
<pre><code class="language-jsx">//예시 데이터 
data : {
    user_name:&quot;홍길동&quot;
    user_email:&quot;abc@email.com&quot;
}</code></pre>
<p>저는 frontend에서는 <code>camelCase</code>를 사용하는 게 편했기 때문에 다음처럼 변경해서 사용해 줘야 했습니다. </p>
<pre><code class="language-jsx">//예시 코드
function getUserInfo(data){
    const body = {
        user_name:data.userName,
        user_email:data.userEmail,
        }
    axios.get(&quot;https://도메인.com/api/userInfo&quot;,body)
    .then(res =&gt; {
        const data = {
                userName:res.data.userName
                userEmail:res.data.userEmail
    }
        return data;
    )    
}</code></pre>
<p>요청을 보낼 때 그리고 응답을 받을 때, 두 번식 변수 명을 변경하다 보니 쓸대 없이 코드가 길어지고 코드가 길어진 만큼 실수가 많아졌습니다. </p>
<p>응답에 속성이 많으면 많을수록 변수 명을 변경하는 과정이 피곤하고 의미 없게 느껴졌습니다. 
<br/>
<br/><br/></p>
<h1 id="해결">해결</h1>
<p><code>camelCase</code>와 <code>snakeCase</code>를 변환하는 함수를 만들어 이 문제를 해결하려 했고 함수의 역할을 다음과 같이 설정했습니다. </p>
<ol>
<li><code>camelCase</code>를 <code>snakeCase</code>로 변경</li>
<li>객체안에 객체가 존재해도 <code>camelCase</code>를 <code>snakeCase</code>로 변경</li>
<li>배열로 이루어진 객체가 존재해도 <code>camelCase</code> 와 <code>snakeCase</code>로 변경</li>
<li>반대로 변환하는 것도 가능한 함수 추가 제작</li>
</ol>
<h3 id="camel-→-snake">camel → snake</h3>
<pre><code class="language-jsx">function camelToSnake(obj: any): any {
  if (Array.isArray(obj)) {
  //객체 배열도 재귀로 해결
    return obj.map((v) =&gt; camelToSnake(v));
  } else if (obj !== null &amp;&amp; obj.constructor === Object) {
    return Object.keys(obj).reduce((result: any, key: string) =&gt; {
      const snakeKey = key.replace(
        /[A-Z]/g,
        (matches) =&gt; &quot;_&quot; + matches.toLowerCase()
      );
        let value = obj[key];

       // 중첩된 객체에 대해 재귀적으로 camelToSnake함수 호출
      if (value !== null &amp;&amp; typeof value === &#39;object&#39;) {
        value = camelToSnake(value);
      }


      result[snakeKey] = value;
      return result;
    }, {});
  }
  return obj;
}</code></pre>
<h3 id="snake→camel">snake→camel</h3>
<pre><code class="language-jsx">export function snakeToCamel(obj: any): any {
  if (Array.isArray(obj)) {
    //객체 배열도 재귀로 해결
    return obj.map((v) =&gt; snakeToCamel(v));
  } else if (obj !== null &amp;&amp; typeof obj === &#39;object&#39;) {
    return Object.keys(obj).reduce((result: any, key: string) =&gt; {
      let camelKey = key.replace(/([-_]\w)/g, (matches) =&gt; matches[1].toUpperCase());
      let value = obj[key];

      // 중첩된 객체에 대해 재귀적으로 snakeToCamel 함수 호출
      if (value !== null &amp;&amp; typeof value === &#39;object&#39;) {
        value = snakeToCamel(value);
      }

      result[camelKey] = value;
      return result;
    }, {});
  }
  return obj;
}</code></pre>
<p>다음과 같이 작성하면 함수의 재귀호출을 활용해서 객체의 배열, 그리고 객체안의 객체도 변수명을 모두 변환할 수 있습니다.!</p>
<pre><code class="language-jsx">//예시 코드
function getUserInfo(data){
    const body = camelToSnake(data)
    axios.get(&quot;https://도메인.com/api/userInfo&quot;,body)
    .then(res =&gt;snakeToCamel(res.data))
}</code></pre>
<p>더럽던 함수도 깔끔하게 바뀐 모습을 볼 수 있습니다. 
<br/>
<br/><br/></p>
<h1 id="느낀점">느낀점</h1>
<hr>
<p>코드 컨벤션의 중요성을 느끼며, 앞으로는 프로젝트 초기에 코드 컨벤션을 명확히 정하고 따르는 것이 중요하다는 것을 배웠습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[www. 있는거 없는거 차이점]]></title>
            <link>https://velog.io/@giant_toothpick/www.-%EC%9E%88%EB%8A%94%EA%B1%B0-%EC%97%86%EB%8A%94%EA%B1%B0-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@giant_toothpick/www.-%EC%9E%88%EB%8A%94%EA%B1%B0-%EC%97%86%EB%8A%94%EA%B1%B0-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Sat, 04 May 2024 13:30:30 GMT</pubDate>
            <description><![CDATA[<p>웹사이트를 제작하는데 문제가 발생했다. </p>
<p>카페 24에서 서버와 <code>example.com</code> 이라는 도메인을 구매해서 사이트를 제작했는데 친구에게 사이트가 제대로 동작하지 않는다는 피드백을 받았다. </p>
<p>문제가 무엇인지 확인하기 위해서 웹페이지에 접속했는데 내 컴퓨터에서는 아무리 해도 에러를 재현할 수가 없었다. </p>
<br/>
<br/>
<br/>
# 원인

<p>나중에 알아보니 친구는 <code>www.example.com</code>으로 접속했고 나는 <code>example.com</code>으로 접속하고 있었다. </p>
<p>이 때문에 backend에서는 도메인에 차이가 있다고 판단하고 cors에러를 발생 시켰던 것이었다. </p>
<blockquote>
<p>난 <a href="http://www.example.com">http://www.example.com</a> 을 구매한 적이 없는데!</p>
</blockquote>
<p>두 개는 분명 다른 도메인이지만 카페24에서 <a href="http://example.com">http://example.com</a> 도메인을 구매했다면 <a href="http://www.example.com%EC%9C%BC%EB%A1%9C%EB%8F%84">www.example.com으로도</a> 접속이 가능하다. 도메인 연결 시 www 서브 도메인이 자동으로 설정되기 때문이다</p>
<blockquote>
</blockquote>
<p>💡 www 서브 도메인은 카페24 내에서 삭제할 수 있다!</p>
<br/>
<br/>
<br/>

<h1 id="해결">해결</h1>
<p>두 가지 방법으로 해결할 수 있는데 하나는 nginx를 활용하는 것이고 하나는 backend 서버에서 cors에 <a href="http://www.example.com%EC%9D%84">www.example.com을</a> 추가하는 방법이 있다.</p>
<h3 id="nginx활용">nginx활용</h3>
<p>해당 프로젝트에서는 nginx를 <code>reverse-proxy</code>로 활용해서 frontend와 backend를 라우팅 했다. </p>
<p>만일 <code>www.example.com</code>으로 접속을 시도한다면 <code>example.com</code>으로 리디렉션하는 코드를 추가해 주었다. </p>
<p><code>/etc/nginx/conf.d/default.conf</code></p>
<pre><code class="language-bash"># www가 붙었는지 확인하는 함
map $http_host $no_www {
    default        0;
    &quot;~^www\.(?&lt;domain&gt;.+)$&quot;    $domain;
}

server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
        listen 443 ssl;
        server_name www.example.com example.com ;

                # www가 붙어있다면 www를 제거한 상태로 다시 접속시키기
        if ($http_host ~* ^www\.) {
        return 301 https://example.com$request_uri;
        }

                #... 나머지 코드
}</code></pre>
<h3 id="backend-설정">backend 설정</h3>
<p>backend 를 <code>django</code>로 만들었다. </p>
<p>CORS_ALLOWED_ORIGINS 에 cors를 허용할 사이트를 추가해 주었다. </p>
<pre><code class="language-bash">CORS_ALLOWED_ORIGINS = [
    &quot;https://example.com&quot;,
    &quot;https://www.example.com&quot;,
]</code></pre>
<br/>
<br/>
<br/>
# 결론

<hr>
<p>카페24에서 도메인을 구매할 때 www. 서브 도메인이 자동으로 생성 되는 것을 이번에 처음 알게 됐다.  그리고 분명히 주소를 알려주어도 www.을 붙여서 접속하는 이용자가 생길 수 있다는 것도 이번에 깨달았다. cors에러는 끝이 없다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리엑트에서 화면 캡처 기능 만들기 ]]></title>
            <link>https://velog.io/@giant_toothpick/%EB%A6%AC%EC%97%91%ED%8A%B8%EC%97%90%EC%84%9C-%ED%99%94%EB%A9%B4-%EC%BA%A1%EC%B2%98-%EA%B8%B0%EB%8A%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@giant_toothpick/%EB%A6%AC%EC%97%91%ED%8A%B8%EC%97%90%EC%84%9C-%ED%99%94%EB%A9%B4-%EC%BA%A1%EC%B2%98-%EA%B8%B0%EB%8A%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Thu, 25 Apr 2024 00:49:38 GMT</pubDate>
            <description><![CDATA[<p>리액트를 사용하면서 특정 구역에 화면을 캡쳐하는 기능을 만들어야 하는 경우가 생겼다. </p>
<h2 id="라이브러리">라이브러리</h2>
<pre><code>npmm i html2canvas</code></pre><p>화면 캡처로 가장 많이 사용되는 라이브러리인 <strong>html2canvas</strong>를 사용했다.</p>
<h2 id="코드">코드</h2>
<pre><code>import React, { useRef } from &#39;react&#39;;
import html2canvas from &#39;html2canvas&#39;;

const CaptureComponent: React.FC = () =&gt; {
//캡처할 html 요소 선택
  const captureRef = useRef&lt;HTMLDivElement&gt;(null);

//캡처 함수 
  const onCapture = () =&gt; {

    if (captureRef.current) {
      html2canvas(captureRef.current).then(canvas =&gt; {

       //사용할 이미지 포멧과 제목 선택
       onSaveAs(canvas.toDataURL(&#39;image/png&#39;), &#39;image-download.png&#39;);
      });
    }
  };

//다운로드 함수 
  const onSaveAs = (uri: string, filename: string) =&gt; {

//a 태그를 생성하고 다운로드받음 
    const link = document.createElement(&#39;a&#39;);
    document.body.appendChild(link);
    link.href = uri;
    link.download = filename;
    link.click();
    document.body.removeChild(link);
  };

  return (
    &lt;div&gt;
      &lt;div ref={captureRef} id=&quot;capture&quot;&gt;
        {/* Your content to be captured */}
      &lt;/div&gt;
      &lt;button onClick={onCapture}&gt;Capture&lt;/button&gt;
    &lt;/div&gt;
  );
};

export default CaptureComponent;
</code></pre><h2 id="마치며">마치며</h2>
<p>필요하다 싶은 기능은 왠만하면 누군가 다 만들어 놓은것 같다 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[firewalld 로 port 우주방어하기 ]]></title>
            <link>https://velog.io/@giant_toothpick/firewalld-%EB%A1%9C-port-%EC%9A%B0%EC%A3%BC%EB%B0%A9%EC%96%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@giant_toothpick/firewalld-%EB%A1%9C-port-%EC%9A%B0%EC%A3%BC%EB%B0%A9%EC%96%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 04 Mar 2024 06:55:12 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/8cf183e7-1d20-44fb-941a-133d6f66be9f/image.png" alt=""></p>
<h1 id="firewalld-란">firewalld 란?</h1>
<hr>
<blockquote>
</blockquote>
<p>Firewalld는 리눅스 시스템의 네트워크 보안을 관리하는 데 사용되는 도구, 이를 통해 사용자는 방화벽 규칙을 정의하고, 네트워크 트래픽을 제어하며, 서비스를 관리할 수 있다. Firewalld는 iptables를 기반으로 하며, 간단한 명령어를 사용하여 방화벽 설정을 관리할 수 있다.</p>
<p>이상한 포트로 악성 사용자가 접속하는 것을 막기위해서는 방확벽 설정이 필수적이라고 한다.이 때 많이 사용하는것이 iptables를 기반으로 하는 firewalld 이란 프로그램이다. </p>
<p>사용할 때 주의할 점과 어떻게 사용하는지 알아보자! </p>
<br/>
<br/>
<br/>

<h1 id="사용법">사용법</h1>
<hr>
<blockquote>
<p>처음에 start 명령어로 방화벽을 시작히면 ports 설정에 아무것도 없기 때문에 기본적으로 아무것도 막지 않는다. </p>
</blockquote>
<p>리눅스 배포판에는 기본적으로 firewalld가 설치되어있다.</p>
<pre><code class="language-bash">sudo systemctl enable firewalld #시스템 부팅시 자동으로 firewalld 활성화
sudo systemctl start firewalld # Firewalld 서비스를 시작
sudo systemctl status firewalld #Firewalld 서비스의 현재 상태확인
sudo systemctl stop firewalld # 방화벽 종료</code></pre>
<p>다음 명령어를 통해 firewalld를 실행해주자</p>
<pre><code class="language-bash">firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2024-03-05 09:30:00 UTC; 2min 30s ago
     Docs: man:firewalld(1)
 Main PID: 12345 (firewalld)
    Tasks: 2 (limit: 32768)
   Memory: 20.0M
   CGroup: /system.slice/firewalld.service
           └─12345 /usr/bin/python3 /usr/sbin/firewalld --nofork --nopid

Mar 05 09:30:00 server systemd[1]: Starting firewalld - dynamic firewall daemon...
Mar 05 09:30:00 server systemd[1]: Started firewalld - dynamic firewall daemon.
</code></pre>
<p>다음과 같은 상태정보가 나오게 된다. </p>
<br/>
<br/>
<br/>

<h1 id="특정-포트-열기">특정 포트 열기</h1>
<hr>
<blockquote>
<p><strong>주의!</strong> 
ssh로 접속 중이라면 반드시 22번 포트를 열어주어야 한다! 안 그러면 튕기기 때문에 주의하자</p>
</blockquote>
<pre><code class="language-bash">sudo firewall-cmd --zone=public --add-port=[열고싶은 포트번호]/tcp --permanent #80번포트 열기
sudo firewall-cmd --zone=public --remove-port=[닫고싶은 포트번호]/tcp --permanent ## 80번 포트 닫기
sudo firewall-cmd --reload # firewalld 재시작</code></pre>
<p>난 다음과 같은 포트를 필수로 열어두었다.</p>
<pre><code class="language-bash">sudo firewall-cmd --zone=public --add-port=80/tcp --permanent #http
sudo firewall-cmd --zone=public --add-port=8000/tcp --permanent #backend
sudo firewall-cmd --zone=public --add-port=443/tcp --permanent #https
sudo firewall-cmd --zone=public --add-port=3000/tcp --permanent #frontend
sudo firewall-cmd --zone=public --add-port=3306/tcp --permanent #database
sudo firewall-cmd --zone=public --add-port=22/tcp --permanent #ssh
sudo firewall-cmd --zone=public --add-port=21/tcp --permanent #ftp</code></pre>
<br/>
<br/>
<br/>


<h1 id="혹시-실행이-안되나요">혹시 실행이 안되나요?</h1>
<hr>
<h3 id="problem"><strong>Problem:</strong></h3>
<p>다음과 같은 커맨드를 입력했을때</p>
<pre><code class="language-bash">systemctl start firewalld</code></pre>
<p>이런 반응이 나오면</p>
<pre><code class="language-bash">**123firewalld.service: Start operation timed out. 
Terminating. firewalld.service: Failed with result &#39;timeout&#39;. 
systemd: Failed to start firewalld - dynamic firewall daemon.**</code></pre>
<h3 id="solution"><strong>Solution:</strong></h3>
<p>다음과 같은 커멘드를 입력하면 된다!</p>
<pre><code class="language-bash">**1systemctl stop firewalld;pkill -f firewalld;systemctl start firewalld**</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[mixed content error 처리하기 (with. Nginx)]]></title>
            <link>https://velog.io/@giant_toothpick/mixed-content-error-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0-with.-Nginx</link>
            <guid>https://velog.io/@giant_toothpick/mixed-content-error-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0-with.-Nginx</guid>
            <pubDate>Sat, 02 Mar 2024 08:33:04 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>mixed content란?
&quot;혼합 콘텐츠 오류&quot;는 웹 페이지에서 보안 연결(HTTPS)을 통해 제공되는 페이지에 비해 보안되지 않은 연결(HTTP)을 통해 로드되는 콘텐츠가 있는 경우 발생한다. 일반적으로 HTTPS로 보호된 페이지에 HTTP로 로드되는 이미지, 스크립트 또는 스타일 시트와 같은 외부 리소스가 있는 경우에 발생한다.</p>
</blockquote>
<p>난 분명 nginx에서 https를 처리하고 있는데 왜 이런일이 발생할까?</p>
<br/>
<br/>
<br/>



<h1 id="원인">원인</h1>
<blockquote>
<p>원인은 요청은 https로 보내는데 응답이 http로 오기 때문이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/71c81d4b-a61f-429b-83f4-79804d273894/image.png" alt=""></p>
<p>https로 온 요청을 nginx가 http로 변환한 후에 backend로 보내는 작업은 하고 있지만 http를 다시 https로 변환하는 작업을 하고 있지 않기 때문이다!</p>
<br/>
<br/>
<br/>

<h1 id="해결">해결</h1>
<blockquote>
<p>nginx 설정을 변경해 주자 </p>
</blockquote>
<pre><code class="language-bash">server{

        // 기존 코드.. 


        location /api {
            rewrite ^/api(/.*)$ $1 break;
            proxy_pass http://localhost:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
    }
}
</code></pre>
<p><strong>코드 동작 순서</strong></p>
<ol>
<li><em><a href="https://example.com/api/someRequest">https://example.com/api/someRequest</a></em> 요청</li>
<li>nginx에서 경로를 <a href="http://localhost:8000/someRequest">http://localhost:8000/someRequest</a> 로 변경후 전달</li>
<li>proxy_set_header Host $host;: 이 지시문은 프록시 서버로 전달되는 요청 헤더에 Host 헤더를 추가</li>
<li>proxy_set_header X-Real-IP $remote_addr;: 이 지시문은 프록시 서버로 전달되는 요청 헤더에 X-Real-IP 헤더를 추가한다. 이 헤더는 원본 클라이언트의 실제 IP 주소를 포함함</li>
<li>proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;: 이 지시문은 프록시 서버로 전달되는 요청 헤더에 X-Forwarded-For 헤더를 추가한다. 이 헤더는 요청이 어디에서 왔는지를 식별하는 데 사용</li>
<li>proxy_set_header X-Forwarded-Proto $scheme;: 이 지시문은 프록시 서버로 전달되는 요청 헤더에 X-Forwarded-Proto 헤더를 추가한다. 이 헤더는 요청이 HTTP인지 HTTPS인지를 나타냄 (이게 제일 중요!!!)</li>
</ol>
<p>마지막 6번 과정 덕분에 backend는 http로 된 요청을 받지만 nginx가 이를 기억하고 backend가 http로 응답을 생성하여 nginx에게로 전달할때 다시금 http -&gt; https 로 변경한 후 클라이언트에게 전달한다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/5815c097-2d1c-4f1b-a54c-a95008a8642b/image.png" alt=""></p>
<p>이제 mixed Content 에러에서 벗어나자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[nginx 로 https 연결 하기! (feat. cafe24)]]></title>
            <link>https://velog.io/@giant_toothpick/nginx-%EB%A1%9C-https-%EC%97%B0%EA%B2%B0-%ED%95%98%EA%B8%B0-feat.-cafe24</link>
            <guid>https://velog.io/@giant_toothpick/nginx-%EB%A1%9C-https-%EC%97%B0%EA%B2%B0-%ED%95%98%EA%B8%B0-feat.-cafe24</guid>
            <pubDate>Sat, 02 Mar 2024 07:40:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/458dccea-30a9-4a71-882a-8ae66b56f788/image.jpeg" alt=""></p>
<blockquote>
<p>nginx를 사용하면 간편하게 https 연결을 할 수 있다고한다. 실제로 backend에서 세팅하는것보다 쉬웠다!</p>
</blockquote>
<br/>

<h1 id="개발-환경">개발 환경</h1>
<ul>
<li>서버 : 카페24 호스팅 서버 </li>
<li>인증서 : 카페24에서 구매</li>
<li>도메인 : 카페24에서 구매</li>
</ul>
<p>개발 환경 자체가 카페 24 기반이다. 인터페이스 자체가 익숙하지 않고 메뉴가 워낙 많아서 더 헷갈렸다. </p>
<br/>
<br/>

<h1 id="인증서-다운받기">인증서 다운받기</h1>
<blockquote>
<p>인증서를 설치하기 전에 반드시 도메인 연결이 되어있어야함!</p>
</blockquote>
<ul>
<li><p>경로 : 카페24 호스팅 로그인 &gt; 나의서비스관리 &gt; 인증서를 구매한 아이디 선택 &gt; 인증서 관리</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/19d8cf86-ff2c-4f9b-bf11-a7ababbb165b/image.png" alt=""></p>
</li>
</ul>
<p> 인증서를 구매하게 되면 이메일 인증을 하게되는데 그거 해줘야 인증서를 받을 수 있는 버튼을 볼 수 있다!</p>
<p> 이제 전부 다운 받아두자 
 <img src="https://velog.velcdn.com/images/giant_toothpick/post/55648807-508e-4623-9511-b202c3696565/image.png" alt=""></p>
<br/>
<br/>

<h1 id="인증서-가공하기">인증서 가공하기</h1>
<blockquote>
<p>인증서를 nginx에서 편하게 사용하기 전에 약간의 가공이 필요하다. </p>
</blockquote>
<h3 id="인증서-암호화-해제">인증서 암호화 해제</h3>
<pre><code class="language-bash">openssl rsa -in ssl.key -out new_ssl.key</code></pre>
<ul>
<li>이렇게 하지 않으면 nginx 다시 킬때마다 암호를 입력해야하니 번거롭지 않게 암호화를 해제해주자</li>
</ul>
<h3 id="인증서-합치기">인증서 합치기</h3>
<pre><code>cat ssl.crt chain_all_ssl.crt &gt; final.crt</code></pre><p><img src="https://velog.velcdn.com/images/giant_toothpick/post/b28b57e9-8483-4442-80ad-15eacc64b306/image.png" alt="파일 목록 갱신된거 확인!"></p>
<p>새롭게 파일이 추가된걸 볼 수 있다.</p>
<br/>
<br/>

<h1 id="인증서-원격-서버로-이동하기">인증서 원격 서버로 이동하기</h1>
<blockquote>
<p>nginx와 함께 관리해주기 위해 etc/nginx/ssl 이라는 폴더를 따로 만들어 인증서를 보관했다.</p>
</blockquote>
<pre><code class="language-bash">scp new_ssl.key [사용자]@[원격 서버 ip]:/etc/nginx/ssl/  
scp final.crt [사용자]@[원격 서버 ip]:/etc/nginx/ssl/</code></pre>
<br/>
<br/>

<h1 id="nginx-ssl-적용하기">nginx ssl 적용하기</h1>
<blockquote>
<p>두가지 설정을 추가했다. </p>
</blockquote>
<ol>
<li>http 로 오면 https 로 리다이렉트</li>
<li>인증서 연결</li>
</ol>
<p>더 많은 설정이 있지만 지금은 이것만으로도 https 연결이 끝난다!</p>
<pre><code class="language-bash">server{
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;  //http 요청오면 https로 보내기
}

server {
    listen 443 ssl;
    server_name example.com;

    // https 연결!!
    ssl_certificate /etc/nginx/ssl/final.crt;
    ssl_certificate_key /etc/nginx/ssl/new_ssl.key;


    location / {
         // frontend 연결 코드.. 
    }

    location /api {
        // backend 연결 코드.. 
    }
}</code></pre>
<br/>
<br/>



<h1 id="혹시-에러가-나시나요">혹시 에러가 나시나요?</h1>
<blockquote>
<p>코딩을 하다보면 정말 뜬금 없는곳에서 에러가 난다. </p>
</blockquote>
<h3 id="1-nginx한테-폴더-접근권한-주기">1. nginx한테 폴더 접근권한 주기</h3>
<ul>
<li>ssl 파일을 넣어놔도 폴더에 접근 권한이 없는경우 인증서에 접근할 수 없는경우가 생기기도 한다. </li>
</ul>
<pre><code class="language-bash">sudo chown -R nginx:nginx /etc/nginx/ssl
sudo chmod -R 755 /etc/nginx/ssl
</code></pre>
<h3 id="2-인증서-들여쓰기-오류">2. 인증서 들여쓰기 오류</h3>
<ul>
<li>아까 인증서를 병합할 때 인증서 사이에 줄바꿈이 일어나지 않는경우가 생겼다면 오류가 발생할 수 있다.</li>
<li>vi 혹은 vim으로 인증서를 열어보고 줄바꿈이 되어있지 않다면 사진처럼 바꿔주자
<img src="https://velog.velcdn.com/images/giant_toothpick/post/c8818e21-33f4-47da-9cdd-99ceeb28ba5a/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[react-router] 튜토리얼 ]]></title>
            <link>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC</link>
            <guid>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC</guid>
            <pubDate>Mon, 28 Aug 2023 08:48:18 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-검색-스피너-추가">⭐ 검색 스피너 추가</h1>
<p>로딩표시가 없으면 검색이 다소 느린 느낌이 듭니다. 어쩔 때는 앱이 멈춘것 처럼 보이기도 합니다. 데이터 베이스를 더 빠르게 만드는것도 중요하지만 더 나은 UX를 위해 검색에 대한 즉각적인 UI 피드백을 추가해 봅시다. </p>
<p>여기서는 <code>useNavigation</code>을 사용합니다. </p>
<br/>

<h3 id="📎-검색-스피너-추가">📎 검색 스피너 추가</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">// existing code

export default function Root() {
  const { contacts, q } = useLoaderData();
  const navigation = useNavigation();
  const submit = useSubmit();

  const searching =
    navigation.location &amp;&amp;
    new URLSearchParams(navigation.location.search).has(
      &quot;q&quot;
    );

  useEffect(() =&gt; {
    document.getElementById(&quot;q&quot;).value = q;
  }, [q]);

  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        &lt;div&gt;
          &lt;Form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
            &lt;input
              id=&quot;q&quot;
              className={searching ? &quot;loading&quot; : &quot;&quot;}
              // existing code
            /&gt;
            &lt;div
              id=&quot;search-spinner&quot;
              aria-hidden
              hidden={!searching}
            /&gt;
            {/* existing code */}
          &lt;/Form&gt;
          {/* existing code */}
        &lt;/div&gt;
        {/* existing code */}
      &lt;/div&gt;
      {/* existing code */}
    &lt;/&gt;
  );
}</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/f372e502-2efd-49a1-816c-d2d54a1b4f1f/image.png" alt=""></p>
<p>이제 검색을 진행할 때 로딩스피너가 추가되었습니다. </p>
<p>여기서 <code>navigation.location</code> 은 앱이 새 URL로 이동하고 이에 대한 데이터를 로드할 때 표시됩니다. 새 URl로 이동할 때 loader가 데이터를 전부 로딩하기 전까지는 여전히 이전페이지를 보여줍니다. 그사이의 시간동안 로딩스피너를 보여주므로써 사용자경험을 향상시킬 수 있습니다. </p>
<br/>

<br/>

<h1 id="⭐-기록스택-관리">⭐ 기록스택 관리</h1>
<p>submit을 하게 되면 제출 기록이 남습니다. 근데 지금 <code>onChange함수</code>로 키보드의 <code>input</code>이 있을 때마다 <code>submit</code>을 해주기 때문에 모든 단어의 기록이 기록스택에 남게 됩니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/40286550-677d-4b1f-a349-981d9ecdcc29/image.png" alt=""></p>
<p>히스토리 스택의 현재 항목을 미렁넣는 대신 다음페이지로 대체하면 이를 방지할 수 있습니다. </p>
<br/>

<h3 id="📎-submit에서-replace-사용하기">📎 submit에서 replace 사용하기</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">// existing code

export default function Root() {
  // existing code

  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        &lt;div&gt;
          &lt;Form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
            &lt;input
              id=&quot;q&quot;
              // existing code
              onChange={(event) =&gt; {
                const isFirstSearch = q == null;
                submit(event.currentTarget.form, {
                  replace: !isFirstSearch,
                });
              }}
            /&gt;
            {/* existing code */}
          &lt;/Form&gt;
          {/* existing code */}
        &lt;/div&gt;
        {/* existing code */}
      &lt;/div&gt;
      {/* existing code */}
    &lt;/&gt;
  );
}</code></pre>
<p>이렇게 하면 첫번째 검색결과가 아니라면 검색 결과를 고체하도록 설계하였습니다. </p>
<br/>

<br/>

<h1 id="⭐-탐색-없는-변경">⭐ 탐색 없는 변경</h1>
<p>지금 까지는 탐색을 통해 데이터를 찾고 변경시켰지만 탐색을 하지 않고 데이터를 변경하는 방법에 대해 알아보겠습니다. <code>useFetcher</code>를 사용하여 탐색을 하지 않고 로더 및 작업과 통신할 수 있습니다. </p>
<blockquote>
<p>탐색을 하지 않는다는것은 url변경에 의한 이동이 없다는 뜻!</p>
</blockquote>
<br/>

<h3 id="📎-favorite-생성하기">📎 <Favorite> 생성하기</h3>
<p>src/routes/contact.jsx</p>
<pre><code class="language-jsx">import {
  useLoaderData,
  Form,
  useFetcher,
} from &quot;react-router-dom&quot;;

// existing code

function Favorite({ contact }) {
  const fetcher = useFetcher();
  let favorite = contact.favorite;

  return (
    &lt;fetcher.Form method=&quot;post&quot;&gt;
      &lt;button
        name=&quot;favorite&quot;
        value={favorite ? &quot;false&quot; : &quot;true&quot;}
        aria-label={
          favorite
            ? &quot;Remove from favorites&quot;
            : &quot;Add to favorites&quot;
        }
      &gt;
        {favorite ? &quot;★&quot; : &quot;☆&quot;}
      &lt;/button&gt;
    &lt;/fetcher.Form&gt;
  );
}</code></pre>
<p>favorite에 따라 그려지는 버튼의 모양을 다르게 합니다.  post 요청이기 때문에 action을 만들어 줍니다.</p>
<br/>

<h3 id="📎-액션-만들기">📎 액션 만들기</h3>
<p>src/routes/contact.jsx</p>
<pre><code class="language-jsx">// existing code
import { getContact, updateContact } from &quot;../contacts&quot;;

export async function action({ request, params }) {
  let formData = await request.formData();
  return updateContact(params.contactId, {
    favorite: formData.get(&quot;favorite&quot;) === &quot;true&quot;,
  });
}

export default function Contact() {
  // existing code
}</code></pre>
<p>이제 양식을 가져와 요청을 보냅니다. post 요청이기 때문에 request.에서 formData()를 가져옵니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/9a0abfc9-3bb8-4b0e-ab07-e13839d92485/image.png" alt=""></p>
<p>이제 favorite 표시를 사용할 수 있습니다. </p>
<p>html에서 Form을 제출할때 기본적으로 <code>action경로로 페이지 이동</code>이 발생합니다. 이것을 <code>navigation(탐색)</code>을 유발한다고 표현하는데 <code>url이 변경</code>되는 걸 말합니다. action이 없더라도 같은 페이지로 <code>navigation(탐색)</code>이 발생하기 때문에 <code>navigation.state 가 loading이 되는 현상</code>이 발생합니다.  좋아요 같은 버튼기능은 이런 기능이 필요 없습니다.</p>
<p>가장 기본적으로 이러한 현상을 막는것이 <code>e.preventDefault()</code> 이고 <code>react-router-dom</code>을 사용할 때는 위의 예시와 같이 <code>useFetcher</code>를 사용합니다. </p>
<p><code>fetcher.Form</code>을 사용하면 페이지 이동이 발생하지 않습니다. 즉 url이 변경되지 않고 기록 스택이 영향을 받지 않습니다. </p>
<p>그래서 왜 사용하냐고요? 만약 페이지를 전환할 때 loader가 데이터를 불러오는데 시간이 걸린다면 개발자는 화면이 전환되는동안 끊기는 느낌을 주지 않기 위해 로딩스피너를 넣을 것입니다. 그 조건이 바로 navigation입니다. </p>
<pre><code class="language-jsx">const navigation = useNavigation()
navigation.state //loading</code></pre>
<p>다음 훅을 사용하면 페이지 전환상태를 받아올 수 있는데 좋아요 버튼을 누를 때마다 페이지 이동이 발생 즉 navigation(탐색)이 발생하면 좋아요. 버튼을 클릭할 때마다 로딩스피너가 보일것입니다. </p>
<p>이것을 방지하기위함 그리고 페이지 이동을 막기 위해 <code>fetcher.Form</code>를 사용합니다. </p>
<p>더욱 다양한 사용법은 <a href="https://reactrouter.com/en/main/hooks/use-fetcher">https://reactrouter.com/en/main/hooks/use-fetcher</a> 공식문서에서 확인할 수 있습니다. </p>
<br/>

<br/>

<h1 id="⭐-낙관적ui">⭐ 낙관적UI</h1>
<p>본 예제에서는 즐겨찾기(Favorite)버튼을 누를 때 약간의 지연이 발생되도록 설계되었습니다. 이는 실제 서비스 환경에서 네트워크 문제가 있을 수 있기 때문입니다. </p>
<p>우리는 <code>navigation.state</code>를 사용했지만 <code>fetcher.state</code>를 사용해서 더 나은 피드백을 제공하도록 할 수 있습니다. </p>
<p>이때 사용하는 전략이 <code>낙관적UI</code>입니다. </p>
<blockquote>
<p>🪄 낙관적 UI란?
사용자 인터페이스 디자인 및 개발 패턴중 하나로, 사용자에게 빠른 피드백과 더 나은 사용자 경험을 제공하기 위해 사용됩니다. 낙관적 UI의 핵심 개념은 사용자의 동작에 대한 응답을 가능한 한 빨리 보여주는 것입니다.</p>
</blockquote>
<br/>

<h3 id="📎-낙관적-전략-사용">📎 낙관적 전략 사용</h3>
<p>src/routes/contact.jsx</p>
<pre><code class="language-jsx">// existing code

function Favorite({ contact }) {
  const fetcher = useFetcher();

  let favorite = contact.favorite;

  if (fetcher.formData) {
    favorite = fetcher.formData.get(&quot;favorite&quot;) === &quot;true&quot;;
  }

  return (
    &lt;fetcher.Form method=&quot;post&quot;&gt;
      &lt;button
        name=&quot;favorite&quot;
        value={favorite ? &quot;false&quot; : &quot;true&quot;}
        aria-label={
          favorite
            ? &quot;Remove from favorites&quot;
            : &quot;Add to favorites&quot;
        }
      &gt;
        {favorite ? &quot;★&quot; : &quot;☆&quot;}
      &lt;/button&gt;
    &lt;/fetcher.Form&gt;
  );
}</code></pre>
<p>이제 버튼을 클릭하면 즉각적으로 상태가 변경됩니다. 실제 제출하는데이터가 있다면 제출된 데이터를 대신 사용하고 제출하는 데이터가 없다면 실제 데이터를 사용하게 됩니다.</p>
<p>만일 업데이트가 실패했다고 하더라도 <code>contact.favorite</code>을 사용하기 때문에 원래 데이터로 돌아갑니다. </p>
<p><code>&lt;button/&gt;</code>을 누르게 되면 fetcher의 상태는 다음과 같이 변경됩니다. </p>
<blockquote>
<p>submitting → loading → idle</p>
</blockquote>
<p>위의 코드에는 <code>fetcher.formData</code>를 사용하여 요청한 <code>favorite</code> 데이터를 가져와 사용합니다. 이 데이터는 <code>submitting</code>과 <code>loading</code> 상태에서 유지되다가 <code>action</code>함수가 완료되면서 <code>fetcher</code>의 상태가 <code>idle</code> 변경되면서 <code>fetcher.formData</code>는 <code>null</code>값으로 변경됩니다. 이때는 <code>추가한 if문</code>이 동작하지 않기 때문에 <code>실제로 변환된 데이터</code>를 가져와 사용하게 됩니다! </p>
<p>이로써 즉시 데이터를 변경할 수 있고, 실제로 네트워크 에러가 나더라도 idle상태에서는 정상적인 favorite 값을제공할 수 있습니다. </p>
<br/>

<h1 id="⭐-찾을-수-없는-데이터">⭐ 찾을 수 없는 데이터</h1>
<p>로드하려는 연락처가 존재하지 않는다면 어떻게 할까요?</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/fdb69f3f-eb18-49d6-bf66-87093cde2cb8/image.png" alt=""></p>
<h3 id="📎-loader-에서-404에러-던지기">📎 loader 에서 404에러 던지기</h3>
<p>src/routes/contact.jsx</p>
<pre><code class="language-jsx">export async function loader({ params }) {
  const contact = await getContact(params.contactId);
  if(!contact){
    throw new Response(&quot;&quot;, {   //구체적인 커스텀 에러를 생성해서 반환할 수 있다. 
      status:404,
      statusText:`custom error page Not Found`
    })
  }
  return { contact };
}</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/f17f30d0-8147-49b4-b0c9-0e5b1e3193e4/image.png" alt=""></p>
<p><code>Cannot read properties of null</code> 을 피하고 오류 경로를 렌더링 하여 사용자에게 구체적인 내용을 알려줄 수 있습니다. </p>
<p>이경우 뒤로가기나 새로고침 밖에 해결할 방법이 없습니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/6ced86f2-7dd3-4482-815c-108c76f37ac2/image.png" alt=""></p>
<p>위 화면과 같은 형태의 에러페이지를 만들어 봅시다. </p>
<h3 id="📎-경로-없는-경로로-하위-경로를-래핑합니다">📎 경로 없는 경로로 하위 경로를 래핑합니다.</h3>
<p>src/index.jsx</p>
<pre><code class="language-jsx">createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    loader: rootLoader,
    action: rootAction,
    errorElement: &lt;ErrorPage /&gt;,
    children: [
      {
        errorElement: &lt;ErrorPage /&gt;,
        children: [
          { index: true, element: &lt;Index /&gt; },
          {
            path: &quot;contacts/:contactId&quot;,
            element: &lt;Contact /&gt;,
            loader: contactLoader,
            action: contactAction,
          },
          /* the rest of the routes */
        ],
      },
    ],
  },
]);</code></pre>
<p>하위 경로에서 오류가 발생하면 경로가 없고 errorElement만 있는 경로에서 오류를 포작하고 렌더링하여 루트경로 의 UI 즉 sidebar는 그대로 유지됩니다.!</p>
<br/>

<br/>

<h1 id="⭐-jsx경로">⭐ JSX경로</h1>
<p>마지막으로 사람들은 jsx를 사용해 경로를 구성하는것을 선호합니다. <code>createRoutesFromElements</code> 로 경로를 구성할 때 JSX 나 객체 사이에는 기능적인 차이가 없으며 단순히 스타일에 따른 선호일 뿐입니다. </p>
<pre><code class="language-jsx">import {
  createRoutesFromElements,
  createBrowserRouter,
  Route,
} from &quot;react-router-dom&quot;;

const router = createBrowserRouter(
  createRoutesFromElements(
    &lt;Route
      path=&quot;/&quot;
      element={&lt;Root /&gt;}
      loader={rootLoader}
      action={rootAction}
      errorElement={&lt;ErrorPage /&gt;}
    &gt;
      &lt;Route errorElement={&lt;ErrorPage /&gt;}&gt;
        &lt;Route index element={&lt;Index /&gt;} /&gt;
        &lt;Route
          path=&quot;contacts/:contactId&quot;
          element={&lt;Contact /&gt;}
          loader={contactLoader}
          action={contactAction}
        /&gt;
        &lt;Route
          path=&quot;contacts/:contactId/edit&quot;
          element={&lt;EditContact /&gt;}
          loader={contactLoader}
          action={editAction}
        /&gt;
        &lt;Route
          path=&quot;contacts/:contactId/destroy&quot;
          action={destroyAction}
        /&gt;
      &lt;/Route&gt;
    &lt;/Route&gt;
  )
);</code></pre>
<br/>

<br/>

<blockquote>
<p>지금 까지 공식문서에 있는 튜토리얼을 따라 작업을 계속했습니다.
Router를 사용한 적은 많았는데 이렇게 세부적인 기능까지 사용한 적은 없었던것 같습니다. 이해하는데 시간이 걸렸지만 그만큼 유용한 시간였습니다. 물론 React Router로 할 수 있는 일은 이것보다 훨씬 더 많기 때문에 공식 문서에서 API를 확인해보세요!</p>
</blockquote>
<br/>

<blockquote>
<p>참고문서
<a href="https://reactrouter.com/en/main/start/tutorial#the-root-route">https://reactrouter.com/en/main/start/tutorial#the-root-route</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[react-router] 튜토리얼 - 사이드바 -]]></title>
            <link>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EC%82%AC%EC%9D%B4%EB%93%9C%EB%B0%94-</link>
            <guid>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EC%82%AC%EC%9D%B4%EB%93%9C%EB%B0%94-</guid>
            <pubDate>Sun, 27 Aug 2023 07:56:38 GMT</pubDate>
            <description><![CDATA[<br/>

<h1 id="⭐-액티브-링크-스타일링">⭐ 액티브 링크 스타일링</h1>
<br/>

<h3 id="📎-사이드바에서-사용-navlink">📎 사이드바에서 사용 “NavLink”</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import {
  Outlet,
  NavLink,
  useLoaderData,
  Form,
  redirect,
} from &quot;react-router-dom&quot;;

export default function Root() {
  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        {/* other code */}

        &lt;nav&gt;
          {contacts.length ? (
            &lt;ul&gt;
              {contacts.map((contact) =&gt; (
                &lt;li key={contact.id}&gt;
                  &lt;NavLink
                    to={`contacts/${contact.id}`}
                    className={({ isActive, isPending }) =&gt;
                      isActive
                        ? &quot;active&quot;
                        : isPending
                        ? &quot;pending&quot;
                        : &quot;&quot;
                    }
                  &gt;
                    {/* other code */}
                  &lt;/NavLink&gt;
                &lt;/li&gt;
              ))}
            &lt;/ul&gt;
          ) : (
            &lt;p&gt;{/* other code */}&lt;/p&gt;
          )}
        &lt;/nav&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>Nav 링크를 사용하면 어던 링크를 사용중인지 확인할 수 있습니다. </p>
<ul>
<li><code>isActive</code> : url이 해당링크를 가리키는지 확인</li>
<li><code>isPending</code> : 내용이 로딩중인지 확인
<img src="https://velog.velcdn.com/images/giant_toothpick/post/421f8eef-327b-4839-a275-47c488913247/image.png" alt=""></li>
</ul>
<p>이제 사용자가 어떤 링크를 보고있는지 확실히 알 수 있게 됩니다. <NavLink/> 안의 함수를 사용해서 <code>isActive</code>와 <code>isPending</code>을 사용할 수 있습니다. </p>
<br/>

<br/>

<h1 id="⭐-글로벌-보류-ui">⭐ 글로벌 보류 UI</h1>
<p>사용자가 앱을 탐색할 때 React Router는 다음 페이지에 대한 데이터가 로드 되는동안 이전페이지를 그대로 둡니다. 이 때 앱이 약간 응답하지 않는다는 느낌을 받을 수 있습니다. </p>
<p>이경우 <code>useNavigation</code>을 사용할 수 있습니다. </p>
<br/>

<h3 id="📎-usenavigation-사용">📎 useNavigation 사용</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import {
  // existing code
  useNavigation,
} from &quot;react-router-dom&quot;;

// existing code

export default function Root() {
  const { contacts } = useLoaderData();
  const navigation = useNavigation();

  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;{/* existing code */}&lt;/div&gt;
      &lt;div
        id=&quot;detail&quot;
        className={
          navigation.state === &quot;loading&quot; ? &quot;loading&quot; : &quot;&quot;  //상태를 가져와 화면을 변경할 수 있음
        }
      &gt;
        &lt;Outlet /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<p><code>useNavigation</code>으로 로딩상태를 받아와 화면에 표시할 수 있습니다.  </p>
<p>여기서 <code>navigation</code>의 상태는 다음과 같습니다. </p>
<ul>
<li>idle</li>
<li>submitting</li>
<li>loading</li>
</ul>
<p>이러한 상태값으로 화면 상단에 스피너나 로딩바를 표시하는 등, 사용자가 앱을 끊김없이 사용한다는 느낌을 줄 수 있습니다. 
<img src="https://velog.velcdn.com/images/giant_toothpick/post/b45cebf8-58ba-4fca-b48c-29c4b0cb8dee/image.png" alt="">
%89%E1%85%A3%E1%86%BA_2023-08-23_%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE_5.10.26.png)</p>
<p>이처럼 이동할 때 살짝 어두워 지는 효과를 줘서 앱이 끊기는 느낌을 최소화 할 수 있습니다. </p>
<br/>

<br/>

<h1 id="⭐-기록-삭제">⭐ 기록 삭제</h1>
<p>이제 삭제 버튼을 만들어 봅시다. </p>
<p>src/routes/contact.jsx</p>
<pre><code class="language-jsx">&lt;Form
  method=&quot;post&quot;
  action=&quot;destroy&quot;
  onSubmit={(event) =&gt; {
    if (
      !confirm(
        &quot;Please confirm you want to delete this record.&quot;
      )
    ) {
      event.preventDefault();
    }
  }}
&gt;
  &lt;button type=&quot;submit&quot;&gt;Delete&lt;/button&gt;
&lt;/Form&gt;</code></pre>
<p>다음 코드를 살펴보면 action에 destroy라고 되어있는것을 볼 수 있습니다.  이것은 <Link/> 에서 to를 쓰는것과 비슷합니다.</p>
<p>무슨뜻이냐 하면 <code>Form</code> 에 있는 <code>Delete</code>버튼을 눌렀을 때 현재 <code>url</code>에서 <code>/destory</code>로 이동한다는 것입니다. 즉</p>
<p><a href="http://localhost:3000/contacts/ysi7vro"><code>http://localhost:3000/contacts/ysi7vro</code></a> ⇒ [<code>http://localhost:3000/contacts/ysi7vro](http://localhost:3000/contacts/ysi7vro)/destory</code> 로 이동하게 됩니다. </p>
<br/>

<h3 id="📎-파괴액션-추가">📎 파괴액션 추가</h3>
<p>이제 해당 버튼이 동작하기 위해 <code>destroy.jsx</code>를 만들어 봅시다. </p>
<p>src/routes/destroy.jsx</p>
<pre><code class="language-jsx">import { redirect } from &quot;react-router-dom&quot;;
import { deleteContact } from &quot;../contacts&quot;;

export async function action({ params }) {
  await deleteContact(params.contactId); // 삭제모션
  return redirect(&quot;/&quot;); // 메인경로로 이동
}</code></pre>
<br/>

<h3 id="📎-경로-구성에-파괴-경로-추가">📎 경로 구성에 파괴 경로 추가</h3>
<p>src/index.jsx</p>
<pre><code class="language-jsx">/* existing code */
import { action as destroyAction } from &quot;./routes/destroy&quot;;

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    /* existing root route props */
    children: [
      /* existing routes */
      {
        path: &quot;contacts/:contactId/destroy&quot;, // 경로추가
        action: destroyAction, //함수만 사용할거기 때문에 elements가 필요없다. 
      },
    ],
  },
]);

/* existing code */</code></pre>
<p>해당 경로는 컴포넌트가 아닌 <code>action</code>함수만을 위한 컴포넌트입니다. </p>
<p>아직 코드만 봐서는 어떻게 동작하는지 어렵게 느껴집니다. 이제 동작원리를 살펴봅시다. </p>
<ol>
<li><code>&lt;Form&gt;</code> 새 <code>POST</code> 요청을 서버에 보내는 기본 <code>브라우저 동작을 방지</code>합니다. 대신 클라이언트 측 라우팅을 사용하여 <code>POST 요청을 생성</code>하여 브라우저를 에뮬레이트합니다. </li>
<li><code>&lt;Form action=”destroy”/&gt;</code> 의경로와 일치하는 <code>‘contact/:contactId/destroy’</code> 에 요청을 보냅니다. </li>
<li><code>contact/:contactId/destroy</code> 경로에 있는 <code>destroy.jsx</code>에서 요청을 실행하게 되며 이는 <code>action</code> 이 동작함을 의미합니다.  </li>
<li>작업이 리디렉션 된 후 React Router는 페이지가 마치 <code>새로고침 된 것 처럼 동작</code>해야하기 때문에 ( 기본 브라우저는 서버에 요청을 할 때 페이지가 새로고침된것 처럼 동작하기 때문에 React도 이를 따라 새로고침한것처럼 행동합니다. ) React-router는 페이지의 데이터에 대한 <code>모든 로더를 호출</code>합니다. (이 덕분에 edit을 실행할 때 다른 컴포넌트인 sidebar도 자동으로 변경됩니다. ) 이것을 <code>재검증</code> 이라고 합니다. ⇒ <code>useLoaderData()</code> 가 새로운 값을 반환하고 구성요소를 업데이트 합니다. </li>
</ol>
<br/>

<br/>

<h1 id="⭐-문맥상의-오류">⭐ 문맥상의 오류</h1>
<p><code>destroy</code> 액션에서 오류를 던지면 어떻게 될까요?</p>
<p>src/routes/destroy.jsx</p>
<pre><code class="language-jsx">export async function action({ params }) {
  throw new Error(&quot;oh dang!&quot;);
  await deleteContact(params.contactId);
  return redirect(&quot;/&quot;);
}</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/6a4bd13c-8171-4884-9c97-361da0067062/image.png" alt=""></p>
<p>rl본적으로 <code>“/”</code>에서 생성했던 에러페이지를 사용하게 됩니다. 하지만 이 경우에는 사용자가 새로고침하는것 이외에 아무것도 할 수 없습니다. </p>
<p>먼저 삭제 경로에 대한 상황별 오류 메시지를 만들어 봅시다. </p>
<p>src/index.jsx</p>
<pre><code class="language-jsx">[
  /* other routes */
  {
    path: &quot;contacts/:contactId/destroy&quot;,
    action: destroyAction,
    errorElement: &lt;div&gt;Oops! There was an error.&lt;/div&gt;,
  },
];</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/b216e092-d4ed-42f3-a649-af7ce13701f9/image.png" alt=""></p>
<p>라우팅에 의해 전체 화면에서 에러가 나오는것이 아닌 <code>children이 종속된 화면</code>에서만 에러가 나오게 됩니다. 이는 새로고침을 통해 문제를 해결하는것 보다 더 많은옵션이 있으며 문제가 없는 페이지 부분과 계속 상호작용할 수 있게 해줍니다. </p>
<br/>

<br/>

<h1 id="⭐-색인경로">⭐ 색인경로</h1>
<p>앱을 로드하면 목록 오른쪽 에 큰 빈페이지가 표시 됩니다. 
<img src="https://velog.velcdn.com/images/giant_toothpick/post/7c9081b5-1f65-4241-973a-dce55323a661/image.png" alt=""></p>
<p>왜 이런 현상이 발생할 까요?</p>
<p>부모경로와 일치하는 자식이 없기 때문에  <code>‘&lt;Outlet/&gt;’에 렌더링 할만한게 없는것</code>입니다. </p>
<br/>

<h3 id="📎-메인-페이지-보여줄화면-그리기">📎 메인 페이지 보여줄화면 그리기</h3>
<p>우선 렌더링 할만한 메인 페이지를 하나 만들어 봅시다. </p>
<p>src/routes/main.jsx</p>
<pre><code class="language-jsx">export default function Index() {
  return (
    &lt;p id=&quot;zero-state&quot;&gt;
      This is a demo for React Router.
      &lt;br /&gt;
      Check out{&quot; &quot;}
      &lt;a href=&quot;https://reactrouter.com&quot;&gt;
        the docs at reactrouter.com
      &lt;/a&gt;
      .
    &lt;/p&gt;
  );
}</code></pre>
<p>이 화면을 <code>“/”</code>와 연결해 봅시다. </p>
<br/>

<h3 id="📎-인덱스-경로-구성">📎 인덱스 경로 구성</h3>
<p>src/index.jsx</p>
<pre><code class="language-jsx">// existing code
import Index from &quot;./routes/index&quot;;

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement: &lt;ErrorPage /&gt;,
    loader: rootLoader,
    action: rootAction,
    children: [
      { index: true, element: &lt;Index /&gt; },  //부모화면이랑 같은경로사용
      /* existing routes */
    ],
  },
]);</code></pre>
<p>이제 부모 컴포넌트경로일 때 <code>{ index:true }</code> 의 컴포넌트가 <code>&lt;Outlet/&gt;</code> 에 렌더링 됩니다. <code>{ path:”” }</code>를 해도 되지만 공식문서에서는 <code>{ index:true}</code> 를 좀 더 권장하고 있습니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/db0a75f4-ea33-4bfd-a8ae-01da23557a98/image.png" alt=""></p>
<p>이제 부모 컴포넌트를 랜더링할 때 디폴트로 렌더링할 자식 컴포넌트가 생겼습니다. </p>
<br/>

<br/>

<h1 id="⭐-취소버튼">⭐ 취소버튼</h1>
<p>편집 페이지에는 아직 취소버튼이 기능이 동작하지 않습니다. 우리는 이 버튼이 뒤로가기와 동일한 동작을 하도록 만들려고 합니다. </p>
<p>이때는 <code>useNavigate</code> 와 <code>React-router</code>의 클릭 핸들러가 필요합니다. </p>
<br/>

<h3 id="📎-취소버튼-클릭-핸들러-추가하기">📎 취소버튼 클릭 핸들러 추가하기</h3>
<p>src/routes/edit.jsx</p>
<pre><code class="language-jsx">import {
  Form,
  useLoaderData,
  redirect,
  useNavigate,
} from &quot;react-router-dom&quot;;

export default function EditContact() {
  const { contact } = useLoaderData();
  const navigate = useNavigate(); //리엑트-라우터 훅 추가하기

  return (
    &lt;Form method=&quot;post&quot; id=&quot;contact-form&quot;&gt;
      {/* existing code */}

      &lt;p&gt;
        &lt;button type=&quot;submit&quot;&gt;Save&lt;/button&gt;
{/* 뒤로가는 동작 실행 */}
        &lt;button
          type=&quot;button&quot;
          onClick={() =&gt; {
            navigate(-1); 
          }}
        &gt;
          Cancel
        &lt;/button&gt;
      &lt;/p&gt;
    &lt;/Form&gt;
  );
}</code></pre>
<p>이제 <code>Cancel 버튼</code>을 누르면 뒤로 가는 동작이 활성화 됩니다. 근데 여기서 <code>submit</code> 버튼처럼 <code>&lt;Form/&gt;</code> 안에 있기 때문에 버튼이 양식을 제출하는것 처럼보이지만 <code>&lt;button type=”button/&gt;</code> 은 해당기능을 미리 방지하고 있습니다. 때문에 <code>e.preventDefault()</code> 을 사용하지 않아도 됩니다. </p>
<br/>

<br/>

<h1 id="⭐-url-검색-매개변수-및-get-제출">⭐ URL 검색 매개변수 및 GET 제출</h1>
<p>일반적인 HTML 문법인 <form/> 은 브라우저에서 어떻게 동작할까요?</p>
<pre><code class="language-jsx">http://127.0.0.1:5173/?q=ryan</code></pre>
<p>검색을 하게 되면 다음과 같은 URL에 쿼리가 포함된 형태가 됩니다. </p>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">&lt;form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
  &lt;input
    id=&quot;q&quot;
    aria-label=&quot;Search contacts&quot;
    placeholder=&quot;Search&quot;
    type=&quot;search&quot;
    name=&quot;q&quot;
  /&gt;
  &lt;div id=&quot;search-spinner&quot; aria-hidden hidden={true} /&gt;
  &lt;div className=&quot;sr-only&quot; aria-live=&quot;polite&quot;&gt;&lt;/div&gt;
&lt;/form&gt;</code></pre>
<p>즉 <code>&lt;form method=”post”/&gt;</code> 와는 다르게 <code>URL에 GET 요청</code>을 넣습니다. </p>
<p>기본 html에서는 다음과 같이 동작하는 react-router에서는 어떻게 동작할 까요?</p>
<br/>

<br/>

<h1 id="⭐-클라이언트-측-라우팅으로-get제출">⭐ 클라이언트 측 라우팅으로 GET제출</h1>
<br/>

<h3 id="📎-form-을-form으로-변경">📎 form 을 Form으로 변경</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">&lt;Form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
  &lt;input
    id=&quot;q&quot;
    aria-label=&quot;Search contacts&quot;
    placeholder=&quot;Search&quot;
    type=&quot;search&quot;
    name=&quot;q&quot;
  /&gt;
  &lt;div id=&quot;search-spinner&quot; aria-hidden hidden={true} /&gt;
  &lt;div className=&quot;sr-only&quot; aria-live=&quot;polite&quot;&gt;&lt;/div&gt;
&lt;/Form&gt;</code></pre>
<br/>

<h3 id="📎-urlsearchparams가-있는-경우-목록-필터링">📎 URLSearchParams가 있는 경우 목록 필터링</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">export async function loader({request}) {
  //post는 formData로 가져오는 반면 get 요청은 url에서 가져오게 된다. 
  const url = new URL(request.url); //url이 문자열로 되어있는데 그걸 객체로 변환해서 사용
  const q = url.searchParams.get(&quot;q&quot;); // get함수를 사용해서 값을 가져옴 , 없을 경우 null이 뜬다. 
  const contacts = await getContacts(q);
  return { contacts };
}</code></pre>
<p>이제 다음과 같이 필터링이 가능합니다. !</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/c2f72600-aeea-4fcf-af55-0367a1abcd57/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/9a1b3dfe-d037-4955-b027-72b502694a4a/image.png" alt=""></p>
<p>이것도 <code>form</code>을 제출하는 과정이지만 <code>post</code>와는 다르게 <code>url만 변경</code>하는것이기 때문에 요청을 처리하는 로직은 <code>action</code>이 아닌 <code>loader</code>에 존재합니다. </p>
<br/>

<br/>

<h1 id="⭐-양식-상태에-url-동기화">⭐ 양식 상태에 URL 동기화</h1>
<p>아직 한가지 문제가 더 존재합니다. 필터링 후에 ‘홍 길동’을 누르게 되면 url에 변화가 생깁니다. 우리는 url에서의 q의 상태를 유지시킬 필요가 있습니다. </p>
<p>이게 무슨말인지 코드수정을 통해 확인해 보겠습니다. </p>
<br/>

<h3 id="📎-q검색-필드-기본값-설정">📎 q검색 필드 기본값 설정</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">// existing code

export async function loader({ request }) {
  const url = new URL(request.url);
  const q = url.searchParams.get(&quot;q&quot;);
  const contacts = await getContacts(q);
  return { contacts, q };
}

export default function Root() {
  const { contacts, q } = useLoaderData();
  const navigation = useNavigation();

  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        &lt;div&gt;
          &lt;Form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
            &lt;input
              id=&quot;q&quot;
              aria-label=&quot;Search contacts&quot;
              placeholder=&quot;Search&quot;
              type=&quot;search&quot;
              name=&quot;q&quot;
              defaultValue={q}
            /&gt;
            {/* existing code */}
          &lt;/Form&gt;
          {/* existing code */}
        &lt;/div&gt;
        {/* existing code */}
      &lt;/div&gt;
&lt;nav&gt;
          {&quot; &quot;}
          {/* 가져온 데이터 화면에 그리기! */}
          {contacts.length ? (
            &lt;ul&gt;
              {contacts.map((contact) =&gt; (
                &lt;li key={contact.id}&gt;
                  &lt;NavLink
                    to={`contacts/${contact.id}/?q=${q}`} //url이 변경되어도 사이드바가 유지될 수 있다.
                    className={(
                      { isActive, isPending } // 현재 링크가 활성화 되었는지 pending중인지 알 수 있다.
                    ) =&gt; (isActive ? &quot;active&quot; : isPending ? &quot;pending&quot; : &quot;&quot;)}
                  &gt;
                    {contact.first || contact.last ? (
                      &lt;&gt;
                        {contact.first} {contact.last}
                      &lt;/&gt;
                    ) : (
                      &lt;i&gt;No Name&lt;/i&gt;
                    )}{&quot; &quot;}
                    {contact.favorite &amp;&amp; &lt;span&gt;★&lt;/span&gt;}
                  &lt;/NavLink&gt;
                &lt;/li&gt;
              ))}
            &lt;/ul&gt;
          ) : (
            &lt;p&gt;
              &lt;i&gt;No contacts&lt;/i&gt;
            &lt;/p&gt;
          )}
        &lt;/nav&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>이제 검색후 클릭을 통해 URL이 변경되어도 검색창에 값이 남아있게 됩니다. </p>
<p>또 NavLink에 query 값을 넣어주면 sidebar에서 클릭을 해도 검색된 목록이 그대로 유지 됩니다. 
<img src="https://velog.velcdn.com/images/giant_toothpick/post/2ce88924-9fd4-407a-9090-509be5de8b75/image.png" alt=""></p>
<br/>

<h3 id="📎-입력값을-url-검색-매개변수와-동기화">📎 입력값을 URL 검색 매개변수와 동기화</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import { useEffect } from &quot;react&quot;;

// existing code

export default function Root() {
  const { contacts, q } = useLoaderData();
  const navigation = useNavigation();

  useEffect(() =&gt; {
    document.getElementById(&quot;q&quot;).value = q;
  }, [q]);

  // existing code
}</code></pre>
<p><code>defaultValue</code> 뿐만 아니라 <code>value</code>와도 값을 동기화 해줍니다. </p>
<br/>

<br/>

<h1 id="⭐-양식제출-onchange">⭐ 양식제출 ‘onChange’</h1>
<p>엔터로 양식을 명시적으로 제출하는것 보다 모든 키 입력에서 필터링 되도록 수행하는것이 좋습니다. </p>
<p>이럴 때는 <code>useSubmit</code>을 사용할 수 있습니다. </p>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">// existing code
import {
  // existing code
  useSubmit,
} from &quot;react-router-dom&quot;;

export default function Root() {
  const { contacts, q } = useLoaderData();
  const navigation = useNavigation();
  const submit = useSubmit();

  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        &lt;div&gt;
          &lt;Form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
            &lt;input
              id=&quot;q&quot;
              aria-label=&quot;Search contacts&quot;
              placeholder=&quot;Search&quot;
              type=&quot;search&quot;
              name=&quot;q&quot;
              defaultValue={q}
              onChange={(event) =&gt; {
                submit(event.currentTarget.form); // 해당 Form을 제출한다는 의미
              }}
            /&gt;
            {/* existing code */}
          &lt;/Form&gt;
          {/* existing code */}
        &lt;/div&gt;
        {/* existing code */}
      &lt;/div&gt;
      {/* existing code */}
    &lt;/&gt;
  );
}</code></pre>
<p><code>currentTarget</code>은 이벤트가 연결된 DOM노드 즉 input이고, <code>currentTarget.form</code>은 입력의 상위 양식 노드입니다. <code>submit</code>은 해당 <code>form</code>을 자동으로 직렬화해서 제출하게 됩니다.  <code>onChange</code>함수로 인해 input값이 변경될때마다 지정한 <code>Form</code>이 제출되기 때문에 더이상 <code>enter</code>를 눌러서 양식을 제출할 필요가 없습니다.!</p>
<br/>

<br/>

<h1 id="⭐-검색-스피너-추가">⭐ 검색 스피너 추가</h1>
<p>로딩표시가 없으면 검색이 다소 느린 느낌이 듭니다. 어쩔 때는 앱이 멈춘것 처럼 보이기도 합니다. 데이터 베이스를 더 빠르게 만드는것도 중요하지만 더 나은 UX를 위해 검색에 대한 즉각적인 UI 피드백을 추가해 봅시다. </p>
<p>여기서는 <code>useNavigation</code>을 사용합니다. </p>
<br/>

<h3 id="📎-검색-스피너-추가">📎 검색 스피너 추가</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">// existing code

export default function Root() {
  const { contacts, q } = useLoaderData();
  const navigation = useNavigation();
  const submit = useSubmit();

  const searching =
    navigation.location &amp;&amp;
    new URLSearchParams(navigation.location.search).has(
      &quot;q&quot;
    );

  useEffect(() =&gt; {
    document.getElementById(&quot;q&quot;).value = q;
  }, [q]);

  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        &lt;div&gt;
          &lt;Form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
            &lt;input
              id=&quot;q&quot;
              className={searching ? &quot;loading&quot; : &quot;&quot;}
              // existing code
            /&gt;
            &lt;div
              id=&quot;search-spinner&quot;
              aria-hidden
              hidden={!searching}
            /&gt;
            {/* existing code */}
          &lt;/Form&gt;
          {/* existing code */}
        &lt;/div&gt;
        {/* existing code */}
      &lt;/div&gt;
      {/* existing code */}
    &lt;/&gt;
  );
}</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/67d16761-0b76-4b6d-8f51-2d0bee7e4000/image.png" alt=""></p>
<p>이제 검색을 진행할 때 로딩스피너가 추가되었습니다. </p>
<p>여기서 <code>navigation.location</code> 은 앱이 새 URL로 이동하고 이에 대한 데이터를 로드할 때 표시됩니다. 새 URl로 이동할 때 loader가 데이터를 전부 로딩하기 전까지는 여전히 이전페이지를 보여줍니다. 그사이의 시간동안 로딩스피너를 보여주므로써 사용자경험을 향상시킬 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[react-router] 튜토리얼 - 데이터로드 -]]></title>
            <link>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A1%9C%EB%93%9C-</link>
            <guid>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A1%9C%EB%93%9C-</guid>
            <pubDate>Sat, 26 Aug 2023 09:35:10 GMT</pubDate>
            <description><![CDATA[<br/>

<h1 id="⭐-데이터-로딩-중">⭐ 데이터 로딩 중</h1>
<p>react는 <code>csr방식</code>을 사용합니다. 때문에 페이지에서 보여지는 데이터를 서버에서 주로 json형태로 불러온 다음 화면을 그립니다. 우리가 특정 페이지로 라우팅 될 때, 서버와 통신할 함수를 미리 만들어 놓고, 자동으로 데이터를 가져온다면 얼마나 편할까요?</p>
<p>react-router는 다음과 같은 모듈을 사용해 해당 기능을 구현했습니다. </p>
<ul>
<li><code>loader</code></li>
<li><code>useLoaderData</code></li>
</ul>
<p>두 모듈은 로더함수만들고 이를 경로에 연결합니다. 이게 무슨말이냐,  특정 URL  <code>“localhost:3000/a/b/c“</code> 와 같은 경로에 만들어둔 <code>loader 함수를 연결</code>하면 해당 URL에 방문할 때 자동으로 함수를 실행시키고 데이터를 불러옵니다. </p>
<p>개발자는 불러온데이터로 화면만 그리면 됩니다. </p>
<p>다음 코드를 살펴봅시다. </p>
<p><code>&lt;Root/&gt;컴포넌트</code>가 연결되어있는 <code>“/”</code> url에 <code>lodaer함수</code>를 연결하도록 하겠습니다. </p>
<p>먼저 <code>loader함수</code>를 만들어 봅시다. </p>
<h3 id="📎-로더로-내보내기">📎 로더로 내보내기</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import { Outlet, Link } from &quot;react-router-dom&quot;;
import { getContacts } from &quot;../contacts&quot;;

export async function loader() {   //데이터를 불러오는 함수 
  const contacts = await getContacts(); //비동기 통신
  return { contacts }; // 객체로 반환하는것은 불러오는데이터를 명확하게 하기 위함 =&gt; 나중에 객체 분해하려고
}</code></pre>
<br/>

<h3 id="📎-경로에-로더-구성">📎 경로에 로더 구성</h3>
<p>src/index.js</p>
<pre><code class="language-jsx">import Root, { loader as rootLoader } from &quot;./routes/root&quot;; //생성한 로더함수 가져오기

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement: &lt;ErrorPage /&gt;,
    loader: rootLoader,  //해당 경로로 진입할 때 로더함수 실행됨
    children: [
      {
        path: &quot;contacts/:contactId&quot;,
        element: &lt;Contact /&gt;,
      },
    ],
  },
]);</code></pre>
<br/>

<h3 id="📎-데이터-액세스-및-렌더링">📎 데이터 액세스 및 렌더링</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import {
  Outlet,
  Link,
  useLoaderData,
} from &quot;react-router-dom&quot;;
import { getContacts } from &quot;../contacts&quot;;

/* other code */

export default function Root() {
  const { contacts } = useLoaderData(); // 해당 훅을 사용해 loader함수를 통해 서버로 부터 가져온 데이터 사용
  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        {/* other code */}

        &lt;nav&gt; {/* 가져온 데이터 화면에 그리기! */}
          {contacts.length ? (
            &lt;ul&gt;
              {contacts.map((contact) =&gt; (
                &lt;li key={contact.id}&gt;
                  &lt;Link to={`contacts/${contact.id}`}&gt;
                    {contact.first || contact.last ? (
                      &lt;&gt;
                        {contact.first} {contact.last}
                      &lt;/&gt;
                    ) : (
                      &lt;i&gt;No Name&lt;/i&gt;
                    )}{&quot; &quot;}
                    {contact.favorite &amp;&amp; &lt;span&gt;★&lt;/span&gt;}
                  &lt;/Link&gt;
                &lt;/li&gt;
              ))}
            &lt;/ul&gt;
          ) : (
            &lt;p&gt;
              &lt;i&gt;No contacts&lt;/i&gt;
            &lt;/p&gt;
          )}
        &lt;/nav&gt;

        {/* other code */}
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>전체적인 흐름을 봅시다. </p>
<blockquote>
<p>특정 url 라우팅  ⇒ loader 함수 실행 ⇒ useLoaderDate() 로 데이터 가져오기 ⇒ 화면 그리기</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/c8b39564-1b09-45d5-8c87-1595e40782cb/image.png" alt=""></p>
<p>이게 React router는 해당 데이터를 UI와 자동으로 동기화 합니다. 아직은 데이터가 없기 때문에 다음과 같은 빈목록이 표시됩니다. </p>
<p>목록은 비어있지만 해당 페이지로 라우팅 될 때 loader에 있는 함수가 자동으로 실행됩니다. </p>
<br/>

<br/>

<h1 id="⭐-데이터-쓰기--html-양식">⭐ 데이터 쓰기 + HTML 양식</h1>
<h3 id="📎-기존-데이터-전송방식">📎 기존 데이터 전송방식</h3>
<p>HTML에서 Form을 사용하면 입력한 데이터를 서버로 보내는 동작을 수행합니다. 이를 <code>폼전송(Form Submisstion)</code> 이라고 합니다. 즉 사용자가 품을 <code>제출(submit)</code>하면 입력한 데이터화 함께 서버로 <code>요청(request)</code>이 보내지는 것입니다. </p>
<p>폼을 제출하는 순간 브라우저는 내부적으로 서버로 요청을 보내기 위해 <code>브라우저 창의 URL을 변경</code>합니다. 이러한 변경은 브라우저의 주소 표시줄의 <code>URL을 새로운 주소로 업데이트 하는 것</code>을 의미합니다. </p>
<p>즉 사용자가 form을 제출하면 브라우저가 새로운 페이지로 이동하는것 처럼 보이게 되며 이때 URL이 변경됩니다. 이런동작을 <code>네비게이션을 유발</code>한다고 표현합니다. </p>
<p>새로고침이 된것처럼 보이지만 실제로 페이지 전체를 새로 로드하는것은 아니고 서버로 데이터를 전송하는것입니다. </p>
<h3 id="📎-라우트-액션-사용하기">📎 라우트 액션 사용하기</h3>
<p>라우트 액션은 라우팅 과정에서 특정 동작을 의미합니다. Form에 의해 URL이 변경되면 해당 URL에 대한 라우트 액션(action)이 발생합니다. </p>
<p>라우트 액션을 사용하기 위해 <code>&lt;Form/&gt;</code>을 만들어 봅시다.</p>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import {
  Outlet,
  Link,
  useLoaderData,
  Form,
} from &quot;react-router-dom&quot;;
import { getContacts, createContact } from &quot;../contacts&quot;;

export async function action() {
  const contact = await createContact();
  return { contact };
}

/* other code */

export default function Root() {
  const { contacts } = useLoaderData();
  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        &lt;div&gt;
          {/* other code */}
          &lt;Form method=&quot;post&quot;&gt;
            &lt;button type=&quot;submit&quot;&gt;New&lt;/button&gt;
          &lt;/Form&gt;
        &lt;/div&gt;

        {/* other code */}
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>src/index.jsx</p>
<pre><code class="language-jsx">/* other imports */

import Root, {
  loader as rootLoader,
  action as rootAction,
} from &quot;./routes/root&quot;;

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement: &lt;ErrorPage /&gt;,
    loader: rootLoader,
    action: rootAction,
    children: [
      {
        path: &quot;contacts/:contactId&quot;,
        element: &lt;Contact /&gt;,
      },
    ],
  },
]);</code></pre>
<p>흐름</p>
<blockquote>
<Form> 컴포넌트를 submit(제출) ⇒ action에 연결되어있는 함수 동작 ⇒ 컴포넌트를 리랜더링하면서 loader로 데이터 다시 불러옴

</blockquote>
<p><code>&lt;Form/&gt;</code> 을 사용할 때와 <code>&lt;form/&gt;</code>을 사용할때 차이점이 있습니다. 위에서 설명했듯이 <Form/>은 페이지를 새로고침하지 않는 클라이언트 라우팅을 이용한 동작입니다.</p>
<p>지금은 제대로된 경로로 요청을 보내는것이 아니기 때문에 에러가 발생하는데 어떤 차이가 있는지 확인해 봅시다.</p>
<p><code>&lt;Form/&gt;</code>을 이용할 때</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/b72725f6-1a36-4e99-8686-fdd4348298ff/image.png" alt=""></p>
<p>기본 <code>&lt;form/&gt;</code> 을 이용할 때</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/5c4fe0a3-b64a-4304-a7aa-916b547d7c6f/image.png" alt=""></p>
<p>이처럼 라우팅을 이용한다는 특징 덕분에 잘못된 요청을 발생시켰을 때, 우리는 <code>router</code>에서 세팅했던 <code>ErrorPage</code>를 그대로 사용할 수 있습니다.</p>
<p><code>createContact()</code>를 <code>action</code>에 연결시켜 주면 다음과 같이 NoName 리스트가 생성됩니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/7a9a222b-01db-4467-aa64-404ac676222e/image.png" alt=""></p>
<br/>

<h1 id="⭐-로더의-url-매개변수">⭐ 로더의 URL 매개변수</h1>
<p>아까 만들었던 <code>path</code>를 살펴봅시다. </p>
<pre><code class="language-jsx">[
  {
    path: &quot;contacts/:contactId&quot;,
    element: &lt;Contact /&gt;,
  },
];</code></pre>
<p><code>:contactId</code> 는 동적 세그먼트로 변환하는 특별한 의미를 가집니다. 이것을 <code>URL Params</code> 또는 <code>params</code> 라고 말합니다. </p>
<p>페이지를 동적으로 로딩할 때 많이 사용하는데, 이 값을 페이지 초기값을 로딩할 때 사용하는 <code>loader</code>에서 사용할 수는 없을까요?</p>
<h3 id="📎-연락처-페이지에-로더를-추가하고-데이터-엑세스">📎 연락처 페이지에 로더를 추가하고 데이터 엑세스</h3>
<p>src/routes/contact.jsx</p>
<pre><code class="language-jsx">import { Form, useLoaderData } from &quot;react-router-dom&quot;;
import { getContact } from &quot;../contacts&quot;;

export async function loader({ params }) { // 구조분해 해서 가져오기 
  const contact = await getContact(params.contactId);
  return { contact };
}

export default function Contact() {
  const { contact } = useLoaderData();
  // existing code
}</code></pre>
<br/>

<h3 id="📎-경로에-로더-구성-1">📎 경로에 로더 구성</h3>
<p>src/index.jsx</p>
<pre><code class="language-jsx">/* existing code */
import Contact, {
  loader as contactLoader,
} from &quot;./routes/contact&quot;;

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement: &lt;ErrorPage /&gt;,
    loader: rootLoader,
    action: rootAction,
    children: [
      {
        path: &quot;contacts/:contactId&quot;,
        element: &lt;Contact /&gt;,
        loader: contactLoader,
      },
    ],
  },
]);

/* existing code */</code></pre>
<p>loader에서 구조분해 한 뒤 파라미터값을 가져올 수 있습니다!</p>
<br/>

<br/>

<h1 id="⭐-데이터-업데이트">⭐ 데이터 업데이트</h1>
<p>이제 <Form/>을 활용할 때가 왔습니다. </p>
<h3 id="📎-편집-페이지-ui추가">📎 편집 페이지 UI추가</h3>
<p>다음과 같은 컴포넌트를 생성해 줍니다. </p>
<p>src/routes/edit.jsx</p>
<pre><code class="language-jsx">import { Form, useLoaderData } from &quot;react-router-dom&quot;;

export default function EditContact() {
  const { contact } = useLoaderData();

  return (
    &lt;Form method=&quot;post&quot; id=&quot;contact-form&quot;&gt;
      &lt;p&gt;
        &lt;span&gt;Name&lt;/span&gt;
        &lt;input
          placeholder=&quot;First&quot;
          aria-label=&quot;First name&quot;
          type=&quot;text&quot;
          name=&quot;first&quot;
          defaultValue={contact.first}
        /&gt;
        &lt;input
          placeholder=&quot;Last&quot;
          aria-label=&quot;Last name&quot;
          type=&quot;text&quot;
          name=&quot;last&quot;
          defaultValue={contact.last}
        /&gt;
      &lt;/p&gt;
      &lt;label&gt;
        &lt;span&gt;Twitter&lt;/span&gt;
        &lt;input
          type=&quot;text&quot;
          name=&quot;twitter&quot;
          placeholder=&quot;@jack&quot;
          defaultValue={contact.twitter}
        /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        &lt;span&gt;Avatar URL&lt;/span&gt;
        &lt;input
          placeholder=&quot;https://example.com/avatar.jpg&quot;
          aria-label=&quot;Avatar URL&quot;
          type=&quot;text&quot;
          name=&quot;avatar&quot;
          defaultValue={contact.avatar}
        /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        &lt;span&gt;Notes&lt;/span&gt;
        &lt;textarea
          name=&quot;notes&quot;
          defaultValue={contact.notes}
          rows={6}
        /&gt;
      &lt;/label&gt;
      &lt;p&gt;
        &lt;button type=&quot;submit&quot;&gt;Save&lt;/button&gt;
        &lt;button type=&quot;button&quot;&gt;Cancel&lt;/button&gt;
      &lt;/p&gt;
    &lt;/Form&gt;
  );
}</code></pre>
<br/>

<h3 id="📎-새로운-편집-경로-추가">📎 새로운 편집 경로 추가</h3>
<p>src/index.jsx</p>
<pre><code class="language-jsx">/* existing code */
import EditContact from &quot;./routes/edit&quot;;

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement: &lt;ErrorPage /&gt;,
    loader: rootLoader,
    action: rootAction,
    children: [
      {
        path: &quot;contacts/:contactId&quot;,
        element: &lt;Contact /&gt;,
        loader: contactLoader,
      },
      {
        path: &quot;contacts/:contactId/edit&quot;,
        element: &lt;EditContact /&gt;,
        loader: contactLoader,
      },
    ],
  },
]);

/* existing code */</code></pre>
<p>코드를 보면 <code>contactLoader</code>를 재사용했음을 알 수 있습니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/ee212380-d4a1-4dde-8e42-a9cb18b76758/image.png" alt=""></p>
<p>이제 edit을 누르면 다음과 같은 화면이 나오게 됩니다. </p>
<br/>

<br/>

<h1 id="⭐-formdata로-연락처-업데이트">⭐ FormData로 연락처 업데이트</h1>
<p>방금 만든 경로로 만든 양식을 랜더링 합니다. 해당 <code>&lt;Form/&gt;</code>을 <code>action</code>에 연결해서 데이터를 업데이트 해봅시다. </p>
<h3 id="📎-편집-모듈에-작업-추가">📎 편집 모듈에 작업 추가</h3>
<p>src/routes/edit.jsx</p>
<pre><code class="language-jsx">import {
  Form,
  useLoaderData,
  redirect,
} from &quot;react-router-dom&quot;;
import { updateContact } from &quot;../contacts&quot;;

export async function action({ request, params }) {
  const formData = await request.formData(); //Form에서 데이터 추출
  const updates = Object.fromEntries(formData); //데이터 객체로 변환
  await updateContact(params.contactId, updates);
  return redirect(`/contacts/${params.contactId}`);
}

/* existing code */</code></pre>
<h3 id="📎-동작을-경로에-연결">📎 <strong>동작을 경로에 연결</strong></h3>
<p>src/index.jsx</p>
<pre><code class="language-jsx">/* existing code */
import EditContact, {
  action as editAction,
} from &quot;./routes/edit&quot;;

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement: &lt;ErrorPage /&gt;,
    loader: rootLoader,
    action: rootAction,
    children: [
      {
        path: &quot;contacts/:contactId&quot;,
        element: &lt;Contact /&gt;,
        loader: contactLoader,
      },
      {
        path: &quot;contacts/:contactId/edit&quot;,
        element: &lt;EditContact /&gt;,
        loader: contactLoader,
        action: editAction,  //Form에 연결할 action 추가하기
      },
    ],
  },
]);

/* existing code */</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/9c6d2cae-c66d-4115-a0cf-d9f913e4ed33/image.png" alt=""></p>
<p>edit한 내용이 잘 반영되고 있습니다!</p>
<blockquote>
<p>이게 어떻게 가능한 걸까요?</p>
</blockquote>
<pre><code class="language-jsx">&lt;input
  placeholder=&quot;First&quot;
  aria-label=&quot;First name&quot;
  type=&quot;text&quot;
  name=&quot;first&quot;
  defaultValue={contact.first}
/&gt;</code></pre>
<p>해당 코드에서 value의 이름은 first가 됩니다. </p>
<p>이러한 input이 담긴 form은 FormData 형식으로 작성 됩니다. </p>
<pre><code class="language-jsx">export async function action({ request, params }) {
  const formData = await request.formData();
  const firstName = formData.get(&quot;first&quot;);
  const lastName = formData.get(&quot;last&quot;);
  // ...
}</code></pre>
<p>그래서 request(요청)을 열어보면 .get()으로 보낼 데이터의 이름을 매개변수로 입력해 꺼내볼 수가 있습니다. </p>
<p>또 추가적으로 <code>Object.formEntries()</code>를 사용해서 <code>formdata</code>를 객체로 표현할 수 있습니다. </p>
<pre><code class="language-jsx">const formData = await request.formData(); // Form데이터 받아오기
const updates = Object.fromEntries(formData); // 데이터를 객체로 변환하기
await updateContact(params.contactId, updates); // 요청 보내기</code></pre>
<p>여기서 말하는 <code>request</code>, <code>request.formData</code>, <code>Object.formEntries</code>는 웹플랫폼에서 제공하는 API 입니다. react-router와는 상관없습니다. </p>
<p>데이터 처리를 마친후 </p>
<pre><code class="language-jsx">return redirect(`/contacts/${params.contactId}`);</code></pre>
<p>react-router가 제공하는 redirect() 으로 클라이언트 사이드로 페이지를 이동합니다. 근데 이상한 점이 하나있습니다. 이동한 페이지는 <code>contacts/:contactId</code> 인데 사이드바까지 모두 리랜더링 된다는 점입니다.  </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/b2ef2147-0ce7-475a-a4ca-a3e45a5fbaa2/image.png" alt=""></p>
<p>페이지를 <code>redirect()</code>하면서 사이드바까지 업데이트 되었습니다. </p>
<p>원래 클라이언트 측 라우팅이 없으면 post요청시 서버가 리디렉션 되면서 <code>새로운 페이지를 다시가져오기</code> 때문에 페이지 전체가 <code>새로고침</code> 되었습니다. react-router는 이 과정을 예뮬레이트하고 작업후에 페이지의 데이터를 자동으로 검증해서 <code>마치 페이지를 새로 불러온것처럼 랜더링해버립니다</code>. </p>
<br/>

<br/>

<h1 id="⭐-새-레코드를-편집-페이지로-리디렉션">⭐ 새 레코드를 편집 페이지로 리디렉션</h1>
<p>새 연락처를 만드는 작업을 업데이트하고  편집 페이지로 리디렉션 해보겠습니다.</p>
<br/>

<h3 id="📎-편집-페이지로-리디렉션">📎 편집 페이지로 리디렉션</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import {
  Outlet,
  Link,
  useLoaderData,
  Form,
  redirect,
} from &quot;react-router-dom&quot;;
import { getContacts, createContact } from &quot;../contacts&quot;;

export async function action() {
  const contact = await createContact();
  return redirect(`/contacts/${contact.id}/edit`);
}</code></pre>
<p>이제 <code>new</code> 버튼을 누르면 페이지가 <code>redirect</code>가 되서 새로만든 후 편집 페이지로 이동합니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/06e16001-f29d-426f-9b6c-77c9104415bb/image.png" alt=""></p>
<p>  <br/>  <br/>  <br/>  <br/></p>
<blockquote>
<p>참고문서 : <a href="https://reactrouter.com/en/main/start/tutorial">https://reactrouter.com/en/main/start/tutorial</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[react-router] 튜토리얼 - 경로 연결 - ]]></title>
            <link>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EA%B2%BD%EB%A1%9C-%EC%97%B0%EA%B2%B0-</link>
            <guid>https://velog.io/@giant_toothpick/react-router-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-%EA%B2%BD%EB%A1%9C-%EC%97%B0%EA%B2%B0-</guid>
            <pubDate>Thu, 24 Aug 2023 16:45:45 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-react-router란">⭐ React Router란</h1>
<p><code>React Router</code>는 React 기반의 웹 애플리케이션에서 <code>라우팅을 관리</code>하기 위한 라이브러리 입니다. 라우팅은 사용자가 웹 내에서 다른 페이지로 이동하는 것을 말하며 <code>React Router</code>는 이를 쉽게 구현할 수 있도록 도와줍니다. </p>
<p>즉 SPA로 구성된 React 애플리케이션의 각컴포넌트를 <code>URL 경로와 매핑</code>시키는 방식으로 동작합니다. </p>
<p>설치 </p>
<h3 id="📎-npm-버전">📎 npm 버전</h3>
<pre><code class="language-bash">npm install react-router-dom localforage match-sorter sort-by</code></pre>
<pre><code class="language-bash">npx create-react-app react-router-tutorial</code></pre>
<p>react-router 공식문서에서 제공해주는 다음 코드를 넣으면 css나 비동기 서버통신을 미리 셋팅할 수 있습니다. </p>
<p>src/index.css</p>
<p><a href="https://gist.githubusercontent.com/ryanflorence/ba20d473ef59e1965543fa013ae4163f/raw/499707f25a5690d490c7b3d54c65c65eb895930c/react-router-6.4-tutorial-css.css">index.css file</a></p>
<p>src/contacts.js</p>
<p><a href="https://gist.githubusercontent.com/ryanflorence/1e7f5d3344c0db4a8394292c157cd305/raw/f7ff21e9ae7ffd55bfaaaf320e09c6a08a8a6611/contacts.js">contacts.js file</a></p>
<h3 id="📎-최종-파일-구조">📎 최종 파일 구조</h3>
<pre><code class="language-bash">src
├── contacts.js
├── index.css
└── index.jsx</code></pre>
<h1 id="⭐-router-추가하기">⭐ Router 추가하기</h1>
<p>클라이언트 사이드 랜더링을 위해 다음코드를 추가해 줍시다. </p>
<h3 id="📎-브라우저-라우터-생성-및-랜더링">📎 브라우저 라우터 생성 및 랜더링</h3>
<p>src/index.js</p>
<pre><code class="language-jsx">import * as React from &quot;react&quot;;
import * as ReactDOM from &quot;react-dom/client&quot;;
import {
  createBrowserRouter,
  RouterProvider,&lt;L
} from &quot;react-router-dom&quot;;
import &quot;./index.css&quot;;

const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;div&gt;Hello world!&lt;/div&gt;,
  },
]);

ReactDOM.createRoot(document.getElementById(&quot;root&quot;)).render(
  &lt;React.StrictMode&gt;
    &lt;RouterProvider router={router} /&gt;
  &lt;/React.StrictMode&gt;
);</code></pre>
<p>router변수에서 모든 라우팅을 세팅한다음 브라우저에 그리게 됩니다. 가장 기본적인 셋팅이기 때문에 처음에 이해가 가지 않는다면 우선은 받아들입시다. </p>
<p>해당 코드에서는 <code>‘/’</code>라는 <code>path</code>에 <code>element</code>를 그리게 됩니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/97c6cba5-9d47-45a9-982b-d7e8c814519a/image.png" alt=""></p>
<p>가장 기본적인 라우팅이라고 할 수 있다. </p>
<h1 id="⭐-루트경로">⭐ 루트경로</h1>
<p>이 앱에서 전역 레이아웃을 추가해 봅시다. </p>
<p>다음경로에 파일을 추가적으로 만들어줍니다. </p>
<h3 id="📎-루트-레이아웃-구성-요소-만들기">📎 루트 레이아웃 구성 요소 만들기</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">export default function Root() {
  return (
    &lt;&gt;
      &lt;div id=&quot;sidebar&quot;&gt;
        &lt;h1&gt;React Router Contacts&lt;/h1&gt;
        &lt;div&gt;
          &lt;form id=&quot;search-form&quot; role=&quot;search&quot;&gt;
            &lt;input
              id=&quot;q&quot;
              aria-label=&quot;Search contacts&quot;
              placeholder=&quot;Search&quot;
              type=&quot;search&quot;
              name=&quot;q&quot;
            /&gt;
            &lt;div
              id=&quot;search-spinner&quot;
              aria-hidden
              hidden={true}
            /&gt;
            &lt;div
              className=&quot;sr-only&quot;
              aria-live=&quot;polite&quot;
            &gt;&lt;/div&gt;
          &lt;/form&gt;
          &lt;form method=&quot;post&quot;&gt;
            &lt;button type=&quot;submit&quot;&gt;New&lt;/button&gt;
          &lt;/form&gt;
        &lt;/div&gt;
        &lt;nav&gt;
          &lt;ul&gt;
            &lt;li&gt;
              &lt;a href={`/contacts/1`}&gt;Your Name&lt;/a&gt;
            &lt;/li&gt;
            &lt;li&gt;
              &lt;a href={`/contacts/2`}&gt;Your Friend&lt;/a&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;/nav&gt;
      &lt;/div&gt;
      &lt;div id=&quot;detail&quot;&gt;&lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>다음과 같은  <code>Root 컴포넌트</code>를 만들고 아까 만든 <code>“/”</code>경로에 추가해 줍니다. </p>
<h3 id="📎-루트경로-설정">📎 루트경로 설정</h3>
<p>scr/index.jsx</p>
<pre><code class="language-jsx">import { createBrowserRouter, RouterProvider } from &quot;react-router-dom&quot;;
import Root from &quot;./routes/Root&quot;;

// ahem
const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root/&gt;, //해당경로에 컴포넌트 추가
  },
]);

const root = ReactDOM.createRoot(document.getElementById(&quot;root&quot;));
root.render(
  &lt;React.StrictMode&gt;
    &lt;RouterProvider router={router} /&gt;
  &lt;/React.StrictMode&gt;
);</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/fd4c5caf-84d4-4f60-ada5-5085d60b10c4/image.png" alt=""></p>
<p><code>‘/’경로</code>가 아까 추가해둔 <code>Root 컴포넌트</code>로 변경되었습니다! 이제 라우팅을 어떻게 하면 좋을지 감이 오셨을 겁니다. </p>
<p>이번엔 <code>your Name</code>이라는 버튼을 한번 눌러봅시다.</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/41254042-54cd-442b-8047-f111edd5298c/image.png" alt=""></p>
<p><a href="http://localhost:3000/contacts/1"><code>http://localhost:3000/contacts/1</code></a> 경로로 라우팅을 설정한적이 없기 때문에 에러가 발생합니다. 경고 메시지를 보니 <code>errorElement</code>나 <code>ErrorBoundary</code>를 설정하라고 나옵니다.</p>
<p>해당 에러를 처리하기 위해 기본적인 오류페이지를 만들어 봅시다. </p>
<h1 id="⭐-찾을-수-없음-오류-처리">⭐ 찾을 수 없음 오류 처리</h1>
<h3 id="📎-오류페이지-만들기">📎 오류페이지 만들기</h3>
<p>src/error-page.jsx</p>
<pre><code class="language-jsx">import { useRouteError } from &quot;react-router-dom&quot;;

export default function ErrorPage() {
  const error = useRouteError(); // 에러 발생 여부를 가져옵니다.
  console.error(error);

  return (
    &lt;div id=&quot;error-page&quot;&gt;
      &lt;h1&gt;Oops!&lt;/h1&gt;
      &lt;p&gt;Sorry, an unexpected error has occurred.&lt;/p&gt;
      &lt;p&gt;
        &lt;i&gt;{error.statusText || error.message}&lt;/i&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h3 id="📎-루트-경로에서-오류컴포넌트-연결">📎 루트 경로에서 오류컴포넌트 연결</h3>
<p>root 경로에 에러페이지를 추가해 줍시다</p>
<pre><code class="language-jsx">const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement : &lt;ErrorPage/&gt;
  },
]);</code></pre>
<p><code>&lt;Root/&gt;</code>에서 발생하는 모든에러는 <code>&lt;ErrorPage/&gt;</code>로 이동하게 됩니다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/887a4596-8ae7-4717-ae84-878a0843dd52/image.png" alt=""></p>
<p>이제 “/”경로에서 에러가 발생할 경우 다음과 같은 에러페이지가 화면에 나타나게 됩니다. </p>
<h1 id="⭐-연락처-경로-ui">⭐ 연락처 경로 UI</h1>
<p>이제 다른 페이지를 만들고 라우팅 해봅시다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/d7050b98-1fcb-42ee-82b6-9c0ee5713b91/image.png" alt=""></p>
<p>왼쪽 sidebar에 있는것이 연락처 목록입니다. </p>
<p>연락처 구성요소 UI 추가</p>
<p>src/routes/contact.jsx</p>
<pre><code class="language-jsx">import { Form } from &quot;react-router-dom&quot;;

export default function Contact() {
  const contact = {
    first: &quot;Your&quot;,
    last: &quot;Name&quot;,
    avatar: &quot;https://placekitten.com/g/200/200&quot;,
    twitter: &quot;your_handle&quot;,
    notes: &quot;Some notes&quot;,
    favorite: true,
  };

  return (
    &lt;div id=&quot;contact&quot;&gt;
      &lt;div&gt;
        &lt;img
          key={contact.avatar}
          src={contact.avatar || null}
        /&gt;
      &lt;/div&gt;

      &lt;div&gt;
        &lt;h1&gt;
          {contact.first || contact.last ? (
            &lt;&gt;
              {contact.first} {contact.last}
            &lt;/&gt;
          ) : (
            &lt;i&gt;No Name&lt;/i&gt;
          )}{&quot; &quot;}
          &lt;Favorite contact={contact} /&gt;
        &lt;/h1&gt;

        {contact.twitter &amp;&amp; (
          &lt;p&gt;
            &lt;a
              target=&quot;_blank&quot;
              href={`https://twitter.com/${contact.twitter}`}
            &gt;
              {contact.twitter}
            &lt;/a&gt;
          &lt;/p&gt;
        )}

        {contact.notes &amp;&amp; &lt;p&gt;{contact.notes}&lt;/p&gt;}

        &lt;div&gt;
          &lt;Form action=&quot;edit&quot;&gt;
            &lt;button type=&quot;submit&quot;&gt;Edit&lt;/button&gt;
          &lt;/Form&gt;
          &lt;Form
            method=&quot;post&quot;
            action=&quot;destroy&quot;
            onSubmit={(event) =&gt; {
              if (
                // eslint-disable-next-line no-restricted-globals
                !confirm(
                  &quot;Please confirm you want to delete this record.&quot;
                )
              ) {
                event.preventDefault();
              }
            }}
          &gt;
            &lt;button type=&quot;submit&quot;&gt;Delete&lt;/button&gt;
          &lt;/Form&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

function Favorite({ contact }) {
  // yes, this is a `let` for later
  let favorite = contact.favorite;
  return (
    &lt;Form method=&quot;post&quot;&gt;
      &lt;button
        name=&quot;favorite&quot;
        value={favorite ? &quot;false&quot; : &quot;true&quot;}
        aria-label={
          favorite
            ? &quot;Remove from favorites&quot;
            : &quot;Add to favorites&quot;
        }
      &gt;
        {favorite ? &quot;★&quot; : &quot;☆&quot;}
      &lt;/button&gt;
    &lt;/Form&gt;
  );
}</code></pre>
<p>어떤 구조로 되어있는지는 화면으로 살펴보고 붙여넣기 합니다. </p>
<h3 id="📎-연락처-구성-요소-가져오기-및-새-경로-만들기">📎 연락처 구성 요소 가져오기 및 새 경로 만들기</h3>
<p>라우팅을 추가해 줍니다. </p>
<pre><code class="language-jsx">import Contact from &quot;./routes/contact&quot;;

// 라우팅
const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement : &lt;ErrorPage/&gt;
  },
  {
    path: &quot;contacts/:contactId&quot;,  //이경로로 이동하면 &lt;Contact/&gt; 가 보인다. 
    element: &lt;Contact /&gt;,
  },
]);</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/1ef1e7a7-7481-4292-a78e-32eb069f21ac/image.png" alt=""></p>
<p>해당 경로로 들어가면 귀여운 고양이가 보이게 됩니다. </p>
<p>여기서 <code>:contactId</code>는 <code>파라미터</code>가 됩니다. (1,2,3,”123”,…) 등 하나의 페이지에서 다양한 값이 필요할 때 사용됩니다. 자세한 내용은 뒤에 살펴봅시다. </p>
<p>이 페이지에는 하나의 문제점이 있습니다. <code>Root컴포넌트</code> 안에 존재하지 않는다는 것입니다. Root 컴포넌트와 경로가 다르기 때문에 <code>sidebar</code>는 더이상 보이지 않습니다. <code>sidebar</code>까지 함께 보이게 하기 위해 <code>Root컴포넌트</code> 안에 <code>Contact</code>를 넣어보도록 하겠습니다. </p>
<h1 id="⭐-중첩경로">⭐ 중첩경로</h1>
<h3 id="연락처-경로를-루트경로의-자식으로-이동">연락처 경로를 루트경로의 자식으로 이동</h3>
<p>코드를 다음과 같이 작성해 봅시다. </p>
<p>src/index.jsx</p>
<pre><code class="language-jsx">// 라우팅
const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;Root /&gt;,
    errorElement : &lt;ErrorPage/&gt;,
    children:[
      {
        path:&quot;contacts/:contactId&quot;,
        element:&lt;Contact/&gt;
      }
    ]
  },

]);</code></pre>
<p>아까와 달리 <code>&lt;Root/&gt; 컴포넌트</code>의 <code>‘/’ 경로</code> 밑에 <code>&lt;Contact/&gt; 컴포넌트</code>가 위치해 있습니다. 해당<code>&lt;Contact/&gt; 컴포넌트</code>를 사용하기 위해서는 <code>&lt;Outlet/&gt;</code>이라는  컴포넌트가 필요합니다. </p>
<h3 id="📎-랜더링">📎 랜더링</h3>
<p>src/routes/root.jsx</p>
<pre><code class="language-jsx">import { Outlet } from &quot;react-router-dom&quot;;

export default function Root() {
  return (
    &lt;&gt;
      {/* all the other elements */}
      &lt;div id=&quot;detail&quot;&gt;
        &lt;Outlet /&gt;   // &lt;Contact/&gt;는 이곳에 위치하게 됩니다. 
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<p><code>Root 컴포넌트</code>의 <code>children</code>의 컴포넌트가 다음 <code>Outlet 컴포넌트</code>에 위치하게 됩니다. 이제 실제 화면을 살펴 봅시다.</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/ffdd1686-c3fe-46fc-b942-fe75e29ae2da/image.png" alt=""></p>
<p>다음 경로에 <code>Root컴포넌트</code>와 <code>Contact 컴포넌트</code>가 모두 포함된걸 볼수있습니다!</p>
<h1 id="⭐-클라이언트-측-라우팅">⭐ 클라이언트 측 라우팅</h1>
<p>이제까지의 코드에서는 사이드바에서 링크를 클릭하면 브라우저가 <code>React Routrer</code>를 사용해서 클라언트 라우팅을 하는게 아닌 <code>URL에 대한 전체 문서요청을 수행해서 페이지</code>를 그렸습니다. 이는 html css javascript를 전부 불러와야하기 때문에 자원낭비 심합니다.</p>
<p>이러한 문제를 해결하기 위해서 앞으로는 <code>&lt;Link/&gt;</code>를 사용해 <code>URL을 업데이트해서 UI를 즉시 랜더링</code> 할 수 있게 해봅시다. </p>
<p>수정하기전</p>
<pre><code class="language-jsx">                    &lt;ul&gt;
              &lt;li&gt;
                &lt;a href={`/contacts/1`}&gt;Your Name&lt;/a&gt;
              &lt;/li&gt;
              &lt;li&gt;
                &lt;a href={`/contacts/2`}&gt;Your Friend&lt;/a&gt;
              &lt;/li&gt;
            &lt;/ul&gt;</code></pre>
<p>수정후</p>
<pre><code class="language-jsx">import { Outlet, Link } from &quot;react-router-dom&quot;;

...
                        &lt;ul&gt;
              &lt;li&gt;
                &lt;Link to={`/contacts/1`}&gt;Your Name&lt;/Link&gt;
              &lt;/li&gt;
              &lt;li&gt;
                &lt;Link to={`/contacts/2`}&gt;Your Friend&lt;/Link&gt;
              &lt;/li&gt;
            &lt;/ul&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/0214c930-e8cb-4338-add3-83d4aa94093f/image.png" alt=""></p>
<p>이제페이지를 이동하는 과정에서 <code>새로고침이 발생하지 않습니다.</code> 클라이언트측에서 url를 업데이트하고 html css javascript를 전부 다시 가져오는것이 아닌 <code>이미 받아온 정적 파일들로 화면을 즉시 만드는것</code>이기 때문에 새로고침이 발생하지 않고 부드러운 페이지 전환이 가능해집니다.</p>
<blockquote>
<p>react-router를 설치하고 router를 세팅하는 방법에 대해 알아보았습니다. 모든 자료는 react-router-dom 공식홈페이지에서 제공합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[AjAX 란?]]></title>
            <link>https://velog.io/@giant_toothpick/AjAX-%EB%9E%80</link>
            <guid>https://velog.io/@giant_toothpick/AjAX-%EB%9E%80</guid>
            <pubDate>Sat, 12 Aug 2023 10:32:00 GMT</pubDate>
            <description><![CDATA[<br/>

<h1 id="⭐-ajaxasynchronous-javascript-and-xml">⭐ AJAX(Asynchronous Javascript And XML)</h1>
<hr>
<p><img src="https://velog.velcdn.com/images/dfty1610/post/1c1788b0-58fd-49aa-adc5-28de63ea3eb0/image.png" alt=""></p>
<p>📖 Ajax는 javascript의 라이브러리중 하나이며, Asynchronous Javascript And XML의 약자로, 비동기적으로 서버와 통신하여 웹 페이지의 일부를 업데이트하거나 데이터를 동기적으로 불러오는 기술이다. </p>
<p>📖 페이지를 새로고침하지 않고도 데이터를 가져올 수 있어 사용자 경험을 향상시키고, 서버와의 통신을 더 효율적으로 만들 수 있다. </p>
<p>📖 이름 그대로 XML을 가져올수도 있지만 최근에는 JSON데이터를 더 많이 사용한다. </p>
<br/>

<h2 id="📌-ajax의-필요성">📌 AJAX의 필요성</h2>
<hr>
<blockquote>
<p>AJAX를 사용하지 않는다면 어떤 불편한 점이 있을까?</p>
</blockquote>
<ol>
<li><code>페이지 전체 새로고침</code> : 특정 부분만 업데이트 할 수 없기 때문에 새로운 데이터를 보려면 페이지 전체를 새로고침 해야한다. 이는 사용자경험 저하, 페이지의 로딩시간증가로 이어진다. </li>
<li><code>서버 부하 증가</code> : 페이지 전체를 새로고침 해야하므로, 서버에 불필요한 요청이 증가한다. (html css javascript 전체를 다시 불러옴)</li>
<li><code>네트워크 트레픽 증가</code> : 페이지 전체를 다시 로드해야 하므로 불필요한 네트워크 트래픽이 발생해 데이터 소모가 늘어난다. </li>
<li><code>사용자 경험 저하</code> : 사용자가 특정 동작을 수행할 때마다 페이지 전체가 새로고침되면서 사용자 경험이 떨어진다. 특히 댓글이나 폼 제출과 같은 작업을 할 때 페이지가 다시 로드되어 사용자가 불편을 겪는다. </li>
</ol>
<br/>

<h2 id="📌-ajax-장점">📌 AJAX 장점</h2>
<hr>
<ul>
<li><code>비동기통신</code> : 페이지의 부분적인 업데이트</li>
<li><code>사용자 경험 향상</code> : 새로고침을 하지 않아 빠른 화면전환과 데이터로딩을 경험</li>
<li><code>서버 부하 감소</code> : 데이터만 가져오기 때문에 서버의 부하가 줄어든다.</li>
<li><code>자원 절약</code> : 데이터만 가져오기 때문에 리소스 전체를 로딩하는것보다 났다</li>
<li><code>동기적 UI 업데이트</code> : 실시간 검색결과나 채팅 기능 구현 가능</li>
</ul>
<h2 id="📌-ajax-단점">📌 AJAX 단점</h2>
<hr>
<ul>
<li><code>브라우저 호환성</code> : 모든 부라우저가 동일하게 AJAX를 지원하지 않을 수 있다.</li>
<li><code>보안 문제</code> : javascript를 통해 서버와 데이터를 주고 받기 때문에 보안이슈가 발생할 수 있다.</li>
<li><code>검색 엔진 최적화(SEO) 어려움</code> : 검색 엔진은 javascript를 해석하는데 한계가 있어서 동적으로 로딩되는 컨텐츠를 인식하지 못할 수도 있다.</li>
<li><code>디버깅과 테스팅 어려움</code> : 비동기 코드의 흐름을 추적하기 어려울 수 있다.</li>
<li><code>코드 복잡성</code> : 코드의 복잡성으로 인해 유지보수가 어려울 수 있고, 콜백지옥과 같은 문제가 발생할 수 있다.</li>
</ul>
<br/>

<br/>

<h1 id="⭐-동기와-비동기">⭐ 동기와 비동기</h1>
<hr>
<p><img src="https://velog.velcdn.com/images/jminkyoung/post/e9ce9093-c339-4e3a-960a-af4cbd956655/%EB%8F%99%EA%B8%B0.png" alt=""></p>
<br/>

<h2 id="📌-동기">📌 동기</h2>
<blockquote>
<p>작업이 순차적으로 진행되는것</p>
</blockquote>
<p>어떤 작업이 시작되면 그작업이 완료되기 전까지는 다음작업이 시작되지 않는다. 이련의 작업들은 순차적으로 실행되며 하나의 작업이 끝나야 다음 작업이 시작된다.</p>
<p>공유 리소스 접근, 디버깅과 예외처리등 순차적으로 진행되어 작업 순서가 보장되어야 할 경우 사용된다. </p>
<br/>

<h2 id="📌-비동기">📌 비동기</h2>
<blockquote>
<p>병렬적으로 여러작업을 동시에 진행할 수 있는것</p>
</blockquote>
<p>작업 결과를 기다리지 않고 다음 작업을 실행할 수 있다. 작업이 완료되면 그 결과를 받아 처리할 수 있다. </p>
<p>예를 들면 네트워크 요청, 파일 입출력, 타이머 등과 같이 시간이 오래 거릴는 작업을 처리할 때 비동기 적으로 작성하면 시간이 오래걸리는 작업이 끝날 때까지 기다리지 않고 다른 작업을 수행할 수 있다.</p>
<p><br/><br/></p>
<h1 id="📄-느낀점">📄 느낀점</h1>
<blockquote>
<p>ajax를 사용하지 않으면 정보를 업데이트하는 방법이 페이지를 처음부터 다시 로딩해야하는 끔찍한 방법 밖에는 없는걸 깨달았다. 없었다면 휴대폰 데이터가 몇배는 나가지 않았을까?</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 랜더링 최적화]]></title>
            <link>https://velog.io/@giant_toothpick/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9E%9C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@giant_toothpick/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9E%9C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Wed, 09 Aug 2023 07:35:07 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-리액트의-랜더링과정">⭐ 리액트의 랜더링과정</h1>
<p>리액트에서는 최적화는 컴포넌트의 리랜더링을 줄이는 과정이라고해도 과언이 아니다. <code>useCallback</code>과 <code>useMemo</code> 그리고 <code>React.memo</code>를 사용하여 메모이제이션 기법으로 리랜더링을 줄이게 된다. </p>
<p>리액트의 렌더링은  <code>Render Phase</code>와 <code>Commit Phase</code> 크게 두가지 로 나뉜다. </p>
<h3 id="📌-render-phase">📌 Render Phase</h3>
<p><code>state</code> 나 <code>props</code> 가 변하면 해당 컴포넌트를 불러서 <code>React.createElement</code>를 동작시켜 컴포넌트를 실행시킨다. 이과정을 리랜더링이라고 한다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/82f234f3-3352-437b-9d51-a40f796b7658/image.png" alt=""></p>
<p>해당 리랜더링 과정을 통해 생성된 새로운 <code>virtual DOM</code>을 만들게 된다. </p>
<h3 id="📌-재조정reconciliation">📌 재조정(Reconciliation)</h3>
<p>이전에 만들어진 <code>virtual DOM</code>과 현재 만든 <code>virtual DOM</code>을 비교하여 실제 <code>Real DOM에 반영</code>할 목록들을 확인한다. </p>
<h3 id="📌-commit-phase">📌 Commit Phase</h3>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/90ccffef-6ff8-4e66-99c3-bafa91ed0d25/image.png" alt=""></p>
<p><code>render phase</code>에서 확인했던 변경이 필요한 목록들을 실제 <code>Real DOM에 적용</code>한다. </p>
<p>변경이 필요한 부분이 없다면 <code>commit phase는 스킵</code>된다. </p>
<p>이해를 돕기 위해 다음과 같은 예시코드를 준비했다</p>
<pre><code class="language-jsx">//부모 컴포넌트
const Parent = ()=&gt;{
    const [value, setValue] = useState(null);

    const handleClick = () =&gt;{};

    useEffect(()=&gt;{
        setTimeout(()=&gt;{
            setValue(&quot;changeValue&quot;);
        },3000);
    },[])

    return(
        &lt;&gt;
            &lt;FirstChild value={value}/&gt;
            &lt;SecondChild onClick={handleClick}/&gt;
        &lt;/&gt;
    )
}</code></pre>
<pre><code class="language-jsx">// 첫번째 자식 컴포넌트
const FirstChild = ({value})=&gt;{
    return &lt;div&gt;{value}&lt;/div&gt;
}</code></pre>
<pre><code class="language-jsx">// 두번째 자식 컴포넌트
const SecondChild = ({ onClick })=&gt;{
    &lt;div onClick={onclick}&gt;
        {Array.from({length:1000}).map((_,idx)=&gt;(
            &lt;GrandChild key={idx+1) order={idx}/&gt;.    //오래걸리는 작업의 예시 
        ))}
    &lt;/div&gt;
}</code></pre>
<p>React의 리랜더링은 조건은 <code>props</code>와 <code>state</code>의 변경에 있다.  <code>Parent</code>의 value의 값이 변경되면서 <code>Parent</code>는 리랜더링이 되고 그로인해 <code>Firstchild</code>도 리랜더링된다. 여기서 주의할 점은 <code>SecondChild</code>의 <code>props</code>값도 변경된다는 것이다 Parent가 리랜더링 되면서 <code>handleClick</code>은 <code>재정의</code> 되고 이전과는다른 <code>handleClick</code>함수가 생성되면서 자연스럽게 <code>SecondChild</code>의 <code>props</code>는 변경되게 된다. <code>SecondChild</code>는 불필요하게 리랜더링 작업을 수행하게 된다.</p>
<p>이 불필요한 과정을 최적화 할 순 없을까?</p>
<br/>

<br/>

<h1 id="⭐-usecallback-사용">⭐ useCallback 사용</h1>
<pre><code class="language-jsx">//부모 컴포넌트
const Parent = ()=&gt;{
    const [value, setValue] = useState(null);

    const handleClick = useCallback(()=&gt;{},[]);

    useEffect(()=&gt;{
        setTimeout(()=&gt;{
            setValue(&quot;changeValue&quot;);
        },3000);
    },[])

    return(
        &lt;&gt;
            &lt;FirstChild value={value}/&gt;
            &lt;SecondChild onClick={handleClick}/&gt;
        &lt;/&gt;
    )
}</code></pre>
<p><code>useCallback</code>을 사용해서 <code>handleClick</code>을 메모이제이션 하였다. 의존성 배열이 변경되지 않는다면 <code>handleClick</code>의 참조값은 변경되지 않는다. 때문에 리랜더링의 조건중 하나인 props를 동일하게 만들었기 때문이다. </p>
<p>그렇다면 이제 리랜더링은 발생하지 않을까? 안타깝게도 여전히 리랜더링이 발생한다. </p>
<p>왜냐하면 Parent가 리랜더링되면서 <code>React.createElement</code>를 사용해 자식컴포넌트들을 재생성하기 때문이다. 그렇다고 <code>useCallback</code>을 사용하는게 아주의미없는것은 아니다. <code>SecondChild</code>는 리랜더링 되지만 <code>render phase</code>의 <code>재조정과정</code>에서 이전 virtual DOM과 변경된 점이 없기 때문에 <code>commit phase를 생략</code>할 수 있다! </p>
<p>그치만 여전히 render phase에서 불필요하게 리랜더링 되고 있다.. </p>
<p>이때 <code>React.memo</code>를 사용하게 된다. <code>React.memo</code>를 사용한 컴포넌트는 <code>props</code>와 <code>state</code>가 변하지 않는다면 컴포넌트의 리랜더링을 막고 메모이제이션한 컴포넌트를 사용하여 <code>render phase를 생략</code>할 수 있다. !</p>
<br/>

<br/>

<h1 id="⭐-두번째-자식-컴포넌트에-reactmemo-사용">⭐ 두번째 자식 컴포넌트에 React.memo 사용</h1>
<pre><code class="language-jsx">// 두번째 자식 컴포넌트
const SecondChild = ({ onClick })=&gt;(
    &lt;div onClick={onclick}&gt;
        {Array.from({length:1000}).map((_,idx)=&gt;(
            &lt;GrandChild key={idx+1) order={idx}/&gt;.    //오래걸리는 작업의 예시 
        ))}
    &lt;/div&gt;
)

export default React.memo(SecondChild); //리액트 메모의 사용</code></pre>
<blockquote>
<p>React.memo가 props를 이전과 비교할 때는 얕은비교를 사용한다.  얕은 비교란 원시타입은 값을 비교하고, 참조타입은 참조값이 같은지를 비교한다.</p>
</blockquote>
<p>이제 <code>React.memo</code>를 통해 컴포넌트 전체를 메모이제이션 해주었다. <code>React.memo</code>는 props가 변경되지 않으면 해당 컴포넌트를 이전에 랜더링 했을 때 생성된 <code>가상 DOM을 재사용하여 리랜더링</code>을 방지한다. </p>
<p>이전에 <code>useCallback</code>을 사용해 <code>handleClick</code>을 재사용하여 props의 변경을 방지했기 때문에 Parent 컴포넌트가 리랜더링이 되어서 <code>SecondChild는 더이상 리랜더링이 되지 않는다!</code> </p>
<br/>

<br/>

<h1 id="⭐-불변하는-값을-props로-넘길때-usememo">⭐ 불변하는 값을 props로 넘길때 useMemo</h1>
<pre><code class="language-jsx">//부모 컴포넌트
const Parent = ()=&gt;{
    const [value, setValue] = useState(null);

    const item = {
        name : &quot;이순신&quot;,
        age : 45
        }

    useEffect(()=&gt;{
        setTimeout(()=&gt;{
            setValue(&quot;changeValue&quot;);
        },3000);
    },[])

    return(
        &lt;&gt;
            &lt;FirstChild value={value}/&gt;
            &lt;SecondChild item={item}/&gt;
        &lt;/&gt;
    )
}</code></pre>
<pre><code class="language-jsx">// 두번째 자식 컴포넌트
const SecondChild = ({ item })=&gt;(
    &lt;div&gt;이름 : {item.name},  나이 : {item.age}&lt;/div&gt;
)

export default React.memo(SecondChild); //리액트 메모의 사용</code></pre>
<p>다음 코드에서 Parent는 3초후에 리랜더링되고 item 변수또한 재정의된다. item은 객체로 참조타입이기 때문에 React는 이전과 다른 객체로 판단하고 SecondChild props가 변경된것으로 판단하여 SecondChild 리랜더링 하게된다. 기껏한 React.memo가 소용없게 되버렸다.</p>
<br/>

<br/>

<h1 id="⭐-usememo-사용">⭐ useMemo 사용</h1>
<p>이런경우에는 값을 저장하는 useMemo를 사용할 수 있다. 의존성 배열이 변경되지 않는다면 useMemo는 메모이제이션한 값을 반환한다. </p>
<pre><code class="language-jsx">//부모 컴포넌트
const Parent = ()=&gt;{
    const [value, setValue] = useState(null);

    const item = {
        name : &quot;이순신&quot;,
        age : 45
        }

    const memoizationItem = useMemo(()=&gt; item, []);

    useEffect(()=&gt;{
        setTimeout(()=&gt;{
            setValue(&quot;changeValue&quot;);
        },3000);
    },[])

    return(
        &lt;&gt;
            &lt;FirstChild value={value}/&gt;
            &lt;SecondChild item={memoizationItem}/&gt;
        &lt;/&gt;
    )
}</code></pre>
<p>이로써 SecondChild의 item의 참조값은 유지되고 props는 변경되지 않아 리랜더링 되지 않고, 메모이제이션한 컴포넌트를 재사용할 수 있다.</p>
<br/>

<br/>

<h1 id="⭐-모든곳에서-usecallback-usememo-reactmemo-사용하면-좋을까">⭐ “모든곳에서 useCallback, useMemo, React,memo 사용하면 좋을까?”</h1>
<p>해당 함수들도 결국 코드이고 메모이제이션을 하기위한 작업이 필요하다. 때문에 props나 state가 자주 변경되는 컴포넌트에 사용하면 오히려 메모리의 낭비를 초례할 수 있다. </p>
<br/>

<br/>

<h1 id="⭐-최적화-도구들을-사용하기-전에-근본적인-코드를-개선">⭐ 최적화 도구들을 사용하기 전에 근본적인 코드를 개선</h1>
<p>기본적으로 코드를 잘 작성해놓고 최적화 도구를 사용하도록하자 </p>
<pre><code class="language-jsx">const Component = () =&gt;{
    const forceUpdate = useForceUpdate(); // 강제 리랜더링하는 함수 

    return(
        &lt;&gt;
            &lt;button onClick={forceUpdate}&gt; force&lt;/button&gt;
            &lt;Consoler value=&quot;fixedValue&quot;/&gt;
        &lt;/&gt;
    )
}</code></pre>
<p>force라는 버튼을 누르면 강제로 리랜더링이 동작하는 컴포넌트이다. 여기서 Consoler의 value값은  변경되지 않았지만 버튼을 눌러 Component를 리랜더링하게 되면 React.createElement가 자동으로 내부 컴포넌트들을 리랜더링하게 된다. </p>
<p>이 코드를 근본적을 개선할 수 있는 방법은 무엇일까?</p>
<pre><code class="language-jsx">const Component = ({children}) =&gt;{
    const forceUpdate = useForceUpdate();
    return(
        &lt;&gt;
            &lt;button onClick={forceUpdate}&gt; force&lt;/button&gt;
            {children}
        &lt;/&gt;
    )
}

function App(){
    &lt;div&gt;
        &lt;Component&gt;
            &lt;Consoler value=&quot;fixedValue&quot;/&gt;
        &lt;/Component&gt;
    &lt;/div&gt;
}</code></pre>
<p>다음과 같이 사용하면 Consoler는 Component 컴포넌트 안에 있는 것이 아니기 때문에 React.createElement로 리랜더링을 하지 않는다. </p>
<p>이처럼 메모이제이션을 사용하기 이전에 근본적으로 최적화를 먼저 해야한다.</p>
<br/>

<h3 id="dont-optimize-rendering-prematurely-do-it-when-needed">“dont optimize rendering prematurely, do it when needed”</h3>
<p>미리 최적화 하지 말자, 필요할 때 하자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CDN 이란?]]></title>
            <link>https://velog.io/@giant_toothpick/CDN-%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@giant_toothpick/CDN-%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Tue, 08 Aug 2023 08:18:52 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-cdn-이란">⭐ CDN 이란?</h1>
<hr>
<p>Content Delivery Network의 약자로 전송 네트워크를 의미한다. CDN은 인터넷 상에서 웹 페이지, 이미지, 비디오 등 다양한 정적 컨텐츠를 더 빠르게 제공하기 위한 분산 <code>네트워크 시스템</code>이다. </p>
<p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https://blog.kakaocdn.net/dn/xBOKW/btrKoS495iu/BH9I4taR8kkeJ6c5cX9rr0/img.png" alt=""></p>
<p>결국 부하를 분산시키고 사용자에게 더 빠르게 컨텐츠를 제공하기 위한 기술이며, 웹사이트의 성능을 향상시키게 된다. </p>
<p>내가 만든 서버는 서울에 있는데 사용자는 유럽에 있다고 가정할 때 유럽에 있는 Cache 서버에 내가 만든 컨텐츠를 저장한다면 더 빠르게 접근할 수 있는 것이다</p>
<br/>

<br/>

<h1 id="⭐-cdn-동작방식">⭐ CDN 동작방식</h1>
<hr>
<p>CDN은 다음과 같은 방식으로 작동한다.  </p>
<h3 id="📌-컨텐츠-복제-및-캐싱">📌 컨텐츠 복제 및 캐싱</h3>
<p>CDN은 원본 서버에 있는 컨텐츠를 여러 지역의 서버에 복제하고 캐싱한다. 이로써 사용자들은 가까운 지역의 서버에서 컨텐츠를 로드할 수 있어, 응답시간이 줄어든다. </p>
<h3 id="📌-지리적-분산">📌 지리적 분산</h3>
<p>CDN은 여러 지역에 있는 서버를 활용하여 컨텐츠를 제공하낟. 사용자가 원격 지역에 있더라도 더 가까운 서버의 컨텐츠를 로드할 수 있어 더 빠른 응답시간을 제공한다. </p>
<h3 id="📌-로드-밸런싱">📌 로드 밸런싱</h3>
<p>CDN은 트래픽을 여러 서버로 분산시켜 부하를 분담한다. 이로써 원본 서버의 부하를 줄이고 더 안정적인 서비스를 제공한다. </p>
<h3 id="📌-보안-및-ddos-방어">📌 보안 및 DDoS 방어</h3>
<p>일부 CDN은 보안 기능과 DDoS(분산 서비스 거부 공격) 방어 기능을 포함하여 웹 사이트의 안전성을 강화한다.</p>
<br/>
<br/>


<h1 id="⭐-대표적인-cdn-서비스">⭐ 대표적인 CDN 서비스</h1>
<hr>
<ul>
<li>Cloudflare</li>
<li>Akamai</li>
<li>Amazon CloudFront</li>
</ul>
<br/>

<br/>

<h1 id="⭐-cdn-사용-예시">⭐ CDN 사용 예시</h1>
<hr>
<p>CDN은 주로 변경될 일 없는 정적 컨텐츠를 더 빠르게 제공하기 위해 사용되는 기술이다. 다음은 CDN활용한 예시이다.</p>
<h3 id="📌-웹사이트-콘텐츠-제공">📌 웹사이트 콘텐츠 제공</h3>
<p>대규모 웹 사이트나 블로그는 이미지, 스타일시트, 스크립트 파일등 정적인 컨텐츠를 CDN을 통해 더 빠르게 제공하며, 이로써 사용자가 웹 페이지를 빠르게 로딩할 수 있다.</p>
<h3 id="📌-미디어-스트리밍">📌 미디어 스트리밍</h3>
<p>동영상 및 오디오 스트리밍 서비스는 CDN을 사용하여 사용자에게 고품질의 미디어 컨텐츠를 신속하게 제공한다. 유튜브, Netflix 등의 서비스에서 CDN을 활용하여 스트리밍 성능을 개선한다. </p>
<h3 id="📌-전자-상거래">📌 전자 상거래</h3>
<p>온라인 쇼핑 웹 사이트에서 제품 이미지, 설명, 가격 정보등을 CDN을 통해 더 빠르게 제공하여 사용자들의 구매 경험을 최적화 한다. </p>
<h3 id="📌-소셜-미디어">📌 소셜 미디어</h3>
<p>소셜 미디어 플랫폼은 이미지 및 비디오 공유를 위한 CDN을 사용하여 사용자들이 콘텐츠를 빠르게 업로드 하고 공유할 수 있도록 한다. </p>
<h3 id="📌-게임-서비스">📌 게임 서비스</h3>
<p>온라인 게임 플랫폼에서는 게임 컨턴츠를 CDN을 통해 제공하여 플레이어들이 더 빠르게 게임을 다운로드하고 플레이할 수 있도록 한다.</p>
<h3 id="📌-애플리케이션-배포">📌 애플리케이션 배포</h3>
<p>소프트 웨어나 애플리케이션의 업데이트, 설치 파일등을 CDN을 통해 제공하여 사용자들이 빠르게 업데이트 받을 수 있게 한다. </p>
<h3 id="📌-뉴스-및-미디어">📌 뉴스 및 미디어</h3>
<p>뉴스 웹 사이트나 미디어 포털은 실시간으로 업데이트 되는 뉴스나 기사를 빠르게 제공하기 위해 CDN을 활용합니다. </p>
<br/>

<br/>

<h1 id="⭐-cdn을-사용하려면-어떻게-해야할까">⭐ CDN을 사용하려면 어떻게 해야할까?</h1>
<hr>
<ol>
<li><code>CDN 제공업체 선택</code> : 다른나라에 있는 서버를 살순 없기 때문에 제공업체를 선택해야한다. </li>
<li><code>도메인을 등록 및 설정</code> : CDN 엔드포인트로 트래픽을 보낼 수 있게 한다.</li>
<li><code>콘텐츠 캐싱 및 배포</code> : CDN에서 제공할 정적 콘텐트를 업로드하고 캐싱한다. </li>
<li><code>DNS 레코드 설정</code> : CDN업체에서 제공하는 DNS 레코드를 도메인 설정에 추가한다. 그래야 웹사이트에 접근할 때 CDN을 통해 컨텐츠가 제공된다. </li>
<li><code>테스트 및 최적화</code> : 테스트를 통해 로딩속도의 변화 트래픽 변화를 확인할 수 있다.</li>
<li><code>보안 및 설정</code> : CDN제공업체에서 보안 설정을 확인하고 필요한 경우 SSL 인증 서를 적용할 수 있다. </li>
<li><code>모니터링 및 유지보수</code> : CDN을 사용하면 뫼터링 도구를 통해 성능을 지속적으로 추적할 수 있다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 메모이제이션]]></title>
            <link>https://velog.io/@giant_toothpick/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98</link>
            <guid>https://velog.io/@giant_toothpick/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A9%94%EB%AA%A8%EC%9D%B4%EC%A0%9C%EC%9D%B4%EC%85%98</guid>
            <pubDate>Tue, 08 Aug 2023 06:19:32 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-리액트-메모이제이션">⭐ 리액트 메모이제이션</h1>
<p>리액트의 메모이 제이션은 함수 컴포넌트 내에서 불필요한 재계산을 방지하고 컴포넌트의 성능을 최적화 하기 위한 기술이다. </p>
<ul>
<li>React.memo</li>
<li>useMemo</li>
<li>useCallback</li>
</ul>
<p>다음과 같은 함수를 활용하여 메모이제이션을 구현할 수 있다. </p>
<br/>

<br/>

<h1 id="⭐-virtual-dom">⭐ Virtual DOM</h1>
<hr>
<p>먼저 메모이제이션이 왜 필요한지 알아보자 </p>
<p><img src="https://blog.logrocket.com/wp-content/uploads/2023/01/5-react-actual-dom-update-repaint.png" alt=""></p>
<p>React의 랜더링 과정은 위 그림과 같다. </p>
<ul>
<li>처음 랜더링시 <code>React</code>는 <code>Virtual DOM</code>을 생성한다.</li>
<li>이후 상태(state) 나 속성(props)이 변경될 때마다 새로운 <code>Virtual DOM</code>을 생성한다.</li>
<li>변경된 Virtual DOM 과 이전 Virtual DOM을 비교하여 <code>변경된 부분</code>을 찾아낸다</li>
<li>변경된 부분을 실제 DOM에 반영하며 이것을 <code>확정(Commit)</code> 이라고 한다.</li>
<li>실제 DOM을 직접 조작하는 대신 변경된 부분만 <code>최소한의 작업으로 업데이트</code>한다.</li>
<li><code>확정(Commit)</code> 단계가 끝나면 브라우저는 실제 DOM을 업데이트하고 <code>변경된 내용이 화면에 반영</code>된다.</li>
</ul>
<p>이처럼 virtual DOM을 사용하면 실제 DOM을 조작하는것 보다 효율적으로 화면을 랜더링 할 수 있다. </p>
<p>여기서 문제는 사소한 변경(state, props)이 생기기라도 하면 <code>virtual DOM</code>을 처음부터 다시 그린다는 것이다.  모든 컴포넌트가 변경될 일이 없기 때문에 , React에서는 컴포넌트를 메모이제이션 하여 불필요한 리랜더링을 방지 할 수 있다. </p>
<p>예를들어 부모컴포넌트의 사소한 변경에 자식컴포넌트들이 전부다 재 랜더링되는건 상당한 낭비라고 할 수 있다. </p>
<br/>

<br/>

<h1 id="⭐-reactmemo">⭐ React.memo</h1>
<hr>
<pre><code class="language-java">const MyComponent = React.memo((props) =&gt; {
  // props를 이용한 컴포넌트 렌더링
});

// 사용법 예시
&lt;MyComponent prop1={value1} prop2={value2} /&gt;</code></pre>
<p>react.memo의 사용은 컴포넌트 전체를 감싸면서 시작된다. React.memo로 감싼 컴포넌트는 props가 변경되지 않는다면 해당컴포넌트는 다시 virtual DOM을 그릴 때 다시 만들지 않고 메모이제이션한 컴포넌트를 재 사용하게 된다.  </p>
<br/>

<br/>

<h1 id="⭐-usememo">⭐ useMemo</h1>
<hr>
<pre><code class="language-java">const memoizedValue = useMemo(() =&gt; {
  // 계산할 값 또는 함수
}, [dependency1, dependency2]);</code></pre>
<p>useMemo는 특정 값의 계산을 메모이제이션하여 의존성이 변경되지 않는 한 이전에 계산한 결과를 재사용한다. </p>
<h3 id="📌-불필요한-재계산-생략">📌 불필요한 재계산 생략</h3>
<p>useMemo는 주로 계산이 <code>오래걸리는 작업의 결과를 보존</code>할 때 사용한다. 그렇지 않으면 컴포넌트를 재 랜더링할 때마다 계산을 다시 해야하기 때문이다. </p>
<pre><code class="language-java">import React, { useState, useMemo } from &#39;react&#39;;

const SumCalculator = ({ n }) =&gt; {
  // 1부터 n까지의 합을 계산하는 함수
  const calculateSum = (n) =&gt; {
    console.log(&#39;Calculating sum...&#39;);
    let sum = 0;
    for (let i = 1; i &lt;= n; i++) {
      sum += i;
    }
    return sum;
  };

  // useMemo를 사용하여 결과를 메모이제이션
  const sum = useMemo(() =&gt; calculateSum(n), [n]);

  return (
    &lt;div&gt;
      &lt;p&gt;1부터 {n}까지의 합: {sum}&lt;/p&gt;
    &lt;/div&gt;
  );
};

export default SumCalculator;</code></pre>
<p>다음 코드는 1~n까지의 합을 계산하는 함수의 값을 메모이제이션을 사용하여 재사용하고 있다. 만약 useMemo를 사용하지 않는다면 해당 컴포넌트를 사용할 때마다 <code>SumCalculator</code>함수를 내부 로직이 다시 수행될 것이다. </p>
<p>위의 예시에서는 n의 값이 변경되지 않는이상 sum은 메모이제이션된 값을 사용하게 된다. </p>
<br/>

<br/>

<h1 id="⭐-usecallback">⭐ useCallback</h1>
<hr>
<pre><code class="language-java">const memoizedCallback = useCallback(() =&gt; {
  // 콜백 함수 로직
}, [dependency1, dependency2]);</code></pre>
<p>React에서 컴포넌트가 리랜더링 될 때 , 함수 컴포넌트는 내부에 선언된 <code>모든 함수를 새로 생성</code>하게 된다. </p>
<h3 id="📌-불필요한-리랜더링-방지">📌 불필요한 리랜더링 방지</h3>
<p>부모 컴포넌트가 리랜더링되면 자식 컴포넌트도 함께 리랜더링 된다. 이 때 자식컴포넌트의 함수가 불필요하게 리랜더링 되는것을 방지하기 위해 사용한다. </p>
<h3 id="📌-자식-컴포넌트에-전달하는-콜백함수-재생성-방지">📌 자식 컴포넌트에 전달하는 콜백함수 재생성 방지</h3>
<p>컴포넌트는 <code>state</code>나 <code>props</code>가 변경되면 리랜더링하게 되어있다. </p>
<p>그럴 때마다 컴포넌트에 모든 함수를 재생성하는데 만약 그 재생성 함수가 자식컴포넌트의 <code>props</code>로 전달하는 함수라면 이전 함수와 형태는 동일하지만 <code>다른 객체로 판단</code>하기 때문에 자식 컴포넌트는 불필요하게 리랜더링이 되고 만다. </p>
<p>다음 예시는 <code>useCallback</code>을 사용하는 방법을 보여주고 있다.  </p>
<p>자식 컴포넌트</p>
<pre><code class="language-java">import React from &#39;react&#39;;

// React.memo를 통해 컴포넌트 전체를 메모이제이션 했다. 
const ChildComponent = React.memo(({ onClick }) =&gt; {
  console.log(&#39;ChildComponent rendered&#39;);

  return (
    &lt;button onClick={onClick}&gt;Click me&lt;/button&gt;
  );
});

export default ChildComponent;</code></pre>
<p>자식 컴포넌트는 리랜더링하지 않기 위해 React.memo까지 사용했다.</p>
<p>useCallback을 사용하지 않은 부모 컴포넌트</p>
<pre><code class="language-java">import React, { useState, useCallback } from &#39;react&#39;;
import ChildComponent from &#39;./ChildComponent&#39;;

const ParentComponent = () =&gt; {
  const [count, setCount] = useState(0);

// ParentComponent가 리랜더링 될때마다 handleClick은 재생성된다. 
  const handleClick = function(){
    setCount((prevCount) =&gt; prevCount + 1);
  }

  return (
    &lt;div&gt;
      &lt;ChildComponent onClick={handleClick} /&gt;
      &lt;p&gt;Count: {count}&lt;/p&gt;
    &lt;/div&gt;
  );
};

export default ParentComponent;</code></pre>
<p>다음은 <code>useCallback</code>을 사용하지 않은 부모 컴포넌트의 예시이다. 자식 컴포넌트에 있는 button을 누를 때마다 count의 값은 변경되고 <code>ParentComponent</code>는 <code>리랜더링</code> 된다. 이때 <code>handleClick</code>도 재생성하게 된다. 새로 생성된 함수는 이전함수와 형태는 같지만 다른함수로 취급하기 때문에 해당 <code>handleClick</code> 함수를 <code>props</code>로 전달 받은 <code>ChildComponent</code>는 똑같 형태의 함수를 내려받았음에도 재랜더링되는 참사가 발생한다.  </p>
<p>useCallback을 사용한 부모 컴포넌트</p>
<pre><code class="language-java">import React, { useState, useCallback } from &#39;react&#39;;
import ChildComponent from &#39;./ChildComponent&#39;;

const ParentComponent = () =&gt; {
  const [count, setCount] = useState(0);

 // 자식에게 넘겨줄 콜백함수를 useCallback을 사용해 메모이제이션 했다
  const handleClick = useCallback(() =&gt; {
    setCount((prevCount) =&gt; prevCount + 1);
  }, []);

  return (
    &lt;div&gt;
      &lt;ChildComponent onClick={handleClick} /&gt;
      &lt;p&gt;Count: {count}&lt;/p&gt;
    &lt;/div&gt;
  );
};

export default ParentComponent;</code></pre>
<p><code>useCallback</code>을 사용했기 때문에 부모컴포넌트가 재랜더링 되더라도 <code>ChildComponent</code>는 항상 같은 <code>handleClick</code> 함수를 받게 된다 때문에 <code>ParentComponent</code>가 리랜더링 되더라도 <code>React.memo</code>를 사용한 <code>ChildComponent</code>는 리랜더링 되지 않고 메모이제이션된 값을 사용할 수 있다.</p>
<br/>

<blockquote>
<p>이런 메모이제이션도 주의할 점이 있다. 자주 변경되는 컴포넌트나 함수에 메모이제이션을 사용하게 되면 계속해서 메모이제이션(캐싱)하는 비용이 발생하기 때문에 주의해야한다. </p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[URL, URI 차이점]]></title>
            <link>https://velog.io/@giant_toothpick/URL-URI-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@giant_toothpick/URL-URI-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Mon, 31 Jul 2023 15:25:08 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-url와-uri-어떤-차이점이-있을까">⭐ URL와 URI, 어떤 차이점이 있을까?</h1>
<blockquote>
<p>‘URL’은 우리에게 참 익숙한 용어이다. 흔히 <code>[웹사이트 주소]</code>로 많이 알고 있다. 그렇다면 URI는 무엇일까?</p>
</blockquote>
<br/>

<h1 id="⭐-uri">⭐ URI</h1>
<p><code>Uniform Resource Identifier</code> : 우리말로는 <strong>통합 자원 식별자</strong> 라고 한다. </p>
<p><img src="https://lh3.googleusercontent.com/jPekKtjGIO0KYv51gbnSYiYFQXSQz5ajwaFUbFszpdxbRBOSyx_TpLL_2yhs2vUABP_WM9Vp11iiVF5snnmEDYpvAhwt3NplXZubmBDzoGLW-EplIroCL69FQynBQW4E6bTqHk9134n7KjsI-Xi4QHbiN37VrE52wzudENjf2MxiXcE4pzBL5-5a1gltyg" alt=""></p>
<ul>
<li><code>Uniform</code>: 리소스를 식별하는 통일된 방식</li>
<li><code>Resource</code>: URL로 식별이 가능한 모든 종류의 자원</li>
<li><code>Identifier</code>: 다른 항목과 구분하기 위해 필요한 정보</li>
</ul>
<p>즉 <code>URI</code>는 인터넷 상의 리소스 “<code>자원 자체</code>”를 식별하는 고유한 문자열 시퀀스다</p>
<p>고유하게 식별만 할 수 있으면 된다.</p>
<br/>

<h1 id="⭐-url">⭐ URL</h1>
<p><code>Uniform Resource Locator</code> : 네트워크상에서 통합 <code>자원(리소스)의 위치</code>를 나타내기 위한 규약</p>
<p>식별자와 위치를 동시에 보여준다. </p>
<p><img src="https://lh6.googleusercontent.com/nuHLpaeUsfAHxF_KaBVv0umHv3WDEiCFXgkKvNaImc5iu-zAxfgfV8CgW8XfWvRfCmtDRR7-K0wn6_0Z2eR6yeaOaW_P4AeD8by3g7IWhgT0cbTi_oVU3IUdzQbbBgSQGChIOtfst5ny1NvvA8GdhV5meyxMTulXRpiyVYt-Q5V8VV3Bhr0wkJO6PvaHhw" alt=""></p>
<p><strong>웹 사이트 주소 + 컴퓨터 네트워크 상의 자원</strong></p>
<p>이는 웹사이트 주소뿐만 아니라 컴퓨터 네트워크 상의 자원을 모두 나타내는 표기법이다. </p>
<p>특정 웹 페이지의 주소에 접속하기 위해서는 웹 사이트의 주소뿐만 아니라 <code>프로토콜(https, http, sftp, smp 등)</code>을 함께 알아야 접속이 가능한데, <code>이들을 모두 나타내는 것이 URL</code> 이다. </p>
<br/>

<h1 id="⭐-uri와-url의-차이점">⭐ URI와 URL의 차이점</h1>
<blockquote>
<p>URI = 식별자
URL= 식별자+위치</p>
</blockquote>
<ul>
<li><a href="http://example.com">example.com</a> : URI 이다. 리소스 이름만 나타내기 때문이다. (고유하게 식별 가능하다)</li>
<li><a href="https://example.com">https://example.com</a> : URL 이다. 이름과 더불어 어떻게 도달할 수 있는지 <code>위치(프로토콜)</code>까지 함께 나타내기 때문이다. (이 또한 고유하게 식별 가능하기 때문에 URI가 될 수 있다. )</li>
</ul>
<br/>

<h3 id="✏️-url은-일종의-uri이다">✏️ URL은 일종의 URI이다.</h3>
<p><img src="https://lh6.googleusercontent.com/2dC-UX4G66CKAuXJuN0JIYmwo7rWdV8cxhOXllbn75D1zNcPqZjgjdusnFpSfuZRaY3cWUzZCZne7TRVhONhXnDiD8OLkGpQIxnu7N35P-20o6I8onKmc2mju_i3HdAK7vtjATNGdUPKlsoFoIMTFwDXUTCJShY9GwsnFgzkgPYPPK8rVJJgU-toMbilow" alt=""></p>
<p><code>URI</code>가 좀 더 포괄적인 개념이며 <code>URL</code>은 이 안에 포함된다. 
이 것은 URL가 URI로 불릴 수도 있다는 뜻이다. </p>
<br/>

<h3 id="✏️-url은-프로토콜과-결합한-형태이다">✏️ URL은 프로토콜과 결합한 형태이다.</h3>
<blockquote>
<p><a href="http://example.com">http://example.com</a> =&gt; URL</p>
</blockquote>
<p>즉, 어떻게 위치를 찾고 도달할 수 있는지까지 포함되어야 하기 때문에 URL은 <code>프로토콜 + 이름(또는 번호)</code>의 형태여야만 한다. </p>
<p>프로토콜(protocol)이란, 리소스에 접근하는 방법을 지정하는 방식이다. 서로다른 네트워크기기에서 정보를 주고 받기 위해서는 어떻게 정보를 주고 받을지 약속이 필요한데 이 약속을 프로토콜이라고 한다. </p>
<p>일반적으로 <code>http</code>, <code>https</code>, <code>ftp</code>등이 여기에 해당한다. </p>
<blockquote>
<p>example.com ⇒ URI
https;//example.com ⇒ URI, URL</p>
</blockquote>
<p>URI는 그 자체로 <code>이름(example.com)</code> 이 거나, <code>이름 + 위치(https://example.com)</code>를 나타낸 형태 모두 해당한다. </p>
<br/>

<h1 id="⭐-url-uri-구조">⭐ URL URI 구조</h1>
<p><img src="https://lh4.googleusercontent.com/K9t30Sjp6cIbsLg67B-amTH927yS3q06E68DvSLK_4t364b38otQLLibFbyj_I0JH6fLB8tp5zEgVObcaSj810X1Xs2XWuwxD-_ibK8JtfsUaGgm5JNVhUhfjcH3vairIchwhKfviglEmKYYk5aH4cc74ncEaW6N9fRS5VZtKS4EGmvGSYIUSLvx04r0xQ" alt=""></p>
<ul>
<li><code>Scheme</code> : 리소스에 접근하는 데 사용할 프로토콜</li>
<li><code>Host</code> : 접근할 대상(서버)의 호스트 명</li>
<li><code>Path</code> : 접근할 대상(서버)의 경로에 대한 상세 정보</li>
</ul>
<blockquote>
<p><code>URN(Uniform Resource Name)</code>
리소스의 위치, 프로토콜, 호스트 등과는 상관없이 각 자원에 이름을 부여한것, 즉 URL은 어떤 특정 서버에 있는 웹 문서를 가리키는 반면, URN은 웹 문서의 물리적인 위치와 상관없이 웹 문서 자체를 나타낸다.</p>
</blockquote>
<p><br/><br/></p>
<h1 id="⭐-결론">⭐ 결론</h1>
<p>URL : 프로토콜이 붙어 있는 주소!
URI : 리소스를 식별할 수 있는 고유한 문자열!</p>
<p><br/><br/></p>
<blockquote>
<p>참고 블로그
<a href="https://www.elancer.co.kr/blog/view?seq=74">https://www.elancer.co.kr/blog/view?seq=74</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저에서 웹페이지를 그리는 과정]]></title>
            <link>https://velog.io/@giant_toothpick/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EA%B7%B8%EB%A6%AC%EB%8A%94-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@giant_toothpick/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-%EC%9B%B9%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EA%B7%B8%EB%A6%AC%EB%8A%94-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Fri, 28 Jul 2023 06:32:59 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-브라우저에서-웹페이지를-그리는-과정">⭐ 브라우저에서 웹페이지를 그리는 과정</h1>
<br/>

<p>평소에 너무 자연스럽게 브라우저를 키고 웹서핑을 해왔지만 그 과정이 어떻게 동작하는지는 자세하게 알아본적이 없는것 같다. frontend개발자를 꿈꾸는 취준생으로서 반드시 알아야 할 필요가 있다고 느꼈다. </p>
<br/>

<h1 id="⭐-응답-파일-해석하기">⭐ 응답 파일 해석하기</h1>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/14.jpg" alt=""></p>
<p>브라우저는 웹페이지를 그리기위해 <code>정적 리소스 (html,css,javascript)</code>를 서버로 부터 가져온다.  해당 파일을 브라우저가 읽고 해석하는 과정을 알아보자 </p>
<br/>

<h3 id="📌-dom-생성하기">📌 DOM 생성하기</h3>
<p>모든 브라우저는 서버에서 html파일을 받으면 브라우저가 이해할 수 있는 방식으로 변환한다. 이것을 <code>파싱</code> 이라고한다.</p>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/0321_----------------_server-copy-1.jpg" alt=""></p>
<p>브라우저는 HTML을 쪼개서 <code>DOM(Document Object Model)</code>이라고 불리는 트리를 만든다. 줄여서 Dom Tree라고 부른다. DOM은 브라우저가 웹페이지를 표현하는 뼈대이며, javascript를 통해 동적으로 변경할 수 있는 API를 제공하기도 한다. (document.getElementById .. 등) </p>
<p>브라우저가 DOM을 다 만들었다면 <code>DOMContentLoaded</code>라는 이벤트를 발생시킨다.  이 이벤트를 최대한 빨리 발생시키는것이 좋은 웹성능을 위한 조건이다. </p>
<blockquote>
<p>DOMContentLoaded : 브라우저가 DOM을 다 만들때 발생되는 이벤트이며, DOM이 만들어지기 전에 javascript가 동작하는것을 막기 위해 사용하는 이벤트이다.</p>
</blockquote>
<br/>

<h3 id="📌-cssom-생성하기">📌 CSSOM 생성하기</h3>
<p>이제 뼈대를 그렸으니 살을 붙일 차례이다. 웹페이지는 html파일로만 이루어져있지 않다. 디자인, 스타일, 반응형을 구현하기 위해서는 CSS가 필요하다. </p>
<p>CSS는 브라우저가 HTML을 파싱하면서 <code>&lt;link&gt; 태그</code>를 만나면 요청되는데, HTML과 마찬가지로 CSS도 브라우저가 이해할 수 있게 파싱해야한다. </p>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/0321_----------------_server-copy-2-1.jpg" alt=""></p>
<p>브라우저는 CSS파싱하면서 <code>CSSOM(SCC Object Model)</code>이라고 불리는 트리를 만든다. 위에서 살펴본것과 동일하다. </p>
<p>각각의 DOM 노드에 색상 크기 위치 배치 방법등을 결정하는 정보들이 있으며, CSSOM 생성은 별개의 스레드에서 이루어지기 때문에 DOM 생성과정을 방해하지 는 않는다. </p>
<br/>

<h3 id="📌-javascript-실행하기">📌 JavaScript 실행하기</h3>
<p>javascript는 브라우저가 HTML 파싱중<code>&lt;script&gt;</code> 태그를 만나면 요청된다. JS는 CSS와 다르게 코드를 해석하고 실행 완료가 될 때까지 DOM 생성을 멈춰버린다! </p>
<p>이는 document.write() 같은 함수로 <code>DOM을 변경할 여지</code>가 있기 때문이다. </p>
<p>JS가 너무 커서 실행이 오래걸린다면, 그만큼 HTML 파싱과 DOM 생성이 지연되고 성능에도 안좋은 영향을 끼친다. </p>
<p>때문에 늘 최적화 방법(async,defer 사용)을 고려해야한다.</p>
<p>보통 <code>&lt;script&gt;</code>태그를 제일 밑에 놔두는 이유도 바로 이때문이다. </p>
<br/>

<h3 id="📌-render-tree로-합치기">📌 Render Tree로 합치기</h3>
<p>HTML, CSS, JS를 잘 처리해서 DOM과 CSSOM을 만들었다면 최종적으로 어떤 요소를 어떻게 그릴지 결정해야한다. </p>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/0321_----------------_server-copy-3.jpg" alt=""></p>
<p>Render Tree는 브라우저가 최종적으로 그릴 요소를 알려준다.  예를 들면 DOM의 script, meta 태그와 CSS의 display:none은 보이지 않는다. </p>
<p>따라서 최종적으로 그려야할 요소만 남게 된다. </p>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/0321_----------------_server-copy-6.jpg" alt=""></p>
<p>CSS와 JS는<code>render-blocking resources</code>라고 한다. JS는 DOM의 생성 과정을 늦추고 CSS는 CSSOM이 모두 생성되어야 Render Tree를 만들수 있기 때문이다. 또 CSSOM생성은 JS의 실행을 멈추기도하고 JS가 너무 크면 Render Tree를 만드는 시점이 지연되기도 한다. </p>
<p>이를 최대한 제거하거나 최적화하는것이 웹성능을 위해 아주 중요한 작업이다. </p>
<h1 id="⭐--웹-페이지-그리기">⭐  웹 페이지 그리기</h1>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/15.jpg" alt=""></p>
<p>브라우저가 <code>Render Tree</code>를 그리는 과정은 <code>Layout → Paint → Composite</code>순서 대로 진행되며 이것을 <code>렌더링 파이프라인</code> 이라고 한다. </p>
<p>앞의 과정은 DOM을 만드는 과정이였다면 이제는 실제로 브라우저에 사이트를 그리는 작업이다. 때문에 시간도 많이 걸리게 된다. </p>
<p>이 과정을 잘 이해하고 있으면 웹 성능을 효과적으로 계선할 수 있다. </p>
<h3 id="📌-크기와-위치-계산하기-layoutreflow">📌 크기와 위치 계산하기-Layout(Reflow)</h3>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/16.jpg" alt=""></p>
<p>layout은 웹 페이지를 기준으로 각 <code>요소 위치와 크기를 결정</code>한다. 윈도우의 크기, <code>Render Tree</code>에서 구했던 스타일등을 적용하게 된다. </p>
<p>이 과정이 처음 일어날 때는 <code>Layout</code>이라고 하지만 JS 등에 의해 <code>재계산</code>이 될 경우 <code>Reflow</code>라고 부른다. </p>
<p><code>Reflow</code> 는 렌더링 파이프라인에서 Paint와 Composite까지 다시 진행하기 때문에 계산이 가장 많이 드는 작업이다. </p>
<h3 id="📌-화면에-색칠하기-paintrepaint">📌 화면에 색칠하기-paint(repaint)</h3>
<p>layout으로 요소들의 크기와 위치를 정했다. paint는 텍스트, 색상, 테두리, 그림자, 등 요소의 <code>시각적인 부분을 그리는 작업</code>이다. 브라우저는 paint 과정을 매우 빠르게 진행하기 때문에 성능에서 크게 중요하지 않다. </p>
<p><code>Reflow</code>에 의해 Paint과정이 재계산된다면 <code>Repaint</code>라고 부른다.</p>
<h3 id="📌-레이어-합치기-composite">📌 레이어 합치기-Composite</h3>
<p><img src="https://blog.imqa.io/content/images/size/w1000/2022/03/0321_----------------_server-copy-5.jpg" alt=""></p>
<p>겹치는 레이어가 생길 때, 각 레이어를 하나로 합치는 과정을 <code>Composite</code>이라고 한다. paint과정은 단순히 색을 칠했다면 composite과정은 요소들을 <code>정확한 순서로 화면에 그리는 작업</code>을 하게 된다. </p>
<p>특히 요소들이 겹칠 때 이 과정은 매우 중요하다. </p>
<p>Composite이 끝나면 드디어 <code>화면에 페이지가 렌더링</code> 된다. </p>
<blockquote>
<p>참고 블로그
<a href="https://blog.imqa.io/webpage_loading_process/">https://blog.imqa.io/webpage_loading_process/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저 www.example.com을 입력하면 일어나는 일]]></title>
            <link>https://velog.io/@giant_toothpick/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-www.example.com%EC%9D%84-%EC%9E%85%EB%A0%A5%ED%95%98%EB%A9%B4-%EC%9D%BC%EC%96%B4%EB%82%98%EB%8A%94-%EC%9D%BC</link>
            <guid>https://velog.io/@giant_toothpick/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-www.example.com%EC%9D%84-%EC%9E%85%EB%A0%A5%ED%95%98%EB%A9%B4-%EC%9D%BC%EC%96%B4%EB%82%98%EB%8A%94-%EC%9D%BC</guid>
            <pubDate>Fri, 28 Jul 2023 04:50:48 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-브라우저-wwwexamplecom을-입력하면-일어나는-일">⭐ 브라우저 <a href="http://www.example.com%EC%9D%84">www.example.com을</a> 입력하면 일어나는 일</h1>
<hr>
<blockquote>
<p><a href="http://www.example.com">www.example.com</a> 을 인터넷에 입력하게 되면 우리에게 친숙한 웹페이지 화면이 보이게 된다. 도대체 어떻게 우리에게 웹페이지가 전달 되는 것일까?</p>
</blockquote>
<h1 id="⭐-전체적인-동작-방식">⭐ 전체적인 동작 방식</h1>
<hr>
<p>전체적으로 어떻게 동작하는지 알아보자 </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/cb3868b5-2afc-46d4-b25b-88393062090e/image.png" alt="그림은 밑의 순서와는 상관이 없습니다."></p>
<br/>

<h3 id="📌-1-dnsdomain-name-system-조회">📌 1. DNS(Domain Name System) 조회</h3>
<p>먼저 브라우저는 입력된 주소 [“<a href="http://www.example.com%E2%80%9D%5D%EC%9D%84">www.example.com”]을</a> ip 주소로 변환해야 한다. </p>
<blockquote>
<p>IP 주소는 인터넷 프로토콜(IP) 기반의 네트워크에서 장치들이 서로를 식별하고 통신할 수 있도록 사용되는 고유한 주소</p>
</blockquote>
<p>이를 위해 브라우저는 DNS(Domain Name System) 서버에 해당 도메인 이름 [“<a href="http://www.example.com%E2%80%9D%5D">www.example.com”]</a> 을 ip 주소로 변환해 달라고 요청한다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/d525eb56-a00c-4a3c-a7c6-80d7cffcd669/image.png" alt=""></p>
<blockquote>
<p>홈플러스(“<a href="http://www.example.com%E2%80%9D">www.example.com”</a>) 전화번호(”93.184.216.34”)를 모를 때 114(DNS)에 전화해서 홈플러스(“<a href="http://www.example.com%E2%80%9D">www.example.com”</a>) 전화번호(”93.184.216.34”)를 묻는다고 생각하면 된다.</p>
</blockquote>
<br/>

<h3 id="📌-2-dns-서버의-응답">📌 2. DNS 서버의 응답</h3>
<p>DNS 서버는 도메인 이름에 해당하는 IP 주소를 가지고 있거나 다른 DNS 서버로 부터 정보를 찾아 응답한다. 이제 부라우저는 DNS 서버로 부터 받은 [”<a href="http://www.example.com%E2%80%9D%5D">www.example.com”]</a> 의 ip주소(”93.184.216.34”) 주소를 사용하여 웹사이트에 접속하게 된다. </p>
<br/>

<h3 id="📌-3-tcp-연결-설정">📌 3. TCP 연결 설정</h3>
<p>브라우저는 웹사이트의 IP 주소로 TCP 연결을 설정한다. 웹 사이트에 대한 HTTP 요청을 보내기 위해서는 <code>TCP/IP 연결</code>이 필요하기 때문이다. </p>
<blockquote>
<p><code>TCP/IP</code> :  네트워크 통신에 사용되는 프로토콜(약속, 규약)의 집합으로, 인터넷을 포함한 다양한 컴퓨터 네트워크에서 데이터를 주고받을 때 사용한다.</p>
</blockquote>
<p>여기서 요청이란 너희 웹페이지를 보고 싶으니 정적 리소스(html,css,javascript)를 달라는 요청이라고 하자 </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/088fbbd4-dbd4-484c-b97c-5bf6d84f5446/image.png" alt=""></p>
<p>TCP 프로토콜의 연결 설정은 그림과 같이 이루어진다. 3번 패킷을 주고 받기 때문에 <code>three-way handshake</code> 라고도 불린다. </p>
<p>이 과정을 통해 서버는 <code>요청을 받을 준비(개방 : Passive open)</code>를 하고 클라이언트는 서버와 <code>연결을 할 준비(요청 : Active open)</code>를 할 수 있게 된다. </p>
<br/>

<h3 id="📌-4-http-요청">📌 4. HTTP 요청</h3>
<p>TCP 연결이 설정되면, 브라우저는 웹사이트 서버로 HTTP 요청을 보낸다. HTTP 요청에는 웹페이지를 가져오기 위한 정보가 포함되어있다.</p>
<br/>

<h3 id="📌-5-서버의-응답">📌 5. 서버의 응답</h3>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/7c5d1596-e01b-480e-afc7-f1676eac73ca/image.png" alt=""></p>
<p>웹사이트를 제공하는 서버는 클라이언트(브라우저)에게 받은 HTTP요청에 대해 처리하고, 필요한 웹페이지 파일과 리소스(html, css, javascript)를 브라우저에 전송한다.</p>
<br/>

<h3 id="📌-6-웹페이지-로딩">📌 6. 웹페이지 로딩</h3>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/75dfce46-8b1a-4918-a3e4-46d3c7878782/image.png" alt=""></p>
<p>브라우저는 서버로 부터 받은 웹페이지 파일과 리소스(html, css, javascript)를 해석하고 웹페이지를 화면에 표시한다. 이후에 추가적인 리소스가 필요하다면 위와 같은 작업을 거쳐 다시한번 추가적으로 리소스를 다운로드한다. </p>
<br/>

<h3 id="📌-7--웹서핑-ㄱㄱ">📌 7 . 웹서핑 ㄱㄱ</h3>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/2c60e7e5-c199-4150-83ad-a9027c3e71ed/image.png" alt=""></p>
<p>이런 과정을 거쳐 우리는 웹페이지를 사용할 수 있게 된다. </p>
<br/>

<br/>

<br/>

<blockquote>
<p>참고 블로그</p>
<p><a href="https://m.blog.naver.com/trvlnbh/222102617762">https://m.blog.naver.com/trvlnbh/222102617762</a></p>
<p><a href="https://velog.io/@jaeyunn_15/Network-www.naver.com%EC%9D%84-%EC%B9%98%EB%A9%B4-%EC%9D%BC%EC%96%B4%EB%82%98%EB%8A%94-%EC%9D%BC">https://velog.io/@jaeyunn_15/Network-www.naver.com을-치면-일어나는-일</a></p>
<p><a href="https://blog.imqa.io/webpage_loading_process/">https://blog.imqa.io/webpage_loading_process/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP IP로 데이터 전송]]></title>
            <link>https://velog.io/@giant_toothpick/TCP-IP%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%86%A1</link>
            <guid>https://velog.io/@giant_toothpick/TCP-IP%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%86%A1</guid>
            <pubDate>Thu, 27 Jul 2023 09:29:27 GMT</pubDate>
            <description><![CDATA[<h1 id="⭐-tcp-ip-란">⭐ TCP IP 란</h1>
<p><code>tcp/ip</code>는 두개의 주요 프로토콜인 <code>TCP(Transmission Control Protocol)</code>와 <code>IP(Internet Protocol)</code>의 이름을 합쳐서 부른것이다. </p>
<p>인터넷을 포함한 다양한 컴퓨터 네트워크에서 데이터를 주고 받을 때 사용하며, 가장 널리 사용되는 인터넷 표준으로 알려져있다. </p>
<br/>

<h1 id="⭐-인터넷에서-데이터를-전송하는-방법">⭐ 인터넷에서 데이터를 전송하는 방법</h1>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/5e35896b-8b76-4aa8-82db-03e9eda9c51a/image.png" alt=""></p>
<p><code>tcp/ip의 4계층 프로토콜</code>을 구현하는 소프트웨어만 있으면 다른 종류의 호스트와도 통신을 할 수 있다. </p>
<p>클라이언트가 서버에게 서비스를 요청하면 클라이언트가 송신측이 되는거고, 서버가 데이터를 수신하는쪽이 된다. 반면 서버가 서비스를 제공하면 송신하는 쪽이 되고, 이때는 클라이언트가 수신측이 된다. </p>
<p>tcp/ip는 4개의 계층이 존재하는데 아래위로 인접한 계층과 데이터를 주고 받으면서 통신을 진행한다. 응용계층부터 네트워크 인터페이스까지 순서 대로 데이터가 거쳐가며 데이터가 전송된다. </p>
<br/>

<h1 id="⭐-송신-호스트의-데이터-전송-과정">⭐ 송신 호스트의 데이터 전송 과정</h1>
<br/>

<h2 id="📌-헤더-추가와-캡슐화">📌 헤더 추가와 캡슐화</h2>
<p>각계층마다 처리할 프로토콜이 존재한다. 각자 맡은 역할이 있다는 뜻이다. 상위 계층으로 부터 받은 데이터에 헤더라는 정보를 붙여 하위계층으로 넘기게 된다. <strong>헤더는 수신호스트의 해당 계층에서 데이터를 처리할 때 필요한 정보가 담겨있다.</strong> 예를 들면 물리주소, ip주소, mac주소, 포트번호, 등이 이에 해당한다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/8190a0fd-cf74-4a65-9a2b-c99b06ace23d/image.png" alt=""></p>
<p>그림처럼 각 계층을 지날 때마다 앞에 붙는 정보를 <code>헤더</code>라고 한다. 송신지 호스트에서 각 계층을 지날 때마다 해당 계층의 프로토콜(역할)을 수행하고 데이터에 해더를 추가하는것을 <code>캡슐화</code> 라고 한다. </p>
<p>수신자 호스트는 이 캡슐화한 데이터의 헤더 부분을 보고 데이터를 처리한다. 이를 통해 상대방과 동일한 프로토콜을 사용할 수 있는 것이다. </p>
<br/>

<h2 id="📌-캡슐화-후-데이터-전송">📌 캡슐화 후 데이터 전송</h2>
<p>송신 호스트에서는 응용 → 전송 → 인터넷 → 네트워크 인터페이스 계층의 순서대로 데이터가 이동한다.  각 계층을 지날 때마다 헤더가 추가 된다. 한 계층을 지날 때마다 계속 캡슐화가 되는것이다. 최하위 계층(네트워크 인터페이스) 에서 이 디지털 데이터(캡슐화한 데이터)를 전기 신호로 변환해서 라우터 등 중간노드를 거쳐 수신호스트의 최하위 계층인 네트워크 인터페이스 계층으로 전달되게 된다. </p>
<br/>

<h1 id="⭐-수신-호스트의-데이터-전송-과정">⭐ 수신 호스트의 데이터 전송 과정</h1>
<br/>

<h2 id="📌-헤더-제거와-역캡슐화">📌 헤더 제거와 역캡슐화</h2>
<p>송신 호스트의 각 계층에서 맡은 역할에 따라 프로토콜을 처리한다. 이 때 헤더 부분의 정보를 사용하게 된다. 프로토콜에 사용된 헤더는 다음계층으로 올라갈 때 제거 된다. 데이터가 하위 계층에서 상위 계층으로 올라갈 수록 헤더가 제거되므로 최종적으로 송신측에서 보낸 데이터만 남게 된다. 이처럼  각 계층을 지날 때마다 해당 계층의 프로토콜을 처리하고 데이터의 헤더가 제거되는것을 <code>역캡슐화</code> 라고한다. </p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/0e468dd6-0647-49f1-8f3d-69cb1305ba10/image.png" alt=""></p>
<br/>

<h2 id="📌-역캡슐화와-데이터-전송">📌 역캡슐화와 데이터 전송</h2>
<p>전송 매체를 통해 전기 신호를 받은 수신 호스트의 네트워크 인터페이스 계층은 이 전기 신호를 디지털 데이터로 변환한다. </p>
<p>수신 호스트에서 데이터는 네트워크 인터페이스 → 인터넷 → 전송 → 응용 계층 순서대로 이동한다. 각 계층을 지날 때마다 <code>송신호스트가 추가한 헤더</code> 정보를 읽고 해당 프로토콜을 수행한다음 사용한 헤더를 제거한 데이터를 상위 계층으로 넘긴다.  최종적으로는 여러개의 헤더를 삭제하고 송신호스트가 보낸 원본 데이터를 응용계층에서 사용하게 된다. </p>
<br/>

<h1 id="⭐-tcp--ip에-의한-데이터-전송">⭐ TCP / IP에 의한 데이터 전송</h1>
<p>캡슐화에는 독특한 특성이 있다. 그 것은 한계층에서 추가된 헤더를 다른 계층에 볼 수 없게 할 수 있는 <code>은닉 효과</code>가 있다는 것이다.  다른 계층의 헤더를 볼 수 없기 때문에 헤더는 송신 호스트에서 추가된 헤더는 수신 호스트의 동일한 계층에서만 사용된다. </p>
<p>이렇게 되면 같은 계층끼리 직접 데이터를 주고 받은것 처럼 되는것이다. </p>
<p>같은 계층끼리 데이터를 사용하는 데이터를 구분해서 부르는 것을 PUD(Protocol Data Unit) 이라고 한다. <code>응용 계층에서는 메시지(Message)</code>, <code>전송 계층에서는 세그먼트(Segment)</code>, <code>인터넷 계층에서는 데이터그램(Datagram) 또는 패킷(Packet)</code>, <code>네트워크 인터페이스 계층에서는 프레임(Frame)</code>이라고 부른다.</p>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/d504b31f-e775-4a38-8f3f-eb5702b71ca7/image.png" alt=""></p>
<br/>

<h1 id="⭐-tcpip에-의한-데이터-처리-과정">⭐ TCP/IP에 의한 데이터 처리 과정</h1>
<p><img src="https://velog.velcdn.com/images/giant_toothpick/post/5bc29a9a-6e09-4b9e-ad23-e3ea2e7a2b3e/image.png" alt=""></p>
<p>각 계층마다 추가되는 헤더 정보는 다 다르지만 적어도 송신지와 수신지의 주소, 헤더에 이어지는 데이터의 종류를 알려주는 식별자 정보가 있습니다. </p>
<p>이더넷 헤더엔 MAC주소가, IP 헤더엔 IP 주소가, TCP 헤더엔 포트 번호라는 주소 정보가 포함되어있다. 응용 계층은 사용하는 프로토콜에 따라 다르지만 이메일의 메일 주소와 같은 주소가 사용되는 경우가 있다. </p>
<p>헤더에 이어지는 데이터의 종류를 알려주는 식별자는 상위층의 프로토콜의 종류를 알려주는 정보다. 이더넷 헤더에는 상위 계층의 프로토콜이 IP라는 정볼를, IP헤더에는 상위계층의 프로토콜이 TCP라는 정보를 , TCP 헤더에는 상위 계층이 애플리케이션이 사용하는 프로토콜에 대한 정보를 담고 있다. </p>
<br/>

<h1 id="⭐-총정리">⭐ 총정리</h1>
<p>ip는 인터넷 전송 과정을 tcp는 데이터를 순서에 맞게 왔는지 중복인지 데이터에 변화는 없었는지 확인한다. 이러한 과정을 캡슐화 역캡슐화를 통해 알아봤다. 각 계층이 무슨일을 하는지 정확히 알수 있었다.</p>
<blockquote>
<p>개쩌는 참고 블로그 
<a href="https://better-together.tistory.com/89?category=887984">https://better-together.tistory.com/89?category=887984</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[GET, POST 차이점]]></title>
            <link>https://velog.io/@giant_toothpick/GET-POST-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@giant_toothpick/GET-POST-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Wed, 26 Jul 2023 08:56:53 GMT</pubDate>
            <description><![CDATA[<br/>

<br/>

<h1 id="⭐-get의-정의">⭐ GET의 정의</h1>
<p>GET은 <code>HTTP 메서드</code> 중 하나로, 서버로 부터 정보를 요청하기 위해 사용되는 메서드다. 주로 데이터를 가져오기 위해 사용되고, 요청하는 데이터는 <code>URL의 쿼리 매개변수</code>로 전달 된다. </p>
<h2 id="📌-get요청-예시">📌 GET요청 예시</h2>
<pre><code class="language-bash">GET /api/data?id=123 HTTP/1.1
Host : www.example.com</code></pre>
<p>위 예시에서 <code>/api/data</code> 는 요청하는 자원의 경로이다. <code>id=123</code> 은 쿼리 매개변수로 데이터를 전달하는 방법이다. <code>=</code> 을 기준으로 키<code>벨류 쌍</code>으로 주어진다. </p>
<h2 id="📌-특징">📌 특징</h2>
<ul>
<li>보는것 처럼 GET 요청은 요청하는 데이터가 URL에 노출된다. 때문에 <code>보안에 취약</code>하다.</li>
<li>브라우저에서 <code>캐싱</code>될 수 있다.  동일한 요청이 여러 번 발생하더라도 실제로 서버로 요청이 가는게 아닌 캐싱된 데이터를 사용할 수도 있다.</li>
<li>데이터를 가져오는 용도로 사용되기 때문에 서버의 상태를 변경시키지 않는다.</li>
</ul>
<br/>

<br/>

<h1 id="⭐-post의-정의">⭐ POST의 정의</h1>
<p>POST도 <code>HTTP 메서드</code> 중 하나로, 서버에 데이터를 전송하여 리소스를 생성하거나 수정하기 위해 사용되는 메서드다. POST 요청은 주로 데이터를 전달하기 위해 <code>요청 본문(body)</code>에 포함되어 전송된다. </p>
<h2 id="📌-post요청예시">📌 POST요청예시</h2>
<pre><code class="language-bash">POST /api/create_user HTTP/1.1
Host: www.example.com
Content-Type: application/json

{
    &quot;username&quot;: &quot;john&quot;,
    &quot;email&quot; : &quot;john@example.com&quot;
}</code></pre>
<p>위 예시에서 <code>‘api/create_user’</code> 는 새로운 사용자를 생성하는 API경로이며, 요청 본문에 JSON 형식의 데이터를 전달하고 있다. </p>
<h2 id="📌-특징-1">📌 특징</h2>
<ul>
<li>POST 요청은 요청 본문에 데이터가 포함되기 때문에 GET보다 보안적으로 더 안전하다. (눈으로 잘 안보임)</li>
<li>브라우저에서 캐싱이 되지 않아 동일한 요청이 여러번 발생하면 서버로의 요청이 항상 발생한다.</li>
<li>데이터를 생성하거나 수정하는 용도로 사용되기 때문에, 서버의 상태를 변경할 수 있다.</li>
</ul>
<br/>

<br/>

<h1 id="⭐-총정리">⭐ 총정리</h1>
<blockquote>
<p>HTTP 메서드인 GET과 POST의 차이점에 대해서 알아보았다. GET은 데이터를 가져오기 위해 사용되고, 캐싱될 수 있다. 반면 POST는 데이터를 생성 수정할 수 있고 보안적으로 안전하나 캐싱될 수 없다. 
처음 HTTP메서드를 사용할 때 POST로도 충분히 데이터를 가져올 수 있는데 왜 그러지 않았는지 이번에 확실히 알 수 있었다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>