<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>junmieee_.log</title>
        <link>https://velog.io/</link>
        <description>이로이로</description>
        <lastBuildDate>Sun, 10 Sep 2023 06:32:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>junmieee_.log</title>
            <url>https://velog.velcdn.com/images/junmieee_/profile/86ebbba5-4569-461c-852a-31c61ac260d4/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. junmieee_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/junmieee_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Next.js 13 vercel 배포시 418(+422) 에러]]></title>
            <link>https://velog.io/@junmieee_/Next.js-13-vercel-%EB%B0%B0%ED%8F%AC%EC%8B%9C-418422-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@junmieee_/Next.js-13-vercel-%EB%B0%B0%ED%8F%AC%EC%8B%9C-418422-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Sun, 10 Sep 2023 06:32:10 GMT</pubDate>
            <description><![CDATA[<p>Next.js 기반의 정적 블로그를 Vercel로 배포하면서 418 에러가 발생해 해결한 과정을 기록한다. </p>
<p>개발 환경에서는 문제가 없었는데 배포를 하니 아래의 에러가 발생하면서 클라이언트 사이드 에러가 떴다. 
<img src="https://velog.velcdn.com/images/junmieee_/post/10bf8bb4-2cf6-4948-94de-e1a0b497ca16/image.png" alt=""></p>
<p>💁 구글링을 해보니 날짜와 시간 처리를 도와주는 date-fns나 dayjs등의 라이브러리가 원인이 될 수 있다고 해서 내가 사용 중인 dayjs 라이브러리를 지웠지만 에러가 해결되지 않았다. ❌</p>
<blockquote>
<p>에러의 근본적인 원인은 시간 생성자 함수인 New Date()를 사용해 시간을 렌더링 했을 때 서버 사이드에서의 시간과 클라이언트 사이드에서의 시간이 맞지 않았기 때문 </p>
</blockquote>
<p>나는 블로그 포스트를 가져올 때 최신 순으로 정렬하기 위해 New Date()를 사용하고 있었기 때문에 해당 부분에서 에러가 나는 것으로 보여졌다.</p>
<pre><code>import { allBlogs, allNotes } from &#39;contentlayer/generated&#39;;

export const allBlogPosts = allBlogs
    .map((post) =&gt; ({
        ...post,
    }))
    .sort((a, b) =&gt; new Date(b.date).getTime() - new Date(a.date).getTime());</code></pre><p><del>해당 부분을 Suspense로 감싸주면 해결된다는 말이 있어서 시도해봤지만  해결되지 않음.</del></p>
<p><a href="https://github.com/vercel/next.js/issues/37489">이곳</a>에서 제시한 방법인 useEffect를 사용해 클라이언트 사이드에서만 렌더링 되도록 코드를 수정하니 드디어 에러가 해결됨👍</p>
<pre><code>export default function Note({ notes, tags }) {
    const [selectedTag, setSelectedTag] = useState(null);
    const [sortedNotes, setSortedNotes] = useState([]);
    const [allTags, settags] = useState([]);

    useEffect(() =&gt; {
        const sorted = notes.sort((a, b) =&gt; Number(new Date(b.date)) - Number(new Date(a.date)))
        setSortedNotes(sorted);
    }, [notes]);

    ..생략..</code></pre><p><em>418(+422)에러는 해결했지만 /Scrivings 페이지에 404 에러가 난다.. 개발 환경에서는 문제가 없었는데 또 배포를 하니 해당 에러가 뜬다... 해결하면 블로그에 기록할 예정😬</em></p>
<p>참고: 
<a href="https://github.com/vercel/next.js/discussions/43921">Next 13: Error: Minified React error #418 #43921</a>
<a href="https://github.com/vercel/next.js/issues/37489">after updating react 18, i have problems with date-fns #37489</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Git]  .gitIgnore에 .env 파일 설정이 안될때]]></title>
            <link>https://velog.io/@junmieee_/Git-.gitIgnore%EC%97%90-.env-%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95%EC%9D%B4-%EC%95%88%EB%90%A0%EB%95%8C</link>
            <guid>https://velog.io/@junmieee_/Git-.gitIgnore%EC%97%90-.env-%ED%8C%8C%EC%9D%BC-%EC%84%A4%EC%A0%95%EC%9D%B4-%EC%95%88%EB%90%A0%EB%95%8C</guid>
            <pubDate>Wed, 02 Aug 2023 03:41:05 GMT</pubDate>
            <description><![CDATA[<p>프로젝트를 진행하던 중 노출되면 안되는 환경변수가 있는.env파일을 깃헙 레포지토리에 올려버려서 급히 .gitignore파일에 .env를 아래와 같이 추가 한 후 커밋 푸시 후 해당 레포지토리를 확인했지만 여전히 .env 파일이 포함되어 있었다.🤫 </p>
<pre><code># dotenv environment variable files
.env
</code></pre><p>알고 보니 Git 저장소에 파일을 추가하면 해당 파일은 추적(tracked) 상태가 된다. 이 상태에서는 파일의 변경 사항을 Git이 감지하고 커밋에 포함시킨다. 따라서 파일을 추적에서 제거하려면 다음과 같은 작업을 해야 한다. </p>
<ol>
<li><p><em>git rm .env --cached</em>: 해당 파일을 추적에서 제거한다. --cached 옵션은 워킹 디렉토리에 있는 파일은 삭제하지 않고, 인덱스(index)에서만 추적을 제거함으로로컬 파일은 그대로 유지되며 Git의 추적에서만 제거된다.</p>
</li>
<li><p><em>git commit -m &quot;&lt;커밋 메시지&gt;&quot;</em>: 변경 사항을 커밋해 추적에서 제거된 파일들이 Git의 히스토리에서 사라지게 된다. </p>
</li>
</ol>
<p>참고: <a href="https://stackoverflow.com/questions/38983153/git-ignore-env-files-not-working%5C">stack overflow</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 실행 컨텍스트 ]]></title>
            <link>https://velog.io/@junmieee_/JavaScript-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-ry6kodob</link>
            <guid>https://velog.io/@junmieee_/JavaScript-%EC%8B%A4%ED%96%89-%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8-ry6kodob</guid>
            <pubDate>Mon, 15 May 2023 14:28:11 GMT</pubDate>
            <description><![CDATA[<h1 id="🌱-실행-컨텍스트란">🌱 실행 컨텍스트란?</h1>
<blockquote>
<p>실행컨텍스트(Execution Context)는 코드가 실행되는 환경을 말한다.실행 컨텍스트는 실행 가능한 코드가 실행되기 위해 필요하나 환경을 제공하는 추상적인 개념으로 함수 실행, 전역 실행 등과 같은 실행 환경을 나타내는데 사용된다. 실행 컨텍스는 스택(Stack)으로 구성되어 있으며, 실행 순서는 스택의 맨 위에서부터 시작된다. </p>
</blockquote>
<p>자바스크립트에서는 실행 컨텍스트가 세 가지 종류로 구분된다. </p>
<ol>
<li><p>전역 실행 컨텍스트(Global Execution Context, GEC): 자바스크립트에서 실행되는 모든 코드는 전역 실행 컨텍스트 내에서 실행된다. 이 실행 컨텍스트의 주요 기능은 <strong><code>전역 객체(Global Object)</code></strong> 를 생성하고 this 값을 전역 객체에 할당한다. </p>
</li>
<li><p>함수 실행 컨텍스트(Function Execution Context, FEC): 함수가 호출될 때마다 생성되는 실행 컨텍스트이다. 함수가 선언될 떄는 실행 컨텍스트가 생성되지 않으며, 함수가 <strong>호출</strong>될 때 실행 컨텍스트가 생성된다. 함수가 호출될 때마다 해당 함수의 FEC가 실행스택에 추가된다. 함수가 실행을 완료하면 해당 FEC가 스택에서 제거된다. </p>
</li>
<li><p>eval()실행 컨텍스트: val() 함수의 성격상 보안에 취약하기 때문에 자주 사용되지 않는다. 그러나 eval() 함수가 사용될 때마다 실행 콘텍스트가 생성된다. </p>
<hr>
</li>
</ol>
<p>💡 콜 스택과 실행 컨텍스트의 개념을 확실히 하기 위해서 ChatGPT에게 얻은 답변💡</p>
<p>✓ 요약: 실행컨텍스트는 객체이며 함수가 호출될 때마다 변수, 함수, 매개변수 등의 정보를 담고 콜스택에 쌓이게 된다. 이 쌓인 콜스택은 메모리에 할당된다. </p>
<blockquote>
<p> ChatGPT: 네, 맞습니다. 콜 스택 안에는 실행 컨텍스트 객체가 쌓이며, 이 콜 스택은 메모리에 할당됩니다.
실행 컨텍스트는 함수 호출이 발생할 때 생성되고, 함수의 실행이 완료되면 콜 스택에서 제거됩니다. 따라서 함수가 호출될 때마다 해당 함수의 실행 컨텍스트가 콜 스택에 쌓이게 됩니다.
각 실행 컨텍스트 객체는 변수, 함수, 매개변수 등의 정보를 담고 있으며, 실행 중인 코드 블록의 환경을 구성합니다. 이 실행 컨텍스트 객체가 콜 스택에 쌓이는 과정은 메모리에 해당 객체를 할당하는 것과 관련이 있습니다. 실행 컨텍스트 객체는 JavaScript 엔진이 메모리에 할당하여 관리하는 객체입니다.
따라서 콜 스택은 메모리에 할당되는 자료 구조로, 그 안에는 실행 컨텍스트 객체들이 쌓이는 형태로 동작합니다. 콜 스택은 함수 호출과 반환의 순서를 관리하고, 실행 컨텍스트 객체들은 각각의 함수 실행에 필요한 정보를 유지하면서 JavaScript 코드의 실행을 지원합니다.</p>
</blockquote>
<hr>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/d4c680b5-3062-4e8c-b041-e1583f29c7b6/image.png" alt=""></p>
<h1 id="💁-실행-컨텍스트는-어떻게-생성될까">💁 실행 컨텍스트는 어떻게 생성될까?</h1>
<h2 id="📎-생성-단계">📎 생성 단계</h2>
<p>실행컨텍스트를 이해하기 위해서는 먼저 선행되어야 할 개념이 있다. 그 중 하나가 자바스크립트의 &#39;호이스팅&#39;이다. </p>
<p>👆🏻 호이스팅이란 변수와 함수 선언이 스코프의 최상단으로 올려진 듯한 현상을 말한다. 실제로 스코프의 최상단에 올려진 것이 아니고 자바스크립트 엔진이 코드를 전체적으로 읽고 변수 같은 정보를 실행컨텍스트 정보를 미리 <code>환경 레코드(Environment Record)</code>에 기록하기 때문이다. </p>
<h3 id="변수-호이스팅">변수 호이스팅</h3>
<p>-- 코드 예시 -- </p>
<pre><code>console.log(status); // undefined
var status = &quot;Studying&quot;;
console.log(status); // Studying
</code></pre><p>자바스크립트에서 위의 코드를 실행하면 전역 실행 컨텍스트 한 칸을 생성해서 콜 스택에 넣는다. 그 후 코드를 읽으며 미리 선언할 식별자를 찾아보고 있다면 생성된 실행 컨텍스트 안의 환경 레코드(Environment ㅅRecord)에 식별자를 기록해 둔다. 아래의 코드의 경우 var 키워드로 선언했기 때문에 초기값을 { status: undefined }로 초기화 해둔다. 이러한 단계를 <code>생성 단계(Creation Phase)</code>라고 한다. </p>
<h2 id="📎-실행-단계">📎 실행 단계</h2>
<p>생성 단계 이후 선언문 외에 나머지 코드를 순차적으로 실행하는 단계이다. 아래의 코드에서는 첫 줄의 status의 값을 출력하는 console.log(status)가 먼저 실행된다. 자바스크립트 엔진은 이를 출력하기 위해 활성화된 실행컨텍스트 내에 환경레코드를 보고 이미 기록된 status 값을 참조해서 출력한다. --&gt; <code>console.log(status); // undefined</code> &lt;-- 그리고 다음 라인을 실행한다. 생성단계에서 선언을 미리 해놨으니 바인딩 된 값(현재는 undefined)을 &quot;Studying&quot;으로 할당만 실행해준다. <code>{status : &quot;Studying&quot;}</code> 이후 마지막 라인을 실행하면 자바스크립트 엔진은 환경 레코드를 참조해서 Status의 값을 Studying으로 결정한다.  </p>
<pre><code>console.log(status); // undefined
var status = &quot;Studying&quot;;
console.log(status); // Studying
</code></pre><p>선언과 동시에 초기화가 이루어지는 var대신 const키워드로 변수를 선언한다면 엔진이 식별자를 기록하긴 하지만 값을 초기화 시켜두진 않는다. 따라서 선언문 이전에 값을 참조하려고 하면 &quot; ReferenceError: Cannot access &#39;status&#39; before initialization&quot;와 같은 Reference Error가 발생한다. </p>
<p> 이 처럼 let이나 const와 같은 변수로 선언한 경우 선언 이전에 식별자를 참조할 수 없는 구역을 TDZ(Temporal Dead Zone), 일시적 사각지대 라고 표현한다.</p>
<p> <img src="https://velog.velcdn.com/images/junmieee_/post/75d58a31-6c3e-4b66-9322-eeb3f7a07d1e/image.png" alt=""> 이미지 출처: <a href="https://ui.toast.com/weekly-pick/ko_20191014">https://ui.toast.com/weekly-pick/ko_20191014</a></p>
<p> 그렇다면 함수의 경우 어떻게 호이스팅이 이루어질까?</p>
<h3 id="함수-호이스팅">함수 호이스팅</h3>
<p><code>함수 선언문(function declaration)</code>은 마찬가지로 해당 스코프의 최상단으로 끌어올려진다.자바스크립트 엔진이 study함수의 선언과 동시에 환경레코드에 {sayHello: f {}} 함수 객체를 생성해서 기록해 둔다. 함수 선언문의 경우 선언과 동시에 함수가 생성되는 것이다. 따라서 함수 선언문을 선언하기 이전에 호출해도 정상적으로 동작한다. </p>
<p>참고로 이 함수 선언문 방식은 함수 선언 이전에 호출할 수 있기 때문에 코드의 가독성과 스코프 규칙 등의 문제로 사용을 지양하는 경향이 있다. </p>
<pre><code>sayHello(); // &quot;Hello&quot;

function sayHello() {
  console.log(&quot;Hello&quot;);
}</code></pre><p><code>함수 표현식(function expression)</code>은 선언문 이전에 실행하려고 하면 실행되지 않는다. 환경레코드에 기록되어 있는 sayHello의 값은 undefined이고 이 undefined 데이터 타입은 함수와 달리 호출될 수 없기 때문에 타입에러가 발생한다. 따라서 변수에 할당된 함수 표현식은 해당 변수가 선언된 위치부터 사용 가능하다. 같은 함수를 const에 선언하면 아직 환경레코드에 기록된 값이 없어 Reference Error가 발생한다. 이는 함수를 &#39;변수&#39;에 담고 있기 때문에 앞서 살펴본 변수 호이스팅과 같은 원리로 동작하는 것이다. </p>
<pre><code>sayHello(); // TypeError: sayHello is not a function

var sayHello = function() {
  console.log(&quot;Hello&quot;);
};</code></pre><h1 id="💁-외부-환경-참조outer-environment-reference">💁 외부 환경 참조(Outer Environment Reference)</h1>
<p>실행 컨텍스트는 렉시컬 환경(Lexical Environment)과 함께 동작한다. 이 때, <code>Outer Outer Environment Reference</code>라는 개념이 등장하는데 간단하게 말하면 이는 현재 실행 컨텍스트의 외부 스코프(outer scope)에 대한 참조를 가리키는 용어이다. <code>Outer Outer Environment Reference</code>는 <code>스코프 체인</code>이라는 것을 통해 변수 검색을 수행한다. </p>
<h3 id="🏷-변수-섀도잉과-스코프-체인">🏷 변수 섀도잉과 스코프 체인</h3>
<p>변수 섀도잉은 동일한 이름의 변수가 중첩된 스코프에서 선언될 때 발생하는 현상이다. 내부 스코프에서 선언된 변수는 외부 스코프의 동일한 이름의 변수를 가리키게 되어 외부 스코프의 변수에 접근할 수 없다. 즉 즉 동일한 이름의 변수로 인해 상위 스코프에서 선언된 식별자의 값이 가려지는 현상이다. 이러한 일련의 과정에서 Outer Environment Reference를 통해 스코프 체인이 형성되어 변수를 검색하는데 이를 스코프 체인(Scope Chain)이라고 한다. 각각의 개념을 아래 코드를 통해 더 자세히 알아보자. </p>
<h4 id="변수-섀도잉variable-shadowing">변수 섀도잉(Variable Shadowing):</h4>
<p>아래 코드에서 hello 함수 내부에서 var x = 20;으로 변수 x를 선언했다. 이로 인해 내부 스코프에서의 x는 외부 스코프의 x를 가리키게 된다. hello 함수 내에서 console.log(x);를 실행하면 내부 스코프의 x 값인 20이 출력됩니다. 하지만 외부 스코프에서 console.log(x);를 실행하면 외부 스코프의 x 값인 10이 출력된다. </p>
<pre><code>var x = 10;

function hello() {
  var x = 20;
  console.log(x); // 내부 스코프의 x 출력: 20
}

hello();
console.log(x); // 외부 스코프의 x 출력: 10</code></pre><h3 id="스코프-체인scope-chain">스코프 체인(Scope Chain):</h3>
<p>아래 코드에서는 outer 함수 내부에서 var y = 20;으로 변수 y를 선언하였고, inner 함수 내부에서 var z = 30;으로 변수 z를 선언했다.</p>
<p>console.log(x + y + z);에서 변수 x, y, z에 접근하여 계산을 수행하는데, 이때 스코프 체인을 통해 변수에 접근한다.</p>
<p>inner 함수 내부에서 z 변수를 찾을 때, 현재 스코프에 z 변수가 선언되어 있기 때문에 바로 해당 값을 사용할 수 있다.</p>
<p>inner 함수 내부에서 y 변수를 찾을 때에는 현재 스코프에 y 변수가 없기 때문에 외부 스코프인 outer 함수의 스코프로 이동하여 y 변수를 검색한다. 이때 스코프 체인을 통해 y 변수에 접근할 수 있고, y 변수의 값인 20을 한다.</p>
<p>마지막으로 inner 함수 내부에서 x 변수를 찾을 때, 현재 스코프와 외부 스코프에 x 변수가 없기 때문에 전역 스코프로 이동하여 x 변수를 검색한다(전역 실행 컨텍스트는 전역 스코프와 관련된 변수와 함수를 초기화 해 놓는다. 따라서 전역 스코프는 더 참조할 바깥이 없는 최상위 스코프이다). 스코프 체인을 통해 x 변수에 접근할 수 있고, x 변수의 값인 10을 사용한다.</p>
<p>따라서 console.log(x + y + z)의 결과는 60이 된다. 이와 같은 스코프 체인을 통해 다른 스코프의 변수에 접근할 수 있음을 알 수 있다.</p>
<pre><code>var x = 10;

function outer() {
  var y = 20;

  function inner() {
    var z = 30;
    console.log(x + y + z); // 스코프 체인을 통해 x, y, z 변수에 접근하여 계산
  }

  inner();
}

outer(); // 결과: 60</code></pre><hr>
<p>💡 스코프와 실행 컨텍스트의 개념이 헷갈려 ChatGPT에게 물어보았다💡</p>
<blockquote>
<p>ChatGPT: 스코프는 변수와 함수에 대한 유효 범위를 말합니다. 스코프는 변수의 접근성과 식별자(identifier)의 해결을 결정합니다. 변수와 함수는 스코프에 의해 참조될 수 있으며, 스코프는 렉시컬 환경(lexical environment)으로 표현될 수 있습니다.
반면에 실행 컨텍스트는 코드 실행을 위해 필요한 환경 정보를 포함하고 있는 객체입니다. 실행 컨텍스트는 변수, 함수 선언, 매개변수 등의 정보를 담고 있으며, 코드 실행에 필요한 렉시컬 환경과 호이스팅 등의 동작을 수행합니다. 실행 컨텍스트는 함수 호출 시마다 생성되며, 스택(Stack)에 쌓여 순차적으로 실행됩니다.
실행 컨텍스트는 실행 가능한 코드(전역 코드, 함수 코드)가 실행되기 전에 생성되고, 코드 실행이 끝나면 스택에서 제거됩니다. 실행 컨텍스트는 현재 실행 중인 코드 블록의 환경 정보를 유지하고, 변수 및 함수 식별자의 해결을 위해 스코프 체인을 구성합니다.
따라서 스코프와 실행 컨텍스트는 서로 다른 개념이지만, 실행 컨텍스트는 스코프를 관리하고 스코프에 따라 변수와 함수에 접근하는 역할을 수행합니다. 때문에 두 용어가 혼동되어 사용되기도 합니다.</p>
</blockquote>
<hr>
<h1 id="💁-정리와-결론">💁 정리와 결론</h1>
<p>종합적으로 실행 컨텍스트는 JavaScript 코드에서 변수와 함수 식별자를 관리하기 위한 수단이다. 이를 통해 코드의 실행 결과를 도출할 수 있고 식별자 결정을 더 효율적으로 할 수 있게 되는 것이다. 
어느 정도 실행 컨텍스트에 대한 윤곽이 머릿속에 잡힌 듯하다. [10분 테코톡] 영상의 발표 내용이 해당 개념을 이해하는 데 정말 도움이 많이 됐다. 영상을 전체적으로 본 이후 다른 자료들을 참고하니 보다 잘 이해가 되었다. 실행컨텍스트에 대해 공부했으니 다음 포스팅에서는 클로저 개념에 대해 공부한 후 포스팅 해야겠다~~!! </p>
<h2 id="참고-자료">참고 자료</h2>
<p><a href="https://www.youtube.com/watch?v=EWfujNzSUmw&amp;t=265s">우아한테크 - 하루의 실행컨텍스트</a></p>
<p><a href="https://leetrue-log.vercel.app/javascript-memory-leak">truelog - C언어에서 이어진 메모리 누수의 발생과 해결방법</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] URL의 파라미터 값 추출 - useLocation과 useParams]]></title>
            <link>https://velog.io/@junmieee_/React-URL%EC%9D%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EA%B0%92-%EC%B6%94%EC%B6%9C-useLocation%EA%B3%BC-useParams</link>
            <guid>https://velog.io/@junmieee_/React-URL%EC%9D%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EA%B0%92-%EC%B6%94%EC%B6%9C-useLocation%EA%B3%BC-useParams</guid>
            <pubDate>Fri, 28 Apr 2023 12:42:42 GMT</pubDate>
            <description><![CDATA[<p>_해당 포스팅은 클론 프로젝트를 진행하며 react-router-dom 라이브러리를 사용해 URL 라우팅을 구현한 부분을 정리한 내용입니다.
_</p>
<h3 id="📎-url-파라미터">📎 URL 파라미터?</h3>
<blockquote>
<p>URL 파라미터는 URL 경로 내에 변수를 포함하는 데 사용되며 search속성을 통해 쿼리 파라미터 값을 추출할 수 있다. /users/:userId 경로에서 :userId가 URL 파라미터를 나타낸다. 특정 정보를 URL 경로에 포함하여 서버와 클라이언트 간의 데이터 공유를 용이하게 한다.</p>
</blockquote>
<h1 id="🏷-uselocation">🏷 useLocation</h1>
<p><strong><code>useLocation</code></strong> 은 현재 라우트의 위치 정보를 반환하는 훅으로 <strong><code>search</code></strong> 속성을 통해 쿼리 파라미터 값을 추출할 수 있다. 해당 정보는 URL 경로(path), 쿼리 문자열(query string), URL 상태(state)정보를 포함한다. </p>
<p><strong><code>useLocation</code></strong> 은 아래와 같이 경로 정보를 담고 있는 객체를 리턴한다.</p>
<pre><code>{
pathname: &#39;/search&#39;, 
search: &#39;?q=%EC%8A%A4%ED%8C%8C%EC%9D%B4%EB%8D%94%EB%A7%A8&#39;, 
hash: &#39;&#39;, 
state: null, 
key: &#39;default&#39;
}
</code></pre><p>아래의 예제 코드에서 <strong><code>useQuery</code></strong> 함수에서 <strong><code>useLoaction</code></strong> 훅을 사용해 현재 URL 정보를 가져온 다음, <strong><code>URLSearchParams</code></strong> 를 사용해 파라미터 값을 추출했다. 이 함수를 사용해 검색어를 가져올 수 있게 했다. </p>
<blockquote>
<p>💡 <strong><code>URLSearchParams</code></strong>: URLSearchParams 객체는 현재 URL의 query string을 다룰 수 있는 인터페이스를 제공한다. get, set, append, delete, has 등의 메서드를 제공하므로, URL 파라미터를 다루는 다양한 기능을 구현할 수 있다.  <a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams">URLSearchParams - Mdn 공식문서</a> </p>
</blockquote>
<pre><code>//Search.js
const SearchPage = () =&gt; {

const [searchResult, setSearchResult] = useState([]);


const useQuery = () =&gt; {
  return new URLSearchParams(useLocation().search);
}
let query = useQuery();
const searchTerm = query.get(&quot;q&quot;);  // /search?q=${e.target.value}

}</code></pre><p>검색어를 가져온 다음 TMDB API에서 <strong><code>axios</code></strong> 라이브러리를 사용해 API 호출을 해 화면에 그려준다. </p>
<pre><code>//Search.js


const SearchPage = () =&gt; {

    const fetchSearchMovie = async (searchTerm) =&gt; {
        try {
            const response = await axios.get(`/search/multi?include_adult=false&amp;query=${searchTerm}`);
            setSearchResult(response.data.results);
        } catch {
            console.log(&#39;error&#39;);
        }
    }

    return (
        &lt;section className=&#39;search-container&#39;&gt;
            {searchResult.map((movie) =&gt; {
                if (movie.backdrop_path !== null &amp;&amp; movie.media_type !== &quot;person&quot;) {
                    const movieImageUrl = &quot;https://image.tmdb.org/t/p/w500&quot; + movie.backdrop_path;
                    return (

                        &lt;&gt;

                            &lt;div className=&#39;movie&#39; key={movie.id}&gt;
                                &lt;div className=&#39;movie__column-poster&#39; onClick={() =&gt; handleClick(movie)} &gt;
                                    &lt;img src={movieImageUrl} alt=&quot;movie&quot; className=&#39;movie__poster&#39; /&gt;
                                &lt;/div&gt;
                            &lt;/div&gt;
                            {modalOpen &amp;&amp; &lt;DetailPage {...movieSelected} setModalOpen={setModalOpen} /&gt;}

                        &lt;/&gt;
                    )
                }
            })}
        &lt;/section&gt;
    )
}


</code></pre><h1 id="🏷-useparams">🏷 useParams</h1>
<p>useParams는 React Router v5부터 도입된 훅으로, <strong><code>동적라우팅</code></strong> 에서 사용되는 URL 파라미터 값을 추출하는 데 사용된다.</p>
<p>&#39;/users/:userId&#39;와 같은 URL 경로를 가진 동적 라우팅의 경우 <strong><code>:userId</code></strong> 는 동적으로 변할 수 있는 파라미터 값이다. 이와 같은 URL경로에 매칭되는 경우 <strong><code>useParams</code></strong> 를 사용해 아래와 같이 URL 파라미터 값을 추출할 수 있다. </p>
<pre><code>import { useParams } from &#39;react-router-dom&#39;;

function User() {
  const { userId } = useParams();

  return (
    &lt;div&gt;User ID: {userId}&lt;/div&gt;
  );
}</code></pre><p>이렇게 추출된 파라미터 값을 이용해 <strong><code>userID</code></strong> 로 해당 유저의 정보를 서버에서 가져와 렌더링 할 수 있다. </p>
<pre><code>import { useParams } from &#39;react-router-dom&#39;;
import { useEffect, useState } from &#39;react&#39;;

function User() {
  const { userId } = useParams();
  const [user, setUser] = useState(null);

  useEffect(() =&gt; {
    // 서버에서 userId에 해당하는 유저 정보를 가져온다.
    fetch(`/api/users/${userId}`)
      .then(response =&gt; response.json())
      .then(data =&gt; setUser(data));
  }, [userId]);

  if (!user) {
    return &lt;div&gt;Loading...&lt;/div&gt;;
  }

  return (
    &lt;div&gt;
      &lt;div&gt;User ID: {user.id}&lt;/div&gt;
      &lt;div&gt;Name: {user.name}&lt;/div&gt;
      &lt;div&gt;Email: {user.email}&lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Nextjs에 Redux 적용하기]]></title>
            <link>https://velog.io/@junmieee_/Nextjs%EC%97%90-Redux-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@junmieee_/Nextjs%EC%97%90-Redux-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 25 Apr 2023 11:25:25 GMT</pubDate>
            <description><![CDATA[<h1 id="redux">Redux</h1>
<h1 id="🏷-nextjs에-redux를-적용해-보자">🏷 Next.js에 Redux를 적용해 보자</h1>
<p>인프런에서 Zerochoi(제로초)님의 Nobird강좌를 수강하며 트위터와 비슷한 SNS 서비스를 구현하는 클론 프로젝트를 진행하는 중 Next.js에 Redux를 실제로 구현하는 내용을 코드 예제와 함께 기록해 보고자 한다.</p>
<h3 id="💁-why-redux">💁 Why Redux?</h3>
<p>그 전에 이 프로젝트에서 상태관리 라이브러리로 리덕스를 채택한 이유를 리덕스의 장단점을 통해 살펴보자. </p>
<p>프로젝트의 규모가 어느정도 이상이 되면 컴포넌트를 적절하게 분리해주는 것이 필수다. 컴포넌트가 작은 단위로 나누어 질수록 다른 컴포넌트에 데이터를 전달하는 것이 무척 까다로워 지는데 이를 해결하기 위해 중앙에서 데이터를 한 번에 관리할 수 있는 Redux, Mobx, ContextAPI 등의 상태관리 프로그램을 채택한다. 여러 가지 상태관리 프로그램 중 팀이나 본인의 성향에 맞게 취사 선택에서 사용하면 된다. </p>
<ul>
<li>Redux는 원리가 간단하기 때문에 에러가 날 수 있는 상황이 많이 없고 에러가 나더라고 추적이 잘 되어 앱이 안정적이여 진다.</li>
<li>action과 state를 이용해 디버깅이 쉬워져 추적이 용이해진다.</li>
<li>대신 코드량이 다른 상태관리 프로그램에 비교해 많은 것이 단점이다.</li>
</ul>
<p>Next.js에서 Redux 적용을 간편하게 도와주는 Next Redux Wrapper 라이브러리를 사용해 진행한다. </p>
<pre><code class="language-jsx">//Next redux wrapper 설치
npm i next-redux-wrapper</code></pre>
<h3 id="👍-본격적으로-nextjs프로젝트에-redux를-적용해-보자">👍 본격적으로 Next.js프로젝트에 Redux를 적용해 보자.</h3>
<p>루트 폴더에 Store폴더를 만들고 그 안에 ConfigureStore.js파일을 생성해 준 후 아래와 같이 작성해 준다. </p>
<pre><code class="language-jsx">//ConfigureStore.js

import { createWrapper } from &#39;next-redux-wrapper&#39;;

// configureStore 여기에서는 일반 redux와 비슷
const configureStore = () =&gt; {


};

// { debug: process.env.NODE_ENV === &quot;development&quot; }
const wrapper = createWrapper(configureStore, {
    debug: process.env.NODE_ENV === &#39;development&#39;,
}); // 두번째는 옵션 객체

export default wrapper</code></pre>
<p>위의 코드로 기본적인 redux 세팅을 해 놓은 후 App.js에서 high order component로 wrapper로 감싸준다. </p>
<pre><code class="language-jsx">//app.js

import React from &#39;react&#39;;
import PropTypes from &#39;prop-types&#39;;
import &#39;antd/dist/antd.css&#39;;
import Head from &#39;next/head&#39;;
import wrapper from &#39;../store/configureStore&#39;;

const NodeBird = ({ Component }) =&gt; {
    return (
        &lt;&gt;
            &lt;Head&gt;
                &lt;meta charSet=&#39;utf-8&#39;&gt;&lt;/meta&gt;
                &lt;title&gt;NodeBird&lt;/title&gt;
            &lt;/Head&gt;
            &lt;Component /&gt;
        &lt;/&gt;
    );
};

NodeBird.prototype = {
    Component: PropTypes.elementType.isRequired,
}

export default wrapper.withRedux(NodeBird);</code></pre>
<aside>
💡 Next.js에 Redux를 적용할 때는 기존의 리액트에 Redux를 적용하는 것과는 다르게 Provider로 감싸주지 않는다. 예전 버전에서는 Provider로 감싸줬지만 Next.js 6버전 부터는 자체적으로 Provider로 감싸기 때문에 해당 코드가 필요 없다.

</aside>

<pre><code class="language-jsx">
//app.js

...

const NodeBird = ({ Component }) =&gt; {
    return (
        &lt;&gt;
                    &lt;Provider store={store}&gt; //
            &lt;Head&gt;
                &lt;meta charSet=&#39;utf-8&#39;&gt;&lt;/meta&gt;
                &lt;title&gt;NodeBird&lt;/title&gt;
            &lt;/Head&gt;
            &lt;Component /&gt;
                    &lt;Provider&gt;
        &lt;/&gt;
    );
};

NodeBird.prototype = {
    Component: PropTypes.elementType.isRequired,
}

export default wrapper.withRedux(NodeBird)</code></pre>
<p>configureStore.js에 Reducer를 정의 한 후 Reducer를 코드로 작성하자. </p>
<pre><code class="language-jsx">//ConfigureStore.js

import { createWrapper } from &#39;next-redux-wrapper&#39;;

import reducer from &#39;../reducers&#39;; //reducer 불러오기

const configureStore = () =&gt; {
        const store = createStore(reducer);
        return store;

};

const wrapper = createWrapper(configureStore, {
    debug: process.env.NODE_ENV === &#39;development&#39;,
}); // 두번째는 옵션 객체

export default w</code></pre>
<p>index.js 파일에서는 <strong><code>combineReducers</code></strong>함수를 사용하여 <strong><code>user</code></strong>와 <strong><code>post</code></strong>두 개의 Reducer를 합쳤다. <strong><code>combineReducers</code></strong>함수는 여러 개의 Reducer 함수를 받아서 하나로 합친 새로운 Reducer 함수를 반환한다.</p>
<pre><code class="language-jsx">//Reducers &gt; index.js

import { HYDRATE } from &#39;next-redux-wrapper&#39;;
import { combineReducers } from &#39;redux&#39;;

import user from &#39;./user&#39;;
import post from &#39;./post&#39;;

// (이전상태, 액션) =&gt; 다음상태
const rootReducer = (state, action) =&gt; {
    switch (action.type) {
        case HYDRATE:
            console.log(&#39;HYDRATE&#39;, action);
            return action.payload;
        default: {
            const combinedReducer = combineReducers({
                user,
                post,
            });
            return combinedReducer(state, action);
        }
    }
};

export default rootReducer;</code></pre>
<p>✓ <strong><code>initialState</code></strong>객체는 <strong><code>user</code></strong> Reducer의 초기 상태를 정의한다. 이 객체는<strong><code>mainPosts</code></strong>, <strong><code>singlePost</code></strong>, <strong><code>imagePaths</code></strong> 를 포함한 상태 프로퍼티를 가지고 있다. </p>
<p>✓ <strong><code>LOAD_POST_REQUEST</code></strong>, <strong><code>LOAD_POST_SUCCESS</code></strong>, <strong><code>LOAD_POST_FAILURE</code></strong> 상수는 <strong><code>user</code></strong> Reducer에서 사용되는 액션 타입을 정의한다. </p>
<p>✓ <strong><code>reducer</code></strong>함수는 이전 상태(<strong><code>state</code></strong>)와 액션(<strong><code>action</code></strong>)을 받아서 새로운 상태를 반환한다. 해당 코드는 <strong><code>immer</code></strong>라이브러리의 <strong><code>produce</code></strong>함수를 사용하여 불변성을 유지하면서 새로운 상태를 만들고 있다. <strong><code>produce</code></strong>함수 내부에서는 <strong><code>draft</code></strong>라는 객체를 수정하면서 새로운 상태를 만든다. <strong><code>draft</code></strong>객체는 이전 상태와 똑같은 구조를 가지고 있지만, 수정이 가능한 객체이다.</p>
<pre><code>//user.js

import produce from &#39;immer&#39;;


export const initialState = {
    mainPosts: [],
    singlePost: null,
    imagePaths: [],
...
}


export const LOAD_POST_REQUEST = &#39;LOAD_POST_REQUEST&#39;;
export const LOAD_POST_SUCCESS = &#39;LOAD_POST_SUCCESS&#39;;
export const LOAD_POST_FAILURE = &#39;LOAD_POST_FAILURE&#39;;

...

const reducer = (state = initialState, action) =&gt; produce(state, (draft) =&gt; {

... 

        case LOAD_POST_REQUEST:
                    draft.loadPostLoading = true;
                    draft.loadPostDone = false;
                    draft.loadPostError = null;
                    break;
                case LOAD_POST_SUCCESS:
                    draft.loadPostLoading = false;
                    draft.loadPostDone = true;
                    draft.singlePost = action.data;
                    break;
                case LOAD_POST_FAILURE:
                    draft.loadPostLoading = false;
                    draft.loadPostError = action.error;
                    break;


... 

}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Redux의 원리와 불변성]]></title>
            <link>https://velog.io/@junmieee_/Redux%EC%9D%98-%EC%9B%90%EB%A6%AC%EC%99%80-%EB%B6%88%EB%B3%80%EC%84%B1</link>
            <guid>https://velog.io/@junmieee_/Redux%EC%9D%98-%EC%9B%90%EB%A6%AC%EC%99%80-%EB%B6%88%EB%B3%80%EC%84%B1</guid>
            <pubDate>Tue, 25 Apr 2023 06:30:10 GMT</pubDate>
            <description><![CDATA[<h1 id="💁-why-redux">💁 Why Redux?</h1>
<p>프로젝트의 규모가 어느정도 이상이 되면 컴포넌트를 적절하게 분리해주는 것이 필수다. 컴포넌트가 작은 단위로 나누어 질수록 다른 컴포넌트에 데이터를 전달하는 것이 무척 까다로워 지는데 이를 해결하기 위해 중앙에서 데이터를 한 번에 관리할 수 있는 Redux, Mobx, ContextAPI 등의 상태관리 프로그램을 채택한다. 여러 가지 상태관리 프로그램 중 팀이나 본인의 성향에 맞게 취사 선택에서 사용하면 된다. 이 포스트는 Redux의 주요 원리와 불변성에 대해 다루고자 한다. </p>
<h2 id="🏷-store">🏷 Store</h2>
<p>데이터의 상태가 관리되는 중앙저장소이다. Store에서 전체 프로그램에 필요한 state들이 저장되어 있어 각 컴포넌트에서 Store의 State에 접근해 조회하고 사용할 수 있다. </p>
<h2 id="🏷-action">🏷 Action</h2>
<p>데이터를 조회하는 것 만이 아니라, 데이터를 수정, 추가, 삭제 기능을 사용하기 위해서는 action이라는 것이 필요하다. 중앙 저장소에 있는 데이터를 바꾸기 위해 action을 만들고 그 action을 dispatch하면 데이터가 변경될 수 있다. </p>
<pre><code class="language-jsx">{
type: &#39;CHANGE_NICKNAME&#39;,
data: &#39;chloeshin&#39;
} </code></pre>
<p>✓Type </p>
<p>Redux의 action 객체는 일반적으로 ‘type’속성을 가지고 있다. 이는 해당 이벤트를 식별하는 ‘문자열’이다. </p>
<p>✓ Payload </p>
<p>액션과 함께 전달되는 데이터를 나타내는 객체이다. 비동기 작업의 상태를 추적하기 위한 추가 정보를 제공할 수 있다. </p>
<p>✓ Dispatch </p>
<p>Store로 action을 보내서 State(상태)를 업데이트하는 메소드이다. dispatch 메소드를 호출하여 액션 객체를 생성하고 Store를 업데이트 한다. 아래는 예시 코드이다. </p>
<pre><code class="language-jsx">// 액션 객체 생성
const addTodoAction = {
  type: &#39;ADD_TODO&#39;,
  payload: {
    text: &#39;Learn Redux&#39;,
    completed: false
  }
};

// Dispatch: 액션 객체를 스토어로 전달하여 상태 업데이트
store.dispatch(addTodoAction);</code></pre>
<h2 id="🏷-reducer">🏷 Reducer</h2>
<p>그렇다면 위에서 처럼 action을 dispatch하면 마법처럼 중앙저장소에 저장되어 있는 데이터의 name이 바뀌는 것일까? 🙅🏻‍♀️ NO 🙅🏻‍♀️ javascript는 우리가 설명해 주지 않는다면 ‘CHANGE_NICKNAME’ 이라는  action을 이해하지 못한다. 그래서 어떻게 바꿔야 하는지 코드를 작성해 주어야 한다.  </p>
<pre><code class="language-jsx">//reducer

switch(action.type) {
    case &#39;CHANGE_NICKNAME&#39;:
        return {
            ...state,
            name: action.data,
    }
}</code></pre>
<h3 id="⚠️⚠️-immutability불변성⚠️⚠️">⚠️⚠️ Immutability(불변성)⚠️⚠️</h3>
<blockquote>
<p>💡 Reducer에서 작성하는 case문에서 return 해주는 객체는 불변성을 꼭 지켜주어야 한다.</p>
</blockquote>
<p>아래와 같이 객체를 새로 만들면 false지만 객체를 참조할 때는 항상 true가 나온다는 점을 기억해야 한다. </p>
<pre><code class="language-jsx">{} === {} //false 

const a = {};
const b = a;
a === b //true</code></pre>
<p>Reducer에서 return하는 객체는 새로운 객체이다. 변경하고자 하는 데이터만 변경하고 객체 자체는 새로 만들어서 보내주어 한다. 객체를 새로 만들어 주어야 이전의 변경 내역 history들이 추적이 되기 때문이다. Redux를 사용하는 주요 목적 중 하나가 History를 관리하기 위해서이다. 만약 참조관계를 통해 Return 하게 된다면 변경한 내역들이 사라져 추적이 어렵게 된다.</p>
<h3 id="redux의-원리-도식화">Redux의 원리 도식화</h3>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/8a589f73-2a93-479f-899b-70d766c87203/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저가 그려지는 원리 및 가상돔]]></title>
            <link>https://velog.io/@junmieee_/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EA%B0%80-%EA%B7%B8%EB%A0%A4%EC%A7%80%EB%8A%94-%EC%9B%90%EB%A6%AC-%EB%B0%8F-%EA%B0%80%EC%83%81%EB%8F%94</link>
            <guid>https://velog.io/@junmieee_/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EA%B0%80-%EA%B7%B8%EB%A0%A4%EC%A7%80%EB%8A%94-%EC%9B%90%EB%A6%AC-%EB%B0%8F-%EA%B0%80%EC%83%81%EB%8F%94</guid>
            <pubDate>Fri, 14 Apr 2023 05:50:22 GMT</pubDate>
            <description><![CDATA[<h1 id="브라우저가-그려지는-원리-및-가상돔">브라우저가 그려지는 원리 및 가상돔</h1>
<p>리액트의 주요 특징 중 하나는 가상돔을 사용한다는 것이다. </p>
<p>가상돔이란 무엇일까? 가상돔을 사용하는 이유에 대해 알기 위해서는 브라우저가 렌더링하는 과정을 알아보아야 한다. </p>
<h3 id="웹-페이지-빌드-과정critical-rendering-path-crp">웹 페이지 빌드 과정(Critical Rendering Path CRP)</h3>
<p>브라우저가 서버에서 페이지에 대한 HTML 응답을 받고 화면에 표시하기 전에 여러 단계가 있다. </p>
<p>웹브라우저가 HTML 문서를 읽고, 스타일을 입히고 뷰포트에 표시하는 과정이다. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/4d8a73f1-a3a7-44db-b5c9-3592fc3d023f/image.png" alt="">
출처 dimension85.com</p>
<ol>
<li><p>Dom Tree 생성</p>
<p> → 렌더 엔진이 문서를 읽어들여서 그것들을 파싱하고 어떤 내용을 페이지에 렌더링할지 결정한다. </p>
</li>
<li><p>Render tree 생성</p>
<p> → 이 단계는 브라우저가 DOM과 CSSOM을 결합하는 곳이며, 이 프로세스는 화면에 보이는 모든 콘텐츠와 스타일 정보를 모두 포함하는 최종 렌더링 트리를 출력한다. </p>
<p> 즉 화면에 표시되는 모든 노드의 콘텐츠 및 스타일 정보를 포함한다. </p>
</li>
<li><p>Layout (reflow)</p>
<p> → 이 단계는 브라우저가 페이지에 표시되는 각 요소의 크기와 위치를 계산하는 단계이다. </p>
</li>
</ol>
<ol>
<li><p>Paint</p>
<p> → 실제 화면에 그리기</p>
</li>
</ol>
<h3 id="어떤-인터렉션에-의해-dom에-변화가-발생하면-그-때-마다-render-tree가-재생성된다">어떤 인터렉션에 의해 DOM에 변화가 발생하면 그 때 마다 Render Tree가 재생성된다.</h3>
<p>-&gt; 즉 모든 요소들의 스타일을 다시 계산, Layout, Repaint 과정을 거치게 된다.</p>
<p>인터렉션이 적은 웹이면 괜찮지만 만약 인터렉션이 많은 웹이면 불필요하게 DOM을 조작하는 비용이 너무 크게 든다. </p>
<blockquote>
<p>이러한 문제로 인해서 나오게 된 것이 가상돔(Virtual Dom)이다. 가상 돔이란 실제 DOM을 메모리에 복사해준 것으로 이해할 수 있다.</p>
</blockquote>
<p>데이터가 바뀌면 가상돔에 렌더링되고 이전에 생긴 가상돔과 비교해서 바뀐 부분만 실제 돔에 적용시킨다. 바뀐 부분을 찾는 과정을 Diffing이라고 부르며, 바뀐 부분만 실제 돔에 적용시켜주는 것을 재조정(Reconciliation)이라고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트란?]]></title>
            <link>https://velog.io/@junmieee_/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%9E%80</link>
            <guid>https://velog.io/@junmieee_/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%9E%80</guid>
            <pubDate>Fri, 14 Apr 2023 05:47:34 GMT</pubDate>
            <description><![CDATA[<h1 id="리액트란">리액트란?</h1>
<h3 id="리액트는-사용자-인터페이스를-만들기-위한-javascript-라이브러리이다">리액트는 사용자 인터페이스를 만들기 위한 JavaScript 라이브러리이다.</h3>
<p>리액트는 인터렉션이 많은 웹 앱을 개발하기 위해서 주로 사용된다. </p>
<p>프레임워크인 Angular와 Vue 와 함께 많이 쓰이고 있는데 리액트는 라이브러리이다. </p>
<p>그렇다면 프레임워크와 라이브러리의 차이점은 무엇일까?</p>
<h2 id="framework----vs----library">Framework    vs    Library</h2>
<p>프레임워크는 어떠한 앱을 만들기 위해 필요한 대부분의 것을 가지고 있는 것이다. </p>
<p>라이브러리는 어떠한 특정 기능을 모듈화 해 놓은 것이다. </p>
<p>→ 프레임 워크는 라이브러리를 포함하고 또한 작성한 소스 코드를 호출한다. 그리고 소스 코드는 어떠한 기능을 구현하기 위해서 라이브러리를 호출하게 된다. </p>
<h3 id="리액트는-프레임워크가-아닌-라이브러리">리액트는 프레임워크가 아닌 라이브러리</h3>
<p>리액트가 라이브러리인 이유는 리액트는 전적으로 UI를 렌더링 하는 데 관여하기 때문이다. </p>
<p>View를 담당하는 것이 리액트이다. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/49b40439-ed73-4727-9e15-431e9be0e94c/image.png" alt=""></p>
<p>출처: <a href="https://rangle.io/blog/how-react-and-redux-brought-back-mvc-and-everyone-loved-it">https://rangle.io/blog/how-react-and-redux-brought-back-mvc-and-everyone-loved-it</a></p>
<p>프레임워크와 같이 페이지를 이동하거나 상태관리와 같은 기능들은 다른 라이브러리를 사용해 도움을 받는다. </p>
<p>화면을 바꾸는 라우팅은 React-router-dom 모듈을 사용하며, 상태관리를 위해서는 redux, mobx 등 여러 모듈을 사용하며, 빌드를 위해서는 webpack, npm, 테스팅을 위해서도 Eslink, Mocha등을 이용한다. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/aa617168-1332-4d2b-8c73-6c79c91eaeed/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] API Routes]]></title>
            <link>https://velog.io/@junmieee_/Next.js-API-Routes</link>
            <guid>https://velog.io/@junmieee_/Next.js-API-Routes</guid>
            <pubDate>Fri, 17 Mar 2023 13:09:47 GMT</pubDate>
            <description><![CDATA[<h3 id="🏷-api란">🏷 API란?</h3>
<p>Application Programming Interface의 약자로 응용 프로그래밍 인터페이스, 즉 컴퓨터나 프로그램 사이의 연결을 의미한다. 프로그램간 소통을 하는 방법에 대하나 정의이다.</p>
<p>_Frontend Service - Backend Service간의 연결
 ➡️ FE Service는 고객과 닿아있고, BE Service는 DB에 닿아있다. </p>
<p> 고객이 DB에 접근하기 위해 FE는 BE와 연결되어야 하고, 이때 API를 활용한다. BE가 제공해주는 API를 통해 DB의 내용을 활용할 수 있다.</p>
<h3 id="🏷-nextjs가-제공하는-api-routes">🏷 Next.js가 제공하는 API Routes</h3>
<blockquote>
<p>Pages/api/*</p>
</blockquote>
<p>아래와 같은 폴더 구조에서 user.js에 가져올 데이터를 코드로 작성해보자. 
<img src="https://velog.velcdn.com/images/junmieee_/post/0268ebfc-0c0b-4635-a26e-a5b9cc5c6763/image.png" alt=""></p>
<p>user.js</p>
<pre><code>export default function handler(req, res) {
    res.status(200).json({ name: &#39;Chloe&#39; })
}![](https://velog.velcdn.com/images/junmieee_/post/87964448-225b-41aa-9273-06ed8d4e5360/image.png)
</code></pre><p>그리고 [info].js에 fetching하는 코드를 작성해 데이터를 가져오자.</p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../../../styles/Home.module.css&#39;;
import Layout from &#39;components/Layout&#39;;
import SubLayout from &#39;components/SubLayout&#39;;
import { useRouter } from &#39;next/router&#39;;
import { useEffect, useState } from &#39;react&#39;;




export default function CategorySlug() {
    const router = useRouter()
    const { username, info } = router.query
    const [name, setName] = useState(&#39;?&#39;)

    useEffect(() =&gt; {
        fetch(&#39;/api/user&#39;).then((res) =&gt; res.json()).then((data) =&gt; {
            setName(data.name)
        })
    })

    return (
        &lt;&gt;
            &lt;h1 className=&#39;title&#39;&gt;
                {username}&#39;s&#39; {info}
            &lt;/h1&gt;
            &lt;h1 className=&#39;title&#39;&gt;
                Name: {name}
            &lt;/h1&gt;

        &lt;/&gt;

    )
}

CategorySlug.getLayout = function getLayout(page) {
    return (
        &lt;Layout&gt;
            &lt;SubLayout&gt;{slug}&lt;/SubLayout&gt;
        &lt;/Layout&gt;
    )

}</code></pre><p>서버를 실행시켜보자. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/5321d44f-d2d5-4fa2-bc39-7454bdbca48d/image.png" alt=""></p>
<h3 id="🏷-dynamic-api-routes">🏷 Dynamic API Routes</h3>
<blockquote>
<p>pages/api/user-info/[uid].js</p>
</blockquote>
<p>아래와 같이 user-info폴더에 [uid].js파일을 만들고 코드를 작성해 보자. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/2be5ff8c-3e3f-4f8a-8a6b-ab1419e23ab4/image.png" alt=""></p>
<p>[uid].js</p>
<pre><code>export default function handler(req, res) {
    const { uid } = req.query
    res.status(200).json({ name: `Chloe ${uid}` })
}</code></pre><p>[info].js</p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../../../styles/Home.module.css&#39;;
import Layout from &#39;components/Layout&#39;;
import SubLayout from &#39;components/SubLayout&#39;;
import { useRouter } from &#39;next/router&#39;;
import { useEffect, useState } from &#39;react&#39;;




export default function CategorySlug() {
    const router = useRouter()
    const { username, info, uid } = router.query
    const [name, setName] = useState(&#39;?&#39;)

    // useEffect(() =&gt; {
    //     fetch(&#39;/api/user&#39;)
    //         .then((res) =&gt; res.json())
    //         .then((data) =&gt; {
    //             setName(data.name)
    //         })
    // }, [])

    useEffect(() =&gt; {

        if (uid != null) {
            fetch(`/api/user-info/${uid}`)
                .then((res) =&gt; res.json())
                .then((data) =&gt; {
                    setName(data.name)
                })
        }
    }, [uid])


    return (
        &lt;&gt;
            &lt;h1 className=&#39;title&#39;&gt;
                {username}&#39;s&#39; {info}
            &lt;/h1&gt;
            &lt;h1 className=&#39;title&#39;&gt;
                Name: {name}
            &lt;/h1&gt;

        &lt;/&gt;

    )
}

CategorySlug.getLayout = function getLayout(page) {
    return (
        &lt;Layout&gt;
            &lt;SubLayout&gt;{slug}&lt;/SubLayout&gt;
        &lt;/Layout&gt;
    )

}</code></pre><p><img src="https://velog.velcdn.com/images/junmieee_/post/2d1e2ace-e619-43a9-969a-66e7758b5882/image.png" alt=""></p>
<h3 id="🏷-api-middlewares">🏷 API Middlewares</h3>
<p>내장 Middleware인 req.cookies나 req.query등을 사용할 수 있다. </p>
<p>cookie는 아래와 같이 사용할 수 있다. </p>
<p>[uid].js</p>
<pre><code>export default function handler(req, res) {
    const cookies = req.cookies
    const { uid } = req.query
    res.status(200).json({ name: `Chloe ${uid} ${JSON.stringify(cookies)}` })
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Shallow Routing]]></title>
            <link>https://velog.io/@junmieee_/Next.js-Shallow-Routing</link>
            <guid>https://velog.io/@junmieee_/Next.js-Shallow-Routing</guid>
            <pubDate>Fri, 17 Mar 2023 12:33:06 GMT</pubDate>
            <description><![CDATA[<h1 id="🏷-router의-slug기능">🏷 Router의 slug기능</h1>
<p>다이내믹 라우팅을 하기 위해서는 pages폴더 안 [slug].js 파일을 생성한다. 
이 slug값에 따라 화면을 다르게 그리기 위해 활용하는 방법을 알아보자.</p>
<p>pages &gt; [slug].js</p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../../../styles/Home.module.css&#39;;
import Layout from &#39;components/Layout&#39;;
import SubLayout from &#39;components/SubLayout&#39;;
import { useRouter } from &#39;next/router&#39;;




export default function CategorySlug() {
    const router = useRouter()
    const { slug } = router.query

    return (
        &lt;&gt;
            &lt;h1 className=&#39;title&#39;&gt;
                {slug}
            &lt;/h1&gt;
        &lt;/&gt;

    )
}

CategorySlug.getLayout = function getLayout(page) {
    return (
        &lt;Layout&gt;
            &lt;SubLayout&gt;{slug}&lt;/SubLayout&gt;
        &lt;/Layout&gt;
    )

}
</code></pre><p>위의 코드와 같이 slug는 router의 쿼리로 관리한다. 그렇기 때문에 아래와 같이 원하는 대로 쿼리 값을 지정해 그려줄 수 있다. </p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../../../styles/Home.module.css&#39;;
import Layout from &#39;components/Layout&#39;;
import SubLayout from &#39;components/SubLayout&#39;;
import { useRouter } from &#39;next/router&#39;;




export default function CategorySlug() {
    const router = useRouter()
    //쿼리 값 지정
    const { slug, from } = router.query

    return (
        &lt;&gt;
            &lt;h1 className=&#39;title&#39;&gt;
                {slug} from {from}
            &lt;/h1&gt;
        &lt;/&gt;

    )
}

CategorySlug.getLayout = function getLayout(page) {
    return (
        &lt;Layout&gt;
            &lt;SubLayout&gt;{slug}&lt;/SubLayout&gt;
        &lt;/Layout&gt;
    )

}</code></pre><p>=&gt; url과 UI
<img src="https://velog.velcdn.com/images/junmieee_/post/2949151e-29d8-4cec-adfd-ad6546ee34f2/image.png" alt=""></p>
<h3 id="🏷-다중-slug">🏷 다중 slug</h3>
<blockquote>
<p>pages/[username]/[info].js
const {username, info} = router.query</p>
</blockquote>
<p>아래의 폴더 구조에서 [info].js파일의 코드를 아래와 같이 작성해보자. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/e34b70fb-df3b-4e59-8799-a7f3fac475c4/image.png" alt=""></p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../../../styles/Home.module.css&#39;;
import Layout from &#39;components/Layout&#39;;
import SubLayout from &#39;components/SubLayout&#39;;
import { useRouter } from &#39;next/router&#39;;




export default function CategorySlug() {
    const router = useRouter()
    const { username, info } = router.query

    return (
        &lt;&gt;
            &lt;h1 className=&#39;title&#39;&gt;
                {username}&#39;s&#39; {info}
            &lt;/h1&gt;
        &lt;/&gt;

    )
}

CategorySlug.getLayout = function getLayout(page) {
    return (
        &lt;Layout&gt;
            &lt;SubLayout&gt;{slug}&lt;/SubLayout&gt;
        &lt;/Layout&gt;
    )

}</code></pre><p><img src="https://velog.velcdn.com/images/junmieee_/post/2114332e-d059-4511-8edd-1d1c8a0e409d/image.png" alt=""></p>
<h1 id="🏷-shallow-routing">🏷 Shallow Routing</h1>
<p>Shallow Routing은 getServerSideProps/getStaticProps 등을 다시 실행시키지 않고, 현재 상태를 잃지 않은 채로 url을 바꾸는 방법이다. </p>
<p>예를 들어 사용자가 어떤 동작을 했고, 그 기록을 query로 남기고 싶을 때(query로 남기면 사용자가 새로고침을 해도 유지가 된다) data fetching을 일으키고 싶지 않다면 shallow routing을 사용하는 것이 효율적이다. </p>
<p>url을 바꾸는 3가지 방식 </p>
<blockquote>
<p>location.replace(&#39;url&#39;) ➡️ 로컬 state 유지 안됨(리렌더)
location.push(url) ➡️ 로컬 state 유지 / data fetching은 일어남
router.push(url, as, {shallow: true}) ➡️ 로컬 state 유지 / data fetching x</p>
</blockquote>
<p>shallow routing을 하기 위해서는 router.push(url, as, {shallow: true}) 로 작성해준다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Routing]]></title>
            <link>https://velog.io/@junmieee_/Next.js-Routing</link>
            <guid>https://velog.io/@junmieee_/Next.js-Routing</guid>
            <pubDate>Fri, 17 Mar 2023 10:56:11 GMT</pubDate>
            <description><![CDATA[<h1 id="router란">Router란?</h1>
<p>흔히 네트워크 용어로 많이 사용된다. 특정 주소가 있고 그 주소에 도달해서 그 주소와 매칭되어 있는 데이터들을 받아서 사용하는 일련의 과정을 Routing이라고 한다. 이를 도와주는 도구를 Router라고 한다. React에서는 별도의 라우터를 제공하지 않기 때문에 React-Router라는 라이브러리를 사용해서 라우팅을 한다. </p>
<p>자세한 사항은 Next.js 공식 홈페이지를 참조하자.
<a><a href="https://nextjs.org/docs/api-reference/next/router">Next.js(router)</a></p>
<p>Next.js의 Routing은 파일시스템 기반으로 파일을 만들면 그것이 즉각적으로 라우터로 인지되고 주소와 매핑이 된다.  </p>
<blockquote>
<p>Pages/ 또는 src/pages/</p>
</blockquote>
<p>파일시스템 기반이기 때문에 폴더의 depth가 그대로 url에 반영된다. </p>
<blockquote>
<p>pages/product/fits-item.js ➡️ /product/first-item
pages/settings/my/info.js ➡️ /settings/my/info</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/2b691536-a356-4b42-97b8-d1c297223fbc/image.png" alt=""></p>
<p>위와 같은 파일 경로의 url은 아래와 같이 표현된다.
<img src="https://velog.velcdn.com/images/junmieee_/post/f1efd411-5c94-44e7-91b0-d97ea7a25f74/image.png" alt=""></p>
<p>하지만 depth가 깊어질수록 참조할 떄 불편함이 생길 수 있다. 이를 위한 setting은 아래와 같다. </p>
<p>루트 경로에 jsconfig.json파일을 생성한다. 이곳에 설정을 하면 절대경로로 접근할 수 있게된다. </p>
<p>jsconfig.json</p>
<pre><code>{
    &quot;compilerOptions&quot;: {
        &quot;baseUrl&quot;: &quot;src&quot;
    }
}</code></pre><p>설정한 후 first-item 페이지의 경로를 아래와 같이 바꿔준다. </p>
<p>이전 경로</p>
<pre><code>import Layout from &#39;../../components/Layout&#39;;
import SubLayout from &#39;../../components/SubLayout&#39;;</code></pre><p>바뀐 경로</p>
<pre><code>import Layout from &#39;components/Layout&#39;;
import SubLayout from &#39;components/SubLayout&#39;;</code></pre><h3 id="🏷-slug">🏷 Slug</h3>
<p>라우팅을 변수명처럼 다이나믹하게 사용하는 방법</p>
<blockquote>
<p>pages/category/[slug].js ➡️ /category/:slug (ex. /category/food)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/8c1e7da2-2925-4330-ab3d-ee444d75bfb3/image.png" alt=""><img src="https://velog.velcdn.com/images/junmieee_/post/f491c1f5-972a-4ac4-8a8d-ba9d7e7d63bc/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Pages/ Layouts ]]></title>
            <link>https://velog.io/@junmieee_/Next.js-Pages-Layouts</link>
            <guid>https://velog.io/@junmieee_/Next.js-Pages-Layouts</guid>
            <pubDate>Fri, 17 Mar 2023 07:57:24 GMT</pubDate>
            <description><![CDATA[<h1 id="pages">Pages</h1>
<p>Next.js에서 pages를 다룰 때 Pre-renders와 SEO개념을 알아야 한다. </p>
<h3 id="🏷-pre-renders">🏷 Pre-renders</h3>
<p>Next.js는 기본적으로 모든 페이지를 Pre-render한다. </p>
<p>아래의 이미지 처럼 페이지를 로드할 때 js를 제외한 기초적인 U가 그려진 상태로 로드하게 된다. 이후 Js 번들이 로드되면 그제서야 Hydration이라는 과정을 거쳐서 사용자와 app이 인터렉션 하게 된다. 
<img src="https://velog.velcdn.com/images/junmieee_/post/d5d5be4b-de79-472d-893e-d7d589e5b865/image.png" alt="">* 출처: Next.js 공식홈페이지</p>
<p>반면 Pre-render가 없는 경우 첫번 째로 로드할 때는 아무것도 없다(ex.react 컴포넌트 등 js로 만든 SPA). Js가 실행되야지만 컴포넌트들을 화면에 그린다. 
<img src="https://velog.velcdn.com/images/junmieee_/post/4a673667-ca0f-4fc8-934c-03a9d0bc720e/image.png" alt="">
*출처: Next.js 공식홈페이지</p>
<h3 id="🏷-pre-rendering과-seo의-상관관계">🏷 Pre-rendering과 SEO의 상관관계</h3>
<p>CSR만 제공한다면, Client(브라우저)처럼 동작하지 않는 검색엔진의 경우 아무런 데이터도 조회해갈 수 없다. 즉, no pre-render의 경우 js가 동작하지 않는 검색엔진에서는 아무것도 읽어갈 수 없다. 하지만 SSR를 하면 js를 실행하지 못하는 검색엔진에서도 타이틀, 대표 이미지등 미리 로드된 정보를 읽어갈 수 있다.  </p>
<h3 id="🏷-nextjs의-pre-rendering-방식">🏷 Next.js의 pre-rendering 방식?</h3>
<blockquote>
<p>SSG(recommended) &amp;&amp; SSR</p>
</blockquote>
<p>SSG는 빌드 타임에 pre-render를 실행해 서버에 부하가 덜하다. 
SSR은 요청 타임에 pre-render를 한다. 즉, 사용자 또는 검색엔진이 해당페이지를 방문했을 때(Url로 요청) node서버가 동작해 화면을 미리 그려서 전달하는 것. Next.js공식문서에 따르면 SSG를 적극적으로 쓰라고 권장하고 있다. </p>
<h3 id="🏷-ssg의-2가지-상황">🏷 SSG의 2가지 상황</h3>
<blockquote>
<p>Page의 내용이 외부 데이터에 의존적인 상황
Page Paths까지 외부 데이터에 의존적인 상황</p>
</blockquote>
<p>첫번 째는 getStaticProps만 가지고도 가능하다. 
두번 째는 getStaticPaths도 함께 활용해야 가능하다. </p>
<h1 id="layout">LayOut</h1>
<p>Layouts는 여러 Page들을 공통적으로 처리할 때 쓰인다. </p>
<blockquote>
<p>여래개의 Layout을 활용하고 싶은 경우는 Components폴더 안에 SubLayout.js파일을 생성하고 해당 페이지에 (Page.getLayout) getLayout함수를 제공한다.</p>
</blockquote>
<p>next.js의 루트 디렉토리 아래 componens 폴더를 만들고 Layout.js 파일을 생성해 아래와 같이 pages안에 공통적으로 들어가는 부분을 담아둔다. </p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;


export default function Layout({ children }) {
  return (
    &lt;div className={styles.container}&gt;
      &lt;Head&gt;
        &lt;title&gt;Create Next App&lt;/title&gt;

        &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
      &lt;/Head&gt;

      &lt;main&gt;
        {children}

      &lt;/main&gt;
      &lt;footer&gt;
  ...</code></pre><p>이후 pages폴더 안에 _app.js파일을 생성한다. _app.js파일은 모든 페이지를 품을 수 있는 구조로 next.js에서 제공한다. 아래와 같이 작성해 준다. </p>
<p>_app.js</p>
<pre><code>import Layout from &quot;../components/Layout&quot;

export default function App({ Component, pageProps }) {
    return (
        &lt;Layout&gt;
            &lt;Component {...pageProps} /&gt;
        &lt;/Layout&gt;
    )
}</code></pre><p>localhost:3000</p>
<p>아래와 같이 footer가 두개 생성된 걸 확인할 수 있다. 
<img src="https://velog.velcdn.com/images/junmieee_/post/9264313d-6cf6-4a48-8efc-5b72c62d09b0/image.png" alt=""></p>
<p>index.js페이지가ㅏ _app.js로 감싸져 있는데 공통된 부분을 Index.js에서 제거하지 않았기 때문이다. </p>
<p>index.js에서 공통된 부분을 지우고 아래와 같이 작성한다. </p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;
import Link from &#39;next/link&#39;;


export async function getServerSideProps() {
  console.log(&#39;server&#39;);
  return {
    props: { time: new Date().toISOString() }
  }
}











export default function Home({ time }) {
  return (
    &lt;&gt;
      &lt;h1 className={styles.title}&gt;
        {time}
      &lt;/h1&gt;
      &lt;h1&gt;&lt;Link href=&quot;/csr&quot;&gt;CSR로&lt;/Link&gt;&lt;/h1&gt;
      &lt;h1&gt;&lt;Link href=&quot;/ssg&quot;&gt;SSG로&lt;/Link&gt;&lt;/h1&gt;
      &lt;h1&gt;&lt;Link href=&quot;/isr&quot;&gt;ISR로&lt;/Link&gt;&lt;/h1&gt;

    &lt;/&gt;
  )
}
</code></pre><p>localhost:3000
<img src="https://velog.velcdn.com/images/junmieee_/post/2fd95402-1dc1-41d5-b972-41dde2e0e9ab/image.png" alt=""></p>
<h3 id="🏷-layout을-여러개-사용할-경우">🏷 Layout을 여러개 사용할 경우</h3>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/ced40037-ea0e-47f0-a4e2-5abadf84a6b8/image.png" alt="">
위와 같은 pages에서 csr, irs, ssg에 home으로 가는 공통적인 a 태그를 만들고 싶을 경우 layout을 활용해 보자. </p>
<p>componenet폴더 안에 SubLayout.js파일을 생성한다. </p>
<pre><code>
import Link from &#39;next/link&#39;;

export default function Layout({ children }) {
    return (
        &lt;div&gt;
            &lt;h1&gt;&lt;Link href=&quot;/&quot;&gt;Home으로 &lt;/Link&gt;&lt;/h1&gt;
            {children}
        &lt;/div&gt;
    )
}
</code></pre><p>csr.js파일에 홈으로 가기 태그를 설정해 보자. 
csr파일 안에 getLayout이라는 함수를 함께 설정해 준다. </p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;
import Link from &#39;next/link&#39;;
import { useEffect, useState } from &#39;react&#39;;
import Layout from &#39;../components/Layout&#39;;
import SubLayout from &#39;../components/SubLayout&#39;;





export default function CSR() {
    const [time, setTime] = useState();

    useEffect(() =&gt; {
        console.log(&#39;Client&#39;)
        setTime(new Date().toISOString());
    }, [])

    return (
        &lt;&gt;
            &lt;h1 className={styles.title}&gt;
                {time}
            &lt;/h1&gt;
        &lt;/&gt;

    )
}

CSR.getLayout = function getLayout(page) {
    return (
        &lt;Layout&gt;
            &lt;SubLayout&gt;{page}&lt;/SubLayout&gt;
        &lt;/Layout&gt;
    )

}</code></pre><p>_app.js에서 Component(page들)가 getLayout함수를 가지고 있다면 그것으로 실행하고 아니라면 해당 page를 리턴하는 코드를 작성해보자.</p>
<pre><code>import Layout from &quot;../components/Layout&quot;

export default function App({ Component, pageProps }) {
    // return (
    //     &lt;Layout&gt;
    //         &lt;Component {...pageProps} /&gt;
    //     &lt;/Layout&gt;
    // )

    const getLayout = Component.getLayout || ((page) =&gt; &lt;Layout&gt;{page}&lt;/Layout&gt;)

    return getLayout(&lt;Component {...pageProps} /&gt;)
}</code></pre><p>아래와 같이 CSR 페이지에 태그가 생성된 것을 확인할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/ba27e4cb-1ac3-4560-987c-a64b68c82199/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 기본 - Data Fetching]]></title>
            <link>https://velog.io/@junmieee_/Next.js-%EA%B8%B0%EB%B3%B8-Data-Fetching</link>
            <guid>https://velog.io/@junmieee_/Next.js-%EA%B8%B0%EB%B3%B8-Data-Fetching</guid>
            <pubDate>Fri, 17 Mar 2023 05:29:22 GMT</pubDate>
            <description><![CDATA[<h2 id="data-fetching-이란">Data Fetching 이란?</h2>
<p>Data를 Fetching하는 말 그대로 데이터를 가져오는 것이다. </p>
<p>화면에 무엇인가 그리려면 결국 어디선가 Data를 가져와야 한다. </p>
<p>그렇다면 Next.js가 제시하는 4가지의 Data Fetching 방법은 무엇일까?</p>
<blockquote>
<p>SSR, CSR, SSG, ISR</p>
</blockquote>
<p>하나씩 차례대로 살펴보자. </p>
<h3 id="☝🏿-ssrserver-side-render">☝🏿 SSR(Server Side Render)</h3>
<p>데이터를 가져와서 그리는 주체가 서버인 것이다. 
Next.js에서 SSR을 담당하는 함수는 getServerSideProps이다. </p>
<p>코드를 통해 자세히 알아보자. </p>
<p>Next.js프로젝트의 pages &gt; index.js안에 getServerSideProps 함수에 time이라는 props를 리턴해 그려보자. </p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;

export async function getServerSideProps() {
  return {
    props: { time: new Date().toISOString() }
  }
}


export default function Home({ time }) {
  return (
    &lt;div className={styles.contain![](https://velog.velcdn.com/images/junmieee_/post/732fedb2-c1bf-43f6-aadd-cb7fea686f84/image.png)
er}&gt;
      &lt;Head&gt;
        &lt;title&gt;Create Next App&lt;/title&gt;

        &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
      &lt;/Head&gt;

      &lt;main&gt;
        &lt;h1 className={styles.title}&gt;
          {time}
        &lt;/h1&gt;

      &lt;/main&gt;
      &lt;footer&gt;

      ...</code></pre><p>위의 코드를 실행하면 아래와 같이 그려진다. 
<img src="https://velog.velcdn.com/images/junmieee_/post/a5255248-70e5-4fce-aaa0-1e8bfea7c23b/image.png" alt=""></p>
<p>🏷 그렇다면 위의 함수가 어디서 동작할까?</p>
<p>코드에 로그를 남겨 알아보자. </p>
<pre><code>
export async function getServerSideProps() {
  console.log(&#39;server&#39;);
  return {
    props: { time: new Date().toISOString() }
  }
}
</code></pre><p>위와 같이 코드를 수정했더니 클라이언트에는 아무런 로그도 남지 않고 아래와 같이 서버에서 동작하는 것을 확인할 수 있다.</p>
<p>서버
<img src="https://velog.velcdn.com/images/junmieee_/post/d9c875d5-b077-4891-b93f-f3412095f804/image.png" alt=""></p>
<h3 id="✌🏿-csrclient-side-render">✌🏿 CSR(Client Side Render)</h3>
<p>SSR의 반대 개념으로 클라이언트가 데이트를 가져와 그리는 주체가 되는 것을 의미한다. </p>
<p>Next.js에서 CSR을 담당하는 함수는 따로 없다. React에서 사용하는 것과 동일하다. </p>
<p>index.js에 csr로를 클릭하면 시간이 나오는 페이지를 코드로 구현해보자. </p>
<pre><code>export default function Home({ time }) {
  return (
    &lt;div className={styles.container}&gt;
      &lt;Head&gt;
        &lt;title&gt;Create Next App&lt;/title&gt;

        &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
      &lt;/Head&gt;

      &lt;main&gt;
        &lt;h1 className={styles.title}&gt;
          {time}
        &lt;/h1&gt;
        &lt;h1&gt;&lt;Link href=&quot;/csr&quot;&gt;CSR로&lt;/Link&gt;&lt;/h1&gt;

      &lt;/main&gt;
      &lt;footer&gt;
      ///</code></pre><p>pages &gt; csr.js</p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;
import Link from &#39;next/link&#39;;
import { useEffect, useState } from &#39;react&#39;;





export default function Home() {
    const [time, setTime] = useState();

    useEffect(() =&gt; {
        console.log(&#39;Client&#39;)
        setTime(new Date().toISOString());
    }, [])

    return (
        &lt;div className={styles.container}&gt;
            &lt;Head&gt;
                &lt;title&gt;Create Next App&lt;/title&gt;

                &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
            &lt;/Head&gt;

            &lt;main&gt;
                &lt;h1 className={styles.title}&gt;
                    {time}
                &lt;/h1&gt;

            &lt;/main&gt;
            ///</code></pre><p>로그를 확인해보면 useEffect는 클라이언트에서 동작하는 것으로 확인할 수 있다. 서버에는 남지 않는다. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/d440b6ae-374b-4ba1-b390-e456a3ed3192/image.png" alt=""></p>
<h3 id="👆🏿✌🏿-ssgstatic-site-generation">👆🏿✌🏿 SSG(Static-Site-Generation)</h3>
<p>SSG는 정적인 사이트를 데이터를 가져와 그려두는 것이다. </p>
<p>SSG를 담당하는 함수는 getStaticProps(with getStaticPaths)이다. </p>
<p>SSG 페이지를 만들어 getStaticProps함수를 동작시켜 보자.</p>
<p>index.js</p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;
import Link from &#39;next/link&#39;;


export async function getServerSideProps() {
  console.log(&#39;server&#39;);
  return {
    props: { time: new Date().toISOString() }
  }
}


export default function Home({ time }) {
  return (
    &lt;div className={styles.container}&gt;
      &lt;Head&gt;
        &lt;title&gt;Create Next App&lt;/title&gt;

        &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
      &lt;/Head&gt;

      &lt;main&gt;
        &lt;h1 className={styles.title}&gt;
          {time}
        &lt;/h1&gt;
        &lt;h1&gt;&lt;Link href=&quot;/csr&quot;&gt;CSR로&lt;/Link&gt;&lt;/h1&gt;
        &lt;h1&gt;&lt;Link href=&quot;/ssg&quot;&gt;SSG로&lt;/Link&gt;&lt;/h1&gt;

      &lt;/main&gt;</code></pre><p>pages &gt; ssg.js</p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;



export async function getStaticProps() {
    console.log(&#39;server&#39;);
    return {
        props: { time: new Date().toISOString() }
    }
}


export default function SSG({ time }) {


    return (
        &lt;div className={styles.container}&gt;
            &lt;Head&gt;
                &lt;title&gt;Create Next App&lt;/title&gt;

                &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
            &lt;/Head&gt;

            &lt;main&gt;
                &lt;h1 className={styles.title}&gt;
                    {time}
                &lt;/h1&gt;

            &lt;/main&gt;
            &lt;footer&gt;</code></pre><p>실행해보면 아래와 같이 서버에서 동작하는 걸 확인할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/junmieee_/post/fffae554-5214-496a-a2c5-1ffe40b684a9/image.png" alt=""></p>
<p>그렇다면 SSG를 생성하는 주체는 서버일까? 정답은 &#39;No&#39;. server에서 동작했던 이유는 개발환경에서 실행했기 때문이다. 개발환경에서는 ssg가 제대로 동작하지 않고 ssr처럼 동작한다. 그럼 build를 해 확인해보자. </p>
<pre><code>yarn build</code></pre><pre><code>yarn start</code></pre><p>localHost:3000에서 확인해 보면 서버에서의 로그가 남지 않는 것을 확인할 수 있다. </p>
<p>빌드를 할 때 ssg페이지가 만들어져 화면에 찍힌 시간이 build 타임으로 찍혀 ssg가 만들어진 것이다. </p>
<p>요약하면 데이터 페칭을 ssg르 할 경우 빌드할 타임에 데이터를 다 가져와서 우리가 보여줄 static한 페이지를 생성해 버린 것이다. </p>
<p>그렇다면 이런 ssg는 언제 쓰일까?</p>
<p>CSR과 같이 새로 페이지를 그릴 때 마다 서버가 동작하면 여러 사용자가 사용할 경우 서버에 부하가 많이 걸릴 것이다. 블로그와 같이 정적인 페이지를 제공해도 문제가 없는 경우 ssg를 사용해 서버 부하를 낮추고 효율적인 페이지를 제공할 수 있다.  </p>
<h3 id="✌🏿✌🏿-irsincremental-static-regeneration">✌🏿✌🏿 IRS(incremental Static Regeneration)</h3>
<p>증분 정적 사이트를 재생성하는, 다시 말해 특정 주기로 정적인 사이트의 데이터를 가져와서 다시 그려두는 것. </p>
<p>이것을 담당하는 함수는 getStaticPops (with revalidate)이다. </p>
<p>pages &gt; isg.js 파일을 아래와 같이 생성해 보자. </p>
<pre><code>import Head from &#39;next/head&#39;;
import styles from &#39;../styles/Home.module.css&#39;;



export async function getStaticProps() {
    console.log(&#39;server&#39;);
    return {
        props: { time: new Date().toISOString() },
        revalidate: 1,
    }
}


export default function ISR({ time }) {


    return (
        &lt;div className={styles.container}&gt;
            &lt;Head&gt;
                &lt;title&gt;Create Next App&lt;/title&gt;

                &lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;
            &lt;/Head&gt;

            &lt;main&gt;
                &lt;h1 className={styles.title}&gt;
                    {time}
                &lt;/h1&gt;

            &lt;/main&gt;
            ///</code></pre><p>서버를 실행했을 때 아래와 같이 1초 간격으로 server로그가 남는 것을 확인할 수 있다. 
<img src="https://velog.velcdn.com/images/junmieee_/post/058568e8-c891-44f7-990c-4140b6e8ad36/image.png" alt=""></p>
<p>특정 시간 마다 revalidate옵션을 사용해 데이터를 새롭게 가져와 보일 수 있게 하는 장점이 있다. 상황에 맞춰서 SSG와 함께 사용하면 효과적일 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git memo]]></title>
            <link>https://velog.io/@junmieee_/Git-memo</link>
            <guid>https://velog.io/@junmieee_/Git-memo</guid>
            <pubDate>Thu, 02 Mar 2023 10:24:30 GMT</pubDate>
            <description><![CDATA[<iframe src="https://giphy.com/embed/hs1wBxNGuR7z2LyzHT" width="480" height="270" frameBorder="0" class="giphy-embed" allowFullScreen></iframe><p><a href="https://giphy.com/gifs/fallontonight-country-yee-haw-go-on-git-hs1wBxNGuR7z2LyzHT">via GIPHY</a></p>


<ul>
<li><p><code>pwd</code></p>
<ul>
<li>현재 위치</li>
</ul>
</li>
<li><p><code>ls -al</code></p>
<ul>
<li>현재 위치의 디렉토리 확인</li>
</ul>
</li>
<li><p><code>cd</code></p>
<ul>
<li>이동</li>
</ul>
</li>
<li><p><code>git init</code></p>
<ul>
<li>.git 파일이 없을 경우 → git으로 관리하겠다는 의미</li>
<li>git init → .git 파일 생성</li>
</ul>
</li>
<li><p><code>git remote -v</code></p>
<ul>
<li>현재 remote를 확인함</li>
<li>remote가 없을 경우, remote 생성</li>
</ul>
</li>
<li><p><code>git remote add &lt;name&gt; &lt;repo url&gt;</code></p>
<ul>
<li>remote 생성</li>
<li>주로 name = origin</li>
</ul>
</li>
<li><p><code>git status</code></p>
<ul>
<li>git 현재 정보 확인</li>
</ul>
</li>
<li><p>git token</p>
<pre><code class="language-jsx">  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</code></pre>
</li>
<li><p><code>git add &lt;파일명&gt;</code></p>
<ul>
<li>git add . → 현재 위치 전체 파일 staging에 추가</li>
</ul>
</li>
<li><p><code>git commit -m &quot;&lt;커밋 메시지&gt;&quot;</code></p>
</li>
<li><p><code>git push &lt;remote name&gt; &lt;branch name&gt;</code></p>
<ul>
<li>Remote name은 보통 origin.</li>
<li>→ staging에서 Repository로 변경사항 적용.</li>
</ul>
</li>
<li><p><code>git checkout -b &lt;branch name&gt;</code></p>
<ul>
<li>해당 브랜치 생성 및 브랜치 변경</li>
<li>그냥 checkout → 브랜치 변경</li>
</ul>
</li>
<li><p><code>git branch</code></p>
<ul>
<li>브랜치 리스트업</li>
</ul>
</li>
</ul>
<p>참고 블로그: <a href="https://velog.io/@4_21ee/TIL-27-Git%EC%B0%AE%EC%95%84%EB%8F%84-%EC%95%8C%EC%95%84%EB%91%AC%EC%95%BC%ED%95%A0-Git%EB%AA%85%EB%A0%B9%EC%96%B4">Git 참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Immer ]]></title>
            <link>https://velog.io/@junmieee_/Immer</link>
            <guid>https://velog.io/@junmieee_/Immer</guid>
            <pubDate>Fri, 24 Feb 2023 08:16:55 GMT</pubDate>
            <description><![CDATA[<p>불편성을 위해 Immer라이브러리 사용</p>
<pre><code>const reducer = (state = initialState, action) =&gt; {
        ....
        case ADD_COMMENT_SUCCESS:
            const postIndex = state.mainPosts.findIndex((y) =&gt; y.id === action.data.postId);
            const post = { ...state.mainPosts[postIndex] };
            post.Comments = [dummyComment(action.data.content), ...post.Comments];
            const mainPosts = [...state.mainPosts];
            mainPosts[postIndex] = post;



            return {
                ...state,
                mainPosts,
                addCommentLoading: false,
                addCommentDone: true,
            };
        case ADD_COMMENT_FAILURE:
            return {
                ...state,
                addCommentLoading: false,
                addCommentError: action.error,
            }

        default:
            return state;
    }
}

</code></pre><p>immer 사용</p>
<pre><code>const reducer = (state = initialState, action) =&gt; {
    return produce(state, (draft) =&gt; {
        switch (action.type) {
            case ADD_POST_REQUEST:
                draft.addPostLoading = true;
                draft.addPostDone = false;
                draft.addPostError = null;
                break;
            case ADD_POST_SUCCESS:
                draft.mainPosts.unshift(dummyPost(action.data))
                draft.addPostLoading = false;
                draft.addPostDone = true;
                break;
            case ADD_POST_FAILURE:
                draft.addPostLoading = false;
                draft.addPostError = action.error;
                break;
            case REMOVE_POST_REQUEST:
                draft.removePostLoading = true;
                draft.removePostDone = false;
                draft.removePostError = null;
                break;
            case REMOVE_POST_SUCCESS:

                draft.mainPosts = draft.mainPosts.filter((y) =&gt; y.id !== action.data);
                draft.removePostLoading = false;
                draft.removePostDone = true;
                break;
            case REMOVE_POST_FAILURE:
                draft.removePostLoading = false;
                draft.removePostError = action.error;
                break;
            case ADD_COMMENT_REQUEST:
                draft.removeCommentLoading = true;
                draft.removeCommentDone = false;
                draft.removeCommentError = null;
            case ADD_COMMENT_SUCCESS:
                {
                    const post = draft.mainPosts.find((v) =&gt; v.id === action.data.postId);
                    post.Comments.unshift(dummyComment(action.data.content));
                    draft.addCommentLoading = false;
                    draft.addCommentDone = true;
                    // const postIndex = state.mainPosts.findIndex((y) =&gt; y.id === action.data.postId);
                    // const post = { ...state.mainPosts[postIndex] };
                    // post.Comments = [dummyComment(action.data.content), ...post.Comments];
                    // const mainPosts = [...state.mainPosts];
                    // mainPosts[postIndex] = post;



                    // return {
                    //     ...state,
                    //     mainPosts,
                    //     addCommentLoading: false,
                    //     addCommentDone: true,
                    // };
                    break;
                }
            case ADD_COMMENT_FAILURE:

                draft.addCommentLoading = false;
                draft.addCommentError = action.error;
                break;
            default:
                break;
        }

    });

}



export default reducer</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Warning: Encountered two children with the same key, `2`]]></title>
            <link>https://velog.io/@junmieee_/Warning-Encountered-two-children-with-the-same-key-2</link>
            <guid>https://velog.io/@junmieee_/Warning-Encountered-two-children-with-the-same-key-2</guid>
            <pubDate>Fri, 24 Feb 2023 04:01:50 GMT</pubDate>
            <description><![CDATA[<p>Nodebird 프로젝트를 진행하면서 마주친 에러 해결</p>
<pre><code>npm i shortid</code></pre><p>*<em>Reducer/Post.js
*</em></p>
<pre><code>import shortId from &#39;shortid&#39;


export const dummyPost = (data) =&gt; ({
    id: shortId.generate(),
    content: data,
    User: {
        id: 1,
        nickname: &#39;Chloe&#39;
    },
    Images: [{
        src: &#39;&#39;
    }],
    Comments: [],

});</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Redux-thunk 이해하기]]></title>
            <link>https://velog.io/@junmieee_/Redux-thunk-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@junmieee_/Redux-thunk-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 24 Feb 2023 03:21:42 GMT</pubDate>
            <description><![CDATA[<p>Next-js에서 Redux-thunk 사용</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js에서 Redux 사용하기]]></title>
            <link>https://velog.io/@junmieee_/Next.js%EC%97%90%EC%84%9C-Redux-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@junmieee_/Next.js%EC%97%90%EC%84%9C-Redux-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 23 Feb 2023 06:03:32 GMT</pubDate>
            <description><![CDATA[<p>Next.js 에서 Redux 사용</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React에서 Redux 사용하기]]></title>
            <link>https://velog.io/@junmieee_/React%EC%97%90%EC%84%9C-Redux-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@junmieee_/React%EC%97%90%EC%84%9C-Redux-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 23 Feb 2023 06:01:40 GMT</pubDate>
            <description><![CDATA[<p>Redux</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSR과 CSR]]></title>
            <link>https://velog.io/@junmieee_/SSR%EA%B3%BC-CSR</link>
            <guid>https://velog.io/@junmieee_/SSR%EA%B3%BC-CSR</guid>
            <pubDate>Thu, 23 Feb 2023 06:00:10 GMT</pubDate>
            <description><![CDATA[<p>SSR</p>
<p>SCR</p>
]]></description>
        </item>
    </channel>
</rss>