<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>hyejin_nk.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 20 Feb 2023 13:17:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. hyejin_nk.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hyejin_nk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[자료구조] 이진트리순회 / BFS , DFS]]></title>
            <link>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84%ED%8A%B8%EB%A6%AC%EC%88%9C%ED%9A%8C-BFS-DFS</link>
            <guid>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84%ED%8A%B8%EB%A6%AC%EC%88%9C%ED%9A%8C-BFS-DFS</guid>
            <pubDate>Mon, 20 Feb 2023 13:17:22 GMT</pubDate>
            <description><![CDATA[<h2 id="breadth-first-search-너비우선-탐색-">Breadth-first Search( 너비우선 탐색 )</h2>
<blockquote>
<ul>
<li>같은 레벨에 있는 노드들을 먼저 거쳐가야 한다. </li>
</ul>
</blockquote>
<ul>
<li>자식노드를 보기전에 형제노드를 먼저 체크한다. (수평레벨 체크)</li>
</ul>
<p>배열이나 리스트로 queue를 만들고 Push 생성 unshift 제거
큐를 만들어서 요소를 추적하고, 
그 방문한 데이터의 리스트를 만들어 마지막에 출력</p>
<h3 id="bfs-의사코드">BFS 의사코드</h3>
<ol>
<li>큐에 루트노드를 위치시킨다. </li>
<li>큐에 무언가가 있으면 루프를 계속 돌린다. <ul>
<li>큐에서 dequeue( 배열 shift ) , 데이터의 리스트에 큐에서 꺼내온 값을 넣어줌.</li>
<li>노드 왼쪽에 값이 있다면 큐에 넣어주기</li>
<li>노드 오른쪽 값이 있다면 큐에 넣어준다.</li>
</ul>
</li>
<li>큐에 아무것도 없어 종료가 되면 방문한 데이터 리스트를 출력한다.</li>
</ol>
<h3 id="bfs-소스코드-이진트리">BFS 소스코드 (이진트리)</h3>
<pre><code class="language-tsx">function bfs(bst){
    let queue=[bst.root]
    let visited=[]
    //자바스크립트는 빈배열 값을 true로 판단
    while(queue.length!==0){
        const node = queue.shift();
        visited.push(node.value);

        if(node.left) queue.push(node.left);
        if(node.right) queue.push(node.right);
    }
    return visited;
}</code></pre>
<p>위의 코드는 자식의 왼쪽 오른쪽 최대 2개의 노드를 가지는 이진트리일 때 이다. 
만약 자식이 2개 이상일 경우 해당 부분은 변경해야한다.</p>
<hr>
<h2 id="depth-first-search--깊이우선-탐색---전위-중위-후위">Depth-first Search ( 깊이우선 탐색 ) / 전위 중위 후위</h2>
<h3 id="dfs-소스코드">DFS 소스코드</h3>
<pre><code class="language-tsx">    class BinarySearchTree{
        constructor(){
            this.root = null;
        }
        insert(value){
            let newNode = new Node(value);

            if(!this.root){ 
                this.root = newNode;
                return this 
            }

            let curNode = this.root;
            let count=0;
            while(curNode){
                if(value === curNode.value) return undefiend;
                if(value &lt; curNode.value) {
                    if(curNode.left === null) {
                        curNode.left = newNode;
                        return this;
                    }
                    curNode = curNode.left
                }
                else {
                    if(curNode.right === null) {
                        curNode.right = newNode;
                        return this;
                    }
                    curNode = curNode.right
                }
            }
            return this;
        }

        find(value){
            if(!this.root) return false;

            let curNode = this.root;
            while(curNode){

                if(curNode.value===value) return true;

                if(value&lt;curNode.value) curNode= curNode.left;
                else curNode = curNode.right;

            }
            return false   
        }

        dfs_pre(){
            let visited= [];
            let current = this.root;

            function traverse(node){
                visited.push(node.value);
                if(node.left) traverse(node.left);
                if(node.right) traverse(node.right);
            }
            traverse(current);
            return visited;
        }
        dfs_post(){
            let visited=[];
            let current = this.root;

            function traverse(node){

                if(node.left) traverse(node.left);
                if(node.right) traverse(node.right);
                visited.push(node.value);
            }
            traverse(current);
            return visited;

        }
    }

    class Node{
        constructor(value){
            this.value = value;
            this.left = null;
            this.right= null;
        }

    }
</code></pre>
<p> <strong>traverse 헬퍼함수</strong>에서 위치만 바꿔주면 전위, 중위, 후위가 된다. </p>
<ul>
<li><p>전위순위 부모노드-왼쪽자식-오른쪽자식</p>
<ol>
<li><p>visited [] 나중에 출력시킬 데이터 </p>
<p>  최상위 노드를 current 변수에 저장 </p>
</li>
<li><p>헬퍼함수를 만든다. </p>
<ul>
<li><p>현재 노드에 저장된 값을 visited 배열에 push</p>
</li>
<li><p>왼쪽 자식이있다면 왼쪽 자식을 넘겨 헬퍼 함수를 또 부른다.</p>
</li>
<li><p>오른쪽 자식이 있다면 오른쪽 자식을 넘겨  헬퍼 함수를 부른다.</p>
<pre><code class="language-tsx">function traverse(node){
  visited.push(node.value);
      if(node.left) traverse(node.left);
  if(node.right) traverse(node.right);
}</code></pre>
</li>
</ul>
</li>
</ol>
</li>
<li><p>후위 왼쪽자식- 오른쪽자식-부모노드</p>
<p>  전위와 동일하지만 할퍼함수에서 순서가 바뀐다. </p>
<p>  왼쪽 헬퍼함수 , 오른쪽 헬퍼함수 현재노드 값 visited 배열에 저장</p>
<pre><code class="language-tsx">  function traverse(node){
      if(node.left) traverse(node.left);
      if(node.right) traverse(node.right);
      visited.push(node.value);
  }</code></pre>
</li>
<li><p>중위 왼쪽 - 부모- 오른쪽</p>
</li>
</ul>
<h2 id="bfs-dfs-어느것을-선택">BFS, DFS 어느것을 선택?</h2>
<ul>
<li>BFS는 큐가 나중에 비어지게 되긴 하지만 데이터를 저장함으로 공간복잡도가 DFS에 비해서 크다. </li>
<li>시간복잡도에서는 차이가없다. 왜냐면 100개 노드가 있다면 모드 방문해야하는 것은 동일하기 때문이다.</li>
<li>깊이보다 너비가 넓은 트리의 경우 DFS가 BFS보다 더 적은 공간을 점유한다.<ul>
<li>트리가 넓다면 너비우선은 큐를 저장하는데에 더 많은 공간을 사용한다.</li>
</ul>
</li>
<li>깊이가 아주 긴 트리라면 DFS가 더 많은 공간을 차지하게 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 백준 / node.js ] 1541.잃어버린 괄호]]></title>
            <link>https://velog.io/@hyejin_nk/%EB%B0%B1%EC%A4%80</link>
            <guid>https://velog.io/@hyejin_nk/%EB%B0%B1%EC%A4%80</guid>
            <pubDate>Tue, 07 Feb 2023 11:46:31 GMT</pubDate>
            <description><![CDATA[<p>최솟값을 가지도록 괄호가 쳐져있다고 생각해야한다. </p>
<p><code>-</code>가 붙은 숫자를 시작으로 뒤에가 가장 값이 클때 괄호를 닫아줘야한다. 
+와 -연산만 존재함으로 가장 값이 클 때는 + 연산이 이어져서 나오는 부분이다. 
(다음 - 를 만나기 전까지! ) </p>
<p><code>10+20-30+40-50</code>
이라고 가정하면 <code>10+20-(30+40)-50</code> 이 가장 작은 값이다. </p>
<blockquote>
<ol>
<li>-를 기준으로 나누면 아래와 같다.
<code>[ &#39;10+20&#39; , &#39;30+40&#39; , &#39;50&#39; ]</code></li>
<li>배열을 순회하면서 +연산이 필요한 부분은 모두 더해준다.
<code>[30, 70, 50]</code></li>
<li><ul>
<li>기준으로 분리했으니, 첫번째 배열부터 시작하여 값을 빼준다. 
<code>정답 : -150</code></li>
</ul>
</li>
</ol>
</blockquote>
<pre><code class="language-tsx">const fs = require(&quot;fs&quot;);

//1. - 기준으로 배열로 분리
let input = fs.readFileSync(&quot;/dev/stdin&quot;).toString().split(&quot;-&quot;);
//1번 input = [ &#39;10+20&#39; , &#39;30+40&#39; , &#39;50&#39; ]

//3번 answer = -130
// reduce에서 값을 -해서 누적하여 값 반환 
let answer = input.reduce((total, item, idx) =&gt; {

  //split : 각각 배열은 문자열로 되어있으며, + 연산자가 있는 것도 있다. 
  //map : + 기준으로 나눠 배열로 만들고 숫자로 만든다.
  //reduce : 값을 누적하여 합해준다 &gt; 합한 값이 나오도록 한다. 
  //2번 sum= [30, 70, 50]
  let sum = item
    .split(&quot;+&quot;)
    .map((a) =&gt; +a)
    .reduce((total, cur) =&gt; total + cur);

  return idx === 0 ? total + sum : total - sum;

}, 0);

console.log(answer);
</code></pre>
<p>리턴할 때 처음에 total-sum 으로 해줬는데 그럴경우, 
-30-70-50이 되어버린다. 
첫번째의 경우는 양수 값이기 때문에 30-70-50이 되어야하기 때문에,
첫번째 인자일 경우는 값을 더할 수 있도록 해야한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[js] 백준 입력받기]]></title>
            <link>https://velog.io/@hyejin_nk/js-%EB%B0%B1%EC%A4%80-%EC%9E%85%EB%A0%A5%EB%B0%9B%EA%B8%B0</link>
            <guid>https://velog.io/@hyejin_nk/js-%EB%B0%B1%EC%A4%80-%EC%9E%85%EB%A0%A5%EB%B0%9B%EA%B8%B0</guid>
            <pubDate>Tue, 07 Feb 2023 10:25:24 GMT</pubDate>
            <description><![CDATA[<p>프로그래머스의 경우는 solution함수에 필요한 정보들을 넘겨주는데,
백준의 경우는 그렇지않다. </p>
<p>js는 입력받는 것이 타 언어에 비해 까다로운데, 이것을 정리해보려고 한다. 
<br></p>
<h2 id="백준-4344번-문제로-입력받기-예시">백준 4344번 문제로 입력받기 예시</h2>
<pre><code>// 예시 입력 데이터 
2
5 50 50 70 80 100
7 100 95 90 80 70 60 50</code></pre><br>

<h3 id="1-rest연산자-이용-개행을-기준으로-배열-생성">1. rest연산자 이용, 개행을 기준으로 배열 생성</h3>
<pre><code class="language-tsx">let [n, ...input] = fs.readFileSync(&quot;index.txt&quot;).toString().split(&quot;\n&quot;);</code></pre>
<p>개행을 기준으로 배열에 담아주는데 첫번째 인자는 개수를 나타내기 때문에 따로 담아주기 위해 배열에 rest 문법을 사용하여 나머지는 input에 담아주었다. </p>
<blockquote>
<p>  &lt;결과&gt;
<code>n = 2</code>
<code>input = [ &#39;5 50 50 70 80 100&#39;, &#39;7 100 95 90 80 70 60 50&#39; ]</code></p>
</blockquote>
<h3 id="2-n번만큼-for문을-돌려-각각-처리를-한다">2. n번만큼 for문을 돌려 각각 처리를 한다.</h3>
<pre><code>공백을 기준으로 나누고 1번과 동일하게 0번째는 요소의 개수를 나타냄으로 num이라는 변수에 받고, 나머지를 data에 묶어준다. </code></pre><pre><code class="language-tsx">for (let i = 0; i &lt; n; i++) {
  let [num, ...data] = input[i].split(&quot; &quot;).map((item) =&gt; +item);

  //필요한 작업처리 후 console.log()
}</code></pre>
<p>map은 각 요소가 숫자로 바껴야하기 때문에 +를 사용하여 숫자로 변경했다. </p>
<blockquote>
<p>&lt;결과&gt;
<code>[ 50, 50, 70, 80, 100 ]
[
  100, 95, 90, 80,
   70, 60, 50
]</code></p>
</blockquote>
<h3 id="문제-소스코드">문제 소스코드</h3>
<pre><code class="language-tsx">const fs = require(&quot;fs&quot;);

var [n, ...input] = fs.readFileSync(&quot;/dev/stdin&quot;).toString().split(&quot;\n&quot;);
for (let i = 0; i &lt; n; i++) {
  let [num, ...data] = input[i].split(&quot; &quot;).map((item) =&gt; +item);
  const avg = data.reduce((total, cur) =&gt; total + cur, 0) / num;

  let highCnt = 0;
  data.forEach((item) =&gt; {
    if (item &gt; avg) highCnt++;
  });
  console.log(((highCnt / num) * 100).toFixed(3) + &quot;%&quot;);
}
</code></pre>
<hr>
<h2 id="프로그래머스처럼-데이터-가공하여-변경">프로그래머스처럼 데이터 가공하여 변경</h2>
<h3 id="데이터-가공">데이터 가공</h3>
<pre><code>// 예시 입력 데이터 
2
5 50 50 70 80 100
7 100 95 90 80 70 60 50</code></pre><p>예시 데이터가 위와 같다고 했을 때, 
solution 함수에 인자가 어떻게 주어지면 좋은지에 대해서 생각해봤다.
<code>(n , [[ , , ],[ , , , ]] )</code>
위와같이 첫번째 인자에는 총 데이터의 개수
두번째 인자의 경우는 배열의 길이가 n개인 배열로 이루어진 데이터로 설정했다. </p>
<pre><code>// 인자로 받을 데이터 형식
( 2, [ [ 50, 50, 70, 80, 100 ], [ 100, 95, 90, 80, 70, 60, 50] ] )</code></pre><h3 id="문제-소스코드-1">문제 소스코드</h3>
<pre><code class="language-tsx"> // 데이터 가공
const fs = require(&quot;fs&quot;);

let n;  // 데이터 개수 
let testCase = [];  // 데이터를 배열로 받음
let input; 

[n, ...input] = fs.readFileSync(&quot;index.txt&quot;).toString().split(&quot;\n&quot;);
for (let i = 0; i &lt; n; i++) {
  let [_, ...data] = input[i].split(&quot; &quot;).map((item) =&gt; +item);
  testCase.push(data);
}

//---------------------------------
function solution(num, testCase) {
  for (let data of testCase) {
    const avg = data.reduce((total, cur) =&gt; total + cur, 0) / data.length;

    let highCnt = 0;
    data.forEach((item) =&gt; {
      if (item &gt; avg) highCnt++;
    });
    console.log(((highCnt / data.length) * 100).toFixed(3) + &quot;%&quot;);
  }
}

solution(n, testCase);
</code></pre>
<p>백준에서는 입력을 모두 처리해줘야하는데, 이를 프로그래머스에서는 인자로 넘겨주기 때문에 데이터를 바로 사용할 수 있다. </p>
<p>백준에서는 자바스크립트를 통해서 문제를 거의 풀지 않았기 때문에 한번 정리해봤고, 
입력하는 방식에 대해서 확실히 이해했다! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리덕스 사용해보기 ]]></title>
            <link>https://velog.io/@hyejin_nk/React-%EB%A6%AC%EB%8D%95%EC%8A%A4-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@hyejin_nk/React-%EB%A6%AC%EB%8D%95%EC%8A%A4-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Fri, 03 Feb 2023 11:01:31 GMT</pubDate>
            <description><![CDATA[<h2 id="redux를-쓰는-이유">redux를 쓰는 이유</h2>
<ol>
<li><p>모든 컴포넌트가 props를 사용하지 않고 state를 직접 꺼내쓸 수 있다. ( prop drilling을 막아줌 )  </p>
</li>
<li><p>상태관리가 용이하다. </p>
<ul>
<li>직접 데이터를 수정하는 것이 아님 → 리듀서를 통해 조작 → 외부에서 데이터를 조작하지 못하게 하면서 의도하지 않게 state가 변경되는 것을 방지</li>
</ul>
<blockquote>
<p>store : 전역으로 관리하는 state를 담는 공간
action: store에 있는 state를 변경하기 위해 보내는 신호
reducer:  state와 이 state를 어떻게 처리할지를 알려주는 action을 파라미터로 받아 처리</p>
</blockquote>
</li>
</ol>
<h2 id="사용방법">사용방법</h2>
<h3 id="설치">설치</h3>
<p><code>yarn add redux react-redux @types/react-redux</code>
``</p>
<h3 id="테스트-코드">테스트 코드</h3>
<p>index.js에서 Provider를 감싼다.</p>
<ol>
<li>rootReducer 정의<ul>
<li>여러개의 리듀서를 하나로 합쳐줌</li>
</ul>
</li>
</ol>
<pre><code class="language-tsx">
// reducers/index.ts

import { combineReducers } from &#39;redux&#39;;
import user from &#39;./user&#39;;

// 여러 reducer를 사용하는 경우 reducer를 하나로 묶어주는 메소드입니다.
// store에 저장되는 리듀서는 오직 1개
const rootReducer = combineReducers({
  user,
});

export default rootReducer;

export type RootState = ReturnType&lt;typeof rootReducer&gt;;</code></pre>
<p>리듀서를 사용할 때는 , 연관이 있는 이름으로 폴더를 만들어서 기능별로 파일을 생성하여 사용한다. </p>
<p> <code>combineReducers</code> 사용하여 한꺼번에 합쳐준다.</p>
<ol start="2">
<li>세부 reducer 정의</li>
</ol>
<pre><code class="language-tsx">
    //액션 , 이름 겹치지 않도록 파일이름 / --
    const CHANGENAME = &#39;user/CHANGE/NAME&#39; as const; // 이름에 + 추가
    const DELETENAME = &#39;user/DELETE/NAME&#39; as const; // 이름 끝 한글자 제거

    //액션 생성함수
    export const changeName = (name: string) =&gt; ({ type: CHANGENAME, name });
    export const deleteName = () =&gt; ({ type: DELETENAME });

    type UserAction = ReturnType&lt;typeof changeName&gt; | ReturnType&lt;typeof deleteName&gt;;

    export interface TUserType {
      name: string;
    }

    //초기값 셋팅 
    const initialState: TUserType = {
      name: &#39;혜진&#39;,
    };

    const user = (currentState = initialState, action: UserAction) =&gt; {
      switch (action.type) {
        case CHANGENAME:
          return {
            ...currentState,
            name: currentState.name + action.name,
          };
        case DELETENAME:
          return {
            ...currentState,
            name:
              currentState.name &amp;&amp;
              currentState.name.slice(0, currentState.name.length - 1),
          };
        default:
          return currentState;
      }
    };

    export default user;</code></pre>
<ol start="3">
<li>app컴포넌트 하위에서 모두 store을 사용할 수 있도록 상위에서 감싸줌</li>
</ol>
<p>  <strong>createStore객체를 사용 →  리액트 앱에서 사용할 리덕스 스토어를 생성</strong></p>
<pre><code class="language-tsx">//index.js

import {createStore} from &#39;redux&#39;;
import rootReducer from &#39;./reducers&#39;;
const store = createStore(rootReducer);

const root = ReactDOM.createRoot(
  document.getElementById(&#39;root&#39;) as HTMLElement,
);
root.render(
  &lt;React.StrictMode&gt;
       &lt;Provider store={store}&gt;
         &lt;App /&gt;
       &lt;/Provider&gt;
  &lt;/React.StrictMode&gt;,
);</code></pre>
<ol start="4">
<li>state 값 사용, 변경  <pre><code class="language-tsx">//state 값 사용
const name = useSelector((state: RootState) =&gt; state.user.name);</code></pre>
</li>
</ol>
<pre><code class="language-tsx">//state 변경
const dispatch = useDispatch();
dispatch(changeName(&#39;+&#39;)) // 보통 onClick과같은 이벤트 함수에 콜백함수로 넣어줌.
</code></pre>
<h4 id="중첩된-컴포넌트에서-간단한-테스트-">*<em>중첩된 컴포넌트에서 간단한 테스트 *</em></h4>
<ul>
<li><p>전체 &gt; Component1 &gt; Conponent2 &gt; Component3 중첩된 구조</p>
</li>
<li><p>Component3에 있는 버튼을 누를 경우 전체를 감싸고 있는 곳에 포함된 UserNam이 변경되어야 한다.</p>
</li>
</ul>
<pre><code class="language-tsx">import { useDispatch, useSelector } from &#39;react-redux&#39;;
import { Button } from &#39;../components/common/button&#39;;
import { RootState } from &#39;../reducers&#39;;
import { changeName, deleteName } from &#39;../reducers/user&#39;;

const ReduxTest = () =&gt; {
  const name = useSelector((state: RootState) =&gt; state.user.name);
  return (
    &lt;div
      style={{
        backgroundColor: &#39;white&#39;,
        height: &#39;100vh&#39;,
        display: &#39;flex&#39;,
        flexDirection: &#39;column&#39;,
        justifyContent: &#39;center&#39;,
        alignItems: &#39;center&#39;,
      }}
    &gt;
      &lt;h2&gt;{`UserName : ${name}`}&lt;/h2&gt;
      &lt;Component1 /&gt;
    &lt;/div&gt;
  );
};

export default ReduxTest;

const Component1 = () =&gt; {
  return (
    &lt;div
      style={{
        backgroundColor: &#39;red&#39;,
        width: &#39;500px&#39;,
        height: &#39;500px&#39;,
        padding: &#39;20px&#39;,
      }}
    &gt;
      &lt;h3&gt;component 1&lt;/h3&gt;
      &lt;Component2&gt;&lt;/Component2&gt;
    &lt;/div&gt;
  );
};
const Component2 = () =&gt; {
  return (
    &lt;div
      style={{
        backgroundColor: &#39;yellow&#39;,
        width: &#39;90%&#39;,
        height: &#39;80%&#39;,
        padding: &#39;20px&#39;,
      }}
    &gt;
      &lt;h3&gt;component 2&lt;/h3&gt;
      &lt;Component3&gt;&lt;/Component3&gt;
    &lt;/div&gt;
  );
};

const Component3 = () =&gt; {
  const dispatch = useDispatch();
  return (
    &lt;div
      style={{
        backgroundColor: &#39;green&#39;,
        width: &#39;90%&#39;,
        height: &#39;80%&#39;,
        padding: &#39;20px&#39;,
      }}
    &gt;
      &lt;h3&gt;component 3&lt;/h3&gt;
      &lt;Button
        width=&quot;80px&quot;
        height=&quot;30px&quot;
        type=&quot;submit&quot;
        css={{ alignSelf: &#39;center&#39; }}
        onClick={() =&gt; dispatch(changeName(&#39;+&#39;))}
      &gt;
        추가
      &lt;/Button&gt;
      &lt;Button
        width=&quot;80px&quot;
        height=&quot;30px&quot;
        type=&quot;submit&quot;
        css={{ alignSelf: &#39;center&#39; }}
        onClick={() =&gt; dispatch(deleteName())}
      &gt;
        삭제
      &lt;/Button&gt;
    &lt;/div&gt;
  );
};</code></pre>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/684048a1-f809-42d3-9463-e2be0e139508/image.png" alt=""></p>
<ul>
<li>기본 default 값은 혜진으로 설정</li>
<li>추가 누를 때마다 상위 컴포넌트의 name 영역에 +가 한개씩 추가</li>
<li>삭제를 누를 때마다 상위 컴포넌트의 name 영역에 +가  한개씩 삭제
<br><br></li>
</ul>
<hr>
<p>상태관리 라이브러리들이 중 recoil을 사용해봤다. </p>
<p>redux를 사용하는 경우가 많아서 과제라던지, 추후에 프로젝트를 진행할 때 도움이 될 것 같아서 테스트용으로 사용해봤다. </p>
<p>스테이트를 변경하거나 추가할 때,
actions, reducer, type 등 보일러 플레이트 코드를 많이 작성해야 하는 단점을 리코일을 사용하고 나서 리덕스를 사용해보니 더욱 와닿았던 것 같다. </p>
<p>확실히 recoil보다 러닝커브가 높은편인 것 같다. 
flux패턴을 이해해야했고,
지금은 정말정말 작은 상태 하나인데도 작성해야하는 코드들이 많았다.</p>
<p>개념을 보았을 때 이해하기 어려웠던 부분이 있었는데, 직접 코드를 작성해보니 이해하기가 좀 더 수월했던것 같다! </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TypeScript] 객체 프로퍼티에 string으로 접근할 경우 에러]]></title>
            <link>https://velog.io/@hyejin_nk/TypeScript-%EA%B0%9D%EC%B2%B4-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EC%97%90-string%EC%9C%BC%EB%A1%9C-%EC%A0%91%EA%B7%BC%ED%95%A0-%EA%B2%BD%EC%9A%B0-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@hyejin_nk/TypeScript-%EA%B0%9D%EC%B2%B4-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EC%97%90-string%EC%9C%BC%EB%A1%9C-%EC%A0%91%EA%B7%BC%ED%95%A0-%EA%B2%BD%EC%9A%B0-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Mon, 30 Jan 2023 02:00:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Element implicitly has an &#39;any&#39; type because expression of type &#39;string&#39; can&#39;t be used to index type</p>
</blockquote>
<ul>
<li>위 오류를 만나게 되었는데, 자바스크립트에서 일반적으로 객체에 프로퍼티로 string을 넘기는 것이 문제가 되지 않는데 타입스크립트에서는 이 부분이 문제가 된다. </li>
</ul>
<pre><code class="language-tsx">interface ButtonProps {
  width?: string;
  height?: string;
  varient?: &#39;default&#39; | &#39;danger&#39; | &#39;normal&#39;;
}
const colors = {
  default: &#39;rgb(36, 41, 47)&#39;,
  danger: &#39;rgb(207, 34, 46)&#39;,
  normal: &#39;rgb(9, 105, 218)&#39;,
};

export const NoneStyleButton = styled.button&lt;ButtonProps&gt;`
  width: ${(props) =&gt; props.width};
  height: ${(props) =&gt; props.height};
  color: ${(props) =&gt;
    props.varient ? colors[props?.varient] : colors[&#39;default&#39;]};

`;
</code></pre>
<p> <code>props.varient ? colors[props?.varient] : colors[&#39;default&#39;]};</code>
해당 부분에서 오류가 났다. 
이유는 varient가 string인데 string으로는 colors 객체에 접근을 할 수 없기 때문에다.</p>
<hr>
<h2 id="해결방법">해결방법</h2>
<blockquote>
<p>1번째 방법 :  string literal 타입의 key로 접근한다.
2번째 방법 : 객체의 타입을 선언할 때 index signature을 사용하면 string으로 접근할 수 있다. </p>
</blockquote>
<h3 id="index-signature">index signature</h3>
<pre><code class="language-tsx">type Varient = &#39;default&#39; | &#39;danger&#39; | &#39;normal&#39;;
interface ButtonProps {
  width?: string;
  height?: string;
  varient?: Varient;
}
type colorType = {
  [key in Varient]: string; // index signature
};
const colors: colorType = {
  default: &#39;rgb(36, 41, 47)&#39;,
  danger: &#39;rgb(207, 34, 46)&#39;,
  normal: &#39;rgb(9, 105, 218)&#39;,
};</code></pre>
<p>colors에 오는 프로퍼티에 좁은 범위로 제한을 주기 위해 Varient에 mapped type을 사용하였고, 
colors객체에 타입을 index signiture로 변경하였다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 이진탐색트리]]></title>
            <link>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89%ED%8A%B8%EB%A6%AC</link>
            <guid>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89%ED%8A%B8%EB%A6%AC</guid>
            <pubDate>Thu, 26 Jan 2023 23:29:08 GMT</pubDate>
            <description><![CDATA[<h1 id="트리">트리</h1>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/d642adb2-c29e-48c0-a52c-e73ea72abe8f/image.png" alt=""></p>
<p>부모와 자식간의 관계로 구성되어 있다. 
리스트는 선형구조 ( 한줄로 있는 구조 )
트리는 비선형 구조 ( 한 갈래에서 여러가지 가지로 뻗어나갈 수 있다 ) </p>
<p>트리는 부모-자식 관계에 따라서 자식 노드만 가리킬 수 있다.
자식이 부모를 가리키거나 형제를 가리킬 수 없다 &gt;&gt; 이것은 그래프다. </p>
<h2 id="용어">용어</h2>
<ul>
<li>루트 - 트리의 최상위 노드 (꼭대기)</li>
<li>자식 - 루트로부터 하위로 연결된 노드 </li>
<li>부모 - 현재 노드에 상위로 연결된 노드</li>
<li>형제 - 같은 부모를 가지는 노드</li>
<li>리프 - 자식이 없는 노드 (끝)</li>
<li>간선(edge) - 한 노드에서 다른 노드로 연결하는 선  </li>
</ul>
<h2 id="트리의-사용-예시">트리의 사용 예시</h2>
<ul>
<li>HTML DOM - HTML 요소인 문자들 사이의 관계는 부모 자식 관계</li>
<li>JSON</li>
<li>프로로그래밍 구문 구조</li>
<li>네트워크 라우팅</li>
<li>인공지능</li>
<li>운영체제에서의 폴더구조 ( 폴더 안의 파일 )</li>
</ul>
<hr>
<h1 id="이진트리">이진트리</h1>
<p>일반적인 트리는 자식이 몇개 있던 상관없지만 이진트리의 경우는 각 노드가 최대 두개의 자식을 가져야한다. </p>
<h2 id="이진탐색트리-bst">이진탐색트리 (BST)</h2>
<ul>
<li>이진 트리의 종류로, 최대 2개의 자식</li>
<li>데이터 순서에 따라 정렬되어 있다.<ul>
<li>특정 노드의 왼쪽은 그 노드보다 작은 값, 오른쪽 노드는 큰 값</li>
</ul>
</li>
</ul>
<h2 id="왜-사용할까-">왜 사용할까 ?</h2>
<ul>
<li><p>작은 것들은 왼쪽, 큰 것들은 오른쪽에 놓는 방식이 어떠한 값을 찾는데에 빠르게 동작한다. </p>
</li>
<li><p>정렬되지 않은 트리와 비교했을 때는 어떠한 값을 찾는다면 이진탐색트리가 더 유리하다.</p>
</li>
<li><p>일반트리에서는 모든 값들을 순회해야하지마 BST에서는 비교할 때마다 탐색해아하는 횟수나 노드의 숫자가 줄어든다 .</p>
</li>
</ul>
<h2 id="bst-구현">BST 구현</h2>
<h3 id="기본구조">기본구조</h3>
<pre><code class="language-tsx">class BinarySearchTree{
        constructor(){
            this.root = null;
        }
    }

    class Node{
        constructor(value){
            this.value = value;
            this.left = null;
            this.right= null;
        }
    }

    let tree= new BinarySearchTree();
    tree.root = new Node(10);
    tree.root.right = new Node(15);
    tree.root.left = new Node(7);

    tree.root.left.right = new Node(9);</code></pre>
<br>


<h3 id="insert-메서드">insert 메서드</h3>
<br>

<p><strong>의사코드</strong></p>
<ul>
<li>넣어줄 생성할 노드를 만든다.</li>
<li>루트에서 시작한다<ul>
<li>루트가 없을 경우, 현재값이 루트가 된다.</li>
<li>루트가 있을 경우, 값이 큰지 작은지 비교한다.<ul>
<li>값이 클 경우<ul>
<li>오른쪽에 자식노드가 있는지 체크한다.<ul>
<li>자식노드가 있다면  그곳과 비교를 반복한다.</li>
<li>자식 노드가 없다면 오른쪽에 추가한다.</li>
</ul>
</li>
</ul>
</li>
<li>값이 작은 경우<ul>
<li>왼쪽에 자식노드가 있는지 체크한다.<ul>
<li>자식 노드가 있다면 그곳과 비교를 반복</li>
<li>자식 노드가 없다면 왼쪽에 추가.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><strong>코드</strong></p>
<pre><code class="language-tsx">insert(value){
        let newNode = new Node(value);

        if(!this.root){ 
            this.root = newNode;
            return this 
        }

        let curNode = this.root;
        let count=0;
        while(curNode){
            if(value === curNode.value) return undefiend;
            if(value &lt; curNode.value) {
                if(curNode.left === null) {
                    curNode.left = newNode;
                    return this;
                }
                curNode = curNode.left
            }
            else {
                if(curNode.right === null) {
                    curNode.right = newNode;
                    return this;
                }
                curNode = curNode.right
            }
        }
        return this;
    }</code></pre>
 <br>

<h3 id="contains메서드">contains메서드</h3>
<p>insert와 값을 비교하는 것은 똑같지만, 만약 현재 노드가 null일 경우는 값을 찾지 못했음을 의미하므로 중단한다. </p>
<p><strong>코드</strong></p>
<pre><code class="language-tsx">find(value){
        if(!this.root) return false;

        let curNode = this.root;
        while(curNode){

            if(curNode.value===value) return true;

            if(value&lt;curNode.value) curNode= curNode.left;
            else curNode = curNode.right;

        }
        return false
    }</code></pre>
<p>현재 노드 기준으로 검사하였다. </p>
<h3 id="bst-시간복잡도">BST 시간복잡도</h3>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/a2a661ae-15f5-4758-a259-b946c7f92155/image.png" alt="">
트리가 위의 그림에 비해서는 노드의 개수가 2배가 추가되었지만 
이진탐색트리는 데이터가 이미 정렬이 되어있기 때문에 한단계만 더 확인하면 된다. </p>
<blockquote>
<p>삽입 - O(log N)<br>탐색 - O(log N)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/6675dc15-1622-4477-b5c0-fc82c4924e78/image.png" alt="">
한쪽으로 치우친 트리의 경우 시간복잡도가 달라질 수 있다. </p>
<p>이럴 경우는 삽입이나 탐색을 할 때 노드의 숫자만큼 단계가 추가되기 때문에 시간복잡도가 O(lon N)에 해당하지 않는다. </p>
<p>만약에 이것을 꼭 트리로 사용해야 한다면, 다른 숫자를 루트로 놓고 이진탐색트리를 다시 작성하는 것이 좋다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] react-hook-form React.forwardRef 에러 해결]]></title>
            <link>https://velog.io/@hyejin_nk/React-react-hook-form-React.forwardRef-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@hyejin_nk/React-react-hook-form-React.forwardRef-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Thu, 26 Jan 2023 11:24:28 GMT</pubDate>
            <description><![CDATA[<h3 id="시도">시도</h3>
<p>이 전에 컴포넌트 분리가 잘 안되었던 것 같아서 
이번에는 재사용 가능한 컴포넌트들을 최대한 분리해보려고 한다. </p>
<p>그래서 이번에 원티드 프리온보딩에서 라벨, 인풋, 에러메시지를 하나로 묶어서 하나의 컴포넌트로 만들고 여기서 input 유효성 검사를 편리하게 하기 위해서 react-hook-form을 사용하였다.</p>
<hr>
<h3 id="문제">문제</h3>
<p>근데 디버깅에서 오류가 나진 않았지만 런타임에서 에러를 만나게 되었다. 
React.forwardRef에 대한 가이드가 있었다. </p>
<pre><code class="language-tsx">

const LoginForm = () =&gt; {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors },
    reset,
  } = useForm&lt;FormValue&gt;();

  const onSubmit: SubmitHandler&lt;FormValue&gt; = async (data) =&gt; {
    //await api 호출
    console.log(data);
    reset();
  };

  const emailRegister = register(&#39;id&#39;, {
    required: { value: true, message: &#39;이메일을 입력해주세요.&#39; },
    pattern: { value: Regex.email, message: &#39;이메일 형식을 입력해주세요.&#39; },
  });

  return (
    &lt;form onSubmit={handleSubmit(onSubmit)} css={formStyle}&gt;
      &lt;AuthInput
        type=&quot;email&quot;
        placeholder=&quot;이메일을 입력해주세요&quot;
        errorMessage={errors?.id?.message}
        {...emailRegister}
      /&gt;
      &lt;Button
        width=&quot;80px&quot;
        height=&quot;30px&quot;
        type=&quot;submit&quot;
        css={{ alignSelf: &#39;center&#39; }}
      &gt;
        로그인
      &lt;/Button&gt;
    &lt;/form&gt;
  );
};</code></pre>
<p>이 전에는 AuthInput이라는 것이 따로 컴포넌트로 분리되어있던 게 아니라, 
해당 로그인 폼 안에서 input 에대한 것들이 모두 들어있었다.
그래서 이 전에는 ref를 내부 컴포넌트로 넘기지 않음으로 이러한 에러를 만나지 않았다. </p>
<p>찾아보니, ref prop은 HTML 엘리먼트 접근할 때 특수한 용도로 사용되기 때문에 일반적인 prop으로 사용을 할 수 없다.
근데 react-hook-form에서 제공하는 register에는 ref가 포함이 되어있어서 이때 발생되는 문제였다. </p>
<hr>
<p>React.forwardRef API를 사용하여 내부 컴포넌트에 refs를 명시적으로 전달할 수 있다.</p>
<pre><code class="language-tsx"> React.forwardRef((props, ref) =&gt; {
    return &lt;input {...props} forwardedRef={ref} /&gt;;
  }</code></pre>
<p>React.forwardRef는 props와 ref 파라미터를 받아 React 노드를 반환하는 렌더링 함수를 받습니다. </p>
<pre><code class="language-Tsx">//input.tsx

interface inputProps {
  type: string;
  name: string;
  placeholder: string;
  errorMessage?: string;
}

export const AuthInput = React.forwardRef&lt;HTMLInputElement, inputProps&gt;(
  ({ type, name, placeholder, errorMessage, ...etc }, ref) =&gt; {
    return (
      &lt;div css={inputWrapStyle}&gt;
        &lt;label htmlFor={name} css={labelStyle}&gt;
          {name.toUpperCase()}
        &lt;/label&gt;
        &lt;input
          css={inputStyle}
          id={name}
          type={type}
          name={name}
          placeholder={placeholder}
          ref={ref}
          {...etc}
        /&gt;
        {errorMessage &amp;&amp; &lt;ErrorMessage content={errorMessage}&gt;&lt;/ErrorMessage&gt;}
      &lt;/div&gt;
    );
  },
);
const ErrorMessage = ({ content }: { content: string | undefined }) =&gt; {
  return &lt;p css={messageStyle}&gt;{content}&lt;/p&gt;;
};</code></pre>
<pre><code class="language-tsx">
export const AuthInput = React.forwardRef&lt;HTMLInputElement, inputProps&gt;(
  ({ type, name, placeholder, errorMessage, ...etc }, ref) =&gt; {
    return (
       &lt;div&gt;
        &lt;input
          css={inputStyle}
          id={name}
          type={type}
          name={name}
          placeholder={placeholder}
          ref={ref}
          {...etc}
        /&gt;
      &lt;/div&gt;
    );
  },
);</code></pre>
<p><code>{type,name,placeholder,errorMessage,...etc}</code> 와 <code>ref</code> 가
React.forwardRef 콜백함수에 인자로 들어가있다. </p>
<hr>
<h3 id="정리">정리</h3>
<p>리액트에서는 DOM을 선택해 직접 접근하기 위해 ref를 사용하게 된다.
재사용하기 위해서 커스텀하게 input 컴포넌트를 만들었고 ref를 props로 넘겨줄 때 문제가 생겼던 부분이었다. 
React.forwardRef api를 활용해서 내부 컴포넌트에 ref를 명시할 수 있게 해주었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 스택 & 큐 ]]></title>
            <link>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%8A%A4%ED%83%9D-%ED%81%90</link>
            <guid>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%8A%A4%ED%83%9D-%ED%81%90</guid>
            <pubDate>Tue, 24 Jan 2023 23:57:52 GMT</pubDate>
            <description><![CDATA[<h2 id="스택-lifo">스택 LIFO</h2>
<ul>
<li>Last In First Out ( 후입선출 ) </li>
<li>가장 마지막에 있는 요소가 먼저 제거</li>
<li>자바스크립트에서 재귀를 다룰 때  콜스택도 마찬가지로 이 스택의 원리이다. </li>
<li>다른 언어는 내장된 경우가 있는데, 자바스크립트에서는 내장되어있지 않아서 배열을 사용하던가, 연결리스트 통하여 구현해야한다. </li>
</ul>
<h3 id="사용되는-예시">사용되는 예시</h3>
<ul>
<li>함수호출을 다루는 콜스택</li>
<li>포토샵 실행취소, 다시실행 ( undo / redo)</li>
<li>브라우저 방문기록</li>
</ul>
<hr>
<h3 id="접근---배열">접근 - 배열</h3>
<ul>
<li><p>배열의 끝 지점의 삽입과 삭제를 다루는 <code>push</code>, <code>pop</code> 함수를 이용한다. </p>
</li>
<li><p>배열의 첫 지점에서 삽입과 삭제를 다루는 <code>unshift</code>, <code>shift</code> 함수를 이용할 수도 있다. </p>
</li>
</ul>
<p>후입선출의 규칙만 지킨다면 어느방향으로 하던 상관은 없지만 
<code>unshift</code>, <code>shift</code> 는 배열에서는 첫번째 인자를 삽입 삭제할 경우 모든 요소들이 다 움직여야하기 때문에 시간복잡도가 <code>O(N)</code>이라 비효율적이다.</p>
<p><code>push</code>, <code>pop</code>의 경우는 <code>O(1)</code> 상수시간이기 때문에 이것을 사용하는게 더 낫다. </p>
<h3 id="접근---연결-리스트-클래스">접근 - 연결 리스트 (클래스)</h3>
<p>인덱스에 접근하는 것이 아니라 그냥 삽입했을 때 순서에 기반해서 정보를 다루기만 하면 되기 때문에 배열에 사용되는 많은 메소드들이 필요가 없다. </p>
<p>단일 연결리스트에서는 한쪽 방향으로만 연결되는 특성 때문에 맨 마지막에 요소를 삽입하거나 삭제할 때 앞에서부터 검색해야 함으로 push, pop보단 unshift,shift 로 처음요소를 삽입하고 제거하는것이 낫다. </p>
<p>후입선출의 규칙은 동일하다! </p>
<pre><code class="language-jsx">class Stack {
    constructor(){
        this.first = null;
        this.last = null;
        this.size=0;
    }

    push(val){
        //앞에 삽입 
        const addData = new Node(val);
        if(this.size ===0){
            this.first = addData;
            this.last = addData;
        }
        else{
            addData.next = this.first;
            this.first = addData;
        }
        return ++this.size;

    }
    pop(){
        //앞을 제거 

        if(this.size ===0 ) return null;

        let deleteData = this.first;
        if(this.first===this.last){
            this.last = null;
        }
        this.first = this.first.next;

        this.size--;
        return deleteData;

    }
}

class Node {
    constructor(val){
        this.val = val;
        this.next = null;
    }
}</code></pre>
<hr>
<h2 id="큐-fifo">큐 FIFO</h2>
<ul>
<li>먼저들어온 것이 먼저 나간다</li>
<li>삽입 enqueue, 삭제 dequeue</li>
</ul>
<h3 id="예시">예시</h3>
<ul>
<li>줄 서는 경우</li>
<li>게임에서 접속하려고 대기 &gt; 가장 오래 기다린 사람을 추적하는 큐 데이터 구조</li>
<li>컴퓨터 백그라운드</li>
<li>프린트</li>
</ul>
<h3 id="접근---배열-1">접근 - 배열</h3>
<ul>
<li>삽입 push 함수 / 삭제 shift함수</li>
<li>삽입 unshift / 삭제 pop 함수 ( 거꾸로 구현 )</li>
<li>삽입 O(1), 삭제 O(N)</li>
</ul>
<h3 id="접근---리스트">접근 - 리스트</h3>
<ul>
<li>맨 뒤에 추가하고 맨 앞에 삭제</li>
<li>맨 앞에 추가하고 맨 뒤에 삭제 ( 거꾸로 )</li>
</ul>
<pre><code class="language-tsx">class Queue {
    constructor(){
        this.first = null;
        this.last = null;
        this.size=0;
    }

    enqueue(val){
        const newNode = new Node(val);
        if(this.size ===0){
            this.first = newNode;
            this.last = newNode;
        }
        else{
            this.last.next = newNode;
            this.last = newNode;
        }
        return ++this.size;

    }
    dequeue(){

        if(this.size ===0 ) return null;

        let deleteData = this.first;
        if(this.first===this.last){
            this.last = null;
        }
        this.first = this.first.next;

        this.size--;
        return deleteData;

    }
}

class Node {
    constructor(val){
        this.val = val;
        this.next = null;
    }
}</code></pre>
<hr>
<h2 id="스택--큐-빅오">스택 &amp; 큐 빅오</h2>
<p>스택과 큐에서는 탐색, 인덱스 위치를 사용해서 접근하는 것이 필요 없다. 
삽입과 제거 후입선출, 선입선출을 잘 지키는 것이 중요하다. </p>
<h3 id="스택">스택</h3>
<p>연결리스트 - 삽입 O(1) / 삭제 O(1)
배열 - 삽입 O(1) / 삭제 O(1) 
복잡도는 상수시간이지만 스택에서는 삽입 삭제만 필요함으로 배열처럼 많은 메서드가 필요없다.
연결리스트의 경우는 코드를 많이 구현해야하기 때문에, 좋은 방법이지만
빠르게 작성을 해야할 경우 배열을 사용하는 것이 낫다.</p>
<h3 id="큐">큐</h3>
<p>연결리스트 - 삽입 O(1) / 삭제 O(1)
배열 - 삽입 O(1) / 삭제 O(N) 혹은 반대 </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 이중연결리스트]]></title>
            <link>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A4%91%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EC%9D%B4%EC%A4%91%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Tue, 24 Jan 2023 04:51:49 GMT</pubDate>
            <description><![CDATA[<h2 id="이중연결-연결리스트">이중연결 연결리스트</h2>
<ul>
<li>양방향으로 이어져있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/98473b1b-4119-4c35-b943-962a7e12bfd6/image.png" alt=""></p>
<ul>
<li><p><strong>단일 연결리스트보다 보완된 점은 ?</strong><br>  만약 맨 마지막 요소를 제거한다고 하면 처음부터 tail을 만나기 전까지 검색을 한 후에 삭제할 수 있고, 
  거꾸로 reverse 해야 할 때도 앞에서부터 순차적으로 하나하나 방향을 바꿔줘야해서 번거롭다.
  이를 이중 연결리스트를 사용하면 보완할 수 있다.</p>
  <Br>
</li>
<li><p><strong>단점은?</strong>
단일 연결리스트보다 유연성은 있지만, 메모리를 더 많이 사용하는 단점이 있다.
단일연결리스트 처럼 다음 것만 저장하는 것이 아니라 이전 것도 저장해야 되기 때문에 메모리를 더 많이 사용한다.</p>
</li>
</ul>
<h3 id="코드">코드</h3>
<h3 id="기본구조">기본구조</h3>
<pre><code>```jsx
class Node {
    constructor(val){
        this.val = val;
        this.prev = null;
        this.next = null;
    }
}

class DoubleLinkedList{
    constructor(){
        this.length=0;
        this.head = null;
        this.tail = null;

    }
}
```</code></pre><h3 id="메서드">메서드</h3>
<ul>
<li><p>push</p>
<ul>
<li><p>추가할 노드를 만든다.</p>
</li>
<li><p>헤드가 널인지, 길이가 0인지 확인 ( 리스트가 비어있는지 체크 )</p>
</li>
<li><p>리스트에 존재할경우</p>
<ul>
<li>테일을 찾아서 테일의 next 프로퍼티를 추가할 노드랑 설정한다.</li>
<li>추가할 노드의 prev 프로퍼티를 테일로 설정</li>
<li>테일 프로퍼티를 추가할 노드를 가리키도록 설정</li>
</ul>
<pre><code class="language-jsx">push(val){
      const newNode = new Node(val);

      //이중연결리스트가 비어있을 때 
      if(this.length===0){
          this.head = newNode;
          this.tail = newNode;
      }
      else{
          newNode.prev = this.tail;
          this.tail.next = newNode;
          this.tail = newNode;
      }

      this.length++;

      return this;
  }</code></pre>
</li>
</ul>
</li>
<li><p>pop</p>
<p>  단일연결리스트와는 다르게 테일(끝) 앞에 있는 요소를 찾아서 next 프로퍼티를 null로 설정해줘야 연결이 끊긴다. </p>
<ul>
<li><p>리스트가 비어있을 때 pop 하려고 하면 undefined 반환 (내보낼 값이 없으니까)</p>
</li>
<li><p>리스트가 비어있지 않을 경우</p>
<ul>
<li>현재 테일을 나중에 출력할 수 있도록 변수에 저장</li>
<li>리스트가 1개라면, head와 tail을 null로 설정</li>
<li>리스트가 2개 이상일 경우<ul>
<li>현재 테일을 테일 앞에있는 노드를 가리키도록 설정  (prev)</li>
<li>새로운 테일이 가리키는 노드 next를 null로 설정</li>
<li>delete노드의 이전 노드를 null로 설정</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">pop(){
      if(this.length===0) return undefined;

</code></pre>
</li>
</ul>
</li>
</ul>
<pre><code>        let deleteNode = this.tail;

        if(this.length ===1){
            this.tail = null;
            this.head = null;
        }else{
            this.tail = deleteNode.prev
            this.tail.next =null;
            deleteNode.prev = null;

        }

        this.length--;
        return deleteNode;

    }
```</code></pre><ul>
<li><p>shift(첫번째 제거)</p>
<ul>
<li><p>길이가 0인지 헤드가 없는지 둘중에 하나만 체크 / 해당한다며 undefined 반환</p>
</li>
<li><p>길이가 1일경우</p>
<ul>
<li>테일과 헤드를 null로 설정해야한다.</li>
</ul>
</li>
<li><p>길이가 1초과일 경우</p>
<ul>
<li>제거할 노드를 저장해준다.</li>
<li>헤드가 삭제할 헤드의 next를 가리킴</li>
<li>연결을 끊어줌<ul>
<li>새로운 헤드의 prev는 null로 설정</li>
<li>삭제할 노드의 next는 null로 설정</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">shift(){ //맨 처음 제거
      if(this.length===0) return undefined;
      //if(!this.head) return undefined;

      const deleteNode = this.head;
      if(this.length ===1){
          this.tail = null;
          this.head = null;
      }
      else{
          this.head = deleteNode.next;
          this.head.prev = null;
          deleteNode.next=null;   
      }

      this.length--;
      return deleteNode;
  }</code></pre>
</li>
</ul>
</li>
<li><p>unshift (첫번째 추가)</p>
<ul>
<li><p>새로운 노드를 만들 값을 인자로 받는다.</p>
</li>
<li><p>리스트가 비어있는경우</p>
<ul>
<li>head와 tail은 추가될 노드를 가리킨다.</li>
</ul>
</li>
<li><p>리스트가 비어있지 않은 경우</p>
<ul>
<li>헤드의 prev 프로퍼티가 새로운 노드를 가리킴</li>
<li>추가할 노드의 next는 헤드를 가리킴</li>
<li>head는 추가할 노드를 가리킴</li>
</ul>
<pre><code class="language-jsx">unshift(val){ //맨 처음 추가
      const newNode = new Node(val);
      if(this.length===0){
          this.head = newNode;
          this.tail = newNode;
      }
      else{
          newNode.next = this.head;
          this.head.prev = newNode;
          this.head = newNode;
      }
      this.length++;

      return this;
  }</code></pre>
</li>
</ul>
</li>
<li><p>get (position)</p>
<ul>
<li><p>index가 음수이거나, length보다 크거나 같을 경우 null 반환</p>
</li>
<li><p>찾을 인덱스의 위치에 따라서 앞에서 갈건지, 뒤에서 갈건지 선택</p>
<ul>
<li>전체 리스트의 길이의 반보다 position이 작거나 같다면<ul>
<li>카운트를 증가하며 앞에서 부터 순회, next (position 만큼)</li>
</ul>
</li>
<li>전체 리스트의 길이의 반보다 position이 크다면<ul>
<li>뒤에서부터 순회, prev ( length - cnt -1이 position보다 클 때까지만 )</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">get(idx){
      if(idx &lt;0 || idx&gt;=this.length) return null;

      let curNode;
      let cnt=0;
      if(idx&lt;=this.length/2){
          curNode = this.head ;
          while(cnt&lt;idx){
              curNode = curNode.next;
              cnt++;
          }
      }
      else{
          curNode = this.tail;

          while(idx&lt;this.length-cnt-1){
              curNode = curNode.prev;
              cnt++;
          }
      }
      return curNode;
  }</code></pre>
</li>
</ul>
</li>
<li><p>set</p>
<ul>
<li><p>get 을 이미 구현했기 때문에, get 을 활용해서 값을 바꿔줄 수 있다.</p>
<pre><code class="language-jsx">set(idx,val){
      let foundNode = this.get(idx);
      if(foundNode) {
          foundNode.val = val
          return true          
      }
      return false;
  }</code></pre>
</li>
</ul>
</li>
<li><p>insert</p>
<ul>
<li><p>인덱스가 유효한지 체크, 음수이거나 배열길이보다 큰경우</p>
</li>
<li><p>인덱스가 0 이면 unshift 사용</p>
</li>
<li><p>index가 length이면 push사용</p>
</li>
<li><p>위의 조건이 모두 아니라면 get 메서드 사용해서 해당위치에 접근</p>
</li>
<li><p>넣을 위치의 앞에 있는 위치의 노드를 찾아냄 get(idx-1)</p>
<pre><code class="language-jsx">insert(idx,val){ // 특정 위치에 넣기

      if(idx&lt;0 || idx&gt;this.length) return false;
      if(idx===0) return this.unshift(val);
      if(idx === this.length) return this.push(val);

      let newNode = new Node(val);
      let prevNode = this.get(idx-1);
      let nextNode = prevNode.next;

      if(prevNode){

          prevNode.next = newNode, newNode.prev = prevNode;
          newNode.next = nextNode, nextNode.prev = newNode;
          this.length++;

          return true;
      }
      return false;
  }</code></pre>
</li>
</ul>
</li>
<li><p>remove</p>
<ul>
<li><p>인덱스가 유효한지 확인, 음수이거나 배열길이와 크거나 같은 경우</p>
</li>
<li><p>index가 0이면 shift() 사용</p>
</li>
<li><p>index가 length-1이면 pop()사용</p>
</li>
<li><p>위의 조건이 아니라면, get()메서드를 사용해서 제거한다.</p>
<ul>
<li>제거할 노드를 변수에 담아준다.</li>
<li>제거할 노드의 prev의 next가 제거할 노드의 next를 가리키게한다.</li>
<li>제거할 노드의 next의 prev가 제거할 노드의 prev를 가리키게한다.</li>
<li>제거할 노드의 next와 prev는 null로 한다.</li>
<li>길이를 감소하고 제거된 노드를 반환한다.</li>
</ul>
<pre><code class="language-jsx">remove(idx){
      if(idx&lt;0 || idx&gt;=this.length) return undefined;

      if(idx===0) return this.shift(idx);
      if(idx===this.length-1) return this.pop();

      let deleteNode = this.get(idx);

      let prevNode = deleteNode.prev;
      let nextNode = deleteNode.next;
      prevNode.next = nextNode;
      nextNode.prev = beforeNode;

      // deleteNode.prev.next = deleteNode.next;
      // deleteNode.next.prev = deleteNode.prev;

      deleteNode.prev=null;
      deleteNode.next=null;

      this.length--;

      return deleteNode;
  }</code></pre>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="이중연결리스트-빅오">이중연결리스트 빅오</h3>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/7bc6f6b8-72cb-45d1-bbfe-f0cb7eecec77/image.png" alt=""></p>
<ul>
<li>단일연결리스트에서 맨 뒤에 제거하는 경우 처음부터 모두 순회해야함으로 O(N)</li>
<li>탐색은 O(N/2)로 만들수 있는 이유는 인덱스의 위치에 따라서 앞에서 순회할지 뒤에서 순회할지 분할해서 접근할 수 있기 때문이다. 최적화는 되었지만 빅오표기법에서는 간단하게 적음으로 O(N)으로 간주한다.</li>
</ul>
<h3 id="이중연결리스트-vs-단일연결리스트">이중연결리스트 vs 단일연결리스트</h3>
<ul>
<li>이중연결리스트는 이전 노드를 가리키는 부분 빼고는 단일리스트와 똑같다.</li>
<li>이중연결 리스트는 데이터를 반대로 다뤄야 하는 경우 쉽다.<ul>
<li>예를들어, 브라우저에서 검색기록을 봐야하는 경우라면 실제로 이중연결리스트를 많이 사용한다.</li>
</ul>
</li>
<li>탐색에서 시간을 절반이 걸리기 때문에 성능이 더 좋다.</li>
<li>하지만, 이중연결리스트가 추가로 만든 포인터 때문에 메모리를 더 소모하는 약점도 있다.</li>
</ul>
<p>즉, 이중연결리스트는 일정한 상황에서는 훨씬 좋을 수 있는데, 메모리가 더 많이 사용되어지는 단점도 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[자료구조] 단일 연결리스트 ]]></title>
            <link>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EB%8B%A8%EC%9D%BC-%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@hyejin_nk/%EC%9E%90%EB%A3%8C%EA%B5%AC%EC%A1%B0-%EB%8B%A8%EC%9D%BC-%EC%97%B0%EA%B2%B0%EB%A6%AC%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Thu, 12 Jan 2023 12:12:34 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/881cc776-11df-4607-87e7-3e7bd0b276f8/image.png" alt=""></p>
<ul>
<li>연결리스트는 아래와 같은 속성을 가지고 있음<ul>
<li>head : 연결리스트의 시작노드를 가리킴</li>
<li>tail : 연결리스트의 마지막 노드를 가리킴</li>
<li>length : 총 길이</li>
</ul>
</li>
<li>각각 노드들은 값을 가지고 있고, 다른 노드를 가리키거나 가리키지 않는 노드들의 구성으로 이루어져 있다.  ( 다음 노드가 없을 경우 아무것도 없음을 의미하는 null)</li>
<li>데이터에 접근하기 위해 사용할 인덱스는 없기 때문에 5번째 요소에 접근할때 다이렉트로 불가능하고, 1번째→2번째→3번째 앞에서부터 순차적으로 접근하게된다.</li>
</ul>
<h3 id="list와-array차이">list와 array차이</h3>
<blockquote>
<p>List</p>
</blockquote>
<ul>
<li>인덱스가 없음</li>
<li>노드와 다음을 가리키는 포인터로 연결된다.</li>
<li>무작위 접근이 불가 (index가 없기 때문) </li>
<li>삽입과 삭제가 쉽다.</li>
</ul>
<blockquote>
<p>Array</p>
</blockquote>
<ul>
<li>인덱스가 있음</li>
<li>삽입, 삭제는 소모되는 비용이 크다</li>
<li>특정 인덱스에서 빠르게 접근할 수 있다. </li>
</ul>
<br>

<hr>
<br>

<h3 id="기본-구조">기본 구조</h3>
<pre><code class="language-jsx">class Node {
    constructor(val){
        this.val = val;
        this.next = null;
    }
}

class SinglyLinkedList{
    constructor(){
        this.head = null;
        this.tail = null;
        this.length=0;
    }
}</code></pre>
<p>큰 틀은 위와 같다. </p>
<ul>
<li><p><strong>push 메소드</strong></p>
<pre><code class="language-jsx">  push(val){
          const node = new Node(val);
          if(this.length ===0 ){ // if(!this.head)
              this.head = node;
              this.tail = node;
          }
          else{
              this.tail.next = node;
              this.tail = node;

          }
          this.length++;

</code></pre>
</li>
</ul>
<pre><code>    }
```

1.  val의 값을 가지는 노드를 생성한다.
2. 만약 this.length===0 이거나 헤드가 없을 경우는 빈 리스트임으로 시작과 끝이 새로 만들어진 노드를 가리킨다.
3. 만약 비어있는 리스트가 아닐 경우, 리스트의 마지막에 새로운 노드를 추가하고 next 연결,  테일을 맨 끝을 가리키도록 한다. 
4. 길이를 1개 증가시킨다.</code></pre><ul>
<li><p><strong>pop 메소드</strong>        </p>
<pre><code class="language-jsx"> pop(){
         if(!this.head) return undefined;
         let current = this.head;
         let newTail = current;

         while(current.next){
             newTail = current;
             current= current.next;
         }
         this.tail = newTail;
         this.tail.next = null;
         this.length--;

         if(this.length === 0){
             this.head = null;
             this.tail = null;
         }
         return current.val;
     }</code></pre>
<p> 역방향 포인터를 가지고 있지 않기 때문에 처음부터 리스트를 따라가야한다. </p>
<ol>
<li>빈 리스트일 경우, return </li>
<li>끝지점에 도달할 때 까지 계속 따라가는 동시에 이전 노드가 어떤것인지를 저장해야한다.</li>
<li>마지막에서 두번째 노드의 next 를 null로 지정하고, tail이 마지막에서 두번째 노드를 가리키도록 한다. </li>
<li>길이를 하나 감소시키고, 방금 제거한 노드를 반환</li>
</ol>
</li>
</ul>
<ul>
<li><p><strong>shift 메소드 (맨 앞 노드 제거)</strong></p>
<p>  현재 헤드가 가리키고 있는 노드를 제거한 후 헤드가 가리키고 있던 리스트의 다음 노드를 가리키도록 한다.</p>
<pre><code class="language-jsx">  shift(){
          if(!this.head) return undefined;
          let currentHead = this.head;

          this.head = currentHead.next;
          this.length--;
          if(this.length===0) this.tail = null;

          return currentHead.val;

      }</code></pre>
   <br>
</li>
<li><p><strong>unshift 메소드 ( 맨 앞에 노드를 추가 )</strong></p>
<pre><code class="language-jsx">  unshift(val){
          const newNode = new Node(val);

          newNode.next = this.head;
          this.head = newNode;
          this.length++;
          if(this.length===1) this.tail = newNode;
      }</code></pre>
   <br>
</li>
<li><p><strong>get 메소드 ( index 위치의 노드 반환)</strong></p>
<pre><code class="language-jsx">  get(idx){
          if(idx&lt;0 || idx&gt;=this.length) return null;
          let cnt = 0;
          let current = this.head;

          while(cnt!==idx){
              current = current.next;
              cnt ++;
          }
          return current;
      }</code></pre>
<ul>
<li><p>찾을 위치가 음수이거나 현재 리스트의 길이보다 클 경우 null 반환 처리</p>
</li>
<li><p>카운트변수 만들어서 계속 다음노드로 이동</p>
</li>
<li><p>카운트 변수가 idx와 같아지면 루프는 종료되고 해당 노드를 반환</p>
<br>
</li>
</ul>
</li>
<li><p><strong>set 메소드( index 위치의 노드 값 변경)</strong></p>
<pre><code class="language-jsx">  set(idx,val){
          let foundNode = this.get(idx);
          if(foundNode){
              foundNode.val = val;    
              return true;
          }
          return false;
      }</code></pre>
<ul>
<li>이미 구현된 get 메서드를 통해서 index위치의 노드를 가져온다.</li>
<li>그 노드의 값을 변경한다.</li>
<li>set 유무에 따른 true, false 반환</li>
</ul>
</li>
</ul>
<pre><code> &lt;br&gt;</code></pre><ul>
<li><p><strong>insert 메소드 ( index 위치에 추가 )</strong></p>
<ul>
<li><p>내풀이</p>
<p>  push나 unshift 와 같은 것들을 활용하면 되었는데 생각을 못했다,,</p>
<pre><code class="language-jsx">  insert(idx,val){
          const newNode = new Node(val);
          let foundNode = this.get(idx-1);

          if(foundNode){
              newNode.next = foundNode.next;
              foundNode.next = newNode;

              if(idx===this.length) this.tail = newNode;

              this.length++;
              return true;
          }
          else{ // 
              newNode.next = this.head;
              this.head = newNode;
              this.length++;
              return true;
          }

          return false;

      }</code></pre>
</li>
</ul>
</li>
</ul>
<pre><code>```jsx
insert(idx,val){
        if(idx&lt;0 || idx&gt;this.legnth) return false;
        if(idx === this.length ) return this.push(val); //맨마지막에 추가
        if(idx === 0) return this.unshift(val);

        let newNode = new Node(val);
        let prev = this.get(idx-1);
        let temp = prev.next;
        prev.next = newNode;
        newNode.next = temp ; 
        this.length++;
        return true;
    }
```

- 인덱스가 음수이거나, 길이보다 클 경우 false반환
    - 길이보다 클 경우는 이전에 있는 노드가 없기 때문에 연결이 안된다.
- 인덱스가 리스트의 길이랑 같으면 맨 마지막에 추가하는 push 메서드 이용
- 인덱스가 0 (처음)이면 맨 처음에 추가하는 unshift 메서드 이용
- 중간에 값을 추가할 경우, 이전 노드를 받아온다.
- 이전 노드가 가리키는 값을 temp 에 저장하고, 이전 노드가 새로운 노드를 가리키게하고, 새로운 노드는temp 를 가리킨다.
- 추가했기때문에 길이 1 증가
- 성공 시 true, 실패 시 false 반환

 &lt;br&gt;</code></pre><ul>
<li><p><strong>remove 메소드</strong></p>
<pre><code class="language-jsx">  remove(idx){
          if(idx&lt;0 || idx&gt;=this.legnth) return undefined;
          if(idx === this.length ) return this.pop(); //맨마지막에 제거
          if(idx === 0) return this.shift(); // 맨 처음 제거 

          let prev = this.get(idx-1);
          let removeNode = prev.next;
          prev.next = removeNode.next;
          this.length--;
          return removeNode;

      }</code></pre>
<ul>
<li><p>인덱스가 음수이거나, 길이보다 크거나 같을 경우 false반환</p>
</li>
<li><p>인덱스가 리스트의 길이랑 같으면 맨 마지막에 삭제하는 pop 메서드 이용</p>
</li>
<li><p>인덱스가 0 (처음)이면 맨 처음에 삭제하는 shift 메서드 이용</p>
</li>
<li><p>중간 인덱스에 값을 삭제할 경우, 이전 노드를 받아온다.</p>
</li>
<li><p>이전 노드가 가리키는것을 제거할 노드가 아닌 제거할 노드가 가리키는 것을 지정해주어 제거할 노드와의 연결을 끊는다.</p>
</li>
<li><p>길이를 1개 감소시키고 삭제된 노드를 반환한다. 실패 시 undefined 반환</p>
<br>
</li>
</ul>
</li>
<li><p><strong>reverse 메소드</strong> </p>
<p>  0부터 채워넣어야하는데 마지막 요소부터 거꾸로 접근이 안됨.</p>
<p>  주어진 연결 리스트의 노드순서가 역으로 연결되도록 해야한다. </p>
<p>  리스트를 따라가면서 순서를 계속 역으로 바꿔나감 </p>
<pre><code class="language-jsx">  reverse(){

          let currentNode = this.head;
          [this.head,this.tail] = [this.tail,this.head];

          let beforeNode = null
          let nextNode;

          while(true){
              nextNode = currentNode.next;

              currentNode.next=beforeNode;

              beforeNode = currentNode;
              currentNode = nextNode;

              if(!currentNode) break;
          }
      }</code></pre>
<ul>
<li>haed와 tail을 바꾼다</li>
<li>이전 노드(beforeNode)를 null로 초기화한다.</li>
<li>a → b  → c 를 반대로 바꿔줘야한다<ul>
<li>a&lt;- b 로 바꾸려면 먼저 a 노드(currentNode)가 가리키는 b노드(nextNode)를 저장한다.</li>
<li>a노드(currentNode)가 이전노드(beforeNode)를 가리키게 한다.</li>
<li>현재 위치한 (currentNode) 노드를 nextNode로 이동한다.</li>
<li>노드가 null일 때까지 반복한다.</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[nextjs] Portal 사용해서 Modal띄우기 ]]></title>
            <link>https://velog.io/@hyejin_nk/nextjs-Portal-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-Modal%EB%9D%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@hyejin_nk/nextjs-Portal-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%84%9C-Modal%EB%9D%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Mon, 02 Jan 2023 11:19:03 GMT</pubDate>
            <description><![CDATA[<h3 id="portal">Portal?</h3>
<p>React portal을 이용하면 UI 를 어디에 렌더링 시킬지 DOM 을 사전에 선택하여 
부모 컴포넌트의 바깥에 렌더링 할 수 있게 해줄 수 있다. 
즉, DOM 의 계층구조 시스템에 종속되지 않으면서 컴포넌트를 렌더링 할 수 있게 된다. </p>
<h3 id="portal-을-사용한-이유">portal 을 사용한 이유</h3>
<p>프로젝트를 진행하면서 부모 컴포넌트의 스타일링 속성에 제약을 받아서 모달이 가려지는 현상으로 z-index값을 조정해줘야 하는 경우가 있었다.
또 최상위에서 전체 스타일에 width:80%를 스타일을 줬다고 가정한다면,
그 안에 modal 이 들어갈 경우 전체 스타일의 영향을 받을 수 밖에 없기 때문에
portal로 분리하도로 했다.</p>
<hr>
<h3 id="portal-구현하기">portal 구현하기</h3>
<h4 id="portal로-이동하여-modal이-렌더링-될-위치-넣어주기">portal로 이동하여 modal이 렌더링 될 위치 넣어주기</h4>
<pre><code class="language-tsx">export default class MyDocument extends Document {
 //코드 생략... 
  render() {
    return (

      &lt;Html&gt;
        &lt;Head /&gt;
        &lt;body&gt;
          &lt;Main /&gt;
          &lt;div id=&quot;modal-root&quot; /&gt;
          &lt;NextScript /&gt;
        &lt;/body&gt;
      &lt;/Html&gt;
    );
  }
}</code></pre>
<p>react에서는  index.html에 넣어주지만,next는 html 파일이 없으므로 _document.tsx파일에 
<code>&lt;div id=&quot;modal-root&quot; /&gt;</code> 를 추가해주었다.</p>
<p>모달을 띄울 때 modal-root 노드가 <strong>portal</strong>의 도착지점이 된다.</p>
<br>

<h4 id="createportal">createPortal</h4>
<pre><code class="language-tsx">// front/components/modal/ModalPortal.tsx

import React, { ReactElement, useEffect, useState } from &quot;react&quot;;
import ReactDom, { createPortal } from &quot;react-dom&quot;;

const Portal = ({ children }: { children: ReactElement }) =&gt; {
  const [mounted, setMounted] = useState&lt;boolean&gt;(false);

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

  if (typeof window === &quot;undefined&quot;) return &lt;&gt;&lt;/&gt;;

  return mounted ? createPortal(children, document.getElementById(&quot;modal-root&quot;) as HTMLElement) : &lt;&gt;&lt;/&gt;;
};

export default Portal;</code></pre>
<h4 id="호출">호출</h4>
<pre><code class="language-tsx">const Login = () =&gt; {
  return (
    &lt;LoginWrap&gt;
      &lt;LoginContainer&gt;
        //일부코드 생략...
        &lt;Portal&gt;
          &lt;FindPasswordModal /&gt;
        &lt;/Portal&gt;
      &lt;/LoginContainer&gt;
    &lt;/LoginWrap&gt;
  );
};</code></pre>
<p>Portal 의 children은 FindPasswordModal로
모달이 modal-root안에서 랜더링된다. </p>
<h3 id="nextjs에서-사용할-때-주의사항">nextjs에서 사용할 때 주의사항</h3>
<pre><code class="language-tsx">const [mounted, setMounted] = useState&lt;boolean&gt;(false);

  useEffect(() =&gt; {
    setMounted(true);
    return () =&gt; setMounted(false);
  }, []);</code></pre>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/642729d4-84fc-4f39-bbd6-1643eafc46c8/image.png" alt=""></p>
<p>Next는 별도의 설정을 하지 않는다면 SSR과 CSR을 동시에 진행한다.
SSR 과정에서 document에 접근하려고 하면 접근할 수 없어서 에러가 난다. 
따라서 CSR을 마친 후, window객체가 있을때만 동작하게 작성해야 한다.</p>
<p>useEffect()를 통해서 mount되면 createPortal이 작동되도록 구현하였다. </p>
<p><code>if (typeof window === &quot;undefined&quot;) return &lt;&gt;&lt;/&gt;;</code>
이 코드를 추가하여도 해결된다. </p>
<h3 id="결과">결과</h3>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/7fc9a2ea-3d9f-46ef-aa5b-fd64b0a07edd/image.png" alt=""></p>
<p>이 전에서는 __next 최상위 루트 안에 포함되어있었다면,
modal-root로 빠져나와 그 안에 포함되어있는 것을 볼 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React-Query 무효화, React Query Devtools 이용]]></title>
            <link>https://velog.io/@hyejin_nk/React-React-Query-%EB%AC%B4%ED%9A%A8%ED%99%94-React-Query-Devtools-%EC%9D%B4%EC%9A%A9</link>
            <guid>https://velog.io/@hyejin_nk/React-React-Query-%EB%AC%B4%ED%9A%A8%ED%99%94-React-Query-Devtools-%EC%9D%B4%EC%9A%A9</guid>
            <pubDate>Thu, 08 Dec 2022 13:54:35 GMT</pubDate>
            <description><![CDATA[<p>React Query Devtools를 사용해보았다. </p>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/46dd4628-e7b7-44bf-852c-655c4435432b/image.png" alt=""></p>
<p>오늘의 할일을 넣어주면</p>
<ol>
<li>오늘의 할일 리스트에 추가된다.</li>
<li>달력에서 12월 8일에 일정이 있음으로 o이모티콘이 추가되어야한다. 
이렇게 두가지가 이루어져야한다. </li>
</ol>
<p>달력에서 데이터를 불러올 때의 쿼리키
<code>[&quot;plan&quot;,&quot;2022&quot;,&quot;12&quot;]</code></p>
<p>오늘의 할일 데이터를 불러올 때 쿼리키
<code>[&quot;plan&quot;,&quot;2022&quot;,&quot;12&quot;,&quot;08&quot;]</code> 각각 지정했다.</p>
<p>할일을 생성할 때, 두 가지 데이터가 새로 불러와져야 함으로
생성할 때 useMutation 에서 쿼리무효화를 아래와 같이 키로 해주면 된다. 
<code>[&quot;plan&quot;,&quot;2022&quot;,&quot;12&quot;]</code>
해당키로 시작하는 것들은 모두 데이터가 무효화 처리되어 다시 패치된다. </p>
<p>위와 같이 하면 당연히 될 줄 알았는데, 
달력에서는 동작을 하지 않는 문제가 생겼다. </p>
<hr>
<p>mutation을 통해서 query 무효화를 했을 때,
같은 키를 사용하면 무효화가 된다고 했는데 되지 않는 문제가 생겼다.</p>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/b1c9d58e-216d-4108-910e-203b3ef2dd3d/image.png" alt="">
단순히 console로 찍었을 때는 같은 데이터가 쿼리키에 들어간다고 생각했으나,
reqct query devtools를 사용해보았더니 년,월,일 중에서 월의 데이터가 잘못들어가고 있었다. </p>
<p>Calendar 에서 사용했던 Data객체에서는 월이 1<del>12가 아니라, 0</del>11로 0부터 시작하기 때문에 숫자가 하나씩 앞당겨졌던 것이다.</p>
<p>뿐만아니라, 특정 동작에 따라 쿼리키가 무효화 되어서 어떤 쿼리키가 fetch 되는지도 알 수 있어서 데이터 패칭하는것을 관리하기 좋을것 같다는 생각이 들었다 </p>
<hr>
<p>추가로 쿼리키를 일일히 지정해주는 것은 너무 헷갈려서 한곳에서 관리를 해주는 것이 좋을 것 같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[nextjs] title 적용 / hoc 패턴활용하기]]></title>
            <link>https://velog.io/@hyejin_nk/nextjs-title-%EC%A0%81%EC%9A%A9-hoc-%ED%8C%A8%ED%84%B4%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@hyejin_nk/nextjs-title-%EC%A0%81%EC%9A%A9-hoc-%ED%8C%A8%ED%84%B4%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 08 Dec 2022 04:58:47 GMT</pubDate>
            <description><![CDATA[<h3 id="1번째-시도">&lt; 1번째 시도 &gt;</h3>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/1ff11079-f4af-4f59-bf0b-8029992a97d5/image.png" alt="">
<img src="https://velog.velcdn.com/images/hyejin_nk/post/ec289deb-3a87-4241-9547-7be998c7811b/image.png" alt=""></p>
<p>이렇게 할 경우, 일일히 페이지에서 html에 Seo컴포넌트를 추가하고 props로 title을 넣어줘야한다. 
이에 대해서 묶어서 어떻게 해결할 수 있을까 ? </p>
<h3 id="2번째-시도">&lt;2번째 시도&gt;</h3>
<p>SSR 
getServerSideProps함수 &gt; pageProps에다가 넘겨준다.
적용해보니 SEO 컴포넌트에 일일히 props를 넘겨주는것과 크게 다르지 않게 느껴졌다.</p>
<pre><code class="language-tsx">// components/Seo.ts
import Head from &quot;next/head&quot;;

export type SeoPageProps = {
  pageTitle: string;
  pageDesc: string;
};

export default function Seo({ pageTitle, pageDesc }: SeoPageProps) {
  return (
    &lt;Head&gt;
      &lt;title&gt;{pageTitle} | Hi Five&lt;/title&gt;
      &lt;meta property=&quot;og:title&quot; content={pageTitle ? pageTitle : &quot;우쥬&quot;} key=&quot;ogtitle&quot; /&gt;
      &lt;meta property=&quot;og:description&quot; content={pageDesc ? pageDesc : &quot;교환일기 | 일정관리 &quot;} key=&quot;ogdesc&quot; /&gt;
    &lt;/Head&gt;
  );
}</code></pre>
<pre><code class="language-tsx">// pages/_app.tsx
export default function App({ Component, pageProps }: AppProps&lt;SeoPageProps&gt;) {
  const [isLightTheme, setIsLightTheme] = useState(true);
  const { pageTitle, pageDesc } = pageProps;
  console.log(pageTitle, pageDesc);

  return (
    &lt;ThemeProvider theme={isLightTheme ? lightTheme : darkTheme}&gt;
      &lt;GlobalStyle /&gt;
      &lt;Layout&gt;
        &lt;Seo pageTitle={pageTitle} pageDesc={pageDesc}&gt;&lt;/Seo&gt;
        &lt;Component {...pageProps} /&gt;
      &lt;/Layout&gt;
    &lt;/ThemeProvider&gt;
  );
}</code></pre>
<pre><code class="language-tsx">//pages/login.tsx

//...일부 생략 

export default function login() {
  return {
   &lt;&gt;&lt;/&gt; 
  }
}
export async function getServerSideProps() {
  return {
    props: {
      pageTitle: &quot;로그인&quot;,
      pageDesc: &quot;우쥬 로그인 페이지 입니다.&quot;,
    },
  };
}</code></pre>
<h3 id="hoc-패턴">&lt;hoc 패턴&gt;</h3>
<p>hoc패턴 &gt;컴포넌트에서 컴포넌트를 호출한다. 
페이지 안에 있는 getServerSideProps를 호출 할 때 WithGetServerSideProps 내에 커스톰된 getServerSideProps가 호출된다. 
ssr에서 context 이용</p>
<p>context.resolvedUrl &gt; 현재 url을 가져올 수 있어서 ,
그 경로일 경우 어떤 title이 올 지 정해주게되면
일일히 props로 넘기지 않아도 url path에 따라서 titl을 지정할 수 있게 된다.</p>
<pre><code class="language-tsx">//hocs/withGetServerSideProps.ts
import { GetServerSideProps, GetServerSidePropsContext } from &quot;next&quot;;

const PATH = {
  HOME: &quot;/&quot;,
  LOGIN: &quot;/login&quot;,
  JOIN: &quot;/join&quot;,
};

const mapPathToTitle: { [key: string]: string } = {
  [PATH.HOME]: &quot;홈&quot;,
  [PATH.LOGIN]: &quot;로그인&quot;,
  [PATH.JOIN]: &quot;회원가입&quot;,

};

const mapPathToDesc: { [key: string]: string } = {
  [PATH.HOME]: &quot;홈 화면 입니다. &quot;,
  [PATH.LOGIN]: &quot;우쥬, 로그인 페이지 입니다. &quot;,
  [PATH.JOIN]: &quot;회원가입 페이지 입니다.&quot;,
};

const withGetServerSideProps = (getServerSideProps: GetServerSideProps) =&gt; {
  console.log(&quot;withGetServerSideProps&quot;);
  return async (context: GetServerSidePropsContext) =&gt; {
    const pagePath = context.resolvedUrl;
    return await getServerSideProps(context).then((res: { [key: string]: any }) =&gt; {
      console.log(res.props);
      return {
        ...res,
        props: {
          ...res.props,
          isError: false,
          pageTitle: mapPathToTitle[pagePath],
          pageDesc: mapPathToDesc[pagePath],
        },
      };
    });
  };
};

export default withGetServerSideProps;</code></pre>
<p>실제로 페이지 내에서 사용할 때 아래와 같이 사용하였다. 
페이지별로 데이터를 패치하거나 해야할 때는 아래코드 내에서 작업해주면 된다. </p>
<pre><code class="language-tsx">export const getServerSideProps = withGetServerSideProps(async (context: GetServerSidePropsContext) =&gt; {
  return {
    props: {},
  };
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[next js] 리다이렉트 시 깜빡임 문제 > 미들웨어 적용하여 페이지별 접근 제어하기 ]]></title>
            <link>https://velog.io/@hyejin_nk/next-js-redirect-%ED%95%A0-%EB%95%8C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B9%9C%EB%B9%A1%EC%9D%B4%EB%8A%94-%ED%98%84%EC%83%81</link>
            <guid>https://velog.io/@hyejin_nk/next-js-redirect-%ED%95%A0-%EB%95%8C-%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B9%9C%EB%B9%A1%EC%9D%B4%EB%8A%94-%ED%98%84%EC%83%81</guid>
            <pubDate>Thu, 01 Dec 2022 04:02:16 GMT</pubDate>
            <description><![CDATA[<h3 id="원하던-동작">&lt;원하던 동작&gt;</h3>
<p>인증된 사용자가 아닐 경우, 특정페이지에 접근하지 못하도록 막고 login 페이지로 리다이렉트 시켜줌.</p>
<pre><code class="language-tsx">  useEffect(() =&gt; {
    if (!user &amp;&amp; router.pathname !== &quot;/&quot; &amp;&amp; router.pathname !== &quot;/join&quot;) {
      console.log(router.pathname);
      router.push(&quot;/login&quot;);
    }
  }, [isLoginState]);</code></pre>
<h3 id="문제">&lt;문제&gt;</h3>
<p>Client에서 직접 router를 통해 redirect를 할 경우, 페이지가 깜빡임
(실제로 해당 페이지에 한번 접근해서 잠깐 보여졌다가 곧바로 redirect 되는 동작)
예를 들어, /mypage에 접근할 경우 해당페이지의 컴포넌트가 잠시 깜빡거리며 보였다가 login페이지로 이동되는 현상이 생김.</p>
<h3 id="해결">&lt;해결&gt;</h3>
<p>미들웨어는 특정 요청과 응답사이에 놓아지는 모듈역할을 한다. 
Server 환경의 미들웨어를 이용해서 위 문제 없이 곧바로 redirect 할 수 있음</p>
<p>미들웨어는 서버자원이라 로컬스토리지, 세션스토리지에 접근 못하지만 쿠키에는 접근 가능하다.
로그인이 완료되면 쿠키에 토큰 값을 저장해주는 부분을 추가했다. </p>
<p>matcher에 해당하는 것만 미들웨어가 적용 시킴으로써 접근 제어할 페이지를 정할 수 있다. 
미들웨어에서는 토큰값을 기반으로하여 matcher와 매치되는 페이지로 이동을 할 경우, login페이지로 redirect 한다.</p>
<p>이렇게되면 미들웨어에서 먼저 가로채어 처리함으로써 /mypage에 접근조차 하지 않게되어 깜빡임이 사라지게 된다. </p>
<pre><code class="language-tsx">import { PATH } from &quot;@hocs/withGetServerSideProps&quot;;
import { NextRequest, NextResponse } from &quot;next/server&quot;;

export default function commonMiddleware(req: NextRequest) {
  const token = req.cookies.get(&quot;userToken&quot;);

  if (!token) {
    return NextResponse.redirect(new URL(&quot;/login&quot;, req.nextUrl));
  }
}

export const config = {
  matcher: [&quot;/diary&quot;, &quot;/stamp&quot;, &quot;/planner&quot;, &quot;/mypage&quot;],
};
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[axios interceptor 적용해보기 ]]></title>
            <link>https://velog.io/@hyejin_nk/axios-interceptor-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@hyejin_nk/axios-interceptor-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Wed, 23 Nov 2022 08:17:25 GMT</pubDate>
            <description><![CDATA[<p>이전 프로젝트에서는 axios를 잘활용하지 못했다는 피드백을 받았다. </p>
<p>아래 코드와 같이 모든 곳에서 headers를 넣어주고 있고, 
content type이 application/json인 경우는 &quot;Content-Type&quot;=&quot;application/json&quot;
이렇게 일일히 넣어주고 있었다.</p>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/7f953c25-796e-48fa-950d-fabcbd65651e/image.png" alt=""></p>
<p>이번 프로젝트에서는 interceptor를 활용해서 요청에서 중복되는 부분을 한번에 처리했다.
http 통신할때 기본 데이터는 Content-Type을 application/json로 지정하고,
이미지나 파일 데이터를 보낼때는 Content-Type를 multipart/form-data 로 바꿔줘야한다.</p>
<pre><code class="language-tsx">
export const axiosInstance = axios.create({
  baseURL: BASE_URL,
  timeout: 3000,
});

axiosInstance.interceptors.request.use(
  //요청을 보내기 전에 수행할 일
  async config =&gt; {
    config.headers = config.headers ?? {};
    if (config.data instanceof FormData) {
      config.headers[&quot;Content-Type&quot;] = &quot;multipart/form-data&quot;;
    } else {
      config.headers[&quot;Content-Type&quot;] = &quot;application/json&quot;;
    }
    config.headers.Authorization = `Bearer ${sessionStorage.getItem(&quot;userToken&quot;)}`;
    return config;
  },
  error =&gt; {
    // 오류 요청을 보내기전 수행할 일
    // ...
    return Promise.reject(error);
  },
);</code></pre>
<p>위와같이 interceptor를 활용해서 Content-Type을 동적으로 지정할 수 있게 만들어
옵션부분을 생락해도 됨으로써, 아래와 같이 중복된 코드가 사라져 깔끔하다. </p>
<pre><code class="language-tsx">export async function createReview(contents: FormData) {
  return await axiosInstance.post(`review/create`, contents);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[에러일지] Props 넘겨줄때 type error | Type '{ type: string; }' is not assignable to type ~~ ]]></title>
            <link>https://velog.io/@hyejin_nk/%EC%97%90%EB%9F%AC%EC%9D%BC%EC%A7%80-Props-%EB%84%98%EA%B2%A8%EC%A4%84%EB%95%8C-type-error-Type-type-string-is-not-assignable-to-type</link>
            <guid>https://velog.io/@hyejin_nk/%EC%97%90%EB%9F%AC%EC%9D%BC%EC%A7%80-Props-%EB%84%98%EA%B2%A8%EC%A4%84%EB%95%8C-type-error-Type-type-string-is-not-assignable-to-type</guid>
            <pubDate>Mon, 07 Nov 2022 07:25:43 GMT</pubDate>
            <description><![CDATA[<p>동그란 버튼이 재사용 되어있어서 이것을 컴포넌트화 해서 사용하려고 하기위해서 
뒤로가기, 추가 , 닫기 버튼에 따라서 이미지가 달라져야함으로 type을 3가지로 주었다. </p>
<pre><code class="language-tsx">// type/weather.ts 
export type RoundButtonType = &quot;back&quot; | &quot;add&quot; | &quot;close&quot;;</code></pre>
<pre><code class="language-tsx">//components/round_button.tsx
import { RoundButtonType } from &quot;../type/weather&quot;;

export default function RoundButton(kind: RoundButtonType) {
  return &lt;button&gt;&lt;/button&gt;;
}</code></pre>
<p>해당버튼을 사용하려는 페이지에서 컴포넌트를 아래와 같이 호출했다. 
<img src="https://velog.velcdn.com/images/hyejin_nk/post/018f947d-8f26-43bd-a33e-f97d32dc187a/image.png" alt=""></p>
<hr>
<blockquote>
<p>문제해결</p>
</blockquote>
<p>1) </p>
<pre><code class="language-tsx">const test: RoundButtonType = &quot;add&quot;;</code></pre>
<p>위와같이 타입을 지정한 후 type에 test 변수를 전달하도록 테스트를 해봤지만 여전히 똑같이 에러가났다. 이부분이 문제가 아니라 props가 객체인데 타입지정을 잘못한 것이 문제였다. </p>
<pre><code class="language-tsx">//components/round_button.tsx
export default function RoundButton(kind: RoundButtonType) {
}</code></pre>
<p>RoundButtonType은 interface나 객체 type로 지정해준게 아니라,
union type으로 문자열에 값일 지정해줬다. </p>
<p>props에서는 객체로 전달되는데 이 부분이 문제였다.</p>
<p>props 안에있는 kind라는 key를 가진 값이 있다고 생각해보면,저렇게 지정해주면 안된다.</p>
<p>해결책 1)
type에 지정한 RoundButtonType은 그대로 두고
매개변수를 객체화 해주는 방법
<code>RoundButton(type: { kind: RoundButtonType })</code></p>
<pre><code class="language-tsx">export default function RoundButton(type: { kind: RoundButtonType }) {
  return (
    &lt;button className=&quot;rounded-full border p-3 shadow-md shadow-gray-800&quot;&gt;
      {changeImg(type.kind)}
    &lt;/button&gt;
  );
}</code></pre>
<p>해결책 2)
타입을 객체로 변경하고,
추가로 버튼에 다른 props들을 받을 수도 있기 때문에 key를 추가</p>
<pre><code class="language-tsx">// type/weather.ts 

//변경전
export type RoundButtonType = &quot;back&quot; | &quot;add&quot; | &quot;close&quot;;

//변경후
export type RoundButtonType = &quot;back&quot; | &quot;add&quot; | &quot;close&quot;;
export interface RoundButtonProps {
  kind: RoundButtonType;
  [key: string]: any;
}

//-------------------------------------------

// components/round_button.tsx 일부
export default function RoundButton({ kind, ...rest }: RoundButtonProps) {
  return (
    &lt;button className=&quot;rounded-full border p-3 shadow-md shadow-gray-800&quot;&gt;
      {changeImg(kind)}
    &lt;/button&gt;
  );
}
</code></pre>
<p>any를 쓰는것이 안좋은것을 알고있지만
우선 지금은 프롭스로 어떤것이 올지 정해져있지 않아서 any로 지정해두었다.</p>
<p>이후 프로젝트를 진행할 때,
prop-types 패키지를 적용해서 관리해보면 좋을것같다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[포트를 이미 사용하고 있는 경우 / Error: listen EADDRINUSE: address already in use]]></title>
            <link>https://velog.io/@hyejin_nk/%ED%8F%AC%ED%8A%B8%EB%A5%BC-%EC%9D%B4%EB%AF%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B3%A0-%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0-Error-listen-EADDRINUSE-address-already-in-use</link>
            <guid>https://velog.io/@hyejin_nk/%ED%8F%AC%ED%8A%B8%EB%A5%BC-%EC%9D%B4%EB%AF%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B3%A0-%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0-Error-listen-EADDRINUSE-address-already-in-use</guid>
            <pubDate>Sun, 25 Sep 2022 06:22:02 GMT</pubDate>
            <description><![CDATA[<h3 id="포트를-이미-사용하고있는-경우">포트를 이미 사용하고있는 경우?</h3>
<br>

<p>postman테스트 해보다가 분명 3009번 포트는 지금 진행하고있는 것밖에 없다고 생각했는데 아래와 같은 오류가 생겼다. </p>
<p><code>Error: listen EADDRINUSE: address already in use :::3009</code></p>
<p>종종 해당 포트를 사용하던 Nodejs 프로세서가 비정상적으로 종료된 경우에도 나타날 수 있다고 한다.</p>
 <Br>


<p>리눅스와 맥os에서는 터미널에서의 lsof 명령어를 통해 프로세스를 확인하고 종료할수 있다. </p>
<p>나는 3009번 포트를 사용하는 프로세스를 강제종료 시켰다. </p>
<ol>
<li><code>lsof -i :3009</code></li>
</ol>
<p>현재 3009번 포트를 사용하는 프로세스가 있다면 출력이나오면서, PID값을 확인할 수 있다. </p>
<ol start="2">
<li>1번에서 받아온 PID를 복사해뒀다가,  <code>kill -9 PID값</code>  명령어를 통해 강제 종료시킨다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[python] enumerate, map, filter, zip]]></title>
            <link>https://velog.io/@hyejin_nk/python-enumerate-map-filter-zip</link>
            <guid>https://velog.io/@hyejin_nk/python-enumerate-map-filter-zip</guid>
            <pubDate>Wed, 21 Sep 2022 05:35:07 GMT</pubDate>
            <description><![CDATA[<p><code>for - in 이터러블한 객체(list, range,enemerate,zip ... )</code></p>
<h3 id="📌-enumerate">📌 enumerate</h3>
<p>값 뿐만 아니라 해당 리스트에 해당하는 <strong>인덱스</strong>까지 알 수 있다.</p>
<pre><code class="language-python">fruits = [&#39;사과&#39;,&#39;배&#39;,&#39;딸기&#39;];
for idx,fruit in enumerate(fruits):
    print(f&quot;{idx+1}번째 과일은 {fruit}&quot;);</code></pre>
 <br>

<h3 id="📌-map">📌 map</h3>
<p><code>map(함수, 리스트/튜플)</code>
함수의 return 값은 항상 존재해야한다.</p>
<p>map객체로 출력되기 때문에 list로 형변환이 필요하다. </p>
<p>리스트 원소에 대해서 <strong>각 함수를 적용</strong>한다. 
map자체를 출력하면 map객체로 출력되기 때문에 리스트로 형변환 하거나 
for문을 사용해서 각 원소들을 출력해야한다. </p>
<pre><code class="language-python">prices = [500,300,1000,3400]

def print_price(price):
    return f&quot;{price}&quot;원
print(list(map(print_price,prices)))

for i in map(print_price,prices):
    print(i); 

#lambda 활용 
list(map(lambda x:f&quot;{x}원&quot;,prices))</code></pre>
<pre><code class="language-python">#10이하의 양의정수의 세제곱 구하기 
list(map(lambda x:x**3,range(1,11)))</code></pre>
<pre><code class="language-python"># 코테에서 일괄적으로 int를 적용하기 위해서도 많이 사용했음 
input_list = list(map(int,input().split()));</code></pre>
<br>

<h3 id="📌-filter">📌 filter</h3>
<p><code>filter(함수, 리스트/튜플)</code>
함수의 return값은 True, False여야 한다.</p>
<p>map과 비슷하지만 <strong>함수가 true인 경우만</strong> 원소를 담아주게된다.
map과 동일하게 list로 형번환하거나 for문으로 출력해야한다. </p>
<pre><code class="language-python">def is_even(x):
    return x%2==0
list(filter(is_even,range(10)))

list(filter(lambda x:x**x,range(10)))</code></pre>
<pre><code class="language-python"># 100이하의 숫자 중 6의 배수를 리스트 형태로 출력 
# 조건 :lambda , filter 사용 
print(list(filter(lambda x:x%6==0,range(1,101))))
# [6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]</code></pre>
<br>

<h3 id="📌-zip">📌 zip</h3>
<p>zip(iterable*)은 동일한 개수로 이루어진 자료형을 묶어 주는 역할을 하는 함수이다. </p>
<pre><code class="language-python">
&gt;&gt;&gt; list(zip([1, 2, 3], [4, 5, 6]))
[(1, 4), (2, 5), (3, 6)]
&gt;&gt;&gt; list(zip(&quot;abc&quot;,&quot;def&quot;))
[(&#39;a&#39;, &#39;d&#39;), (&#39;b&#39;, &#39;e&#39;), (&#39;c&#39;, &#39;f&#39;)]</code></pre>
<pre><code class="language-python">&gt;&gt;&gt; for x, y in zip(range(10), range(10)):  # 두개의 0~9까지 숫자모음
&gt;&gt;&gt;     print(x, y)
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 아주 간단한 커피주문 계산 (2)]]></title>
            <link>https://velog.io/@hyejin_nk/React-%EC%95%84%EC%A3%BC-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BB%A4%ED%94%BC%EC%A3%BC%EB%AC%B8-%EA%B3%84%EC%82%B0-2</link>
            <guid>https://velog.io/@hyejin_nk/React-%EC%95%84%EC%A3%BC-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BB%A4%ED%94%BC%EC%A3%BC%EB%AC%B8-%EA%B3%84%EC%82%B0-2</guid>
            <pubDate>Mon, 15 Aug 2022 14:46:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>변경전 
MenuList 같은 컴포넌트가 중복되는 것을 알 수 있다.
지금은 데이터가 몇개 되지 않지만 이것이 백개만 넘어가더라도 일일히 다 수정을 해야하는 번거로움도 있다.</p>
</blockquote>
<pre><code class="language-jsx">return (
    &lt;&gt;
      &lt;MenuList price=&quot;4100&quot; menu=&quot;아메리카노&quot; handleClick={handleClick} /&gt;
      &lt;MenuList price=&quot;4600&quot; menu=&quot;카페라떼&quot; handleClick={handleClick} /&gt;
      &lt;MenuList price=&quot;5100&quot; menu=&quot;캐러멜 마키아토&quot; handleClick={handleClick} /&gt;
      &lt;button onClick={handleClickTotal}&gt;총 가격 &lt;/button&gt; : {payMoney}
      &lt;p&gt;남는 돈: {change} &lt;/p&gt;
    &lt;/&gt;
  );</code></pre>
<br>
<br>

<blockquote>
<p>변경 후
coffee라는 객체를 가진 배열이 있다고 가정하고, 
map함수를 활용하여 MenuList 컴포넌트를 coffee라는 객체에 따라 유동적으로 사용할 수 있도록 변경했다. </p>
</blockquote>
<pre><code class="language-jsx">//data 
const coffee = [
  {
    menu: &quot;아메리카노&quot;,
    price: 4100,
    count: 0,
  },
  {
    menu: &quot;카페라떼&quot;,
    price: 4600,
    count: 0,
  },
  {
    menu: &quot;캬라멜 마끼아또&quot;,
    price: 5100,
    count: 0,
  },
];</code></pre>
<pre><code class="language-jsx">// 
return (
    &lt;&gt;
      {coffee &amp;&amp;
        coffee.map((item) =&gt; (
          &lt;MenuList
            price={item.price}
            menu={item.menu}
            handleClick={handleClick}
          /&gt;
        ))}
      &lt;button onClick={handleClickTotal}&gt;총 가격 &lt;/button&gt; : {payMoney}
      &lt;p&gt;남는 돈: {change} &lt;/p&gt;
    &lt;/&gt;
  );
</code></pre>
<br>
<br>

<blockquote>
<p>전체코드 </p>
</blockquote>
<pre><code class="language-jsx">import React, { useState, useEffect } from &quot;react&quot;;

function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;Menu /&gt;
    &lt;/div&gt;
  );
}
//data
const coffee = [
  {
    menu: &quot;아메리카노&quot;,
    price: 4100,
    count: 0,
  },
  {
    menu: &quot;카페라떼&quot;,
    price: 4600,
    count: 0,
  },
  {
    menu: &quot;캬라멜 마끼아또&quot;,
    price: 5100,
    count: 0,
  },
];

//  -----------------------------함수형 컴포넌트 분리
const Menu = () =&gt; {
  const MONEY = 50000; // 지정한 회비

  const [data, setData] = useState([]);
  const [payMoney, setPayMoney] = useState(0);
  const [change, setChange] = useState(0);

  const handleClickTotal = (e) =&gt; {
    //총 가격 계산
    const total = data.reduce((pre, cur) =&gt; {
      return pre + cur.price * cur.count;
    }, 0);

    //가격에 따라서 총 금액과 남는돈 set
    setPayMoney(total);
    setChange(MONEY - total);
  };

  //데이터 잘 보이는지 보려는 용도
  useEffect(() =&gt; {
    console.log(&quot;Menu useEffect&quot;, data);
  }, [data]);

  const handleClick = (pass) =&gt; {
    //console.log(&quot;pass data:&quot;, pass);

    const newData = [...data];
    const exist = newData.find((item) =&gt; pass.menu === item.menu);

    //같은 데이터가 존재하지 않는다면 넣어주고
    if (!exist) {
      newData.push(pass);
    }
    //같은 데이터가 존재한다면 count값만 갱신
    else {
      newData.forEach((item, index) =&gt; {
        if (item.menu === pass.menu) {
          newData[index].count = pass.count;
        }
      });
    }
    setData(newData);
  };

  return (
    &lt;&gt;
      {coffee &amp;&amp;
        coffee.map((item) =&gt; (
          &lt;MenuList
            price={item.price}
            menu={item.menu}
            handleClick={handleClick}
          /&gt;
        ))}
      &lt;button onClick={handleClickTotal}&gt;총 가격 &lt;/button&gt; : {payMoney}
      &lt;p&gt;남는 돈: {change} &lt;/p&gt;
    &lt;/&gt;
  );
};

const MenuList = ({ price, menu, handleClick }) =&gt; {
  const [count, setCount] = useState(0);

  //useEffect로 변경하여 부모컴포넌트에 데이터를 전달
  useEffect(() =&gt; {
    handleClick({ price, menu, count });
  }, [count]);

  //이것도 왜 2번호출되지 ?
  // useEffect(() =&gt; {
  //   console.log(&quot;useEffect : &quot;);
  // }, []);

  const handleClickAdd = () =&gt; setCount(count + 1);

  const handleClickDelete = () =&gt;
    setCount((current) =&gt; (current &gt; 0 ? current - 1 : 0));

  return (
    &lt;p&gt;
      &lt;span&gt;{`${price}원 ${menu}: `}&lt;/span&gt;
      &lt;span&gt;{`${count}`}&lt;/span&gt;
      &lt;button onClick={handleClickAdd}&gt;추가&lt;/button&gt;
      &lt;button onClick={handleClickDelete}&gt;감소&lt;/button&gt;
    &lt;/p&gt;
  );
};

export default App;
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 아주 간단한 커피주문 계산 (1)]]></title>
            <link>https://velog.io/@hyejin_nk/React-%EC%95%84%EC%A3%BC-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BB%A4%ED%94%BC%EC%A3%BC%EB%AC%B8-%EA%B3%84%EC%82%B0-1</link>
            <guid>https://velog.io/@hyejin_nk/React-%EC%95%84%EC%A3%BC-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%BB%A4%ED%94%BC%EC%A3%BC%EB%AC%B8-%EA%B3%84%EC%82%B0-1</guid>
            <pubDate>Mon, 15 Aug 2022 14:21:13 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>문제:</strong> 정해진 예산 5만원이 있고 어떠한 커피를 주문하는지에 따라서 해당 메뉴가 카운팅 되고,
총 가격 버튼을 누를 경우 총 얼마를 지불해야하는지, 거스름돈이 출력된다.</p>
</blockquote>
<ol>
<li>연습겸 파일을 별도로 분리하지 않고 우선 App.js에 한꺼번에 작성.
( 추후에는 분리해서 import export로 사용할 예정 ) </li>
</ol>
<pre><code class="language-jsx">import React, { useState } from &quot;react&quot;;

function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;Menu /&gt;
    &lt;/div&gt;
  );
}

//  -----------------------------함수형 컴포넌트 분리
const Menu = () =&gt; {
  const [data, setData] = useState([]);

  const parentFunction = (pass) =&gt; {
    console.log(pass);
    const newData = [...data];
    newData.push(pass);
    setData(newData);
  };

  return (
    &lt;&gt;
      &lt;MenuList
        price=&quot;4100&quot;
        menu=&quot;아메리카노&quot;
        parentFunction={parentFunction}
      /&gt;
      &lt;MenuList price=&quot;4600&quot; menu=&quot;카페라떼&quot; parentFunction={parentFunction} /&gt;
      &lt;MenuList
        price=&quot;5100&quot;
        menu=&quot;캬라멜 마끼아또&quot;
        parentFunction={parentFunction}
      /&gt;
      &lt;button&gt;총 가격 &lt;/button&gt; :&lt;p&gt;남는 돈: &lt;/p&gt;
    &lt;/&gt;
  );
};

const MenuList = ({ price, menu, parentFunction }) =&gt; {
  const [count, setCount] = useState(0);
  const handleClickAdd = () =&gt; {
    setCount((current) =&gt; current + 1);

    console.log(count);
    parentFunction({ price, menu, count });
  };
  const handleClickDelete = () =&gt; {
    setCount((current) =&gt; (current &gt; 0 ? current - 1 : 0));

    console.log(count);
  };
  return (
    &lt;p&gt;
      {`${price}원 ${menu}: ${count}`}
      &lt;button onClick={handleClickAdd}&gt;추가&lt;/button&gt;
      &lt;button onClick={handleClickDelete}&gt;감소&lt;/button&gt;
    &lt;/p&gt;
  );
};

export default App;</code></pre>
<br>

<blockquote>
<p>1) 문제점 
더해진 값에 따라서 버튼을 클릭할 때마다 화면에는 증가하는 값이 잘 출력이 된다. 
하지만, count를 바로 찍어도 더해진 값이 출력되지 않는다.
useState의 setCount는 비동기로 동작하기 때문에 그러한 것으로 보인다. </p>
</blockquote>
<pre><code class="language-jsx">const handleClickAdd = () =&gt; {
    setCount((current) =&gt; current + 1); // setCount(count + 1);

    console.log(count);
    parentFunction({ price, menu, count });
  };</code></pre>
<p>해당 값(<strong>count</strong>)이 변할때를 감지하는<strong><code>useEffect()</code></strong> 를 추가하여 해결</p>
<pre><code class="language-jsx">import React, { useState, useEffect } from &quot;react&quot;;

function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;Menu /&gt;
    &lt;/div&gt;
  );
}

//  -----------------------------함수형 컴포넌트 분리

const Menu = () =&gt; {
  const MONEY = 50000; // 지정한 회비

  const [data, setData] = useState([]);
  const [payMoney, setPayMoney] = useState(0);
  const [change, setChange] = useState(0);

  const handleClickTotal = (e) =&gt; {
    //총 가격 계산
    const total = data.reduce((pre, cur) =&gt; {
      return pre + cur.price * cur.count;
    }, 0);

    //가격에 따라서 총 금액과 남는돈 set
    setPayMoney(total);
    setChange(MONEY - total);
  };

  useEffect(() =&gt; {
    console.log(&quot;Menu useEffect&quot;, data);
  }, [data]);

  const handleClick = (pass) =&gt; {
    //console.log(&quot;pass data:&quot;, pass);

    const newData = [...data];
    const exist = newData.find((item) =&gt; pass.menu === item.menu);

    //같은 데이터가 존재하지 않는다면 넣어주고
    if (!exist) {
      newData.push(pass);
    }
    //같은 데이터가 존재한다면 count값만 갱신
    else {
      newData.forEach((item, index) =&gt; {
        if (item.menu === pass.menu) {
          newData[index].count = pass.count;
        }
      });
    }
    setData(newData);
  };

  return (
    &lt;&gt;
      &lt;MenuList price=&quot;4100&quot; menu=&quot;아메리카노&quot; handleClick={handleClick} /&gt;
      &lt;MenuList price=&quot;4600&quot; menu=&quot;카페라떼&quot; handleClick={handleClick} /&gt;
      &lt;MenuList price=&quot;5100&quot; menu=&quot;캬라멜 마끼아또&quot; handleClick={handleClick} /&gt;
      &lt;button onClick={handleClickTotal}&gt;총 가격 &lt;/button&gt; : {payMoney}
      &lt;p&gt;남는 돈: {change} &lt;/p&gt;
    &lt;/&gt;
  );
};

const MenuList = ({ price, menu, handleClick }) =&gt; {
  const [count, setCount] = useState(0);

  //useEffect로 변경하여 부모컴포넌트에 데이터를 전달
  useEffect(() =&gt; {
    handleClick({ price, menu, count });
  }, [count]);

  //이것도 왜 2번호출되지 ?
  // useEffect(() =&gt; {
  //   console.log(&quot;useEffect : &quot;);
  // }, []);

  const handleClickAdd = () =&gt; setCount(count + 1);

  const handleClickDelete = () =&gt;
    setCount((current) =&gt; (current &gt; 0 ? current - 1 : 0));

  return (
    &lt;p&gt;
      &lt;span&gt;{`${price}원 ${menu}: `}&lt;/span&gt;
      &lt;span&gt;{`${count}`}&lt;/span&gt;
      &lt;button onClick={handleClickAdd}&gt;추가&lt;/button&gt;
      &lt;button onClick={handleClickDelete}&gt;감소&lt;/button&gt;
    &lt;/p&gt;
  );
};

export default App;

</code></pre>
<ul>
<li>useEffect를 추가하여 count 값이 변화함에 따라서 부모 컴포넌트에 커피의 카운팅을 전달한다.</li>
<li>useState로 총 지불해야할 금액과 남은 금액을 변수로 담아 출력한다. 
ㄴ 버튼을 누를 경우 총 가격을 계산 후 setPayMoney,setChange를 통해 값을 변경한다. </li>
</ul>
<blockquote>
<p>2) 문제점 / useEffect 의 동작</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hyejin_nk/post/2077ad69-2a5d-4251-bbbf-e89629ee08ff/image.png" alt="">
useEffect는 랜더링 될 때와 값이 변경될때 호출이되는 것으로 알고있는데,
처음 랜더링될때 이것이 2번 호출되는 것으로 보이며,
<code>&lt;MenuList&gt;</code>의 마지막 요소가 data로 미리 들어가면서 한번더 useEffect가 호출되는 문제점을 볼 수 있었다. </p>
<p>콘솔로 찍어보지 않았을 때는 제대로 동작하는걸로 착각할 수 있다.
현재 코드는 뭔가 이상해보인다. </p>
]]></description>
        </item>
    </channel>
</rss>