<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>taetae-5.log</title>
        <link>https://velog.io/</link>
        <description>이게 마즐까?</description>
        <lastBuildDate>Tue, 27 Feb 2024 23:34:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. taetae-5.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/taetae-5" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[FE Article] 리액트 커뮤니티의 훅(hook)들]]></title>
            <link>https://velog.io/@taetae-5/FE-Article-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0%EC%9D%98-%ED%9B%85hook%EB%93%A4</link>
            <guid>https://velog.io/@taetae-5/FE-Article-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0%EC%9D%98-%ED%9B%85hook%EB%93%A4</guid>
            <pubDate>Tue, 27 Feb 2024 23:34:46 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>react-hook-form: 간단한 코드 몇 줄 만으로 무거운 폼(form) 코드를 처리할 수 있습니다.</p>
</li>
<li><p>useUpdateEffect: 업데이트 시에만 동작하는 useEffect입니다</p>
</li>
<li><p>useCopyToClipborad: 클립보드 복사가 필요할 때 사용합니다</p>
</li>
<li><p>useLocalStorage: 브라우저 로컬 스토리지를 간단하게 사용합니다</p>
</li>
<li><p>useHover: 엘리먼트가 hover 됐는지 확인할 때 사용합니다</p>
</li>
<li><p>useIdle: 사용자가 유휴(idle) 상태인지 확인할 때 사용합니다</p>
</li>
<li><p>useClickAway: 특정 컴포넌트의 외부를 클릭할 경우 함수를 트리거합니다.</p>
</li>
<li><p>useDebounce: 특정 함수를 실행하기 까지 일정시간 기다립니다</p>
</li>
<li><p>useWindowSize: 스크린의 높이, 너비를 변화를 감지하여 업데이트합니다</p>
</li>
<li><p>useSwr: 컴포넌트가 마운트 될 때 발생하는 요청을 쉽게 처리할 수 있습니다</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect와 useLayoutEffect 차이점]]></title>
            <link>https://velog.io/@taetae-5/useEffect%EC%99%80-useLayoutEffect-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@taetae-5/useEffect%EC%99%80-useLayoutEffect-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Thu, 02 Nov 2023 00:36:33 GMT</pubDate>
            <description><![CDATA[<h3 id="차이점">차이점.</h3>
<p><strong>useLayoutEffect</strong>는 동기적으로, 렌더링 전에 호출이 일어나고 <strong>useEffect</strong>는 비동기적으로 렌더링 후에 호출이 일어난다.</p>
<p>성능 문제가 발생할 경우만 <strong>useLayoutEffect</strong>을 사용하고 기본적으로는 <strong>useEffect</strong>를 사용하는 것이 좋다. </p>
<p>제일 큰 차이점은 <strong>useEffect</strong>는 비동기적으로 동작하고 <strong>useLayoutEffect</strong>는 동기적으로 동작해요. 리액트에서 <strong>useEffect</strong>는 <strong>렌더링</strong>이 끝나고 특정 행동을 수행하고, <strong>useLayoutEffect</strong>는 렌더링 전에 특정 행동을 수행해요.</p>
<h3 id="각각-언제-사용">각각 언제 사용?</h3>
<p>기본적으로, 데이터 조회와 상태 업데이트 시 <strong><code>useEffect</code></strong>를 사용하고, 레이아웃과 관련된 작업을 수행할 때에만 <strong><code>useLayoutEffect</code></strong>를 사용하는 것이 일반적으로 좋은 방법입니다. <strong><code>useLayoutEffect</code></strong>는 동기적으로 실행되며, DOM 변경 사항에 직접적인 영향을 미치므로 사용 시점을 신중하게 결정해야 합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[당신의 이미지는 어디로? (Feat. Base64)]]></title>
            <link>https://velog.io/@taetae-5/%EB%8B%B9%EC%8B%A0%EC%9D%98-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%8A%94-%EC%96%B4%EB%94%94%EB%A1%9C-Base64%EC%97%90%EC%84%9C-DB-</link>
            <guid>https://velog.io/@taetae-5/%EB%8B%B9%EC%8B%A0%EC%9D%98-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%8A%94-%EC%96%B4%EB%94%94%EB%A1%9C-Base64%EC%97%90%EC%84%9C-DB-</guid>
            <pubDate>Wed, 11 Oct 2023 00:07:50 GMT</pubDate>
            <description><![CDATA[<p><strong>내용</strong></p>
<p>기존의 했던 방식은 <code>Base64</code>로 변환처리를 하면 당연히 압축으로 인한 데이터 손실이 있다고 판단됐기 때문에 <code>formData</code> 형식에 맞게 요청을 하고 이미지를 S3의 bucket과 같은 저장소로 보내거나</p>
<p><code>Firebase</code>와 같은 클라우드 데이터베이스의 경우 <code>Firebase storage</code> 와 같은 bucket 저장소를 사용했다. 물론 이는 중요했고, 비교적 용량이 큰 이미지들 예를 들어서 상세 페이지에 필요한 이미지들과 같은 것들은 변환 처리를 하게 되면 압축 과정에서 잃게 되는 것들이 많고 해상도가 깨질 수 도 있다. 그리고 가장 중요한 것은 <code>Base64</code> 의 변환 방식은 인코딩하기 위해서는 제한된 용량이 있었다.  그렇기에 결국엔 압축을 위해서 용량의 한계도 있는 것을 이것을 과연 써야 하나? 그렇기에 많은 사람들이 Base64의 인코딩 방식에 회의적인 시각이 보였다.</p>
<p><strong>과연 그럴까?</strong></p>
<p>사실 모든 기능들엔 이유가 있음을 확인하였다. 분명한것은 Base64는 단점이 극단적으로 부각이 된다. <code>데이터의 용량</code> , CSS를 통한 이미지 랜더링 저하, 캐시기능 저하 하지만 이것은 어디까지나 큰 용량의 이미지라면 그럴 수있다.</p>
<p>반면에 <code>썸네일</code> 이나 아이콘과 같은 작은 저용량의 이미지들을 필요로 하는 DOM에 대해서는 과연 고용량의 이미지가 필요할까? 작은 용량에 대해서 반드시 그럴 필요는 없고 변환을 통해서 DB에 바로 저장을 하는 것이 더 편하고 효율적이라는 것을 깨달았다.</p>
<p><strong>느낀점</strong></p>
<p>어느 기술이든 과유불급일 수 있다. 마치 많은 사람들이 Base64는 절대 쓰면 안되는 것 처럼 표현되어 있는데 너무 과하게 쓰면 그렇겠지만 장단점을 잘 파악하고 적재적소에 잘 사용한다면 base64 인코딩도 그렇게 나쁜 것만은 아니다.</p>
<p>Base64는 절대 쓰면 안되는 것 처럼 표현되어 있지만 너무 과하게 쓰면 그렇겠지만 장단점을 잘 파악하고 적재적소에 잘 사용한다면 base64 인코딩도 그렇게 나쁜 것만은 아니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ES2023] 배열과 관련된 자바스크립트 새로운 기능]]></title>
            <link>https://velog.io/@taetae-5/ES2023%EC%9D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@taetae-5/ES2023%EC%9D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%83%88%EB%A1%9C%EC%9A%B4-%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Fri, 26 May 2023 07:27:03 GMT</pubDate>
            <description><![CDATA[<p>최근에 <code>ES2023</code> 스팩이 마무리 되면서 출시될 예정이다. 특히 배열과 관련된 기능에서 추가가 되었다.</p>
<p><img src="https://velog.velcdn.com/images/taetae-5/post/5c791e88-b25e-460b-827a-2947ad7cde8d/image.png" alt=""></p>
<p>(출처 - <a href="https://github.com/tc39/proposals/blob/main/finished-proposals.md">https://github.com/tc39/proposals/blob/main/finished-proposals.md</a>)</p>
<h2 id="1-array-find-from-last">1. Array find from last</h2>
<p>(출처 - <a href="https://github.com/tc39/proposal-array-find-from-last">https://github.com/tc39/proposal-array-find-from-last</a>)</p>
<p>기존의 <code>array</code> 타입에 두 개의 메소드가 추가가 되는데, 바로 <code>.findLast()</code>, <code>findLastIndex()</code> 입니다.</p>
<blockquote>
<p><strong><em>💡요약</em></strong>
배열의 마지막 요소를 기준으로 탐색</p>
</blockquote>
<h3 id="동기motivation에는-다음과-같이-명시-되었는데">동기(Motivation)에는 다음과 같이 명시 되었는데,</h3>
<blockquote>
<p>Finding an element in an array is a very common programming pattern.
The proposal has a major concerns: Semantical. Which means clearly representing the operation i want.
And with the changes. There&#39;s a sugar here: Performance. Avoid obvious overhead. And may improve the constant factors in the time complexity. Even there&#39;s not an order of magnitude change. But it&#39;s may useful in some performance-sensitive scenarios. eg: React render function.</p>
</blockquote>
<p>요약하자면, 자바스크립트의 배열에서 요소를 찾는 방법에 대한 이해와 관련된 내용을 다루며, 성능 개선을 위해 변경이 된다는 것을 나타내고 있다. 특히, <strong>React의 랜더링 함수가 성능에 민감한 상황에서 사용될 수 있다(?)는 예시를 들고 있다.</strong> </p>
<h3 id="기존의-find-와-findindex를-사용">기존의 <code>find()</code> 와 <code>findIndex()</code>를 사용</h3>
<pre><code class="language-js">const array = [{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }];

array.find(n =&gt; n.value % 2 === 1); // { value: 1 }
array.findIndex(n =&gt; n.value % 2 === 1); // 0</code></pre>
<h3 id="특정요소를-배열의-끝에서-찾을-때는">특정요소를 배열의 끝에서 찾을 때는,</h3>
<p> <code>reverse()</code> 를 사용하고 <code>find()</code>를 하는 것이 처음부터 <code>find</code> 를 하는 것보다 나은 선택이었다.
 즉, 내가 원하는 요소가 배열의 앞 부분 보다는 끝 부분에서 찾아서 볼 때가 유리하다고 판단이 될 때는 이렇게 역순으로 만든 뒤, 찾는 것이 유리했다.
 하지만, 이렇게 될 경우 원본 배열을 변경하면서 배열의 무결성을 해치게 된다. 즉, <code>배열의 사이드 이팩트</code>가 발생한다.</p>
<pre><code class="language-js">
// ======== Before the proposal =========== 

// find
[...array].reverse().find(n =&gt; n.value % 2 === 1); // { value: 3 }

// findIndex
array.length - 1 - [...array].reverse().findIndex(n =&gt; n.value % 2 === 1); // 2
array.length - 1 - [...array].reverse().findIndex(n =&gt; n.value === 42); // should be -1, but 4
</code></pre>
<h3 id="findlast"><code>findLast()</code></h3>
<p>하지만 새로운 <code>findLast()</code> 메서드를 사용하게 된다면, 원본 배열을 해치지 않고 사용할 수 있다. <code>reverse()</code>를 사용할 때 생기는 오버헤드나, 퍼포먼스 부문을 고려했을때, 제안이 되었다.</p>
<pre><code class="language-js">// ======== In the proposal =========== 
// find
array.findLast(n =&gt; n.value % 2 === 1); // { value: 3 }</code></pre>
<h3 id="findlastindex"><code>findLastIndex()</code></h3>
<p>기존의 <code>findIndex()</code> 와 마찬가지로 있을 return값은 같다.</p>
<pre><code class="language-js">// findIndex
array.findLastIndex(n =&gt; n.value % 2 === 1); // 2
array.findLastIndex(n =&gt; n.value === 42); // -1
</code></pre>
<h2 id="2-change-array-by-copy">2. Change Array by copy</h2>
<p>(출처 - <a href="https://github.com/tc39/proposal-change-array-by-copy">https://github.com/tc39/proposal-change-array-by-copy</a>)</p>
<p>배열의 복사에 관한 기능은 네 가지가 추가가 되었는데, 다음과 같습니다.</p>
<ul>
<li><code>Array.prototype.toReversed() -&gt; Array</code></li>
<li><code>Array.prototype.toSorted(compareFn) -&gt; Array</code></li>
<li><code>Array.prototype.toSpliced(start, deleteCount, ...items) -&gt; Array</code></li>
<li><code>Array.prototype.with(index, value) -&gt; Array</code></li>
</ul>
<p>작업 대상 배열을 직접적으로 하는 것 대신에, 작업 수행 배열의 복사본을 반환하도록 합니다. 
기존에 배열을 처리했을 때, <code>mutation</code>을 방지 하기 위해서 복사본을 만들어주고, 그 다음 복사본을 변경하면서 호출했지만 그부분에 대해서 퍼포먼스에 대해서 최적화를 시켰다.</p>
<blockquote>
<p><strong><em>💡요약</em></strong>
변형된 복사 배열 반환, 원본 배열의 변경 x</p>
</blockquote>
<h3 id="기존에-배열의-역순으로-만들기-위해서는">기존에 배열의 역순으로 만들기 위해서는??</h3>
<pre><code class="language-js">const sequence = [1, 2, 3];

const reversed = [...sequence].reverse()</code></pre>
<p>이러한 작업을 이제 단순히 <code>toReversed()</code>로 대체할 수 있다. </p>
<h3 id="toreversed-tosorted-with-예시"><code>toReversed()</code>, <code>toSorted()</code>, <code>with()</code> 예시</h3>
<ul>
<li><code>toReversed()</code> : 배열의 역순으로 배열을 복사하고 반환한다.</li>
<li><code>toSorted()</code> : 배열을 정렬한 뒤, 복사하고 반환한다.</li>
<li><code>with()</code> : 첫 번 째 인자에는 index를 넣고, 두 번째 인자에 바뀔 요소를 넣어서 변환된 배열을 출력하도록 한다.</li>
</ul>
<p>복사본이 반환(return) 되고, 원본은 그대로 유지가 된다.</p>
<pre><code class="language-js">const sequence = [1, 2, 3];
sequence.toReversed(); // =&gt; [3, 2, 1] 
sequence; // =&gt; [1, 2, 3] 


const outOfOrder = new Uint8Array([3, 1, 2]);
outOfOrder.toSorted(); // =&gt; Uint8Array [1, 2, 3]
outOfOrder; // =&gt; Uint8Array [3, 1, 2]

const correctionNeeded = [1, 1, 3];
correctionNeeded.with(1, 2); // =&gt; [1, 2, 3]
correctionNeeded; // =&gt; [1, 1, 3]
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD(테스트 주도 개발)에 대하여..]]></title>
            <link>https://velog.io/@taetae-5/TDD%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</link>
            <guid>https://velog.io/@taetae-5/TDD%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC</guid>
            <pubDate>Thu, 25 May 2023 12:26:32 GMT</pubDate>
            <description><![CDATA[<p>요즘 취준을 하면서 매일같이 보는 <code>자격요건</code>에 TDD라는 단어가 자주 보인다. 도대체 TDD가 뭐길래? 심심찮게 보이고 있어 결국 공부를 해야겠다고 마음을 먹게 되면서 정리를 하게 되었다. </p>
<h2 id="tddtest-driven-development란">TDD(Test Driven Development)란?</h2>
<p><code>TDD</code>는 테스트 주도 개발은 개발방식 중 하나로 테스트 코드를 먼저 작성하고, 그 테스트를 통과시킬 수 있는 최소한의 코드로 개발을 진행하는 것을 말한다.
즉, TDD의 주요 아이디어는 개발자가 코드를 작성하기 전에 테스트를 먼저 작성한다는 것이다.</p>
<h2 id="tdd의-목표는">TDD의 목표는?</h2>
<p><code>TDD</code>의 궁극적인 목표는 Clean Code that works 즉, 작동하는 클린코드를 말한다. <code>TDD</code>의 다른말로는, Test First Programming 으로, 테스트를 먼저 작동하는 방식을 의미한다.</p>
<blockquote>
<p><strong><em>테스트란?</em></strong>
프로그램을 실행하여 오류와 결함을 검증하고 애플리케이션이 요구사항에 맞게 동작하는지 검증하는 절차를 의미한다.</p>
</blockquote>
<h2 id="왜-tdd가-필요한가-feat-기술부채">왜 TDD가 필요한가? (feat 기술부채)</h2>
<p><code>TDD</code>를 도입하는 이유는 대표적으로 <code>기술부채</code>를 막기 위함이다. </p>
<h3 id="1-기술부채란">1) 기술부채란?</h3>
<blockquote>
<p><strong><em>기술부채란?</em></strong>
기술 부채(Technical debt)는 소프트웨어 개발에서 발생하는 개념으로, 개발자가 의도적으로 혹은 무의식적으로 소프트웨어의 품질을 희생하고 생산성을 높이기 위해 채택한 결정 또는 절차이다.</p>
</blockquote>
<p>이는 소프트웨어 개발 과정에서 더 빠르게 결과물을 제공하거나, 기능을 추가하기 위해 일시적인 해결책을 선택하는 것을 의미합니다.</p>
<p>즉, 다시말하면 품질보다 생산성에 초점을 두면 결국, 문제가 터질 수 있다는 말이다. </p>
<p>그러면 어떤 상황에서 기술부채가 생겨날까?</p>
<h3 id="2-기술부채가-발생하는-상황">2) 기술부채가 발생하는 상황</h3>
<p>기술 부채는 주로 <code>코드의 임시적 해결</code>, <code>설계의 절충</code> , <code>기술적인 빚</code> 등의 세 가지 경우에서 발생할 수 있다.</p>
<p><strong><em>1. 코드의 임시적인 해결</em></strong>
개발자가 기능을 빠르게 추가하기 위해 효율적인 해결책 대신에 <code>임시</code>로 작동하는 코드를 작성하는 경우입니다. 이는 나중에 코드를 개선하거나 <strong>리팩토링해야 하는 부담</strong>을 야기할 수 있습니다.</p>
<p><strong><em>2. 설계의 절충</em></strong>
제한된 시간이나 자원으로 인해 효율적인 설계를 <code>생략</code>하거나 <code>간단한 설계</code>를 선택하는 경우입니다. 이는 기능 추가 또는 변경이 발생할 때 코드를 수정하기 어렵게 만들 수 있습니다.</p>
<p><strong><em>3. 기술적인 빚</em></strong>
새로운 기술이나 도구를 적용하기 위해 초기에 일부 품질을 희생하는 경우입니다. 이는 추후에 기술적인 빚을 상환하거나 코드를 수정해야 할 필요성을 야기할 수 있습니다.</p>
<blockquote>
<p>결국, 기술부채는 처음에는 빠른 개발과 생산성 향상을 제공하지만, 시간이 지나면서 문제 발생과 비용을 더욱 증가시킬 수 있다. (코드의 가독성, 유지 보수성 저하, 버그의 발생 증가, 성능 저하)</p>
</blockquote>
<p>기술 부채를 관리하기 위해서는 개발자와 관리자들은 부채의 존재를 인식하고, 적절한 시기에 <code>리팩토링</code>이나 <code>코드 개선</code> 작업을 수행해야 한다. </p>
<h2 id="tdd-cycle">TDD Cycle</h2>
<p><img src="https://velog.velcdn.com/images/taetae-5/post/7bc5e233-d42e-4c77-bc3b-0fd4f1d1dada/image.png" alt=""></p>
<p><code>TDD</code>는 다음과 같이 <code>실패</code> -&gt; <code>통과</code> -&gt; <code>개선</code> 사이클을 갖게 되는데, 
처음에 실패한 뒤, 통과를 시킨다. 그다음 통과된 코드에서 중복을 제거하고 개선된 코드의 사이클을 둔다.
하지만, 문제는 테스트가 가능한 코드를 구현하는 것인데, 규모가 커지고 로직이 복잡할 수록 이러한 TDD를 구현하는 것이 어려울 수 있다. </p>
<h2 id="how-to-make-testable-code">How to make Testable Code</h2>
<p>테스트해야할 코드나 기능단위의 덩어리가 커지거나 상태관리에 대해서 복잡해질 경우 <code>Testable Code</code> 즉, 테스트가 가능한 코드를 구현하는 것은 어려움이 따른다. </p>
<blockquote>
<p>따라서, <strong><em>관심사의 분리(Separation of Concerns)</em></strong>가 중요하다.</p>
</blockquote>
<p>*관심사 분리란?</p>
<blockquote>
<p>개별 요소들이 자신이 관심받고 있는 곳에만 집중하여 관심을 기울이는 SW 엔지니어링 기법이다.</p>
</blockquote>
<h2 id="count-증감-버튼에-따라는-숫자-출력-테스트-예시">Count 증감 버튼에 따라는 숫자 출력 테스트 예시</h2>
<pre><code>📦components
 ┣ 📜AwesomeCounter.test.tsx
 ┗ 📜AwesomeCounter.tsx</code></pre><p>증감 버튼과 관련 컴포넌트에 대해서 공통의 관심사에 맞게 네이밍과 배치를 하였다.</p>
<pre><code class="language-tsx">interface Props {
  initialValue?: number;
}

const AwesomeCounter = ({ initialValue }: Props) =&gt; {
  const [count, setCount] = useState(initialValue ?? 0);

  const add = () =&gt; {
    setCount((prev) =&gt; prev + 1);
  };

  const remove = () =&gt; {
    setCount((prev) =&gt; {
      const result = prev - 1;
      if (result &lt; 0) {
        return 0;
      }
      return result;
    });
  };
  return (
    &lt;div&gt;
      &lt;h1&gt;Awesome Counter&lt;/h1&gt;
      &lt;button onClick={remove}&gt;Remove&lt;/button&gt;
      &lt;span&gt;{count}&lt;/span&gt;
      &lt;button onClick={add}&gt;Add&lt;/button&gt;
    &lt;/div&gt;
  );
};

export default AwesomeCounter;</code></pre>
<h3 id="1-test--props로-초기값을-전달할-때-테스트">1. TEST : Props로 초기값을 전달할 때, 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">7</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">7</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-jsx">test(&quot;it should have the correct initial value when set to 7&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={7} /&gt;);
  const count = screen.queryByText(7); // 문자열로 비교하도록
  expect(count).toBeInTheDocument();
});</code></pre>
<h3 id="2-props로-초기값을-전달하지-않을-때-0-출력-테스트">2. Props로 초기값을 전달하지 않을 때, 0 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center"><code>null</code></td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should have the correct initial value when of 0&quot;, () =&gt; {
  render(&lt;AwesomeCounter /&gt;);
  const count = screen.queryByText(0);
  expect(count).toBeInTheDocument();
});</code></pre>
<h3 id="3-초깃값이-0일때-add-버튼-클릭-시-1-출력-테스트">3. 초깃값이 0일때, add 버튼 클릭 시 1 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should increment the count by 1 when the add button is clicked&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={0} /&gt;);
  const addButton = screen.getByText(&quot;Add&quot;);
  fireEvent.click(addButton);
  const count = screen.getByText(1);
  expect(count).toBeVisible();
});</code></pre>
<h3 id="4-초기값이-1일때-add-버튼-클릭시-2-출력-테스트">4. 초기값이 1일때, add 버튼 클릭시 2 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">2</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should have the correct initial value add is clicked once&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={1} /&gt;);
  const addButton = screen.getByText(&quot;Add&quot;);
  fireEvent.click(addButton);
  const count = screen.getByText(2);
  expect(count).toBeVisible();
});</code></pre>
<h3 id="5-초기값이-1일때-add-버튼-두-번-클릭시-3-출력-테스트">5. 초기값이 1일때, add 버튼 두 번 클릭시 3 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">1</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">3</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should have the correct initial value add is clicked twice&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={1} /&gt;);
  const addButton = screen.getByText(&quot;Add&quot;);
  fireEvent.click(addButton);
  fireEvent.click(addButton);
  const count = screen.queryByText(3);
  expect(count).toBeVisible();
});</code></pre>
<h3 id="6-초기값이-5일때-remove-버튼-클릭시-4-출력-테스트">6. 초기값이 5일때, remove 버튼 클릭시 4 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">5</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">4</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should have the correct initial value remove is clicked once&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={5} /&gt;);
  const removeButton = screen.getByText(&quot;Remove&quot;);
  fireEvent.click(removeButton);
  const count = screen.queryByText(4);
  expect(count).toBeVisible();
});</code></pre>
<h3 id="7-초기값이-5일때-remove-버튼-두-번-클릭시-3-출력-테스트">7. 초기값이 5일때, remove 버튼 두 번 클릭시 3 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">5</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">3</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should have the correct initial value remove is clicked twice&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={5} /&gt;);
  const removeButton = screen.getByText(&quot;Remove&quot;);
  fireEvent.click(removeButton);
  fireEvent.click(removeButton);
  const count = screen.queryByText(3);
  expect(count).toBeVisible();
});</code></pre>
<h3 id="8-초기값이-0일때-remove-버튼-두-번-클릭시-0-출력-테스트">8. 초기값이 0일때, remove 버튼 두 번 클릭시 0 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should not allow a negative number when the initial value is 0 and remove is clicked&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={0} /&gt;);
  const removeButton = screen.getByText(&quot;Remove&quot;);
  fireEvent.click(removeButton);
  const count = screen.queryByText(0);
  expect(count).toBeVisible();
});</code></pre>
<h3 id="9-초기값이-2일때-remove-버튼-네-번-클릭시-0-출력-테스트">9. 초기값이 2일때, remove 버튼 네 번 클릭시 0 출력 테스트</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">초깃값</td>
<td align="center">2</td>
</tr>
<tr>
<td align="center">결과</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">테스트</td>
<td align="center"><code>PASS</code></td>
</tr>
</tbody></table>
<pre><code class="language-tsx">test(&quot;it should not allow a negative number when the initial value is 0 and remove is clicked&quot;, () =&gt; {
  render(&lt;AwesomeCounter initialValue={2} /&gt;);
  const removeButton = screen.getByText(&quot;Remove&quot;);
  fireEvent.click(removeButton);
  fireEvent.click(removeButton);
  fireEvent.click(removeButton);
  fireEvent.click(removeButton);

  const count = screen.queryByText(0);
  expect(count).toBeVisible();
});</code></pre>
<h2 id="tdd를-공부하며-느낀점">TDD를 공부하며 느낀점</h2>
<p>단순 겉핥기식으로 기본적인 기능단위의 TDD를 구현하였으나, 과연 이게 복잡해지는 기능 단위에 대해서는 어떻게 구현해야할지 어려움이 생길 것 같다고 판단이 된다.
그리고 단순히 구현하기 전에 테스트 작성을 먼저 진행하면서 초기 시간적으로 더 오래 걸리게 되는 것은 어쩔수 없는 것 같다.</p>
<p>하지만 그럼에도 장점이 있다면?</p>
<p>잘 구현하면 오류를 조기에 발견하는 것과 개발 초기 단계부터 관심사분리에 더 집중하면서 리팩토링에 대해 더 많이 생각해볼 수 있을 것 같다.</p>
<p>하지만 이것을 과연 어떻게 잘 적용을 해야할지는 더 해보면서 느껴봐야할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[mongoose 삭제 메서드]]></title>
            <link>https://velog.io/@taetae-5/mongoose-%EC%82%AD%EC%A0%9C-%EB%A9%94%EC%84%9C%EB%93%9C</link>
            <guid>https://velog.io/@taetae-5/mongoose-%EC%82%AD%EC%A0%9C-%EB%A9%94%EC%84%9C%EB%93%9C</guid>
            <pubDate>Tue, 25 Apr 2023 08:02:57 GMT</pubDate>
            <description><![CDATA[<h2 id="mongoose-model-delete-method">Mongoose model delete method</h2>
<blockquote>
<p>대표적인 메서드</p>
</blockquote>
<ul>
<li>remove</li>
<li>deleteOne</li>
<li>findOneAndDelete</li>
</ul>
<p><code>Mongoose</code>의 버전이 <code>^4</code> 이상에서부터는 <code>remove</code> 메서드 대신 <code>deleteOne</code>, <code>deleteMany</code>, <code>findByIdAndDelete</code> 등의 새로운 메서드를 사용하게 되면서, 기존의 <code>remove</code>메서드는 더이상 지원하지 않게 되었다.</p>
<h3 id="기존-사용의-예시">기존 사용의 예시</h3>
<pre><code class="language-js">
const remove = async (req, res) =&gt; {
  try {
    const {
      params: { reviewId },
      user: { id },
    } = req;

    const review = await reviewModel.findOne({
      _id: reviewId,
      user: id,
    });

    if (!review) return responseHandler.notFound(res);

    await review.remove()

    responseHandler.ok(res);
  } catch {
    responseHandler.error(res);
  }
};
</code></pre>
<h3 id="새로-변경-예시">새로 변경 예시</h3>
<pre><code class="language-js">const remove = async (req, res) =&gt; {
  try {
    const {
      params: { reviewId },
      user: { id },
    } = req;

    const review = await reviewModel.findOne({
      _id: reviewId,
      user: id,
    });

    if (!review) return responseHandler.notFound(res);

    await review.deleteOne();

    responseHandler.ok(res);
  } catch {
    responseHandler.error(res);
  }
};
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[대취타] 프로젝트 후기 ]]></title>
            <link>https://velog.io/@taetae-5/%EB%8C%80%EC%B7%A8%ED%83%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@taetae-5/%EB%8C%80%EC%B7%A8%ED%83%80-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Mon, 17 Apr 2023 09:38:31 GMT</pubDate>
            <description><![CDATA[<p><img src="https://user-images.githubusercontent.com/75871005/232443886-5d18928e-f259-4906-a3a4-51faae580ae0.gif" alt="">
<img src="https://user-images.githubusercontent.com/75871005/232444628-994ce589-8ee5-4d76-8e82-d86879b4ee02.gif" alt=""></p>
<h2 id="프로젝트를-마치며">프로젝트를 마치며</h2>
<p>대략 두 달간동안 개인 프로젝트를 하며 진행했다. 취준을 하던차에 자기소개서들을 작성하면서 작성했던 <code>자기소개서를 보기좋게 관리해주는 서비스</code>가있다면 좋겠다는 생각으로 직접 만들어봐야겠다싶어 시작하게 되었다.</p>
<p>혼자서 프로젝트를 하면서 확실히 협업의 중요성을 더욱 느껴지게 되었다. 협업을 통해서 팀원들과 데이터의 스키마를 같이 상의하거나 어떤 라이브러리가 더 적합할지에 대한 협의가 필요한데 혼자서 하려하니 이게 과연 적합할지에 대한 고민을 공유하기에 다소 부족함이 있었다. 그리고 UI/UX적인 요소도 팀원들과 의논을 통해서 진행했다면 더욱 짧은 시간안에 완성될 수 있지 않았을까하는 생각을 갖게 되었다.  😭 😭</p>
<h2 id="자기소개서-관리-서비스">자기소개서 관리 서비스</h2>
<ul>
<li>재사용을 고려한 UI컴포넌트 만들기</li>
<li>Styled-component에서 emotion 사용하기</li>
<li>API 명세서도 작성해보기</li>
<li>데이터를 상태관리 or 캐싱된 데이터 사용하기</li>
<li>Auth를 검증을 위한 미들웨어 설정과 jwt 토큰을 사용하기</li>
<li>vercel을 통해 모노 레포 배포</li>
</ul>
<h3 id="작업-기간">작업 기간</h3>
<blockquote>
<p><strong>2023.02 ~ 2023.04</strong></p>
</blockquote>
<h3 id="기술-스택">기술 스택</h3>
<ul>
<li>FE - React / React query / emotion / recoil / Axios</li>
<li>BE - Express / firebase </li>
</ul>
<h2 id="라이브러리-선정">라이브러리 선정</h2>
<h3 id="redux-vs-react-query">Redux vs React Query</h3>
<p>이번 프로젝트에서는 상태관리에 대해서 어떤 라이브러리를 사용할까에 대한 고민이 있었다. 상태관리를 위한 대표적인 라이브러리로 <code>Redux</code>가 있고, 서버의 상태관리를 관리하는 라이브러리로 <code>React Query</code>가 있다. <code>Redux</code>와 <code>React Query</code>는 모두 React 애플리케이션에서 <strong>상태를 관리하기 위한 라이브러리</strong>이지만, <strong>사용목적에 따라서 차이가 있다.</strong> </p>
<h4 id="what-is-purpose-of-redux">What is Purpose of Redux?</h4>
<p>Redux는 상태를 <code>전역적으로 관리</code>하는 데 중점을 두고 있지만, React Query는 <code>데이터 가져오기</code>와 <code>캐싱</code>에 중점을 두고 있다. 따라서, Redux는 대규모 애플리케이션의 복잡한 상태 관리에 적합하며, <strong>상태가 전역에서 공유되는 경우에 사용하는 것이 적합</strong>하다.</p>
<h4 id="what-is-purpose-of-react-query">What is Purpose of React Query?</h4>
<p>React Query는 <strong>API 호출과 데이터 캐싱</strong>을 간단하게 처리하기 위한 목적으로 React Query는 각각의 데이터를 독립적으로 가져오고 관리하며, 컴포넌트 단위로 사용된다. 따라서 SPA나 중간 규모의 애플리케이션에 적합성이 있다.</p>
<blockquote>
<p>사용자 개인의 데이터르 관리하는 서비스로서 <code>CRUD</code>가 많기에 보다 나은 사용경험을 적용하기 위해서 React Query로 캐싱된 데이터를 이용하는 것이 좀 더 적합하지 않을까(?)하는 생각이 들어 React Query를 사용했다. </p>
</blockquote>
<h3 id="emotion-vs-styled-component">Emotion vs Styled-component</h3>
<p>CSS-in-JS의 라이브러리로 대표적으로 <code>Emotion</code> 과 <code>styled-components</code>가 있다. 블로그나 사이트들 등을 참고해보니 대게 <code>emotion</code>이 <code>styled-components</code>보다 조금 가볍고 빠르다고 알려져있다.</p>
<h3 id="그렇다면-어떤-차이점이">그렇다면 어떤 차이점이?</h3>
<h4 id="문법">문법?</h4>
<ul>
<li>emotion의 경우 css props로 css를 더 활용도 높게 조립할 수 있었다. 이를 통해 프로젝트의 UI와 관련된 컴포넌트에 대해서 커스텀이 쉽게 사용이 가능하다고 생각 되었다. </li>
<li>예를들어서 재사용 가능한 <code>Button</code> 을 만들었을때 추가적인 CSS요소가 필요할 수 있었다. 이떄 <code>CSS</code> Props를 내려주므로써 그때그떄 대응이 편리했다.</li>
</ul>
<h4 id="서버-렌더링">서버 렌더링</h4>
<ul>
<li>Emotion은 서버 측 렌더링을 지원하고, styled-components는 서버 측 렌더링을 지원하지만 구현 방법이 Emotion과 다르다. 하지만, SSR로 구현한 것은 아니었으니 이건 패스였다. 나중에 Next.js를 활용할때 사용하면 더 적합할 것 같다. </li>
</ul>
<h4 id="jsx-pragma">JSX pragma</h4>
<ul>
<li>물론 emotion의 경우 단점이라면 스타일을 추가하고 싶을 떄, <code>/** @jsx jsx */</code>을 컴포넌트마다 작성해야하는 단점이 있었다.</li>
</ul>
<blockquote>
<p>결론적으로 테마나 글로벌 스타일을 전역적으로 타입을 선언해서 사용하는 것에 더 사용이 유용하여 emtion을 적용하였다.</p>
</blockquote>
<h2 id="진행하며-고민했던-부분들">진행하며 고민했던 부분들?</h2>
<ul>
<li>임시 저장 기능</li>
<li>검색 기능</li>
</ul>
<h3 id="임시-저장-기능">임시 저장 기능</h3>
<p>사용자들이 자기소개서 작성을 할 때, 사람마다 스타일이 있겠지만 나같은 경우도 벨로그와 같은 글을 작성할 때, 수시로 <code>임시저장</code> 기능을 한다. 그렇기떄문에 <code>임시 저장</code>을 구현해야겠다는 생각을 하게 되었다.</p>
<p>먼저 사용자의 입장에서 사용 흐름을 정리해보면 다음과 같았다</p>
<h4 id="임시-저장-기능-서비스-흐름">임시 저장 기능 서비스 흐름</h4>
<p><img src="https://user-images.githubusercontent.com/75871005/232443911-658197c0-a043-4fac-a8c7-df7dfd76ce3a.gif" alt=""></p>
<ol>
<li>글을 작성하기 위해 <code>+</code> 버튼을 누른다.</li>
<li>글 작성 페이지로 들어간다. </li>
<li>글을 작성하던 도중 <code>임시 저장</code> 버튼을 누른다.</li>
<li><code>뒤로가기</code> 혹은 다른 네비게이션 메뉴 버튼을 클릭 후, 페이지 이동을 한다.</li>
<li>다시 작성했던 글을 보기 위해 임시 글 페이지에 들어간다.</li>
<li>임시 글 목록에서 해당 글을 클릭한뒤, 작성 페이지로 이동한다.</li>
<li>이때 이동시 해당 글의 페이지 url은 <code>/parma</code>가 뒤에 들어간다.</li>
<li>이에 맞게 데이터가 불러와진다.</li>
</ol>
<p>따라서, 글을 작성하는 행위에 있어서 <code>작성 페이지</code>, 작성한 글의 <code>상세 페이지</code>가 필요했다. 
결국 기능은 비슷하기에 하나의 컴포넌트에서 재사용이 가능하도록 구현하는 것이 좋겠다는 생각을 하게 되었다.</p>
<h4 id="post--put-어떻게-구분할까">POST / PUT 어떻게 구분할까?</h4>
<p>초기에 작성할 때는 <code>POST</code>를 해야하고, 도중에 임시 저장을 할 땐, <code>PUT</code>을 해야하는데 이때를 어떻게 구분을 할 수 있을까 생각을 하였을때, 단순히 <code>useState</code>로 상태관리해보자니 새로고침시 다시 초기화된다는 단점이 있었다 따라서, <code>Recoil</code>을 통해 전역상태 라이브러리를 사용하여 새로고침에도 대응하는 것이 좋겠다 판단 하였다.</p>
<p>페이지에 들어올 때, <code>/:id</code> <code>params</code>가 없다면 빈문자열을 전역상태로 넣고, 있을 경우에 해당 <code>/:id</code> params를 전역상태에 넣어준다. </p>
<ul>
<li>임시 저장버튼을 누를때, id의 전역상태가 없다면 HTTP요청으로<code>POST</code>를 사용하고 return된 데이터의 id를 전역상태로 관리해준다.</li>
<li>그리고 다시 임시저장 기능때 전역상태의 id가 있다면, HTTP 요청을 PUT로 하게 해주면서, 임시 저장의 기능을 구현할 수 있었다.</li>
</ul>
<h3 id="검색-기능">검색 기능</h3>
<p><img src="https://user-images.githubusercontent.com/75871005/232445967-8d2426c5-c626-4155-b777-1b1ff2017896.gif" alt="">
처음에 Server-side에서 <code>/api/v1/search</code>의 라우터를 통해 검색 로직을 작성하다보니 한가지 의문이 들었다.
과연 나의 데이터안에서 검색 요청을 하는 것인데, 굳이 api 요청을 하는 것이 적합할까??
어쨋든 firebase 내에서도 쿼리를 통해 데이터를 찾고 해당 데이터를 가져오고 가져온 데이터를 뿌려주는 역할을 하겠지만 보다 많은 유저가 사용하고 많은 데이터가 있을 경우 개인의 데이터를 찾기에 이러한 방법이 적합할지 의문이었다.</p>
<h4 id="그렇다면-적합한-것은">그렇다면 적합한 것은?</h4>
<blockquote>
<p>React Query</p>
</blockquote>
<p><code>React query</code>의 가장 강력한 위력은 데이터 캐싱에 있지 않을까 한다. 미리 캐싱된 데이터를 가공 처리하여 검색할 때마다 보여주면 굳이 검색 요청을 하지 않아도 되기에 요청의 비용도 줄일 수 있었다. </p>
<h2 id="프로젝트의-회고">프로젝트의 회고</h2>
<p>혼자서 진행하다보니 정말 많은 공부와 지식을 쌓는 좋은 경험이었지만 반대로 아주 베이스가 되는 부분들 부터 서버와 데이터 스키마 등 기능적인 부분에서도 혼자서 고민하며 진행하다보니 시간적 소요가 정말 길어졌지 않을까 했다.😇😇
하지만 혼자인 나에게 한줄기 빛 같던 선생님이 있었는데 그건 바로 <strong>The King GPT!!!</strong></p>
<p><img src="https://velog.velcdn.com/images/taetae-5/post/b817f9cd-167a-4397-87af-99d62f5bf0ab/image.png" alt=""></p>
<p>물론 GPT자체에만 의존한다면 정말 검증도 되지 않은? 코드들을 알려주거나 확실치 않은 답변을 내려줄때가 많다. 따라서 공식문서와 적절히 교차 검증은 필수이다.</p>
<p>덕분에 일의 능률이나 생산성이 정말 크게 상승될 수 있었던 <strong>The King GPT</strong>야 고맙다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Router v6에서 추가된 index routes??]]></title>
            <link>https://velog.io/@taetae-5/React-Router-v6%EC%97%90%EC%84%9C-%EC%B6%94%EA%B0%80%EB%90%9C-index-routes</link>
            <guid>https://velog.io/@taetae-5/React-Router-v6%EC%97%90%EC%84%9C-%EC%B6%94%EA%B0%80%EB%90%9C-index-routes</guid>
            <pubDate>Thu, 13 Apr 2023 12:12:55 GMT</pubDate>
            <description><![CDATA[<h2 id="index-routes란">index routes란?</h2>
<p><strong>React Router v6</strong>에서 추가된 <strong><code>index routes</code></strong>는 <strong>특정 경로에 대한 기본 경로(default route)를 설정하는 방법</strong>이다.</p>
<blockquote>
<p>즉, <strong><em>특정 경로로 접근할 때 경로가 정확히 일치하지 않을 때 기본 경로를 지정할 수 있다.</em></strong></p>
</blockquote>
<p>(이전 버전인 <del>v5</del>에서는 기본 경로를 설정하기 위해서는 <code>&lt;Redirect&gt;</code> 컴포넌트를 사용해야 했습니다.)</p>
<p><strong><code>index routes</code></strong>는 <code>&lt;Route&gt;</code> 컴포넌트에 <strong>index prop</strong>을 설정하여 구현할 수 있게 된다. </p>
<p>예를 들어, 다음과 같이 /teams 경로에 대한 index route를 설정할 수 있습니다.</p>
<pre><code class="language-jsx">&lt;Routes&gt;
  &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
  &lt;Route path=&quot;dashboard&quot; element={&lt;Dashboard /&gt;} /&gt;
&lt;/Routes&gt;</code></pre>
<p>여기서 <code>/</code> 경로는 <code>&lt;Home/&gt;</code> 컴포넌트로, <code>/dashboard</code> 경로는 <code>&lt;Dashboard/&gt;</code> 컴포넌트로 매핑되는데 이 때, 사용자가 <code>/dashboard</code> 경로 이외의 다른 경로로 접근할 경우, <strong>404 에러 페이지를 보여주는 것이 일반적이었다.</strong></p>
<p>하지만 <strong><code>index routes</code></strong>를 사용하면, 다음과 같이 설정할 수 있습니다.</p>
<pre><code class="language-jsx">&lt;Routes&gt;
  &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
  &lt;Route index element={&lt;Dashboard /&gt;} path=&quot;dashboard&quot; /&gt;
&lt;/Routes&gt;
</code></pre>
<p>여기서 <strong>index</strong> 속성을 추가하여 <code>/dashboard</code> 경로에 대한 기본 경로를 설정하게된다.</p>
<p>이제 사용자가 <code>/dashboard</code> 경로 이외의 다른 경로로 접근하면, 여전히 404 에러 페이지를 볼 수 있지만, <code>/dashboard</code> 경로로 접근할 경우에는 <code>&lt;Dashboard/&gt;</code> 컴포넌트가 표시됩니다.</p>
<p><strong><code>index routes</code></strong> 를 사용하면, 일치하는 경로가 없는 경우에 기본 페이지를 표시할 수 있으며, <strong>코드의 가독성과 유지 보수성도 향상된다.</strong></p>
<p>다른 예시로,</p>
<pre><code class="language-jsx">Route path=&quot;teams&quot; element={&lt;Teams /&gt;}&gt;
  &lt;Route path=&quot;:teamId&quot; element={&lt;Team /&gt;} /&gt;
  &lt;Route path=&quot;new&quot; element={&lt;NewTeamForm /&gt;} /&gt;
  &lt;Route index element={&lt;LeagueStandings /&gt;} /&gt;
&lt;/Route&gt;</code></pre>
<p>위 코드에서 <code>&lt;Route index&gt;</code>는 <code>/teams</code> 경로에 대한 기본 경로를 설정하고 있다.
이 경로에서 <code>/teams</code>와 정확히 일치하는 경로를 나태는데, </p>
<p><strong><code>index routes</code></strong> 는 중첩된 라우트에서도 사용할 수 있다. 예를 들어, 다음과 같이 /teams/:teamId 경로의 index route를 설정할 수 있습니다.</p>
<pre><code class="language-jsx">&lt;Route path=&quot;teams&quot; element={&lt;Teams /&gt;}&gt;
  &lt;Route path=&quot;:teamId&quot; element={&lt;Team /&gt;} &gt;
    &lt;Route index element={&lt;TeamDashboard /&gt;} /&gt;
  &lt;/Route&gt;
&lt;/Route&gt;
</code></pre>
<p>위 코드에서 <code>&lt;Route index&gt;</code>는 <code>/teams/:teamId</code> 경로에 대한 기본 경로를 설정하고 있습니다. 이 경로는 <code>/teams/:teamId</code>와 정확히 일치하는 경로를 나타냅니다.</p>
<h2 id="결론적으로">결론적으로</h2>
<blockquote>
<p>index routes를 사용하면 <code>&lt;Redirect&gt;</code> 컴포넌트를 사용하지 않고도 경로에 대한 기본 경로를 설정할 수 있습니다.</p>
</blockquote>
<p>또한, 중첩된 라우트에서도 일관성 있는 방식으로 기본 경로를 설정할 수 있어서 더욱 직관적인 코드를 작성할 수 있게 된다.</p>
<h2 id="참고">참고</h2>
<p>React Router v6 - <a href="https://reactrouter.com/en/main/start/concepts#index-routes">https://reactrouter.com/en/main/start/concepts#index-routes</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[전역상태 무엇을 쓸까? Recoil과 context의 비교]]></title>
            <link>https://velog.io/@taetae-5/Recoil%EA%B3%BC-context%EC%9D%98-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@taetae-5/Recoil%EA%B3%BC-context%EC%9D%98-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Wed, 12 Apr 2023 09:20:45 GMT</pubDate>
            <description><![CDATA[<h2 id="전역상태-관리는-왜-필요할까">전역상태 관리는 왜 필요할까?</h2>
<p>-일반적으로 React는 단방향 바인딩으로 부모 컴포넌트에서 자식 컴포넌트로 props(자식요소)를 전달한다. 그러나 컴포넌트를 분리하다보면 컴포넌트 안에 컴포넌트를 여러개 해야하는 경우가 발생하고 이럴때, <code>props-drilling</code>의 문제가 나타난다. 자식 컴포넌트에 <code>props</code>를 계속 전달하면 코드가 지저분해지며, 무리한 리랜더링 문제가 발생할 수 있다. 이러한 경우에 대비해서 전역상태 관리의 필요성이 나타나며 전역상태 라이브러리들이 출시되었다.</p>
<p>물론, 양방향 바인딩인 vue.js의 경우에도 vuex인 store인 라이브러리들이 존재한다. 따라서, 양방향이든 단방향이든 문제라기보다는 상태 변화를 효과적이고 적절하게 필요한 컴포넌트에서 사용하는 것이 중요하다.</p>
<p>React에서 대표적인 전역상태관리로 <code>context</code>, <code>redux</code>와 가장 최근에 출시된 <code>recoil</code>이 있다. 여기에서는 <code>useContext</code>와 <code>Recoil</code>에 대해서만 다루겠다.</p>
<h3 id="context">Context</h3>
<p><code>useContext</code>로 사용하는 React의 훅이다. <code>createContext</code> 함수를 사용하여 context 객체를 생성하고 <code>Provider</code> 컴포넌트를 통해 context값을 하위 컴포넌트로 전달할 수 있다. <code>seContext</code> 훅 을 사용하여 해당 컨텍스트의 값을 받아와서 사용할 수 있습니다.</p>
<h3 id="recoil">Recoil</h3>
<p><code>Recoil</code>은 Facebook에서 개발한 상태 관리 라이브러리입니다. <code>useState</code>와 비슷한 문법을 사용하면서도 전역 상태 관리를 할 수 있습니다. <code>Atom</code>이라는 단위로 상태를 정의하고, <code>selector</code>를 사용하여 상태 값을 가공하거나 다른 <code>Atom</code>의 값을 사용할 수 있습니다.</p>
<h3 id="언제-전역상태-관리가-필요할까🤔">언제 전역상태 관리가 필요할까?🤔</h3>
<p>아마 대표적으로는 로그인과 같은 인증상태관리 또는 테마에 대해서 전역상태관리가 필요할 것이다. 이때, 개인적인 생각으로는 Recoil이 더 적합(?)한 것같다. 왜냐하면 Recoil은 상태 변경 시 변경된 상태에 대한 업데이트가 발생하면 자동으로 리랜더링이 된다.</p>
<p>이는 useState와 같은 다른 상태 관리 라이브러리와 비교했을 때도 마찬가지이지만, <code>useContext</code>와 같은 <code>Context API</code>를 사용한 상태 관리보다 더욱 효율적인 렌더링을 가능하게 한다.</p>
<p>이는 <code>Recoil이 내부적으로 상태 값의 의존성 그래프(dependency graph)를 추적하고, 해당 그래프에서 변경된 부분만 리렌더링하도록 최적화하기 때문이다.</code>
*<em>이를 통해, 관련 없는 부분까지 리렌더링하는 것을 방지하고, 불필요한 성능 저하를 막을 수 있습니다.
*</em></p>
<p>또한, <code>Recoil</code>은 성능 최적화를 위해 Suspense와 같은 React의 다른 기능들과도 연동이 가능합니다. 이를 통해, 데이터가 로드되지 않은 상태에서 렌더링하는 것을 방지하고, 로딩 중인 상태를 처리할 수 있다.</p>
<p>따라서, Recoil은 내부적으로 상태 값의 의존성 그래프를 추적하고, 이를 최적화함으로써 더욱 효율적인 렌더링을 가능하게 합니다. 이러한 특징은 useContext와 같은 다른 상태 관리 방법보다 Recoil을 더욱 성능적으로 효율적인 상태 관리 방법으로 만든다.</p>
<h3 id="그렇다면-언제-context를-쓰고-언제-recoil을-사용하는-것이-좋을까-🤔">그렇다면 언제 Context를 쓰고 언제 Recoil을 사용하는 것이 좋을까? 🤔</h3>
<p>Context와 Recoil은 React에서 상태 관리를 위한 라이브러리이며, 둘 다 컴포넌트 간에 상태를 전달하고 공유하는 방법을 제공한다. </p>
<p><code>useContext</code>와 <code>Recoil</code>은 전역 상태 관리이지만 방식에서 차이가 있다. 만약 간단한 상태 값의 전달이 필요하다면 <code>useContext</code>를 사용하는 것이 좋고, 전역 상태를 관리하거나 상태 값이 복잡한 경우에는 <code>Recoil</code>을 사용하는 것이 좋다. </p>
<h4 id="recoil-예시">Recoil 예시</h4>
<pre><code class="language-js">import { atom } from &#39;recoil&#39;;

export const isLoggedInState = atom({
  key: &#39;isLoggedIn&#39;,
  default: false,
});</code></pre>
<p>위에서 정의한 전역 상태를 컴포넌트에서 사용하여 로그인 상태를 관리할 수 있습니다.</p>
<pre><code class="language-js">import React from &#39;react&#39;;
import { useRecoilState } from &#39;recoil&#39;;
import { isLoggedInState } from &#39;./recoil/atoms&#39;;

function LoginButton() {
  const [isLoggedIn, setIsLoggedIn] = useRecoilState(isLoggedInState);

  function handleLogin() {
    setIsLoggedIn(true);
  }

  function handleLogout() {
    setIsLoggedIn(false);
  }

  return (
    &lt;&gt;
      {isLoggedIn ? (
        &lt;button onClick={handleLogout}&gt;로그아웃&lt;/button&gt;
      ) : (
        &lt;button onClick={handleLogin}&gt;로그인&lt;/button&gt;
      )}
    &lt;/&gt;
  );
}
</code></pre>
<ul>
<li>위 코드에서는 isLoggedInState를 useRecoilState 훅을 통해 가져와서, 해당 상태가 변경될 때마다 컴포넌트가 자동으로 리렌더링됩니다. </li>
<li>이는 Recoil이 내부적으로 상태 값의 의존성 그래프를 추적하고, 해당 그래프에서 변경된 부분만 리렌더링하도록 최적화하기 때문입니다. </li>
<li>따라서, isLoggedInState 값이 변경될 때마다 컴포넌트가 불필요하게 리렌더링되지 않고, 효율적인 렌더링이 가능해집니다.</li>
<li>반면, useContext를 사용한 상태 관리는 이러한 최적화가 자동으로 이루어지지 않기 때문에, Memoization 등의 기술을 활용하여 최적화를 수동으로 구현해야 합니다.</li>
</ul>
<h3 id="context-예시">Context 예시</h3>
<pre><code class="language-js">import React, { useContext, useState } from &quot;react&quot;;

// createContext 함수로 컨텍스트 생성
const CounterContext = React.createContext();

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

  // 상태와 상태변경 함수를 value에 포함시켜 Provider 반환
  return (
    &lt;CounterContext.Provider value={{ count, setCount }}&gt;
      {props.children}
    &lt;/CounterContext.Provider&gt;
  );
}

function Counter() {
  // useContext를 이용해 컨텍스트에서 상태와 상태변경 함수를 가져옴
  const { count, setCount } = useContext(CounterContext);

  return (
    &lt;div&gt;
      &lt;h2&gt;Count: {count}&lt;/h2&gt;
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;Increment&lt;/button&gt;
      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;Decrement&lt;/button&gt;
    &lt;/div&gt;
  );
}

function App() {
  return (
    &lt;CounterProvider&gt;
      &lt;Counter /&gt;
    &lt;/CounterProvider&gt;
  );
}

export default App;</code></pre>
<p><code>createContext</code>로 <code>CounterContext</code>라는 컨텍스트를 생성하고, <code>CounterProvider</code> 함수에서 상태와 상태변경 함수를 관리합니다. <code>CounterProvider</code> 함수는 <code>createContext</code>를 이용해 생성한 <code>CounterContext.Provider</code>를 반환하며, 이를 이용해 Counter 컴포넌트에서 useContext를 통해 상태와 상태변경 함수를 가져와 사용합니다. 상태변경 함수를 호출할 때마다 Counter 컴포넌트가 리렌더링됩니다.</p>
<h3 id="context의-장점과-단점">Context의 장점과 단점</h3>
<h4 id="장점">장점</h4>
<ul>
<li>전역 상태를 관리하기 편리하며, 중복 코드를 줄일 수 있다</li>
<li>상태를 주입하고 받을 컴포넌트의 계층 구조가 단순할 경우 구현하기 쉽다.</li>
<li>비교적 러닝커브가 길고 복잡한 Redux와 같은 상태 관리 라이브러리를 사용하지 않고도 쉽게 구현할 수 있습니다.</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li>Context의 상태 변화가 일어날 때마다 해당 컴포넌트와 그 자식 컴포넌트들이 모두 리렌더링되므로, 성능 이슈가 발생할 수 있습니다.<ul>
<li>사실 이 부분때문에 오늘의 Recoil와 같이 설명하게 되었다.</li>
</ul>
</li>
<li>Context를 사용하기 위해서는 상태를 변경하는 로직을 직접 구현해야 하므로 복잡한 로직에 적용하기 어렵다.</li>
<li>상태를 관리하기 위한 코드가 매우 많아질 수 있다.</li>
</ul>
<h3 id="recoil의-장점과-단점">Recoil의 장점과 단점</h3>
<h4 id="장점-1">장점</h4>
<ul>
<li>상태 간의 의존성 그래프를 쉽게 관리할 수 있습니다.</li>
<li>상태 변화에 대한 최적화가 내장되어 있어, 상태 변화가 발생할 때마다 전체 컴포넌트를 다시 렌더링하지 않아도 됩니다.</li>
<li>대규모 애플리케이션에서 사용하기 적합하며, 다양한 상황에서 사용 가능한 높은 유연성을 제공합니다.</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>초기 구현이 어려울 수 있습니다.</li>
<li>상태를 관리하기 위해 사용해야 하는 코드가 많아질 수 있습니다.</li>
</ul>
<h3 id="결론적으로">결론적으로</h3>
<ul>
<li><code>Context</code>는 단순한 상태 전달이나 트리 상태 전달을 위한 유용한 패턴을 제공하며, 작은 규모의 애플리케이션에서는 적합합니다. </li>
<li>반면, <code>Recoil</code>은 대규모 애플리케이션의 복잡한 상태 관리를 위해 사용할 수 있습니다. 상황에 따라 두 가지 방법을 적절하게 조합하여 사용할 수도 있습니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[RSET API] RESTIful한 URL 설계]]></title>
            <link>https://velog.io/@taetae-5/API-url%EC%97%90-api</link>
            <guid>https://velog.io/@taetae-5/API-url%EC%97%90-api</guid>
            <pubDate>Wed, 12 Apr 2023 08:46:53 GMT</pubDate>
            <description><![CDATA[<h3 id="1-rest-api란">1. REST API란?</h3>
<ul>
<li>Representational State Transfer </li>
<li>REST API란 REST Architecture의 제약 조건을 준수하는 애플리케이션 프로그래밍 인터페이스를 뜻한다.</li>
<li>즉, 자원을 이름(자원의 표현)으로 표현하고 자원의 상태(정보)를 주고 받는 것을 의미한다.</li>
<li>REST는 URI를 통해 자원을 표시하고 HTTP Method를 이용하여 해당 자원의 행위를 정해주며 그 결과를 받는 것을 말한다.</li>
</ul>
<h3 id="2-rest의-구성요소">2. REST의 구성요소</h3>
<blockquote>
<p>REST는 자원, 행위, 표현으로 구성된다.</p>
</blockquote>
<ul>
<li>자원(Resource)는 URI가 가리키는 리소스</li>
<li>행위(Verb)는 HTTP Method (ex. GET/ POST/ PUT/ DELETE 등..)</li>
<li>표현(Representation)은 리소스의 특정 시점 상태를 반영하고 있는 정보</li>
</ul>
<h3 id="3-rest-api-uri-작성-규칙">3. REST API URI 작성 규칙</h3>
<h4 id="api에는-어떻게-이름을-붙여야-할까">API에는 어떻게 이름을 붙여야 할까?</h4>
<ul>
<li>보통의 경우에 <code>api/버전/Resource</code> 의 형태로 사용한다.</li>
</ul>
<h4 id="자원resource-앞에-api버전-를-붙이는-것이-뭐가-좋을까">자원(Resource) 앞에 <code>/api/버전</code> 를 붙이는 것이 뭐가 좋을까?</h4>
<p>기존에 API를 설계할 때, 단순히 <code>엔드포인트/resource</code>를 붙여서 사용해왔다. 하지만, 다른 개발자들의 git을 보면서 앞에 <code>/api</code>를 붙여서 사용하는 것을 보게 되면서 &#39;앞에 /api가 무슨 의미가 있지?&#39; 하는 생각이 들었다. </p>
<p>다른 사람들의 벨로그와 stackoverflow를 보면서 정리하자면</p>
<ol>
<li>/api를 붙이면 url만 봐도 API라는 것을 알 수 있어서 편리하다.</li>
<li>서버 자체가 웹 페이지를 가지는 경우 API 주소가 분리되는 것이 좋을 것이다.</li>
<li>/api/v1처럼 versioning을 하면 사용중이던 API의 버전을 바꾸어야할 때 용이하다.</li>
</ol>
<p>생각해보니 맞는말만 써서 생각이 변하게 되었다. <code>/api</code>가 없을 경우 단순히 REST API인지 페이지 라우팅인지 헷갈릴수 있는 부분이었는데 이번 프로젝트를 하면서 <code>/api</code>를 붙여 사용하니 API주소가 분리되어 편리하였다.
물론 나같은 경우 서버 자체가 웹 페이지를 가지는 경우는 아니었지만 이러한 방식으로 사용하는 것이 더 좋았다. 또한, <code>/api/</code> 뒤에 <code>v1</code>과 같은 버전을 붙이면서 어떻게 변경되었는지 알 수 있는 부분이었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MongoDB & Mongoose]]></title>
            <link>https://velog.io/@taetae-5/MongoDB-Mongoose</link>
            <guid>https://velog.io/@taetae-5/MongoDB-Mongoose</guid>
            <pubDate>Wed, 12 Apr 2023 07:40:04 GMT</pubDate>
            <description><![CDATA[<h3 id="no-sql">No SQL</h3>
<ul>
<li>NO SQL = Not Only SQl
NO SQL 대표적인 유형으로는 <code>Document</code>,<code>key-value</code>,<code>Graph</code>가 있다. 그 중에서 MongoDB의 경우 <code>Document</code>에 속한다.</li>
</ul>
<h3 id="mongodb">MongoDB</h3>
<ul>
<li>Data가 JSON-like-document로 저장이 된다.</li>
<li>다시말해 보통의 DB인 SQL처럼 <code>column</code>과 <code>row</code> 기반이 아닌, JSON형태로 원하는 어떤 종류의 형태와 모양으로 데이터를 저장이 가능하다.</li>
<li>즉, 모양과 형태의 제약이 없다는 보통의 SQL과는 큰 차별점이 있다.</li>
</ul>
<h3 id="mongoose">Mongoose</h3>
<ul>
<li>Node.js와 MongoDB를 연결해주는 ODM(Object Document Mapping)중에서 가장 유명한 라이브러리이다. </li>
<li>즉 Mongoose는 자바스크립트 환경에서도 MongoDB를 사용할 수 있도록 해주는 역할을 한다.</li>
</ul>
<h5 id="odm-object와-document를-1대1로-매칭하는-역할">*ODM: Object와 Document를 1대1로 매칭하는 역할</h5>
<p align="center">
<img src="https://velog.velcdn.com/images/taetae-5/post/70cac425-1a23-4d0a-975d-ee82c56cb785/image.png" width="400px" height="500px"/>
</p>

<ul>
<li>DB연결, Schema정의, Schema에서 Model로 변환, Model을 이용한 Query 등</li>
</ul>
<h3 id="mongoose-설치">Mongoose 설치</h3>
<pre><code class="language-shell">npm install mongoose</code></pre>
<h3 id="mongodb와-연결하기">MongoDB와 연결하기</h3>
<ul>
<li>Mongoose를 <code>import</code></li>
<li>Mongoose의 <code>connect</code> 메소드를 사용한다.
(기본적으로 localhost와 연결)</li>
</ul>
<pre><code class="language-js">import mongoose from &#39;mongoose&#39;

mongoose.connect(`localhsot:${port}/${db이름}`,{
  // 옵션 
  useNewUrlParser:true // mongoDB v5부터는 true로 추가해야한다.
  // url을 parsing할 떄, 새로운 parser를 사용하는 것
  })

const db = mongoose.connection
db.on(&quot;error&quot;, (error)=&gt;console.log(error))
db.once(&quot;open&quot;, ()=&gt;console.log(&quot;open&quot;))</code></pre>
<ul>
<li><code>Mongoose.connection</code>은 Mongoose로 연결한 첫 번째 연결</li>
<li><code>connection.on()</code> / <code>connection.once()</code>는 Event Emitter 계열의 이벤트 Listen</li>
</ul>
<h3 id="schema">Schema</h3>
<ul>
<li>MongoDB에는 Schema가 없지만, Mongoose에서 RDBMS처럼 Schema를 정의할 수 있다. </li>
<li>Document 내부의 각 field가 어떤 식으로 되어있는지를 정의</li>
<li>SQL의 table의 개념과 유사하다.</li>
</ul>
<h4 id="schema-정의">Schema 정의</h4>
<ul>
<li><code>mongoose</code>를 <code>import</code>한 뒤, <code>new mongoose.Schema()</code>를 통해 생성이 가능하다.</li>
<li>각 field 별로 type, required, unique 등의 옵션 선택이 가능하다.,<pre><code class="language-js"></code></pre>
</li>
</ul>
<p>const userSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true,
      unique: true,
    },
    displayName: {
      type: String,
      required: true,
    },
    password: {
      type: String,
      required: true,
      select: false,
    },
    // 암호화할 때 랜덤한 값을 생성하도록
    salt: {
      type: String,
      required: true,
      select: false,
    },
  },
);</p>
<p>const userModel = mongoose.model(&quot;User&quot;, userSchema);</p>
<pre><code>- Schema에서 Model로 변환한다.

### Mongoose - populate

- `Mongoose.populate()`는 다른 `collections`의 data를 참조핧 때 사용한다.
- RDBMS에서는 JOIN을 하는 것과 유사한 것으로 `MongoDB`에서는 JOIN이 없기 때문에, 이러한 방식으로 참조를 한다. 

#### 사용법

- 반드시, type을 `mongoose.Schema.Types.ObjectId`로 설정해야한다.
- `ObjectId` : MongoDB에서 `Collection`에 data를 넣을 떄 자동으로 부여한다.
    - firebase에서 collection에 자동으로 id설정하는 것과 유사하다고 볼 수 있다.
- RDBMS의 primary key처럼 사용이 가능하다.
- `ref` : 해당 ObjectID로 어떤 model을 참조할 것인지 나타낸다.

```js
export default mongoose.model(
  // Review 모델을 정의
  &quot;Review&quot;,
  // Review 스키마 정의
  mongoose.Schema(
    {
      user: {
        type: Schema.Types.ObjectId,
        ref: &quot;User&quot;, // User 모델 참조
        required: true,
      },
      content: {
        type: String,
        required: true,
      },
      mediaType: {
        type: String,
        enum: [&quot;tv&quot;, &quot;movie&quot;],
        required: true,
      },
      mediaId: {
        type: String,
        required: true,
      },
      mediaTitle: {
        type: String,
        required: true,
      },
      mediaPoster: {
        type: String,
        required: true,
      },
    },
    modelOptions
  )
);

const reviews = media.reviews = await reviewModel.find({ mediaId }).populate(&quot;user&quot;).sort(&quot;-createdAt&quot;);
</code></pre><h3 id="mongoose---querycreate">Mongoose - query(CREATE)</h3>
<ul>
<li><p>데이터의 조회하는 방식을 의미한다.</p>
</li>
<li><p>비동기 함수이므로, <code>async, await, then(), callbcak</code>등을 사용하여 코드 실행 순서를 <code>반드시</code> 보장해야한다.</p>
</li>
<li><p><code>Model.create({...})</code> </p>
</li>
<li><p><code>save()</code> : 객체를 새로 만들고 저장한다. </p>
</li>
</ul>
<h3 id="mongoose---queryread">Mongoose - query(READ)</h3>
<ul>
<li>`Model.find({...}) : 조건에 부합하는 documents를 <strong>모두</strong> 불러온다.<ul>
<li>firebase의 <code>getDocs</code>와 유사하다.</li>
</ul>
</li>
<li><code>Model.findOne({...})</code> : 조건에 부합하는 documents 중 <strong>하나만</strong> 불러온다.<ul>
<li>firebase의 <code>getDoc</code>과 유사하다.</li>
</ul>
</li>
</ul>
<h3 id="mongoose---queryupdate">Mongoose - query(UPDATE)</h3>
<ul>
<li><code>Model.findByIdAndUpdate({...})</code> : 해당 id의 documents를 불러와서 갱신하도록 한다.</li>
</ul>
<pre><code class="language-js">cousrModel.findByIdAndUpdate(
  id, 
  {classTime:&quot;Mon E, Web E&quot;}
):
</code></pre>
<ul>
<li><code>Model.updateOne({...})</code> : 조건에 부합하는 document를 불러와서 갱신한다.</li>
</ul>
<pre><code class="language-js">coursModel.updateOne(
  {_id:id},
  { coursName : &quot;Open Source&quot;}
)</code></pre>
<h3 id="mongoose---querydelete">Mongoose - query(DELETE)</h3>
<ul>
<li><code>Model.findOneAndDelete({...})</code> : 조건에 부합하는 document중 하나만 삭제한다. 삭제할 데이터를 return</li>
<li><code>Model.deleteOne({...})</code> : 조건에 부합하는 document중 하나만 삭제한다. 삭제할 데이터를 return하지 않는다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Query에서 쿼리 삭제하기]]></title>
            <link>https://velog.io/@taetae-5/React-Query%EC%97%90%EC%84%9C-%EC%BF%BC%EB%A6%AC-%EC%82%AD%EC%A0%9C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@taetae-5/React-Query%EC%97%90%EC%84%9C-%EC%BF%BC%EB%A6%AC-%EC%82%AD%EC%A0%9C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 30 Mar 2023 16:55:37 GMT</pubDate>
            <description><![CDATA[<p>React Query에서 query를 삭제하려면 removeQuery 함수를 사용할 수 있습니다. 이 함수는 query의 고유한 키 값을 매개 변수로 사용합니다.</p>
<p>예를 들어, myQuery라는 이름의 query가 있다면 다음과 같이 removeQuery 함수를 호출하여 삭제할 수 있습니다.</p>
<pre><code class="language-js">import { useQueryClient } from &#39;react-query&#39;;

const queryClient = useQueryClient();
queryClient.removeQuery(&#39;myQuery&#39;);</code></pre>
<p>위의 예제에서는 useQueryClient 훅을 사용하여 queryClient 객체를 가져온 다음, removeQuery 함수를 호출하여 myQuery를 삭제합니다.</p>
<p>또는, removeQueries 함수를 사용하여 여러 개의 query를 한번에 삭제할 수도 있습니다. 이 함수는 query의 고유한 키 값들의 배열을 매개 변수로 사용합니다.</p>
<pre><code class="language-js">queryClient.removeQueries([&#39;myQuery1&#39;, &#39;myQuery2&#39;, &#39;myQuery3&#39;]);
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[ React 18 에서 useLocalStorage 구현]]></title>
            <link>https://velog.io/@taetae-5/React-18-%EC%97%90%EC%84%9C-useLocalStorage-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@taetae-5/React-18-%EC%97%90%EC%84%9C-useLocalStorage-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Fri, 10 Feb 2023 03:59:24 GMT</pubDate>
            <description><![CDATA[<p>localStorage를 통해 칸반의 추가, 삭제, 업데이트, 드래그, 드랍을 하고자 변경감지될 때마다 localStorage에서 꺼내고 빼서 쓸 수 있는 Hook이 필요했다.</p>
<h3 id="초기에-구현했던-uselocalstorage">초기에 구현했던 useLocalStorage</h3>
<pre><code class="language-ts">
import React from &quot;react&quot;;
import { useEventCallback, useEventListener } from &quot;usehooks-ts&quot;;

function useLocalStorage&lt;T&gt;(key: string, initialValue: T) {
  const [storedValue, setStoredValue] = useState&lt;T&gt;(() =&gt; {
    if (typeof window === &quot;undefined&quot;) {
      return initialValue;
    }
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.log(error);
      return initialValue;
    }
  });

  const setValue =   (value: T | ((val: T) =&gt; T)) =&gt; {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.log(error);
    }
  };

  return [storedValue, setValue] as const;
}
export default useLocalStorage;
</code></pre>
<h4 id="값-읽기">값 읽기</h4>
<p>초기의 state값으로 localStorage에서 getItem(key) 메소드를 호출했을 때 해당 key에 대한 항목이 존재하면 string 형태로 반환하며, 없으면 null을 반환한다.</p>
<p>해당 key에 대한 항목이 존재한다면 사용자가 그 값을 한 번이라도 저장했다는 소리가 된다. 그러니까 초기값 대신 저장된 값을 반환해야 한다. </p>
<p>반면 null이 반환되었을 경우 사용자가 값을 저장한 적이 없거나 삭제했다는 의미이기 때문에 초기값을 반환해야 한다. 따라서 어떤 key에 대해 읽어진 값은 아래와 같이 쓸 수 있다.</p>
<pre><code class="language-ts">if (typeof window === &quot;undefined&quot;) {
  return initialValue;
}</code></pre>
<ul>
<li>SSR의 경우 <code>window is undefined</code> 오류가 발생할 수 있다. </li>
<li>따라서 window를 한 번 체크하고 초기값을 반환하도록 한다.</li>
</ul>
<h3 id="값-쓰기">값 쓰기</h3>
<p>localStorage의 setItem(key, value) 메소드를 이용하면 값을 쓸 수 있다. 여기서 value에는 반드시 string 만 전달해야 한다. 저장할 객체가 string이 아닌 경우 일반적으로는 JSON.stringify를 이용해 string 형태로 변환한다. 따라서 값을 쓰는 부분은 아래와 같이 쓸 수 있다.</p>
<pre><code class="language-ts">
const valueToStore = value instanceof Function ? value(storedValue) : value;</code></pre>
<ul>
<li>값이 함수가 되도록 허용하여 useState와 동일한 API를 사용하도록 대응</li>
</ul>
<br/>

<h2 id="하지만-문제-발생">하지만, 문제 발생!</h2>
<blockquote>
<p>새로고침시에 동기화가 되지 않는 문제 발생</p>
</blockquote>
<ul>
<li>칸반의 카드를 추가하거나 업데이트 삭제, drag, drop할 경우 화면에서는 바뀌지만</li>
<li>새로 고침을 할 경우, 맨 마지막에 했던 동작에 관해서만 동기화가 되었다.. </li>
<li>다른 코드에서 localStorage를 같이 사용할 수 있지만, 거기서 값을 업데이트할 경우, 이 훅에서는 알 수 있는 방법이 없다.</li>
<li>따라서, 기존의 값을 계속 들고 있게 되는 현상으로 화면 랜더링에서 일관성이 깨지므로, 새로고침시 동기화가 되지 않았다.</li>
</ul>
<p>따라서, <strong>값을 구독</strong>할 수 있도록 event를 감지할 수 있는 기능이 필요하다.</p>
<h3 id="값-구독">값 구독</h3>
<p>localStorage 의 변화가 있으면 custom event 를 발생시키도록 하고, 이를 수신하면서 값의 업데이트 여부를 알아내는 것이 필요하다. </p>
<p><code>usehooks-ts: useLocalStorage</code> 에서 확인할 수 있다.</p>
<pre><code class="language-ts">  const setValue = (value: T | ((val: T) =&gt; T)) =&gt; {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
      setStoredValue(valueToStore);
      window.dispatchEvent(new Event(&quot;local-storage&quot;));
    } catch (error) {
      console.log(error);
    }
  };
</code></pre>
<p>setValue를 사용할 때, setState후,  <code>dispatchEvent(event)</code>를 호출해 요소에 있는 이벤트를 <code>실행</code>시켜 모든 useLocalStorage 후크가 알림을 받도록 맞춤 이벤트를 한다.
커스텀 이벤트인 <code>local-storage</code>로 이벤트 핸들러가 일반 브라우저 이벤트처럼 이벤트에 반응할 수 있게 된다.</p>
<h4 id="useeventlistener">useEventListener</h4>
<ul>
<li>이 커스텀 훅을 통해서, storage의 업데이트 시 커스텀 이벤트 &#39;local-storage&#39; 를 발생시키고 여기에 대한 핸들러를 붙여 사용하고 있다.</li>
</ul>
<pre><code class="language-ts">
  // this only works for other documents, not the current one
  useEventListener(&#39;storage&#39;, handleStorageChange)

  // this is a custom event, triggered in writeValueToLocalStorage
  // See: useLocalStorage()
  useEventListener(&#39;local-storage&#39;, handleStorageChange)</code></pre>
<p><code>&#39;storage&#39;</code>의 업데이트 시 커스텀 이벤트 <code>&#39;local-storage&#39;</code> 를 발생시키고 여기에 대한 핸들러를 붙여 사용하고 있다.</p>
<h3 id="uselocalstorage-from-usehooks-ts">useLocalStorage from usehooks-ts</h3>
<pre><code class="language-ts">import React from &quot;react&quot;;
import { useEventListener } from &quot;usehooks-ts&quot;;

function useLocalStorage&lt;T&gt;(key: string, initialValue: T) {
  const readValue = React.useCallback((): T =&gt; {
    // &quot;window is undefined&quot; 에러 방지
    if (typeof window === &quot;undefined&quot;) {
      return initialValue;
    }
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.warn(`Error reading localStorage key “${key}”:`, error);
      return initialValue;
    }
  }, [initialValue, key]);

  const [storedValue, setStoredValue] = React.useState&lt;T&gt;(readValue);

  const setValue = (value: T | ((val: T) =&gt; T)) =&gt; {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
      setStoredValue(valueToStore);
      window.dispatchEvent(new Event(&quot;local-storage&quot;));
    } catch (error) {
      console.log(error);
    }
  };


  React.useEffect(() =&gt; {
    setStoredValue(readValue());
  }, []);

  const handleStorageChange = React.useCallback(
    (event: StorageEvent | CustomEvent) =&gt; {
      if ((event as StorageEvent)?.key &amp;&amp; (event as StorageEvent).key !== key) {
        return;
      }
      setStoredValue(readValue());
    },
    [key, readValue]
  );

  useEventListener(&quot;storage&quot;, handleStorageChange);
  useEventListener(&quot;local-storage&quot;, handleStorageChange);

  return [storedValue, setValue] as const;
}
export default useLocalStorage;
</code></pre>
<ul>
<li>주의 : usehooks-ts를 설치하기 위해서 node 16버전 이상이 설치되어있어야한다.</li>
</ul>
<h3 id="참고">참고</h3>
<ul>
<li>useLoacalStorage - <a href="https://usehooks-ts.com/react-hook/use-local-storage">https://usehooks-ts.com/react-hook/use-local-storage</a></li>
<li>useEventListeer - <a href="https://usehooks-ts.com/react-hook/use-event-listener">https://usehooks-ts.com/react-hook/use-event-listener</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[CORS란?]]></title>
            <link>https://velog.io/@taetae-5/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91</link>
            <guid>https://velog.io/@taetae-5/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EA%B8%B0%EC%88%A0%EB%A9%B4%EC%A0%91</guid>
            <pubDate>Sun, 29 Jan 2023 15:13:37 GMT</pubDate>
            <description><![CDATA[<h3 id="cors란">CORS란?</h3>
<p>브라우저에서는 보안적인 이유로 cross-origin HTTP 요청들을 제한합니다. 그래서 cross-origin 요청을 하려면 서버의 동의가 필요합니다. 만약 서버가 동의한다면 브라우저에서는 요청을 허락하고, 동의하지 않는다면 브라우저에서 거절합니다.</p>
<p>이러한 허락을 구하고 거절하는 메커니즘을 HTTP-header를 이용해서 가능한데, 이를 CORS(Cross-Origin Resource Sharing)라고 부릅니다.</p>
<p>그래서 브라우저에서 cross-origin 요청을 안전하게 할 수 있도록 하는 메커니즘입니다.</p>
<h3 id="cors는-왜-필요한가">cors는 왜 필요한가?</h3>
<p>CORS가 없이 모든 곳에서 데이터를 요청할 수 있게 되면, 다른 사이트에서 원래 사이트를 흉내낼 수 있게 됩니다. 예를 들어서 기존 사이트와 완전히 동일하게 동작하도록 하여 사용자가 로그인을 하도록 만들고, 로그인했던 세션을 탈취하여 악의적으로 정보를 추출하거나 다른 사람의 정보를 입력하는 등 공격을 할 수 있습니다. 이렇나 공격을 할 수 없도록 브라우저에서 보호하고, 필요한 경우 에만 서버와 협의하여 요청할 수 있도록 하기 위해서 필요합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[쇼핑몰 프로젝트] 5. Firebase Auth로 로그인 기능 구현하기]]></title>
            <link>https://velog.io/@taetae-5/%EC%87%BC%ED%95%91%EB%AA%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-5.-Firebase-Auth%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@taetae-5/%EC%87%BC%ED%95%91%EB%AA%B0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-5.-Firebase-Auth%EB%A1%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 16 Jan 2023 14:27:40 GMT</pubDate>
            <description><![CDATA[<h2 id="1-firebase-authentication">1. Firebase Authentication</h2>
<h3 id="11-firebase-초기세팅">1.1. firebase 초기세팅</h3>
<p>📍 <strong>clientApp.ts</strong></p>
<pre><code class="language-ts">// lib/firebase/clientApp.ts
import firebase from &quot;firebase/compat/app&quot;;
import &quot;firebase/auth&quot;;
import &quot;firebase/firestore&quot;;
import &quot;firebase/analytics&quot;;
import &quot;firebase/performance&quot;;

// Your web app&#39;s Firebase configuration
const clientCredentials = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

export const initFirebase = () =&gt; {
  if (!firebase.apps.length) {
    firebase.initializeApp(clientCredentials);
    console.log(&quot;😀 Firebase was successfully init&quot;);
  }
};</code></pre>
<ul>
<li>firebase에서 필요한 것들로만 가져와서 <code>clientCredentials</code>로 initialize한 firebase를 변수(initFirebase)에 할당한다.</li>
<li>이후 이것을 이용해 로그인할 수 있도록 <code>auth()</code>를 호출해 export해 준다.</li>
</ul>
<h3 id="2-공급업체">2. 공급업체</h3>
<p>로그인 업체를 설정해야한다.</p>
<blockquote>
<p><em><strong>업체 설정 경로</strong></em>
Firebase &gt; Console &gt; 해당 project 선택 &gt; Authentication</p>
</blockquote>
<p><img src="blob:https://velog.io/63a7baaf-3dcf-4c14-bc0e-dbd51f96dac6" alt="업로드중.."></p>
<blockquote>
<p>이번 포스팅에선 개로그인에 자주 쓰이는 구글과 email형식만 를 다뤄보도록 하겠습니다.
이 외에 이메일, 핸드폰 번호 등의 다른 방법들을 사용해 보고 싶다면, 공식 사이트를 참고하세요.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[FE Study] Javascript - (5)]]></title>
            <link>https://velog.io/@taetae-5/FE-Study-Javascript-5</link>
            <guid>https://velog.io/@taetae-5/FE-Study-Javascript-5</guid>
            <pubDate>Thu, 12 Jan 2023 06:31:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-스코프">1. 스코프</h2>
<blockquote>
<p><em><strong>스코프란? 💡</strong></em></p>
</blockquote>
<ul>
<li>모든 식별자(변수 이름, 함수 이름, 클래스의 이름 등)는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효범위를 결정한다. 이를 스코프라 한다.</li>
<li>스코프는 <strong><code>식별자가 유효한 범위</code></strong>를 의미한다.</li>
</ul>
<pre><code class="language-js">function add(x,y){
  // 매개변수는 함수 몸체 내부에서만 참조할 수 있다.
  // 즉, 매개변수의 스코프(유효범위)는 함수 몸체 내부이다.
  console.log(x,y) // 2, 5
  return x+y
}
add(2,5)

// 매개변수는 함수 몸체 내부에서만 참조할 수 있다.
console.log(x,y) // ReferenceError: x is not defined
</code></pre>
<h3 id="11-식별자-결정identifier-resolution">1.1. 식별자 결정(identifier resolution)</h3>
<blockquote>
<p><em><strong>식별자 결정이란? 💡</strong></em></p>
</blockquote>
<ul>
<li><p>`자바스크립트 엔진이 스코프 체인을 통해 참조할 변수를 검색(결정)하는 것</p>
</li>
<li><p>아래의 예시처럼 자바스크립트 엔진은 같은 두 개의 변수 중에서 어떤 변수를 참조해야 할 것인지를 결정하는 것을 말한다.</p>
</li>
</ul>
<pre><code class="language-js">
const x = &#39;global&#39;;

function foo(){
    const x = &#39;local&#39;
    console.log(x) // local
}

foo()

console.log(x) // global</code></pre>
<h3 id="12-스코프-개념이-없다면">1.2. 스코프 개념이 없다면?</h3>
<ul>
<li>스코프라는 개념이 없다면 같은 이름을 갖는 변수는 충돌을 일으키므로, 프로그램 전체에서 하나 밖에 사용할 수 없을 것이다. </li>
<li>스코프를 통해서 식별자인 변수 이름의 충돌을 방지하고 같은 이름의 변수를 사용할 수 있게 한다.</li>
<li>스코프 내에서 식별자는 유일해야 하지만, 다른 스코프에서 같은 이름의 식별자를 사용할 수 있게 한다. 즉, <code>스코프는 네임스페이스</code>이다.</li>
</ul>
<blockquote>
<p><em><strong>식별자의 특성 💡</strong></em></p>
</blockquote>
<ul>
<li>식별자는 어떤 값을 구별할 수 있어야 하므로, 유일 해야한다.</li>
</ul>
<h2 id="2-스코프의-종류">2. 스코프의 종류</h2>
<table>
<thead>
<tr>
<th align="center">구분</th>
<th align="center">설명</th>
<th align="center">스코프</th>
<th align="center">변수</th>
</tr>
</thead>
<tbody><tr>
<td align="center">전역</td>
<td align="center">코드의 가장 바깥 영역</td>
<td align="center">전역 스코프</td>
<td align="center">전역 변수</td>
</tr>
<tr>
<td align="center">지역</td>
<td align="center">함수 몸체 내부</td>
<td align="center">지역 스코프</td>
<td align="center">지역 변수</td>
</tr>
</tbody></table>
<h3 id="21-전역-스코프">2.1. 전역 스코프</h3>
<blockquote>
<p><em><strong>전역이란?💡</strong></em></p>
</blockquote>
<ul>
<li>전역은 전역 스코프를 만든다. </li>
<li>전역에서 변수를 선언하면 전역 스코프를 갖는 전역 변수가 된다.</li>
<li><strong>전역 변수는 어디서든 참조할 수 있다.</strong></li>
</ul>
<h3 id="22-지역-스코프">2.2. 지역 스코프</h3>
<blockquote>
<p><em><strong>지역이란?💡</strong></em></p>
</blockquote>
<ul>
<li><strong>함수 몸체 내부</strong>를 말한다.</li>
<li>지역에 변수를 선언하면 지역 스코프를 갖는 지역 변수가 된다. </li>
<li>지역 변수는 자신이 선언된 지역과 하위 지역(중첩 함수)에서만 참조할 수 있다.</li>
<li>즉, <strong>지역 변수는 자신의 지역 스코프와 하위 지역 스코프에서 유효하다.</strong></li>
</ul>
<p>*중첩함수</p>
<ul>
<li>함수 몸체 내부에서 정의한 함수</li>
</ul>
<h2 id="3-스코프-체인">3. 스코프 체인</h2>
<blockquote>
<p><em><strong>스코프 체인이란?💡</strong></em></p>
</blockquote>
<ul>
<li><strong>스코프가 함수의 중첩에 의해 계층적 구조를 갖는다.</strong></li>
<li>모든 스코프는 하나의 계층적 구조로 연결되며, 모든 지역 스코프의 최상위 스코프는 전역 스코프다.</li>
<li>스코프가 계층적으로 연결된 것을 <strong>스코프 체인</strong>이라 한다.</li>
</ul>
<p>ex) 전역 스코프 ← outer 함수 지역 스코프 ← inner 함수 지역 스코프</p>
<p><strong>*변수를 참조할 때, 자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색(identifier resolution)한다.</strong>
이를 통해 상위 스코프에서 선언한 변수를 하위 스코프에서도 참조할 수 있다.
하지만, 하위 스코프에서 선언한 변수는 상위 스코프에서 참조할 수 없다.</p>
<blockquote>
<p><em><strong>렉시컬 환경이란?💡</strong></em></p>
</blockquote>
<ul>
<li>스코프 체인은 물리적인 실체로 존재한다.</li>
<li>자바스크립트 엔진은 코드를 실행하기에 앞서 렉시컬 환경을 실제로 생성한다.</li>
<li>스코프 체인은 실핼 컨텍스트의 렉시컬 환경을 단방향으로 연결한 것이다.</li>
<li>전역 렉시컬 환경은 코드가 로드되면 곧바로 생성되고 함수의 렉시컬 환경은 함수가 호출되면 곧바로 생성된다.</li>
</ul>
<h2 id="4-함수-레벨-스코프">4. 함수 레벨 스코프</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[[FE Study] Javascript - (4)]]></title>
            <link>https://velog.io/@taetae-5/FE-Study-Javascript-4</link>
            <guid>https://velog.io/@taetae-5/FE-Study-Javascript-4</guid>
            <pubDate>Wed, 04 Jan 2023 07:19:04 GMT</pubDate>
            <description><![CDATA[<h2 id="1-함수">1. 함수</h2>
<blockquote>
<p><em><strong>함수란? 💡</strong></em></p>
</blockquote>
<ul>
<li>일련의 과정을 문(statement)로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것을 의미한다.</li>
<li>함수는 <strong>함수 정의</strong>를 통해 생성한다.</li>
</ul>
<p>*함수의 구성요소</p>
<ul>
<li>매개 변수(parameter)</li>
<li>인수(argument)</li>
<li>반환(return value)</li>
</ul>
<pre><code class="language-js">// 함수 정의
function add(x,y){
    return x+y;
}
// 함수 호출
var result = add(2,5);

// 함수 add에 인수(argument) 2,5를 전달하면서 호출하면 반환값 7을 반환한다.
console.log(result)</code></pre>
<h3 id="11-함수를-사용하는-이유">1.1. 함수를 사용하는 이유</h3>
<blockquote>
<p><em><strong>Key point💡</strong></em> 코드의 재사용성</p>
</blockquote>
<p>코드의 중복을 억제하고 재사용을 높이는 함수는 <strong>유지보수의 편의성</strong>을 높이고 <strong>코드의 신뢰성</strong>을 높이는 효과가 있다.</p>
<h3 id="12-함수-리터럴">1.2. 함수 리터럴</h3>
<p>자바스크립트의 함수는 객체 타입의 값이다. 따라서 숫자 값을 숫자 리터럴로 생성하고 객체를 객체 리터럴로 생성하는 것처럼 함수도 함수 리터럴로 생성할 수 있다. 함수 리터럴은 function 키워드, 함수이름, 매개변수 목록, 함수 몸체로 구성된다.</p>
<pre><code class="language-js">// 변수에 함수 리터럴을 할당
const f = function add(x,y){
    return x+y
}</code></pre>
<h3 id="13-함수-정의">1.3. 함수 정의</h3>
<p>1) 함수 선언문</p>
<pre><code class="language-js">function add(x,y){
    return x+y
}</code></pre>
<p>2) 함수 표현식</p>
<pre><code class="language-js">const add = function(x,y){
    return x+y
}</code></pre>
<p>3) Function 생성자 함수</p>
<pre><code class="language-js">// 변수에 함수 리터럴을 할당
const add = new Function(&#39;x&#39;,&#39;y&#39;,&#39;return x+y&#39;)</code></pre>
<p>4) 화살표 함수(ES6)</p>
<pre><code class="language-js">// 변수에 함수 리터럴을 할당
const add = (x,y) =&gt; {
    return x+y
}</code></pre>
<h3 id="14-함수-선언문과-함수-표현식">1.4. 함수 선언문과 함수 표현식</h3>
<p>1) 함수 선언문(Function Declarations)</p>
<ul>
<li>일반적인 프로그래밍 언어에서의 함수 선언과 비슷한 형식이다.</li>
</ul>
<pre><code class="language-js">function 함수명() {
  구현 로직
}
</code></pre>
<pre><code class="language-js">// 예시
function funcDeclarations() {
  return &#39;A function declaration&#39;;
}
funcDeclarations(); // &#39;A function declaration&#39;
</code></pre>
<ul>
<li>함수 선언문은 코드를 구현한 위치와 관계없이 자바스크립트의 특징인 호이스팅에 따라 브라우저가 자바스크립트를 해석할 때 맨 위로 끌어 올려진다.</li>
</ul>
<blockquote>
<p><em><strong>Key point💡</strong></em> </p>
</blockquote>
<ul>
<li>함수 선언문은 표현식이 아닌 <strong>문(statement)</strong>이다.</li>
</ul>
<p>2) 함수 표현식</p>
<ul>
<li>유연한 자바스크립트 언어의 특징을 활용한 선언 방식</li>
<li>JavaScript는 일급 객체 언어이기 때문에 변수에 함수를 할당할 수 있다.</li>
</ul>
<pre><code class="language-js">var 함수명 = function () {
  구현 로직
};
</code></pre>
<pre><code class="language-js">// 예시
var funcExpression = function () {
    return &#39;A function expression&#39;;
}
funcExpression(); // &#39;A function expression&#39;</code></pre>
<p>3) 함수 선언문과 표현식의 차이점</p>
<blockquote>
<p><em><strong>Key point💡</strong></em> </p>
</blockquote>
<ul>
<li>함수 선언문은 호이스팅에 영향을 받지만, 함수 표현식은 호이스팅에 영향을 받지 않는다.</li>
<li>함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점이 다르기 때문이다.</li>
</ul>
<pre><code class="language-js">
// 실행 전
logMessage();
sumNumbers();

// 함수 선언문
function logMessage() {
  return &#39;worked&#39;;
}

// 함수 표현식
var sumNumbers = function () {
  return 10 + 20;
};
</code></pre>
<ul>
<li><strong>호이스팅</strong>에 의해 자바스크립트 해석기는 코드를 아래와 같이 인식한다.</li>
</ul>
<pre><code class="language-js">// 실행 시
function logMessage() {
  return &#39;worked&#39;;
}

var sumNumbers;

logMessage(); // &#39;worked&#39;
sumNumbers(); // Uncaught TypeError: sumNumbers is not a function

sumNumbers = function () {
  return 10 + 20;
};</code></pre>
<ul>
<li>변수 선언 식별자 <code>var</code>에 의해 호이스팅이 적용되어 위치가 상단으로 끌어올려졌다.</li>
</ul>
<pre><code class="language-js">var sumNumbers;

logMessage();
sumNumbers();</code></pre>
<ul>
<li><p>하지만 실제 sumNumbers 에 할당될 function 로직은 호출된 이후에 선언되므로, sumNumbers 는 함수로 인식하지 않고 변수로 인식한다.</p>
</li>
<li><p>변수 선언은 런타임 이전에 실행되어 <code>undefined</code>로 초기화되지만 <strong>변수 할당문의 값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로 함수 표현식의 함수 리털럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.</strong></p>
</li>
<li><p>따라서 *<em>함수 표현식으로 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생한다. *</em></p>
</li>
</ul>
<h3 id="15-왜-함수-표현식을-사용할까">1.5. 왜 함수 표현식을 사용할까?</h3>
<p>1) 함수 표현식 장점</p>
<blockquote>
<p><em><strong>Key point💡</strong></em> </p>
</blockquote>
<ul>
<li>‘함수 표현식이 호이스팅에 영향을 받지 않는다’ 는 특징 이외에도 함수 선언식보다 유용하게 쓰이는 경우는 다음과 같다.<ul>
<li>클로져로 사용</li>
<li>콜백으로 사용 (다른 함수의 인자로 넘길 수 있음)</li>
</ul>
</li>
</ul>
<p><em>*함수 표현식으로 클로져 생성하기</em></p>
<blockquote>
<p><em><strong>클로져💡</strong></em> </p>
</blockquote>
<ul>
<li>함수를 실행하기 전에 해당 함수에 변수를 넘기고 싶을 때 사용된다. 더 쉽게 이해하기 위해 아래 예제를 살펴보자.</li>
</ul>
<h3 id="16-화살표-함수">1.6. 화살표 함수</h3>
<ul>
<li>화살표 함수는 기존 함수의 선언문 또는 함수 표현식을 완전히 대체하기 위해 디자인 된 것이 아니라, 화살표 함수는 기존의 함수보다 표현, 내부 동작이 간략화되어 있다.</li>
</ul>
<blockquote>
<p><em><strong>화살표 함수💡</strong></em> </p>
</blockquote>
<ul>
<li>화살표 함수는 생성자 함수로 사용할 수 없으며 기존의 함수와 this 바인딩 방식이 다르고, prototype 프로퍼티가 없으며 arguments 객체를 생성하지 않는다.</li>
</ul>
<h2 id="2-다양한-함수의-형태">2. 다양한 함수의 형태</h2>
<h3 id="21-재귀-함수">2.1. 재귀 함수</h3>
<blockquote>
<p><em><strong>재귀 함수💡</strong></em> </p>
</blockquote>
<ul>
<li>재귀 함수는 자기 자신을 호출하는 행위를 말한다. 재귀 함수는 탈출 조건이 없으면 함수가 무한 호출되어 스택 오버플로(stack overflow) 에러가 발생한다.</li>
</ul>
<h3 id="23-콜백-함수">2.3. 콜백 함수</h3>
<blockquote>
<p><em><strong>콜백 함수💡</strong></em> </p>
</blockquote>
<ul>
<li>함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 <strong>콜백함수</strong>라고 한다.</li>
</ul>
<h3 id="24-고차함수">2.4. 고차함수</h3>
<blockquote>
<p><em><strong>고차함수💡</strong></em> </p>
</blockquote>
<ul>
<li>매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 <strong>고차함수(HOF)</strong>라고 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조와 알고리즘] 자료구조(5) - 시간복잡도
]]></title>
            <link>https://velog.io/@taetae-5/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B05-%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</link>
            <guid>https://velog.io/@taetae-5/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B05-%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84</guid>
            <pubDate>Tue, 03 Jan 2023 18:29:32 GMT</pubDate>
            <description><![CDATA[<h3 id="1-알고리즘-복잡도-계산이-필요한-이유">1. 알고리즘 복잡도 계산이 필요한 이유?</h3>
<p>하나의 문제를 푸는 알고리즘은 다양할 수 있다.</p>
<ul>
<li>정수의 절대값 구하기<ul>
<li>1, -1 -&gt;&gt; 1</li>
<li>방법1: 정수값을 제곱한 값에 다시 루트를 씌우기</li>
<li>방법2: 정수가 음수인지 확인해서, 음수일 때만, -1을 곱하기</li>
</ul>
</li>
</ul>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em>
다양한 알고리즘 중 어느 알고리즘이 더 좋은지 분석하기 위해, 복잡도를 정의하고 계산한다.</p>
</blockquote>
<h3 id="2-알고리즘-복잡도-주요-계산-항목">2. 알고리즘 복잡도 주요 계산 항목</h3>
<ol>
<li><strong>시간 복잡도</strong>: 알고리즘 실행 속도 - <code>가장 중요</code></li>
<li><strong>공간 복잡도</strong>: 알고리즘이 사용하는 메모리 사이즈</li>
</ol>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em>
프로그래밍에서 시간 복잡도에 가장 영향을 많이 미치는 요소는 <strong>반복문</strong>이다.</p>
</blockquote>
<h3 id="3-알고리즘-성능-표기법">3. 알고리즘 성능 표기법</h3>
<ul>
<li><p>Big O (빅-오) 표기법: O(N)</p>
<ul>
<li>알고리즘 최악의 실행 시간을 표기</li>
<li><strong>가장 많이/일반적으로 사용함</strong></li>
<li><strong>아무리 최악의 상황이라도, 이정도의 성능은 보장한다는 의미이기 때문</strong></li>
</ul>
</li>
<li><p>Ω (오메가) 표기법:  Ω(N)</p>
<ul>
<li>오메가 표기법은 알고리즘 최상의 실행 시간을 표기</li>
</ul>
</li>
<li><p>Θ (세타) 표기법: Θ(N)</p>
<ul>
<li>오메가 표기법은 알고리즘 평균 실행 시간을 표기</li>
</ul>
</li>
</ul>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em>
시간 복잡도 계산은 반복문이 핵심 요소임을 인지하고, 계산 표기는 최상, 평균, 최악 중, 최악의 시간인 Big-O 표기법을 중심으로 익혀야한다.</p>
</blockquote>
<h3 id="4-대문자-o-표기법">4. 대문자 O 표기법</h3>
<ul>
<li><p>빅 오 표기법, Big-O 표기법 이라고도 부름</p>
</li>
<li><p>O(입력)</p>
<ul>
<li>입력 n 에 따라 결정되는 시간 복잡도 함수</li>
<li>O(1), O($log n$), O(n), O(n$log n$), O($n^2$), O($2^n$), O(n!)등으로 표기함</li>
<li>입력 n 의 크기에 따라 기하급수적으로 시간 복잡도가 늘어날 수 있음<ul>
<li>O(1) &lt; O($log n$) &lt; O(n) &lt; O(n$log n$) &lt; O($n^2$) &lt; O($2^n$) &lt; O(n!)<ul>
<li>참고: log n 의 베이스는 2 - $log_2 n$</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>단순하게 입력 n에 따라, 몇번 실행이 되는지를 계산하면 됩니다.</p>
<ul>
<li><p><strong>표현식에 가장 큰 영향을 미치는 n 의 단위로 표기합니다.</strong></p>
</li>
<li><p>n이 1이든 100이든, 1000이든, 10000이든 실행을</p>
<ul>
<li><p>무조건 2회(상수회) 실행한다: O(1) </p>
<ul>
<li>n에 따라 변경되는것이 없다.<pre><code class="language-python">  if n &gt; 10:
       print(n)</code></pre>
</li>
</ul>
</li>
<li><p>n에 따라, n번, n + 10 번, 또는 3n + 10 번등 실행한다: O(n)</p>
<pre><code class="language-python">      variable = 1
      for num in range(3):
          for index in range(n):
               print(index)</code></pre>
<ul>
<li>n에 따라, 반복문이 3 x n번 실행하게 됨</li>
</ul>
</li>
<li><p>n에 따라, $n^2$번, $n^2$ + 1000 번, 100$n^2$ - 100, 또는 300$n^2$ + 1번등 실행한다: O($n^2$)</p>
<pre><code class="language-python">      variable = 1
      for i in range(300):
          for num in range(n):
              for index in range(n):
                   print(index)</code></pre>
<ul>
<li>n에 따라, 300 x n x n + 1번 실행</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code>    &lt;img src=&quot;http://www.fun-coding.org/00_Images/bigo.png&quot; width=400/&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조와 알고리즘] 자료구조(4) - 링크드리스트]]></title>
            <link>https://velog.io/@taetae-5/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B03-%EB%A7%81%ED%81%AC%EB%93%9C%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@taetae-5/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B03-%EB%A7%81%ED%81%AC%EB%93%9C%EB%A6%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Mon, 02 Jan 2023 17:14:05 GMT</pubDate>
            <description><![CDATA[<h3 id="1-링크드리스트란linked-list">1. 링크드리스트란(Linked List)?</h3>
<blockquote>
<p><em>*<em>Key Ponint 💡 *</em></em> 
링크드 리스트란?</p>
</blockquote>
<ul>
<li><strong>연결 리스트</strong> 라고도 함 </li>
<li>배열은 순차적으로 연결된 공간에 데이터를 나열하는 데이터 구조<ul>
<li>배열은 <strong>미리 특정한 연결된 공간을 예약</strong>을 하고 거기에 데이터를 쓰고 읽는 구조</li>
<li>링크드 리스트는 미리 예약을 하지 않고 필요할 때마다 데이터를 더 추가하는 구조</li>
<li><strong>배열의 단점을 극복한 자료구조</strong>가 <code>링크드 리스트</code>라고 한다.</li>
</ul>
</li>
<li>링크드 리스트는 떨어진 곳에 존재하는 데이터를 화살표로 연결해서 관리하는 데이터 구조<ul>
<li>링크드리스트는 데이터와 데이터를 가리키는 주소를 한번에 담고 있고 이것을 하나의 데이터로 부른다.<ul>
<li>이러한 <code>데이터</code> + <code>데이터의 주소</code> = <code>노드(Node)</code></li>
</ul>
</li>
</ul>
</li>
<li><font color='#BF360C'>본래 C언어에서는 주요한 데이터 구조이지만, 파이썬은 리스트 타입이 링크드 리스트의 기능을 모두 지원</font></li>
</ul>
<h3 id="2-링크드-리스트-기본-구조와-용어">2. 링크드 리스트 기본 구조와 용어</h3>
<blockquote>
<p><em>*<em>Key Ponint 💡 *</em></em> 
링크드 리스트 구조</p>
</blockquote>
<ul>
<li>노드(Node) : 데이터 저장 단위(데이터 값, 포인터)로 구성</li>
<li>포인터(Pointer) : 각 노드 안에서, 다음이나 이전의 노드와의 연결 정보를 가지고 있는 공간<pre><code>  - 즉, 다음 데이터를 가리키는 주소값</code></pre></li>
</ul>
<ul>
<li>일반적인 링크드 리스트 형태
<img src="https://www.fun-coding.org/00_Images/linkedlist.png" alt="">
(출처: wikipedia, <a href="https://en.wikipedia.org/wiki/Linked_list">https://en.wikipedia.org/wiki/Linked_list</a>)</li>
</ul>
<ul>
<li>노드 : 3개</li>
<li>포인터 : 3개 (화살표)</li>
</ul>
<h3 id="3-링크드-리스트-예시">3. 링크드 리스트 예시</h3>
<ul>
<li>보통 파이썬에서는 링크드 리스트 구현시, 파이썬 클래스를 활용한다.<ul>
<li>파이썬 객체지향 문법 이해 필요</li>
<li>참고 : <a href="https://www.fun-coding.org/PL&amp;OOP1-3.html">https://www.fun-coding.org/PL&amp;OOP1-3.html</a><ul>
<li>10 ~ 12</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p><em>*<em>Key Ponint 💡 *</em></em>
<em><strong>node를 만들기 위해서는 class를 사용하여야 한다.</strong></em>
why? 하나의 노드에는 데이터와 데이터 주소를 저장해야 하기 때문에, 그에 맞는 구조는 클래스가 수월하다. </p>
</blockquote>
<pre><code class="language-python">
class Node:
    # 객체를 만들때, self가 항상 앞에 붙는다.
    # 하지만, 인자로 사용x
    def __init__(self, data, next=None): # next=None 두 번째 인자를 넣으면 주소로 인지하고
        self.data = data
        self.next = next # 주소값을 여기에 넣어준다.

# Node에 인자르 두개 써 넣으면 주소도 입력하고, 그렇지 않으면 None으로 받는다
</code></pre>
<ul>
<li>node와 node 연결하기(포인터 활용)</li>
</ul>
<pre><code class="language-python">
node1=Node(1) # next=None으로 디폴트 값이므로 인자는 하나만 넣어도 된다.
node2=Node(2)
node1.next=node2 # 노드와 노드의 연결 (next로 연결)
head=node1 # 가장 맨 앞의 노드

print(head) # &lt;__main__.Node object at 0x00000245867501C0&gt; -&gt; node1의 주소값
print(node1.next) # &lt;__main__.Node object at 0x0000024586750AC0&gt; -&gt; node2의 주소값
print(node2.next) # None
</code></pre>
<blockquote>
<p><em>*<em>Key Ponint 💡 *</em></em>
<strong>init</strong>의 이해</p>
</blockquote>
<ul>
<li><p>컨스트럭터(생성자)라고 불리는 초기화를 위한 함수(메서드)이다.</p>
</li>
<li><p>인스턴스화를 실시할 때, 반드시 처음에 호출되는 특수한 함수</p>
</li>
<li><p>오브젝트 생성(인스턴스를 생성)과 관련하여 데이터의 초기를 실시하는 함수이다. </p>
<ul>
<li><strong>init</strong>()은 반드시 첫 번째 인수로 self를 지정해야한다. </li>
<li>self에는 인스턴스 자체가 전달되어 있다.</li>
<li>이로 인해, 최과 메소드 내에 인스턴스 변수를 작성하거나, 참고하는 것이 가능해진다. </li>
<li>클래스를 생성할 때에 지정한 인수는 초기화 메소드의 2 번째부터 작성해 나가면 된다.</li>
</ul>
<pre><code class="language-python">
class SomeClass:
  def __init__(self,something):#constructor
      self.something = something
</code></pre>
<h3 id="4-다양한-링크드-리스트-구조">4. 다양한 링크드 리스트 구조</h3>
<blockquote>
<p><em>*<em>Key Ponint 💡 *</em></em>
더블 링크드 리스트 기본 구조</p>
</blockquote>
</li>
<li><p>이중 연결 리스트라고도 한다.
장점 : 양방향으로 연결되어 있어서 노드 탐색이 양쪽으로 모두 가능</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조와 알고리즘] 자료구조(3) - 스택]]></title>
            <link>https://velog.io/@taetae-5/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B03-%EC%8A%A4%ED%83%9D</link>
            <guid>https://velog.io/@taetae-5/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B03-%EC%8A%A4%ED%83%9D</guid>
            <pubDate>Mon, 02 Jan 2023 07:46:57 GMT</pubDate>
            <description><![CDATA[<h3 id="1-스택stack이란">1. 스택(Stack)이란?</h3>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em> 스택이란?</p>
</blockquote>
<ul>
<li>데이터를 제한적으로 접근할 수 있는 구조<ul>
<li>한 쪽 끝에서만 자료를 넣거나 뺼 수 있는 구조</li>
</ul>
</li>
<li><strong>가장 나중에 쌓은 데이터를 가장 먼저 빼낼 수 있는 데이터 구조</strong><ul>
<li>큐 : <strong>FIFO 정책</strong> : 첫 번째 넣은 것이 첫 번째로 나옴</li>
<li>스택 : <strong>LIFO 정책</strong> : 마지막에 넣은 것이 첫 번째로 </li>
</ul>
</li>
</ul>
<h3 id="2-스택의-구조">2. 스택의 구조</h3>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em> 스택의 구조</p>
</blockquote>
<ul>
<li>스택은 <code>LIFO(Last In, First Out)</code> 또는 <code>FILO(First In, Last Out)</code> 데이터 관리 방식을 따른다.<ul>
<li>LIFO : <strong>마지막에 넣은 데이터를 가장 먼저 추출하는 데이터 관리 정책</strong></li>
<li>FILO : <strong>처음에 넣은 데이터를 가장 마지막에 추출하는 데이터 관리 정책</strong></li>
</ul>
</li>
<li>대표적인 스택의 활용<ul>
<li>컴퓨터 내부의 프로세스 구조의 함수 동작 방식</li>
</ul>
</li>
<li>주요기능<ul>
<li><code>push()</code> : 데이터를 스택에 넣기</li>
<li><code>pop()</code> : 데이터를 스택에서 꺼내기</li>
</ul>
</li>
</ul>
<p><img src="http://www.fun-coding.org/00_Images/stack.png" alt=""></p>
<h3 id="3-스택-구조와-프로세스-스택">3. 스택 구조와 프로세스 스택</h3>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em> </p>
</blockquote>
<ul>
<li>스택 구조는 프로세스 실행 구조의 가장 기본<ul>
<li>함수 호출시 프로세스 실행 구조를 스택과 비교해서 이해가 필요하다.</li>
</ul>
</li>
</ul>
<pre><code class="language-python">
# 재귀함수
def recursive(data):
    if data&lt;0:
        print(&quot;ended&quot;)
    else:
        print(data)
        recursive(data-1)
        print(&quot;returned&quot;, data)



recursive(4)

# 4
# 3
# 2
# 1
# 0
# ended
# returned 0
# returned 1
# returned 2
# returned 3
# returned 4
</code></pre>
<h3 id="4-자료-구조-스택의-장단점">4. 자료 구조 스택의 장단점</h3>
<ul>
<li>장점<ul>
<li>구조가 단순해서, 구현이 쉽다.</li>
<li><strong>데이터 저장/읽기 속도가 빠르다.</strong></li>
</ul>
</li>
</ul>
<ul>
<li><p>단점(일반적인 스택 구현시)</p>
<ul>
<li><p>데이터 최대 갯수를 미리 정해야 한다.</p>
<ul>
<li><strong>파이썬의 경우 재귀함수는 1000번까지만 호출</strong>이 가능하다. </li>
</ul>
</li>
<li><p><strong>저장 공간의 낭비가 발생</strong>할 수 있다.</p>
<ul>
<li>미리 최대 갯수만큼 저장 공간을 확보해야 한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em> 
스택은 미리 <strong>단순하고 빠른 성능</strong>을 위해 사용되므로, 보통 <strong>배열 구조</strong> 를 활용해서 구현하는 것이 일반적이다. 이 경우, 위에서 열거한 단점이 있을 수 있다.</p>
</blockquote>
<h3 id="5-파이썬-리스트-기능에서-제공하는-메서드로-스택-사용해보기">5. 파이썬 리스트 기능에서 제공하는 메서드로 스택 사용해보기</h3>
<blockquote>
<p><em><strong>Key Ponint 💡</strong></em> <code>append(push)</code>, <code>pop</code> 메서드 제공</p>
</blockquote>
<pre><code class="language-python"># apppend 와 pop 메서드 사용

data_stack = list()

data_stack.append(1)
data_stack.append(2)


# 나중에 입력된 값이 먼저 제거 된다.
data_stack.pop() # 2

# 남아있는 값은 가장 먼저 입력된 값 : 1
data_stack # 1</code></pre>
]]></description>
        </item>
    </channel>
</rss>